WaniKani Open Framework Statistics Filters

Additional statistics filters for the WaniKani Open Framework

当前为 2020-12-10 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name          WaniKani Open Framework Statistics Filters
// @namespace     https://www.wanikani.com
// @description   Additional statistics filters for the WaniKani Open Framework
// @author        prouleau
// @version       1.4.1
// @include       https://www.wanikani.com/*
// @license       MIT; http://opensource.org/licenses/MIT
// @grant         none
// ==/UserScript==

(function(wkof) {
	'use strict';

	var wkofMinimumVersion = '1.0.52';

	if (!wkof) {
		var response = confirm('WaniKani Open Framework Date Filters 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;
	}
	var settingsDialog;
	var settingsScriptId = 'statsFilters';
	var settingsTitle = 'Statistics Filters';

	var needToRegisterFilters = true;

	var filterNamePrefix = 'statsFilters_';
	var leechLteqFilterName = filterNamePrefix + 'LeechLteq';
	var meaningCorAnsLteqFilterName = filterNamePrefix + 'meaningCorAnsLteq';
	var meaningCorAnsGteqFilterName = filterNamePrefix + 'meaningCorAnsGteq';
	var readingCorAnsLteqFilterName = filterNamePrefix + 'readingCorAnsLteq';
	var readingCorAnsGteqFilterName = filterNamePrefix + 'readingCorAnsGteq';
	var meaningIncorAnsLteqFilterName = filterNamePrefix + 'meaningIncorAnsLteq';
	var meaningIncorAnsGteqFilterName = filterNamePrefix + 'meaningIncorAnsGteq';
	var readingIncorAnsLteqFilterName = filterNamePrefix + 'readingIncorAnsLteq';
	var readingIncorAnsGteqFilterName = filterNamePrefix + 'readingIncorAnsGteq';
	var totalCorAnsLteqFilterName = filterNamePrefix + 'totalCorAnsLteq';
	var totalCorAnsGteqFilterName = filterNamePrefix + 'totalCorAnsGteq';
	var totalIncorAnsLteqFilterName = filterNamePrefix + 'totalIncorAnsLteq';
	var totalIncorAnsGteqFilterName = filterNamePrefix + 'totalIncorAnsGteq';
	var percentageTotalCorLteqFilterName = filterNamePrefix + 'percentageTotalCorLteq';
	var percentageTotalCorGteqFilterName = filterNamePrefix + 'percentageTotalCorGteq';
	var percentageMeaningCorLteqFilterName = filterNamePrefix + 'pecentagerMeaningCorLteq';
	var percentageMeaningCorGteqFilterName = filterNamePrefix + 'percentageMeaningCorGteq';
	var percentageReadingCorLteqFilterName = filterNamePrefix + 'percentageReadingCorLteq';
	var percentageReadingCorGteqFilterName = filterNamePrefix + 'percentageReadingCorGteq';
	var meaningCurStrLteqFilterName = filterNamePrefix + 'meaningCurStrLteq';
	var meaningCurStrGteqFilterName = filterNamePrefix + 'meaningCurStrGteq';
	var meaningMaxStrLteqFilterName = filterNamePrefix + 'meaningMaxStrLteq';
	var meaningMaxStrGteqFilterName = filterNamePrefix + 'meaningMaxStrGteq';
	var readingCurStrLteqFilterName = filterNamePrefix + 'readingCurStrLteq';
	var readingCurStrGteqFilterName = filterNamePrefix + 'readingCurStrGteq';
	var readingMaxStrLteqFilterName = filterNamePrefix + 'readingMaxStrLteq';
	var readingMaxStrGteqFilterName = filterNamePrefix + 'readingMaxStrGteq';
	var bothCurStrLteqFilterName = filterNamePrefix + 'bothCurStrLteq';
	var bothCurStrGteqFilterName = filterNamePrefix + 'bothCurStrGteq';
	var minCurStrLteqFilterName = filterNamePrefix + 'minCurStrLteq';
	var minCurStrGteqFilterName = filterNamePrefix + 'minCurStrGteq';
	var numReviewsLteqFilterName = filterNamePrefix + 'numReviewsLteq';
	var numReviewGteqFilterName = filterNamePrefix + 'numReviewsGteq';

	var supportedFilters = [leechLteqFilterName, meaningCorAnsLteqFilterName, meaningCorAnsGteqFilterName, readingCorAnsLteqFilterName, readingCorAnsGteqFilterName,
                            meaningIncorAnsLteqFilterName, meaningIncorAnsGteqFilterName, readingIncorAnsLteqFilterName, readingIncorAnsGteqFilterName,
                            totalCorAnsLteqFilterName, totalCorAnsGteqFilterName, totalIncorAnsLteqFilterName, totalIncorAnsGteqFilterName,
                            percentageTotalCorLteqFilterName, percentageTotalCorGteqFilterName, percentageMeaningCorLteqFilterName, percentageMeaningCorGteqFilterName,
                            percentageReadingCorLteqFilterName, percentageReadingCorGteqFilterName, meaningCurStrLteqFilterName, meaningCurStrGteqFilterName,
                            meaningMaxStrLteqFilterName, meaningMaxStrGteqFilterName, readingCurStrLteqFilterName, readingCurStrGteqFilterName,
                            readingMaxStrLteqFilterName, readingMaxStrGteqFilterName, bothCurStrLteqFilterName, bothCurStrGteqFilterName,
                            minCurStrLteqFilterName, minCurStrGteqFilterName, numReviewsLteqFilterName, numReviewGteqFilterName];

	function updateFiltersWhenReady() {
        wkof.set_state(settingsScriptId, 'loading');
		needToRegisterFilters = true;
		waitForItemDataRegistry().then(registerFilters);
	}

	function waitForItemDataRegistry() {
		return wkof.wait_state('wkof.ItemData.registry', 'ready');
	}

	function registerFilters() {
		if (!needToRegisterFilters) {
			return;
		}

		supportedFilters.forEach(function(filterName) {
			delete wkof.ItemData.registry.sources.wk_items.filters[filterName];
		});

		registerLeechLteqFilter();
		registerMeaningCorAnsGteqFilter();
		registerMeaningCorAnsLteqFilter();
		registerReadingCorAnsGteqFilter();
		registerReadingCorAnsLteqFilter();
		registerMeaningIncorAnsGteqFilter();
		registerMeaningIncorAnsLteqFilter();
		registerReadingIncorAnsGteqFilter();
		registerReadingIncorAnsLteqFilter();
		registerTotalCorAnsGteqFilter();
		registerTotalCorAnsLteqFilter();
		registerTotalIncorAnsGteqFilter();
		registerTotalIncorAnsLteqFilter();
		registerPercentageTotalCorGteqFilter();
		registerPercentageTotalCorLteqFilter();
		registerPercentageMeaningCorGteqFilter();
		registerPercentageMeaningCorLteqFilter();
		registerPercentageReadingCorGteqFilter();
		registerPercentageReadingCorLteqFilter();
		registerMeaningCurStrGteqFilter();
		registerMeaningCurStrLteqFilter();
		registerMeaningMaxStrGteqFilter();
		registerMeaningMaxStrLteqFilter();
		registerReadingCurStrGteqFilter();
		registerReadingCurStrLteqFilter();
		registerReadingMaxStrGteqFilter();
		registerReadingMaxStrLteqFilter();
		registerBothCurStrGteqFilter();
		registerBothCurStrLteqFilter();
		registerMinCurStrGteqFilter();
		registerMinCurStrLteqFilter();
		registerNumberReviewsGteqFilter();
		registerNumberReviewsLteqFilter();

		needToRegisterFilters = false;
        wkof.set_state(settingsScriptId, 'ready');
	}

	// BEGIN Leech
    let leechLteqHover_tip = 'Select items that have a leech value less than or equal to the stated value.';

	function registerLeechLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[leechLteqFilterName] = {
			type: 'number',
			label: 'Leech value <=',
			default: 10,
			filter_func: leechLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: leechLteqHover_tip,
		};
	}

	function leechLteqFilter(filterValue, item) {
		if (item.review_statistics === undefined) {
			return false;
		}

		var reviewStats = item.review_statistics;
		var meaningScore = getLeechScore(reviewStats.meaning_incorrect, reviewStats.meaning_current_streak);
		var readingScore = getLeechScore(reviewStats.reading_incorrect, reviewStats.reading_current_streak);

		return meaningScore <= filterValue && readingScore <= filterValue;
	}

    // Leech scores a rounded because otherwise the behavior of the filter is not consistent with rounded values presented to users in scripts.
    // In other words, data would be not filtered when based on user facing data it appears that it should be.
	function getLeechScore(incorrect, currentStreak) {
		return Math.round((incorrect / Math.pow((currentStreak || 0.5), 1.5)) * 100) / 100;
	}
	// END Leech


	// BEGIN Meaning correct answers
    let meaningCorAnsGteqFilterHover_tip = 'Selects items where the number of correct\nanswers for Meaning is &gt;= this value.';
    let meaningCorAnsLteqFilterHover_tip = 'Selects items where the number of correct\nanswers for Meaning is &lt;= this value.';

	function registerMeaningCorAnsGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[meaningCorAnsGteqFilterName] = {
			type: 'number',
			label: 'Meaning Cor. Ans. &gt;=',
			default: 0,
            filter_func: meaningCorAnsGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: meaningCorAnsGteqFilterHover_tip,
		};
	}

	function meaningCorAnsGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return review_statistics.meaning_correct >= filterValue;
	}

	function registerMeaningCorAnsLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[meaningCorAnsLteqFilterName] = {
			type: 'number',
			label: 'Meaning Cor. Ans. &lt;=',
			default: 100,
			filter_func: meaningCorAnsLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: meaningCorAnsLteqFilterHover_tip,
		};
	}

	function meaningCorAnsLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return review_statistics.meaning_correct <= filterValue;
	}
    // END Meaning correct answers

	// BEGIN Reading correct answers
    let readingCorAnsGteqFilterHover_tip = 'Selects items where the number of correct\nanswers for Reading is &gt;= this value.';
    let readingCorAnsLteqFilterHover_tip = 'Selects items where the number of correct\nanswers for Reading is &lt;= this value.';

	function registerReadingCorAnsGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[readingCorAnsGteqFilterName] = {
			type: 'number',
			label: 'Reading Cor. Ans. &gt;=',
			default: 0,
            filter_func: readingCorAnsGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: readingCorAnsGteqFilterHover_tip,
		};
	}

	function readingCorAnsGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return true;
        }
		return review_statistics.reading_correct >= filterValue;
	}

	function registerReadingCorAnsLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[readingCorAnsLteqFilterName] = {
			type: 'number',
			label: 'Reading Cor. Ans. &lt;=',
			default: 100,
			filter_func: readingCorAnsLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: readingCorAnsLteqFilterHover_tip,
		};
	}

	function readingCorAnsLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return true;
        }
		return review_statistics.reading_correct <= filterValue;
	}
	// END Reading correct answers

	// BEGIN Meaning incorrect answers
    let meaningIncorAnsGteqFilterHover_tip = 'Selects items where the number of incorrect\nanswers for Meaning is &gt;= this value.';
    let meaningIncorAnsLteqFilterHover_tip = 'Selects items where the number of incorrect\nanswers for Meaning is &lt;= this value.';

	function registerMeaningIncorAnsGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[meaningIncorAnsGteqFilterName] = {
			type: 'number',
			label: 'Meaning Incor. Ans. &gt;=',
			default: 0,
            filter_func: meaningIncorAnsGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: meaningIncorAnsGteqFilterHover_tip,
		};
	}

	function meaningIncorAnsGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return review_statistics.meaning_incorrect >= filterValue;
	}

	function registerMeaningIncorAnsLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[meaningIncorAnsLteqFilterName] = {
			type: 'number',
			label: 'Meaning Incor. Ans. &lt;=',
			default: 100,
			filter_func: meaningIncorAnsLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: meaningIncorAnsLteqFilterHover_tip,
		};
	}

	function meaningIncorAnsLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return review_statistics.meaning_incorrect <= filterValue;
	}
	// END Meaning incorrect answers

	// BEGIN Reading incorrect answers
    let readingIncorAnsGteqFilterHover_tip = 'Selects items where the number of incorrect\nanswers for Reading is &gt;= this value.';
    let readingIncorAnsLteqFilterHover_tip = 'Selects items where the number of incorrect\nanswers for Reading is &lt;= this value.';

	function registerReadingIncorAnsGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[readingIncorAnsGteqFilterName] = {
			type: 'number',
			label: 'Reading Incor. Ans. &gt;=',
			default: 0,
            filter_func: readingIncorAnsGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: readingIncorAnsGteqFilterHover_tip,
		};
	}

	function readingIncorAnsGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return true;
        }
		return review_statistics.reading_incorrect >= filterValue;
	}

	function registerReadingIncorAnsLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[readingIncorAnsLteqFilterName] = {
			type: 'number',
			label: 'Reading Incor. Ans. &lt;=',
			default: 100,
			filter_func: readingIncorAnsLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: readingIncorAnsLteqFilterHover_tip,
		};
	}

	function readingIncorAnsLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return true;
        }
		return review_statistics.reading_incorrect <= filterValue;
	}
	// END Reading incorrect answers

	// BEGIN Total correct answers
    let totalCorAnsGteqFilterHover_tip = 'Selects items where the total number\nof correct answers is &gt;= this value.';
    let totalCorAnsLteqFilterHover_tip = 'Selects items where the total number\nof correct answers is &lt;= this value.';

	function registerTotalCorAnsGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[totalCorAnsGteqFilterName] = {
			type: 'number',
			label: 'Total Cor. Ans. &gt;=',
			default: 0,
            filter_func: totalCorAnsGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: totalCorAnsGteqFilterHover_tip,
		};
	}

	function totalCorAnsGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return review_statistics.meaning_correct >= filterValue;
        }
		return review_statistics.meaning_correct + review_statistics.reading_correct >= filterValue;
	}

	function registerTotalCorAnsLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[totalCorAnsLteqFilterName] = {
			type: 'number',
			label: 'Total Cor. Ans. &lt;=',
			default: 100,
			filter_func: totalCorAnsLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: totalCorAnsLteqFilterHover_tip,
		};
	}

	function totalCorAnsLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return review_statistics.meaning_correct <= filterValue;
        }
		return review_statistics.meaning_correct + review_statistics.reading_correct <= filterValue;
	}
	// END Total correct answers

	// BEGIN Number of reviews
    // The number of reviews is always equal to the total correct answers because
    // you need to answer correctly exactly once to terminate a review
    // Therefore this is the same filters as Total correct answer with a different user
    // interface to inform the user of what they are doing

    let numberReviewsGteqFilterHover_tip = 'Selects items where the total number\nof reviews is &gt;= this value.';
    let numberReviewsLteqFilterHover_tip = 'Selects items where the total number\nof reviews is &lt;= this value.';

	function registerNumberReviewsGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[numReviewGteqFilterName] = {
			type: 'number',
			label: 'Number of Reviews &gt;=',
			default: 0,
            filter_func: totalCorAnsGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: numberReviewsGteqFilterHover_tip,
		};
	}

	function registerNumberReviewsLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[numReviewsLteqFilterName] = {
			type: 'number',
			label: 'Number of Reviews &lt;=',
			default: 100,
			filter_func: totalCorAnsLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: numberReviewsLteqFilterHover_tip,
		};
	}

	// END Number of reviews

	// BEGIN Total incorrect answers
    let totalIncorAnsGteqFilterHover_tip = 'Selects items where the total number\nof incorrect answers is &gt;= this value.';
    let totalIncorAnsLteqFilterHover_tip = 'Selects items where the total number\nof incorrect answers is &lt;= this value.';

	function registerTotalIncorAnsGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[totalIncorAnsGteqFilterName] = {
			type: 'number',
			label: 'Total Incor. Ans. &gt;=',
			default: 0,
            filter_func: totalIncorAnsGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: totalIncorAnsGteqFilterHover_tip,
		};
	}

	function totalIncorAnsGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return review_statistics.meaning_incorrect >= filterValue;
        }
		return review_statistics.meaning_incorrect + review_statistics.reading_incorrect >= filterValue;
	}

	function registerTotalIncorAnsLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[totalIncorAnsLteqFilterName] = {
			type: 'number',
			label: 'Total Incor. Ans. &lt;=',
			default: 100,
			filter_func: totalIncorAnsLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: totalIncorAnsLteqFilterHover_tip,
		};
	}

	function totalIncorAnsLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return review_statistics.meaning_incorrect <= filterValue;
        }
		return review_statistics.meaning_incorrect + review_statistics.reading_incorrect <= filterValue;
	}
	// END Total incorrect answers

    //==============================================
    // Percentages filters
    //
    // Needs rounding because otherwise user facing data is usually floored and inconsistencies in displayed data will occur
    // Flooring is used instead of rounding because otherwise we could have 100% correct while missing some answers due to rounding

	function percentageRounding(value){return Math.floor(value * 100)};

	// BEGIN Percentage Total Correct
    let percentageTotalCorGteqFilterHover_tip = 'Selects items where % of correct answers\non Total is &gt;= this value.';
    let percentageTotalCorLteqFilterHover_tip = 'Selects items where % of correct answers\non Total is &lt;= this value.';

	function registerPercentageTotalCorGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[percentageTotalCorGteqFilterName] = {
			type: 'number',
			label: '%Total Correct &gt;=',
			default: 0,
            filter_func: percentageTotalCorGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: percentageTotalCorGteqFilterHover_tip,
		};
	}

	function percentageTotalCorGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return review_statistics.percentage_correct >= filterValue;
	}

	function registerPercentageTotalCorLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[percentageTotalCorLteqFilterName] = {
			type: 'number',
			label: '%Total Correct &lt;=',
			default: 100,
			filter_func: percentageTotalCorLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: percentageTotalCorLteqFilterHover_tip,
		};
	}

	function percentageTotalCorLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return review_statistics.percentage_correct <= filterValue;
	}
	// END Percentage Total Correct

    // BEGIN Percentage Meaning Correct
    let percentageMeaningCorGteqFilterHover_tip = 'Selects items where % of correct answers\non Meaning is &gt;= this value.';
    let percentageMeaningCorLteqFilterHover_tip = 'Selects items where % of correct answers\non Meaning is &lt;= this value.';

	function registerPercentageMeaningCorGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[percentageMeaningCorGteqFilterName] = {
			type: 'number',
			label: '%Meaning Correct &gt;=',
			default: 0,
            filter_func: percentageMeaningCorGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: percentageMeaningCorGteqFilterHover_tip,
		};
	}

	function percentageMeaningCorGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return percentageRounding(review_statistics.meaning_correct / (review_statistics.meaning_correct + review_statistics.meaning_incorrect)) >= filterValue;
	}

	function registerPercentageMeaningCorLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[percentageMeaningCorLteqFilterName] = {
			type: 'number',
			label: '%Meaning Correct &lt;=',
			default: 100,
			filter_func: percentageMeaningCorLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: percentageMeaningCorLteqFilterHover_tip,
		};
	}

	function percentageMeaningCorLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return percentageRounding(review_statistics.meaning_correct / (review_statistics.meaning_correct + review_statistics.meaning_incorrect)) <= filterValue;
	}
	// END Percentage Meaning Correct

	// BEGIN Percentage Reading Correct
    let percentageReadingCorGteqFilterHover_tip = 'Selects items where % of correct answers\non Reading is &gt;= this value.';
    let percentageReadingCorLteqFilterHover_tip = 'Selects items where % of correct answers\non Reading is &lt;= this value.';

	function registerPercentageReadingCorGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[percentageReadingCorGteqFilterName] = {
			type: 'number',
			label: '%Reading Correct &gt;=',
			default: 0,
            filter_func: percentageReadingCorGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: percentageReadingCorGteqFilterHover_tip,
		};
	}

	function percentageReadingCorGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return true;
        }
		return percentageRounding(review_statistics.reading_correct / (review_statistics.reading_correct + review_statistics.reading_incorrect)) >= filterValue;
	}

	function registerPercentageReadingCorLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[percentageReadingCorLteqFilterName] = {
			type: 'number',
			label: '%Reading Correct &lt;=',
			default: 100,
			filter_func: percentageReadingCorLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: percentageReadingCorLteqFilterHover_tip,
		};
	}

	function percentageReadingCorLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return true;
        }
		return percentageRounding(review_statistics.reading_correct / (review_statistics.reading_correct + review_statistics.reading_incorrect)) <= filterValue;
	}
	// END Percentage Reading Correct

    //======================================================
    // Streak Filters
    //
    // Wanikani starts the streak count at 1, that is when the last review is failed, the streak is 1, not 0.
    // User facing data is adjusted in Item Inspector to start the streak count at 0 because starting at 1 is not intuitive.
    // The filters do so too because the filter value in the settings panel is also user facing data that may be correlated with Item Inspector data.
    // This is done by adjusting the filter value through passing streakValueAdjustment to the filter_value_map: registering option.
    // This adjustment is not made in leech calculations because a- this use of streaks is not user facing data - and b- there is an history of
    // calculating leech values this way and it would be disruptive to change it.

    function streakValueAdjustment(param){return param + 1};


	// BEGIN Meaning Current Streak
    let meaningCurStrGteqFilterHover_tip = 'Selects items where current Meaning streak is &gt;= this value.';
    let meaningCurStrLteqFilterHover_tip = 'Selects items where current Meaning streak is &lt;= this value.';

	function registerMeaningCurStrGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[meaningCurStrGteqFilterName] = {
			type: 'number',
			label: 'Meaning Cur. Str. &gt;=',
			default: 0,
            filter_value_map: streakValueAdjustment,
            filter_func: meaningCurStrGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: meaningCurStrGteqFilterHover_tip,
		};
	}

	function meaningCurStrGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return review_statistics.meaning_current_streak >= filterValue;
	}

	function registerMeaningCurStrLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[meaningCurStrLteqFilterName] = {
			type: 'number',
			label: 'Meaning Cur. Str. &lt;=',
			default: 100,
            filter_value_map: streakValueAdjustment,
			filter_func: meaningCurStrLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: meaningCurStrLteqFilterHover_tip,
		};
	}

	function meaningCurStrLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return review_statistics.meaning_current_streak <= filterValue;
	}
	// END Meaning Current Streak

	// BEGIN Meaning Maximum Streak
    let meaningMaxStrGteqFilterHover_tip = 'Selects items where maximum Meaning streak is &gt;= this value.';
    let meaningMaxStrLteqFilterHover_tip = 'Selects items where maximum Meaning streak is &lt;= this value.';

	function registerMeaningMaxStrGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[meaningMaxStrGteqFilterName] = {
			type: 'number',
			label: 'Meaning Max. Str. &gt;=',
			default: 0,
            filter_value_map: streakValueAdjustment,
            filter_func: meaningMaxStrGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: meaningMaxStrGteqFilterHover_tip,
		};
	}

	function meaningMaxStrGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return review_statistics.meaning_max_streak >= filterValue;
	}

	function registerMeaningMaxStrLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[meaningMaxStrLteqFilterName] = {
			type: 'number',
			label: 'Meaning Max. Str. &lt;=',
			default: 100,
            filter_value_map: streakValueAdjustment,
			filter_func: meaningMaxStrLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: meaningMaxStrLteqFilterHover_tip,
		};
	}

	function meaningMaxStrLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
		return review_statistics.meaning_max_streak <= filterValue;
	}
	// END Meaning Maximum Streak

    // BEGIN Reading Current Streak
    let readingCurStrGteqFilterHover_tip = 'Selects items where current Reading streak is &gt;= this value.';
    let readingCurStrLteqFilterHover_tip = 'Selects items where current Reading streak is &lt;= this value.';

	function registerReadingCurStrGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[readingCurStrGteqFilterName] = {
			type: 'number',
			label: 'Reading Cur. Str. &gt;=',
			default: 0,
            filter_value_map: streakValueAdjustment,
            filter_func: readingCurStrGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: readingCurStrGteqFilterHover_tip,
		};
	}

	function readingCurStrGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return true;
        }
		return review_statistics.reading_current_streak	 >= filterValue;
	}

	function registerReadingCurStrLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[readingCurStrLteqFilterName] = {
			type: 'number',
			label: 'Reading Cur. Str. &lt;=',
			default: 100,
            filter_value_map: streakValueAdjustment,
			filter_func: readingCurStrLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: readingCurStrLteqFilterHover_tip,
		};
	}

	function readingCurStrLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return true;
        }
		return review_statistics.reading_current_streak	 <= filterValue;
	}
	// END Reading Current Streak

	// BEGIN Reading Maximum Streak
    let readingMaxStrGteqFilterHover_tip = 'Selects items where maximum Reading streak is &gt;= this value.';
    let readingMaxStrLteqFilterHover_tip = 'Selects items where maximum Reading streak is &lt;= this value.';

	function registerReadingMaxStrGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[readingMaxStrGteqFilterName] = {
			type: 'number',
			label: 'Reading Max. Str. &gt;=',
			default: 0,
            filter_value_map: streakValueAdjustment,
            filter_func: readingMaxStrGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: readingMaxStrGteqFilterHover_tip,
		};
	}

	function readingMaxStrGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return true;
        }
		return review_statistics.reading_max_streak >= filterValue;
	}

	function registerReadingMaxStrLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[readingMaxStrLteqFilterName] = {
			type: 'number',
			label: 'Reading Max. Str. &lt;=',
			default: 100,
            filter_value_map: streakValueAdjustment,
			filter_func: readingMaxStrLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: readingMaxStrLteqFilterHover_tip,
		};
	}

	function readingMaxStrLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return true;
        }
		return review_statistics.reading_max_streak <= filterValue;
	}
	// END Reading Maximum Streak

	// BEGIN Both Current Streaks
    let bothCurStrGteqFilterHover_tip = 'Selects items where both current Meaning streak\nand current Reading streak are &gt;= this value.';
    let bothCurStrLteqFilterHover_tip = 'Selects items where both current Meaning streak\nand current Reading streak are &lt;= this value.';

	function registerBothCurStrGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[bothCurStrGteqFilterName] = {
			type: 'number',
			label: 'Both Cur. Str. &gt;=',
			default: 0,
            filter_value_map: streakValueAdjustment,
            filter_func: bothCurStrGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: bothCurStrGteqFilterHover_tip,
		};
	}

	function bothCurStrGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return review_statistics.meaning_current_streak >= filterValue;
        } else {
		    return review_statistics.meaning_current_streak >= filterValue && review_statistics.reading_current_streak >= filterValue;
        }
	}

	function registerBothCurStrLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[bothCurStrLteqFilterName] = {
			type: 'number',
			label: 'Both Cur. Str. &lt;=',
			default: 100,
            filter_value_map: streakValueAdjustment,
			filter_func: bothCurStrLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: bothCurStrLteqFilterHover_tip,
		};
	}

	function bothCurStrLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return review_statistics.meaning_current_streak <= filterValue;
        } else {
		    return review_statistics.meaning_current_streak <= filterValue && review_statistics.reading_current_streak <= filterValue;
        }
	}
	// END Both Current Streaks

	// BEGIN Minimum Current Streaks
    let minCurStrGteqFilterHover_tip = 'Selects items where the minimum of current Meaning streak\nand current Reading streak is &gt;= this value.';
    let minCurStrLteqFilterHover_tip = 'Selects items where the minimum of current Meaning streak\nand current Reading streak is &lt;= this value.';

	function registerMinCurStrGteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[minCurStrGteqFilterName] = {
			type: 'number',
			label: 'Minimum Cur. Str. &gt;=',
			default: 0,
            filter_value_map: streakValueAdjustment,
            filter_func: minCurStrGteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: minCurStrGteqFilterHover_tip,
		};
	}

	function minCurStrGteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return review_statistics.meaning_current_streak >= filterValue;
        } else {
		    return review_statistics.meaning_current_streak >= filterValue && review_statistics.reading_current_streak >= filterValue;
        };
	}

	function registerMinCurStrLteqFilter() {
		wkof.ItemData.registry.sources.wk_items.filters[minCurStrLteqFilterName] = {
			type: 'number',
			label: 'Minimum Cur. Str. &lt;=',
			default: 100,
            filter_value_map: streakValueAdjustment,
			filter_func: minCurStrLteqFilter,
			set_options: function(options) { options.review_statistics = true; },
			hover_tip: minCurStrLteqFilterHover_tip,
		};
	}

	function minCurStrLteqFilter(filterValue, item) {
        let review_statistics = item.review_statistics;
		if (review_statistics === undefined) {
			return false;
		}
        if (item.object === 'radical'){
	    	return review_statistics.meaning_current_streak <= filterValue;
        } else {
	    	return review_statistics.meaning_current_streak <= filterValue || review_statistics.reading_current_streak <= filterValue;
        }
	}
	// END Minimum Current Streaks

    updateFiltersWhenReady();
})(window.wkof);