Wanikani: Back to back

Makes reading and meaning appear back to back in reviews and lessons

当前为 2022-04-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Wanikani: Back to back
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.2.3
  5. // @description Makes reading and meaning appear back to back in reviews and lessons
  6. // @author Kumirei
  7. // @include /^https://(www|preview).wanikani.com/(lesson|review|extra_study)/session/
  8. // @license MIT
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. ;(function (wkof, $) {
  13. // Page related info
  14. let currentItemKey, questionTypeKey, UIDPrefix, traceFunctionName
  15. let REVIEWS, LESSONS, EXTRA_STUDY
  16. if (/REVIEW/i.test(location.pathname)) {
  17. REVIEWS = true
  18. currentItemKey = 'currentItem'
  19. questionTypeKey = 'questionType'
  20. UIDPrefix = ''
  21. traceFunctionName = /randomQuestion/
  22. } else if (/LESSON/i.test(location.pathname)) {
  23. LESSONS = true
  24. currentItemKey = 'l/currentQuizItem'
  25. questionTypeKey = 'l/questionType'
  26. UIDPrefix = 'l/stats/'
  27. traceFunctionName = /selectItem/
  28. } else if (/EXTRA_STUDY/i.test(location.pathname)) {
  29. EXTRA_STUDY = true
  30. currentItemKey = 'currentItem'
  31. questionTypeKey = 'questionType'
  32. UIDPrefix = 'e/stats/'
  33. traceFunctionName = /selectQuestion/
  34. }
  35.  
  36. // Script info
  37. const script_name = 'Back 2 Back'
  38. const script_id = 'back2back'
  39.  
  40. // Make sure WKOF is installed
  41. confirm_wkof(script_name).then(start)
  42.  
  43. // Startup
  44. function start() {
  45. wkof.include('Menu,Settings')
  46. wkof.ready('Menu,Settings').then(load_settings).then(install)
  47. }
  48.  
  49. // Installs script functions on page
  50. function install() {
  51. install_menu()
  52. install_back2back()
  53. install_prioritization()
  54.  
  55. console.log(
  56. 'Beware, "Back To Back" is installed and may cause other scripts using Math.random ' +
  57. `in a function called ${traceFunctionName} to misbehave.`,
  58. )
  59. }
  60.  
  61. // Set up back to back meaning/reading reviews
  62. function install_back2back() {
  63. // Wrap jStorage.set(key, value) to ignore the value when the key is for the current item AND one item has
  64. // already been partially answered. If an item has been partially answered, then set the current item to
  65. // that item instead.
  66. const original_set = $.jStorage.set
  67. const new_set = function (key, value, options) {
  68. if (key === currentItemKey && wkof.settings[script_id].active) {
  69. const active_queue = $.jStorage.get(active_queue_key, [])
  70. for (const item of active_queue) {
  71. const UID = (item.type == 'Kanji' ? 'k' : 'v') + item.id
  72. const stats = $.jStorage.get(UIDPrefix + UID)
  73. // Change the item if it has been answered in the session, regardless of whether the answer
  74. // was correct.
  75. if (stats) {
  76. if (stats.mc) $.jStorage.set(questionTypeKey, 'reading')
  77. if (stats.rc) $.jStorage.set(questionTypeKey, 'meaning')
  78. return original_set.call(this, key, item, options)
  79. }
  80. }
  81. }
  82. return original_set.call(this, key, value, options)
  83. }
  84. $.jStorage.set = new_set
  85. }
  86.  
  87. // Set up prioritization of reading or meaning
  88. function install_prioritization() {
  89. // Run every time item changes
  90. $.jStorage.listenKeyChange(currentItemKey, prioritize)
  91. // Initialize session to prioritized question type
  92. prioritize()
  93. }
  94.  
  95. // Prioritize reading or meaning
  96. function prioritize() {
  97. const prio = wkof.settings[script_id].prioritize
  98. const item = $.jStorage.get(currentItemKey)
  99. // Skip if item is a radical, it is already the right question, or no priority is selected
  100. if (item.type == 'Radical' || $.jStorage.get(questionTypeKey) == prio || 'none' == prio) return
  101. const UID = (item.type == 'Kanji' ? 'k' : 'v') + item.id
  102. const done = $.jStorage.get(UIDPrefix + UID)
  103. // Change the question if no question has been answered yet,
  104. // Or the priority question has not been answered correctly yet
  105. if (!done || !done[prio == 'reading' ? 'rc' : 'mc']) {
  106. $.jStorage.set(questionTypeKey, prio)
  107. $.jStorage.set(currentItemKey, item)
  108. }
  109. }
  110.  
  111. /* ----------------------------------------------------------*/
  112. // WKOF setup
  113. /* ----------------------------------------------------------*/
  114.  
  115. // Makes sure that WKOF is installed
  116. async function confirm_wkof() {
  117. if (!wkof) {
  118. let response = confirm(
  119. `${script_name} requires WaniKani Open Framework.\nClick "OK" to be forwarded to installation instructions.`,
  120. )
  121. if (response) {
  122. window.location.href =
  123. 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549'
  124. }
  125. return
  126. }
  127. }
  128.  
  129. // Load WKOF settings
  130. function load_settings() {
  131. const defaults = {
  132. prioritize: 'none',
  133. active: true,
  134. }
  135. return wkof.Settings.load(script_id, defaults)
  136. }
  137.  
  138. // Installs the options button in the menu
  139. function install_menu() {
  140. const config = {
  141. name: script_id,
  142. submenu: 'Settings',
  143. title: script_name,
  144. on_click: open_settings,
  145. }
  146. wkof.Menu.insert_script_link(config)
  147. }
  148.  
  149. // Opens settings dialogue when button is pressed
  150. function open_settings() {
  151. let config = {
  152. script_id: script_id,
  153. title: script_name,
  154. on_save: prioritize,
  155. content: {
  156. active: { type: 'checkbox', label: 'Active', default: true },
  157. prioritize: {
  158. type: 'dropdown',
  159. label: 'Prioritize',
  160. default: 'reading',
  161. content: { none: 'None', reading: 'Reading', meaning: 'Meaning' },
  162. },
  163. },
  164. }
  165. let dialog = new wkof.Settings(config)
  166. dialog.open()
  167. }
  168. })(window.wkof, window.jQuery)