您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Inspect Items in Tabular Format
当前为
// ==UserScript== // @name WaniKani Item Inspector // @namespace wk-dashboard-item-inspector // @description Inspect Items in Tabular Format // @author prouleau // @version 1.3.1 // @include https://www.wanikani.com/dashboard // @include https://www.wanikani.com/ // @grant none // ==/UserScript== (function() { 'use strict'; //------------------------------ // Wanikani Framework //------------------------------ if (!window.wkof) { let response = confirm('WaniKani Dashboard Leech List script 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 quiz_setup_state = 'init'; function open_settings_dialog() { wkof.Settings.load('Item_Inspector') .then(function(){ quiz_setup_state = 'ready'; init_settings(); }) open_quiz_settings(); } //------------------------------ // Styling //------------------------------ var leechTableCss = ` .noLeeches { margin: 0 0 20px 0; } .radicalCharacterImgSize { font-size: 12px; } /* Control Bar */ .WkitControlBar { margin: 7px 0.6% 7px 2.6%; } /* to prevent rendering problems */ .WkitTopBar { display: inline-block; width: 103%; margin-left: -29px; } .WkitHeader { background-color: LightGray; height: 40px; border-radius: 5px; } .WkitTitle { font-size: 150%; padding-right: 285px; text-align: center; } .WkitButton { display: inline; float: left; border-width: 1px; border-radius: 3px; margin: 5px; margin-top: 8px; } .WkitSelector { display: inline; float: left; margin-left: 5px; margin-top: 5px; border-width: 1px; border-color: Black; color: Black; background-color: LightGray; } .emptyMessage { margin: 7px 0.6% 7px 2.6%; padding: 5px; padding-left: 15px; padding-top: 10px; background-color: Azure; border-radius: 8px; } /* TOOL TIPS */ .WkisTooltip { position: relative; display: inline-block; } .WkisTooltip .WkisTooltipContent { display: none; visibility: hidden; background-color: black; color: #fff; font-size: 100%; width: max-content; text-align: left; margin: 5px; padding: 5px; border-radius: 3px; position: absolute; bottom: 30px; left: 0%; z-index: 1; } .WkisTooltip:hover div.WkisTooltipContent { visibility: visible; display: inline-block; z-index: 50; } .WkisTooltip .WkisTooltipContent::after { content: " "; position: absolute; top: 100%; /* At the bottom of the tooltip */ left: 1em; margin-left: -5px; border-width: 5px; border-style: solid; border-color: black transparent transparent transparent; } .WkisLabel, { width: max-content; padding: 4px; } .WkisTipValue { width: max-content; padding: 4px; } /* END TOOL TIPS /*makes space*/ #leech-table:after{ clear: none; } #leaderboard:after{ clear: none; }`; 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: 'Item_Inspector', name: 'Item_Inspector', 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'; 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; var config = { script_id: 'Item_Inspector', 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: {type:'page',label:'Settings',content:{ hoursFormat: {type:'dropdown',label:'Hours Format',hover_tip:'Choose the format for displaying hours',content:{'12hours':'12h', '24hours':'24h',} }, }}, 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,on_change:refresh_table_on_preset_click,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:{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.', on_change:update_table_presets, content:{'Meaning_Brief':'Meaning Brief', 'Meaning_Full':'Meaning Full', 'Reading_Brief':'Reading Brief', 'Reading_Full':'Reading Full', 'Leech':'Leach Value', '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', 'Level':'Level', 'Srs':'SRS Stage', 'Review_Date':'Review Date', 'Review_Wait':'Review Wait Time', 'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Lesson_Date':'Lesson Date',} }, sort1: {type:'dropdown',label:'Primary Sort Criterion',hover_tip:'Items will be sorted by this criterion.', on_change:update_table_presets, content:{'Default':'Default', 'Leech':'Leach Value', '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', 'Level':'Level', 'Srs':'SRS Stage', 'Review_Date':'Review Date', 'Review_Wait':'Review Wait Time', 'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Lesson_Date':'Lesson Date',} }, sort2: {type:'dropdown',label:'Secondary Sort Criterion',hover_tip:'Items will be sorted by this criterion when the primary criterion is of equal values.', on_change:update_table_presets, content:{'Default':'Default', 'Leech':'Leach Value', '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', 'Level':'Level', 'Srs':'SRS Stage', 'Review_Date':'Review Date', 'Review_Wait':'Review Wait Time', 'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Lesson_Date':'Lesson Date'} }, sect_tbl_tooltips:{type:'section',label:'Table Tooltips'}, tooltip1: {type:'dropdown',label:'First Tooltip Element',hover_tip:'The first line of data that will be displayed on the tooltip.', on_change:update_table_presets, content:{'None':'None', 'Meaning_Brief':'Meaning Brief', 'Meaning_Full':'Meaning Full', 'Reading_Brief':'Reading Brief', 'Reading_Full':'Reading Full', 'Leech':'Leach Value', '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', 'Level':'Level', 'Srs':'SRS Stage', 'Review_Date':'Review Date', 'Review_Wait':'Review Wait Time', 'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Lesson_Date':'Lesson Date',} }, tooltip2: {type:'dropdown',label:'Second Tooltip Element',hover_tip:'The second line of data that will be displayed on the tooltip.', on_change:update_table_presets, content:{'None':'None', 'Meaning_Brief':'Meaning Brief', 'Meaning_Full':'Meaning Full', 'Reading_Brief':'Reading Brief', 'Reading_Full':'Reading Full', 'Leech':'Leach Value', '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', 'Level':'Level', 'Srs':'SRS Stage', 'Review_Date':'Review Date', 'Review_Wait':'Review Wait Time', 'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Lesson_Date':'Lesson Date',} }, tooltip3: {type:'dropdown',label:'Third Tooltip Element',hover_tip:'The third line of data that will be displayed on the tooltip.', on_change:update_table_presets, content:{'None':'None', 'Meaning_Brief':'Meaning Brief', 'Meaning_Full':'Meaning Full', 'Reading_Brief':'Reading Brief', 'Reading_Full':'Reading Full', 'Leech':'Leach Value', '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', 'Level':'Level', 'Srs':'SRS Stage', 'Review_Date':'Review Date', 'Review_Wait':'Review Wait Time', 'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Lesson_Date':'Lesson Date',} }, tooltip4: {type:'dropdown',label:'Fourth Tooltip Element',hover_tip:'The fourth line of data that will be displayed on the tooltip.', on_change:update_table_presets, content:{'None':'None', 'Meaning_Brief':'Meaning Brief', 'Meaning_Full':'Meaning Full', 'Reading_Brief':'Reading Brief', 'Reading_Full':'Reading Full', 'Leech':'Leach Value', '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', 'Level':'Level', 'Srs':'SRS Stage', 'Review_Date':'Review Date', 'Review_Wait':'Review Wait Time', 'Passed_Date':'Passed Guru Date', 'Burned_Date':'Burned Date', 'Lesson_Date':'Lesson Date',} }, sect_tbl_settings:{type:'section',label:'Other Settings'}, 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.', on_change:update_table_presets,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', on_change:update_table_presets,default:0 }, }, }}, }}, }}, }, }}; populate_items_config(config); quiz.settings_dialog = new wkof.Settings(config); wkof.Settings.load('Item_Inspector') .then(function(){ quiz_settings_state = 'ready'; open_quiz_settings(); }) } //======================================================================== // 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() //------------------------------------------------------------------------ function open_quiz_settings() { if (quiz_settings_state !== 'ready') return 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); quiz.settings_dialog.open(); } //======================================================================== // save_quiz_settings() //------------------------------------------------------------------------ function save_quiz_settings(settings) { quiz.settings = settings; populate_presets($('#Item_Inspector_source'), settings.ipresets, settings.active_ipreset); // check if the table list is empty if (!quiz.settings.ipresets.length){ init_settings(); // restore defaults }; fetch_items() .then(function(){ initCurrentItem(); updatePage() }) } //======================================================================== // close_quiz_settings() //------------------------------------------------------------------------ function close_quiz_settings(settings) { quiz_settings_state = 'setup'; } //======================================================================== // 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); refresh_table_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; 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:{table_data:"Leech",sort1:"Default",sort2:"Default",tooltip1:"Meaning_Full",tooltip2:"Reading_Full",tooltip3:"None",tooltip4:"None",randomSelection:0,leechStreakLimit:0}}}, {name:'Failed Last Review', content:{wk_items:{enabled:true,filters:{additionalFilters_failedLastReview:{enabled:true,value:24}}}, tableContents:{table_data:"Leech",sort1:"Default",sort2:"Default",tooltip1:"Meaning_Full",tooltip2:"Reading_Full",tooltip3:"None",tooltip4:"None",randomSelection:0,leechStreakLimit:0}}}, {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:{table_data:"Srs",sort1:"Default",sort2:"Default",tooltip1:"Review_Date",tooltip2:"Review_Wait",tooltip3:"None",tooltip4:"None",randomSelection:0,leechStreakLimit:0}}}, {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:{table_data:"Srs",sort1:"Default",sort2:"Default",tooltip1:"Review_Date",tooltip2:"Review_Wait",tooltip3:"None",tooltip4:"None",randomSelection:0,leechStreakLimit:0}}}, ]; table_defaults = {table_data:"Leech",sort1:"Default",sort2:"Default",tooltip1:"Meaning_Full",tooltip2:"Reading_Full",tooltip3:"None",tooltip4:"None",randomSelection:0,leechStreakLimit:0,}; // 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); } } if (settings.table_data) {refresh_table_presets(settings.active_ipreset)}; 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,'<').replace(/>/g,'>'); 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); refresh_table_presets(active_preset); } //======================================================================== // refresh_table_presets() //------------------------------------------------------------------------ function refresh_table_presets(active_preset){ // Convert tablePresets to new Review_Date name and add the fouth tooltip, sort keys, randomSelection and leechStreakLimit 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].sort2 == undefined){tablePresets[i].sort2 = 'Default'}; if (tablePresets[i].tooltip4 == undefined){tablePresets[i].tooltip4 = 'None'}; if (tablePresets[i].randomSelection == undefined){tablePresets[i].randomSelection = 0}; if (tablePresets[i].leechStreakLimit == undefined){tablePresets[i].leechStreakLimit = 0}; } // change the settings the framework wants to use and let the framework // update the UI. let presets = quiz.settings.tablePresets[active_preset]; quiz.settings.table_data = presets.table_data; quiz.settings.sort1 = presets.sort1; quiz.settings.sort2 = presets.sort2; quiz.settings.tooltip1 = presets.tooltip1; quiz.settings.tooltip2 = presets.tooltip2; quiz.settings.tooltip3 = presets.tooltip3; quiz.settings.tooltip4 = presets.tooltip4; quiz.settings.randomSelection = presets.randomSelection; quiz.settings.leechStreakLimit = presets.leechStreakLimit; } //======================================================================== // refresh_table_on_preset_click() //------------------------------------------------------------------------ function refresh_table_on_preset_click(){ // change the settings the framework wants to use and let the framework // update the UI. let active_preset = $('#Item_Inspector_active_ipreset').prop('selectedIndex'); let presets = quiz.settings.tablePresets[active_preset]; quiz.settings.table_data = presets.table_data; quiz.settings.sort1 = presets.sort1; quiz.settings.sort2 = presets.sort2; quiz.settings.tooltip1 = presets.tooltip1; quiz.settings.tooltip2 = presets.tooltip2; quiz.settings.tooltip3 = presets.tooltip3; quiz.settings.tooltip4 = presets.tooltip4; quiz.settings.randomSelection = presets.randomSelection; quiz.settings.leechStreakLimit = presets.leechStreakLimit; } //======================================================================== // update_table_presets() //------------------------------------------------------------------------ function update_table_presets(){ // copy the settings the framework wants to use and let the framework // manage the UI. let active_preset = $('#Item_Inspector_active_ipreset').prop('selectedIndex'); let presets = quiz.settings.tablePresets[active_preset]; presets.table_data = quiz.settings.table_data; presets.sort1 = quiz.settings.sort1; presets.sort2 = quiz.settings.sort2; presets.tooltip1 = quiz.settings.tooltip1; presets.tooltip2 = quiz.settings.tooltip2; presets.tooltip3 = quiz.settings.tooltip3; presets.tooltip4 = quiz.settings.tooltip4; presets.randomSelection = quiz.settings.randomSelection; presets.leechStreakLimit = quiz.settings.leechStreakLimit; } //######################################################################## // 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() { 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 ((settingName != 'randomSelection') && (settingName != 'leechStreakLimit') && (setting != 'None') && (setting != 'Default')) {src_cfg.options[metadata[setting].endPoint] = true}; } if (presets.leechStreakLimit != 0) {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 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': 'EN: ', 'tableEntry': meaningsBrief, 'tooltipEntry': meaningsBrief, 'sortkey': ((item) => {return 0}), 'sortOrder': 'Ascending', 'sortkey2': ((item) => {return 0}), 'sortOrder2': 'Ascending', 'endPoint' : 'subjects', }, 'Meaning_Full': {'exists': ((item) => {return true}), 'label': 'EN: ', 'tableEntry': meaningsFullTable, 'tooltipEntry': meaningsFull, 'sortkey': ((item) => {return 0}), 'sortOrder': 'Ascending', 'sortkey2': ((item) => {return 0}), 'sortOrder2': 'Ascending', 'endPoint' : 'subjects', }, 'Reading_Brief': {'exists': ((item) => {return item.data.readings}), 'label': 'JP: ', 'tableEntry': readingsBrief, 'tooltipEntry': readingsBrief, 'sortkey': ((item) => {return 0}), 'sortOrder': 'Ascending', 'sortkey2': ((item) => {return 0}), 'sortOrder2': 'Ascending', 'endPoint' : 'subjects', }, 'Reading_Full': {'exists': ((item) => {return item.data.readings}), 'label': 'JP: ', 'tableEntry': readingsFull, 'tooltipEntry': readingsFull, 'sortkey': ((item) => {return 0}), 'sortOrder': 'Ascending', 'sortkey2': ((item) => {return 0}), 'sortOrder2': 'Ascending', 'endPoint' : 'subjects', }, 'Leech': {'exists': ((item) => {return true}), 'label': 'Leech: ', 'tableEntry': leechScore, 'tooltipEntry': leechScore, 'sortkey': leechScore, 'sortOrder': 'Descending', 'sortkey2': ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct : 101)}), 'sortOrder2': 'Ascending', 'endPoint' : 'review_statistics', }, 'Percentage_Correct': {'exists': ((item) => {return true}), 'label': '%Total: ', 'tableEntry': ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct+'%' : 'None')}), 'tooltipEntry': ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct+'%' : 'None')}), 'sortkey': ((item) => {return (item.review_statistics ? item.review_statistics.percentage_correct : 101)}), 'sortOrder': 'Ascending', 'sortkey2': leechScore, 'sortOrder2': 'Descending', 'endPoint' : 'review_statistics', }, 'Meaning_Correct': {'exists': ((item) => {return true}), 'label': '%Meaning: ', 'tableEntry': ((item) => {return (item.review_statistics ? MeaningCorrect(item)+'%' : 'None')}), 'tooltipEntry': ((item) => {return (item.review_statistics ? MeaningCorrect(item)+'%' : 'None')}), 'sortkey': ((item) => {return (item.review_statistics ? MeaningCorrect(item) : 101)}), 'sortOrder': 'Ascending', 'sortkey2': leechScore, 'sortOrder2': 'Descending', 'endPoint' : 'review_statistics', }, 'Reading_Correct': {'exists': ((item) => {return item.data.readings}), 'label': '%Reading: ', 'tableEntry': ((item) => {return (item.review_statistics ? ReadingCorrect(item)+'%' : 'None')}), 'tooltipEntry': ((item) => {return (item.review_statistics ? ReadingCorrect(item)+'%' : 'None')}), 'sortkey': ((item) => {return (item.review_statistics ? ReadingCorrect(item) : 101)}), 'sortOrder': 'Ascending', 'sortkey2': leechScore, 'sortOrder2': 'Descending', 'endPoint' : 'review_statistics', }, 'Meaning_Current_Streak': {'exists': ((item) => {return true}), 'label': 'Meaning Current Streak: ', 'tableEntry': ((item) => {return (item.review_statistics.meaning_current_streak - 1)}), 'tooltipEntry': ((item) => {return (item.review_statistics.meaning_current_streak -1)}), 'sortkey': ((item) => {return (item.review_statistics.meaning_current_streak)}), 'sortOrder': 'Ascending', 'sortkey2': leechScore, 'sortOrder2': 'Descending', 'endPoint' : 'review_statistics', }, 'Reading_Current_Streak': {'exists': ((item) => {return true}), 'label': 'Reading Current Streak: ', 'tableEntry': ((item) => {return (item.review_statistics.reading_current_streak - 1)}), 'tooltipEntry': ((item) => {return (item.review_statistics.reading_current_streak - 1)}), 'sortkey': ((item) => {return (item.review_statistics.reading_current_streak)}), 'sortOrder': 'Ascending', 'sortkey2': leechScore, 'sortOrder2': 'Descending', 'endPoint' : 'review_statistics', }, 'Level': {'exists': ((item) => {return true}), 'label': 'Level: ', 'tableEntry': ((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', }, 'Srs': {'exists': ((item) => {return true}), 'label': 'SRS: ', 'tableEntry': ((item) => {return (item.assignments ? (item.assignments.srs_stage != undefined ? srsName[item.assignments.srs_stage] : 'Locked') : 'Locked')}), 'tooltipEntry': ((item) => {return (item.assignments ? (item.assignments.srs_stage != undefined ? srsName[item.assignments.srs_stage] : 'Locked') : 'Locked')}), 'sortkey': ((item) => {return (item.assignments ? (item.assignments.srs_stage != undefined ? item.assignments.srs_stage : 10) : 10)}), 'sortOrder': 'Ascending', 'sortkey2': ((item) => {return (item.assignments ? (item.assignments.available_at != undefined ? Date.parse(item.assignments.available_at) : theFuture) : theFuture)}), 'sortOrder2': 'Ascending', 'endPoint' : 'assignments', }, 'Review_Date': {'exists': ((item) => {return true}), 'label': 'Review: ', 'tableEntry': ((item) => {return (item.assignments ? (item.assignments.available_at != undefined ? makeDate(item) : 'Unscheduled') : 'Unscheduled')}), 'tooltipEntry': ((item) => {return (item.assignments ? (item.assignments.available_at != undefined ? makeDate(item) : 'Unscheduled') : 'Unscheduled')}), 'sortkey': ((item) => {return (item.assignments ? (item.assignments.available_at != undefined ? trimDate(Date.parse(item.assignments.available_at)) : theFuture) : theFuture)}), 'sortOrder': 'Ascending', 'sortkey2': ((item) => {return (item.assignments ? (item.assignments.srs_stage != undefined? item.assignments.srs_stage : 10) : 10)}), 'sortOrder2': 'Ascending', 'endPoint' : 'assignments', }, 'Review_Wait': {'exists': ((item) => {return true}), 'label': 'Wait: ', 'tableEntry': ((item) => {return (item.assignments ? (item.assignments.available_at != undefined ? reviewWait(item) : 'Unscheduled') : 'Unscheduled')}), 'tooltipEntry': ((item) => {return (item.assignments ? (item.assignments.available_at != undefined ? reviewWait(item) : 'Unscheduled') : 'Unscheduled')}), 'sortkey': ((item) => {return (item.assignments ? (item.assignments.available_at != undefined ? trimDate(Date.parse(item.assignments.available_at)) : theFuture) : theFuture)}), 'sortOrder': 'Ascending', 'sortkey2': ((item) => {return (item.assignments ? (item.assignments.srs_stage != undefined? item.assignments.srs_stage : 10) : 10)}), 'sortOrder2': 'Ascending', 'endPoint' : 'assignments', }, 'Passed_Date': {'exists': ((item) => {return true}), 'label': 'Passed: ', 'tableEntry': ((item) => {return (item.assignments ? (item.assignments.passed_at != undefined ? makePassedDate(item) : 'Not yet') : 'Not yet')}), 'tooltipEntry': ((item) => {return (item.assignments ? (item.assignments.passed_at != undefined ? makePassedDate(item) : 'Not yet') : 'Not yet')}), 'sortkey': ((item) => {return (item.assignments ? (item.assignments.passed_at != undefined ? trimDate(Date.parse(item.assignments.passed_at)) : 0) : 0)}), 'sortOrder': 'Ascending', 'sortkey2': ((item) => {return (item.assignments ? (item.assignments.srs_stage != undefined ? item.assignments.srs_stage : 10) : 10)}), 'sortOrder2': 'Ascending', 'endPoint' : 'assignments', }, 'Burned_Date': {'exists': ((item) => {return true}), 'label': 'Burned: ', 'tableEntry': ((item) => {return (item.assignments ? (item.assignments.burned_at != undefined ? makeBurnedDate(item) : 'Not yet') : 'Not yet')}), 'tooltipEntry': ((item) => {return (item.assignments ? (item.assignments.burned_at != undefined ? makeBurnedDate(item) : 'Not yet') : 'Not yet')}), 'sortkey': ((item) => {return (item.assignments ? (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', }, 'Lesson_Date': {'exists': ((item) => {return true}), 'label': 'Lesson: ', 'tableEntry': ((item) => {return (item.assignments ? (item.assignments.started_at != undefined ? makeLessonDate(item) : 'Not yet') : 'Not yet')}), 'tooltipEntry': ((item) => {return (item.assignments ? (item.assignments.started_at != undefined ? makeLessonDate(item) : 'Not yet') : 'Not yet')}), 'sortkey': ((item) => {return (item.assignments ? (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', }, } 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){ let reviewStats = item.review_statistics; if (reviewStats){ 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 makePassedDate(item){ return formatDate(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 makeLessonDate(item){ return formatDate(new Date(item.assignments.started_at), false, /* is_next_date */); } //======================================================================== // Print date in pretty format. //------------------------------------------------------------------- function formatDate(d, is_next_date){ 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 { 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){ var meanings = item.data.meanings[0].meaning; for (var k = 1; k < item.data.meanings.length; k++){ if(item.data.meanings[k].primary){ meanings = item.data.meanings[k].meaning + ', ' + meanings; } else { meanings += ', ' + item.data.meanings[k].meaning}; } 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() { let leechStreakLimit = quiz.settings.tablePresets[quiz.settings.active_ipreset].leechStreakLimit; if (leechStreakLimit != 0) { quiz.items = quiz.items.filter((item => {return (item.review_statistics.meaning_current_streak < (leechStreakLimit+1)) || (item.review_statistics.reading_current_streak < (leechStreakLimit+1))})); }; let randomSelection = quiz.settings.tablePresets[quiz.settings.active_ipreset].randomSelection; if (randomSelection != 0){ quiz.items = shuffle(quiz.items).slice(0, randomSelection); }; let tableKey = quiz.settings.tablePresets[quiz.settings.active_ipreset].table_data; let sort1 = quiz.settings.tablePresets[quiz.settings.active_ipreset].sort1; let sort2 = quiz.settings.tablePresets[quiz.settings.active_ipreset].sort2; let sortKey, sortOrder; let sortKey2, sortOrder2; if ((sort1 == "Default") && (sort2 == 'Default')){ sortKey = metadata[tableKey].sortkey; sortOrder = metadata[tableKey].sortOrder; sortKey2 = metadata[tableKey].sortkey2; sortOrder2 = metadata[tableKey].sortOrder2; } else if ((sort1 == "Default") && (sort2 != "Default")){ sortKey = metadata[tableKey].sortkey; sortOrder = metadata[tableKey].sortOrder; sortKey2 = metadata[sort2].sortkey; sortOrder2 = metadata[sort2].sortOrder; } else if ((sort1 != "Default") && (sort2 == "Default")){ sortKey = metadata[sort1].sortkey; sortOrder = metadata[sort1].sortOrder; sortKey2 = metadata[sort1].sortkey2; sortOrder2 = metadata[sort1].sortOrder2; } else { sortKey = metadata[sort1].sortkey; sortOrder = metadata[sort1].sortOrder; sortKey2 = metadata[sort2].sortkey; 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)}); }; createTopLeechTables(quiz.items); } function itemsCharacterCallback (itemsData){ //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(itemsData.characters!= null) { return itemsData.characters; } else if (itemsData.character_images!= null){ let imgUrl = ''; for (let i = 0; i < itemsData.character_images.length; i++) { if (itemsData.character_images[i].content_type == "image/png"){ imgUrl = itemsData.character_images[i].url; } } return '<img class="radical-'+itemsData.slug+' radicalCharacterImgSize" src="'+imgUrl+'"></img>'; } else { //if both characters and character_images are somehow absent try using slug instead return itemsData.slug; } } function makeTableEntry(item, selectedTable){ let tableData = metadata[quiz.settings.tablePresets[selectedTable].table_data]; return (tableData.exists(item) ? tableData.tableEntry(item) : '') } function makeTooltips(item, selectedTable){ let html = ''; let presets = quiz.settings.tablePresets[selectedTable] let tableName = presets.tooltip1; if (tableName != "None") { let tableData = metadata[tableName]; if (tableData.exists(item)) { html += '<tr><td class="WkisLabel">'+tableData.label+'</td><td class="WkisTipValue">'+tableData.tooltipEntry(item)+'</td></tr>' }; }; tableName = presets.tooltip2; if (tableName != "None") { let tableData = metadata[tableName]; if (tableData.exists(item)) { html += '<tr><td class="WkisLabel">'+tableData.label+'</td><td class="WkisTipValue">'+tableData.tooltipEntry(item)+'</td></tr>' }; }; tableName = presets.tooltip3; if (tableName != "None") { let tableData = metadata[tableName]; if (tableData.exists(item)) { html += '<tr><td class="WkisLabel">'+tableData.label+'</td><td class="WkisTipValue">'+tableData.tooltipEntry(item)+'</td></tr>' }; }; tableName = presets.tooltip4; if (tableName != "None" && tableName != undefined) { let tableData = metadata[tableName]; if (tableData.exists(item)) { html += '<tr><td class="WkisLabel">'+tableData.label+'</td><td class="WkisTipValue">'+tableData.tooltipEntry(item)+'</td></tr>' }; }; if (html != '') { html = '<div class="WkisTooltipContent"><table><tbody>'+html+'</tbody></table></div>'; }; return html; } /* control variables for the currently displayed tables*/ var currentItem = 0; var nbItems = 0; function initCurrentItem(){ currentItem = 0; } /* Callbacks for buttons */ function clickedBackward(event) { //test prevents multiple clicks if (!event.detail || event.detail == 1) { if ((currentItem - nbDisplayedItems) >= 0) { currentItem -= nbDisplayedItems; }; /* refresh the page */ createTopLeechTables(quiz.items); }; } function clickedForward(event) { //test prevents multiple clicks if (!event.detail || event.detail == 1) { if ((currentItem + nbDisplayedItems) < nbItems) { currentItem += nbDisplayedItems; }; /* refresh the page */ createTopLeechTables(quiz.items); }; } function selectTable(event) { let active_preset = quiz.settings.active_ipreset = $('#WkitTableSelector').prop('selectedIndex'); currentItem = 0; fetch_items() .then(function(){ updatePage() }) } const numberOfTables = 3; const leechesPerTable = 11; const nbDisplayedItems = numberOfTables * leechesPerTable; function createTopLeechTables(items) { let sectionContents = ""; let sectionContainer = ""; let topBlock = ""; 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; let activeTable = quiz.settings.active_ipreset; //make sure we don't create empty tables if there are too few leeches if(items.length == 0){ //if no leeches 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 leech tables for (var i = 0; i < nrOfTables; i++){ //In case there are less than the requested amount of leeches if(items.length <= endNumberTable){ endNumberTable = items.length; nrOfTables = i - 1; } sectionContents += ` <div class="span4" style="width=290px;"> <section class="kotoba-table-list dashboard-sub-section" style="position: relative;"> <h3 class="small-caps">Items ${startnumberTable+1}-${endNumberTable} of ${itemsLength}</h3> <table> <tbody>`; for (var j = startnumberTable; j < endNumberTable; j++){ let tableEntry = makeTableEntry(items[j], activeTable); let tooltip = makeTooltips(items[j], activeTable); sectionContents += `<tr class="${items[j].object}">` + '<td>' + `<div class="WkisTooltip"><a target="_blank" href="${items[j].data.document_url}"><span lang="ja">${itemsCharacterCallback(items[j].data)}</span></a>` + tooltip + '</div>' + '</td>' + '<td>' + '<div style="text-align: right"><a><span style="color: Gainsboro">'+tableEntry+'</span></a>' + '</div>' + '</td>' + '</tr>'; } //preparing for next table startnumberTable += numberPerTable; endNumberTable += numberPerTable; sectionContents += '</tbody>' + '</table>' + '</section>' + '</div>'; } /* build containers for the table elements */ sectionContainer = `<section id="WkitTopBar" class="WkitTopBar"></section> `; topBlock = '<div id="WkitControlBar" class="WkitControlBar">' + '<div class="WkitHeader">' + '<button id="WkitBackwardButton" type="button" class="WkitButton">◄</button>' + '<button id="WkitForwardButton" type="button" class="WkitButton">►</button>' + '<select id="WkitTableSelector" class="WkitSelector" title="Choose the table you want to display"></select>'+ '<p class="WkitTitle"><b>Wanikani Item Inspector</b></p>' + '</div>' + '</div>' + '<div id="leech_table"><p>The table goes here</p></div>'; //check if a leech table is already there, insert container and topBlock if not already there and then update the contents if(document.getElementById("leech_table")) { $('#leech_table').html(sectionContents);//replace existing table } else { // add containers since not there if ($('section.progression').length) { $('section.progression').after(sectionContainer); } else { $('section.srs-progress').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 // update contents $('#leech_table').html(sectionContents);//replace existing list }; // Populate the dropdown with the configured tables var tableList = ''; var ipresets = quiz.settings.ipresets; for (var table of ipresets) { tableList += '<option>' + table.name.replace(/</g,'<').replace(/>/g,'>') +'</option>' }; var dropdown = $('#WkitTableSelector'); dropdown.html(tableList); $('#WkitTableSelector').prop('selectedIndex',activeTable); } function eventHandlers() { /* Define the button actions. Must be done when the DOC is completed */ $("#WkitBackwardButton").click(clickedBackward); $("#WkitForwardButton").click(clickedForward); $('#WkitTableSelector').change(selectTable); }; function loadSettings() { wkof.Settings.load('Item_Inspector') .then(function(){ quiz_setup_state = 'ready'; var defaults = { }; var settings = $.extend(true, {}, defaults, wkof.settings.Item_Inspector); wkof.settings.Item_Inspector = quiz.settings = settings; }) } //------------------------------------------ // Starting the program // at the end to ensure the global variables are defined //------------------------------------------ wkof.include('ItemData, Menu, Settings'); wkof.ready('ItemData, Menu, Settings') .then(install_css) .then(install_menu) .then(init_settings) .then(function(){return wkof.Settings.load('Item_Inspector')}) .then(function(){ quiz_setup_state = 'ready'; var defaults = { }; var settings = $.extend(true, {}, defaults, wkof.settings.Item_Inspector); wkof.settings.Item_Inspector = quiz.settings = settings; }) .then(fetch_items) .then(function(){ initCurrentItem(); updatePage() }) .then(eventHandlers); })();