// ==UserScript==
// @name Wanikani: R/K/V Tiny Bars
// @namespace http://tampermonkey.net/
// @version 1.5.2
// @description Adds colored bars to individually track radicals / kanji / vocab
// @author Thalanor
// @match https://www.wanikani.com/
// @match https://www.wanikani.com/dashboard
// @match https://preview.wanikani.com/
// @match https://preview.wanikani.com/dashboard
// @license MIT; http://opensource.org/licenses/MIT
// @grant none
// ==/UserScript==
(function() {
// Code snippet like in most other userscripts - check for WKOF
var wkof = window.wkof;
var display_position = 'replace_vanilla';
var recent_failure_threshold = 95;
var show_global_bars = 1;
var show_recent_failures = 1;
var show_failed_properties = 1;
var show_vocab_backlog = 1;
var show_vocab_progression = 1;
var show_kanji_progression = 1;
var show_radical_progression = 1;
var reverse_color_order = 0;
var display_size = 0;
var hide_guru_items = 1;
var collapse_locked_items = 1;
if (!wkof) {
var response = confirm('Wanikani: Global wkof_data Bars requires WaniKani Open Framework.\n Click "OK" to be forwarded to installation instructions.');
if (response) window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
return;
}
else {
var wkof_data = {
counts: {0: {burned: 0, enlightened: 0, master: 0, guru1: 0, guru2: 0, apprentice1: 0, apprentice2: 0, apprentice3: 0, apprentice4: 0, total: 0},
1: {burned: 0, enlightened: 0, master: 0, guru1: 0, guru2: 0, apprentice1: 0, apprentice2: 0, apprentice3: 0, apprentice4: 0, total: 0},
2: {burned: 0, enlightened: 0, master: 0, guru1: 0, guru2: 0, apprentice1: 0, apprentice2: 0, apprentice3: 0, apprentice4: 0, total: 0}
},
//srs_info: {},
recent_failures: {},
backlog_vocab: {},
lvl_radical: {},
lvl_kanji: {},
lvl_vocab: {},
vocab_backlog_locked: 0,
vocab_backlog_initiate: 0,
vocab_backlog_remaining: 0,
vocab_backlog_lowest_level: 60,
vocab_backlog_highest_level: 0,
failed_count: 0,
failed_lowest_level: 60,
failed_highest_level: 0,
radicals_total: 0,
radicals_learned: 0,
kanji_total: 0,
kanji_needed: 0,
kanji_learned: 0,
kanji_locked: 0,
vocab_total: 0,
vocab_learned: 0,
vocab_locked: 0,
}
var flg_darktheme = is_dark_theme_rfindley();
wkof.include('Menu, Settings, ItemData');
wkof.ready('Menu, Settings, ItemData')
.then(load_settings)
.then(install_menu)
.then(init_data)
.then(calc_bars)
.then(make_html);
}
// This function is called when the Settings module is ready to use.
function load_settings() {
var defaults = {
display_position : 'replace_vanilla',
recent_failure_threshold : 95,
show_global_bars : 1,
show_recent_failures : 1,
show_failed_properties : 1,
show_vocab_backlog : 1,
show_vocab_progression : 1,
show_kanji_progression : 1,
show_radical_progression : 1,
reverse_color_order : 0,
display_size : 0,
hide_guru_items : 1,
collapse_locked_items : 1
};
wkof.Settings.load('wanikani_rkvtinybars', defaults)
.then(update_settings);
}
// Add settings menu to the menu
function install_menu() {
var config = {
name: 'wanikani_rkvtinybars',
submenu: 'Settings',
title: 'RKV Tiny Bars',
on_click: open_settings
};
wkof.Menu.insert_script_link(config);
}
// Define settings menu layout
function open_settings(items) {
var config = {
script_id: 'wanikani_rkvtinybars',
title: 'RKV Tiny Bars',
on_save: update_settings,
on_close: update_settings,
content: {/*
tabset_settings: {
type: 'tabset',
content: {
page_general: {
type: 'page',
label: 'Layout',
content: {*/
module_settings: {
type: 'group',
label: 'Module Settings',
content: {
display_position: {
type: 'dropdown',
label: 'Display position',
hover_tip: 'Choose where to display the script card',
default: 'replace_vanilla',
content: {
replace_vanilla: 'Replace default progression',
below_vanilla: 'Dedicated card below default'
}
},
}
},
level_progression: {
type: 'group',
label: 'Level progression',
content: {
display_size: {
type: 'dropdown',
label: 'Item display size',
hover_tip: 'Choose the size of individual item tiles',
default: 0,
content: {
0: 'Standard',
1: 'Compact'
}
},
show_radical_progression: {
type: 'checkbox',
label: 'Show current level radicals',
hover_tip: 'Show the current level radical progression (radicals SRS)',
default: 'true'
},
show_kanji_progression: {
type: 'checkbox',
label: 'Show current level kanji',
hover_tip: 'Show the current level kanji progression (kanji SRS)',
default: 'true'
},
show_vocab_progression: {
type: 'checkbox',
label: 'Show current level vocabulary',
hover_tip: 'Show the current level vocabulary progression (vocab SRS)',
default: 'true'
},
show_vocab_backlog: {
type: 'checkbox',
label: 'Show backlog vocabulary',
hover_tip: 'Show apprentice vocabulary of previous levels remaining',
default: 'true'
},
hide_guru_items: {
type: 'checkbox',
label: 'Show only apprentice items',
hover_tip: 'Only show apprentice stages of current level radicals and kanji',
default: 'true'
},
collapse_locked_items: {
type: 'checkbox',
label: 'Sum up and collapse locked items',
hover_tip: 'Display the number of locked items instead of showing them individually',
default: 'true'
}
}
},
global_progression: {
type: 'group',
label: 'Global progression',
content: {
show_global_bars: {
type: 'checkbox',
label: 'Show the R/K/V progress bars',
hover_tip: 'Show colored progress bars for radicals, kanji and vocabulary',
default: 'true'
},
reverse_color_order: {
type: 'checkbox',
label: 'Reverse bar section order',
hover_tip: 'Change color ordering from apprentice -> burned to burned -> apprentice',
default: 'false'
}
}
},
recently_failed: {
type: 'group',
label: 'Recently failed',
content: {
show_recent_failures: {
type: 'checkbox',
label: 'Show recently failed items',
hover_tip: 'Show items recently failed (meaning or reading) in reviews',
default: 'true'
},
show_failed_properties: {
type: 'checkbox',
label: 'Show failed meaning or reading',
hover_tip: 'Show either or both meaning or reading, depending on what was failed',
default: 'true'
},
recent_failure_threshold: {
type: 'dropdown',
label: 'Recent failure threshold',
hover_tip: 'How much time of SRS interval must be remaining for the failed item to show.',
default: 95,
content: {
99: '99% of SRS',
95: '95% of SRS',
90: '90% of SRS',
75: '75% of SRS',
50: '50% of SRS'
}
}
}
}
}
}
var dialog = new wkof.Settings(config);
dialog.open();
}
function update_settings(settings) {
display_position = settings.display_position;
recent_failure_threshold = settings.recent_failure_threshold;
show_global_bars = settings.show_global_bars;
show_recent_failures = settings.show_recent_failures;
show_failed_properties = settings.show_failed_properties;
show_vocab_backlog = settings.show_vocab_backlog;
show_vocab_progression = settings.show_vocab_progression;
show_kanji_progression = settings.show_kanji_progression;
show_radical_progression = settings.show_radical_progression;
reverse_color_order = settings.reverse_color_order;
display_size = settings.display_size;
hide_guru_items = settings.hide_guru_items;
collapse_locked_items = settings.collapse_locked_items;
wkof.Settings.save("wanikani_rkvtinybars");
}
function init_data() {
var resolve, promise = new Promise((res, rej)=>{resolve=res;});
var config = {wk_items: {options: {assignments: true, review_statistics: true}
}
};
wkof.ItemData.get_items(config).then((data)=>{
var user_level = wkof.user.level;
var backlog_vocabs = 0;
var lvl_radicals = 0;
var lvl_kanji = 0;
var lvl_vocab = 0;
var recent_failures = 0;
//var debug = 0;
for (var key in data) {
const item = data[key];
//debug++;
var id = item.id;
var srs_stage = 0;
var item_level = item.data.level;
var item_type = item.object;
var item_key = -1;
if (item.assignments) {
srs_stage = item.assignments.srs_stage;
if ((item.review_statistics !== null) && (item.assignments.available_at !== null)) {
var reading_failed = item.review_statistics.reading_current_streak === 1;
var meaning_failed = item.review_statistics.meaning_current_streak === 1;
if (reading_failed || meaning_failed) {
var availableAt = new Date(item.assignments.available_at);
var intervalStart = new Date(item.assignments.available_at);
var secondsUntilReview = seconds_between(new Date(Date.now()), availableAt);
var secondsInSRS = getSecondsInSRS(srs_stage);
var percentUntil = Math.ceil(secondsUntilReview*100/secondsInSRS);
var itemReading = null;
if (item.data.readings !== undefined && item.data.meanings !== null) {
itemReading = item.data.readings[0].reading;
}
var itemMeaning = null;
if (item.data.meanings !== undefined && item.data.meanings !== null) {
itemMeaning = item.data.meanings[0].meaning;
}
if (percentUntil >= recent_failure_threshold) {
wkof_data.recent_failures[recent_failures] = {"id": id, "chars": item.data.characters, "images": item.data.character_images, "srs": srs_stage,
"reading": reading_failed ? itemReading : null, "meaning": meaning_failed ? itemMeaning : null};
recent_failures++;
wkof_data.failed_count++;
wkof_data.failed_lowest_level = Math.min(wkof_data.failed_lowest_level, item_level);
wkof_data.failed_highest_level = Math.max(wkof_data.failed_highest_level, item_level);
}
}
}
} else {
srs_stage = -1;
}
if (item_type === "radical") {
item_key = 0;
} else if (item_type === "kanji") {
item_key = 1;
} else if (item_type === "vocabulary") {
item_key = 2;
} else if (item_type === "kana_vocabulary") {
item_key = 2;
}
//wkof_data.srs_info[id] = {"key": item_key, "srs": srs_stage};
if (item_level === user_level) {
if (item_key === 0) {
wkof_data.lvl_radical[lvl_radicals] = {"id": id, "chars": item.data.characters, "images": item.data.character_images, "srs": srs_stage};
lvl_radicals++;
wkof_data.radicals_total++;
if (srs_stage > 4) {
wkof_data.radicals_learned++;
}
}
if (item_key === 1) {
lvl_kanji++;
wkof_data.kanji_total++;
if (srs_stage >= (collapse_locked_items ? 0 : -1)) {
wkof_data.lvl_kanji[lvl_kanji] = {"chars": item.data.characters, "images": item.data.character_images, "srs": srs_stage};
if (srs_stage > 4) {
wkof_data.kanji_learned++;
}
} else {
wkof_data.kanji_locked++;
}
}
if (item_key === 2) {
lvl_vocab++;
wkof_data.vocab_total++;
if (srs_stage >= (collapse_locked_items ? 0 : -1)) {
wkof_data.lvl_vocab[lvl_vocab] = {"chars": item.data.characters, "images": item.data.character_images, "srs": srs_stage};
if (srs_stage > 4) {
wkof_data.vocab_learned++;
}
} else {
wkof_data.vocab_locked++;
}
}
}
if (show_vocab_backlog && item_level < user_level && srs_stage < 5) {
if (item_key === 2) {
wkof_data.backlog_vocab[backlog_vocabs] = {"chars": item.data.characters, "images": item.data.character_images, "srs": srs_stage};
backlog_vocabs++;
wkof_data.vocab_backlog_remaining++;
wkof_data.vocab_backlog_lowest_level = Math.min(wkof_data.vocab_backlog_lowest_level, item_level);
wkof_data.vocab_backlog_highest_level = Math.max(wkof_data.vocab_backlog_highest_level, item_level);
if (srs_stage < 0) {
wkof_data.vocab_backlog_locked++;
} else if (srs_stage === 0) {
wkof_data.vocab_backlog_initiate++;
}
}
}
wkof_data.kanji_needed = Math.ceil(wkof_data.kanji_total*9/10);
if (srs_stage == 1) {
wkof_data.counts[item_key].apprentice1++;
} else if (srs_stage == 2) {
wkof_data.counts[item_key].apprentice2++;
} else if (srs_stage == 3) {
wkof_data.counts[item_key].apprentice3++;
} else if (srs_stage == 4) {
wkof_data.counts[item_key].apprentice4++;
} else if (srs_stage == 5) {
wkof_data.counts[item_key].guru1++;
} else if (srs_stage == 6) {
wkof_data.counts[item_key].guru2++;
} else if (srs_stage === 7) {
wkof_data.counts[item_key].master++;
} else if (srs_stage === 8) {
wkof_data.counts[item_key].enlightened++;
} else if (srs_stage === 9) {
wkof_data.counts[item_key].burned++;
}
wkof_data.counts[item_key].total++;
}
resolve();
})
return promise;
}
function calc_bars() {
var bardata = {bars: {0: {burned: 0, enlightened: 0, master: 0, guru1: 0, guru2: 0, apprentice1: 0, apprentice2: 0, apprentice3: 0, apprentice4: 0, count_learned: 0, count_learned_progress_since_last: 0, count_total: 0, label: "Radicals"},
1: {burned: 0, enlightened: 0, master: 0, guru1: 0, guru2: 0, apprentice1: 0, apprentice2: 0, apprentice3: 0, apprentice4: 0, count_learned: 0, count_learned_progress_since_last: 0, count_total: 0, label: "Kanji"},
2: {burned: 0, enlightened: 0, master: 0, guru1: 0, guru2: 0, apprentice1: 0, apprentice2: 0, apprentice3: 0, apprentice4: 0, count_learned: 0, count_learned_progress_since_last: 0, count_total: 0, label: "Vocabulary"}
},
backlog_vocab: {},
lvl_radical: {},
lvl_kanji: {},
lvl_vocab: {},
vocab_backlog_remaining: 0,
vocab_backlog_locked: 0,
vocab_backlog_initiate: 0,
vocab_backlog_lowest_level: 0,
vocab_backlog_highest_level: 0,
failed_count: 0,
failed_lowest_level: 0,
failed_highest_level: 0,
radicals_total: 0,
radicals_learned: 0,
kanji_total: 0,
kanji_needed: 0,
kanji_learned: 0,
kanji_locked: 0,
vocab_total: 0,
vocab_learned: 0,
vocab_locked: 0,
}
var prevdata = JSON.parse(localStorage.getItem('WKRKVTinyBarsLearnedProgressV020'));
if (prevdata == null) {
prevdata = {0: 0, 1: 0, 2: 0};
}
for (var key in bardata["bars"]) {
bardata["bars"][key].burned = wkof_data.counts[key].burned * 100 / wkof_data.counts[key].total;
bardata["bars"][key].enlightened = wkof_data.counts[key].enlightened * 100 / wkof_data.counts[key].total;
bardata["bars"][key].master = wkof_data.counts[key].master * 100 / wkof_data.counts[key].total;
bardata["bars"][key].guru2 = wkof_data.counts[key].guru2 * 100 / wkof_data.counts[key].total;
bardata["bars"][key].guru1 = wkof_data.counts[key].guru1 * 100 / wkof_data.counts[key].total;
bardata["bars"][key].apprentice4 = wkof_data.counts[key].apprentice4 * 100 / wkof_data.counts[key].total;
bardata["bars"][key].apprentice3 = wkof_data.counts[key].apprentice3 * 100 / wkof_data.counts[key].total;
bardata["bars"][key].apprentice2 = wkof_data.counts[key].apprentice2 * 100 / wkof_data.counts[key].total;
bardata["bars"][key].apprentice1 = wkof_data.counts[key].apprentice1 * 100 / wkof_data.counts[key].total;
bardata["bars"][key].count_total = wkof_data.counts[key].total;
bardata["bars"][key].count_learned = wkof_data.counts[key].burned + wkof_data.counts[key].enlightened + wkof_data.counts[key].master + wkof_data.counts[key].guru2 + wkof_data.counts[key].guru1;
bardata["bars"][key].count_learned_progress_since_last = bardata["bars"][key].count_learned - prevdata[key];
prevdata[key] = bardata["bars"][key].count_learned;
}
bardata["lvl_radical"] = wkof_data.lvl_radical;
bardata["lvl_kanji"] = wkof_data.lvl_kanji;
bardata["lvl_vocab"] = wkof_data.lvl_vocab;
bardata["recent_failures"] = wkof_data.recent_failures;
bardata.backlog_vocab = wkof_data.backlog_vocab;
bardata.vocab_backlog_locked = wkof_data.vocab_backlog_locked;
bardata.vocab_backlog_initiate = wkof_data.vocab_backlog_initiate;
bardata.vocab_backlog_remaining = wkof_data.vocab_backlog_remaining;
bardata.vocab_backlog_lowest_level = wkof_data.vocab_backlog_lowest_level;
bardata.vocab_backlog_highest_level = wkof_data.vocab_backlog_highest_level;
bardata.failed_count = wkof_data.failed_count;
bardata.failed_lowest_level = wkof_data.failed_lowest_level;
bardata.failed_highest_level = wkof_data.failed_highest_level;
bardata.radicals_learned = wkof_data.radicals_learned;
bardata.radicals_total = wkof_data.radicals_total;
bardata.kanji_learned = wkof_data.kanji_learned;
bardata.kanji_needed = wkof_data.kanji_needed;
bardata.kanji_total = wkof_data.kanji_total;
bardata.kanji_locked = wkof_data.kanji_locked;
bardata.vocab_learned = wkof_data.vocab_learned;
bardata.vocab_total = wkof_data.vocab_total;
bardata.vocab_locked = wkof_data.vocab_locked;
localStorage.setItem('WKRKVTinyBarsLearnedProgressV020', JSON.stringify(prevdata));
return bardata;
}
function make_html(bardata) {
$('head').append('<style id="rkv_bars">'+
'.rkv_bars {'+
' margin: 15px 0 15px;'+
' position: relative;'+
'}'+
//'.rkv_bars .sidebar {'+
//' position: absolute;'+
//' left: -320px;'+
//' top: 0px;'+
//'}'+
'.rkv_bars .headline {'+
' font-size: 14px;'+
' font-weight: 700;'+
'}'+
'.dashboard-progress {'+
(display_position === "replace_vanilla" ? ' padding: 0px !important;' : '')+
'}'+
'.rkv_bars .rkvbars_container {'+
' border-radius: 5px;'+
(!flg_darktheme ? 'background: #ffffff !important;' : 'background: #232629 !important;') +
' padding: 2px;'+
' overflow: visible;'+
' position: relative;'+
' font-size: ' + (display_size == 0 ? 14 : 12) + 'px;'+
'}'+
'.rkv_bars .label {'+
' border-radius: 0px;'+
' background: transparent;'+
' font-size: 12px; ' +
' line-height: 12px; ' +
(!flg_darktheme ? 'color: #ffffff !important;' : 'color: #000000 !important;') +
' position: absolute;' +
' text-shadow: none;'+
' top: 0px;'+
' z-index: 10;'+
'}'+
'.rkv_bars .label-left {'+
' left: 0px;'+
'}'+
'.rkv_bars .label-right {'+
' right: 0px;'+
'}'+
'.rkv_bars .label-change {'+
' right: -38px;'+
' width: 30px;'+
' height: 12px;'+
' font-size: 12px; ' +
' line-height: 12px; ' +
' border-top-left-radius: 0px;'+
' border-bottom-left-radius: 0px;'+
' border-top-right-radius: 7px;'+
' border-bottom-right-radius: 7px;'+
'}'+
'.rkv_bars .label-change-good {'+
(!flg_darktheme ? 'background: #33bb33;' : 'background: #66ff66;') +
'}'+
'.rkv_bars .label-change-bad {'+
(!flg_darktheme ? 'background: #bb3333;' : 'background: #ff6666;') +
'}'+
'.rkv_bars .rkvbars_bar {'+
' border-radius: 0px;'+
(!flg_darktheme ?
'background-image: linear-gradient(135deg, #a7a7a7 25%, #a0a0a0 25%, #a0a0a0 50%, #a7a7a7 50%, #a7a7a7 75%, #a0a0a0 75%, #a0a0a0 100%);' :
'background-image: linear-gradient(135deg, #878787 25%, #808080 25%, #808080 50%, #878787 50%, #878787 75%, #808080 75%, #808080 100%);') +
' background-size: 20px 20px;'+
' margin: 5px;'+
' position: relative; '+
' height: 16px;' +
' overflow: visible ;' +
'}'+
'.rkv_bars .shadow {'+
' -moz-box-shadow: inset 0px 3px 4px -3px #666;'+
' -webkit-box-shadow: inset 0px 3px 4px -3px #666;'+
' box-shadow: inset 0px 3px 4px -3px #666;'+
'}'+
'.rkv_bars .element {'+
' border-radius: 0px;'+
' padding: 0px;'+
' float: left;'+
' height: 16px;'+
'}'+
'.rkv_bars .charbox_element {'+
' border-radius: 2px;' +
' margin: 2px;'+
' -moz-box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.7);' +
' -webkit-box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.7);' +
' box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.7);' +
' padding-left: ' + (display_size == 0 ? 2 : 1) + 'px;'+
' padding-right: ' + (display_size == 0 ? 2 : 0) + 'px;'+
' padding-top: ' + (display_size == 0 ? 4 : 0) + 'px;'+
' padding-bottom: 0px;'+
' float: left;'+
' height: ' + (display_size == 0 ? 24 : 20) + 'px;'+
' font-size: ' + (display_size == 0 ? 20 : 14) + 'px;'+
' text-align: center;'+
' color: #000000 !important;'+
'}'+
'.rkv_bars .reading_meaning {'+
' border-radius: 2px;' +
' margin-top: ' + (display_size == 0 ? 0 : 2) + 'px;'+
' margin-left: 2px;'+
' margin-right: 2px;'+
' padding-left: 2px;'+
' padding-right: 2px;'+
' float: right;'+
' height: ' + (display_size == 0 ? 20 : 16) + 'px;'+
' font-size: ' + (display_size == 0 ? 12 : 10) + 'px;'+
' text-align: center;'+
' color: #000000 !important;'+
' background-color: #ffffff !important;'+
'}'+
'.rkv_bars .charbox {'+
' min-width: ' + (display_size == 0 ? 24 : 20) + 'px;'+
'}'+
'.rkv_bars .levelup_separator {'+
' width: 4px;'+
' margin-left: ' + (display_size == 0 ? 12 : 10) + 'px;'+
' margin-right: ' + (display_size == 0 ? 12 : 10) + 'px;'+
(!flg_darktheme ?
' background-image: linear-gradient(135deg, #B351CC 25%, #9130A9 25%, #9130A9 50%, #B351CC 50%, #B351CC 75%, #9130A9 75%, #9130A9 100%);' :
' background-image: linear-gradient(135deg, #239950 25%, #5BCC8A 25%, #5BCC8A 50%, #239950 50%, #239950 75%, #5BCC8A 75%, #5BCC8A 100%);') +
' background-size: 10px 10px;'+
'}'+
'.rkv_bars .charcard {'+
' padding-top: 16px;'+
' margin: 8px;'+
' position: relative; '+
' overflow: visible;' +
'}'+
'.rkv_bars .locked {'+
(!flg_darktheme ?
' background-image: linear-gradient(135deg, #a7a7a7 25%, #a0a0a0 25%, #a0a0a0 50%, #a7a7a7 50%, #a7a7a7 75%, #a0a0a0 75%, #a0a0a0 100%);' :
' background-image: linear-gradient(135deg, #878787 25%, #808080 25%, #808080 50%, #878787 50%, #878787 75%, #808080 75%, #808080 100%);') +
' background-size: 10px 10px;'+
'}'+
'.rkv_bars .srs-1 {'+
(!flg_darktheme ?
' background-image: linear-gradient(135deg, #a7a7a7 25%, #a0a0a0 25%, #a0a0a0 50%, #a7a7a7 50%, #a7a7a7 75%, #a0a0a0 75%, #a0a0a0 100%);' :
' background-image: linear-gradient(135deg, #878787 25%, #808080 25%, #808080 50%, #878787 50%, #878787 75%, #808080 75%, #808080 100%);') +
'}'+
'.rkv_bars .srs0 {'+
(!flg_darktheme ? ' background: #EEEEEE !important;' : 'background: #CCCCCC !important;') +
'}'+
'.rkv_bars .srs1 {'+
(!flg_darktheme ? ' background: #DDBCD2 !important;' : 'background: #A0C8E5 !important;') +
'}'+
'.rkv_bars .srs2 {'+
(!flg_darktheme ? ' background: #DD9BC7 !important;' : 'background: #7CB8E2 !important;') +
'}'+
'.rkv_bars .srs3 {'+
(!flg_darktheme ? ' background: #E070BB !important;' : 'background: #4EA6E0 !important;') +
'}'+
'.rkv_bars .srs4 {'+
(!flg_darktheme ? ' background: #F200A1 !important;' : 'background: #1187DB !important;') +
'}'+
'.rkv_bars .srs5 {'+
(!flg_darktheme ? ' background: #C351EC !important;' : 'background: #1EAA77 !important;') +
'}'+
'.rkv_bars .srs6 {'+
(!flg_darktheme ? ' background: #A130D9 !important;' : 'background: #1EDD4D !important;') +
'}'+
'.rkv_bars .srs7 {'+
(!flg_darktheme ? ' background: #3758DD !important;' : 'background: #FDBC4B !important;') +
'}'+
'.rkv_bars .srs8 {'+
(!flg_darktheme ? ' background: #009CEA !important;' : 'background: #F67400 !important;') +
'}'+
'.rkv_bars .srs9 {'+
(!flg_darktheme ? ' background: #FAB319 !important;' : 'background: #DA4453 !important;') +
'}'+
'</style>');
var section = document.createElement('section');
section.className = 'rkv_bars inner_section';
var container = document.createElement('div');
container.className = 'rkvbars_container';
if (show_global_bars) {
for (var key in bardata["bars"]) {
var list = document.createElement('div');
list.className = 'rkvbars_bar shadow';
var progress_since_last = bardata["bars"][key].count_learned_progress_since_last;
if (progress_since_last > 0) {
$(list).append('<div class="label label-change label-change-good">'+ '+' + progress_since_last + '</div>');
} else if (progress_since_last < 0) {
$(list).append('<div class="label label-change label-change-bad">'+ '−' + Math.abs(progress_since_last) + '</div>');
}
var percentage = Math.round((bardata["bars"][key].count_learned / bardata["bars"][key].count_total * 100) * 100) / 100
$(list).append('<div class="label label-left">'+ bardata["bars"][key].label + '</div>');
$(list).append('<div class="label label-right">'+ bardata["bars"][key].count_learned + ' / ' + bardata["bars"][key].count_total + ' (' + percentage + '%)' + '</div>');
if (reverse_color_order) {
$(list).append('<div class="srs1 element shadow" style="width: '+ bardata["bars"][key].apprentice1 + '%"></div>');
$(list).append('<div class="srs2 element shadow" style="width: '+ bardata["bars"][key].apprentice2 + '%"></div>');
$(list).append('<div class="srs3 element shadow" style="width: '+ bardata["bars"][key].apprentice3 + '%"></div>');
$(list).append('<div class="srs4 element shadow" style="width: '+ bardata["bars"][key].apprentice4 + '%"></div>');
$(list).append('<div class="srs5 element shadow" style="width: '+ bardata["bars"][key].guru1 + '%"></div>');
$(list).append('<div class="srs6 element shadow" style="width: '+ bardata["bars"][key].guru2 + '%"></div>');
$(list).append('<div class="srs7 element shadow" style="width: '+ bardata["bars"][key].master + '%"></div>');
$(list).append('<div class="srs8 element shadow" style="width: '+ bardata["bars"][key].enlightened + '%"></div>');
$(list).append('<div class="srs9 element shadow" style="width: '+ bardata["bars"][key].burned + '%"></div>');
} else {
$(list).append('<div class="srs9 element shadow" style="width: '+ bardata["bars"][key].burned + '%"></div>');
$(list).append('<div class="srs8 element shadow" style="width: '+ bardata["bars"][key].enlightened + '%"></div>');
$(list).append('<div class="srs7 element shadow" style="width: '+ bardata["bars"][key].master + '%"></div>');
$(list).append('<div class="srs6 element shadow" style="width: '+ bardata["bars"][key].guru2 + '%"></div>');
$(list).append('<div class="srs5 element shadow" style="width: '+ bardata["bars"][key].guru1 + '%"></div>');
$(list).append('<div class="srs4 element shadow" style="width: '+ bardata["bars"][key].apprentice4 + '%"></div>');
$(list).append('<div class="srs3 element shadow" style="width: '+ bardata["bars"][key].apprentice3 + '%"></div>');
$(list).append('<div class="srs2 element shadow" style="width: '+ bardata["bars"][key].apprentice2 + '%"></div>');
$(list).append('<div class="srs1 element shadow" style="width: '+ bardata["bars"][key].apprentice1 + '%"></div>');
}
$(container).append(list);
}
}
if (show_recent_failures && bardata.failed_count > 0) {
var faillist = document.createElement('div');
faillist.className = 'charcard';
$(faillist).append('<span class="headline">Level ' + bardata.failed_lowest_level + '-' + bardata.failed_highest_level + ' Recently failed items (> ' + recent_failure_threshold + '% of SRS interval remaining)</span><br>');
for(var i = 9; i >= 0; i--) {
for (var failed in bardata["recent_failures"]) {
var srs_level = bardata["recent_failures"][failed]["srs"];
if (srs_level == i) {
var chars = bardata["recent_failures"][failed]["chars"];
var images = bardata["recent_failures"][failed]["images"];
failed_properties = "";
if (show_failed_properties) {
var reading = bardata["recent_failures"][failed]["reading"];
var meaning = bardata["recent_failures"][failed]["meaning"];
var readingDiv = '<span class="reading_meaning">' + reading + '</span>';
var meaningDiv = '<span class="reading_meaning">' + meaning + '</span>';
failed_properties = (reading !== null ? readingDiv : "") + (meaning !== null ? meaningDiv : "");
}
var srs_class = "srs" + srs_level;
if (chars) {
$(faillist).append('<div class="charbox_element charbox ' + srs_class + '">' + chars + failed_properties + '</div>');
} else if (images) {
$(faillist).append('<div class="charbox_element charbox ' + srs_class + '"><img src="' + bardata["recent_failures"][failed]["images"][8]["url"] + '" width="' + (display_size == 0 ? 20 : 14) + 'px" height="' + (display_size == 0 ? 20 : 14) + 'px"></img></div>');
}
}
}
}
$(faillist).append('<br clear="all" />');
$(container).append(faillist);
}
if (show_vocab_backlog && bardata.vocab_backlog_remaining > 0) {
var voclist_backlog = document.createElement('div');
voclist_backlog.className = 'charcard';
$(voclist_backlog).append('<span class="headline">Level ' + bardata.vocab_backlog_lowest_level + '-' + bardata.vocab_backlog_highest_level + ' Vocabulary backlog (' + bardata.vocab_backlog_remaining + ' remaining from earlier levels, ' + bardata.vocab_backlog_initiate + ' not started, ' + bardata.vocab_backlog_locked + ' locked)</span><br>');
for(var i = 9; i >= 0; i--) {
for (var voc_b in bardata["backlog_vocab"]) {
var srs_level = bardata["backlog_vocab"][voc_b]["srs"];
if (srs_level == i) {
var vocbchars = bardata["backlog_vocab"][voc_b]["chars"];
var srs_class = "srs" + srs_level;
$(voclist_backlog).append('<div class="charbox_element charbox ' + srs_class + '">' + vocbchars + '</div>');
}
}
}
if (bardata.vocab_backlog_locked > 0) {
$(voclist_backlog).append('<div class="charbox_element locked"> +' + bardata.vocab_backlog_locked + ' </div>');
}
$(voclist_backlog).append('<br clear="all" />');
$(container).append(voclist_backlog);
}
if (show_radical_progression && bardata.radicals_learned < bardata.radicals_total) {
var radlist = document.createElement('div');
radlist.className = 'charcard';
$(radlist).append('<span class="headline">Level ' + wkof.user.level + ' Radicals Progression (' + bardata.radicals_learned + ' of ' + bardata.radicals_total + ' learned)</span><br>');
for(var i = 9; i >= -1; i--) {
for (var rad in bardata["lvl_radical"]) {
var srs_level = bardata["lvl_radical"][rad]["srs"];
if (!hide_guru_items || srs_level < 5) {
if (srs_level == i) {
var radchars = bardata["lvl_radical"][rad]["chars"];
var images = bardata["lvl_radical"][rad]["images"];
var srs_class = "srs" + srs_level;
if (radchars) {
$(radlist).append('<div class="charbox_element charbox ' + srs_class + '">' + radchars + '</div>');
} else if (images) {
$(radlist).append('<div class="charbox_element charbox ' + srs_class + '"><img src="' + bardata["lvl_radical"][rad]["images"][8]["url"] + '" width="' + (display_size == 0 ? 20 : 14) + 'px" height="' + (display_size == 0 ? 20 : 14) + 'px"></img></div>');
}
}
}
}
}
$(radlist).append('<br clear="all" />');
$(container).append(radlist);
}
if (show_kanji_progression && bardata.kanji_learned < bardata.kanji_total) {
var kanlist = document.createElement('div');
kanlist.className = 'charcard';
$(kanlist).append('<span class="headline">Level ' + wkof.user.level + ' Kanji Progression (' + bardata.kanji_learned + ' of ' + bardata.kanji_total + ' learned, ' + (bardata.kanji_needed - bardata.kanji_learned) + ' more required)</span><br>');
var kanji_count = 0;
var levelup_threshold_marker = bardata.kanji_needed;
if (hide_guru_items) {
levelup_threshold_marker -= bardata.kanji_learned;
}
for(var i = 9; i >= -1; i--) {
for (var kan in bardata["lvl_kanji"]) {
var srs_level = bardata["lvl_kanji"][kan]["srs"];
if (!hide_guru_items || srs_level < 5) {
if (srs_level == i) {
if (kanji_count == levelup_threshold_marker) {
$(kanlist).append('<div class="charbox_element levelup_separator"></div>');
}
var kanchars = bardata["lvl_kanji"][kan]["chars"];
var srs_class = "srs" + srs_level;
if (kanchars) {
$(kanlist).append('<div class="charbox_element charbox ' + srs_class + '">' + kanchars + '</div>');
}
kanji_count++;
}
}
}
}
if (bardata.kanji_locked > 0) {
$(kanlist).append('<div class="charbox_element locked"> +' + bardata.kanji_locked + ' </div>');
}
$(kanlist).append('<br clear="all" />');
$(container).append(kanlist);
}
if (show_vocab_progression && bardata.vocab_learned < bardata.vocab_total) {
var voclist = document.createElement('div');
voclist.className = 'charcard';
$(voclist).append('<span class="headline">Level ' + wkof.user.level + ' Vocabulary Progression (' + bardata.vocab_learned + ' of ' + bardata.vocab_total + ' learned)</span><br>');
var vocab_count = 0;
for(var i = 9; i >= -1; i--) {
for (var voc in bardata["lvl_vocab"]) {
var srs_level = bardata["lvl_vocab"][voc]["srs"];
if (!hide_guru_items || srs_level < 5) {
if (srs_level == i) {
var vocchars = bardata["lvl_vocab"][voc]["chars"];
var srs_class = "srs" + srs_level;
if (vocchars) {
$(voclist).append('<div class="charbox_element charbox ' + srs_class + '">' + vocchars + '</div>');
}
vocab_count++;
}
}
}
}
if (bardata.vocab_locked > 0) {
$(voclist).append('<div class="charbox_element locked"> +' + bardata.vocab_locked + ' </div>');
}
$(voclist).append('<br clear="all" />');
$(container).append(voclist);
}
section.appendChild(container);
if (display_position === "replace_vanilla") {
$('.progress-component').after(section);
$('.progress-component').remove();
} else {
$('.srs-progress').after(section);
}
}
// Handy little function that rfindley wrote. Checks whether the theme is dark.
function is_dark_theme_rfindley() {
// Grab the <html> background color, average the RGB. If less than 50% bright, it's dark theme.
return $('body').css('background-color').match(/\((.*)\)/)[1].split(',').slice(0,3).map(str => Number(str)).reduce((a, i) => a+i)/(255*3) < 0.5;
}
function seconds_between(d1, d2) {
if (d1 == "Invalid Date") return 1;
var diff = d2.getTime()-d1.getTime(); // milliseconds
var tzd = d1.getTimezoneOffset()-d2.getTimezoneOffset(); // minutes
diff += tzd*60*1000+1;
return Math.ceil(diff/1000);
}
function getSecondsInSRS(srs_stage) {
var hours;
switch(srs_stage) {
case 1: hours = 4; break;
case 2: hours = 8; break;
case 3: hours = 1*24; break;
case 4: hours = 2*24; break;
case 5: hours = 7*24; break;
case 6: hours = 14*24; break;
case 7: hours = 30*24; break;
case 8: hours = 4*30*24; break;
default: hours = 1; break;
}
return hours*60*60;
}
})();