WaniKani Lesson Cap

Limits the amount of lessons you can do with your current review queue.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         WaniKani Lesson Cap
// @namespace    https://github.com/valeth
// @version      0.6.4
// @description  Limits the amount of lessons you can do with your current review queue.
// @author       valeth
// @match        https://www.wanikani.com/dashboard
// @match        https://www.wanikani.com/lesson
// @match        https://www.wanikani.com/lesson/session
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==

/* jshint ignore:start */

const debugging = false;
// Generated by CoffeeScript 2.0.2
(function() {
  // =======[ WKOF Guard ]=======

  // coffeelint: disable=max_line_length
  var btn_continue, calculate_score, confirmed, continue_button, default_settings, get_items, get_or_wait, install_menu, install_settings, log, render_all, render_available, render_results, round2, settings, settings_dialog, srs_levels, start_button, titleize, update;

  if (window.wkof == null) {
    confirmed = confirm('WaniKani Lesson Cap requires the WaniKani Open Framework.\nDo you want to be forwarded to the installation instructions?');
    if (confirmed) {
      window.location.href = "https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549";
    }
    return;
  }

  // coffeelint: enable=max_line_length

  // =======[ Globals ]=======
  btn_continue = void 0;

  settings_dialog = void 0;

  default_settings = {
    max_score: 100
  };

  srs_levels = [
    {
      stage: 1,
      rating: 6,
      ident: 'apprentice1'
    },
    {
      stage: 2,
      rating: 3,
      ident: 'apprentice2'
    },
    {
      stage: 3,
      rating: 1.04,
      ident: 'apprentice3'
    },
    {
      stage: 4,
      rating: 0.51,
      ident: 'apprentice4'
    },
    {
      stage: 5,
      rating: 0.14,
      ident: 'guru1'
    },
    {
      stage: 6,
      rating: 0.07,
      ident: 'guru2'
    },
    {
      stage: 7,
      rating: 0.03,
      ident: 'master'
    },
    {
      stage: 8,
      rating: 0.01,
      ident: 'enlightened'
    }
  ];

  // =======[ Utility functions ]=======
  log = {
    info: function(message) {
      return console.log("[WKLC] " + message);
    },
    error: function(message) {
      return console.error("[WKLC] " + message);
    },
    debug: function(message) {
      if (debugging) {
        return console.log("[WKLC] " + message);
      }
    },
    inspect: function(thing) {
      if (debugging) {
        return console.debug(thing);
      }
    }
  };

  // Format a given string into "titleized" format.
  // Example:
  //   someIdentifier1 -> Some Identifier 1
  titleize = function(string) {
    var char, i, len, ref, tmp;
    tmp = string[0].toUpperCase();
    ref = string.slice(1);
    for (i = 0, len = ref.length; i < len; i++) {
      char = ref[i];
      if (char === char.toUpperCase()) {
        tmp += ` ${char}`;
      } else {
        tmp += char;
      }
    }
    return tmp;
  };

  round2 = function(num) {
    return Math.round(num * 100) / 100;
  };

  get_or_wait = function(key, callback) {
    var value;
    value = wkof.get_state(key);
    if (value != null) {
      return callback(value);
    }
    return wkof.wait_state(key, "*", function(value, _) {
      return callback(value);
    });
  };

  // =======[ Rendering ]=======
  start_button = function(action) {
    var btn_start, lesson_queue_count;
    btn_start = $("#start-session > a");
    if (btn_start[0] == null) {
      return;
    }
    lesson_queue_count = $("#lesson-queue-count");
    switch (action) {
      case "disable":
        log.info("Disabling lesson start button...");
        btn_start.addClass("wklc-force-hidden");
        return lesson_queue_count.addClass("wklc-lesson-count");
      case "enable":
        log.info("Enabling lesson start button...");
        btn_start.removeClass("wklc-force-hidden");
        return lesson_queue_count.removeClass("wklc-lesson-count");
    }
  };

  continue_button = function(action) {
    if (btn_continue == null) {
      btn_continue = $("#lesson-ready-continue");
    }
    if (btn_continue[0] == null) {
      return;
    }
    switch (action) {
      case "disable":
        log.info("Disabling lesson continue button...");
        return btn_continue.remove();
      case "enable":
        log.info("Enabling lesson continue button...");
        return $("#screen-lesson-ready .btn-set").append(btn_continue);
    }
  };

  render_all = function() {
    return get_items(function(score, stats) {
      var batch_size, batches, max_score, remaining_score, score_per_item;
      render_results(score, stats);
      batch_size = $.jStorage.get("l/batchSize");
      max_score = settings("max_score");
      remaining_score = max_score - score;
      score_per_item = batch_size * (srs_levels[0].rating / 2);
      batches = Math.floor(remaining_score / score_per_item);
      if (batches >= 1) {
        start_button("enable");
      }
      if (batches >= 2) {
        continue_button("enable");
      }
      return render_available(batch_size, batches);
    });
  };

  render_results = function(score, stats) {
    var max_score, wklc, wklc_items;
    if ($("#lessons-summary")[0] == null) {
      return;
    }
    log.debug("Rendering results...");
    wklc = $(".wklc");
    if (wklc[0] != null) {
      wklc.remove();
    }
    max_score = settings("max_score");
    wklc_items = stats.reduce(function(acc, stat, index) {
      return acc += `<li class='wklc-item wklc-${srs_levels[index].ident}'\n    title='${titleize(srs_levels[index].ident)}'>\n  <span>${round2(stat.count)}</span>\n  <span>(${round2(stat.score)})</span>\n</li>`;
    }, "");
    return $("#lessons-summary").prepend($(`<div class="wklc">\n  <ul class="wklc-items">${wklc_items}</ul>\n  <div class='wklc-right'>\n    <div class='wklc-item wklc-score'>\n      <span>Score:</span>\n      <span>${round2(score)}</span> / <span>${max_score}</span>\n    </div>\n    <button class='wklc-item wklc-btn-settings'\n            title='Settings'>⚙</button>\n  </div>\n</div>`));
  };

  render_available = function(batch_size, batches) {
    var allowed_lessons, lessons_queue_count, title;
    if ($("#lessons-summary")[0] == null) {
      return;
    }
    allowed_lessons = $(".wklc-allowed-lessons");
    if (allowed_lessons[0] != null) {
      allowed_lessons.remove();
    }
    lessons_queue_count = +$("#lesson-queue-count").text();
    if (lessons_queue_count === 0 && !debugging) {
      return;
    }
    title = `This is the amount of batches of ${batch_size}\nlessons you can do without exceeding the max score.`;
    allowed_lessons = $(`<span class='wklc-allowed-lessons' title='${title}'>\n  ${batches + ' Available'}\n</span>`);
    if (batches < 1) {
      allowed_lessons.addClass("disabled");
      allowed_lessons.attr("title", "Lessons button disabled because you can do\nless than one session before exceeding max score.");
    }
    return $("#start-session").append(allowed_lessons);
  };

  // ======[ API Requests and Calculations ]=======
  get_items = function(callback) {
    return wkof.ItemData.get_items({
      wk_items: {
        options: {
          assignments: true
        },
        filters: {
          srs: {
            value: ["lock", "burn"],
            invert: true
          },
          level: "1 to +0" // from first level to current
        }
      }
    }).then(function(items) {
      var score, stats;
      [score, stats] = calculate_score(items);
      return callback(score, stats);
    });
  };

  calculate_score = function(items) {
    var stats, total_score;
    stats = srs_levels.map(function() {
      return {
        count: 0,
        score: 0.0
      };
    });
    items.forEach(function(cur) {
      var stage;
      stage = cur.assignments.srs_stage - 1;
      if (stage < 0 || stage > 7) {
        return;
      }
      stats[stage].count += 1;
      return stats[stage].score = (stats[stage].count * srs_levels[stage].rating) / 2;
    });
    wkof.set_state("wklc.stats", stats);
    total_score = stats.reduce(function(acc, cur) {
      return acc += round2(cur.score);
    }, 0);
    wkof.set_state("wklc.current_score", total_score);
    return [total_score, stats];
  };

  // =======[ Setup ]=======
  update = function() {
    // need to disable buttons on every update again
    start_button("disable");
    continue_button("disable");
    return render_all();
  };

  install_menu = function() {
    var open_settings;
    open_settings = function() {
      if (settings_dialog != null) {
        return settings_dialog.open();
      }
    };
    wkof.Menu.insert_script_link({
      script_id: "wklc",
      submenu: "Settings",
      title: "WaniKani Lesson Cap",
      on_click: open_settings
    });
    return $(document).on("click", ".wklc-btn-settings", open_settings);
  };

  settings = function(key) {
    return wkof.settings.wklc[key];
  };

  install_settings = function() {
    var save_settings;
    save_settings = function() {
      wkof.Settings.save("wklc");
      update();
      return log.info("Settings saved");
    };
    wkof.Settings.load("wklc", default_settings);
    return settings_dialog = new wkof.Settings({
      script_id: "wklc",
      title: "WaniKani Lesson Cap",
      on_save: save_settings,
      settings: {
        pg_general: {
          type: "page",
          label: "General",
          content: {
            max_score: {
              type: "number",
              label: "Max score"
            }
          }
        }
      }
    });
  };

  $(document).on("click", "#batch-items .active-quiz", update);

  $(document).on("click", "#quiz-ready-continue", update);

  $(document).ready(function() {
    var modules;
    modules = "Apiv2, ItemData, Menu, Settings";
    // coffeelint: disable=max_line_length
    wkof.load_css("https://raw.githubusercontent.com/valeth/wanikani-lesson-cap/wkof/compiled/style.css");
    // coffeelint: enable=max_line_length
    wkof.include(modules);
    wkof.ready("Menu").then(install_menu);
    wkof.ready("Settings").then(install_settings);
    return wkof.ready(modules).then(update);
  });

}).call(this);


/* jshint ignore:end */