Greasy Fork 还支持 简体中文。

Wanikani: Levels by SRS

Displays your level for each SRS stage.

目前為 2020-03-12 提交的版本,檢視 最新版本

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Wanikani: Levels by SRS
// @namespace    http://tampermonkey.net/
// @version      1.1.2
// @description  Displays your level for each SRS stage.
// @author       Kumirei
// @match        https://www.wanikani.com/dashboard
// @match        https://www.wanikani.com
// @include      *preview.wanikani.com*
// @grant        none
// ==/UserScript==

(function() {
		//check that the Wanikani Framework is installed
		var script_name = 'Levels By SRS';
		if (!window.wkof) {
				if (confirm(script_name+' requires Wanikani Open Framework.\nDo you want to be forwarded to the installation instructions?'))
						window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
				return;
		}
		//if it's installed then do the stuffs
		else {
				wkof.include('Menu,Settings,ItemData');
				wkof.ready('Menu,Settings,ItemData')
						.then(load_settings)
						.then(install_menu)
						.then(add_css)
						.then(fetch_and_update);
		}

		// Fetches items and updates display
		function fetch_and_update() {
				fetch_items()
						.then(process_items)
						.then(update_display);
		}

		// Fetches the relevant items
		function fetch_items() {
				var [promise, resolve] = new_promise();
				var config = {
						wk_items: {
								options: {assignments: true},
								filters: {level: "1..+0"}
						}
				};
				wkof.ItemData.get_items(config).then(resolve);
				return promise;
		}

		// Retreives the levels by srs
		function process_items(data) {
				// Sort by level
				var levels = {};
				for (var i=0; i<data.length; i++) {
						var item = data[i];
						var level = item.data.level;
						if (!levels[level]) levels[level] = [];
						levels[level].push(item);
				}
				// Go through items level by level
				var srs_levels = {Ini: [0, 0, 0], App: [0, 0, 0], Gur: [0, 0, 0], Mas: [0, 0, 0], Enl: [0, 0, 0], Bur: [0, 0, 0]};
				for (i=1; i<wkof.user.level+1; i++) {
						// Get counts for level
						var by_srs = {Ini: 0, App: 0, Gur: 0, Mas: 0, Enl: 0, Bur: 0, total: 0};
						for (var j=0; j<levels[i].length; j++) {
								item = levels[i][j];
								if (item.assignments) by_srs[item.assignments.srs_stage_name.slice(0, 3)]++;
								else by_srs['Ini']++;
								by_srs.total++;
						}
						// Check if srs_level should be increased
						var types = ['Ini', 'App', 'Gur', 'Mas', 'Enl', 'Bur'];
						var cumulative = 0;
						for (j=0; j<types.length; j++) {
								var count = by_srs[types[j]];
								if (1-cumulative/by_srs.total >= wkof.settings.levels_by_srs.threshold/100 && i == srs_levels[types[j]][0]+1) {
										srs_levels[types[j]][0]++;
								}
								else if (1-cumulative/by_srs.total <= wkof.settings.levels_by_srs.threshold/100 && i == srs_levels[types[j]][0]+1) {
										srs_levels[types[j]][1] = 1-cumulative/by_srs.total;
										srs_levels[types[j]][2] = by_srs.total;
								}
								cumulative += count;
						}
				}
				return srs_levels;
		}

		// Updates the display element
		function update_display(srs_levels) {
				var types = ['App', 'Gur', 'Mas', 'Enl', 'Bur'];
				// If the element doesn't already exist, create it
				var display = $('#levels_by_srs');
				if (!display.length) {
						display = $('<div id="levels_by_srs"'+(is_dark_theme() ? ' class="dark_theme"' : '')+'></div>');
						for (var i=0; i<types.length; i++) display.append('<div class="'+types[i]+'"><span class="level_label">Level: </span><span class="value"></span></div>');
						$('.srs-progress').append(display);
				}
				// Update
				for (i=0; i<types.length; i++) {
						var elem = $(display).find('.'+types[i]+' span.value')[0];
						elem.innerText = srs_levels[types[i]][0];
						elem.parentElement.setAttribute('title', Math.floor(srs_levels[types[i]][1]*100) + '% to level ' + (srs_levels[types[i]][0]+1) + ' (' + Math.round(srs_levels[types[i]][1] * srs_levels[types[i]][2]) + ' of ' + srs_levels[types[i]][2] + ')');
				}
		}

		// Load stored settings or set defaults
		function load_settings() {
				var defaults = {threshold: 90};
				return wkof.Settings.load('levels_by_srs', defaults);
		}

		// Installs the options button in the menu
		function install_menu() {
				var config = {
						name: 'levels_by_srs',
						submenu: 'Settings',
						title: 'Levels By SRS',
						on_click: open_settings
				};
				wkof.Menu.insert_script_link(config);
		}

		// Create the options
		function open_settings(items) {
				var config = {
						script_id: 'levels_by_srs',
						title: 'Levels By SRS',
						on_save: fetch_and_update,
						content: {
								threshold: {
										type: 'number',
										label: 'Threshold',
										hover_tip: 'Percentage to consider level done',
										default: 90,
								}
						}
				}
				var dialog = new wkof.Settings(config);
				dialog.open();
		}

		// Adds the script's CSS to the page
		function add_css() {
				$('head').append(`<style id="levels_by_srs_CSS">
								  #levels_by_srs {
								      background: #434343;
								      border-radius: 0 0 3px 3px;
								      height: 30px;
								      line-height: 30px;
								      color: rgb(240, 240, 240);
								      display: flex;
								      justify-content: space-around;
								  }
								  #levels_by_srs > div {
								      display: flex;
								  }
								  #levels_by_srs .level_label {
                                      font-size: 16px;
                                      margin-right: 5px;
								  }
                                  #levels_by_srs .value {
                                      font-size: 16px;
								      font-weight: normal;
                                  }
								  #levels_by_srs.dark_theme {
								      background: #232629;
								      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7), 2px 2px 2px rgba(0, 0, 0, 0.7);
								      margin: 0 3px 3px 3px;
								  }
								  #levels_by_srs.dark_theme > div:not(:last-child) {
								      border-right: 1px solid #31363b;
								  }
								  .srs-progress > ul > li {
								      border-radius: 0 !important;
								  }
								  </style>`);
		}

		// Returns a promise and a resolve function
		function new_promise() {
				var resolve, promise = new Promise((res, rej)=>{resolve = res;});
				return [promise, resolve];
		}

		// 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;
		}
})();