您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Limits the amount of lessons you can do with your current review queue.
// ==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 */