WaniKani Lesson Hover Details

Show lesson breakdown by type on hover

  1. // ==UserScript==
  2. // @name WaniKani Lesson Hover Details
  3. // @namespace https://www.wanikani.com
  4. // @description Show lesson breakdown by type on hover
  5. // @author seanblue
  6. // @version 1.2.1
  7. // @include https://www.wanikani.com/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function(wkof, $) {
  12. 'use strict';
  13.  
  14. if (!wkof) {
  15. var response = confirm('WaniKani Lesson Hover Details script requires WaniKani Open Framework.\n Click "OK" to be forwarded to installation instructions.');
  16.  
  17. if (response) {
  18. window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
  19. }
  20.  
  21. return;
  22. }
  23.  
  24. const levelSettingOptions = {
  25. None: 0,
  26. CurrentLevel: 1,
  27. PriorLevels: 2
  28. };
  29.  
  30. // USER SETTING: Change this to display current level data, prior level data, or no level data in parentheses. Use one of the options from "levelSettingOptions".
  31. const levelSetting = levelSettingOptions.CurrentLevel;
  32.  
  33. const lessonMenuItemSelector = '.navigation .navigation-shortcut--lessons a';
  34. const lessonDashboardItemSelector = 'a.lessons-and-reviews__lessons-button';
  35.  
  36. const popoverTemplate = '<div class="popover review-time"><div class="arrow"></div><div class="popover-inner"><div class="popover-content"><p></p></div></div></div>';
  37.  
  38. const popoverConfig = {
  39. html: true,
  40. animation: false,
  41. placement: 'bottom',
  42. trigger: 'hover',
  43. template: popoverTemplate
  44. };
  45.  
  46. const style = `<style>
  47. .popover { width: auto; }
  48. .lhd-table { display: table; margin: 0; padding: 0; }
  49. .lhd-row { display: table-row; margin: 0; padding: 0; }
  50. .lhd-cell { display: table-cell; margin: 0; font-size: 0.875rem; }
  51. .lhd-cell-title { font-weight: bold; padding: 0 5px 0 0; text-align: right; }
  52. .lhd-cell-value { padding: 0 0 0 5px; text-align: left; }
  53. </style>`;
  54.  
  55. $('head').append(style);
  56.  
  57. wkof.include('Apiv2');
  58. wkof.ready('Apiv2').then(fetchData);
  59.  
  60. function fetchData() {
  61. let promises = [];
  62. promises.push(wkof.Apiv2.get_endpoint('user'));
  63. promises.push(wkof.Apiv2.get_endpoint('summary'));
  64. promises.push(wkof.Apiv2.get_endpoint('subjects'));
  65.  
  66. Promise.all(promises).then(processData);
  67. }
  68.  
  69. function processData(results) {
  70. let lessonCounts = getLessonCount(results);
  71. setupMenuPopover(lessonCounts);
  72. setupDashboardPopover(lessonCounts);
  73. }
  74.  
  75. function getLessonCount(results) {
  76. let currentLevel = results[0].level;
  77. let summary = results[1];
  78. let subjects = results[2];
  79.  
  80. let lessonCounts = {
  81. radical: 0,
  82. kanji: 0,
  83. vocabulary: 0,
  84. kana_vocabulary: 0,
  85. currentLevel : {
  86. radical: 0,
  87. kanji: 0,
  88. vocabulary: 0,
  89. kana_vocabulary: 0
  90. }
  91. };
  92.  
  93. // Pull the list of subject_ids from the lesson list in 'summary'.
  94. let lessonSubjectIds = summary.lessons[0].subject_ids;
  95. lessonSubjectIds.forEach(function(subjectId) {
  96. let item = subjects[subjectId];
  97. lessonCounts[item.object]++;
  98.  
  99. if (item.data.level === currentLevel) {
  100. lessonCounts.currentLevel[item.object]++;
  101. }
  102. });
  103.  
  104. return lessonCounts;
  105. }
  106.  
  107. function setupMenuPopover(lessonCounts) {
  108. let lessonMenuItem = $(lessonMenuItemSelector);
  109. if (lessonMenuItem.length === 0) {
  110. return;
  111. }
  112.  
  113. lessonMenuItem.attr('data-content', getPopoverHtml(lessonCounts)).popover(popoverConfig);
  114. }
  115.  
  116. function setupDashboardPopover(lessonCounts) {
  117. let lessonDashboardItem = $(lessonDashboardItemSelector);
  118. if (lessonDashboardItem.length === 0) {
  119. return;
  120. }
  121.  
  122. lessonDashboardItem.attr('data-content', getPopoverHtml(lessonCounts)).popover(popoverConfig);
  123. }
  124.  
  125. function getPopoverHtml(lessonCounts) {
  126. return `<div class="lhd-table">
  127. ${getPopoverSectionHtml(lessonCounts, 'Radicals', 'radical')}
  128. ${getPopoverSectionHtml(lessonCounts, 'Kanji', 'kanji')}
  129. ${getPopoverSectionHtml(lessonCounts, 'Vocab', 'vocabulary')}
  130. ${getPopoverSectionHtml(lessonCounts, 'Kana Vocab', 'kana_vocabulary')}
  131. </div>`;
  132. }
  133.  
  134. function getPopoverSectionHtml(lessonCounts, sectionHeader, sectionKey) {
  135. let rowOpen = '<div class="lhd-row">';
  136.  
  137. let headerCell = `<div class="lhd-cell lhd-cell-title">${sectionHeader}</div>`;
  138. let lessonCountCell = `<div class="lhd-cell lhd-cell-value">${lessonCounts[sectionKey]}</div>`;
  139.  
  140. let lessonLevelCountCell = '';
  141.  
  142. if (levelSetting === levelSettingOptions.CurrentLevel) {
  143. lessonLevelCountCell = `<div class="lhd-cell lhd-cell-value">(${lessonCounts.currentLevel[sectionKey]} current level)</div>`;
  144. }
  145. else if (levelSetting === levelSettingOptions.PriorLevels) {
  146. lessonLevelCountCell = `<div class="lhd-cell lhd-cell-value">(${lessonCounts[sectionKey] - lessonCounts.currentLevel[sectionKey]} prior levels)</div>`;
  147. }
  148.  
  149. let rowClose = '</div>';
  150.  
  151. return `${rowOpen}${headerCell}${lessonCountCell}${lessonLevelCountCell}${rowClose}`;
  152. }
  153. })(window.wkof, window.jQuery);

QingJ © 2025

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