WaniKani Item Inspector

Inspect Items in Tabular Format

目前為 2020-09-24 提交的版本,檢視 最新版本

// ==UserScript==
// @name          WaniKani Item Inspector
// @namespace     wk-dashboard-item-inspector
// @description   Inspect Items in Tabular Format
// @author        prouleau
// @version       1.7.2
// @include       https://www.wanikani.com/dashboard
// @include       https://www.wanikani.com/
// @grant         none
// ==/UserScript==

(function() {
    'use strict';
    //------------------------------
    // Wanikani Framework
    //------------------------------
    if (!window.wkof) {
        var script_name = 'Wanikani Item Inspector';
        var response = confirm(script_name + ' 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;
    };

    const config = {
        wk_items: {
            options: {
                review_statistics: true
            },
            filters: {
                level: '1..+0', //only retrieve items from lv 1 up to and including current level
                srs: {value: 'lock, init, burn', invert: true} //exlude locked, initial and burned items
            }
        }
    };

    var scriptId = 'Item_Inspector';
    var scriptTitle = 'Item Inspector';

    var quiz_setup_state = 'init';
    function open_settings_dialog() {
        wkof.Settings.load(scriptId)
            .then(function(){
            quiz_setup_state = 'ready';
            init_settings();
        })
        open_quiz_settings();
    }


    //------------------------------
    // Styling
    //------------------------------
    function table_css(){
        let is_dark = is_dark_theme();
        var leechTableCss = `
            /* Control Bar */

           .WkitControlBar {
                  margin: 7px 0.6% 7px 2.6%;
            }

            /* to prevent rendering problems */
            #WkitTopBar {
                  display: inline-block;
                  width: 103.2%;
                  margin-left: -27px;
                  padding-top: 3px;
                  padding-bottom: 3px;
                  padding-right: 3px;
                  padding-left: 3px;
             }


            #WkitTopBar .WkitSmallCaps {
                  font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
                  margin: 2px;
                  padding: 7.5px 30px;
                  background-color: #d5d5d5;
                  text-align: center;
                  border-radius: 5px 5px 0 0;
                  font-size: 11px;
                  font-weight: bold;
                  letter-spacing: 0;
                  line-height: 20px;
                  color: #555;
                  text-transform: uppercase;
                  text-shadow: 0 1px 0 #fff;
                  ${is_dark ? 'align-items: center;' : ''}
                  ${is_dark ? 'background-color: #1c1e21;' : ''}
                  ${is_dark ? 'box-shadow: 0 1px 1px rgba(0, 0, 0, 0.7), 0 2px 2px rgba(0, 0, 0, 0.7);' : ''}
                  ${is_dark ? 'color: #bcbcbc;' : ''}
                  ${is_dark ? 'display: flex;' : ''}
                  ${is_dark ? 'margin-bottom:;' : ''}
                  ${is_dark ? 'max-height: 60px;' : ''}
                  ${is_dark ? 'padding-bottom: 3px;' : ''}
                  ${is_dark ? 'padding-top: 3px;' : ''}
                  ${is_dark ? 'text-align: left;' : ''}
            }

            #WkitTopBar .WkitTableList {
                  position: relative;
                  margin: 0 0 30px;
            }

            #WkitTopBar .WkitTableList table{
                  width: 100%;
                  max-width: 100%;
                  background-color: transparent;
                  border-collapse: collapse;
                  border-spacing: 0;
            }

            #WkitTopBar .WkitTableList table,
            #WkitTopBar .right table{
                  width: 100%;
                  line-height: 1em;
                  color: #fff;
            }

            #WkitTopBar .WkitTableList table a {
                  display: block;
                  padding: 0.7em 1em;
                  margin: 0px;
                  color: #fff;
                  text-decoration: none;
                  -webkit-transition: text-shadow ease-out 0.3s;
                  -moz-transition: text-shadow ease-out 0.3s;
                  -o-transition: text-shadow ease-out 0.3s;
                  transition: text-shadow ease-out 0.3s;
            }

            #WkitTopBar .WkitTableList table tr{
                  border-top: 0;
                  border-bottom: 0;
                  border-left: 0;
                  text-shadow: 0 1px 0 rgba(0,0,0,0.2);
            }

            #WkitTopBar .WkitTableList table td{
                  -webkit-box-sizing: border-box;
                  -moz-box-sizing: border-box;
                  box-sizing: border-box;
            }

           #WkitTopBar .WkitTableList table tr[class=vocabulary]{
                  background-color: #a100f1;
                  background-image: -moz-linear-gradient(top, #a0f, #9300dd);
                  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#a0f), to(#9300dd));
                  background-image: -webkit-linear-gradient(top, #a0f, #9300dd);
                  background-image: -o-linear-gradient(top, #a0f, #9300dd);
                  background-image: linear-gradient(to bottom, #a0f, #9300dd);
                  background-repeat: repeat-x;
                  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFAA00FF', endColorstr='#FF9300DD', GradientType=0);
                  border-top: 1px solid #c655ff;
                  border-bottom: 1px solid #8800cc;
                  border-left: 1px solid #c655ff;        }

            #WkitTopBar .WkitTableList table tr[class=kanji]{
                  background-color: #f100a1;
                  background-image: -moz-linear-gradient(top, #f0a, #dd0093);
                  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f0a), to(#dd0093));
                  background-image: -webkit-linear-gradient(top, #f0a, #dd0093);
                  background-image: -o-linear-gradient(top, #f0a, #dd0093);
                  background-image: linear-gradient(to bottom, #f0a, #dd0093);
                  background-repeat: repeat-x;
                  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFF00AA', endColorstr='#FFDD0093', GradientType=0);
                  border-top: 1px solid #f6c;
                  border-bottom: 1px solid #cc0088;
                  border-left: 1px solid #f6c;
            }

            #WkitTopBar .WkitTableList table tr[class=radical]{
                  background-color: #00a1f1;
                  background-image: -moz-linear-gradient(top, #0af, #0093dd);
                  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0af), to(#0093dd));
                  background-image: -webkit-linear-gradient(top, #0af, #0093dd);
                  background-image: -o-linear-gradient(top, #0af, #0093dd);
                  background-image: linear-gradient(to bottom, #0af, #0093dd);
                  background-repeat: repeat-x;
                  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FF00AAFF', endColorstr='#FF0093DD', GradientType=0);
                  border-top: 1px solid #88d7ff;
                  border-bottom: 1px solid #069;
                  border-left: 1px solid #88d7ff;
            }

            #WkitTopBar .WkitItemList {
                  visibility: visible;
                  position: relative;
                  background-color: #0000001a;
                  display: flex;
                  flex-direction: row;
                  flex-wrap: wrap;
                  justify-content: space-between;
                  line-height: 1;
                  width: 96.1%;
                  margin-left: 29px;
                  margin-right: 4px;
                  padding-top: 3px;
                  padding-bottom: 3px;
                  padding-right: 3px;
                  padding-left: 3px;
                  border-radius: 5px;
            }

            #WkitTopBar .WkitSmallCapsList {
                  font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
                  background-color: #cccccc;
                  text-align: center;
                  width: max-content;
                  font-weight: bold;
                  letter-spacing: 0;
                  color: #241f42;
                  text-transform: uppercase;
                  border-radius: 5px;
                  border-color: #000000;
                  border-width: 1px;
                  border-style: solid;
                  ${is_dark ? 'align-items: center;' : ''}
                  ${is_dark ? 'background-color: #1c1e21;' : ''}
                  ${is_dark ? 'box-shadow: 0 1px 1px rgba(0, 0, 0, 0.7), 0 2px 2px rgba(0, 0, 0, 0.7);' : ''}
                  ${is_dark ? 'color: #bcbcbc;' : ''}
                  ${is_dark ? 'display: flex;' : ''}
                  ${is_dark ? 'margin-bottom:;' : ''}
                  ${is_dark ? 'max-height: 60px;' : ''}
                  ${is_dark ? 'padding-bottom: 3px;' : ''}
                  ${is_dark ? 'padding-top: 3px;' : ''}
                  ${is_dark ? 'text-align: left;' : ''}
            }

            #WkitTopBar .WkitSmallCapsList.WkitReading {
                  margin-top: 2px;
                  margin-bottom: 2px;
                  margin-left: 5px;
                  margin-right: 5px;
                  line-height: 1em;
                  padding: 15px 20px;
                  font-size: 14px;
             }

            #WkitTopBar .WkitSmallCapsList.WkitMeaning {
                  margin-top: 2px;
                  margin-bottom: 2px;
                  margin-left: 5px;
                  margin-right: 5px;
                  padding: 8px 20px;
                  font-size: 12px;
             }

            #WkitTopBar .WkitItemListed {
                display: inline-block;
                margin: 10px 5px;
                text-decoration-line: none;
            }

           #WkitTopBar .WkitItemList .WkitTooltipContent {
                padding: -4px;
            }

            #WkitTopBar .WkitItemListed span {
                border-radius: 5px;
                /*border-color: #000000;
                border-width: 1px;
                border-style: solid;*/
                color: #ffffff;
                line-height: 1em;
                padding: 6px;
                margin: 0;
                box-shadow: inset 0 -2px 0 rgba(0,0,0,0.2);
            }

            #WkitTopBar .WkitItemListed a {
                text-decoration-line: none;
            }

            #WkitTopBar .WkitMarker span {
                border-radius: 5px;
                font-size: 14px;
                line-height: 1em;
                padding: 14px 8px;
                margin: 0;
                box-shadow: inset 0 -2px 0 rgba(0,0,0,0.2);
                background-image: linear-gradient(0deg, #c1cdc5, #b0bfb5);
                background-color: #c1cdc5;
            }

            #WkitTopBar .WkitMarkerMeaning span {
                border-radius: 5px;
                font-size: 14px;
                line-height: 1em;
                padding: 6px 6px;
                margin: 0;
                text-decoration-line: none;
                box-shadow: inset 0 -2px 0 rgba(0,0,0,0.2);
                background-image: linear-gradient(0deg, #c1cdc5, #b0bfb5);
                background-color: #c1cdc5;
            }

            #WkitTopBar .left a {
                font-size: 74px;
                line-height: 73px;
                display: block;
                border-radius: 3px;
                margin: 3px 10px 0 3px;
            }

            #WkitTopBar .item.vocabulary .left a {
                margin-right: 3px;
                text-align: center;
            }

            #WkitTopBar .items .radical img {
                height: 14px;
                filter: invert(1);
            }

            #WkitTopBar .WkitItemListed img{
                 width: 30px;
                 height: 30px;
                 filter: invert(1);
             }


            #WkitTopBar .WkitHeader {
                  background-color: ${is_dark ? '#232629' : '#5c6c705c'};
                  display: -webkit-flex;
                  display: flex;
                  justify-content: space-between;
                  padding: 3px;
                  width: calc(100% - 8px);
                  border-radius: 5px;
                  margin-left: -2px;
             }

            #WkitTopBar .WkitControlLeft {
                  display: block;
                  vertical-align: middle;
                  height: 100%;
                  padding-left: 2px;
            }

            #WkitTopBar .WkitControlRight {
                  display: block;
                  vertical-align: middle;
                  -webkit-flex: 0 0 318px;
                  -ms-flex: 0 0 318px;
                  -moz-flex: 0 0 318px;
                  flex: 0 0 318px;
                  height: 100%;
                  padding-right: 3px;
            }

            #WkitTopBar .WkitTitle {
                   vertical-align: middle;
                   font-size: 150%;
                   text-align: center;
                   margin-top: 3px;
                   margin-bottom: 3px;
            }

           #WkitTopBar .WkitButton {
                   display: inline;
                   float: left;
                   vertical-align: middle;
                   border-width: 1px;
                   border-radius: 3px;
                   text-align: center;
                   min-width: 30px;
                   width: max-content;
                   height: 30px;
                   margin: 2px;
                   margin-top: 5px;
                   margin-bottom: 5px;
                   background-color: #efefef;
                   font-family: FontAwesome;
            }

            #WkitTopBar .WkitButton a {
                  color: rgb(0, 0, 0);
            }

            /* #WkitTopBar .WkitButtonLook {
                  background-color: ${is_dark ? 'rgb(30, 33, 35)' : 'current-color'};
                  border-bottom-color: -internal-light-dark(rgb(118, 118, 118), rgb(195, 195, 195));
                  border-bottom-style: ${is_dark ? 'none' : 'outset'};
                  border-image-outset: 0;
                  border-image-repeat: stretch;
                  border-image-slice: 100%;
                  border-image-source: none;
                  border-image-width: 1;
                  border-left-color: rgb(118, 118, 118);
                  border-left-style: ${is_dark ? 'none' : 'outset'};
                  border-right-color: rgb(118, 118, 118);
                  border-right-style: ${is_dark ? 'none' : 'outset'};
                  border-top-color: rgb(118, 118, 118);
                  border-top-style: ${is_dark ? 'none' : 'outset'};
                  box-sizing: border-box;
                  color: ${is_dark ? 'rgb(188, 188, 188)' : 'rgb(0, 0, 0)'};
                  cursor: pointer;
                  font-family: FontAwesome;
                  font-stretch: 100%;
                  font-style: normal;
                  font-variant-caps: normal;
                  font-variant-east-asian: normal;
                  font-variant-ligatures:normal;
                  font-variant-numeric: normal;
                  font-weight: 400;
                  letter-spacing: normal;
                  line-height: 20px;
                  padding-bottom: 1px;
                  padding-left: 6px;
                  padding-right: 6px;
                  padding-top: 1px;
                  text-decoration: none;
                  text-indent: 0px;
                  text-rendering: auto;
                  text-shadow: none;
                  text-size-adjust: 100%;
                  text-size-adjust: 100%;
                  text-transform: none;
                  word-spacing: 0px;
                  writing-mode: horizontal-tb;
                  -webkit-border-image:none;
            }

            #WkitTopBar .WkitButtonLook a {
                  text-decoration: none;
            }*/

            #WkitTopBar button a {
                  text-decoration: none;
            }


           #WkitTopBar .WkitButtonLeft {
                   float: left;
            }

            #WkitTopBar .WkitButtonRight {
                   float: right;
            }

            #WkitTopBar .WkitEnglishButton {
                   font-size: 18px;
                   font-weight: bold;
            }

            #WkitTopBar .WkitSelector {
                   display: inline;
                   float: left;
                   vertical-align: middle;
                   margin-right: 3px;
                   margin-left: 3px;
                   margin-top: 5px;
                   margin-bottom: 5px;
                   border-width: 1px;
                   border-color: Black;
                   color: Black;
                   background-color: #efefef;
            }

            #WkitTopBar .WkitClipBoard {
                  display: inline-block;
                  width: 96%;
                  height: 420px;
                  margin-left: 29px;
             }

            #WkitTopBar .emptyMessage {
                  margin: 7px 0.6% 7px 2.6%;
                  padding: 5px;
                  padding-left: 15px;
                  padding-top: 10px;
                  background-color: Azure;
                  border-radius: 8px;
             }

            /* Pop ups, aka TOOL TIPS */

            #WkitTopBar .WkitTooltip {
                  display: inline-block;
                  position: relative;
                  padding: 0px;
            }

            #WkitTopBar .WkitTooltip .WkitTooltipContent {
                  display: inline-block;
                  visibility: hidden;
                  background-color: black;
                  color: #fff;
                  font-size: 100%;
                  width: max-content;
                  max-width: 445px;
                  text-align: left;
                  margin: 5px;
                  padding: 2px;
                  border-radius: 3px;
                  position: absolute;
                  z-index: 1;
            }

            #WkitTopBar .WkitTableList .WkitTooltip .WkitTooltipContent {
                  bottom: 30px;
                  left: 0%;
            }


            #WkitTopBar .WkitItemList .WkitTooltipIcon.WkitFirstItem .WkitTooltipContent {
                  top: 55px;
                  left: 50%;
                  max-width: 250px;
            }


            #WkitTopBar .WkitItemList .WkitTooltipIcon.WkitLaterItem .WkitTooltipContent {
                  bottom: 55px;
                  left: 50%;
                  max-width: 250px;
            }


            #WkitTopBar .WkitItemList .WkitTooltipIconMeaning.WkitFirstItem .WkitTooltipContent {
                  top: 35px;
                  left: 50%;
                  max-width: 250px;
            }


            #WkitTopBar .WkitItemList .WkitTooltipIconMeaning.WkitLaterItem .WkitTooltipContent {
                  bottom: 35px;
                  left: 50%;
                  max-width: 250px;
            }


            #WkitTopBar .WkitTableList .WkitTooltip:hover div.WkitTooltipContent {
                  visibility: visible;
                  display: inline-block;
                  z-index: 50;
            }

            #WkitTopBar .WkitItemList .WkitTooltip:hover div.WkitTooltipContent {
                  visibility: visible;
                  display: inline-block;
                  z-index: 50;
                  transition-delay: 0.5s;
                  transform: translateX(-50%);
            }

            #WkitTopBar .WkitTableList .WkitTooltip .WkitTooltipContent::after {
                  content: " ";
                  position: absolute;
                  top: 100%; /* At the bottom of the tooltip */
                  left: 1em;
                  border-width: 5px;
                  border-style: solid;
                  border-color: black transparent transparent transparent;
            }

            #WkitTopBar .WkitItemList .WkitTooltip.WkitFirstItem  .WkitTooltipContent::after {
                  content: " ";
                  position: absolute;
                  bottom: 100%; /* At the top of the tooltip */
                  left: 50%;
                  border-width: 5px;
                  border-style: solid;
                  border-color: transparent transparent black transparent;
            }

            #WkitTopBar .WkitItemList .WkitTooltip.WkitLaterItem .WkitTooltipContent::after {
                  content: " ";
                  position: absolute;
                  top: 100%; /* At the bottom of the tooltip */
                  left: 50%;
                  border-width: 5px;
                  border-style: solid;
                  border-color: black transparent transparent transparent;
            }

             #WkitTopBar .WkitTooltip2 {
                  position: relative;
                  display: inline-block;
                  width: 30px;
            }

            #WkitTopBar .WkitTooltip2 .WkitEnlargedTooltip {
                  display: none;
                  visibility: hidden;
                  background-color: black;
                  color: #fff;
                  font-size: 100%;
                  width: max-content;
                  border-radius: 3px;
                  position: absolute;
                  bottom: 30px;
                  left: 0%;
                  padding: 2px;
                  z-index: 1;
            }

            #WkitTopBar .WkitTooltip2:hover div.WkitEnlargedTooltip {
                  visibility: visible;
                  display: inline-block;
                  z-index: 50;
            }

            #WkitTopBar .WkitTooltip2 .WkitEnlargedTooltip::after {
                  content: " ";
                  position: absolute;
                  top: 100%; /* At the bottom of the tooltip */
                  left: 1em;
                  border-width: 5px;
                  border-style: solid;
                  border-color: black transparent transparent transparent;
            }

            #WkitTopBar .WkitTooltip .radical {
                  background: #00a1f1;
            }

            #WkitTopBar .WkitTooltip .kanji {
                  background: #ff00aa;
            }

            #WkitTopBar .WkitTooltip .vocabulary {
                  background: #9800e8;
            }

            #WkitTopBar .WkitTooltip2 .radical {
                  padding: 5px;
                  background: #00a1f1;
             }

            #WkitTopBar .WkitTooltip2 .kanji {
                  padding: 5px;
                  background: #ff00aa;
            }

            #WkitTopBar .WkitTooltip2 .vocabulary {
                  padding: 5px;
                  background: #9800e8;
            }

            #WkitTopBar .WkitTooltipIcon .radical {
                  font-size: 30px;
                  background-image: linear-gradient(0deg, #0af, #0093dd);
                  background-color: #00a1f1;
            }

            #WkitTopBar .WkitTooltipIcon .kanji {
                  font-size: 30px;
                  background-image: linear-gradient(0deg, #f0a, #dd0093);
                  background-color: #ff00aa;
            }

            #WkitTopBar .WkitTooltipIcon .vocabulary {
                  font-size: 30px;
                  background-image: linear-gradient(0deg, #9800e89e, #7e00c2);
                  background-color: #9800e8;
            }

            #WkitTopBar .WkitTooltipIcon .WkitMarker {
                  margin: 5px;
                  padding: 13px 3px;
            }

            #WkitTopBar .WkitTooltipIcon .WkitMarkerMeaning {
                  margin: 10px 5px;
                  /*padding: 13px 3px;*/
            }

            #WkitTopBar .WkitTooltip2 img{
                 width: 80px;
                 height: 80px;
                 align-items: center;
             }

           #WkitTopBar .radical img {
                 filter: invert(1);
           }

           #WkitTopBar .left span {
                  font-size: 35px;
                  line-height: 40px;
                  display: block;
                  align-items: center;
                  text-align: center;
                  border-radius: 3px;
                  margin: 5px;
                  padding: 5px;
           }

           #WkitTopBar .WkitEnlargedTooltip span {
                  font-size: 60px;
                  line-height: 58px;
                  display: block;
                  border-radius: 3px;
                  margin: 5px;
           }

           #WkitTopBar .right  {
                  padding-left: 7px;
                  padding-right: 7px;
                  padding-bottom: 7px;
           }

           #WkitTopBar .right table td:first-child {
                  padding-right: 10px;
                  font-weight: bold;
           }

           #WkitTopBar .right table td {
                  padding-top: 3px;
           }

           #WkitTopBar .WkitLabel, {
                  padding: 4px;
           }`

        var leechStyling = document.createElement('style');
        leechStyling.type='text/css';
        if(leechStyling.styleSheet){
            leechStyling.styleSheet.cssText = leechTableCss;
        }else{
            leechStyling.appendChild(document.createTextNode(leechTableCss));
        }
        document.getElementsByTagName('head')[0].appendChild(leechStyling);
    };

    //------------------------------
    // Menu
    //------------------------------
    var settings_dialog;

    function install_menu() {
        wkof.Menu.insert_script_link({
            script_id: scriptId,
            name: scriptId,
            submenu:   'Settings',
            title:     'Item Inspector',
            on_click:  open_settings_dialog
        });
    }


    //------------------------------
    // Settings - new version
    //------------------------------

    //########################################################################
    // QUIZ DATA
    //########################################################################

    var quiz = {
        // Dialogs
        dialog: null,
        settings_dialog: null,

        // Item Lists
        items: [],
        group_list: [],
        serial_list: [],
        index: null,

        // Status
        showing_help: false,
        mode: 'loading',

        // Question Info
        qinfo: {
            load: null,
            prep: null,
            cache: {},
        },

        // Stats
        stats: {
            round: 1,
            total: 0,
            correct: 0,
            incorrect: 0,
        },

        // Functions
        start: null,
        shuffle: null,
        requiz: null,
        ask: null,
        submit: null,
        prev: null,
        next: null,
        close: null,
    };


    //########################################################################
    // QUIZ SETTINGS DIALOG
    //########################################################################

    //========================================================================
    // setup_quiz_settings()
    //------------------------------------------------------------------------
    var quiz_settings_state = 'init';
    var exportedInfo = ['export1','export2','export3','export4','export5','export6','export7','export8','export9','export10',
                        'export11','export12','export13','export14','export15','export16','export17','export18','export19','export20',
                        'export21','export22','export23','export24','export25','export26','export27','export28','export29',];
    var wideElements = {'Review_Date':true, 'Passed_Date':true, 'Burned_Date':true, 'Resurrected_Date':true, 'Lesson_Date':true, 'Unlock_Date':true, };
    function setup_quiz_settings() {
        if (quiz_settings_state === 'init') {
            quiz_settings_state = 'loading';
            return wkof.ready('Settings')
                .then(function(){
                quiz_settings_state = 'setup';
                setup_quiz_settings();
            });
        }
        if (quiz_settings_state !== 'setup') return;

        let tableElementContents = {'Meaning_Brief':'Meaning Brief',
                                    'Reading_Brief':'Reading Brief', 'Reading_Full':'Reading Full', 'Leech':'Leach Value',
                                    'Meaning_Correct_Answers': 'Meaning Correct Answers', 'Meaning_Incorrect_Answers': 'Meaning Incorrect Answers',
                                    'Reading_Correct_Answers': 'Reading Correct Answers', 'Reading_Incorrect_Answers': 'Reading Incorrect Answers',
                                    'Total_Correct_Answers': 'Total Correct Answers','Total_Incorrect_Answers': 'Total Incorrect Answers',
                                    'Percentage_Correct': 'Percentage Correct Total','Meaning_Correct': 'Percentage Correct Meaning','Reading_Correct': 'Percentage Correct Reading',
                                    'Meaning_Current_Streak' : 'Meaning Current Streak', 'Reading_Current_Streak': 'Reading Current Streak',
                                    'Meaning_Max_Streak' : 'Meaning Maximum Streak', 'Reading_Max_Streak': 'Reading Maximum Streak',
                                    'Level':'Level',
                                    'Srs':'SRS Stage', 'Review_Date':'Review Date', 'Review_Wait':'Review Wait Time',
                                    'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Resurrected_Date': 'Resurrected Date',
                                    'Lesson_Date':'Lesson Date', 'Unlock_Date': 'Unlock Date'};
        let dataElementContents = {'None':'None', 'Meaning_Brief':'Meaning Brief', 'Meaning_Full':'Meaning Full',
                                   'Reading_Brief':'Reading Brief', 'Reading_Full':'Reading Full', 'Leech':'Leach Value',
                                   'Meaning_Correct_Answers': 'Meaning Correct Answers', 'Reading_Correct_Answers': 'Reading Correct Answers',
                                   'Meaning_Incorrect_Answers': 'Meaning Incorrect Answers', 'Reading_Incorrect_Answers': 'Reading Incorrect Answers',
                                   'Total_Correct_Answers': 'Total Correct Answers','Total_Incorrect_Answers': 'Total Incorrect Answers',
                                   'Percentage_Correct': 'Percentage Correct Total','Meaning_Correct': 'Percentage Correct Meaning','Reading_Correct': 'Percentage Correct Reading',
                                   'Meaning_Current_Streak' : 'Meaning Current Streak', 'Reading_Current_Streak': 'Reading Current Streak',
                                   'Meaning_Max_Streak' : 'Meaning Maximum Streak', 'Reading_Max_Streak': 'Reading Maximum Streak',
                                   'Level':'Level',
                                   'Srs':'SRS Stage', 'Review_Date':'Review Date', 'Review_Wait':'Review Wait Time',
                                   'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Resurrected_Date': 'Resurrected Date',
                                   'Lesson_Date':'Lesson Date', 'Unlock_Date': 'Unlock Date'};
        let sortElementContents = {'Default':'Default', 'Meaning_Brief':'Meaning Brief', 'Meaning_Full':'Meaning Full',
                                   'Reading_Brief':'Reading Brief', 'Reading_Full':'Reading Full', 'Leech':'Leach Value',
                                   'Meaning_Correct_Answers': 'Meaning Correct Answers', 'Reading_Correct_Answers': 'Reading Correct Answers',
                                   'Meaning_Incorrect_Answers': 'Meaning Incorrect Answers', 'Reading_Incorrect_Answers': 'Reading Incorrect Answers',
                                   'Total_Correct_Answers': 'Total Correct Answers','Total_Incorrect_Answers': 'Total Incorrect Answers',
                                   'Percentage_Correct': 'Percentage Correct Total','Meaning_Correct': 'Percentage Correct Meaning','Reading_Correct': 'Percentage Correct Reading',
                                   'Meaning_Current_Streak' : 'Meaning Current Streak', 'Reading_Current_Streak': 'Reading Current Streak',
                                   'Meaning_Max_Streak' : 'Meaning Maximum Streak', 'Reading_Max_Streak': 'Reading Maximum Streak',
                                   'Level':'Level',
                                   'Srs':'SRS Stage', 'Review_Date':'Review Date', 'Review_Wait':'Review Wait Time',
                                   'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Resurrected_Date': 'Resurrected Date',
                                   'Lesson_Date':'Lesson Date', 'Unlock_Date': 'Unlock Date'};
        let wordCloudContents = {'No Repeat': 'Don\'t Repeat', 'Leech':'Leach Value',
                                 'Meaning_Correct_Answers': 'Meaning Correct Answers', 'Reading_Correct_Answers': 'Reading Correct Answers',
                                 'Meaning_Incorrect_Answers': 'Meaning Incorrect Answers', 'Reading_Incorrect_Answers': 'Reading Incorrect Answers',
                                 'Total_Correct_Answers': 'Total Correct Answers','Total_Incorrect_Answers': 'Total Incorrect Answers',
                                 'Percentage_Correct': 'Percentage Correct Total','Meaning_Correct': 'Percentage Correct Meaning','Reading_Correct': 'Percentage Correct Reading',
                                 'Meaning_Current_Streak' : 'Meaning Current Streak', 'Reading_Current_Streak': 'Reading Current Streak',
                                 'Meaning_Max_Streak' : 'Meaning Maximum Streak', 'Reading_Max_Streak': 'Reading Maximum Streak',
                                };

        let exportElementContents = {'None': 'Not Exported', 'Item':'Item', 'Type':'Item Type (Rad, Kan, Voc)', 'Export_Date': 'Export Date',
                                     'Meaning_Brief':'Meaning Brief', 'Meaning_Full':'Meaning Full',
                                     'Reading_Brief':'Reading Brief', 'Reading_Full':'Reading Full', 'Leech':'Leach Value',
                                     'Meaning_Correct_Answers': 'Meaning Correct Answers', 'Meaning_Incorrect_Answers': 'Meaning Incorrect Answers',
                                     'Reading_Correct_Answers': 'Reading Correct Answers', 'Reading_Incorrect_Answers': 'Reading Incorrect Answers',
                                     'Total_Correct_Answers': 'Total Correct Answers','Total_Incorrect_Answers': 'Total Incorrect Answers',
                                     'Percentage_Correct': 'Percentage Correct Total','Meaning_Correct': 'Percentage Correct Meaning','Reading_Correct': 'Percentage Correct Reading',
                                     'Meaning_Current_Streak' : 'Meaning Current Streak', 'Reading_Current_Streak': 'Reading Current Streak',
                                     'Meaning_Max_Streak' : 'Meaning Maximum Streak', 'Reading_Max_Streak': 'Reading Maximum Streak',
                                     'Level':'Level',
                                     'Srs':'SRS Stage', 'Review_Date':'Review Date',
                                     'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Resurrected_Date': 'Resurrected Date',
                                     'Lesson_Date':'Lesson Date', 'Unlock_Date': 'Unlock Date'};

        var exportTabConfig = {type:'page',label:'Export',hover_tip:'Define the exported items of your table',
                               content:{
                                   sect_tbl_export:{type:'section',label:'Exported Columns'},
                                   includeTitle: {type: 'checkbox', label:'Include a Title Line', default: false, hover_tip: 'Adds a title line before the exported items',
                                                  path:'@tablePresets[@active_ipreset].includeTitle', },
                                   export1: {type:'dropdown',label:'Exported Column no 1',hover_tip:'The 1st column of exported information', default:'None',
                                             path:'@tablePresets[@active_ipreset].export1', content:exportElementContents,
                                            },
                                   export2: {type:'dropdown',label:'Exported Column no 2',hover_tip:'The 2nd column of exported information', default:'None',
                                             path:'@tablePresets[@active_ipreset].export2', content:exportElementContents,
                                            },
                                   export3: {type:'dropdown',label:'Exported Column no 3',hover_tip:'The 3rd column of exported information', default:'None',
                                             path:'@tablePresets[@active_ipreset].export3', content:exportElementContents,
                                            },
                                   export4: {type:'dropdown',label:'Exported Column no 4',hover_tip:'The 4th column of exported information', default:'None',
                                             path:'@tablePresets[@active_ipreset].export4', content:exportElementContents,
                                            },
                                   export5: {type:'dropdown',label:'Exported Column no 5',hover_tip:'The 5th column of exported information', default:'None',
                                             path:'@tablePresets[@active_ipreset].export5', content:exportElementContents,
                                            },
                                   export6: {type:'dropdown',label:'Exported Column no 6',hover_tip:'The 6th column of exported information', default:'None',
                                             path:'@tablePresets[@active_ipreset].export6', content:exportElementContents,
                                            },
                                   export7: {type:'dropdown',label:'Exported Column no 7',hover_tip:'The 7th column of exported information', default:'None',
                                             path:'@tablePresets[@active_ipreset].export7', content:exportElementContents,
                                            },
                                   export8: {type:'dropdown',label:'Exported Column no 8',hover_tip:'The 8th column of exported information', default:'None',
                                             path:'@tablePresets[@active_ipreset].export8', content:exportElementContents,
                                            },
                                   export9: {type:'dropdown',label:'Exported Column no 9',hover_tip:'The 9th column of exported information', default:'None',
                                             path:'@tablePresets[@active_ipreset].export9', content:exportElementContents,
                                            },
                                   export10: {type:'dropdown',label:'Exported Column no 10',hover_tip:'The 10th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export10', content:exportElementContents,
                                             },
                                   export11: {type:'dropdown',label:'Exported Column no 11',hover_tip:'The 11th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export11', content:exportElementContents,
                                             },
                                   export12: {type:'dropdown',label:'Exported Column no 12',hover_tip:'The 12th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export12', content:exportElementContents,
                                             },
                                   export13: {type:'dropdown',label:'Exported Column no 13',hover_tip:'The 13th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export13', content:exportElementContents,
                                             },
                                   export14: {type:'dropdown',label:'Exported Column no 14',hover_tip:'The 14th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export14', content:exportElementContents,
                                             },
                                   export15: {type:'dropdown',label:'Exported Column no 15',hover_tip:'The 15th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export15', content:exportElementContents,
                                             },
                                   export16: {type:'dropdown',label:'Exported Column no 16',hover_tip:'The 16th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export16', content:exportElementContents,
                                             },
                                   export17: {type:'dropdown',label:'Exported Column no 17',hover_tip:'The 17th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export17', content:exportElementContents,
                                             },
                                   export18: {type:'dropdown',label:'Exported Column no 18',hover_tip:'The 18th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export18', content:exportElementContents,
                                             },
                                   export19: {type:'dropdown',label:'Exported Column no 19',hover_tip:'The 19th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export19', content:exportElementContents,
                                             },
                                   export20: {type:'dropdown',label:'Exported Column no 20',hover_tip:'The 20th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export20', content:exportElementContents,
                                             },
                                   export21: {type:'dropdown',label:'Exported Column no 21',hover_tip:'The 21th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export21', content:exportElementContents,
                                             },
                                   export22: {type:'dropdown',label:'Exported Column no 22',hover_tip:'The 22th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export22', content:exportElementContents,
                                             },
                                   export23: {type:'dropdown',label:'Exported Column no 23',hover_tip:'The 23th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export23', content:exportElementContents,
                                             },
                                   export24: {type:'dropdown',label:'Exported Column no 24',hover_tip:'The 24th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export24', content:exportElementContents,
                                             },
                                   export25: {type:'dropdown',label:'Exported Column no 25',hover_tip:'The 25th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export25', content:exportElementContents,
                                             },
                                   export26: {type:'dropdown',label:'Exported Column no 26',hover_tip:'The 26th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export26', content:exportElementContents,
                                             },
                                   export27: {type:'dropdown',label:'Exported Column no 27',hover_tip:'The 27th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export27', content:exportElementContents,
                                             },
                                   export28: {type:'dropdown',label:'Exported Column no 28',hover_tip:'The 28th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export28', content:exportElementContents,
                                             },
                                   export29: {type:'dropdown',label:'Exported Column no 29',hover_tip:'The 29th column of exported information', default:'None',
                                              path:'@tablePresets[@active_ipreset].export29', content:exportElementContents,
                                             },
                               },
                              }

        var contentsTabConfig = {type:'page',label:'Contents',hover_tip:'Define the contents of your table',
                                 content:{
                                     sect_tbl_cnts:{type:'section',label:'Table Entry'},
                                     table_data: {type:'dropdown',label:'Table Data Element',hover_tip:'The data that will be displayed on the table.',
                                                  path:'@tablePresets[@active_ipreset].table_data',
                                                  content:tableElementContents,
                                                 },
                                     sort1: {type:'dropdown',label:'Primary Sort Criterion',hover_tip:'Items will be sorted by this criterion.',
                                             path:'@tablePresets[@active_ipreset].sort1',
                                             content:sortElementContents,
                                            },
                                     sortOrder1: {type:'dropdown',label:'Primary Sort Order',hover_tip:'Items will be sorted in this order.',
                                                  path:'@tablePresets[@active_ipreset].sortOrder1',
                                                  content:{'Default': 'Default', 'Ascending': 'Ascending', 'Descending': 'Descending',},
                                                 },
                                     sort2: {type:'dropdown',label:'Secondary Sort Criterion',hover_tip:'Items will be sorted by this criterion when the primary criterion is of equal values.',
                                             path:'@tablePresets[@active_ipreset].sort2',
                                             content:sortElementContents,
                                            },
                                     sortOrder2: {type:'dropdown',label:'Secondary Sort Order',hover_tip:'Items will be sorted in this order when the primary criterion is of equal values.',
                                                  path:'@tablePresets[@active_ipreset].sortOrder2',
                                                  content:{'Default': 'Default', 'Ascending': 'Ascending', 'Descending': 'Descending',},
                                                 },
                                     displayMeaning: {type: 'checkbox', label:'Show Items by Their Meanings', hover_tip:'Display the items by their English meanings',
                                                      path:'@tablePresets[@active_ipreset].displayMeaning', default: false, },
                                     sect_tbl_tooltips:{type:'section',label:'Table Popups'},
                                     tooltip1: {type:'dropdown',label:'First Popup Element',hover_tip:'The first line of data that will be displayed on the popup.',
                                                path:'@tablePresets[@active_ipreset].tooltip1', default:'None',
                                                content:dataElementContents,
                                               },
                                     tooltip2: {type:'dropdown',label:'Second Popup Element',hover_tip:'The second line of data that will be displayed on the popup.',
                                                path:'@tablePresets[@active_ipreset].tooltip2', default:'None',
                                                content:dataElementContents,
                                               },
                                     tooltip3: {type:'dropdown',label:'Third Popup Element',hover_tip:'The third line of  data that will be displayed on the popup.',
                                                path:'@tablePresets[@active_ipreset].tooltip3', default:'None',
                                                content:dataElementContents,
                                               },
                                     tooltip4: {type:'dropdown',label:'Fourth Popup Element',hover_tip:'The fourth line of  data that will be displayed on the popup.',
                                                path:'@tablePresets[@active_ipreset].tooltip4', default:'None',
                                                content:dataElementContents,
                                               },
                                     tooltip5: {type:'dropdown',label:'Fifth Popup Element',hover_tip:'The fifth line of  data that will be displayed on the popup.',
                                                path:'@tablePresets[@active_ipreset].tooltip5', default:'None',
                                                content:dataElementContents,
                                               },
                                     tooltip6: {type:'dropdown',label:'Sixth Popup Element',hover_tip:'The sixth line of  data that will be displayed on the popup.',
                                                path:'@tablePresets[@active_ipreset].tooltip6', default:'None',
                                                content:dataElementContents,
                                               },
                                     sect_tbl_settings:{type:'section',label:'Other Settings'},
                                     enlargingTooltip: {type: 'checkbox', label:'Popup for Enlarged Items', hover_tip:'Adds a popup in tables at the right of the item to show an enlarged version of the item',
                                                        path:'@tablePresets[@active_ipreset].enlargingTooltip', default: false, },
                                     showMarkers: {type: 'checkbox', label:'Show Markers', hover_tip:'Show the markers for the table data in list of icons markers',
                                                   path:'@tablePresets[@active_ipreset].showMarkers', default: true, },
                                     showHours: {type: 'checkbox', label:'Show Hours in Markers', hover_tip:'Show the hours and minutes for dates in list of icons markers',
                                                 path:'@tablePresets[@active_ipreset].showHours', default: false, },
                                     randomSelection: {type:'number',label:'Random Selection',hover_tip:'A non zero value returns a random selection of the stated number of items.\n0 returns all items.',
                                                       path:'@tablePresets[@active_ipreset].randomSelection', default:0
                                                      },
                                     leechStreakLimit: {type:'number',label:'Leech Streak Limit',hover_tip:'Do not display an item when current streak for both meaning and reading is equal or greater to this limit.\nA value of 0 disable this feature',
                                                        path:'@tablePresets[@active_ipreset].leechStreakLimit', default:0
                                                       },
                                 },
                                };

        var settingsTabConfig ={type:'page',label:'Settings',
                                     content:{
                                        sect_tbl_cnts:{type:'section',label:'Interface Configuration'},
                                        position: {type: 'dropdown', label: 'Position', default: 2, hover_tip: 'Where on the dashboard to install Item Inspector',
                                                   content: {0: "Top", 1: "Below forecast", 2: "Below SRS", 3: "Below panels", 4: "Bottom"},
                                                  },
                                        hoursFormat: {type:'dropdown',label:'Hours Format',hover_tip:'Choose the format for displaying hours',content:{'12hours':'12h', '24hours':'24h',},
                                                      default: '24h',},
                                        listMode: {type: 'checkbox', label:'Display as List', hover_tip:'Display the table as a list of icons',
                                                   default: false, },
                                        numberOfLines: {type:'dropdown',label:'Number of Lines',hover_tip:'The number of lines that will be displayed in a table.', default:11,
                                                        content:{8:"8", 9:"9", 10:"10", 11:"11", 12:"12", 13:"13", 14:"14", 15:"15",},
                                                       },
                                        audioSource: {type:'dropdown',label:'Source of Audio',hover_tip:'Which audio is played in audio mode.', default:'random',
                                                        content:{'male': 'Male, Kenichi','female': 'Female, Kyoko', 'random': 'Random',},
                                                       },
                                        sect_tbl_cnts2:{type:'section',label:'Items Export to Clipboard'},
                                        noLatin: {type: 'checkbox', label:'No Latin Characters', default: false, hover_tip:'Radicals with latin characters not exported if set',},
                                        oneItemPerLine: {type: 'checkbox', label:'One item per line', default: false, hover_tip: 'One item per line if set\nAll items in one paragraph otherwise',},
                                        exportLimit: {type: 'number', label:'Export Limit', default: 0, hover_tip: 'Maximum number of items exported\n0 means no limit',},
                                        repeatWordCloud: {type: 'dropdown', label:'Repeat for Word Cloud', default: 'No Repeat', hover_tip: 'Repeat the items according this number',
                                                          content:wordCloudContents,},
                                       }
                                 }

        var config = {
            script_id: scriptId,
            title: 'Item Inspector',
            pre_open: preopen_quiz_settings,
            on_save: save_quiz_settings,
            on_close: close_quiz_settings,
            on_refresh: refresh_quiz_settings,
            no_bkgd: true,
            settings: {pgSettings: settingsTabConfig,
                       pg_items: {type:'page',label:'Tables',hover_tip:'Choose the table for which you want to define the settings',
                                   content:{
                                        grp_ipre_list: {type:'group',label:'Table List',content:{
                                            active_ipreset: {type:'list',refresh_on_change:true,hover_tip:'Choose a table to edit',content:{}},
                                        }},
                                        grp_ipre: {type:'group',label:'Selected Table',
                                                   content:{
                                                       sect_ipre_name: {type:'section',label:'Table Name'},
                                                       ipre_name: {type:'text',label:'Edit Table Name',on_change:refresh_ipresets,path:'@ipresets[@active_ipreset].name',hover_tip:'Enter a name for the selected table'},

                                                       sect_ipre_srcs: {type:'section',label:'Table Settings'},
                                                       ipre_srcs: {type:'tabset',
                                                                   content:{table_contents: contentsTabConfig, table_export: exportTabConfig},
                                                                  }
                                                   },
                                                   }
                                     },
                                  },
                       }};

        populate_items_config(config);

        quiz.settings_dialog = new wkof.Settings(config);

    }

    //========================================================================
    // preopen_quiz_settings()
    //------------------------------------------------------------------------
    function preopen_quiz_settings(dialog) {
        var btn_grp =
            '<div class="pre_list_btn_grp">'+
            '<button type="button" ref="###" action="new" class="ui-button ui-corner-all ui-widget" title="Create a new table">New</button>'+
            '<button type="button" ref="###" action="up" class="ui-button ui-corner-all ui-widget" title="Move the selected table up in the list"><span class="icon-arrow-up"></span></button>'+
            '<button type="button" ref="###" action="down" class="ui-button ui-corner-all ui-widget" title="Move the selected table down in the list"><span class="icon-arrow-down"></span></button>'+
            '<button type="button" ref="###" action="delete" class="ui-button ui-corner-all ui-widget" title="Delete the selected table">Delete</button>'+
            '</div>';

        var wrap = dialog.find('#Item_Inspector_active_ipreset').closest('.row');
        wrap.addClass('pre_list_wrap');
        wrap.prepend(btn_grp.replace(/###/g, 'ipreset'));
        wrap.find('.pre_list_btn_grp').on('click', 'button', preset_button_pressed);

        $('#Item_Inspector_ipre_srcs .row:first-child').each(function(i,e){
            var row = $(e);
            var right = row.find('>.right');
            row.prepend(right);
            row.addClass('src_enable');
        });

        // Customize the item source filters.
        var srcs = $('#Item_Inspector_ipre_srcs');
        var flt_grps = srcs.find('.wkof_group');
        flt_grps.addClass('filters');
        var filters = flt_grps.find('.row');
        filters.prepend('<div class="enable"><input type="checkbox"></div>');
        filters.on('change', '.enable input[type="checkbox"]', toggle_filter);

        init_settings();
        refresh_ipresets();
    }

    //========================================================================
    // open_quiz_settings()
    //------------------------------------------------------------------------
    var old_position;
    function open_quiz_settings() {
        if (quiz_settings_state !== 'ready') return call_setup_quiz_settings();
        quiz_settings_state = 'open';
        var backup = {};
        quiz.backup = backup;
        //backup.max_quiz_size = quiz.settings.max_quiz_size;
        backup.ipre = JSON.stringify(quiz.settings.ipresets[quiz.settings.active_ipreset].content);
        old_position = quiz.settings.position;
        quiz.settings_dialog.open();

        function call_setup_quiz_settings(){
            setup_quiz_settings();
            wkof.Settings.load(scriptId)
                .then(function(){
                quiz_settings_state = 'ready';
                open_quiz_settings();
            });
        };
    }

    //========================================================================
    // save_quiz_settings()
    //------------------------------------------------------------------------
    function save_quiz_settings(settings) {
        quiz.settings = settings;
        populate_presets($('#Item_Inspector_source'), settings.ipresets, settings.active_ipreset);
        quiz.settings.tablePresets[quiz.settings.active_ipreset].currentItem = currentItem = 0;
        quiz.settings.tablePresets[quiz.settings.active_ipreset].nextCurrentItem = nextCurrentItem = 0;
        quiz.settings.tablePresets[quiz.settings.active_ipreset].itemHistory = itemHistory = [];
        // check if the table list is empty
        if (!quiz.settings.ipresets.length){
            init_settings(); // restore defaults
        };

        fetch_items('table')
            .then(function(){
            initCurrentItem();
            setNumberOfLines();
            populateDropdown();
            if (old_position != quiz.settings.position){
                if (document.getElementById("WkitTopBar")){
                    $('#WkitTopBar').empty();
                    $('#WkitTopBar').remove();
                };
                insertContainer();
                eventHandlers();
            };
            updatePage();
        })
    }

    //========================================================================
    // close_quiz_settings()
    //------------------------------------------------------------------------
    function close_quiz_settings(settings) {
        quiz_settings_state = 'setup';
        quiz.settings = wkof.settings[scriptId]; // working around an obscure bug
    }

    //========================================================================
    // refresh_quiz_settings()
    //------------------------------------------------------------------------
    function refresh_quiz_settings(settings) {
        $('#Item_Inspector_ipre_srcs .wkof_group .row').each(function(i,e){
            var row = $(e);
            var panel = row.closest('[role="tabpanel"]');
            var source = panel.attr('id').match(/^Item_Inspector_pg_(.*)$/)[1];
            var filter_name = row.find('.setting').attr('name').slice((source+'_flt_').length);
            var preset = quiz.settings.ipresets[quiz.settings.active_ipreset].content;
            var enabled = false;
            try {
                enabled = preset[source].filters[filter_name].enabled;
            } catch(e) {}

            if (enabled) {
                row.addClass('checked');
            } else {
                row.removeClass('checked');
            }
            row.find('.enable input[type="checkbox"]').prop('checked', enabled);
        });
    }

    //========================================================================
    // refresh_ipresets()
    //------------------------------------------------------------------------
    function refresh_ipresets() {
        var settings = quiz.settings;
        populate_presets($('#Item_Inspector_active_ipreset'), settings.ipresets, settings.active_ipreset);
    }

    //========================================================================
    // preset_button_pressed()
    //------------------------------------------------------------------------
    function preset_button_pressed(e) {
        var settings = quiz.settings;
        var tablePresets = settings.tablePresets;
        var ref = e.currentTarget.attributes.ref.value;
        var action = e.currentTarget.attributes.action.value;
        var selected = Number(settings['active_'+ref]);
        var presets = settings[ref+'s'];
        var elem = $('#Item_Inspector_active_'+ref);

        var dflt;
        dflt = {name:'<untitled>', content:$.extend(true, {}, ipre_defaults)};

        switch (action) {
            case 'new':
                presets.push(dflt);
                tablePresets.push(table_defaults);
                selected = presets.length - 1;
                settings[ref+'s'] = presets;
                settings['active_'+ref] = selected;
                settings.tablePresets = tablePresets;
                populate_presets(elem, presets, selected);
                quiz.settings_dialog.refresh();
                $('#Item_Inspector_'+ref.slice(0,4)+'_name').focus().select();
                break;

            case 'up':
                if (selected <= 0) break;
                presets = [].concat(presets.slice(0, selected-1), presets[selected], presets[selected-1], presets.slice(selected+1));
                tablePresets = [].concat(tablePresets.slice(0, selected-1), tablePresets[selected], tablePresets[selected-1], tablePresets.slice(selected+1));
                selected--;
                settings[ref+'s'] = presets;
                settings['active_'+ref] = selected;
                settings.tablePresets = tablePresets;
                populate_presets(elem, presets, selected);
                break;

            case 'down':
                if (selected >= presets.length-1) break;
                presets = [].concat(presets.slice(0, selected), presets[selected+1], presets[selected], presets.slice(selected+2));
                tablePresets = [].concat(tablePresets.slice(0, selected), tablePresets[selected+1], tablePresets[selected], tablePresets.slice(selected+2));
                selected++;
                settings[ref+'s'] = presets;
                settings['active_'+ref] = selected;
                settings.tablePresets = tablePresets;
                populate_presets(elem, presets, selected);
                break;

            case 'delete':
                presets = presets.slice(0, selected).concat(presets.slice(selected+1));
                tablePresets = tablePresets.slice(0, selected).concat(tablePresets.slice(selected+1));
                selected = Math.max(0, selected-1);
                settings[ref+'s'] = presets;
                settings['active_'+ref] = selected;
                settings.tablePresets = tablePresets;
                populate_presets(elem, presets, selected);
                quiz.settings_dialog.refresh();
                break;
        }
    }

    //========================================================================
    // init_settings()
    //------------------------------------------------------------------------
    var table_defaults;
    function init_settings() {
        var idx;
        // Merge some defaults
        var defaults = {
        };
        var settings = $.extend(true, {}, defaults, wkof.settings.Item_Inspector);
        wkof.settings.Item_Inspector = quiz.settings = settings;
        /*var settings;
        settings = quiz.settings = wkof.settings.Item_Inspector;*/


        let ipresets_defaults = [
            {name:'Leeches', content:{wk_items:{enabled:true,filters:{srs:{enabled:true,value:{appr1:true,appr2:true,appr3:true,appr4:true,guru1:true,guru2:true,mast:true}}
                                                                      ,additionalFilters_leechTraining:{enabled:true,value:1}}},
                                      tableContents:{currentItem:0,table_data:"Leech",sort1:"Default",sortOrder1:'Default',sort2:"Default",sortOrder2:'Default',
                                                     tooltip1:"Meaning_Full",tooltip2:"Reading_Full",tooltip3:"None",tooltip4:"None",tooltip5:"None",
                                                     tooltip6:"None",displayMeaning:false,enlargingTooltip:true,showMarkers:true,showHours:false,randomSelection:0,leechStreakLimit:0,
                                                     includeTitle:false}}},
            {name:'Failed Last Review', content:{wk_items:{enabled:true,filters:{additionalFilters_failedLastReview:{enabled:true,value:24}}},
                                                 tableContents:{currentItem:0,table_data:"Leech",sort1:"Default",sortOrder1:'Default',sort2:"Default",sortOrder2:'Default',
                                                                tooltip1:"Meaning_Full",tooltip2:"Reading_Full",tooltip3:"None",tooltip4:"None",tooltip5:"None",
                                                                tooltip6:"None",displayMeaning:false,enlargingTooltip:true,showMarkers:true,showHours:false,randomSelection:0,leechStreakLimit:0,
                                                                includeTitle:false,}}},
            {name:'Current Level SRS', content:{wk_items:{enabled:true,filters:{level:{enabled:true,value:"+0"},srs:{enabled:true,value:{appr1:true,appr2:true,appr3:true,appr4:true,guru1:true,guru2:true,mast:true,enli:true}}}},
                                                tableContents:{currentItem:0,table_data:"Srs",sort1:"Default",sortOrder1:'Default',sort2:"Default",sortOrder2:'Default',
                                                               tooltip1:"Review_Date",tooltip2:"Review_Wait",tooltip3:"None",tooltip4:"None",tooltip5:"None",
                                                               tooltip6:"None",displayMeaning:false,enlargingTooltip:false,showMarkers:true,showHours:false,randomSelection:0,leechStreakLimit:0,
                                                               includeTitle:false,}}},
            {name:'Previous Level SRS', content:{wk_items:{enabled:true,filters:{level:{enabled:true,value:"-1"},srs:{enabled:true,value:{appr1:true,appr2:true,appr3:true,appr4:true,guru1:true,guru2:true,mast:true,enli:true}}}},
                                                 tableContents:{currentItem:0,table_data:"Srs",sort1:"Default",sortOrder1:'Default',sort2:"Default",sortOrder2:'Default',
                                                                tooltip1:"Review_Date",tooltip2:"Review_Wait",tooltip3:"None",tooltip4:"None",tooltip5:"None",
                                                                tooltip6:"None",displayMeaning:false,enlargingTooltip:false,showMarkers:true,showHours:false,randomSelection:0,leechStreakLimit:0,
                                                                includeTitle:false,}}},
        ];
        table_defaults = {currentItem:0,table_data:"Leech",sort1:"Default",sortOrder1:'Default',sort2:"Default",sortOrder2:'Default',
                          tooltip1:"Meaning_Full",tooltip2:"Reading_Full",tooltip3:"None",tooltip4:"None",tooltip5:"None",tooltip6:"None",
                          displayMeaning:false,enlargingTooltip:false,showMarkers:true,showHours:false,randomSelection:0,leechStreakLimit:0,includeTitle:false,};
        for (var i = 0; i < exportedInfo.length; i++){
            table_defaults[exportedInfo[i]] = 'None';
        };
        // define defaults if presets not yet defined
        if (settings.ipresets === undefined) {
            settings.ipresets = ipresets_defaults;
            settings.active_ipreset = 0;
            settings.tablePresets = init_table_presets(ipresets_defaults);
        };
        // restore defaults when ipresets list is empty
        if (!settings.ipresets.length) {
            settings.ipresets = ipresets_defaults;
            settings.active_ipreset = 0;
            settings.tablePresets = init_table_presets(ipresets_defaults);
        };
        if (ipre_defaults) {
            for (idx in settings.ipresets) {
                settings.ipresets[idx].content = $.extend(true, {}, ipre_defaults, settings.ipresets[idx].content);
            }
        }

        function init_table_presets(ipresets_defaults){
            var x = [];
            for (var y of ipresets_defaults){x.push(y.content.tableContents)};
            return x;
        }
    }

    //========================================================================
    // populate_items_config()
    //------------------------------------------------------------------------
    var ipre_defaults;
    function populate_items_config(config) {
        var ipre_srcs = config.settings.pg_items.content.grp_ipre.content.ipre_srcs.content;
        var srcs = wkof.ItemData.registry.sources;
        ipre_defaults = {};
        let src_name = 'wk_items';
        var src = srcs[src_name];
        var pg_content = {};
        ipre_srcs['pg_'+src_name] = {type:'page',label:'Filters',content:pg_content};
        var settings = {};
        ipre_defaults[src_name] = settings;

        // Add 'Filters' section.
        if (src.filters && Object.keys(src.filters).length > 0) {
            settings.filters = {};
            var flt_content = {};
            pg_content['grp_'+src_name+'_filters'] = {type:'group',label:'',content:flt_content};
            for (var flt_name in src.filters) {
                var flt = src.filters[flt_name];
                settings.filters[flt_name] = {enabled:false, value:flt.default};
                switch (flt.type) {
                    case 'checkbox':
                        flt_content[src_name+'_flt_'+flt_name] = {
                            type:'checkbox',
                            label:flt.label,
                            default:flt.default,
                            path:'@ipresets[@active_ipreset].content["'+src_name+'"].filters["'+flt_name+'"].value',
                            hover_tip:flt.hover_tip
                        }
                        break;
                    case 'multi':
                        var dflt = flt.default;
                        if (typeof flt.filter_value_map === 'function') dflt = flt.filter_value_map(dflt);
                        flt_content[src_name+'_flt_'+flt_name] = {
                            type:'list',
                            multi:true,
                            size:Math.min(4,Object.keys(flt.content).length),
                            label:flt.label,
                            content:flt.content,
                            default:dflt,
                            path:'@ipresets[@active_ipreset].content["'+src_name+'"].filters["'+flt_name+'"].value',
                            hover_tip:flt.hover_tip
                        }
                        settings.filters[flt_name].value = dflt;
                        break;
                    case 'text':
                    case 'number':
                    case 'input':
                        flt_content[src_name+'_flt_'+flt_name] = {
                            type:flt.type,
                            label:flt.label,
                            placeholder:flt.placeholder,
                            default:flt.default,
                            path:'@ipresets[@active_ipreset].content["'+src_name+'"].filters["'+flt_name+'"].value',
                            hover_tip:flt.hover_tip
                        }
                        break;
                    case 'button':
                        flt_content[src_name+'_flt_'+flt_name] = {
                            type:flt.type,
                            label:flt.label,
                            on_click:flt.on_click,
                            hover_tip:flt.hover_tip
                        }
                        break;
                }
            }
        }
    }

    //========================================================================
    // toggle_filter()
    //------------------------------------------------------------------------
    function toggle_filter(e) {
        var row = $(e.delegateTarget);
        var panel = row.closest('[role="tabpanel"]');
        var source = panel.attr('id').match(/^Item_Inspector_pg_(.*)$/)[1];
        var enabled = row.find('.enable input[type="checkbox"]').prop('checked');
        var preset = quiz.settings.ipresets[quiz.settings.active_ipreset].content;
        var filter_name = row.find('.setting').attr('name').slice((source+'_flt_').length);

        if (enabled) {
            row.addClass('checked');
        } else {
            row.removeClass('checked');
        }
        try {
            preset[source].filters[filter_name].enabled = enabled;
        } catch(e) {}
    }

    //========================================================================
    // populate_presets()
    //------------------------------------------------------------------------
    function populate_presets(elem, presets, active_preset) {
        var html = '';
        for (var idx in presets) {
            var preset = presets[idx];
            var name = preset.name.replace(/</g,'&lt;').replace(/>/g,'&gt;');
            html += '<option name="'+idx+'">'+name+'</option>';
        }
        var elem_name = elem.attr('id')
        if (elem_name === 'Item_Inspector_source' && quiz.custom.has_ipreset) {
            html += '<option name="custom">('+quiz.custom.ipreset.name+')</option>';
            if (quiz.custom.using_ipreset) active_preset = presets.length;
        }
        elem.html(html);
        elem.children().eq(active_preset).prop('selected', true);

    }

    //========================================================================
    // add_defaults()
    //------------------------------------------------------------------------
    function add_defaults(){
        // Add the settings that were added in new versions
        let settings = quiz.settings;
        if (settings.hoursFormat == undefined){settings.hoursFormat = '24h'};
        if (settings.position == undefined){settings.position = 2};
        if (settings.numberOfLines == undefined){settings.numberOfLines = 11};
        if (settings.listMode == undefined){settings.listMode = false};
        if (settings.audioSource == undefined){settings.audioSource = 'random'};
        if (settings.audioMode == undefined){settings.audioMode = false};
        if (settings.noLatin == undefined){settings.noLatin = false};
        if (settings.oneItemPerLine == undefined){settings.oneItemPerLine = false};
        if (settings.exportLimit == undefined){settings.exportLimit = 0};
        if (settings.repeatWordCloud == undefined){settings.repeatWordCloud = 'No Repeat'};

        let tablePresets = quiz.settings.tablePresets;
        for (var i = 0; i < tablePresets.length; i++){
            for (var settingName in tablePresets[i]){
                var setting = tablePresets[i][settingName];
                if (setting == 'Review_Date12' || setting == 'Review_Date24' ) {tablePresets[i][settingName] = 'Review_Date'};
            };

            if (tablePresets[i].sort1 == undefined){tablePresets[i].sort1 = 'Default'};
            if (tablePresets[i].sortOrder1 == undefined){tablePresets[i].sortOrder1 = 'Default'};
            if (tablePresets[i].sort2 == undefined){tablePresets[i].sort2 = 'Default'};
            if (tablePresets[i].sortOrder2 == undefined){tablePresets[i].sortOrder2 = 'Default'};
            if (tablePresets[i].tooltip4 == undefined){tablePresets[i].tooltip4 = 'None'};
            if (tablePresets[i].tooltip5 == undefined){tablePresets[i].tooltip5 = 'None'};
            if (tablePresets[i].tooltip6 == undefined){tablePresets[i].tooltip6 = 'None'};
            if (tablePresets[i].displayMeaning == undefined){tablePresets[i].displayMeaning = false};
            if (tablePresets[i].enlargingTooltip == undefined){tablePresets[i].enlargingTooltip = false};
            if (tablePresets[i].showMarkers == undefined){tablePresets[i].showMarkers = true};
            if (tablePresets[i].showHours == undefined){tablePresets[i].showHours = false};
            if (tablePresets[i].randomSelection == undefined){tablePresets[i].randomSelection = 0};
            if (tablePresets[i].leechStreakLimit == undefined){tablePresets[i].leechStreakLimit = 0};
            if (tablePresets[i].includeTitle == undefined){tablePresets[i].includeTitle = 0};
            if (tablePresets[i].currentItem == undefined){tablePresets[i].currentItem = 0};
            if (tablePresets[i].nextCurrentItem == undefined){tablePresets[i].nextCurrentItem = 0};
            if (tablePresets[i].itemHistory == undefined){tablePresets[i].itemHistory = []};
            for (var j = 0; j < exportedInfo.length; j++){
                if(tablePresets[i][exportedInfo[j]] == undefined) tablePresets[i][exportedInfo[j]] = 'None';
            };
            if (quiz.settings.repeatWordCloud === "Don\'t Reteat"){quiz.settings.repeatWordCloud = 'No Repeat'}; //fixing the consequence of a bug
            function a (){ return quiz.settings_dialog.save()}
            a();
        }
    }

    //########################################################################
    // QUIZ DIALOG
    //########################################################################

    //========================================================================
    // install_css()
    //------------------------------------------------------------------------
    function install_css() {
        $('head').append(
            '<style id="Item_Inspector_css" type="text/css">'+

            //--[ Settings dialog ]-------------------------------------------
            '#wkof_ds div[role="dialog"][aria-describedby="wkofs_Item_Inspector"] {z-index:12002;}'+

            '#wkofs_Item_Inspector.wkof_settings .pre_list_btn_grp {width:60px;float:left;margin-right:2px;}'+
            '#wkofs_Item_Inspector.wkof_settings .pre_list_btn_grp button {width:100%; padding:2px 0;}'+
            '#wkofs_Item_Inspector.wkof_settings .pre_list_btn_grp button:not(:last-child) {margin-bottom:2px;}'+
            '#wkofs_Item_Inspector.wkof_settings .pre_list_wrap {display:flex;}'+
            '#wkofs_Item_Inspector.wkof_settings .pre_list_wrap .right {flex:1;}'+
            '#wkofs_Item_Inspector.wkof_settings .pre_list_wrap .list {overflow:auto;height:100%;}'+

            '#wkofs_Item_Inspector.wkof_settings .filters .row {border-top:1px solid #ccc; padding:6px 4px; margin-bottom:0;}'+
            '#wkofs_Item_Inspector.wkof_settings .filters .row:not(.checked) {padding-top:0px;padding-bottom:0px;}'+
            '#wkofs_Item_Inspector .filters .row .enable input[type="checkbox"] {margin:0;}'+
            '#wkofs_Item_Inspector.narrow .filters .row.checked .right input[type="checkbox"]:after {content:"⇐yes?";margin-left:28px;line-height:30px;}'+
            '#wkofs_Item_Inspector .filters .row.checked {background-color:#f7f7f7;}'+
            '#wkofs_Item_Inspector .filters .row:not(.checked) {opacity:0.5;}'+
            '#wkofs_Item_Inspector .filters .row .enable {display:inline; margin:0; float:left;}'+
            '#wkofs_Item_Inspector:not(.narrow) .filters .left {width:170px;}'+

            '#wkofs_Item_Inspector .filters .row .enable input[type="checkbox"] {margin:0 4px 0 0;}'+
            '#wkofs_Item_Inspector .filters .row:not(.checked) .right {display:none;}'+
            '#wkofs_Item_Inspector .filters .row:not(.checked) .left label {text-align:left;}'+
            '#wkofs_Item_Inspector.narrow .filters .row .left {width:initial;}'+
            '#wkofs_Item_Inspector.narrow .filters .row .left label {line-height:30px;}'+
            '#wkofs_Item_Inspector #Item_Inspector_ipre_srcs .src_enable .left {width:initial;}'+
            '#wkofs_Item_Inspector #Item_Inspector_ipre_srcs .src_enable .left label {text-align:left;width:initial;line-height:30px;}'+
            '#wkofs_Item_Inspector #Item_Inspector_ipre_srcs .src_enable .right {float:left; margin:0 4px;width:initial;}'+
            //----------------------------------------------------------------

            '</style>'
        );
    }

    //========================================================================
    // fetch_items()
    //------------------------------------------------------------------------
    function fetch_items(required) {
        var settings = quiz.settings;
        var ipreset = settings.ipresets[settings.active_ipreset].content;

        //set_mode('loading');
        var config = {};
        for (var src_name in ipreset) {
            var src_preset = ipreset[src_name];
            //if (!src_preset.enabled) continue;
            if (!wkof.ItemData.registry.sources[src_name]) continue;
            var src_cfg = {};
            config[src_name] = src_cfg;
            src_cfg.filters = {};
            //if (src_name === 'wk_items') src_cfg.options = {study_materials: true};
            if (src_name === 'wk_items') src_cfg.options = {};
            var ipre_filters = src_preset.filters;
            for (var flt_name in ipre_filters) {
                var ipre_flt = ipre_filters[flt_name];
                if (!ipre_flt.enabled) continue;
                if (!wkof.ItemData.registry.sources[src_name].filters[flt_name]) continue;
                src_cfg.filters[flt_name] = {value: ipre_flt.value};
                if (ipre_flt.invert === true) src_cfg.filters[flt_name].invert = true;
            }
        }

        let presets = quiz.settings.tablePresets[settings.active_ipreset];
        for (var settingName in presets){
            var setting = presets[settingName];
            if ((setting != 'None') && (setting != 'Default')) {
                if (metadata[setting] != undefined){
                    if (required === 'export'){
                        src_cfg.options[metadata[setting].endPoint] = true;
                    } else if (!exportedInfo.includes(settingName)){
                        src_cfg.options[metadata[setting].endPoint] = true;
                    };
                };
            };
        }
        if (presets.leechStreakLimit != 0) {src_cfg.options.review_statistics = true};
        if (required === 'wordCloud') {src_cfg.options.review_statistics = true};

        return wkof.ItemData.get_items(config)
            .then(function(items){
            quiz.items = items;
        });
    }


    //------------------------------
    // Calculating displayed data
    //------------------------------

    var theFuture = Date.parse("01 Jan 2090 00:00:00 GMT");
    var infinity = 100000000; // big enough
    var srsName = ["Initiate","Apprentice I","Apprentice II","Apprentice III","Apprentice IV","Guru I","Guru II","Master","Enlightened","Burned"];

    var metadata = {'Meaning_Brief': {'exists': ((item) => {return true}), 'label': 'Meaning ',
                                      'tableEntry': meaningsBrief,
                                      'tableEntryMarker': ((item)=>''),
                                      'tooltipEntry': meaningsBrief,
                                      'sortkey': ((item) => {return 0}),
                                      'sortOrder': 'Ascending',
                                      'sortkey2': ((item) => {return 0}),
                                      'sortOrder2': 'Ascending',
                                      'endPoint' : 'subjects',
                                      'title': 'Meaning',
                                      'export': ((item)=>('"'+meaningsBrief(item)+'"'))
                                     },
                    'Meaning_Full': {'exists': ((item) => {return true}), 'label': 'Meaning ',
                                     'tableEntry': meaningsFullTable,
                                     'tableEntryMarker': ((item)=>''),
                                     'tooltipEntry': meaningsFull,
                                     'sortkey': ((item) => {return 0}),
                                     'sortOrder': 'Ascending',
                                     'sortkey2': ((item) => {return 0}),
                                     'sortOrder2': 'Ascending',
                                     'endPoint' : 'subjects',
                                     'title': 'Meaning',
                                     'export': ((item)=>('"'+meaningsFull(item)+'"'))
                                    },
                    'Reading_Brief': {'exists': ((item) => {return !(item.data.readings == undefined)}), 'label': 'Reading ',
                                      'tableEntry': readingsBrief,
                                      'tableEntryMarker': ((item)=>''),
                                      'tooltipEntry': readingsBrief,
                                      'sortkey': ((item) => {return 0}),
                                      'sortOrder': 'Ascending',
                                      'sortkey2': ((item) => {return 0}),
                                      'sortOrder2': 'Ascending',
                                      'endPoint' : 'subjects',
                                      'title': 'Reading',
                                      'export': ((item)=>('"'+readingsBrief(item)+'"'))
                                     },
                    'Reading_Full': {'exists': ((item) => {return !(item.data.readings == undefined)}), 'label': 'Reading ',
                                     'tableEntry': readingsFull,
                                     'tableEntryMarker': ((item)=>''),
                                     'tooltipEntry': readingsFull,
                                     'sortkey': ((item) => {return 0}),
                                     'sortOrder': 'Ascending',
                                     'sortkey2': ((item) => {return 0}),
                                     'sortOrder2': 'Ascending',
                                     'endPoint' : 'subjects',
                                     'title': 'Reading',
                                     'export': ((item)=>('"'+readingsFull(item)+'"'))
                                    },
                    'Leech': {'exists': ((item) => {return true}), 'label': 'Leech ',
                              'tableEntry': leechScore,
                              'tableEntryMarker': leechScore,
                              'tooltipEntry': leechScore,
                              'tableEntryMarker': leechScore,
                              'sortkey': leechScore,
                              'sortOrder': 'Descending',
                              'sortkey2': ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct : infinity)}),
                              'sortOrder2': 'Ascending',
                              'endPoint' : 'review_statistics',
                              'wordCloud': ((item) => {return Math.round(leechScore(item))}),
                              'title': 'Leech',
                              'export': ((item)=>'"'+leechScore(item).toLocaleString()+'"'),
                             },
                    'Total_Incorrect_Answers': {'exists': ((item) => {return true}), 'label': 'Tot.&nbsp;Incor. ',
                                                'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect + item.review_statistics.reading_incorrect : 'unavailable')}),
                                                'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect + item.review_statistics.reading_incorrect : 'unavailable')}),
                                                'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect + item.review_statistics.reading_incorrect : 'unavailable')}),
                                                'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect + item.review_statistics.reading_incorrect : infinity)}),
                                                'sortOrder': 'Descending',
                                                'sortkey2': leechScore,
                                                'sortOrder2': 'Descending',
                                                'endPoint' : 'review_statistics',
                                                'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect + item.review_statistics.reading_incorrect : 'unavailable')}),
                                                'title': '"Total Incor."',
                                                'export': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect + item.review_statistics.reading_incorrect : 'unavailable')})
                                               },
                    'Total_Correct_Answers': {'exists': ((item) => {return true}), 'label': 'Tot.&nbsp;Cor. ',
                                              'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct + item.review_statistics.reading_correct : 'unavailable')}),
                                              'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct + item.review_statistics.reading_correct : 'unavailable')}),
                                              'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct + item.review_statistics.reading_correct : 'unavailable')}),
                                              'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct + item.review_statistics.reading_correct : infinity)}),
                                              'sortOrder': 'Ascending',
                                              'sortkey2': leechScore,
                                              'sortOrder2': 'Descending',
                                              'endPoint' : 'review_statistics',
                                              'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct + item.review_statistics.reading_correct : 'unavailable')}),
                                              'title': '"Total Cor."',
                                              'export': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct + item.review_statistics.reading_correct : 'unavailable')})
                                             },
                    'Percentage_Correct': {'exists': ((item) => {return true}), 'label': '%Total ',
                                           'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct+'%' : 'unavailable')}),
                                           'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct+'%' : 'unavailable')}),
                                           'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct+'%' : 'unavailable')}),
                                           'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct : infinity)}),
                                           'sortOrder': 'Ascending',
                                           'sortkey2': leechScore,
                                           'sortOrder2': 'Descending',
                                           'endPoint' : 'review_statistics',
                                           'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct : 0)}),
                                           'title': '%Total',
                                           'export': ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct+'%' : 'unavailable')})
                                          },
                    'Meaning_Correct': {'exists': ((item) => {return true}), 'label': '%Meaning ',
                                        'tableEntry': ((item) => {return (item.review_statistics ? MeaningCorrect(item)+'%' : 'unavailable')}),
                                        'tableEntryMarker': ((item) => {return (item.review_statistics ? MeaningCorrect(item)+'%' : 'unavailable')}),
                                        'tooltipEntry':  ((item) => {return (item.review_statistics ? MeaningCorrect(item)+'%' : 'None')}),
                                        'sortkey': ((item) => {return (item.review_statistics ? MeaningCorrect(item) : infinity)}),
                                        'sortOrder': 'Ascending',
                                        'sortkey2': leechScore,
                                        'sortOrder2': 'Descending',
                                        'title': '%Meaning',
                                        'export': ((item) => {return (item.review_statistics ? MeaningCorrect(item)+'%' : 'unavailable')})
                                       },
                    'Reading_Correct': {'exists': ((item) => {return item.data.readings}), 'label': '%Reading ',
                                        'tableEntry': ((item) => {return (item.review_statistics ? ReadingCorrect(item)+'%' : 'unavailable')}),
                                        'tableEntryMarker': ((item) => {return (item.review_statistics ? ReadingCorrect(item)+'%' : 'unavailable')}),
                                        'tooltipEntry':  ((item) => {return (item.review_statistics ? ReadingCorrect(item)+'%' : 'unavailable')}),
                                        'sortkey': ((item) => {return (item.review_statistics ? ReadingCorrect(item) : infinity)}),
                                        'sortOrder': 'Ascending',
                                        'sortkey2': leechScore,
                                        'sortOrder2': 'Descending',
                                        'endPoint' : 'review_statistics',
                                        'wordCloud': ((item) => {return (item.review_statistics ? ReadingCorrect(item) : 0)}),
                                        'title': '%Reading',
                                        'export': ((item) => {return (item.review_statistics ? ReadingCorrect(item)+'%' : 'unavailable')})
                                       },
                    'Meaning_Incorrect_Answers': {'exists': ((item) => {return true}), 'label': 'Mg&nbsp;Incor. ',
                                                  'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect : 'unavailable')}),
                                                  'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect : 'unavailable')}),
                                                  'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect : 'unavailable')}),
                                                  'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect : infinity)}),
                                                  'sortOrder': 'Descending',
                                                  'sortkey2': leechScore,
                                                  'sortOrder2': 'Descending',
                                                  'endPoint' : 'review_statistics',
                                                  'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect : 'unavailable')}),
                                                  'title': '"Meaning Incor."',
                                                  'export': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_incorrect : 'unavailable')})
                                                 },
                    'Meaning_Correct_Answers': {'exists': ((item) => {return true}), 'label': 'Mg&nbsp;Cor. ',
                                                'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct : 'unavailable')}),
                                                'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct : 'unavailable')}),
                                                'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct : 'unavailable')}),
                                                'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct : infinity)}),
                                                'sortOrder': 'Ascending',
                                                'sortkey2': leechScore,
                                                'sortOrder2': 'Descending',
                                                'endPoint' : 'review_statistics',
                                                'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct : 'unavailable')}),
                                                'title': '"Meaning Cor."',
                                                'export': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_correct : 'unavailable')})
                                               },
                    'Meaning_Current_Streak': {'exists': ((item) => {return true}), 'label': 'Mg&nbsp;Cur.&nbsp;Str. ',
                                               'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_current_streak - 1 : 'unavailable')}),
                                               'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_current_streak - 1 : 'unavailable')}),
                                               'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.meaning_current_streak -1 : 'unavailable')}),
                                               'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_current_streak : infinity)}),
                                               'sortOrder': 'Ascending',
                                               'sortkey2': leechScore,
                                               'sortOrder2': 'Descending',
                                               'endPoint' : 'review_statistics',
                                               'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_current_streak - 1 : 'unavailable')}),
                                               'title': '"Meaning Cur. Str."',
                                               'export': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_current_streak - 1 : 'unavailable')})
                                              },
                    'Meaning_Max_Streak': {'exists': ((item) => {return true}), 'label': 'Mg&nbsp;Max.&nbsp;Str. ',
                                           'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_max_streak - 1 : 'unavailable')}),
                                           'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_max_streak - 1 : 'unavailable')}),
                                           'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.meaning_max_streak -1 : 'unavailable')}),
                                           'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_max_streak : infinity)}),
                                           'sortOrder': 'Ascending',
                                           'sortkey2': leechScore,
                                           'sortOrder2': 'Descending',
                                           'endPoint' : 'review_statistics',
                                           'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_max_streak - 1 : 'unavailable')}),
                                           'title': '"Meaning Max. Str."',
                                           'export': ((item) => {return (item.review_statistics ? item.review_statistics.meaning_max_streak - 1 : 'unavailable')})
                                          },
                    'Reading_Incorrect_Answers': {'exists': ((item) => {return true}), 'label': 'Rg&nbsp;Incor. ',
                                                  'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.reading_incorrect : 'unavailable')}),
                                                  'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.reading_incorrect : 'unavailable')}),
                                                  'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.reading_incorrect : 'unavailable')}),
                                                  'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.reading_incorrect : infinity)}),
                                                  'sortOrder': 'Descending',
                                                  'sortkey2': leechScore,
                                                  'sortOrder2': 'Descending',
                                                  'endPoint' : 'review_statistics',
                                                  'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.reading_incorrect : 'unavailable')}),
                                                  'title': '"Reading Incor."',
                                                  'export': ((item) => {return (item.review_statistics ? item.review_statistics.reading_incorrect : 'unavailable')})
                                                 },
                    'Reading_Correct_Answers': {'exists': ((item) => {return true}), 'label': 'Rg&nbsp;Cor. ',
                                                'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.reading_correct : 'unavailable')}),
                                                'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.reading_correct : 'unavailable')}),
                                                'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.reading_correct : 'unavailable')}),
                                                'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.reading_correct : infinity)}),
                                                'sortOrder': 'Ascending',
                                                'sortkey2': leechScore,
                                                'sortOrder2': 'Descending',
                                                'endPoint' : 'review_statistics',
                                                'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.reading_correct : 'unavailable')}),
                                                'title': '"Reading Cor."',
                                                'export': ((item) => {return (item.review_statistics ? item.review_statistics.reading_correct : 'unavailable')})
                                               },
                    'Reading_Current_Streak': {'exists': ((item) => {return true}), 'label': 'Rg&nbsp;Cur.&nbsp;Str. ',
                                               'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.reading_current_streak - 1 : 'unavailable')}),
                                               'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.reading_current_streak - 1 : 'unavailable')}),
                                               'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.reading_current_streak - 1 : 'unavailable')}),
                                               'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.reading_current_streak : infinity)}),
                                               'sortOrder': 'Ascending',
                                               'sortkey2': leechScore,
                                               'sortOrder2': 'Descending',
                                               'endPoint' : 'review_statistics',
                                               'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.reading_current_streak - 1 : 'unavailable')}),
                                               'title': '"Reading Cur. Str."',
                                               'export': ((item) => {return (item.review_statistics ? item.review_statistics.reading_current_streak - 1 : 'unavailable')})
                                              },
                    'Reading_Max_Streak': {'exists': ((item) => {return true}), 'label': 'Rg&nbsp;Max.&nbsp;Str. ',
                                           'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.reading_max_streak - 1 : 'unavailable')}),
                                           'tableEntryMarker': ((item) => {return (item.review_statistics ? item.review_statistics.reading_max_streak - 1 : 'unavailable')}),
                                           'tooltipEntry':  ((item) => {return (item.review_statistics ? item.review_statistics.reading_max_streak - 1 : 'unavailable')}),
                                           'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.reading_max_streak : infinity)}),
                                           'sortOrder': 'Ascending',
                                           'sortkey2': leechScore,
                                           'sortOrder2': 'Descending',
                                           'endPoint' : 'review_statistics',
                                           'wordCloud': ((item) => {return (item.review_statistics ? item.review_statistics.reading_max_streak - 1 : 'unavailable')}),
                                           'title': '"Reading Max. Str."',
                                           'export': ((item) => {return (item.review_statistics ? item.review_statistics.reading_max_streak - 1 : 'unavailable')})
                                          },
                    'Level': {'exists': ((item) => {return true}), 'label': 'Level ',
                              'tableEntry': ((item) => {return item.data.level}),
                              'tableEntryMarker': ((item) => {return item.data.level}),
                              'tooltipEntry': ((item) => {return item.data.level}),
                              'sortkey': ((item) => {return item.data.level}),
                              'sortOrder': 'Ascending',
                              'sortkey2': ((item) => {return 0}),
                              'sortOrder2': 'Ascending',
                              'endPoint' : 'subjects',
                              'title': 'Level',
                              'export': ((item) => {return item.data.level})
                             },
                    'Srs': {'exists': ((item) => {return true}), 'label': 'SRS ',
                            'tableEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.srs_stage != undefined ? srsName[item.assignments.srs_stage] : 'Locked') : 'Locked')}),
                            'tableEntryMarker': ((item) => {return (item.assignments != undefined ? (item.assignments.srs_stage != undefined ? srsName[item.assignments.srs_stage] : 'Locked') : 'Locked')}),
                            'tooltipEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.srs_stage != undefined ? srsName[item.assignments.srs_stage] : 'Locked') : 'Locked')}),
                            'sortkey': ((item) => {return (item.assignments != undefined ? (item.assignments.srs_stage != undefined ? item.assignments.srs_stage : 10) : 10)}),
                            'sortOrder': 'Ascending',
                            'sortkey2': ((item) => {return (item.assignments != undefined ? (item.assignments.available_at != undefined ? Date.parse(item.assignments.available_at) : theFuture) : theFuture)}),
                            'sortOrder2': 'Ascending',
                            'endPoint' : 'assignments',
                            'title': 'SRS',
                            'export': ((item) => {return (item.assignments != undefined ? (item.assignments.srs_stage != undefined ? srsName[item.assignments.srs_stage] : 'Locked') : 'Locked')})
                           },
                    'Review_Date': {'exists': ((item) => {return true}), 'label': 'Review ',
                                    'tableEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.available_at != undefined ? makeDate(item) : 'Unscheduled') : 'Unscheduled')}),
                                    'tableEntryMarker': ((item) => {return (item.assignments != undefined ? (item.assignments.available_at != undefined ? makeDateMarker(item) : 'Unscheduled') : 'Unscheduled')}),
                                    'tooltipEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.available_at != undefined ? makeDate(item) : 'Unscheduled') : 'Unscheduled')}),
                                    'sortkey': ((item) => {return (item.assignments != undefined ? (item.assignments.available_at != undefined ? trimDate(Date.parse(item.assignments.available_at)) : theFuture) : theFuture)}),
                                    'sortOrder': 'Ascending',
                                    'sortkey2': ((item) => {return (item.assignments != undefined ? (item.assignments.srs_stage != undefined? item.assignments.srs_stage : 10) : 10)}),
                                    'sortOrder2': 'Ascending',
                                    'endPoint' : 'assignments',
                                    'title': '"Review Date"',
                                    'export': ((item) => {return (item.assignments != undefined ? (item.assignments.available_at != undefined ? forExportDate(item.assignments.available_at) : 'Unscheduled') : 'Unscheduled')})
                                   },
                    'Review_Wait': {'exists': ((item) => {return true}), 'label': 'Wait ',
                                    'tableEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.available_at != undefined ? reviewWait(item) : 'Unscheduled') : 'Unscheduled')}),
                                    'tableEntryMarker': ((item) => {return (item.assignments != undefined ? (item.assignments.available_at != undefined ? reviewWait(item) : 'Unscheduled') : 'Unscheduled')}),
                                    'tooltipEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.available_at != undefined ? reviewWait(item) : 'Unscheduled') : 'Unscheduled')}),
                                    'sortkey': ((item) => {return (item.assignments != undefined ? (item.assignments.available_at != undefined ? trimDate(Date.parse(item.assignments.available_at)) : theFuture) : theFuture)}),
                                    'sortOrder': 'Ascending',
                                    'sortkey2': ((item) => {return (item.assignments != undefined ? (item.assignments.srs_stage != undefined? item.assignments.srs_stage : 10) : 10)}),
                                    'sortOrder2': 'Ascending',
                                    'endPoint' : 'assignments',
                                    'title': 'Wait',
                                    'export': ((item) => 'None')
                                   },
                    'Passed_Date': {'exists': ((item) => {return true}), 'label': 'Passed ',
                                    'tableEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.passed_at != undefined ? makePassedDate(item) : 'Not yet') : 'Not yet')}),
                                    'tableEntryMarker': ((item) => {return (item.assignments != undefined ? (item.assignments.passed_at != undefined ? makePassedDateMarker(item) : 'Not yet') : 'Not yet')}),
                                    'tooltipEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.passed_at != undefined ? makePassedDate(item) : 'Not yet') : 'Not yet')}),
                                    'sortkey': ((item) => {return (item.assignments != undefined ? (item.assignments.passed_at != undefined ? trimDate(Date.parse(item.assignments.passed_at)) : 0) : 0)}),
                                    'sortOrder': 'Ascending',
                                    'sortkey2': ((item) => {return (item.assignments != undefined ? (item.assignments.srs_stage != undefined ? item.assignments.srs_stage : 10) : 10)}),
                                    'sortOrder2': 'Ascending',
                                    'endPoint' : 'assignments',
                                    'title': '"Passed Guru"',
                                    'export': ((item) => {return (item.assignments != undefined ? (item.assignments.passed_at != undefined ? forExportDate(item.assignments.passed_at) : '"Not yet"') : '"Not yet"')})
                                   },
                    'Burned_Date': {'exists': ((item) => {return true}), 'label': 'Burned ',
                                    'tableEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.burned_at != undefined ? makeBurnedDate(item) : 'Not yet') : 'Not yet')}),
                                    'tableEntryMarker': ((item) => {return (item.assignments != undefined ? (item.assignments.burned_at != undefined ? makeBurnedDateMarker(item) : 'Not yet') : 'Not yet')}),
                                    'tooltipEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.burned_at != undefined ? makeBurnedDate(item) : 'Not yet') : 'Not yet')}),
                                    'sortkey': ((item) => {return (item.assignments != undefined ? (item.assignments.burned_at != undefined ? trimDate(Date.parse(item.assignments.burned_at)) : 0) : 0)}),
                                    'sortOrder': 'Ascending',
                                    'sortkey2': ((item) => {return item.data.level}),
                                    'sortOrder2': 'Ascending',
                                    'endPoint' : 'assignments',
                                    'title': 'Burned',
                                    'export': ((item) => {return (item.assignments != undefined ? (item.assignments.burned_at != undefined ? forExportDate(item.assignments.burned_at) : '"Not yet"') : '"Not yet"')})
                                   },
                    'Resurrected_Date': {'exists': ((item) => {return true}), 'label': 'Resurrected ',
                                         'tableEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.resurrected_at != undefined ? makeResurrectedDate(item) : 'Not yet') : 'Not yet')}),
                                         'tableEntryMarker': ((item) => {return (item.assignments != undefined ? (item.assignments.resurrected_at != undefined ? makeResurrectedDateMarker(item) : 'Not yet') : 'Not yet')}),
                                         'tooltipEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.resurrected_at != undefined ? makeResurrectedDate(item) : 'Not yet') : 'Not yet')}),
                                         'sortkey': ((item) => {return (item.assignments != undefined ? (item.assignments.resurrected_at != undefined ? trimDate(Date.parse(item.assignments.resurrected_at)).toDateString() : 0) : 0)}),
                                         'sortOrder': 'Ascending',
                                         'sortkey2': ((item) => {return item.data.level}),
                                         'sortOrder2': 'Ascending',
                                         'endPoint' : 'assignments',
                                         'title': 'Resurrected',
                                         'export': ((item) => {return (item.assignments != undefined ? (item.assignments.resurrected_at != undefined ? forExportDate(item.assignments.resurrected_at) : '"Not yet"') : '"Not yet"')})
                                        },
                    'Lesson_Date': {'exists': ((item) => {return true}), 'label': 'Lesson ',
                                    'tableEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.started_at != undefined ? makeLessonDate(item) : 'Not yet') : 'Not yet')}),
                                    'tableEntryMarker': ((item) => {return (item.assignments != undefined ? (item.assignments.started_at != undefined ? makeLessonDateMarker(item) : 'Not yet') : 'Not yet')}),
                                    'tooltipEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.started_at != undefined ? makeLessonDate(item) : 'Not yet') : 'Not yet')}),
                                    'sortkey': ((item) => {return (item.assignments != undefined ? (item.assignments.started_at ? trimDate(Date.parse(item.assignments.started_at)) : theFuture) : theFuture)}),
                                    'sortOrder': 'Ascending',
                                    'sortkey2': ((item) => {return item.data.level}),
                                    'sortOrder2': 'Ascending',
                                    'endPoint' : 'assignments',
                                    'title': 'Lesson',
                                    'export': ((item) => {return (item.assignments != undefined ? (item.assignments.started_at != undefined ? forExportDate(item.assignments.started_at) : '"Not yet"') : '"Not yet"')})
                                   },
                    'Unlock_Date': {'exists': ((item) => {return true}), 'label': 'Unlock ',
                                    'tableEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.unlocked_at != undefined ? makeUnlockDate(item) : 'Not yet') : 'Not yet')}),
                                    'tableEntryMarker': ((item) => {return (item.assignments != undefined ? (item.assignments.unlocked_at != undefined ? makeUnlockDateMarker(item) : 'Not yet') : 'Not yet')}),
                                    'tooltipEntry': ((item) => {return (item.assignments != undefined ? (item.assignments.unlocked_at != undefined ? makeUnlockDate(item) : 'Not yet') : 'Not yet')}),
                                    'sortkey': ((item) => {return (item.assignments != undefined ? (item.assignments.unlocked_at ? trimDate(Date.parse(item.assignments.unlocked_at)) : theFuture) : theFuture)}),
                                    'sortOrder': 'Ascending',
                                    'sortkey2': ((item) => {return item.data.level}),
                                    'sortOrder2': 'Ascending',
                                    'endPoint' : 'assignments',
                                    'title': 'Unlock',
                                    'export': ((item) => {return (item.assignments != undefined ? (item.assignments.unlocked_at != undefined ? forExportDate(item.assignments.unlocked_at) : '"Not yet"') : '"Not yet"')})
                                   },
                    'Item': {'exists': ((item) => {return true}), 'label': 'Item ',
                             'tableEntry': ((item) => {return 'None'}),
                             'tableEntryMarker': ((item) => {return 'None'}),
                             'tooltipEntry':  ((item) => {return 'None'}),
                             'sortkey':  ((item) => {return 0}),
                             'sortOrder': 'Ascending',
                             'sortkey2':  ((item) => {return 0}),
                             'sortOrder2': 'Ascending',
                             'endPoint' : 'subjects',
                             'title': 'Item',
                             'export': ((item) => {return (item.data.characters ? item.data.characters : item.data.slug)})
                            },
                    'Type': {'exists': ((item) => {return true}), 'label': 'Type ',
                             'tableEntry':  ((item) => {return 'None'}),
                             'tableEntryMarker':  ((item) => {return 'None'}),
                             'tooltipEntry':  ((item) => {return 'None'}),
                             'sortkey':  ((item) => {return 0}),
                             'sortOrder': 'Ascending',
                             'sortkey2':  ((item) => {return 0}),
                             'sortOrder2': 'Ascending',
                             'endPoint' : 'assignments',
                             'title': 'Type',
                             'export': ((item) => {return (item.object != undefined ? item.object : 'unavailable')})
                            },
                    'Export_Date': {'exists': ((item) => {return true}), 'label': 'Export&nbsp;Date',
                                    'tableEntry':  ((item) => {return 'None'}),
                                    'tableEntryMarker':  ((item) => {return 'None'}),
                                    'tooltipEntry':  ((item) => {return 'None'}),
                                    'sortkey':  ((item) => {return 0}),
                                    'sortOrder': 'Ascending',
                                    'sortkey2':  ((item) => {return 0}),
                                    'sortOrder2': 'Ascending',
                                    'endPoint' : 'subjects',
                                    'title': '"Export Date"',
                                    'export': ((item) => {return today()})
                                   },
                   }

    function MeaningCorrect(item){
        let stats = item.review_statistics;
        let denominator = stats.meaning_correct + stats.meaning_incorrect;
        if (denominator == 0){
            return 100
        } else {
            return Math.floor(100 * stats.meaning_correct / denominator)
        }
    }

    function ReadingCorrect(item){
        let stats = item.review_statistics;
        let denominator = stats.reading_correct + stats.reading_incorrect;
        if (denominator == 0){
            return 100
        } else {
            return Math.floor(100 * stats.reading_correct / denominator)
        }
    }


    function leechScore(item){
        if (item.review_statistics != undefined){
            let reviewStats = item.review_statistics;
            let meaningScore = getLeechScore(reviewStats.meaning_incorrect, reviewStats.meaning_current_streak);
            let readingScore = getLeechScore(reviewStats.reading_incorrect, reviewStats.reading_current_streak);

            return Math.max(meaningScore, readingScore);
        } else {return 0}
    }

    function getLeechScore(incorrect, currentStreak) {
        //get incorrect number than lessen it using the user's correctStreak
        let leechScore = incorrect / Math.pow((currentStreak || 0.5), 1.5); // '||' => if currentstreak zero make 0.5 instead (prevents dividing by zero)
        leechScore = Math.round(leechScore * 100) / 100; //round to two decimals
        return leechScore;
    }

    function reviewWait(item){
        return (Date.parse(item.assignments.available_at) < Date.now() ? 'Now' : s_to_dhm((Date.parse(item.assignments.available_at)-Date.now())/1000))
    }

    // Converts seconds to days, hours, and minutes
    function s_to_dhm(s) {
        var d = Math.floor(s/60/60/24);
        var h = Math.floor(s%(60*60*24)/60/60);
        var m = Math.ceil(s%(60*60*24)%(60*60)/60);
        return (d>0?d+'d ':'')+(h>0?h+'h ':'')+(m>0?m+'m':'1m');
    }

    function makeDate(item){
        return formatDate(new Date(item.assignments.available_at), true, /* is_next_date */);
    }

    function makeDateMarker(item){
        return formatDateMarker(new Date(item.assignments.available_at), true, /* is_next_date */);
    }

    function makePassedDate(item){
        return formatDate(new Date(item.assignments.passed_at), false, /* is_next_date */);
    }

    function makePassedDateMarker(item){
        return formatDateMarker(new Date(item.assignments.passed_at), false, /* is_next_date */);
    }

    function makeBurnedDate(item){
        return formatDate(new Date(item.assignments.burned_at), false, /* is_next_date */);
    }

    function makeBurnedDateMarker(item){
        return formatDateMarker(new Date(item.assignments.burned_at), false, /* is_next_date */);
    }

    function makeResurrectedDate(item){
        return formatDate(new Date(item.assignments.resurrected_at), false, /* is_next_date */);
    }

    function makeResurrectedDateMarker(item){
        return formatDateMarker(new Date(item.assignments.resurrected_at), false, /* is_next_date */);
    }

    function makeLessonDate(item){
        return formatDate(new Date(item.assignments.started_at), false, /* is_next_date */);
    }

    function makeLessonDateMarker(item){
        return formatDateMarker(new Date(item.assignments.started_at), false, /* is_next_date */);
    }

    function makeUnlockDate(item){
        return formatDate(new Date(item.assignments.unlocked_at), false, /* is_next_date */);
    }
    function makeUnlockDateMarker(item){
        return formatDateMarker(new Date(item.assignments.unlocked_at), false, /* is_next_date */);
    }

    function forExportDate(date) {
        return new Date(date).toISOString().slice(0, 16).replace('T', ' ');
    }

    function formatDateMarker(date) {
        var YY = ''+date.getFullYear(),
            MM = ''+(date.getMonth()+1),
            DD = ''+date.getDate(),
            hh = ''+date.getHours(),
            mm = ''+date.getMinutes();

        if (MM.length < 2) MM = '0' + MM;
        if (DD.length < 2) DD = '0' + DD;
        let s = YY+'-'+MM+'-'+DD;
        if (quiz.settings.tablePresets[quiz.settings.active_ipreset].showHours){
            if (quiz.settings.hoursFormat === '24hours') {
                s += ' '+('0'+hh).slice(-2)+':'+('0'+mm).slice(-2);
            } else {
                s += ' '+(((hh+11)%12)+1)+':'+('0'+mm).slice(-2)+['am','pm'][Math.floor(date.getHours()/12)];
            }
        };
        return s;
    }

    function today() {
        var d = new Date(),
            month = '' + (d.getMonth() + 1),
            day = '' + d.getDate(),
            year = d.getFullYear();

        if (month.length < 2) month = '0' + month;
        if (day.length < 2) day = '0' + day;
        return [year, month, day].join('-');
    }

    //========================================================================
    // Print date in pretty format.
    //-------------------------------------------------------------------
    function formatDate(d, is_next_date, show_year){
        var s = '';
        var now = new Date();
        var YY = d.getFullYear(),
            MM = d.getMonth(),
            DD = d.getDate(),
            hh = d.getHours(),
            mm = d.getMinutes(),
            one_day = 24*60*60*1000;

        if (is_next_date && d < now) return "Available Now";
        var same_day = ((YY == now.getFullYear()) && (MM == now.getMonth()) && (DD == now.getDate()) ? 1 : 0);

        //    If today:  "Today 8:15pm"
        //    otherwise: "Wed, Apr 15, 8:15pm"
        if (same_day) {
            s += 'Today ';
        } else if (show_year){
            s += d.toISOString().slice(10);
        } else {
            s += ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][d.getDay()]+', '+
                ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][MM]+' '+DD+', ';
        }
        if (quiz.settings.hoursFormat === '24hours') {
            s += ('0'+hh).slice(-2)+':'+('0'+mm).slice(-2);
        } else {
            s += (((hh+11)%12)+1)+':'+('0'+mm).slice(-2)+['am','pm'][Math.floor(d.getHours()/12)];
        }

        return s;
    }

    //========================================================================
    // Reduce date in milliseconds to minutes boundary
    // This makes secondary sorting match the primary sort data seen by the user
    //------------------------------------------------------------------------
    function trimDate(d){
        return Math.floor((d/1000)/60)*1000*60;
    }

    function meaningsFullTable(item){
        var meanings = meaningsFull(item);
        if (meanings.length > 20){meanings = meanings.slice(0, 20) + '...'}
        return meanings;
    }

    function meaningsFull(item){
        // make sure meanings are returned in the order WK provides them, but primary comes first
        var meanings = "";
        var meaningsPrimary = "";
        for (var k = 0; k < item.data.meanings.length; k++){
            if(item.data.meanings[k].primary){
                if (meaningsPrimary.length != 0){
                    meaningsPrimary += ', ' + item.data.meanings[k].meaning;
                } else {
                    meaningsPrimary = item.data.meanings[k].meaning;
                }
            } else {
                if (meanings.length != 0){
                    meanings += ', ' + item.data.meanings[k].meaning;
                } else {
                    meanings = item.data.meanings[k].meaning;
                }
            }
        }
        if (meanings.length != 0){meaningsPrimary = meaningsPrimary + ', ' + meanings}
        return meaningsPrimary;
    }


    function meaningsBrief(item){
        var meanings = item.data.meanings[0].meaning;
        for (var k = 0; k < item.data.meanings.length; k++){if (item.data.meanings[k].primary){return item.data.meanings[k].meaning}};
        return meanings;
    }

    function readingsFull(item){
        // make sure readings are returned in the order WK provides them, but primary comes first
        var readings = "";
        var readingsPrimary = "";
        for (var k = 0; k < item.data.readings.length; k++){
            if(item.data.readings[k].primary){
                if (readingsPrimary.length != 0){
                    readingsPrimary += ', ' + item.data.readings[k].reading;
                } else {
                    readingsPrimary = item.data.readings[k].reading;
                }
            } else {
                if (readings.length != 0){
                    readings += ', ' + item.data.readings[k].reading;
                } else {
                    readings = item.data.readings[k].reading;
                }
            }
        }
        if (readings.length != 0){readingsPrimary = readingsPrimary + ', ' + readings}
        return readingsPrimary;
    }

    function readingsBrief(item){
        var readings = item.data.readings[0].reading;
        for (var k = 0; k < item.data.readings.length; k++){if (item.data.readings[k].primary){return item.data.readings[k].reading}};
        return readings;
    }

    function shuffle(items){
        for(let i = items.length - 1; i > 0; i--){
            const j = Math.floor(Math.random() * i);
            const temp = items[i];
            items[i] = items[j];
            items[j] = temp;
        }
        return items
    }

    function updatePage(noCreateTable) {
        let leechStreakLimit = quiz.settings.tablePresets[quiz.settings.active_ipreset].leechStreakLimit;
        if (leechStreakLimit != 0) {
            quiz.items = quiz.items.filter((item => {return ((item.review_statistics != undefined) ?
                                                             ((item.review_statistics.meaning_current_streak < (leechStreakLimit+1)) || (item.review_statistics.reading_current_streak < (leechStreakLimit+1))) : false)}));
        };

        let randomSelection = quiz.settings.tablePresets[quiz.settings.active_ipreset].randomSelection;
        if (randomSelection != 0){
            quiz.items = shuffle(quiz.items).slice(0, randomSelection);
        };

        let settings = quiz.settings.tablePresets[quiz.settings.active_ipreset];
        let tableKey = settings.table_data;
        let sort1 = settings.sort1;
        let sort2 = settings.sort2;
        let sortKey, sortKey2;
        let sortOrder = settings.sortOrder1;
        let sortOrder2 = settings.sortOrder2;
        if ((sort1 == "Default") && (sort2 == 'Default')){
            sortKey = metadata[tableKey].sortkey;
            if (sortOrder === 'Default'){sortOrder = metadata[tableKey].sortOrder};
            sortKey2 = metadata[tableKey].sortkey2;
            if (sortOrder2 === 'Default'){sortOrder2 = metadata[tableKey].sortOrder2};
        } else if ((sort1 == "Default") && (sort2 != "Default")){
            sortKey = metadata[tableKey].sortkey;
            if (sortOrder === 'Default'){sortOrder = metadata[tableKey].sortOrder};
            sortKey2 = metadata[sort2].sortkey;
            if (sortOrder2 === 'Default'){sortOrder2 = metadata[sort2].sortOrder};
        } else if ((sort1 != "Default") && (sort2 == "Default")){
            sortKey = metadata[sort1].sortkey;
            if (sortOrder === 'Default'){sortOrder = metadata[sort1].sortOrder};
            sortKey2 = metadata[sort1].sortkey2;
            if (sortOrder2 === 'Default'){sortOrder2 = metadata[sort1].sortOrder2};
        } else {
            sortKey = metadata[sort1].sortkey;
            if (sortOrder === 'Default'){sortOrder = metadata[sort1].sortOrder};
            sortKey2 = metadata[sort2].sortkey;
            if (sortOrder === 'Default'){sortOrder2 = metadata[sort2].sortOrder};
        }
        quiz.items.forEach(((item) => {item.sortKey = sortKey(item); item.sortKey2 = sortKey2(item)}));

        if ((sortOrder == 'Descending') && (sortOrder2 == 'Descending')){
            quiz.items = quiz.items.sort(function(a, b){return b.sortKey == a.sortKey ? b.sortKey2 - a.sortKey2 : b.sortKey - a.sortKey });
        } else if ((sortOrder == 'Descending') && (sortOrder2 == 'Ascending')){
            quiz.items = quiz.items.sort(function(a, b){return b.sortKey == a.sortKey ? a.sortKey2 - b.sortKey2 : b.sortKey - a.sortKey });
        } else if ((sortOrder == 'Ascending') && (sortOrder2 == 'Descending')){
            quiz.items = quiz.items.sort(function(a, b){return a.sortKey == b.sortKey ? b.sortKey2 - a.sortKey2 : a.sortKey - b.sortKey });
        } else {
            quiz.items = quiz.items.sort(function(a, b){return(a.sortKey == b.sortKey ? a.sortKey2 - b.sortKey2 : a.sortKey - b.sortKey)});
        };

        if (!noCreateTable){displayItems()};
    };


    function displayItems(){
        if (quiz.settings.listMode){
            createItemList(quiz.items);
        } else {
            createTopLeechTables(quiz.items);
        };
    };

    function itemsCharacterCallback (item){
        let maxItemLength = 28;
        if (quiz.settings.tablePresets[quiz.settings.active_ipreset].displayMeaning){
            let text = meaningsBrief(item);
            if (wideElements[quiz.settings.tablePresets[quiz.settings.active_ipreset].table_data]){
                return [text.slice(0,maxItemLength), false , ''];
            } else {
                return [text, false, ''];
            }
        };
        //check if an item has characters. Kanji and vocabulary will always have these but wk-specific radicals (e.g. gun, leaf, stick) use images instead
        if(item.data.characters!= null) {
            return [item.data.characters, true, 'lang="JP"'];
        } else if (item.data.character_images!= null){
            let imgUrl = item.data.character_images.find(a=>a.content_type=="image/svg+xml"&&a.metadata.inline_styles).url;
            return ['<img height=14px width=14px src='+imgUrl+'"></img>', true, 'lang="JP"'];
        } else {
            //if both characters and character_images are somehow absent try using slug instead
            return [item.data.slug, true, 'lang="JP"'];
        }
    }

    function makeTableEntry(item, selectedTable){
        let tableData = metadata[quiz.settings.tablePresets[selectedTable].table_data];
        if (tableData.exists(item)) {
            return (tableData.exists(item) ? tableData.tableEntry(item) : '')
        }
    }

    function makeTooltipEntry(item, tableName){
        let html = '';
        if (tableName != "None") {
            //alert(tableName);
            let tableData = metadata[tableName];
            //alert(JSON.stringify(tableData));
            if (tableData.exists(item)) {
                html = '<tr><td class="WkitLabel">'+tableData.label+'</td><td class="WkitTipValue">'+tableData.tooltipEntry(item)+'</td></tr>'
            };
        };
        return html;
    };

    var audioCounter = 0;
    function makeItemLink(item, itemData, itemClass){
        if (quiz.settings.audioMode && item.object === 'vocabulary'){
            let gender = quiz.settings.audioSource;
            let audioId = 'Wkitaudio'+audioCounter; audioCounter++;
            let html = '';

            if (gender == 'male' || gender == 'random'){
                let soundUrlmpeg = item.data.pronunciation_audios.find(a=>a.content_type=="audio/mpeg"&&a.metadata.gender==='male');
                if (soundUrlmpeg != undefined){soundUrlmpeg = soundUrlmpeg.url};
                let soundUrlogg = item.data.pronunciation_audios.find(a=>a.content_type=="audio/ogg"&&a.metadata.gender==='male');
                if (soundUrlogg != undefined){soundUrlogg = soundUrlogg.url};
                if (soundUrlmpeg != undefined && soundUrlogg != undefined){
                    if(soundUrlogg != undefined) html += '<link ref="prefetch" href="'+soundUrlogg+'">';
                    if(soundUrlmpeg != undefined) html += '<link ref="prefetch" href="'+soundUrlmpeg+'">';
                    html += '<audio id="'+audioId+'male">';
                    if(soundUrlogg != undefined) html += '<source src="'+soundUrlogg+'" type="audio/ogg"></source>';
                    if(soundUrlmpeg != undefined) html += '<source src="'+soundUrlmpeg+'" type="audio/mpeg"></source>';
                    html += '</audio>';
                }
            };

            if (gender == 'female' || gender == 'random'){
                let soundUrlmpeg = item.data.pronunciation_audios.find(a=>a.content_type=="audio/mpeg"&&a.metadata.gender==='female');
                if (soundUrlmpeg != undefined){soundUrlmpeg = soundUrlmpeg.url};
                let soundUrlogg = item.data.pronunciation_audios.find(a=>a.content_type=="audio/ogg"&&a.metadata.gender==='female');
                if (soundUrlogg != undefined){soundUrlogg = soundUrlogg.url};
                if (soundUrlmpeg != undefined && soundUrlogg != undefined){
                    if(soundUrlogg != undefined) html += '<link ref="prefetch" href="'+soundUrlogg+'">';
                    if(soundUrlmpeg != undefined) html += '<link ref="prefetch" href="'+soundUrlmpeg+'">';
                    html += '<audio id="'+audioId+'female">';
                    if(soundUrlogg != undefined) html += '<source src="'+soundUrlogg+'" type="audio/ogg"></source>';
                    if(soundUrlmpeg != undefined) html += '<source src="'+soundUrlmpeg+'" type="audio/mpeg"></source>';
                    html += '</audio>';
                }
            };

            //html += '<a onclick="document.getElementById('+"'"+audioId+"'"+').play();">';
            html += '<a onclick="playAudio('+"'"+audioId+"','"+gender+"'"+');">';
            html +=     '<span '+itemClass+'>'+itemData[0]+'</span>';
            html += '</a>';
            return html
        } else {
            return '<a style="padding-right:0;" target="_blank" href="'+item.data.document_url+'"><span '+itemClass+'}>'+itemData[0]+'</span></a>'
        };
    };

    function makeTooltips(item, selectedTable, mode){
        let html = '';
        let presets = quiz.settings.tablePresets[selectedTable]

        html += makeTooltipEntry(item, presets.tooltip1);
        html += makeTooltipEntry(item, presets.tooltip2);
        html += makeTooltipEntry(item, presets.tooltip3);
        html += makeTooltipEntry(item, presets.tooltip4);
        html += makeTooltipEntry(item, presets.tooltip5);
        html += makeTooltipEntry(item, presets.tooltip6);
        if (mode === 'list') html += makeTooltipEntry(item, presets.table_data);

        var info = {
            type: item.object,
            characters: item.data.characters,
            url: item.data.document_url,
            svg: (item.data.characters === null ? item.data.character_images.find(a=>a.content_type=="image/svg+xml"&&a.metadata.inline_styles).url : null),
        };

        html = '<div class="WkitTooltipContent">'+
            '<div class="left">'+
            '<span class="'+info.type+'" style="color: #ffffff !important;">'+(info.characters === null ? '<img src="'+info.svg+'">' : info.characters)+'</span>'+
            '</div>'+
            (html != '' ? '<div class="right"><table class="WkitTablePopup"><tbody>'+html+'</tbody></table></div>' : '')+
            '</div>';
        return html;
    }

    function makeEnlargingTooltip (item, selectedTable){
        let html;
        if (quiz.settings.tablePresets[selectedTable].enlargingTooltip === true){
            html = '<div class="WkitEnlargedTooltip">'+
                '<span class="'+item.object+'" style="color: #ffffff !important;">'+(item.data.characters === null ? '<img src="'+item.data.character_images.find(a=>{ return a.content_type=="image/svg+xml"&&a.metadata.inline_styles}).url+'"' : item.data.characters)+'</span>'+
                '</div>';
        } else {
            html = '';
        };
        return html;
    };

    /* Event handlers for buttons */
    function clickedBackward(event) {
        if (!event.detail || event.detail == 1) {
            if (itemHistory.length != 0){
                nextCurrentItem = currentItem;
                currentItem = itemHistory.pop();
                let workaround = nextCurrentItem+' '+parseInt(quiz.items.length)+' ';
                quiz.settings.tablePresets[quiz.settings.active_ipreset].currentItem = currentItem;
                quiz.settings.tablePresets[quiz.settings.active_ipreset].currentItem = nextCurrentItem;
                quiz.settings.tablePresets[quiz.settings.active_ipreset].itemHistory = itemHistory;
                quiz.settings_dialog.save().then(displayItems)
            };
        };
        return
    }

    function clickedForward(event) {
        //test prevents multiple clicks
        if (!event.detail || event.detail == 1) {
            /*if ((currentItem + nbDisplayedItems) < quiz.items.length) {
            currentItem += nbDisplayedItems;*/
            if(currentItem < nextCurrentItem){
                itemHistory.push(currentItem);
                currentItem = nextCurrentItem;
                let workaround = nextCurrentItem+' '+parseInt(quiz.items.length)+' ';
                quiz.settings.tablePresets[quiz.settings.active_ipreset].currentItem = currentItem;
                quiz.settings.tablePresets[quiz.settings.active_ipreset].nextCurrentItem = nextCurrentItem;
                quiz.settings.tablePresets[quiz.settings.active_ipreset].itemHistory = itemHistory;
                quiz.settings_dialog.save().then(displayItems)
            };
        };
        return
    }

    function selectTable(event) {
        quiz.settings.active_ipreset = $('#WkitTableSelector').prop('selectedIndex');
        quiz.settings.tablePresets[quiz.settings.active_ipreset].currentItem = currentItem = 0;
        quiz.settings.tablePresets[quiz.settings.active_ipreset].nextCurrentItem = nextCurrentItem = 0;
        quiz.settings.tablePresets[quiz.settings.active_ipreset].itemHistory = itemHistory = [];
        quiz.settings_dialog.save()
            .then(function(){return fetch_items('table')})
            .then(updatePage)
    };

    function toggleDisplay(event) {
        //test prevents multiple clicks
        if (!event.detail || event.detail == 1) {
            quiz.settings.tablePresets[quiz.settings.active_ipreset].currentItem = currentItem = 0;
            quiz.settings.tablePresets[quiz.settings.active_ipreset].nextCurrentItem = nextCurrentItem = 0;
            quiz.settings.tablePresets[quiz.settings.active_ipreset].itemHistory = itemHistory = [];
            quiz.settings.listMode = !quiz.settings.listMode;
            if (quiz.settings.listMode){nbDisplayedItems = sizeOfList} else {nbDisplayedItems = sizeOfTable};
            quiz.settings_dialog.save().then(displayItems)
        };
        return
    };

    function toggleLanguage(event) {
        //test prevents multiple clicks
        if (!event.detail || event.detail == 1) {
            let a = quiz.settings.tablePresets[quiz.settings.active_ipreset].displayMeaning;
            quiz.settings.tablePresets[quiz.settings.active_ipreset].displayMeaning = !a;
            quiz.settings_dialog.save().then(formatControlBar).then(displayItems)
        };
        return
    };

    function toggleAudio(event) {
        //test prevents multiple clicks
        if (!event.detail || event.detail == 1) {
            let a = quiz.settings.audioMode;
            quiz.settings.audioMode = !a;
            quiz.settings_dialog.save().then(formatControlBar).then(displayItems)
        };
        return
    };


    /* control variables for the currently displayed tables*/
    var currentItem = 0;
    var nextCurrentItem;
    var itemHistory = 0;
    var nbItems = 0;

    function initCurrentItem(){
        currentItem = quiz.settings.tablePresets[quiz.settings.active_ipreset].currentItem;
        if (currentItem == undefined){currentItem = 0; nextCurrentItem = 0; itemHistory = []};
        nextCurrentItem = quiz.settings.tablePresets[quiz.settings.active_ipreset].nextCurrentItem;
        if (nextCurrentItem == undefined){currentItem = 0; nextCurrentItem = 0; itemHistory = []};
        itemHistory = quiz.settings.tablePresets[quiz.settings.active_ipreset].itemHistory;
        if (itemHistory == undefined){currentItem = 0; nextCurrentItem = 0; itemHistory = []};
    }

    var script = '<script type="text/javascript">'
       script += 'function playAudio(id, gender){';
       script +=      'if (gender === "random"){gender = ["male", "female"][Math.floor(Math.random()*2)]};'
       script +=      'id += gender;';
       script +=      'document.getElementById(id).play();';
       script += '};';
       script += '</script>';

    const numberOfTables = 3;
    var leechesPerTable;
    var nbDisplayedItems;
    var sizeOfList;
    var sizeOfTable;
    function setNumberOfLines (){
        if (quiz.settings.numberOfLines === undefined){
            leechesPerTable = 11;
        } else {
            leechesPerTable = Number(quiz.settings.numberOfLines);
        };
        sizeOfTable = numberOfTables * leechesPerTable;
        sizeOfList = 115;
        if (quiz.settings.listMode){nbDisplayedItems = sizeOfList} else {nbDisplayedItems = sizeOfTable};
    };

    function createTopLeechTables(items) {
        let sectionContents = "";
        let numberPerTable = leechesPerTable;
        let startnumberTable = currentItem;
        let endNumberTable = currentItem + numberPerTable;
        let itemsLength = items.length;
        let nrOfTables = numberOfTables;
        let totalNumberOfLeeches = numberPerTable * nrOfTables;
        let meanings = "";
        let readings = "";
        nbItems = items.length;
        audioCounter = 0;
        let activeTable = quiz.settings.active_ipreset;

        //make sure we don't create empty tables if there are too few items
        if(items.length == 0){
            nrOfTables = 0;
            sectionContents += `<div class="emptyMessage"><p><b>No items match your criteria. You may try relaxing them if possible.</b></p></div>`;
        } else if(items.length < totalNumberOfLeeches) { //if less leeches available then user requested
            var ratio = items.length / (numberPerTable*3);
            if(ratio <= 0.34){
                nrOfTables = 1;
            } else if(ratio <= 0.67){
                nrOfTables = 2;
            }
        } else if (numberPerTable >= totalNumberOfLeeches){ //if table capacity greater than user's requested amount of leeches
            nrOfTables = 1;
        }

        //Create tables
        sectionContents += script;
        for (var i = 0; i < nrOfTables; i++){
            //In case there are less than the requested amount of items
            if(items.length <= endNumberTable){
                endNumberTable = items.length;
                nrOfTables = i - 1;
            };
            sectionContents += `
            <div class="span4" style="width=290px;">
                <div class="WkitTableList" style="position: relative;">
                    <h3 class="WkitSmallCaps">Items ${startnumberTable+1}-${endNumberTable} of ${itemsLength}</h3>
                        <table>
                            <tbody>`;
        for (var j = startnumberTable; j < endNumberTable; j++){
            let itemData = itemsCharacterCallback(items[j]);
            let tableEntry = makeTableEntry(items[j], activeTable);
            let itemLink = makeItemLink(items[j], itemData, 'class="WkitItem'+itemData[2]+'"');
            let tooltip = makeTooltips(items[j], activeTable, 'table');
            let tooltip2;
            if (itemData[1]){
                tooltip2 = '<div class="WkitTooltip2"><p><span> </span></p>'+makeEnlargingTooltip(items[j], activeTable)+'</div>';
            } else {
                tooltip2 = '';
            };
            sectionContents += `<tr class="${items[j].object}">` +
                '<td>' +
                   '<div class="WkitTooltip">' +
                      itemLink +
                      tooltip +
                   '</div>' +
                    tooltip2 +
                '</td>' +
                '<td>' +
                    '<div style="text-align: right"><a style="padding-left:0;"><span style="color: Gainsboro">'+tableEntry+'</span></a>' +
                    '</div>' +
                '</td>' +
             '</tr>';
        }
        //preparing for next table
        startnumberTable += numberPerTable;
        endNumberTable += numberPerTable;

        sectionContents += '</tbody>' +
            '</table>' +
            '</div>' +
            '</div>';

        }

        if(j+1 < items.length){nextCurrentItem = j+1};
        $('#leech_table').html(sectionContents);//replace existing table
    }

    function createItemList(items){
        let startNumberItem = currentItem;
        let endNumberItem = items.length;
        let itemsLength = items.length;
        let selected_table = quiz.settings.active_ipreset;
        let meaningMode = quiz.settings.tablePresets[selected_table].displayMeaning;
        let get_table_data = metadata[quiz.settings.tablePresets[selected_table].table_data].tableEntryMarker;
        let showMarkers = quiz.settings.tablePresets[selected_table].showMarkers;
        audioCounter = 0;

        // These variables control how many items will be on the screen so they don't overflow it
        let maxCharCount = meaningMode ? 550.0 : 336.0;
        let overheadVocab = 0.9;
        let overheadOther = 0.8;
        let markerFactor = 0.27;
        let markerOverhead = 0.9;
        let meaningFactor = 0.27;
        let meaningOverhead = 0.9;
        let itemLimit = (meaningMode ? 135 : 74);

        let html = script;
        let charCount = 0;
        let itemCount = 0;
        let i = startNumberItem;
        let oldMarker = undefined;
        let newMarker = undefined;
        let markerString;
        while (charCount < maxCharCount && i+1 < endNumberItem){
            newMarker = get_table_data(items[i]);
            if (newMarker != oldMarker){
                oldMarker = newMarker;
                if (showMarkers) html += makeMarkerElement(newMarker, meaningMode);
                markerString = (typeof newMarker === 'number' ? newMarker.toString() : newMarker);
                charCount += (markerString.length * markerFactor) + markerOverhead;
            };
            if (meaningMode){
                let meaning = metadata.Meaning_Brief.tableEntry(items[i]);
                html += makeListElement(items[i], selected_table, charCount, 'WkitTooltipIconMeaning', itemLimit);
                charCount += meaning.length * meaningFactor + meaningOverhead;
            } else {
                html += makeListElement(items[i], selected_table, charCount, 'WkitTooltipIcon', itemLimit);
                charCount += (items[i].data.characters === undefined || items[i].data.characters === null ? 1 + overheadOther : (items[i].data.characters.length === 1 ? 1 + overheadOther : items[i].data.characters.length + overheadVocab));
            };
            itemCount++;
            i++;
        };
        if (i + 1 < endNumberItem){nextCurrentItem = i + 1} else {i++ /* for adjusting the output */};

        let html2 = '';
        html2 = '<div class="WkitItemList">';
        html2 +=    `<div class="WkitSmallCapsList${meaningMode ? ' WkitMeaning' : ' WkitReading'}"><span>Items ${startNumberItem+1}-${i} of ${itemsLength}</span></div>`;
        html2 += html;
        html2 += '</div>';
        $('#leech_table').html(html2);//replace existing table

        function makeListElement(item, selected_table, charCount, itemClass, firstObjectLimit){
            let itemData = itemsCharacterCallback(item);
            let itemLink = makeItemLink(item, itemData, ' class="'+item.object+'"style="color: #ffffff !important;"');
            var elem = '<div class="WkitTooltip '+itemClass+' '+item.object+''+(charCount <= firstObjectLimit ? " WkitFirstItem" : " WkitLaterItem")+'">'+
                makeTooltips(item, selected_table, 'list') +
                '<div class="WkitItemListed">'+
                itemLink +
                '</div>'+
                '</div>';

            return elem;
        };

        function makeMarkerElement(marker, meaningMode){
            var elem = '<div class="WkitTooltip WkitTooltipIcon'+(meaningMode ? ' WkitMarkerMeaning' : ' WkitMarker')+'">'+
                '<div class="'+(meaningMode ? 'WkitMarkerMeaning' : 'WkitMarker')+'" style="color: #000000 !important;"><span>'+marker+'</span></div>'+
                '</div>';

            return elem;
        };
    };

    function fillClipboard(){
        // make sure the repeat field endpoint is available in the items
        if (quiz.settings.repeatWordCloud != 'No Repeat'){
            fetch_items('wordCloud');
            updatePage(true);
        };
        let items = quiz.items;
        let text = '';
        let noLatin = quiz.settings.noLatin;
        if (noLatin === undefined){noLatin = false};
        let oneItemPerLine = quiz.settings.oneItemPerLine;
        if (oneItemPerLine === undefined){oneItemPerLine = false};
        let exportLimit = quiz.settings.exportLimit;
        if (exportLimit === undefined){exportLimit = 0};
        let exportCount = 0;
        let repeatWordCloud = quiz.settings.repeatWordCloud;
        let repeatCount;

        for (var i = 0; i < items.length; i++){
            let itemsData = items[i].data;
            if (repeatWordCloud != 'No Repeat'){
                repeatCount = metadata[repeatWordCloud].wordCloud(items[i]);
            } else {
                repeatCount = 1;
            };
            for (var j = 0; j < repeatCount; j++){
                if(itemsData.characters!= null) {
                    text += itemsData.characters+' ';
                    if (oneItemPerLine){text += '\n'};
                } else if (!noLatin){
                    text += itemsData.slug+' ';
                    if (oneItemPerLine){text += '\n'};
                };
            };
            if (repeatCount != 0){exportCount += 1;};
            if (exportLimit != 0 && exportCount >= exportLimit){break};
        };

        let textarea = '<textarea id="WkitTextArea" class="WkitClipBoard">';
        $('#leech_table').html(textarea);//replace existing table
        $('#WkitTextArea').val(text);
        $('#WkitTextArea').select();
        document.execCommand('copy');
        alert(exportCount+' items have been copied in the clipboard.');

        fetch_items('table')
            .then(function(){
            updatePage();
        })
    };

    function exportTable(){
        let currentPreset = quiz.settings.tablePresets[quiz.settings.active_ipreset];
        let currentColumn = currentPreset[exportedInfo[0]];
        if (currentColumn === undefined || currentColumn == 'None' ){
            alert('The first column is not exported.\nPlease configure your export settings.');
            return;
        };

        // make sure all export data is available in the items
        fetch_items('export');
        updatePage(true);
        let items = quiz.items;
        let text = '';
        let exportCount = 0;

        let firstElement = true;
        if (currentPreset.includeTitle){
            for (var i = 0; i < exportedInfo.length; i++){
                currentColumn = currentPreset[exportedInfo[i]];
                if (currentColumn !='None' && currentColumn != undefined){
                    if (firstElement){
                        text += metadata[currentColumn].title;
                        firstElement = false;
                    } else {
                        text += ', '+metadata[currentColumn].title;
                    };
                };
            };
            text += '\n';
        };
        for (i = 0; i < items.length; i++){
            firstElement = true;
            for (var j = 0; j < exportedInfo.length; j++){
                let currentColumn = currentPreset[exportedInfo[j]];
                if (currentColumn !='None' && currentColumn != undefined){
                    if (firstElement){
                        text += (metadata[currentColumn].exists(items[i]) ? metadata[currentColumn].export(items[i]) : '');
                        firstElement = false;
                    } else {
                        text += ', '+(metadata[currentColumn].exists(items[i]) ? metadata[currentColumn].export(items[i]) : '');
                    };
                };
            };
            text += '\n';
            exportCount += 1;
        };

        let textarea = '<textarea id="WkitTextArea" class="WkitClipBoard">';
        $('#leech_table').html(textarea);//replace existing table
        $('#WkitTextArea').val(text);
        $('#WkitTextArea').select();
        document.execCommand('copy');
        alert(exportCount+' items have been exported to the clipboard.');

        fetch_items('table')
            .then(function(){
            updatePage();
        })
    };

    function insertContainer(){
        /* build containers for the table elements */
        const helpURL = 'https://community.wanikani.com/t/userscript-wanikani-item-inspector/44564';
        let sectionContainer = '<div id="WkitTopBar" class="WkitTopBar"></div> ';
        let topBlock = '<div id="WkitControlBar" class="WkitControlBar">' +
            '<div class="WkitHeader">' +
            '<div class="WkitControlLeft">'+
            '<button id="WkitBackwardButton" type="button" class="WkitButton WkitButtonleft">&#9668</button>' +
            '<button id="WkitForwardButton" type="button" class="WkitButton WkitButtonleft">&#9658</button>' +
            '<select id="WkitTableSelector" class="WkitSelector" title="Choose the table you want to display"></select>'+
            '<button id="WkitToogleDisplay" type="button" class="WkitButton WkitButtonleft" title="Toggles between Tables and Lists of icons" style="font-size:18px; font-weight: bold; padding-bottom: 5px; padding-top: 3px; padding-left: 3px; padding-right: 4px;">&#9022;</button>' +
            '<button id="WkitToogleLanguage" type="button" class="WkitButton WkitButtonleft" title="Toggles the current page between English and Japanese\nPages other than the current one are unaffected." style="font-size:15px; font-weight: 500;" lang="JP"></button>' +
            '<button id="WkitToogleAudio" type="button" class="WkitButton WkitButtonleft" title="Toggles clicking on vocabulary items between\nlinks to the item page and playing audio." style="font-size:15px; font-weight: bold; padding-bottom: 1px"></button>' +
            '</div>' +
            '<p class="WkitTitle"><b>Wanikani Item Inspector</b></p>' +
            '<div class="WkitControlRight">'+
            //'<div id="WkitDocumentation" class="WkitButton WkitButtonLook WkitButtonRight" title="Go to Item Inspector Page" style="font-size:22px; font-weight: bold; padding: 4px"><a href="'+helpURL+'" target="_blank">?</a></div>' +
            '<button id="WkitDocumentation"type="button" class="WkitButton WkitButtonRight" title="Go to Item Inspector Page" style="font-size:22px; font-weight: bold; padding: 4px"><a href="'+helpURL+'" target="_blank">?</a></button>' +
            '<button id="WkitSettings" type="button" class="WkitButton WkitButtonRight icon-gear" title="Settings" style="font-size:20px;"></button>' +
            '<button id="WkitWordExport" type="button" class="WkitButton WkitButtonRight" title="Place table items in clipboard\nPaste in your software of choice" style="font-size:14px; padding-top: 3px; padding-right: 3px; padding-left: 5px; max-width: 20px;">&#9925;</button>' +
            '<button id="WkitExport" type="button" class="WkitButton WkitButtonRight fas fa-bullhorn" title="Export to format csv in clipboard\nPaste in your software of choice" style="font-size:20px;">&#8686;</button>' +
            '</div>' +
            '</div>' +
            '</div>'+
            '<div id="leech_table"><p>The table goes here</p></div>';

        //class="fas fa-bullhorn" class="fas fa-bullseye"

        if (quiz.settings.position === undefined) {quiz.settings.position = 2};
        let position = [".progress-and-forecast", '.progress-and-forecast', '.srs-progress',  '.span12 .row', '.span12 .row:last-of-type',][quiz.settings.position];
        if (quiz.settings.position == 0){
            $(position).before(sectionContainer);
        } else {
            $(position).after(sectionContainer);
        }
        // insert the top block - must be separate from the sectionContainer to work around a bug
        $('#WkitTopBar').append(topBlock);  // must be appended - someone else may be there due to the bug
        formatControlBar();
        populateDropdown();
    };

    function populateDropdown(){
        // Populate the dropdown with the configured tables
        let activeTable = quiz.settings.active_ipreset;
        var tableList = '';
        var ipresets = quiz.settings.ipresets;
        for (var table of ipresets) {
            tableList += '<option>' + table.name.replace(/</g,'&lt;').replace(/>/g,'&gt;') +'</option>'
        };
        var dropdown = $('#WkitTableSelector');
        dropdown.html(tableList);
        $('#WkitTableSelector').prop('selectedIndex',activeTable);
    };

    function formatControlBar (){
        if (quiz.settings.tablePresets[quiz.settings.active_ipreset].displayMeaning){$('#WkitToogleLanguage').html('字')} else {$('#WkitToogleLanguage').html('<span class="WkitEnglishButton">E</span>')};
        if (quiz.settings.audioMode){$('#WkitToogleAudio').html('<span style="padding-left: 2px;">&#9654;</span>')} else {$('#WkitToogleAudio').html('<span style="text-decoration: underline; font-size: 18px; padding-bottom: 8px; padding-left: 0px;">L</span>')};
    };

    function eventHandlers() {
        /* Define the button actions. Must be done when the DOC is completed */
        $("#WkitBackwardButton").click(clickedBackward);
        $("#WkitForwardButton").click(clickedForward);
        $('#WkitTableSelector').change(selectTable);
        $('#WkitToogleDisplay').click(toggleDisplay);
        $('#WkitToogleLanguage').click(toggleLanguage);
        $('#WkitToogleAudio').click(toggleAudio);
        $("#WkitWordExport").click(fillClipboard);
        $("#WkitExport").click(exportTable);
        $("#WkitSettings").click(open_settings_dialog);
    };

    //------------------------------------------
    // Starting the program
    // at the end to ensure the global variables are defined
    //------------------------------------------
    var notAdditionalFilters = false;
    function check_additional_filters(){
        if (!window.wkof.ItemData.registry.sources.wk_items.filters.additionalFilters_leechTraining) {
            script_name = 'Wanikani Item Inspector';
            response = confirm(script_name + ' requires  WaniKani Open Framework Additional Filters.\nIf you have already installed it please enable the filters in the settings.\n Click "OK" to be forwarded to installation instructions.');
            if (response) {
                window.location.href = 'https://community.wanikani.com/t/userscript-wanikani-open-framework-additional-filters-recent-lessons-leech-training-related-items-and-more/30512';
            };
        };
        notAdditionalFilters = true;
    };

    function initSequence(){
        if (notAdditionalFilters){
            install_css();
            install_menu();
            init_settings();
            setup_quiz_settings();
            wkof.Settings.load(scriptId)
               .then( function(){
                    table_css();
                    init_settings();
                    add_defaults();
                    initCurrentItem();
                    setNumberOfLines();
                    insertContainer();
                    eventHandlers();
                    fetch_items()
                       .then(updatePage);
            })
        }
    }

    wkof.include('ItemData, Menu, Settings')
    wkof.ready('ItemData, Menu, Settings')
        .then(check_additional_filters)
        .then(initSequence)

    if (notAdditionalFilters){return};

    // 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或关注我们的公众号极客氢云获取最新地址