WaniKani Item Annotator

Annotates radical, kanji and vocab pages with SRS colours. Original script by jeshuamorrissey.

  1. // ==UserScript==
  2. // @name WaniKani Item Annotator
  3. // @namespace mempo
  4. // @description Annotates radical, kanji and vocab pages with SRS colours. Original script by jeshuamorrissey.
  5. // @author Mempo
  6. // @version 1.3.2
  7. // @include http://www.wanikani.com/radical*
  8. // @include http://www.wanikani.com/kanji*
  9. // @include http://www.wanikani.com/vocabulary*
  10. // @include http://www.wanikani.com/account*
  11. // @include https://www.wanikani.com/radical*
  12. // @include https://www.wanikani.com/kanji*
  13. // @include https://www.wanikani.com/vocabulary*
  14. // @include https://www.wanikani.com/account*
  15. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
  16. // @grant none
  17. // ==/UserScript==
  18.  
  19. var $ = jQuery;
  20.  
  21. console.log('@@@@ start of WaniKani Item Annotator');
  22.  
  23. var apiKey = 'b998b40a4587405b3ca65fa03705dce4';
  24.  
  25. if(apiKey === null){ //not initialized yet
  26. console.log('#### no apiKey found');
  27. if (window.location.href.indexOf('account') != - 1) {
  28. apiKey = "" + retrieveAPIkey();
  29. console.log('@@@@@' + apiKey);
  30. $.jStorage.set('WIA_apiKey', apiKey);
  31. } else {
  32. var okcancel = confirm('WaniKani Item Annotator has no API key entered!\nPress OK to go to your settings page and retrieve your API key!');
  33. if (okcancel == true) {
  34. window.location = 'https://www.wanikani.com/settings/account';
  35. return;
  36. }
  37. }
  38. }
  39.  
  40. console.log('#### apiKey is: ' + apiKey);
  41. // Determine which API call we are going to make.
  42. var target = 'kanji';
  43. if (window.location.href.indexOf('vocabulary') >= 0) {
  44. target = 'vocabulary';
  45. } else if (window.location.href.indexOf('radicals') >= 0) {
  46. target = 'radicals';
  47. }
  48.  
  49. //console.log('@@@ target is: ' + target);
  50. var css =
  51. '.WIA_apprentice {' +
  52. ' background: #f100a0 linear-gradient(to bottom,#f0a,#dd0093) !important;' +
  53. ' border-color: #f100a0 !important;' +
  54. '} ' +
  55. '.WIA_guru {' +
  56. ' background: #882d9e linear-gradient(to bottom,#aa38c6,#882d9e) !important;' +
  57. ' border-color: #882d9e !important;' +
  58. '} ' +
  59. '.WIA_master {' +
  60. ' background: #294ddb linear-gradient(to bottom,#5571e2,#294ddb) !important;' +
  61. ' border-color: #294ddb !important;' +
  62. '} ' +
  63. '.WIA_enlighten {' +
  64. ' background: #0093dd linear-gradient(to bottom,#0af,#0093dd) !important;' +
  65. ' border-color: #0093dd !important;' +
  66. '} ' ;
  67.  
  68.  
  69. // ADD CSS
  70. addStyle(css);
  71.  
  72. // Load the API data.
  73.  
  74.  
  75. console.log('before API call');
  76.  
  77. $.get(apiURL(target), function(xhr) {
  78. // Parse the response.
  79. /*
  80. console.log("###### JSON RESPONSE");
  81. console.log(xhr);
  82. */
  83.  
  84. // Build up an item mapping from Kanji --> Information
  85. var itemMapping = {};
  86.  
  87. // Get the actual request information. If the target is vocabulary, for some reason
  88. // we have to got an additional level into 'request_information.general'. This is
  89. // probably to account for specialised vocab which will be added later.
  90. var information = xhr.requested_information;
  91. if (target === 'vocabulary') {
  92. information = information.general;
  93. }
  94.  
  95. for (var i in information) {
  96. var item = information[i];
  97.  
  98. // Extract the character (Kanji) from the item.
  99. var character = item.character;
  100.  
  101. // If we are looking at radicals, use the meaning instead (convert the meaning to
  102. // the 'user friendly' format).
  103. if (target === 'radicals') {
  104. character = item.meaning.toLowerCase();
  105. }
  106.  
  107. //console.log("ITEMMAPPING CHARACTER:" + character);
  108.  
  109. // Get the SRS level from the item. The 'user_specific' object will be `null` if the item
  110. // hasn't been unlocked yet. In this case, just set the SRS level to `null`.
  111. var srs = null;
  112. if (item.user_specific) {
  113. srs = item.user_specific.srs;
  114. }
  115.  
  116. // Build the mapping for this character.
  117. itemMapping[character] = {
  118. 'srs': srs
  119. };
  120. }
  121. /*
  122. console.log('&&&&& ITEM MAPPING');
  123. console.log(itemMapping);
  124. */
  125. // Actually do stuff with this mapping.
  126. main(itemMapping, target);
  127. });
  128.  
  129. /**
  130. * Mapping of SRS --> Object, where the object contains a series
  131. * of transformation colors. These transformations will be applied
  132. * via the element.style property, so should have priority.
  133. */
  134.  
  135.  
  136.  
  137. /**
  138. * Main function: actually annotate the elements. Takes as input information from
  139. * the WK API as a mapping from Japanese Element --> Object. In this case, the
  140. * object need only contain the SRS level of the element.
  141. */
  142. function main(itemMapping, target) {
  143. //console.log('inside main function');
  144.  
  145. // Find all characters on the page.
  146. var elements = $('.character-item');
  147. //console.log('size of elements is ' + elements.size());
  148. var i= 0;
  149. for (i=0;i<elements.length;i++) {
  150. var element = elements[i];
  151. //console.log("elements:" + element);
  152. /*
  153. // If this isn't actually an element (could happen, who knows), just skip it.
  154. if (!element.querySelector || !element.style) {
  155. console.log('///////////////////////////// What the shit why are we here.');
  156. continue;
  157. }
  158. */
  159.  
  160.  
  161. // The japanese value to look up in the item mapping is the text of this element.
  162. var japanese = element.querySelector('.character').innerText;
  163.  
  164. // If we happen to be looking at radicals, some of them use pictures instead. It is
  165. // simpler to use the radical meaning in this case (as there is only one meaning).
  166. // The meaning is stored in the last list element within the element (for some reason
  167. // there is a &nbsp; list element first).
  168. if (target === 'radicals') {
  169. var radicalLink = element.querySelector('a').getAttribute('href');
  170. japanese = radicalLink.slice(radicalLink.lastIndexOf('/') + 1);
  171. //console.log('@@@@@ ' + japanese);
  172. }
  173.  
  174. /*
  175. console.log('@@@@@ japanese var:');
  176. console.log(japanese);
  177. console.log(itemMapping[japanese]);
  178.  
  179. */
  180.  
  181. // If we couldn't find the SRS information for the element, or the element hasn't been unlocked
  182. // yet, just ignore it.
  183. /*
  184. if (!japanese.srs) {
  185. console.log('///////////// What are you doing here????');
  186. continue;
  187. }
  188. */
  189.  
  190. // Find the corresponding colors.
  191. //var colors = colorDict[japanese.srs];
  192. $(element).addClass("WIA_" + itemMapping[japanese].srs);
  193. }
  194. }
  195. function retrieveAPIkey() {
  196. var apiKey = document.getElementById('user_api_key').value;
  197. alert('API key was set to: ' + apiKey);
  198. if (apiKey) {
  199. return apiKey;
  200. }
  201. }
  202.  
  203. function apiURL(target){
  204. return 'https://www.wanikani.com/api/user/' + apiKey + '/' + target;
  205. }
  206.  
  207. function addStyle(aCss) {
  208. var head, style;
  209. head = document.getElementsByTagName('head')[0];
  210. if (head) {
  211. style = document.createElement('style');
  212. style.setAttribute('type', 'text/css');
  213. style.textContent = aCss;
  214. head.appendChild(style);
  215. return style;
  216. }
  217. return null;
  218. }
  219.  

QingJ © 2025

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