Wanikani: Progress Percentages

Calculates the percentage of known kanji for each JLPT level, Joyo grade, Frequency bracket, and various other sources.

当前为 2020-03-30 提交的版本,查看 最新版本

// ==UserScript==
// @name         Wanikani: Progress Percentages
// @namespace    http://tampermonkey.net/
// @version      1.2.0
// @description  Calculates the percentage of known kanji for each JLPT level, Joyo grade, Frequency bracket, and various other sources.
// @author       Kumirei
// @match        https://www.wanikani.com/
// @match        https://www.wanikani.com/dashboard
// @include      *preview.wanikani.com*
// @require      https://gf.qytechs.cn/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: 'search',
                       }
		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: {
                        search: 'Left of search',
                        below_srs: 'Below SRS boxes',
                    },
                    on_change: (setting, value)=>{
                        let elem = $('.progress_percentages');
                        if (value=="search") $('#search-form').before(elem);
                        if (value=="below_srs") $('.srs-progress').after(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;
				if (key != "other") var 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;
                              }
                              #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;
                              }
                              .progress_percentages .level {
                                  font-weight: bold;
                              }
                              .progress_percentages span {
                                  font-size: 16px;
                              }
                              #search > div > div > div {
                                  display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 20px 20px;
                              }
                              #search-form {
                                  grid-column: 3;
                                  width: 100% !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 == "search") $('#search-form').before(section);
        else $(".srs-progress").after(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;
	}
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址