WaniKani Disable Default Answers

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

  1. // ==UserScript==
  2. // @name WaniKani Disable Default Answers
  3. // @namespace *://www.wanikani.com
  4. // @version 0.2.1
  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 || url.indexOf('lesson/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. var observer2 = new MutationObserver(function(mutations) {
  80. $("#supplement-voc-meaning .col1 h2:nth-child(1)").html('Acceptable Answers');
  81. var answer_array = [$('#meaning').html().toLowerCase(),];
  82. if ($('#supplement-voc-synonyms').html() != '(None)')
  83. answer_array = answer_array.concat($('#supplement-voc-synonyms').html().toLowerCase().split(', '));
  84. var html = '';
  85. for(var i = 0; i<answer_array.length; i++){
  86. html += '<p class="blackened ' + isException(word, answer_array[i]) + '">' + answer_array[i] + '</p>';
  87. if(i+1<answer_array.length) html += ', ';
  88. }
  89. $('#supplement-voc-synonyms').html(html);
  90. $('p.blackened').hover(function(){
  91. $(this).toggleClass('hover_blacklist');
  92. });
  93. $('p.blackened').click(function(){
  94. $(this).toggleClass('not_accepted');
  95. if(!isException(word, $(this).text())) exception_array.push([word, $(this).text()]);
  96. else removeException(word, $(this).text());
  97.  
  98. $.jStorage.set('exception_array', exception_array);
  99. });
  100. });
  101. observer2.observe($('div#character').get(0), { attributes: true, childList: true, characterData: true });
  102.  
  103. waitForKeyElements("section#item-info-meaning", function(){
  104. $("#item-info-col1").prepend('<section class="blacklist"><h2>Acceptable Meanings</h2></section>');
  105.  
  106. var answer_array = $('section#item-info-meaning').text().slice(8).toLowerCase().split(', ');
  107.  
  108. var html = '';
  109. for(var i = 0; i<answer_array.length; i++){
  110. html += '<p class="blackened ' + isException(word, answer_array[i]) + '">' + answer_array[i] + '</p>';
  111. if(i+1<answer_array.length) html += ', ';
  112. }
  113. $('section.blacklist').append(html);
  114. $('section#item-info-meaning').css('display','none');
  115. if ($('#answer-form input').attr('lang') == 'ja') $('section.blacklist').css('display','none');
  116. $('p.blackened').hover(function(){
  117. $(this).toggleClass('hover_blacklist');
  118. });
  119. $('p.blackened').click(function(){
  120. $(this).toggleClass('not_accepted');
  121. if(!isException(word, $(this).text())) exception_array.push([word, $(this).text()]);
  122. else removeException(word, $(this).text());
  123.  
  124. $.jStorage.set('exception_array', exception_array);
  125. });
  126. });
  127. }
  128. })();
  129.  
  130. function waitForKeyElements (
  131. selectorTxt, /* Required: The jQuery selector string that
  132. specifies the desired element(s).
  133. */
  134. actionFunction, /* Required: The code to run when elements are
  135. found. It is passed a jNode to the matched
  136. element.
  137. */
  138. bWaitOnce, /* Optional: If false, will continue to scan for
  139. new elements even after the first match is
  140. found.
  141. */
  142. iframeSelector /* Optional: If set, identifies the iframe to
  143. search.
  144. */
  145. ) {
  146. var targetNodes, btargetsFound;
  147.  
  148. if (typeof iframeSelector == "undefined")
  149. targetNodes = $(selectorTxt);
  150. else
  151. targetNodes = $(iframeSelector).contents ()
  152. .find (selectorTxt);
  153.  
  154. if (targetNodes && targetNodes.length > 0) {
  155. btargetsFound = true;
  156. /*--- Found target node(s). Go through each and act if they
  157. are new.
  158. */
  159. targetNodes.each ( function () {
  160. var jThis = $(this);
  161. var alreadyFound = jThis.data ('alreadyFound') || false;
  162.  
  163. if (!alreadyFound) {
  164. //--- Call the payload function.
  165. var cancelFound = actionFunction (jThis);
  166. if (cancelFound)
  167. btargetsFound = false;
  168. else
  169. jThis.data ('alreadyFound', true);
  170. }
  171. } );
  172. }
  173. else {
  174. btargetsFound = false;
  175. }
  176.  
  177. //--- Get the timer-control variable for this selector.
  178. var controlObj = waitForKeyElements.controlObj || {};
  179. var controlKey = selectorTxt.replace (/[^\w]/g, "_");
  180. var timeControl = controlObj [controlKey];
  181.  
  182. //--- Now set or clear the timer as appropriate.
  183. if (btargetsFound && bWaitOnce && timeControl) {
  184. //--- The only condition where we need to clear the timer.
  185. clearInterval (timeControl);
  186. delete controlObj [controlKey];
  187. }
  188. else {
  189. //--- Set a timer, if needed.
  190. if ( ! timeControl) {
  191. timeControl = setInterval ( function () {
  192. waitForKeyElements ( selectorTxt,
  193. actionFunction,
  194. bWaitOnce,
  195. iframeSelector
  196. );
  197. },
  198. 300
  199. );
  200. controlObj [controlKey] = timeControl;
  201. }
  202. }
  203. waitForKeyElements.controlObj = controlObj;
  204. }
  205.  
  206. function isException(word, exception) {
  207. for(var i=0; i<exception_array.length; i++){
  208. if(exception_array[i][0] == word && exception_array[i][1] == exception)
  209. return "not_accepted";
  210. }
  211. return false;
  212. }
  213.  
  214. function removeException(word, exception) {
  215. for(var i=0; i<exception_array.length; i++){
  216. if(exception_array[i][0] == word && exception_array[i][1] == exception) {
  217. exception_array.splice(i,1);
  218. return "Done";
  219. }
  220. }
  221. return "Failed";
  222. }
  223.  
  224. function WKO_ignoreAnswer() {
  225. $('#answer-form fieldset').removeClass('correct');
  226. $("#answer-form form").effect("shake", {}, 400).find("input").focus();
  227. return true;
  228. }