您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Calculates the percentage of known kanji for each JLPT level, Joyo grade, Frequency bracket, and various other sources.
// ==UserScript== // @name Wanikani: Progress Percentages // @namespace http://tampermonkey.net/ // @version 1.2.8 // @description Calculates the percentage of known kanji for each JLPT level, Joyo grade, Frequency bracket, and various other sources. // @author Kumirei // @include /^https://(www|preview).wanikani.com/(dashboard)?$/ // @require https://greasyfork.org/scripts/377613-wanikani-open-framework-jlpt-joyo-and-frequency-filters/code/Wanikani%20Open%20Framework%20JLPT,%20Joyo,%20and%20Frequency%20filters.user.js // @license MIT; http://opensource.org/licenses/MIT // @grant none // ==/UserScript== ;(function () { // Make sure WKOF is installed var wkof = window.wkof if (!wkof) { var response = confirm( 'Wanikani: JLPT Progress requires WaniKani Open Framework.\n Click "OK" to be forwarded to installation instructions.', ) if (response) window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549' return } else { // Install menu wkof.include('Menu,Settings') wkof.ready('Menu,Settings').then(load_settings).then(install_menu) // Initiate progress variable var progress = { jlpt: { 1: { learned: 0, total: 1232 }, 2: { learned: 0, total: 367 }, 3: { learned: 0, total: 367 }, 4: { learned: 0, total: 166 }, 5: { learned: 0, total: 79 }, }, joyo: { 1: { learned: 0, total: 80 }, 2: { learned: 0, total: 160 }, 3: { learned: 0, total: 200 }, 4: { learned: 0, total: 200 }, 5: { learned: 0, total: 185 }, 6: { learned: 0, total: 181 }, 9: { learned: 0, total: 1130 }, }, freq: { 500: { learned: 0, total: 500 }, 1000: { learned: 0, total: 500 }, 1500: { learned: 0, total: 500 }, 2000: { learned: 0, total: 500 }, 2500: { learned: 0, total: 500 }, }, other: { nhk: { learned: 0, total: 0 }, news: { learned: 0, total: 0 }, aozora: { learned: 0, total: 0 }, twitter: { learned: 0, total: 0 }, wikipedia: { learned: 0, total: 0 }, }, } // Fetch lesson info then process it wkof.include('ItemData') wkof.ready('ItemData').then(update_progress).then(calculate_percentages).then(display_data) } // Loads settings function load_settings() { var defaults = { cumulative: false, threshold: 1, position: 'top' } wkof.Settings.load('progress_percentages', defaults) } // Installs the options button in the menu function install_menu() { var config = { name: 'progress_percentages_settings', submenu: 'Settings', title: 'Progress Percentages', on_click: open_settings, } wkof.Menu.insert_script_link(config) } // Creates the options function open_settings(items) { var config = { script_id: 'progress_percentages', title: 'Progress Percentages', content: { cumulative: { type: 'checkbox', label: 'Cumulative percentages', hover_tip: 'Eg. N3 = N3 + N4 + N4', default: false, }, threshold: { type: 'list', label: 'Learned threshold', hover_tip: 'Items at or above this SRS level will be counted as learned', multi: false, size: 9, default: '1', content: { 1: 'Apprentice 1', 2: 'Apprentice 2', 3: 'Apprentice 3', 4: 'Apprentice 4', 5: 'Guru 1', 6: 'Guru 2', 7: 'Master', 8: 'Enlightened', 9: 'Burned', }, }, position: { type: 'dropdown', label: 'Position', hover_tip: 'Position of the Progress Percentages element on the dashboard', default: 'search', content: { top: 'Top of page', below_srs: 'Below SRS boxes', }, on_change: (setting, value) => { let elem = $('.progress_percentages') elem.toggleClass('span12', value == 'top') if (value == 'top') $('#search-form').before(elem) if (value == 'below_srs') $('.srs-progress').append(elem) }, }, }, on_save: () => { progress = { jlpt: { 1: { learned: 0, total: 1232 }, 2: { learned: 0, total: 367 }, 3: { learned: 0, total: 367 }, 4: { learned: 0, total: 166 }, 5: { learned: 0, total: 79 }, }, joyo: { 1: { learned: 0, total: 80 }, 2: { learned: 0, total: 160 }, 3: { learned: 0, total: 200 }, 4: { learned: 0, total: 200 }, 5: { learned: 0, total: 185 }, 6: { learned: 0, total: 181 }, 9: { learned: 0, total: 1130 }, }, freq: { 500: { learned: 0, total: 500 }, 1000: { learned: 0, total: 500 }, 1500: { learned: 0, total: 500 }, 2000: { learned: 0, total: 500 }, 2500: { learned: 0, total: 500 }, }, other: { nhk: { learned: 0, total: 0 }, news: { learned: 0, total: 0 }, aozora: { learned: 0, total: 0 }, twitter: { learned: 0, total: 0 }, wikipedia: { learned: 0, total: 0 }, }, } update_progress().then(calculate_percentages).then(update_element) }, } var dialog = new wkof.Settings(config) dialog.open() } // Updates element function update_element(percentages) { for (var key in percentages) { for (var level in percentages[key]) { var stage = key == 'jlpt' ? 6 - level : level var elem = $('#' + key + '_' + stage)[0] elem.title = percentages[key][stage].learned + (key != 'other' ? ' of ' + percentages[key][stage].total : '') + ' learned' elem.children[1].innerText = percentages[key][stage].percent + '%' } } } // Retreives lesson data function update_progress() { var resolve, promise = new Promise((res, rej) => { resolve = res }) var config = { wk_items: { options: { assignments: true }, filters: { item_type: 'kan', include_jlpt_data: true, include_joyo_data: true, include_frequency_data: true, }, }, } wkof.ItemData.get_items(config).then((data) => { for (var key in data) { if (data[key].assignments && data[key].assignments.started_at != null) { var keys = [ ['jlpt_level', 'jlpt'], ['joyo_grade', 'joyo'], ['frequency', 'freq'], ['nhk_frequency', 'nhk'], ['news_frequency', 'news'], ['aozora_frequency', 'aozora'], ['twitter_frequency', 'twitter'], ['wikipedia_frequency', 'wikipedia'], ] keys.forEach((val, i) => { var level = data[key][val[0]] if (level && data[key].assignments.srs_stage >= wkof.settings.progress_percentages.threshold) { if (level < 1) { progress.other[val[1]].learned++ progress.other[val[1]].total += level } else progress[val[1]][level].learned++ } }) } } resolve() }) return promise } function calculate_percentages() { var show_cum = wkof.settings.progress_percentages.cumulative var percentages = { jlpt: { 1: {}, 2: {}, 3: {}, 4: {}, 5: {} }, joyo: { 1: {}, 2: {}, 3: {}, 4: {}, 5: {}, 6: {}, 9: {} }, freq: { 500: {}, 1000: {}, 1500: {}, 2000: {}, 2500: {} }, other: { nhk: {}, news: {}, aozora: {}, twitter: {}, wikipedia: {} }, } for (var key in percentages) { var cumulative = [0, 0] for (var level in percentages[key]) { var stage = key == 'jlpt' ? 6 - level : level var learned = progress[key][stage].learned var total = progress[key][stage].total cumulative[0] += learned cumulative[1] += total let percent if (key != 'other') percent = show_cum ? cumulative[0] / cumulative[1] : learned / total else percent = total percent = percent < 0.1 ? Math.floor(percent * 1000) / 10 : Math.floor(percent * 100) percentages[key][stage].percent = percent percentages[key][stage].learned = show_cum ? cumulative[0] : learned percentages[key][stage].total = show_cum ? cumulative[1] : total } } return percentages } function display_data(percentages) { // Add css $('head').append(`<style id="progress_percentages"> .progress_percentages { display: flex; height: 28px; background: #434343; color: rgb(240, 240, 240); line-height: 28px; //margin-bottom: 0; border-radius: 5px; text-align: center; grid-row: 1; grid-column: 1 / span 2; //margin-top: 15px; } .srs-progress .progress_percentages { margin-top: 5px; } #search-form { grid-row: 1; } .progress_percentages .PPprogress { display: flex; width: 100%; justify-content: space-around; } .progress_percentages .PPbtn { width: 20px; height: auto; color: rgb(240,240,240); padding: 0 5px; cursor: pointer; font-size: 16px; } .progress_percentages .level { font-weight: bold; } .progress_percentages .percent { font-weight: normal !important; } .progress_percentages span { font-size: 16px !important; display: inline !important; } </style>`) if (is_dark_theme()) { $('head').append(`<style id="progress_percentages_dark"> .progress_percentages { box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7), 2px 2px 2px rgba(0, 0, 0, 0.7); } .progress_percentages > div { background: #232629 !important; } </style>`) } // Add elements var section = document.createElement('section') section.className = 'progress_percentages' var active_set = localStorage.getItem('WKProgressPercentagesActiveSet') || 'jlpt' var [next, prev] = get_new_sets(active_set) var next_button = document.createElement('div') next_button.className = 'next PPbtn' next_button.innerHTML = '<i class="link icon-chevron-right"></i>' next_button.onclick = toggle_percentages next_button.current = active_set next_button.next = next var prev_button = document.createElement('div') prev_button.className = 'prev PPbtn' prev_button.innerHTML = '<i class="link icon-chevron-left"></i>' prev_button.onclick = toggle_percentages prev_button.current = active_set prev_button.next = prev var list = document.createElement('div') list.className = 'PPprogress' for (var key in percentages) { for (var level in percentages[key]) { var stage = key == 'jlpt' ? 6 - level : level var prefix = key == 'jlpt' ? 'N' : key == 'joyo' ? 'G' : key == 'freq' ? 'F' : '' var label = key == 'other' ? stage == 'nhk' ? 'NHK' : stage.charAt(0).toUpperCase() + stage.slice(1) : key == 'freq' ? stage / 500 : stage $(list).append( '<div class="' + key + '_percentages stage ' + (key == active_set ? '' : 'hidden') + '" id="' + key + '_' + stage + '" title="' + percentages[key][stage].learned + (key != 'other' ? ' of ' + percentages[key][stage].total : '') + ' learned"><span class="level">' + prefix + label + ' </span><span class="percent">' + percentages[key][stage].percent + '%</span></div>', ) } } section.appendChild(prev_button) section.appendChild(list) section.appendChild(next_button) if (wkof.settings.progress_percentages.position == 'top') { //section.className += ' span12' $('.progress-and-forecast').before(section) } else if (wkof.settings.progress_percentages.position == 'below_srs') $('.srs-progress').append(section) else $('.progress-and-forecast').before(section) } // Switches which percentages are showing function toggle_percentages(event) { var button = event.target if (button.nodeName == 'I') button = button.parentElement var current_set = button.current var next_set = button.next $('.' + current_set + '_percentages').toggleClass('hidden') $('.' + next_set + '_percentages').toggleClass('hidden') var next_button = $('.progress_percentages .next')[0] var prev_button = $('.progress_percentages .prev')[0] var [next, prev] = get_new_sets(next_set) next_button.next = next next_button.current = next_set prev_button.next = prev prev_button.current = next_set localStorage.setItem('WKProgressPercentagesActiveSet', next_set) } // Returns the next and previous sets function get_new_sets(current_set) { var sets = ['jlpt', 'joyo', 'freq', 'other'] var current_index = sets.indexOf(current_set) return [sets[(current_index + 1) % 4], sets[(current_index + 3) % 4]] } // Handy little function that rfindley wrote. Checks whether the theme is dark. function is_dark_theme() { // Grab the <html> background color, average the RGB. If less than 50% bright, it's dark theme. return ( $('body') .css('background-color') .match(/\((.*)\)/)[1] .split(',') .slice(0, 3) .map((str) => Number(str)) .reduce((a, i) => a + i) / (255 * 3) < 0.5 ) } })()