JPDB Add WaniKani Info To Review

6/17/2024, 5:34:38 PM

  1. // ==UserScript==
  2. // @name JPDB Add WaniKani Info To Review
  3. // @namespace JPDB_Add_WaniKani_Info_To_Review
  4. // @match https://jpdb.io/review*
  5. // @grant GM_xmlhttpRequest
  6. // @version 0.03
  7. // @author Flipp Fuzz
  8. // @description 6/17/2024, 5:34:38 PM
  9. // @run-at document-idle
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15. // Handling of answer is copied from https://greasyfork.org/en/scripts/459772-jpdb-auto-reveal-answer-sentence/code
  16. // -こう- implemented the switch from question to answer without really navigating
  17. // by simply replacing the content and location.href on clicking the "show answer" button
  18. window.onload = observeUrlChange(setup);
  19.  
  20. // this handles refreshing the page while on the answer screen
  21. setup();
  22. })();
  23.  
  24. function observeUrlChange(onChange) {
  25. let oldHref = document.location.href;
  26. const body = document.querySelector("body");
  27. const observer = new MutationObserver(mutations => {
  28. mutations.forEach(() => {
  29. if (oldHref !== document.location.href) {
  30. oldHref = document.location.href;
  31. onChange();
  32. }
  33. });
  34. });
  35. observer.observe(body, { childList: true, subtree: true });
  36. }
  37.  
  38. function getWkItem(type, lookupString) {
  39. // type needs to be vocabulary or kanji
  40. const url = `https://www.wanikani.com/${type}/${lookupString}`;
  41.  
  42. return new Promise(resolve => {
  43. GM_xmlhttpRequest({
  44. method: "GET",
  45. url,
  46. onload: resolve
  47. });
  48. }).then(result => {
  49. // console.log(result);
  50. if(result.status == 200) {
  51. const titleSpan = result.responseXML.querySelector('#turbo-body > div.site-container > div.site-content-container > div:nth-child(3) > header > h1 > span');
  52. if(titleSpan) {
  53. return [lookupString, url, titleSpan.innerText];
  54. }
  55. return null;
  56. }
  57. });
  58. }
  59.  
  60. function setup() {
  61. if(document.querySelector('body > div.container.bugfix > div > div.review-reveal')) {
  62. // Figure out where to insert our new div
  63. const insertAfterElement = document.querySelector('body > div.container.bugfix > div > div.review-reveal > div.result.vocabulary > div > div.subsection-meanings');
  64.  
  65. // Find out what is our vocab word
  66. const wordA = document.querySelector('body > div.container.bugfix > div > div.review-reveal > div.answer-box > div.plain > a');
  67. const word = wordA.getAttribute('href').split(/[?#]/)[0].split('/').pop();
  68.  
  69. // Terminate if we can't find the word or location to insert
  70. if(!insertAfterElement || !wordA || !word) {
  71. console.log(`insertAfterElement: ${insertAfterElement}`);
  72. console.log(`wordA: ${wordA}`);
  73. console.log(`word: ${word}`);
  74. return;
  75. }
  76.  
  77. const wkItemsToSearch = [
  78. ['vocabulary', word]
  79. ];
  80.  
  81. // Find out all the Kanjis
  82. const KanjisA = document.querySelectorAll('body > div.container.bugfix > div > div.review-reveal > div.result.vocabulary > div > div.subsection-composed-of-kanji > div > div > div.spelling > a');
  83. KanjisA.forEach((kanjiA) => {
  84. let localKanji = kanjiA.getAttribute('href').split(/[?#]/)[0].split('/').pop();
  85. wkItemsToSearch.push(['kanji', localKanji]);
  86. });
  87.  
  88. Promise.all(
  89. wkItemsToSearch.map((entry) => getWkItem(entry[0], entry[1]))
  90. ).then((results) => {
  91. // console.log(results);
  92. results.forEach((result) => {
  93. if(result) {
  94. addWkItem(result[0], result[1], result[2]);
  95. }
  96. });
  97. });
  98. }
  99. }
  100.  
  101. function addMainDiv() {
  102. // Create the div if it is not present.
  103. // Otherwise, return the div
  104. const mainInnderDiv = document.querySelector('#JpdbAddWaniKaniInfoToReviewInnerDiv');
  105. if(mainInnderDiv)
  106. return mainInnderDiv;
  107.  
  108. // Figure out where to insert our new div
  109. const insertAfterElement = document.querySelector('body > div.container.bugfix > div > div.review-reveal > div.result.vocabulary > div > div.subsection-meanings');
  110.  
  111. let subsectionWkLinkDiv = document.createElement('div');
  112. subsectionWkLinkDiv.setAttribute('class', 'subsection-wk-link');
  113.  
  114. let subsectionWkLinkLabel = document.createElement('h6');
  115. subsectionWkLinkLabel.setAttribute('class', 'subsection-label');
  116. subsectionWkLinkLabel.textContent = 'WaniKani';
  117. subsectionWkLinkDiv.appendChild(subsectionWkLinkLabel);
  118.  
  119. let subsectionWkLinkSubsectionDiv = document.createElement('div');
  120. subsectionWkLinkSubsectionDiv.setAttribute('class', 'subsection');
  121. subsectionWkLinkDiv.appendChild(subsectionWkLinkSubsectionDiv);
  122. subsectionWkLinkDiv.setAttribute("id", "JpdbAddWaniKaniInfoToReviewInnerDiv");
  123.  
  124. insertAfterElement.insertAdjacentElement('afterend', subsectionWkLinkDiv);
  125.  
  126. return subsectionWkLinkDiv;
  127. }
  128.  
  129. function addWkItem(word, url, title) {
  130. const mainDiv = addMainDiv();
  131. console.log(mainDiv);
  132.  
  133. let itemP = document.createElement('p');
  134. mainDiv.appendChild(itemP);
  135.  
  136. let itemA1 = document.createElement('a');
  137. itemA1.textContent = `${word} ${title} `;
  138. itemA1.setAttribute('href', url);
  139. itemA1.setAttribute('target', '_blank');
  140. itemP.appendChild(itemA1);
  141. }