คุณต้องเข้าสู่ระบบหรือลงทะเบียนก่อนดำเนินการต่อ
Quiz yourself on Wanikani items
// ==UserScript== // @name Wanikani Self-Study Quiz // @namespace rfindley // @description Quiz yourself on Wanikani items // @version 3.1.9 // @match https://www.wanikani.com/* // @exclude https://www.wanikani.com/extra_study/* // @exclude https://www.wanikani.com/lesson/* // @exclude https://www.wanikani.com/review/* // @match https://preview.wanikani.com/* // @exclude https://preview.wanikani.com/extra_study/* // @exclude https://preview.wanikani.com/lesson/* // @exclude https://preview.wanikani.com/review/* // @require https://unpkg.com/[email protected]/umd/wanakana.min.js // @copyright 2022+, Robin Findley // @license MIT; http://opensource.org/licenses/MIT // @run-at document-end // @grant none // ==/UserScript== window.ss_quiz = {}; (function(gobj) { /* global $, wkof, wanakana */ /* eslint no-multi-spaces: "off" */ //=================================================================== // Initialization of the Wanikani Open Framework. //------------------------------------------------------------------- var script_name = 'Self-Study Quiz'; var wkof_version_needed = '1.1.3'; if (!window.wkof) { if (confirm(script_name+' requires Wanikani Open Framework.\nDo you want to be forwarded to the installation instructions?')) { window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549'; } return; } if (wkof.version.compare_to(wkof_version_needed) === 'older') { if (confirm(script_name+' requires Wanikani Open Framework version '+wkof_version_needed+'.\nDo you want to be forwarded to the update page?')) { window.location.href = 'https://greasyfork.org/en/scripts/38582-wanikani-open-framework'; } return; } wkof.include('Jquery,Menu'); wkof.ready('Jquery,Menu').then(install_menu); function install_menu() { wkof.Menu.insert_script_link({ name: 'selfstudyquiz', submenu: 'Open', title: 'Self-Study Quiz', on_click: open_quiz }); } //######################################################################## // 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: 'ss_quiz', title: 'Self-Study Quiz', pre_open: preopen_quiz_settings, on_save: save_quiz_settings, on_close: close_quiz_settings, on_refresh: refresh_quiz_settings, no_bkgd: true, settings: { pg_questions: {type:'page',label:'Questions',hover_tip:'Choose what quiz questions you want to be asked',content:{ grp_qpre_list: {type:'group',label:'Presets List',content:{ active_qpreset: {type:'list',refresh_on_change:true,hover_tip:'Question Presets',content:{}}, }}, grp_qpre: {type:'group',label:'Selected Preset',content:{ sect_qpre_name: {type:'section',label:'Preset Name'}, qpre_name: {type:'text',label:'Edit Preset Name',on_change:refresh_qpresets,path:'@qpresets[@active_qpreset].name',hover_tip:'Enter a name for the selected preset'}, sect_qpre_question: {type:'section',label:'Questions <span class="fa fa-circle-arrow-right icon-circle-arrow-right"></span> Answers'}, char2mean: {type:'checkbox',label:'Rad/Kan/Voc <span class="fa fa-circle-arrow-right icon-circle-arrow-right"></span> Meaning',path:'@qpresets[@active_qpreset].content.char2mean',hover_tip:'Question: A radical or kanji character, or vocab word drawn with kanji\nAnswer: The meaning in English'}, char2read: {type:'checkbox',label:'Kan/Voc <span class="fa fa-circle-arrow-right icon-circle-arrow-right"></span> Reading',path:'@qpresets[@active_qpreset].content.char2read',hover_tip:'Question: A kanji character, or vocab word drawn with kanji\nAnswer: The Japanese reading, in hiragana or katakana'}, read2mean: {type:'checkbox',label:'Voc Reading <span class="fa fa-circle-arrow-right icon-circle-arrow-right"></span> Meaning',path:'@qpresets[@active_qpreset].content.read2mean',hover_tip:'Question: A kanji or vocab reading, in hiragana or katakana\nAnswer: The meaning in English'}, mean2read: {type:'checkbox',label:'Voc Meaning <span class="fa fa-circle-arrow-right icon-circle-arrow-right"></span> Reading',path:'@qpresets[@active_qpreset].content.mean2read',hover_tip:'Question: A vocab word in English\nAnswer: The Japanese reading, in hiragana or katakana'}, aud2mean: {type:'checkbox',label:'Voc Audio <span class="fa fa-circle-arrow-right icon-circle-arrow-right"></span> Meaning',path:'@qpresets[@active_qpreset].content.aud2mean',hover_tip:'Question: A vocab word, in spoken audio\nAnswer: The meaning in English'}, aud2read: {type:'checkbox',label:'Voc Audio <span class="fa fa-circle-arrow-right icon-circle-arrow-right"></span> Reading',path:'@qpresets[@active_qpreset].content.aud2read',hover_tip:'Question: A vocab word, in spoken audio\nAnswer: The Japanese reading, in hiragana or katakana'}, }}, }}, pg_items: {type:'page',label:'Items',hover_tip:'Choose what items you want to be quizzed on',content:{ grp_ipre_list: {type:'group',label:'Presets List',content:{ active_ipreset: {type:'list',refresh_on_change:true,hover_tip:'Item Presets',content:{}}, }}, grp_ipre: {type:'group',label:'Selected Preset',content:{ sect_ipre_name: {type:'section',label:'Preset Name'}, ipre_name: {type:'text',label:'Edit Preset Name',on_change:refresh_ipresets,path:'@ipresets[@active_ipreset].name',hover_tip:'Enter a name for the selected preset'}, sect_ipre_srcs: {type:'section',label:'Item Sources'}, ipre_srcs: {type:'tabset',content:{}}, }}, }}, pg_opts: {type:'page',label:'Settings',hover_tip:'Configure the user interface settings',content:{ grp_quiz_size: {type:'group',label:'Quiz Size',content:{ max_quiz_size: {type:'number',label:'Maximum Quiz Size',hover_tip:'Set the approximate maximum quiz size. (0 for unlimited)',default:0}, }}, grp_synonyms: {type:'group',label:'Synonyms',content:{ synonyms_order: {type:'dropdown',label:'Synonym order in Help',hover_tip:'Set the order that synonyms appear in Help hints. (default: First)',default:'first',content:{first:'First',last:'Last'}}, }}, grp_typos: {type:'group',label:'Typo Tolerance',content:{ allow_typos: {type:'checkbox',label:'Allow typos',hover_tip:'When enabled, English answers with minor typos will be accepted.',default:true}, }}, grp_help: {type:'group',label:'Wrong Answers',content:{ autoshow_correct: {type:'checkbox',label:'Auto-show Correct Answer',hover_tip:'Automatically show the correct answer\nwhen you answer incorrectly.',default:false}, }}, grp_msgs: {type:'group',label:'Warning Messages',content:{ show_slightly_off: {type:'checkbox',label:'Answer is slightly off',path:'@messages.show_slightly_off',hover_tip:'Tells you when your answer is slightly off',default:true}, show_multi_reading: {type:'checkbox',label:'Has multiple readings',path:'@messages.show_multi_reading',hover_tip:'Tells you when an item has multiple readings',default:false}, }}, grp_halt: {type:'group',label:'Override Lightning',content:{ halt_slightly_off: {type:'checkbox',label:'Halt if slightly off',path:'@messages.halt_slightly_off',hover_tip:'Override lightning mode when your answer is slightly off',default:true}, halt_multi_reading: {type:'checkbox',label:'Halt if multiple readings',path:'@messages.halt_multi_reading',hover_tip:'Override lightning mode when an item has multiple readings',default:false}, }}, grp_audio: {type:'group',label:'Audio',content:{ audio_type: {type:'dropdown',label:'Audio file type',hover_tip:'Audio file type (default=mp3)',default:'mp3',content:{mp3:'mp3',ogg:'ogg'}}, audio_gender: {type:'dropdown',label:'Speaker',hover_tip:'',default:'rotate',content:{rotate:'Rotate',random:'Random',male:'Male',female:'Female'}}, }}, }}, }, }; populate_items_config(config); quiz.settings_dialog = new wkof.Settings(config); 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 preset">New</button>'+ '<button type="button" ref="###" action="up" class="ui-button ui-corner-all ui-widget" title="Move the selected preset up in the list"><span class="fa fa-arrow-up icon-style"></span></button>'+ '<button type="button" ref="###" action="down" class="ui-button ui-corner-all ui-widget" title="Move the selected preset down in the list"><span class="fa fa-arrow-down icon-style"></span></button>'+ '<button type="button" ref="###" action="delete" class="ui-button ui-corner-all ui-widget" title="Delete the selected preset">Delete</button>'+ '</div>'; var wrap = dialog.find('#ss_quiz_active_qpreset').closest('.row'); wrap.addClass('pre_list_wrap'); wrap.prepend(btn_grp.replace(/###/g, 'qpreset')); wrap.find('.pre_list_btn_grp').on('click', 'button', preset_button_pressed); wrap = dialog.find('#ss_quiz_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); $('#ss_quiz_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 = $('#ss_quiz_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_qpresets(); 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.qpre = JSON.stringify(quiz.settings.qpresets[quiz.settings.active_qpreset].content); 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($('#ss_quiz_qna'), settings.qpresets, settings.active_qpreset); populate_presets($('#ss_quiz_source'), settings.ipresets, settings.active_ipreset); var qpre = JSON.stringify(quiz.settings.qpresets[quiz.settings.active_qpreset].content); var ipre = JSON.stringify(quiz.settings.ipresets[quiz.settings.active_ipreset].content); var reshuffle = (qpre !== quiz.backup.qpre) || (quiz.settings.max_quiz_size !== quiz.backup.max_quiz_size); var refetch = (ipre !== quiz.backup.ipre); var redraw = (quiz.settings.synonyms_order !== quiz.backup.synonyms_order); delete quiz.backup; if (refetch) { fetch_items().then(quiz.start); } else if (reshuffle) { quiz.start(); } else if (redraw) { quiz.qinfo.cache = {}; quiz.ask(); } } //======================================================================== // close_quiz_settings() //------------------------------------------------------------------------ function close_quiz_settings(settings) { quiz_settings_state = 'setup'; } //======================================================================== // refresh_quiz_settings() //------------------------------------------------------------------------ function refresh_quiz_settings(settings) { $('#ss_quiz_ipre_srcs .wkof_group .row').each(function(i,e){ var row = $(e); var panel = row.closest('[role="tabpanel"]'); var source = panel.attr('id').match(/^ss_quiz_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_qpresets() //------------------------------------------------------------------------ function refresh_qpresets() { var settings = quiz.settings; populate_presets($('#ss_quiz_active_qpreset'), settings.qpresets, settings.active_qpreset); } //======================================================================== // refresh_ipresets() //------------------------------------------------------------------------ function refresh_ipresets() { var settings = quiz.settings; populate_presets($('#ss_quiz_active_ipreset'), settings.ipresets, settings.active_ipreset); } //======================================================================== // preset_button_pressed() //------------------------------------------------------------------------ function preset_button_pressed(e) { var settings = quiz.settings; 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 = $('#ss_quiz_active_'+ref); var dflt; if (ref === 'qpreset') { dflt = {name:'<untitled>', content:$.extend(true, {}, qpre_defaults)}; } else { dflt = {name:'<untitled>', content:$.extend(true, {}, ipre_defaults)}; } switch (action) { case 'new': presets.push(dflt); selected = presets.length - 1; settings[ref+'s'] = presets; settings['active_'+ref] = selected; populate_presets(elem, presets, selected); quiz.settings_dialog.refresh(); $('#ss_quiz_'+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)); selected--; settings[ref+'s'] = presets; settings['active_'+ref] = selected; 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)); selected++; settings[ref+'s'] = presets; settings['active_'+ref] = selected; populate_presets(elem, presets, selected); break; case 'delete': presets = presets.slice(0, selected).concat(presets.slice(selected+1)); if (presets.length === 0) presets = [dflt]; selected = Math.max(0, selected-1); settings[ref+'s'] = presets; settings['active_'+ref] = selected; populate_presets(elem, presets, selected); quiz.settings_dialog.refresh(); break; } } //======================================================================== // init_settings() //------------------------------------------------------------------------ var qpre_defaults = {char2mean:false, char2read:false, read2mean:false, mean2read:false, aud2mean:false, aud2read:false}; function init_settings() { var idx; // Merge some defaults var defaults = { pairing: 'reading_first', allow_typos: true, play_audio: true, mute_audio: false, autoshow_correct: false, max_quiz_size: 0, // 0 = unlimited messages: { show_slightly_off: true, show_multi_reading: false, halt_slightly_off: true, halt_multi_reading: false, } }; var settings = $.extend(true, {}, defaults, wkof.settings.ss_quiz); wkof.settings.ss_quiz = quiz.settings = settings; if (settings.qpresets === undefined) { settings.qpresets = [ {name:'All Questions', content:{char2mean:true, char2read:true, read2mean:true, mean2read:true, aud2mean:true, aud2read:true}}, {name:'Japanese to English', content:{char2mean:true, char2read:true, read2mean:false, mean2read:false, aud2mean:false, aud2read:false}}, {name:'English to Japanese', content:{char2mean:false, char2read:false, read2mean:false, mean2read:true, aud2mean:false, aud2read:false}}, {name:'Audio Quiz', content:{char2mean:false, char2read:false, read2mean:false, mean2read:false, aud2mean:true, aud2read:true}}, ]; settings.active_qpreset = 0; } for (idx in settings.qpresets) { settings.qpresets[idx].content = $.extend(true, {}, qpre_defaults, settings.qpresets[idx].content); } if (settings.messages === undefined) { settings.messages = {show_slightly_off:true, show_multi_reading:false, halt_slightly_off:true, halt_multi_reading:false} } if (settings.ipresets === undefined) { settings.ipresets = [ {name:'All Learned Items', content:{wk_items:{enabled:true,filters:{srs:{enabled:true,value:{appr1:true,appr2:true,appr3:true,appr4:true,guru1:true,guru2:true,mast:true,enli:true,burn:true}}}}}}, {name:'Apprentice Items', content:{wk_items:{enabled:true,filters:{srs:{enabled:true,value:{appr1:true,appr2:true,appr3:true,appr4:true}}}}}}, {name:'Burned Items', content:{wk_items:{enabled:true,filters:{srs:{enabled:true,value:{burn:true}}}}}}, {name:'R###rrected Items', content:{wk_items:{enabled:true,filters:{have_burned:{enabled:true,value:true},srs:{enabled:true,value:{appr1:true,appr2:true,appr3:true,appr4:true,guru1:true,guru2:true,mast:true,enli:true}}}}}}, ]; settings.active_ipreset = 0; } if (ipre_defaults) { for (idx in settings.ipresets) { settings.ipresets[idx].content = $.extend(true, {}, ipre_defaults, settings.ipresets[idx].content); } } } //======================================================================== // 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 = {}; for (var src_name in srcs) { var src = srcs[src_name]; var pg_content = {}; ipre_srcs['pg_'+src_name] = {type:'page',label:src.description,content:pg_content}; var settings = {}; ipre_defaults[src_name] = settings; pg_content[src_name+'_enable'] = { type:'checkbox', label:'Include this source', path:'@ipresets[@active_ipreset].content["'+src_name+'"].enabled', hover_tip:'Check to include this data source in the quiz' }; // Enable Wanikani source by default. settings.enabled = (src_name === 'wk_items'); // Add 'Options' section. 'wk_items' is handled automatically. if (src_name !== 'wk_items') { if (src.options && Object.keys(src.options).length > 0) { settings.options = {}; var opt_content = {}; pg_content['grp_'+src_name+'_options'] = {type:'group',label:'Options',content:opt_content}; for (var opt_name in src.options) { var opt = src.options[opt_name]; switch (opt.type) { case 'checkbox': opt_content[src_name+'_opt_'+opt_name] = { type:'checkbox', label:opt.label, default:opt.default, hover_tip:opt.hover_tip } break; } } } } // 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:'Filters',content:flt_content}; for (var flt_name in src.filters) { var flt = src.filters[flt_name]; var dflt; if (flt.no_ui) continue; 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', validate: flt.validate, hover_tip:flt.hover_tip } break; case 'list': case 'multi': 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:(flt.type === 'multi' ? true : false), 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', validate: flt.validate, hover_tip:flt.hover_tip } settings.filters[flt_name].value = dflt; break; case 'dropdown': dflt = flt.default; if (typeof flt.filter_value_map === 'function') dflt = flt.filter_value_map(dflt); flt_content[src_name+'_flt_'+flt_name] = { type:'dropdown', multi:false, 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', validate: flt.validate, 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', validate: flt.validate, 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, validate: flt.validate, 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(/^ss_quiz_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) {} } //######################################################################## // QUIZ DIALOG //######################################################################## //======================================================================== // install_css() //------------------------------------------------------------------------ function install_css() { $('head').append(` <style id="ss_quiz_css" type="text/css"> .noselect {-webkit-touch-callout:none; -webkit-user-select:none; -khtml-user-select:none; -moz-user-select: none; -ms-user-select:none; user-select: none;} #ss_quiz [lang="ja"] {font-family: "Meiryo","Yu Gothic","Hiragino Kaku Gothic Pro","TakaoPGothic","Yu Gothic","ヒラギノ角ゴ Pro W3","メイリオ","Osaka","MS PGothic","MS Pゴシック",sans-serif;} #ss_quiz {position:fixed; z-index:12001; width:573px; background-color:#000; border-radius:8px; border:8px solid rgba(0,0,0,0.85); font-size:16px; line-height:16px; font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;} #ss_quiz * {text-align:center;} #ss_quiz .titlebar {cursor:move; text-align:left; padding-bottom:4px; font-size:1.125em; font-weight:bold; line-height:1.125em; background-color:rgba(0,0,0,0.85); color:#ddd;} #ss_quiz .titlebar .button {display:inline-block; float:right; height:20px; width:20px; line-height:1em; cursor:pointer; border:1px solid rgba(255,255,255,0.2); border-radius:4px;} #ss_quiz .prev, #ss_quiz .next {display:inline-block; width:80px; color:#fff; line-height:8em; cursor:pointer;} #ss_quiz .prev:hover {background-image:linear-gradient(to left, rgba(0,0,0,0), rgba(0,0,0,0.2));} #ss_quiz .next:hover {background-image:linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.2));} #ss_quiz .prev {float:left;} #ss_quiz .next {float:right;} #ss_quiz .cfgbar {background-color:rgba(32,32,32,0.85); padding:4px 0; border-bottom:1px solid #444;white-space:nowrap;} #ss_quiz .cfgbar select {margin:0; background:transparent; color:rgba(255,255,255,0.5); border:1px solid #777; width:248px; height:2em; text-align:left; font-size:0.875em; border-radius:4px; padding:4px 6px;} #ss_quiz .cfgbar option {color:#000;} #ss_quiz .cfgbar .button {display:inline-block; width:24px; height:24px; cursor:pointer; color:#777; font-size:24px; vertical-align:middle;} #ss_quiz .cfgbar .button:hover {color:#ccc;} #ss_quiz .statusbar {line-height:1em; color:rgba(255,255,255,0.5); background-color:rgba(32,32,32,0.85);} #ss_quiz .settings {float:left; padding:6px 8px; text-align:left; line-height:1.5em; font-size:0.875em;} #ss_quiz .settings span[class*="icon-"] {font-size:1.3em; padding:0 2px;} #ss_quiz .settings .ss_audio {padding-left:0; padding-right:4px;} #ss_quiz .settings .ss_done {font-size:1.25em;} #ss_quiz .settings .ss_pair {font-weight:bold;} #ss_quiz .settings span {cursor:pointer;} #ss_quiz .settings > span:hover {color:rgba(255,255,204,0.8);} #ss_quiz .settings span.active {color:#ffc;} #ss_quiz .stats_labels {text-align:right; font-family:monospace; font-size:14px; line-height:14px; white-space:pre;} #ss_quiz .stats {float:right; text-align:right; color:rgba(255,255,255,0.8); font-family:monospace; padding:0 5px;} #ss_quiz .fa-audio:before {content:"\\f028";} #ss_quiz .ss_audio {display:inline-block;box-sizing:border-box;width:22px;text-align:left} #ss_quiz .ss_audio:before {content:"\\f026";} #ss_quiz .ss_audio.active:before {content:"\\f028";} #ss_quiz .ss_audio.mute {color:rgba(255,0,0,0.8);} #ss_quiz .ss_audio.mute:hover {color:rgba(255,127,127,0.8);} #ss_quiz[data-qtype="characters"] .question {font-size:2em;} #ss_quiz .question wk-character-image {display:inline-block;height:1em;width:1em;--color-text:white;} #ss_quiz .atype {font-size:1.75em; line-height:2em; cursor:default; color:#fff; border-top:1px solid #000; border-bottom:1px solid #000;} #ss_quiz[data-atype="reading"] .atype {color:#fff; text-shadow:-1px -1px 0 #000; border-top:1px solid #555; border-bottom:1px solid #000; background-color:#2e2e2e; background-image:linear-gradient(to bottom, #3c3c3c, #1a1a1a); background-repeat:repeat-x;} #ss_quiz[data-atype="meaning"] .atype {color:#555; text-shadow:-1px -1px 0 rgba(255,255,255,0.1); border-top:1px solid #d5d5d5; border-bottom:1px solid #c8c8c8; background-color:#e9e9e9; background-image:linear-gradient(to bottom, #eee, #e1e1e1); background-repeat:repeat-x;} #ss_quiz .help {display:none; position:absolute; top:3%; left:13%; width:74%; box-sizing:border-box; border:2px solid #000; border-radius:15px; padding:4px; color:#555; text-shadow:2px 2px 0 rgba(0,0,0,0.13); background-color:rgba(255,255,255,0.9); font-size:0.8em; line-height:1.2em; } #ss_quiz.help .help {display:inherit;} #ss_quiz .message {visibility:hidden; position:absolute; bottom:3%; left:13%; width:74%; box-sizing:border-box; border:2px solid #000; border-radius:15px; padding:4px; color:#555; text-shadow:2px 2px 0 rgba(0,0,0,0.13); background-color:rgba(255,255,255,0.9); font-size:0.6em; line-height:1.2em; opacity:0; transition:visibility 0.25s, opacity 0.25s linear; } #ss_quiz.message .message {visibility:visible; opacity:1; transition:visibility 0s, opacity 0.25s linear;} #wkof_ds #ss_quiz .answer {font-size:1.75em; background-color:#ddd; padding:8px;} #wkof_ds #ss_quiz .answer input { width:100%; background-color:#fff; height:2em; margin:0; border:2px solid #000; padding:0; box-sizing:border-box; border-radius:0; font-size:1em; } #wkof_ds #ss_quiz[data-r###lt="correct"] .answer input {color:#fff; background-color:#8c8; text-shadow:2px 2px 0 rgba(0,0,0,0.2);} #wkof_ds #ss_quiz[data-r###lt="incorrect"] .answer input {color:#fff; background-color:#f03; text-shadow:2px 2px 0 rgba(0,0,0,0.2);} #ss_quiz .btn.requiz {position:absolute; top:6px; right:6px; padding-left:6px; padding-right:6px;} #ss_quiz .qwrap {height:8em; position:relative; clear:both; font-size:1.75em} #ss_quiz[data-itype="radical"] .qwrap, #ss_quiz .summary .que[title~="Radical"] {background-color:var(--color-radical);} #ss_quiz[data-itype="kanji"] .qwrap, #ss_quiz .summary .que[title~="Kanji"] {background-color:var(--color-kanji);} #ss_quiz[data-itype="vocabulary"] .qwrap, #ss_quiz .summary .que[title~="Vocabulary"] {background-color:var(--color-vocabulary);} #ss_quiz[data-itype="kana_vocabulary"] .qwrap, #ss_quiz .summary .que[title~="Vocabulary"] {background-color:var(--color-vocabulary);} #ss_quiz .qwrap > .center {display:none; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);} #ss_quiz[data-mode="loading"] .qwrap {background-color:#ccc; opacity:0.5;} #wkof_ds #ss_quiz[data-mode="loading"] .answer {opacity:0.5;} #ss_quiz[data-mode="question"] .question {display:block;} #ss_quiz .question {overflow-x:auto; overflow-y:hidden; color:#fff; text-align:center; line-height:1.1em; font-size:1em; cursor:default;} #ss_quiz .question .fa-audio {font-size:2.5em; cursor:pointer;} #ss_quiz[data-mode="summary"] .summary {display:block;} #ss_quiz .summary {display:none; position:absolute; width:74%; height:100%; background-color:rgba(0,0,0,0.7); color:#fff; font-weight:bold;} #ss_quiz .summary h3 { background-image:linear-gradient(to bottom, #3c3c3c, #1a1a1a); background-repeat:repeat-x; border-top:1px solid #777; border-bottom:1px solid #000; margin:0; box-sizing:border-box; text-shadow:2px 2px 0 rgba(0,0,0,0.5); color:#fff; font-size:0.8em; font-weight:bold; line-height:40px; } #ss_quiz .summary .errors {position:absolute; top:40px; bottom:0px; width:100%; margin:0; overflow-y:auto; list-style-type:none;} #ss_quiz .summary li {margin:4px 0 0 0; font-size:0.6em; font-weight:bold; line-height:1.4em;} #ss_quiz .summary .errors span {display:inline-block; padding:2px 4px 0px 4px; border-radius:4px; line-height:1.1em; max-width:50%; vertical-align:middle; cursor:pointer;} #ss_quiz .errors .fa-long-arrow-right {padding:0px 8px;} #ss_quiz .summary .ans {background-color:#fff; color:#000;} #ss_quiz .summary .wrong {color:#f22;}`+ //--[ Settings dialog ]------------------------------------------- `#wkof_ds div[role="dialog"][aria-describedby="wkofs_ss_quiz"] {z-index:12002;} #wkofs_ss_quiz.wkof_settings .pre_list_btn_grp {width:60px;float:left;margin-right:2px;} #wkofs_ss_quiz.wkof_settings .pre_list_btn_grp button {width:100%; padding:2px 0;} #wkofs_ss_quiz.wkof_settings .pre_list_btn_grp button:not(:last-child) {margin-bottom:2px;} #wkofs_ss_quiz.wkof_settings .pre_list_wrap {display:flex;} #wkofs_ss_quiz.wkof_settings .pre_list_wrap .right {flex:1;} #wkofs_ss_quiz.wkof_settings .pre_list_wrap .list {overflow:auto;height:100%;} #wkofs_ss_quiz.wkof_settings .filters .row {border-top:1px solid #ccc; padding:6px 4px; margin-bottom:0;} #wkofs_ss_quiz.wkof_settings .filters .row:not(.checked) {padding-top:0px;padding-bottom:0px;} #wkofs_ss_quiz .filters .row .enable input[type="checkbox"] {margin:0;} #wkofs_ss_quiz.narrow .filters .row.checked .right input[type="checkbox"]:after {content:"⇐yes?";margin-left:28px;line-height:30px;} #wkofs_ss_quiz .filters .row.checked {background-color:#f7f7f7;} #wkofs_ss_quiz .filters .row:not(.checked) {opacity:0.5;} #wkofs_ss_quiz .filters .row .enable {display:inline; margin:0; float:left;} #wkofs_ss_quiz:not(.narrow) .filters .left {width:170px;} #wkofs_ss_quiz .filters .row .enable input[type="checkbox"] {margin:0 4px 0 0;} #wkofs_ss_quiz .filters .row:not(.checked) .right {display:none;} #wkofs_ss_quiz .filters .row:not(.checked) .left label {text-align:left;} #wkofs_ss_quiz.narrow .filters .row .left {width:initial;} #wkofs_ss_quiz.narrow .filters .row .left label {line-height:30px;} #wkofs_ss_quiz #ss_quiz_ipre_srcs .src_enable .left {width:initial;} #wkofs_ss_quiz #ss_quiz_ipre_srcs .src_enable .left label {text-align:left;width:initial;line-height:30px;} #wkofs_ss_quiz #ss_quiz_ipre_srcs .src_enable .right {float:left; margin:0 4px;width:initial;}`+ //---------------------------------------------------------------- '</style>' ); } //======================================================================== // open_quiz() //------------------------------------------------------------------------ var quiz_setup_state = 'init'; function open_quiz(custom_options) { if (quiz_setup_state === 'init') { quiz_setup_state = 'loading'; install_css(); wkof.include('ItemData, Settings'); wkof.ready('ItemData, Settings').then(function(){ return wkof.Settings.load('ss_quiz'); }).then(function(){ quiz_setup_state = 'ready'; init_settings(); open_quiz(custom_options); }); } if (quiz_setup_state !== 'ready') return; var quiz_html = '<div id="ss_quiz" class="dialog" data-itype="loading" data-atype="meaning" data-mode="question">'+ ' <div class="titlebar noselect">Self-Study Quiz<span class="button" title="Close the quiz.\nHotkey: Rapid triple-tap [Esc]">x</span></div>'+ ' <div class="cfgbar">'+ ' <select id="ss_quiz_qna" title="Choose what quiz questions you want to be asked"></select>'+ ' <select id="ss_quiz_source" title="Choose what items you want to be quizzed on"></select>'+ ' <span class="fa fa-repeat icon-style shuffle button" title="Shuffle Quiz (Ctrl-S)\nDouble-click to reset Round counter"></span>'+ ' <span class="fa fa-cog icon-style config button" title="Configure presets"></span>'+ ' </div>'+ ' <div class="statusbar">'+ ' <div class="settings noselect">'+ ' <span class="fa fa-bolt icon-style ss_lightning" title="Lightning Mode: Skip <enter> on correct answers (Ctrl-L)"></span>'+ ' <span class="fa fa-audio icon-style ss_audio" title="Toggle when to play audio (Ctrl-Shift-A)\n* Red = Never play audio\n* Gray = Audio questions only\n* Yellow = Audio questions, After correct reading, Opening help for reading\n\nTo play audio immediately, press (Ctrl-A)"></span>'+ ' <span class="fa fa-question icon-style ss_help" title="Help: Peek at item info (F1, Ctrl-H, or ?)"></span>'+ ' <span class="fa fa-step-forward icon-style ss_done" title="End the quiz and show summary (Esc or Ctrl-E)"></span><br />'+ ' <span class="ss_pair" title="Pairing mode: Group reading and meaning together (Ctrl-P)">Pairing: <span class="data">Disabled</span></span>'+ ' </div>'+ ' <div class="stats"></div>'+ ' <div class="stats_labels">Round:<br>Remaining:<br>Correct:<br>Incorrect:</div>'+ ' </div>'+ ' <div class="qwrap">'+ ' <div class="prev" title="Previous question (Ctrl-Left)"><i class="fa fa-chevron-left icon-style"></i></div>'+ ' <div class="next" title="Next question (Ctrl-Right)"><i class="fa fa-chevron-right icon-style"></i></div>'+ ' <div class="question center"></div>'+ ' <div class="help"></div>'+ ' <div class="message"></div>'+ ' <div class="summary center">'+ ' <h3>Summary - <span class="percent">100%</span> Correct <button class="btn requiz" title="Re-quiz wrong items">Re-quiz</button></h3>'+ ' <ul class="errors"></ul>'+ ' </div>'+ ' </div>'+ ' <div class="atype">Loading...</div>'+ ' <div class="answer"><input type="text" lang="en" value=""></div>'+ '</div>'; if (quiz.dialog) quiz.close(); var dialog = (quiz.dialog = $(quiz_html)); var settings = quiz.settings; init_custom_options(custom_options); populate_presets(dialog.find('#ss_quiz_qna'), settings.qpresets, settings.active_qpreset); populate_presets(dialog.find('#ss_quiz_source'), settings.ipresets, settings.active_ipreset); wkof.Settings.background.open(); $('#wkof_ds').append(dialog); dialog.css('top', Math.max(0,Math.floor((window.innerHeight - dialog.outerHeight()) / 2))); dialog.css('left', Math.floor((window.innerWidth - dialog.outerWidth()) / 2)); // Initialize settings var settings_bar = dialog.find('.statusbar .settings'); if (settings.lightning_mode === true) settings_bar.find('.ss_lightning').addClass('active'); if (settings.repeat_quiz === true) settings_bar.find('.ss_repeat').addClass('active'); if (settings.shuffle_on_repeat === true) settings_bar.find('.ss_shuffle').addClass('active'); if (settings.play_audio === true) settings_bar.find('.ss_audio').addClass('active'); if (settings.mute_audio === true) settings_bar.find('.ss_audio').addClass('mute'); toggle_pairing(null, true /* initialize */); // Events dialog.find('.settings .ss_lightning').on('click', toggle_lightning); dialog.find('.settings .ss_audio').on('click', toggle_audio); dialog.find('.settings .ss_help').on('click', toggle_help); dialog.find('.settings .ss_pair').on('click', toggle_pairing); dialog.find('.settings .ss_done').on('click', process_escape); dialog.find('.prev').on('click', quiz.prev); dialog.find('.next').on('click', quiz.next); dialog.find('.titlebar').on('mousedown touchstart', drag); dialog.find('.cfgbar .button.shuffle').on('click', manual_shuffle); dialog.find('.cfgbar .button.config').on('click', open_quiz_settings); dialog.find('.titlebar .button').on('click', close_quiz); dialog.find('.summary .requiz').on('click', quiz.requiz); dialog.find('.question').on('click', '.fa-audio', play_audio.bind(null,true,null)); $('#ss_quiz_qna').on('change', qpreset_changed); $('#ss_quiz_source').on('change', ipreset_changed); $('body').on('keydown.ss_quiz_key keypress.ss_quiz_key', quiz_key_handler); freeze_body(); set_mode('loading'); fetch_items().then(quiz.start); } //======================================================================== // init_custom_options() //------------------------------------------------------------------------ function init_custom_options(custom) { if (!custom) { quiz.custom = { has_ipreset: false, using_ipreset: false, has_qpreset: false, using_qpreset: false, } return; } quiz.custom = custom; if (custom.qpreset) { quiz.custom.has_qpreset = true; quiz.custom.using_qpreset = true; } if (custom.ipreset) { quiz.custom.has_ipreset = true; quiz.custom.using_ipreset = true; } } //======================================================================== // close_quiz() //------------------------------------------------------------------------ function close_quiz(e) { unfreeze_body(); $('body').off('.ss_quiz_key'); quiz.dialog.remove(); wkof.Settings.background.close(); if (quiz.custom && typeof quiz.custom.on_close === 'function') quiz.custom.on_close(); } var body_scroll_y; function freeze_body() { body_scroll_y = window.scrollY; $('body').css('overflow', 'hidden').scrollTop(body_scroll_y); } function unfreeze_body() { $('body').css('overflow','unset'); window.scroll({top:body_scroll_y}); } //======================================================================== // qpreset_changed() //------------------------------------------------------------------------ function qpreset_changed(e) { var settings = quiz.settings; var selected = e.target.selectedOptions[0].attributes.name.value; if (selected === 'custom') { quiz.custom.using_qpreset = true; } else { quiz.custom.using_qpreset = false; settings.active_qpreset = selected; wkof.Settings.save('ss_quiz'); } quiz.start(); } //======================================================================== // ipreset_changed() //------------------------------------------------------------------------ function ipreset_changed(e) { var settings = quiz.settings; var selected = e.target.selectedOptions[0].attributes.name.value; if (selected === 'custom') { quiz.custom.using_ipreset = true; } else { quiz.custom.using_ipreset = false; settings.active_ipreset = selected; wkof.Settings.save('ss_quiz'); } fetch_items().then(quiz.start); } //======================================================================== // 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 === 'ss_quiz_qna' && quiz.custom.has_qpreset) { html += '<option name="custom">('+quiz.custom.qpreset.name+')</option>'; if (quiz.custom.using_qpreset) active_preset = presets.length; } else if (elem_name === 'ss_quiz_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); } //######################################################################## // 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: load_qinfo, prep: prep_qinfo, cache: {}, }, // Stats stats: { round: 1, total: 0, correct: 0, incorrect: 0, }, // Functions start: start_quiz, shuffle: shuffle_quiz, requiz: requiz, ask: ask_question, submit: submit_answer, prev: prev_question, next: next_question, close: close_quiz, }; gobj.open = open_quiz; //======================================================================== // fetch_items() //------------------------------------------------------------------------ function fetch_items() { var settings = quiz.settings; var ipreset = (quiz.custom.using_ipreset ? quiz.custom.ipreset.content : 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}; 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; } } return wkof.ItemData.get_items(config) .then(function(items){ quiz.items = items; }); } //======================================================================== // shuffle_quiz() //------------------------------------------------------------------------ function shuffle_quiz() { var settings = quiz.settings; var qpreset = (quiz.custom.using_qpreset ? quiz.custom.qpreset.content : settings.qpresets[settings.active_qpreset].content); var pairing = settings.pairing || 'disabled'; var valid_question_types = { char2read: {radical:false, kanji:true, vocabulary:true, kana_vocabulary:false}, char2mean: {radical:true, kanji:true, vocabulary:true, kana_vocabulary:true }, read2mean: {radical:false, kanji:false, vocabulary:true, kana_vocabulary:false}, mean2read: {radical:false, kanji:false, vocabulary:true, kana_vocabulary:true }, aud2read: {radical:false, kanji:false, vocabulary:true, kana_vocabulary:true }, aud2mean: {radical:false, kanji:false, vocabulary:true, kana_vocabulary:true }, }; var id, idx, item, qset; var grp_list = quiz.group_list = []; quiz.stats.total = 0; switch (pairing) { case 'disabled': var qna = ['char2mean','char2read','read2mean','mean2read','aud2mean','aud2read']; for (id in quiz.items) { item = quiz.items[id]; for (idx in qna) { var qtype = qna[idx]; if (valid(qtype)) { grp_list.push({item:item, qna:[qtype], order:Math.random()}); } } } break; case 'reading_first': for (id in quiz.items) { item = quiz.items[id]; qset = []; if (valid('char2read')) qset.push('char2read'); if (valid('char2mean')) qset.push('char2mean'); if (qset.length > 0) grp_list.push({item:item, qna:qset, order:Math.random()}); if (valid('mean2read')) grp_list.push({item:item, qna:['mean2read'], order:Math.random()}); if (valid('read2mean')) grp_list.push({item:item, qna:['read2mean'], order:Math.random()}); qset = []; if (valid('aud2read')) qset.push('aud2read'); if (valid('aud2mean')) qset.push('aud2mean'); if (qset.length > 0) grp_list.push({item:item, qna:qset, order:Math.random()}); } break; case 'meaning_first': for (id in quiz.items) { item = quiz.items[id]; qset = []; if (valid('char2mean')) qset.push('char2mean'); if (valid('char2read')) qset.push('char2read'); if (qset.length > 0) grp_list.push({item:item, qna:qset, order:Math.random()}); if (valid('read2mean')) grp_list.push({item:item, qna:['read2mean'], order:Math.random()}); if (valid('mean2read')) grp_list.push({item:item, qna:['mean2read'], order:Math.random()}); qset = []; if (valid('aud2mean')) qset.push('aud2mean'); if (valid('aud2read')) qset.push('aud2read'); if (qset.length > 0) grp_list.push({item:item, qna:qset, order:Math.random()}); } break; case 'random_order': for (id in quiz.items) { item = quiz.items[id]; qset = []; if (Math.random() < 0.5) { if (valid('char2read')) qset.push('char2read'); if (valid('char2mean')) qset.push('char2mean'); } else { if (valid('char2mean')) qset.push('char2mean'); if (valid('char2read')) qset.push('char2read'); } if (qset.length > 0) grp_list.push({item:item, qna:qset, order:Math.random()}); if (valid('mean2read')) grp_list.push({item:item, qna:['mean2read'], order:Math.random()}); if (valid('read2mean')) grp_list.push({item:item, qna:['read2mean'], order:Math.random()}); qset = []; if (Math.random() < 0.5) { if (valid('aud2read')) qset.push('aud2read'); if (valid('aud2mean')) qset.push('aud2mean'); } else { if (valid('aud2mean')) qset.push('aud2mean'); if (valid('aud2read')) qset.push('aud2read'); } if (qset.length > 0) grp_list.push({item:item, qna:qset, order:Math.random()}); } break; } grp_list.sort(function(a,b){return a.order - b.order;}); var serial_list = quiz.serial_list = []; for (var idx1 in grp_list) { for (var idx2 in grp_list[idx1].qna) { serial_list.push([idx1, idx2]); } } quiz.qinfo.cache = {}; quiz.stats.real_total = quiz.stats.total; if (settings.max_quiz_size > 0) quiz.stats.total = Math.min(quiz.stats.total, settings.max_quiz_size); function valid(qtype) { var valid = ((qpreset[qtype] === true) && (valid_question_types[qtype][item.object] === true)); if (valid) quiz.stats.total++; return valid; } } //######################################################################## // QUIZ //######################################################################## //======================================================================== // jw_distance() - Jaro-Winkler Distance //------------------------------------------------------------------------ function jw_distance(a, c) {var h,b,d,k,e,g,f,l,n,m,p;if(a.length>c.length) {c=[c,a];a=c[0];c=c[1];}k=~~Math.max(0,c.length/2-1);e=[];g=[];b=n=0;for(p=a.length;n<p;b=++n){for(h=a[b],l=Math.max(0,b-k),f=Math.min(b+k+1,c.length),d=m=l;l<=f?m<f:m>f;d=l<=f?++m:--m){if(g[d]===undefined&&h===c[d]){e[b]=h;g[d]=c[d];break;}}}e=e.join("");g=g.join("");d=e.length;if(d){b=f=k=0;for(l=e.length;f<l;b=++f){h=e[b];if(h!==g[b])k++;}b=g=e=0;for(f=a.length;g<f;b=++g){h=a[b];if(h===c[b])e++;else break;}a=(d/a.length+d/c.length+(d-~~(k/2))/d)/3;a+=0.1*Math.min(e,4)*(1-a);}else{a=0;}return a;} //======================================================================== // to_title_case() - Make first letter of each word upper-case. //------------------------------------------------------------------------ function to_title_case(str) {return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});} //======================================================================== // start_quiz() //------------------------------------------------------------------------ function start_quiz(options) { if (!options) options = {}; if (options.keep_round_count !== true) options.keep_round_count = false; // Default 'false' if (!options.keep_round_count) quiz.stats.round = 1; quiz.stats.correct = 0; quiz.stats.incorrect = 0; quiz.index = 0; quiz.shuffle(); if (quiz.stats.total === 0) return set_mode('no_items'); set_mode('question'); } //======================================================================== // requiz() //------------------------------------------------------------------------ function requiz() { quiz.do_requiz = true; quiz.next(); } //======================================================================== // set_mode() //------------------------------------------------------------------------ function set_mode(mode) { var dialog = quiz.dialog; if (mode === 'previous') mode = quiz.last_mode; dialog.attr('data-mode', mode); switch (mode) { case 'loading': dialog.attr('data-itype', 'loading'); dialog.attr('data-atype', 'loading'); dialog.find('.atype').html('Loading...'); break; case 'no_items': dialog.attr('data-itype', 'loading'); dialog.attr('data-atype', 'reading'); dialog.find('.atype').html('No questions found!'); break; case 'question': ask_question(); break; case 'summary': dialog.attr('data-atype', 'reading'); dialog.find('.atype').html('[Enter] for new quiz, [Esc] to return'); dialog.find('.answer input').val('').prop('readonly', true); dialog.attr('data-r###lt', ''); populate_errors(); break; } if (quiz.mode !== quiz.last_mode) quiz.last_mode = quiz.mode; quiz.mode = mode; } function is_svg(img) {return (img.content_type === 'image/svg+xml') && (img.metadata.inline_styles === true);} //======================================================================== // ask_question() //------------------------------------------------------------------------ function ask_question(erase_old_answer) { var dialog = quiz.dialog; var qinfo = quiz.qinfo.load(quiz.index); toggle_help('off'); dialog.attr('data-itype', qinfo.item.type); dialog.attr('data-qtype', qinfo.question.type); dialog.attr('data-atype', qinfo.answer.type); if (quiz.message_timer) { clearTimeout(quiz.message_timer); delete quiz.message_timer; } dialog.removeClass('message'); // Draw the question var question = dialog.find('.question'); question.attr('lang', qinfo.question.lang); if (qinfo.question.html || qinfo.item.type !== 'radical') { question.html(qinfo.question.html); } else { var svg_url = qinfo.item.object.data.character_images.filter(is_svg)[0].url; question.html('<wk-character-image src="'+svg_url+'" />'); } // Initialize the answer var input = $('#ss_quiz .answer input'); var old_answer = get_user_answer(quiz.index); if (erase_old_answer) { if (old_answer[0] !== '') quiz.stats[old_answer[0]]--; update_quiz_stats(); set_user_answer(quiz.index, '', ''); old_answer = ['', '']; } if (old_answer[0] !== '') { dialog.attr('data-r###lt', old_answer[0]); input.val(old_answer[1]).prop('readonly', true); } else { dialog.attr('data-r###lt', ''); input.val('').prop('readonly', false); } if (qinfo.answer.lang === 'ja') { if (input.attr('lang') !== 'ja') { input.attr('lang', 'ja'); wanakana.bind(input[0], {IMEMode:true}); } } else { if (input.attr('lang') === 'ja') { input.attr('lang', 'en'); wanakana.unbind(input[0]); } } // Populate the help window if (qinfo.question.type !== 'characters' && qinfo.answer.type === 'reading') { dialog.find('.help').attr('lang',qinfo.answer.lang).html(to_title_case(qinfo.answer.good.join(', '))+(' ('+qinfo.item.object.data.characters+')')+(qinfo.answer.help_suffix || '')); } else { dialog.find('.help').attr('lang',qinfo.answer.lang).html(to_title_case(qinfo.answer.good.join(', '))+(qinfo.answer.help_suffix || '')); } dialog.find('.atype').html(qinfo.answer.html); // Update progress stats update_quiz_stats(); // If question is audio, play audio now. if (!erase_old_answer && qinfo.question.type === 'audio') { play_audio(true /* force_play */, qinfo); } input.focus(); quiz.qinfo.prep(quiz.index); } //======================================================================== // play_audio() //------------------------------------------------------------------------ function play_audio(force_play, qinfo) { if (quiz.settings.mute_audio) return; if (!force_play && !quiz.settings.play_audio) return; if (!qinfo) qinfo = quiz.qinfo.load(quiz.index); if (!qinfo) return; if (!qinfo.question.audio_promise) return; qinfo.question.audio_promise.then(function(qinfo){ if (!((quiz.index === qinfo.index) || (quiz.settings.lightning_mode && quiz.index === (qinfo.index + 1)))) return; qinfo.question.audio.currentTime = 0; qinfo.question.audio.play(); }); } //======================================================================== // load_qinfo() //------------------------------------------------------------------------ function load_qinfo(index) { if (index < 0 || index >= quiz.stats.total) return null; if (!quiz.qinfo.cache[index]) populate_qinfo(index); return quiz.qinfo.cache[index]; } //======================================================================== // prep_qinfo() //------------------------------------------------------------------------ function prep_qinfo(index) { Object.keys(quiz.qinfo.cache).forEach(function(cache_idx){ if (cache_idx < index-2 || cache_idx > index+2) { delete quiz.qinfo.cache[cache_idx]; } }); for (var ofs = 1; ofs <= 2; ofs++) { populate_qinfo(index+ofs); } } //======================================================================== // populate_qinfo() //------------------------------------------------------------------------ function populate_qinfo(index) { if (index < 0 || index >= quiz.stats.total) return; if (quiz.qinfo.cache[index]) return; var qinfo = {index:index, item:{}, question:{}, answer:{}}; quiz.qinfo.cache[index] = qinfo; var grp_idx = quiz.serial_list[index]; var group = quiz.group_list[grp_idx[0]]; var item = group.item; var qnatype = group.qna[grp_idx[1]]; qinfo.first_in_group = (grp_idx[1] == 0); qinfo.item.type = item.object; qinfo.item.object = item; qinfo.question.type = { char2read:'characters', char2mean:'characters', mean2read:'meaning', read2mean:'reading', aud2read:'audio', aud2mean:'audio' }[qnatype]; qinfo.answer.type = { char2read:'reading', char2mean:'meaning', mean2read:'reading', read2mean:'meaning', aud2read:'reading', aud2mean:'meaning' }[qnatype]; qinfo.answer.html = to_title_case((qinfo.item.type==='kana_vocabulary'?'Kana Vocabulary':qinfo.item.type)+' '+qinfo.answer.type); var synonyms = []; try {synonyms = item.study_materials.meaning_synonyms || [];} catch(e) {} var meanings = item.data.meanings.map(meaning => meaning.meaning); if (typeof item.data.auxiliary_meanings !== 'undefined') { meanings = meanings.concat(item.data.auxiliary_meanings.filter(m=>m.type==='whitelist').map(m=>m.meaning)) } if (quiz.settings.synonyms_order === 'first') { meanings = synonyms.concat(meanings).map(meaning => meaning.toLowerCase()); } else { meanings = meanings.concat(synonyms).map(meaning => meaning.toLowerCase()); } if (qinfo.item.type === 'vocabulary' || qinfo.item.type === 'kana_vocabulary') { qinfo.question.audio_promise = new Promise(function(resolve, reject){ qinfo.question.audio = new Audio(); qinfo.question.audio.oncanplaythrough = function(){ resolve(qinfo); } if (item.id !== undefined) { var audio_sources = item.data.pronunciation_audios; var filtered_sources; switch (quiz.settings.audio_type) { case 'mp3': filtered_sources = audio_sources.filter(a => a.content_type == 'audio/mpeg'); break; case 'ogg': filtered_sources = audio_sources.filter(a => a.content_type == 'audio/ogg'); break; default: filtered_sources = audio_sources; } if (filtered_sources.length !== 0) audio_sources = filtered_sources; switch (quiz.settings.audio_gender) { case 'male': filtered_sources = audio_sources.filter(a => a.metadata.gender == 'male'); break; case 'female': filtered_sources = audio_sources.filter(a => a.metadata.gender == 'female'); break; case 'rotate': quiz.gender = quiz.gender || 'female'; quiz.gender = (quiz.gender === 'female' ? 'male' : 'female'); filtered_sources = audio_sources.filter(a => a.metadata.gender == quiz.gender); break; default: filtered_sources = audio_sources; } if (filtered_sources.length !== 0) audio_sources = filtered_sources; if (audio_sources.length === 0) { qinfo.question.audio.src = null; } else { qinfo.question.audio.src = audio_sources[Math.floor(Math.random() * audio_sources.length)].url; } } }); } switch (qinfo.question.type) { case 'characters': qinfo.question.lang = 'ja'; if (item.data.characters === '') item.data.characters = null; qinfo.question.html = item.data.characters; break; case 'reading': qinfo.question.lang = 'ja'; qinfo.question.html = item.data.readings.map(reading => reading.reading).join(', '); break; case 'meaning': qinfo.question.lang = 'en'; qinfo.question.html = to_title_case(meanings.join(', ')); break; case 'audio': qinfo.question.lang = 'ja'; qinfo.question.html = '<span class="fa fa-audio"></span>'; qinfo.answer.help_suffix = '<br><span lang="ja">('+item.data.characters+')</span>'; break; } var idx, idx2, reading; qinfo.answer.other = []; qinfo.answer.bad = []; qinfo.answer.reading_type = ''; switch (qinfo.answer.type) { case 'reading': qinfo.answer.good = []; qinfo.answer.lang = 'ja'; if (qinfo.item.type === 'kana_vocabulary') { qinfo.answer.good.push(item.data.characters); } else { for (idx in item.data.readings) { reading = item.data.readings[idx]; if (qinfo.item.type === 'vocabulary' || reading.accepted_answer) { qinfo.answer.good.push(reading.reading); if (qinfo.item.type === 'kanji') { qinfo.answer.reading_type = reading.type.replace('yomi','\'yomi'); } } else { qinfo.answer.other.push(reading.reading); } } } qinfo.answer.bad = meanings; break; case 'meaning': qinfo.answer.good = meanings; qinfo.answer.lang = 'en'; if (!item.data.readings) break; for (idx in item.data.readings) { reading = item.data.readings[idx]; if (qinfo.item.type === 'vocabulary' || reading.accepted_answer) { qinfo.answer.bad.push(reading.reading); } } break; } } //======================================================================== // get_user_answer() //------------------------------------------------------------------------ function get_user_answer(index) { var grp_idx = quiz.serial_list[index]; var group = quiz.group_list[grp_idx[0]]; if (!group.answer || !group.answer[grp_idx[1]]) return ['', '']; return group.answer[grp_idx[1]]; } //======================================================================== // set_user_answer() //------------------------------------------------------------------------ function set_user_answer(index, status, answer) { var grp_idx = quiz.serial_list[index]; var group = quiz.group_list[grp_idx[0]]; if (!group.answer) group.answer = []; group.answer[grp_idx[1]] = [status, answer]; } //======================================================================== // submit_answer() //------------------------------------------------------------------------ function submit_answer() { var dialog = quiz.dialog; var input = $('#ss_quiz .answer input'); var qinfo = quiz.qinfo.load(quiz.index); var item = qinfo.item.object; var itype = qinfo.item.type; var atype = qinfo.answer.type; var raw_answer = input.val().trim(); var answer = raw_answer; var action = 'fail'; var msgcfg = quiz.settings.messages; var is_exact = true; var is_multi = false; var message; if (answer === '') { atype = 'ignore'; action = 'shake'; } switch (atype) { case 'reading': answer = wanakana.toHiragana(answer); if (qinfo.answer.good.indexOf(answer) >= 0 || qinfo.answer.good.indexOf(raw_answer) >= 0) { action = 'correct'; if (qinfo.answer.good.length > 1) is_multi = true; if (is_multi && msgcfg.show_multi_reading) message = 'This item has multiple readings'; } else if (itype === 'kanji' && qinfo.answer.other.indexOf(answer) >= 0) { action = 'shake'; message = 'We\'re looking for the '+to_title_case(qinfo.answer.reading_type)+' reading'; } else { var bad = qinfo.answer.bad.map(function(english){ return wanakana.toHiragana(english.toLowerCase()); }); if (bad.indexOf(answer) >= 0) { action = 'shake'; message = 'We\'re looking for the reading, not the meaning'; } else if (!wanakana.isKana(answer)) { action = 'shake'; message = 'Your answer contains invalid characters'; } else { action = 'incorrect'; } } break; case 'meaning': var is_correct = false; is_exact = false; answer = answer.toLowerCase(); var allow_typos = (quiz.settings.allow_typos === true); for (var idx in qinfo.answer.good) { var good_answer = qinfo.answer.good[idx]; if (answer === good_answer) { is_correct = true; is_exact = true; break; } else if (allow_typos && (good_answer.match(/[0-9]/) == null) && jw_distance(good_answer, answer) > 0.9) { is_correct = true; } } if (is_correct) { action = 'correct'; if (!is_exact && msgcfg.show_slightly_off === true) message = "Your answer was slightly off"; } else { var alt_answer = wanakana.toHiragana(answer,{IMEMode:true}); if (qinfo.answer.bad.indexOf(alt_answer) >= 0) { action = 'shake'; message = 'We\'re looking for the meaning, not the reading'; } else { action = 'incorrect'; } } break; } if (action !== 'shake') set_user_answer(quiz.index, action, answer); switch (action) { case 'correct': quiz.stats.correct++; // If question is reading, play audio now. if (qinfo.answer.type === 'reading' && qinfo.question.type !== 'audio') { play_audio(false /* force_play */, qinfo); } if ((quiz.settings.lightning_mode === true) && (!is_multi || !msgcfg.show_multi_reading || !msgcfg.halt_multi_reading) && (is_exact || !msgcfg.show_slightly_off || !msgcfg.halt_slightly_off )) { return quiz.next(); } else { update_quiz_stats(); input.prop('readonly', true); } dialog.attr('data-r###lt', 'correct'); break; case 'shake': shake(input); input.focus(); break; case 'incorrect': quiz.stats.incorrect++; update_quiz_stats(); input.prop('readonly', true); dialog.attr('data-r###lt', 'incorrect'); if (quiz.settings.autoshow_correct && !quiz.showing_help) { toggle_help('on'); } break; } if (message) { dialog.find('.message').text(message); dialog.addClass('message'); if (quiz.message_timer) { clearTimeout(quiz.message_timer); delete quiz.message_timer; } quiz.message_timer = setTimeout(function(){ dialog.removeClass('message'); quiz.message_timer = undefined; },2750); } } //======================================================================== // shake() //------------------------------------------------------------------------ function shake(elem) { var dist = '15px'; var speed = 75; var right = {padding:'0 '+dist+' 0 0'}, left = {padding:'0 0 0 '+dist}, center = {padding:"0 0 0 0"}; elem.animate(left,speed/2).animate(right,speed) .animate(left,speed).animate(right,speed) .animate(left,speed).animate(center,speed/2); } //======================================================================== // prev_question() //------------------------------------------------------------------------ function prev_question() { switch (quiz.mode) { case 'question': if (quiz.index > 0) quiz.index--; quiz.ask(); break; case 'summary': if (quiz.index === quiz.stats.total) { quiz.index = quiz.stats.total - 1; update_quiz_stats(); } set_mode('question'); break; } quiz.ask(); } //======================================================================== // next_question() //------------------------------------------------------------------------ function next_question() { switch (quiz.mode) { case 'question': if (quiz.index < quiz.stats.total-1) { quiz.index++; quiz.ask(); } else { quiz.index = quiz.stats.total; update_quiz_stats(); set_mode('summary'); } break; case 'summary': if (quiz.do_requiz) { delete quiz.do_requiz; if (!quiz.original_items) { quiz.original_items = quiz.items; } quiz.items = quiz.requiz_items; delete quiz.requiz_items; } else { delete quiz.requiz_items; if (quiz.original_items) { quiz.items = quiz.original_items; delete quiz.original_items; } quiz.stats.round++; } quiz.start({keep_round_count:true}); break; } } //======================================================================== // populate_errors() //------------------------------------------------------------------------ function populate_errors() { var dialog = quiz.dialog; var percent_elem = dialog.find('.summary .percent'); var errors_elem = dialog.find('.summary .errors'); var total = quiz.stats.correct + quiz.stats.incorrect; var percent = (total === 0 ? 100 : 100 * quiz.stats.correct / total); percent_elem.text((Math.round(percent*100)/100).toString()+'%'); if (total === quiz.stats.correct) { $('#ss_quiz .summary .requiz').addClass('hidden'); } else { $('#ss_quiz .summary .requiz').removeClass('hidden'); } var idx; var err_list = dialog.find('.summary .errors'); err_list.html(''); var requiz_items = {}; quiz.requiz_items = []; for (idx = 0; idx < quiz.stats.total; idx++) { var grp_idx = quiz.serial_list[idx]; var group = quiz.group_list[grp_idx[0]]; if (!group.answer) continue; var answer = group.answer[grp_idx[1]]; if (!answer || answer[0] !== 'incorrect') continue; var item = group.item; if (!requiz_items[item.id]) { requiz_items[item.id] = 1; quiz.requiz_items.push(item); } var itype = (item.object === 'kana_vocabulary' ? 'vocabulary' : item.object); var qnatype = group.qna[grp_idx[1]]; answer = answer[1]; var qtype = { char2read:'characters', char2mean:'characters', mean2read:'meaning', read2mean:'reading', aud2read:'audio', aud2mean:'audio' }[qnatype]; var atype = { char2read:'reading', char2mean:'meaning', mean2read:'reading', read2mean:'meaning', aud2read:'reading', aud2mean:'meaning' }[qnatype]; var qlang = (qtype === 'meaning' ? 'en' : 'ja'); var alang = (atype === 'meaning' ? 'en' : 'ja'); var qtitle = to_title_case(itype+' '+atype); var atitle; switch (atype) { case 'meaning': var synonyms = []; try {synonyms = item.study_materials.meaning_synonyms || [];} catch(e) {} var meanings = item.data.meanings.map(meaning => meaning.meaning); if (quiz.settings.synonyms_order === 'first') { meanings = synonyms.concat(meanings).map(meaning => meaning.toLowerCase()); } else { meanings = meanings.concat(synonyms).map(meaning => meaning.toLowerCase()); } atitle = meanings.join(', '); break; case 'reading': atitle = to_title_case(item.data.readings.map(reading => reading.reading).join(', ')); if (qtype !== 'characters') atitle += ' ('+item.data.characters+')'; break; } var qtext = item.data.slug; if (qtype === 'audio') { qtext += ' <i class="fa fa-audio icon-style"></i>'; } else if (qtype === 'meaning') { synonyms = []; try {synonyms = item.study_materials.meaning_synonyms || [];} catch(e) {} meanings = item.data.meanings.map(meaning => meaning.meaning); if (quiz.settings.synonyms_order === 'first') { meanings = synonyms.concat(meanings).map(meaning => meaning.toLowerCase()); } else { meanings = meanings.concat(synonyms).map(meaning => meaning.toLowerCase()); } qtext = meanings.join(', '); } var atext = answer + ' <i class="fa fa-times-circle wrong"></i>'; err_list.append( '<li><span class="que" lang="'+qlang+'" title="'+qtitle+'">'+qtext+'</span>'+ '<i class="fa fa-long-arrow-right icon-style"></i>'+ '<span class="ans" lang="'+alang+'" title="'+atitle+'">'+atext+'</span></li>' ); } } //======================================================================== // update_quiz_stats() //------------------------------------------------------------------------ function update_quiz_stats() { var stats = $('#ss_quiz .stats_labels'); var stats_width = quiz.stats.total.toString().length; // Number of digits in quiz counter var remaining = quiz.stats.total - quiz.index; stats.html( 'Round: '+(' '+quiz.stats.round).slice(-1*stats_width)+'<br>'+ 'Remaining: '+(' '+remaining).slice(-1*stats_width)+'<br>'+ 'Correct: '+(' '+quiz.stats.correct).slice(-1*stats_width)+'<br>'+ 'Incorrect: '+(' '+quiz.stats.incorrect).slice(-1*stats_width) ); } //======================================================================== // quiz_key_handler() //------------------------------------------------------------------------ var keycode_xlat = { '8':'Backspace', '13':'Enter', '27':'Escape', '37':'ArrowLeft', '39':'ArrowRight', '65':'KeyA', '69':'KeyE', '72':'KeyH', '76':'KeyL', '80':'KeyP', '82':'KeyR', '83':'KeyS', '112':'F1', }; function quiz_key_handler(e) { if (quiz_settings_state === 'open') return true; var input = quiz.dialog.find('.answer input'); var input_readonly = input.prop('readonly'); var code; if (e.type === 'keydown') { if (e.originalEvent.keyCode) { code = keycode_xlat[e.originalEvent.keyCode] || 'Unknown'; } else { code = e.originalEvent.code; } } else { code = String.fromCharCode(e.charCode); } if (code === 'Enter') { if (quiz.mode === 'question' && !input_readonly) { quiz.submit(e); } else { quiz.next(); } } else if (code === 'Escape') { process_escape(); } else if (code === 'F1' || code === '?') { toggle_help(); } else if (code === 'Backspace') { // Prevent backspace from navigating away from the page. if (quiz.mode !== 'question') return false; if (input_readonly) quiz.ask(true /* erase_old_answer */); return true; } else if (e.ctrlKey || e.metaKey) { switch (code) { case 'KeyA': if (e.shiftKey) { toggle_audio(); } else { play_audio(true); } break; case 'KeyE': process_escape(); break; // End case 'KeyH': toggle_help(); break; // Help case 'KeyL': toggle_lightning(); break; // Lightning case 'KeyP': toggle_pairing(); break; // Pairing case 'KeyR': // Re-quiz if (quiz.mode !== 'summary' || quiz.dialog.find('.summary .requiz').hasClass('hidden')) break; quiz.requiz(); break; case 'KeyS': manual_shuffle(); break; case 'ArrowLeft': quiz.prev(); break; case 'ArrowRight': quiz.next(); break; default: return true; } } else { var is_special = (e.key.length !== 1); if (is_special) return true; // Let the browser handle regular keys in the input box if (e.target === input[0]) return true; // Let the browser handle all other keys while not in question mode. if (quiz.mode !== 'question') return true; } return false; } //======================================================================== // manual_shuffle() //------------------------------------------------------------------------ function manual_shuffle() { var keep_round_count = true; if (quiz.shuffle_timer === undefined) { quiz.shuffle_timer = setTimeout(function(){ delete quiz.shuffle_timer; }, 1000); } else { clearTimeout(quiz.shuffle_timer); delete quiz.shuffle_timer; keep_round_count = false; } quiz.start({keep_round_count:keep_round_count}); } //======================================================================== // process_escape() //------------------------------------------------------------------------ function process_escape() { if (quiz.escape_timer === undefined) { quiz.escape_counter = 1; quiz.escape_timer = setTimeout(function(){ delete quiz.escape_counter; delete quiz.escape_timer; }, 750); } else { quiz.escape_counter++; if (quiz.escape_counter === 3) { clearTimeout(quiz.escape_timer); delete quiz.escape_timer; quiz.close(); return; } } switch (quiz.mode) { case 'question': set_mode('summary'); break; case 'summary': if (quiz.index === quiz.stats.total) quiz.index = quiz.stats.total-1; set_mode('previous'); break; } } //======================================================================== // toggle_audio() //------------------------------------------------------------------------ function toggle_audio() { var elem = $('#ss_quiz .settings .ss_audio'); if (quiz.settings.mute_audio) { quiz.settings.mute_audio = false; quiz.settings.play_audio = false; elem.removeClass('mute'); elem.removeClass('active'); } else if (quiz.settings.play_audio) { quiz.settings.mute_audio = true; quiz.settings.play_audio = false; elem.addClass('mute'); elem.removeClass('active'); } else { quiz.settings.mute_audio = false; quiz.settings.play_audio = true; elem.removeClass('mute'); elem.addClass('active'); } wkof.Settings.save('ss_quiz'); } //======================================================================== // toggle_help() //------------------------------------------------------------------------ function toggle_help(value) { if (quiz.mode !== 'question') return; var elem = $('#ss_quiz .settings .ss_help'); switch (value) { case 'on': elem.addClass('active'); quiz.dialog.addClass('help'); quiz.showing_help = true; break; case 'off': elem.removeClass('active'); quiz.dialog.removeClass('help'); quiz.showing_help = false; break; default: elem.toggleClass('active'); quiz.dialog.toggleClass('help'); quiz.showing_help = !quiz.showing_help; break; } var qinfo = quiz.qinfo.load(quiz.index); if (quiz.showing_help && qinfo.answer.type === 'reading') play_audio(false /* force_play */); } //======================================================================== // toggle_lightning() //------------------------------------------------------------------------ function toggle_lightning() { var elem = $('#ss_quiz .settings .ss_lightning'); elem.toggleClass('active'); quiz.settings.lightning_mode = elem.hasClass('active'); wkof.Settings.save('ss_quiz'); } //======================================================================== // toggle_pairing() //------------------------------------------------------------------------ function toggle_pairing(e, initialize) { var elem_pair = $('#ss_quiz .settings .ss_pair'); var elem_data = elem_pair.find('.data'); var values = ['disabled', 'reading_first', 'meaning_first', 'random_order']; var value = Math.max(0, values.indexOf(quiz.settings.pairing)); if (!initialize) value = (value + 1) % values.length; quiz.settings.pairing = value = values[value]; wkof.Settings.save('ss_quiz') switch (value) { case 'disabled': elem_data.text('Disabled'); elem_pair.removeClass('active'); break; case 'reading_first': elem_data.text('Reading First'); elem_pair.addClass('active'); break; case 'meaning_first': elem_data.text('Meaning First'); elem_pair.addClass('active'); break; case 'random_order': elem_data.text('Random Order'); elem_pair.addClass('active'); break; } if (!initialize) quiz.start({keep_round_count:true}); } //======================================================================== // drag() //------------------------------------------------------------------------ function drag(e) { var dlg = $(e.currentTarget).closest('.dialog'); var pos = dlg.position(); var ofs = {x: e.pageX-pos.left, y: e.pageY-pos.top}; $('body') .on('mousemove.ss_quiz_drag touchmove.ss_quiz_drag', function(e){ dlg.css({left: Math.max(0,e.pageX-ofs.x), top: Math.max(0,e.pageY-ofs.y)}); }) .on('mouseup.ss_quiz_drag touchend.ss_quiz_drag', function(e){ $('body').off('.ss_quiz_drag'); }); } wkof.set_state('ss_quiz', 'ready'); })(window.ss_quiz);