WaniKani Disable Default Answers

Prevent default answers from being marked as correct. Answer in your synonyms.

目前为 2017-12-06 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name WaniKani Disable Default Answers
  3. // @namespace *://www.wanikani.com
  4. // @version 0.1.4
  5. // @description Prevent default answers from being marked as correct. Answer in your synonyms.
  6. // @author polv
  7. // @match *://www.wanikani.com/*vocabulary/*
  8. // @match *://www.wanikani.com/*kanji/*
  9. // @match *://www.wanikani.com/*radical/*
  10. // @match *://www.wanikani.com/review/session*
  11. // @match *://www.wanikani.com/lesson/session*
  12. // @copyright 2017 polv
  13. // ==/UserScript==
  14.  
  15. var word = "";
  16. var exception_array = $.jStorage.get('exception_array', []);
  17.  
  18. (function() {
  19. 'use strict';
  20.  
  21. // Hook into App Store
  22. 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) {}
  23.  
  24. $(`<style type='text/css'>
  25. .hover_blacklist, .not_accepted {
  26. text-decoration:line-through;
  27. }
  28. .blackened {
  29. display: inline;
  30. }
  31. </style>`).appendTo('head');
  32. var url = document.URL;
  33. var answer_array = [];
  34. if (url.indexOf('vocabulary') != -1 || url.indexOf('kanji') != -1 || url.indexOf('radical') != -1) {
  35. word = $('span.japanese-font-styling-correction:first').text().trim();
  36. $('div.alternative-meaning:first').addClass('blacklist');
  37. $('div.blacklist h2').remove();
  38. $('div.blacklist').prepend('<h2>Acceptable Answers</h2>');
  39.  
  40. var text = $('div.span12 h1').text().replace(/[0-9]|[\u3000-\u9faf]/g, '').trim();
  41. if($('div.blacklist p').text() !== '') $('div.blacklist p').prepend(', ');
  42. $('div.blacklist p').prepend(text);
  43. answer_array = $('div.blacklist p').text().trim().toLowerCase().split(', ');
  44.  
  45. var html = '';
  46. for(var i = 0; i<answer_array.length; i++){
  47. html += '<p class="blackened ' + isException(word, answer_array[i]) + '">' + answer_array[i] + '</p>';
  48. if(i+1<answer_array.length) html += ', ';
  49. }
  50. $('div.blacklist p').remove();
  51. $('div.blacklist').append(html);
  52. $('p.blackened').hover(function(){
  53. $(this).toggleClass('hover_blacklist');
  54. });
  55.  
  56. $('p.blackened').click(function(){
  57. $(this).toggleClass('not_accepted');
  58. if(!isException(word, $(this).text())) exception_array.push([word, $(this).text()]);
  59. else removeException(word, $(this).text());
  60. $.jStorage.set('exception_array', exception_array);
  61. });
  62. } else if (url.indexOf('review/session') != -1) {
  63. $.jStorage.listenKeyChange('currentItem', function(key) {
  64. word = $('div#character').text().trim();
  65. });
  66.  
  67. var observer = new MutationObserver(function(mutations) {
  68. for(var i=0; i<mutations.length; ++i) {
  69. if(mutations[i].target.classList[0]) {
  70. if(isException(word, $("#user-response").val())){
  71. console.log('Exception!');
  72. WKO_ignoreAnswer();
  73. wkdoublecheck.set_state('first_submit');
  74. }
  75. }
  76. }
  77. });
  78. observer.observe($('#answer-form fieldset').get(0), { attributes: true });
  79.  
  80. waitForKeyElements("section#item-info-meaning", function(){
  81. $("#item-info-col1").prepend('<section class="blacklist"><h2>Acceptable Meanings</h2></section>');
  82.  
  83. var answer_array = $('section#item-info-meaning').text().slice(8).toLowerCase().split(', ');
  84.  
  85. var html = '';
  86. for(var i = 0; i<answer_array.length; i++){
  87. html += '<p class="blackened ' + isException(word, answer_array[i]) + '">' + answer_array[i] + '</p>';
  88. if(i+1<answer_array.length) html += ', ';
  89. }
  90. $('section.blacklist').append(html);
  91. $('section#item-info-meaning').css('display','none');
  92. if ($('#answer-form input').attr('lang') == 'ja') $('section.blacklist').css('display','none');
  93. $('p.blackened').hover(function(){
  94. $(this).toggleClass('hover_blacklist');
  95. });
  96. $('p.blackened').click(function(){
  97. $(this).toggleClass('not_accepted');
  98. if(!isException(word, $(this).text())) exception_array.push([word, $(this).text()]);
  99. else removeException(word, $(this).text());
  100.  
  101. $.jStorage.set('exception_array', exception_array);
  102. });
  103. });
  104. } else if (url.indexOf('lesson/session') != -1) {
  105. }
  106. })();
  107.  
  108. function waitForKeyElements (
  109. selectorTxt, /* Required: The jQuery selector string that
  110. specifies the desired element(s).
  111. */
  112. actionFunction, /* Required: The code to run when elements are
  113. found. It is passed a jNode to the matched
  114. element.
  115. */
  116. bWaitOnce, /* Optional: If false, will continue to scan for
  117. new elements even after the first match is
  118. found.
  119. */
  120. iframeSelector /* Optional: If set, identifies the iframe to
  121. search.
  122. */
  123. ) {
  124. var targetNodes, btargetsFound;
  125.  
  126. if (typeof iframeSelector == "undefined")
  127. targetNodes = $(selectorTxt);
  128. else
  129. targetNodes = $(iframeSelector).contents ()
  130. .find (selectorTxt);
  131.  
  132. if (targetNodes && targetNodes.length > 0) {
  133. btargetsFound = true;
  134. /*--- Found target node(s). Go through each and act if they
  135. are new.
  136. */
  137. targetNodes.each ( function () {
  138. var jThis = $(this);
  139. var alreadyFound = jThis.data ('alreadyFound') || false;
  140.  
  141. if (!alreadyFound) {
  142. //--- Call the payload function.
  143. var cancelFound = actionFunction (jThis);
  144. if (cancelFound)
  145. btargetsFound = false;
  146. else
  147. jThis.data ('alreadyFound', true);
  148. }
  149. } );
  150. }
  151. else {
  152. btargetsFound = false;
  153. }
  154.  
  155. //--- Get the timer-control variable for this selector.
  156. var controlObj = waitForKeyElements.controlObj || {};
  157. var controlKey = selectorTxt.replace (/[^\w]/g, "_");
  158. var timeControl = controlObj [controlKey];
  159.  
  160. //--- Now set or clear the timer as appropriate.
  161. if (btargetsFound && bWaitOnce && timeControl) {
  162. //--- The only condition where we need to clear the timer.
  163. clearInterval (timeControl);
  164. delete controlObj [controlKey];
  165. }
  166. else {
  167. //--- Set a timer, if needed.
  168. if ( ! timeControl) {
  169. timeControl = setInterval ( function () {
  170. waitForKeyElements ( selectorTxt,
  171. actionFunction,
  172. bWaitOnce,
  173. iframeSelector
  174. );
  175. },
  176. 300
  177. );
  178. controlObj [controlKey] = timeControl;
  179. }
  180. }
  181. waitForKeyElements.controlObj = controlObj;
  182. }
  183.  
  184. function isException(word, exception) {
  185. for(var i=0; i<exception_array.length; i++){
  186. if(exception_array[i][0] == word && exception_array[i][1] == exception)
  187. return "not_accepted";
  188. }
  189. return false;
  190. }
  191.  
  192. function removeException(word, exception) {
  193. for(var i=0; i<exception_array.length; i++){
  194. if(exception_array[i][0] == word && exception_array[i][1] == exception) {
  195. exception_array.splice(i,1);
  196. return "Done";
  197. }
  198. }
  199. return "Failed";
  200. }
  201.  
  202. function WKO_ignoreAnswer() {
  203. $('#answer-form fieldset').removeClass('correct');
  204. $("#answer-form form").effect("shake", {}, 400).find("input").focus();
  205. return true;
  206. }