🏠 Home 

Wanikani Open Framework - Progress module

Progress module for Wanikani Open Framework

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/38577/1091792/Wanikani%20Open%20Framework%20-%20Progress%20module.js

  1. // ==UserScript==
  2. // @name Wanikani Open Framework - Progress module
  3. // @namespace rfindley
  4. // @description Progress module for Wanikani Open Framework
  5. // @version 1.0.11
  6. // @copyright 2022+, Robin Findley
  7. // @license MIT; http://opensource.org/licenses/MIT
  8. // ==/UserScript==
  9. (function(global) {
  10. //########################################################################
  11. //------------------------------
  12. // Published interface
  13. //------------------------------
  14. global.wkof.Progress = {
  15. update: update_progress,
  16. popup_delay: get_or_set_popup_delay,
  17. }
  18. //########################################################################
  19. const default_popup_delay = 2500; // Delay before popup will open (in milliseconds).
  20. var popup_delay = default_popup_delay;
  21. var show_popup = true;
  22. var popup_delay_started = false, popup_delay_expired = false, popup_timer;
  23. var externals_requested = false, externals_loaded = false;
  24. var progress_bars = {};
  25. var user_closed = false;
  26. var dialog_visible = false, dialog;
  27. //------------------------------
  28. // Set the delay before the progress dialog pops up.
  29. //------------------------------
  30. function get_or_set_popup_delay(delay, silent) {
  31. if (typeof delay !== 'undefined' && delay !== null) {
  32. if (delay === 'default') delay = default_popup_delay;
  33. delay = Number(delay);
  34. if (Number.isNaN(delay)) throw 'Invalid value for popup_delay';
  35. show_popup = (delay >= 0);
  36. localStorage.setItem('wkof.Progress.popup_delay', delay);
  37. popup_delay = delay;
  38. }
  39. if (silent !== true) console.log('popup_delay ' + (show_popup ? ('= ' + popup_delay) : 'is disabled'));
  40. }
  41. //------------------------------
  42. // Update the progress bar.
  43. //------------------------------
  44. function update_progress(data) {
  45. if (data) update_data(data);
  46. if (!dialog_visible && !have_pending()) return shutdown();
  47. // We have something pending, but don't show dialog until popup_delay has passed.
  48. if (!popup_delay_started) return start_popup_delay();
  49. // Popup delay has passed. Show progress.
  50. if (!popup_delay_expired) return;
  51. update_dialog();
  52. }
  53. //------------------------------
  54. // Update our stored progress bar status
  55. //------------------------------
  56. function update_data(data) {
  57. var bar = progress_bars[data.name];
  58. if (!bar) progress_bars[data.name] = bar = {label: data.label};
  59. bar.is_updated = true;
  60. bar.value = data.value;
  61. bar.max = data.max;
  62. if (bar.max === 0) {
  63. bar.value = 1;
  64. bar.max = 1;
  65. }
  66. // Don't retain items that complete before the dialog pops up.
  67. if (!popup_delay_expired && (bar.value >= bar.max)) delete progress_bars[data.name];
  68. }
  69. //------------------------------
  70. // Check if some progress is still pending.
  71. //------------------------------
  72. function have_pending() {
  73. var all_done = true;
  74. for (name in progress_bars) {
  75. var progress_bar = progress_bars[name];
  76. if (progress_bar.value < progress_bar.max) all_done = false;
  77. }
  78. return !all_done;
  79. }
  80. //------------------------------
  81. // Delay the dialog from popping up until progress takes at least N milliseconds.
  82. //------------------------------
  83. function start_popup_delay() {
  84. get_or_set_popup_delay(localStorage.getItem('wkof.Progress.popup_delay'), true /* silent */);
  85. if (!show_popup) return;
  86. popup_delay_started = true;
  87. popup_timer = setTimeout(function() {
  88. popup_delay_expired = true;
  89. update_progress();
  90. }, popup_delay);
  91. }
  92. //------------------------------
  93. // Update the contents of the progress dialog (if it's currently visible)
  94. //------------------------------
  95. function update_dialog() {
  96. if (!externals_requested) {
  97. externals_requested = true;
  98. load_externals()
  99. .then(function() {
  100. externals_loaded = true;
  101. update_progress();
  102. });
  103. return;
  104. }
  105. if (!externals_loaded) return;
  106. if (user_closed) return;
  107. if (!dialog_visible) {
  108. dialog_visible = true;
  109. if (!document.querySelector('#wkof_ds')) {
  110. let ds = document.createElement('div');
  111. ds.setAttribute('id', 'wkof_ds');
  112. document.body.prepend(ds);
  113. }
  114. dialog = $('<div id="wkof_progbar_dlg" class="wkofs_progress_dlg" style="display:none;"></div>');
  115. dialog.dialog({
  116. title: 'Loading Data...',
  117. minHeight: 20,
  118. maxHeight: window.innerHeight,
  119. height: 'auto',
  120. dialogClass: 'wkof_progbar_dlg',
  121. modal: false,
  122. resizable: false,
  123. autoOpen: false,
  124. appendTo: '#wkof_ds',
  125. close: dialog_close
  126. });
  127. dialog.dialog('open');
  128. }
  129. var all_done = true;
  130. for (name in progress_bars) {
  131. var progress_bar = progress_bars[name];
  132. if (progress_bar.value < progress_bar.max) all_done = false;
  133. var bar = $('#wkof_progbar_dlg .wkof_progbar_wrap[name="'+name+'"]');
  134. if (bar.length === 0) {
  135. bar = $('<div class="wkof_progbar_wrap" name="'+name+'"><label>'+progress_bar.label+'</label><div class="wkof_progbar"></div></div>');
  136. var bars = $('#wkof_progbar_dlg .wkof_progbar_wrap');
  137. bars.push(bar[0]);
  138. $('#wkof_progbar_dlg').append(bars.sort(bar_label_compare));
  139. }
  140. if (progress_bar.is_updated) {
  141. progress_bar.is_updated = false;
  142. bar.find('.wkof_progbar').progressbar({value: progress_bar.value, max: progress_bar.max});
  143. }
  144. }
  145. if (all_done) shutdown();
  146. }
  147. function dialog_close() {
  148. dialog.dialog('destroy');
  149. dialog_visible = false;
  150. user_closed = true;
  151. }
  152. //------------------------------
  153. // Load external support files (jquery UI and stylesheet)
  154. //------------------------------
  155. function load_externals() {
  156. var css_url = wkof.support_files['jqui_wkmain.css'];
  157. wkof.include('Jquery');
  158. return wkof.ready('document, Jquery')
  159. .then(function(){
  160. return Promise.all([
  161. wkof.load_script(wkof.support_files['jquery_ui.js'], true /* cache */),
  162. wkof.load_css(css_url, true /* cache */)
  163. ]);
  164. })
  165. .then(function(){
  166. // Workaround... https://community.wanikani.com/t/19984/55
  167. delete $.fn.autocomplete;
  168. });
  169. }
  170. //------------------------------
  171. // Comparison function for sorting progress bars.
  172. //------------------------------
  173. function bar_label_compare(a, b) {
  174. var a = $(a).find('label').text();
  175. var b = $(b).find('label').text();
  176. return a.localeCompare(b);
  177. }
  178. //------------------------------
  179. // Shut down the dialog box and cancel the popup delay timer.
  180. //------------------------------
  181. function shutdown() {
  182. // If popup timer was pending, cancel it.
  183. if (popup_delay_started && !popup_delay_expired) clearTimeout(popup_timer);
  184. popup_delay_started = false;
  185. popup_delay_expired = false;
  186. // If progress dialog is open, close it.
  187. if (dialog_visible) dialog.dialog('close');
  188. user_closed = false;
  189. progress_bars = {};
  190. }
  191. function set_ready_state() {
  192. // Delay guarantees include() callbacks are called before ready() callbacks.
  193. setTimeout(function(){wkof.set_state('wkof.Progress', 'ready');}, 0);
  194. }
  195. set_ready_state();
  196. })(window);