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