// ==UserScript==
// @name Wanikani Reorder Ultimate 2 BETA
// @namespace https://www.wanikani.com
// @description Learn in the order you want to.
// @version 2.0.201
// @include *://www.wanikani.com/lesson/session*
// @include *://www.wanikani.com/review/session*
// @grant none
// ==/UserScript==
(function ($) {$.each(['hide', 'addClass'], function (i, ev) { var el = $.fn[ev]; $.fn[ev] = function () { this.trigger(ev); return el.apply(this, arguments); }; }); })(jQuery);/*
* HTML5 Sortable jQuery Plugin
* http://farhadi.ir/projects/html5sortable
*
* Copyright 2012, Ali Farhadi
* Released under the MIT license.
*/
(function(e){var t,n=e();e.fn.sortable=function(r){var i=String(r);r=e.extend({connectWith:false},r);return this.each(function(){if(/^enable|disable|destroy$/.test(i)){var s=e(this).children(e(this).data("items")).attr("draggable",i=="enable");if(i=="destroy"){s.add(this).removeData("connectWith items").off("dragstart.h5s dragend.h5s selectstart.h5s dragover.h5s dragenter.h5s drop.h5s")}return}var o,u,s=e(this).children(r.items);var a=e("<"+(/^ul|ol$/i.test(this.tagName)?"li":"div")+' class="sortable-placeholder">');s.find(r.handle).mousedown(function(){o=true}).mouseup(function(){o=false});e(this).data("items",r.items);n=n.add(a);if(r.connectWith){e(r.connectWith).add(this).data("connectWith",r.connectWith)}s.attr("draggable","true").on("dragstart.h5s",function(n){if(r.handle&&!o){return false}o=false;var i=n.originalEvent.dataTransfer;i.effectAllowed="move";i.setData("Text","dummy");u=(t=e(this)).addClass("sortable-dragging").index()}).on("dragend.h5s",function(){if(!t){return}t.removeClass("sortable-dragging").show();n.detach();if(u!=t.index()){t.parent().trigger("sortupdate",{item:t})}t=null}).not("a[href], img").on("selectstart.h5s",function(){this.dragDrop&&this.dragDrop();return false}).end().add([this,a]).on("dragover.h5s dragenter.h5s drop.h5s",function(i){if(!s.is(t)&&r.connectWith!==e(t).parent().data("connectWith")){return true}if(i.type=="drop"){i.stopPropagation();n.filter(":visible").after(t);t.trigger("dragend.h5s");return false}i.preventDefault();i.originalEvent.dataTransfer.dropEffect="move";if(s.is(this)){if(r.forcePlaceholderSize){a.height(t.outerHeight())}t.hide();e(this)[a.index()<e(this).index()?"after":"before"](a);n.not(a).detach()}else if(!n.is(this)&&!e(this).children(r.items).length){n.detach();e(this).append(a)}return false})})}})(jQuery);
var activeLevels = [];
var dataset = {};
var lastUpdate = new Date().getTime();
var lessonset = {
quick: 'l',
queue: 'l/lessonQueue',
active: 'l/activeQueue',
updateVisual: function() {
$('li[data-index="0"]').click();
var items = $.jStorage.get(dataset.queue).concat($.jStorage.get(dataset.active));
$.jStorage.set('l/count/rad', sorter.filterType("rad", items).length);
$.jStorage.set('l/count/kan', sorter.filterType("kan", items).length);
$.jStorage.set('l/count/voc', sorter.filterType("voc", items).length);
}
};
var reviewset = {
quick: 'r',
queue: 'reviewQueue',
active: 'activeQueue',
updateVisual: function() {
var item = $.jStorage.get(dataset.active)[Math.floor(window.Math.random(true))];
if (item) {
$.jStorage.set('currentItem', item);
}
var count = $.jStorage.get(dataset.queue).length + $.jStorage.get(dataset.active).length;
$("#available-count").html(count); // to stop the double-up bug.
}
};
var ordered = false;
var settings = {
data: {
sorttypes: !0,
sortlevels: !0,
onebyone: !1,
quickNext: !1,
priority: {
'rad': 1,
'kan': 2,
'voc': 3,
},
questionTypeMode: "0",
typePriorityMode: "0"
},
load: function() {
var a = $.jStorage.get('WKU/' + dataset.quick + '/settings');
if (a === null) {
settings.save();
return settings.load();
}
utilities.log("Loading settings...");
for (var s in settings.data) {
if (a[s] !== null) settings.data[s] = a[s];
}
$('#types item').sort(function(a, b) {
return sorter.getHTMLElementPriority(a) - sorter.getHTMLElementPriority(b);
}).appendTo('#types');
$('#sort-types').prop('checked', settings.data.sorttypes).change();
$('#sort-levels').prop('checked', settings.data.sortlevels).change();
$('#priority').removeClass().addClass(utilities.settingsValueToClass('priority', settings.data.typePriorityMode));
$('#quick-next').toggleClass('active', settings.data.quickNext);
if (dataset.quick === 'r') {
$('#mode').prop('checked', settings.data.onebyone).change();
$('#priority2').removeClass().addClass(utilities.settingsValueToClass('priority2', settings.data.questionTypeMode));
}
utilities.log(settings.data);
},
save: function() {
$.jStorage.set('WKU/' + dataset.quick + '/settings', settings.data);
}
};
var setup = {
init: function() {
try {
$('div[id*="loading"]').off('hide');
console.dlog = [];
utilities.log("WKU Init()");
if (!setup.update.check()) {
setup.update.patch();
} else {
setup.ui.create();
}
} catch (err) {
$('#supplement-info, #information').first().after('<div id="error" style="text-align:center;">An error has occurred within WaniKani Reorder Ultimate. Please post the error below on the forum thread.<br><a href="https://www.wanikani.com/chat/api-and-third-party-apps/8471" target="_blank">https://www.wanikani.com/chat/api-and-third-party-apps/8471</a><br><br>' + err + '<br>' + err.stack + '<br><br>Logs:<br>' + console.dlog.join('<br>') + '</div>');
}
},
update: {
apply: function() {
try {
utilities.log("Applying UID levels");
var queue = $.jStorage.get(dataset.active).concat($.jStorage.get(dataset.queue));
var current = $.jStorage.get('currentItem');
var list = $.jStorage.get('uids') || [];
$.each(queue, function() {
this.level = list[utilities.toUID(this)] || 0;
if (current && utilities.toUID(this) === utilities.toUID(current)) {
$.jStorage.set('currentItem', this);
}
if (activeLevels.indexOf(this.level) == -1) {
activeLevels.push(this.level);
}
});
activeLevels.sort(function(a, b) {
return a - b;
});
var review = queue.splice((dataset.quick === 'r' ? 10 : $.jStorage.get('l/batchSize')));
$.jStorage.set(dataset.active, queue);
$.jStorage.set(dataset.queue, review);
dataset.updateVisual();
} catch (err) {
$('#supplement-info, #information').first().after('<div id="error" style="text-align:center;">An error has occurred within WaniKani Reorder Ultimate. Please post the error below on the forum thread.<br><a href="https://www.wanikani.com/chat/api-and-third-party-apps/8471" target="_blank">https://www.wanikani.com/chat/api-and-third-party-apps/8471</a><br><br>' + err + '<br>' + err.stack + '<br><br>Logs:<br>' + console.dlog.join('<br>') + '</div>');
}
},
check: function() {
try {
utilities.log("Checking for UID updates");
var all = $.jStorage.get(dataset.queue).concat($.jStorage.get(dataset.active));
var list = $.jStorage.get('uids') || [];
return list && all.every(function(ele) {
return ele && list[utilities.toUID(ele)];
});
} catch (err) {
$('#supplement-info, #information').first().after('<div id="error" style="text-align:center;">An error has occurred within WaniKani Reorder Ultimate. Please post the error below on the forum thread.<br><a href="https://www.wanikani.com/chat/api-and-third-party-apps/8471" target="_blank">https://www.wanikani.com/chat/api-and-third-party-apps/8471</a><br><br>' + err + '<br>' + err.stack + '<br><br>Logs:<br>' + console.dlog.join('<br>') + '</div>');
}
},
patch: function() {
try {
utilities.log("Gathering data for patch");
var count = 0;
var all = {};
$('#information, #supplement-info').first().after('<div id="updateUID" style="text-align:center;">WKU::Please Wait::Currently updating UID list... <span id="id-percent">0%</span></div>');
["radicals", "kanji", "vocabulary"].forEach(function(ele1, ind1, arr1) {
["PLEASANT", "PAINFUL", "DEATH", "HELL", "PARADISE", "REALITY"].forEach(function(ele2, ind2, arr2) {
$.get('https://www.wanikani.com/' + ele1 + '?difficulty=' + ele2, function(resp) {
$('#id-percent').text(Math.round((++count / (arr1.length * arr2.length)) * 100) + "%");
$(resp).find('section[id^=level-]').each(function() {
var level = $(this).attr('id').replace(/[^\d]/g, "");
$(this).find('.character-item').each(function() {
var a = $(this).attr('id');
a = a.substr(0, 1) + a.replace(/[^\d]/g, "");
all[a] = level;
});
});
if (count >= (arr1.length * arr2.length)) {
$.jStorage.set('uids', all);
utilities.log("DONE!");
$('#updateUID').remove();
setup.ui.create();
}
}).fail(function() {
$('#supplement-info, #information').first().after('<div id="error" style="text-align:center;">WaniKani Reorder Ultimate has failed to load [' + ele2 + ' level ' + ele1 + ']<br>Please reload the page.</div>');
});
});
});
} catch (err) {
$('#supplement-info, #information').first().after('<div id="error" style="text-align:center;">An error has occurred within WaniKani Reorder Ultimate. Please post the error below on the forum thread.<br><a href="https://www.wanikani.com/chat/api-and-third-party-apps/8471" target="_blank">https://www.wanikani.com/chat/api-and-third-party-apps/8471</a><br><br>' + err + '<br>' + err.stack + '<br><br>Logs:<br>' + console.dlog.join('<br>') + '</div>');
}
}
},
ui: {
create: function() {
setup.update.apply();
$('head').append('<link rel="stylesheet" href="https://cdn.rawgit.com/xMunch/866bd26bf87579087976/raw/126960bed6b964fb71e98cd0df54789ebdc0d1e7/style.min.css">');
$.get('https://gist.githubusercontent.com/xMunch/a448f5999a682f114d61/raw/UI.HTML', function(data) {
utilities.log("Creating UI...");
var info = $('#supplement-info, #information').first();
info.after(data);
$('#version').text("v" + GM_info.script.version);
if (dataset.quick === 'l') {
$('.ui').find('#r-only').remove();
}
if (!setup.update.check()) {
$('.ui-small').first().before('<div id="updateUID-failed" style="text-align:center; display:none;">Failed to update UIDs.<br>Complain here <a href="https://www.wanikani.com/chat/api-and-third-party-apps/8471" target="_blank">https://www.wanikani.com/chat/api-and-third-party-apps/8471</a><div>');
$('#updateUID-failed').fadeIn(500).delay(7500).fadeOut(500);
}
$('fieldset').on('addClass', function(evt) {
if (settings.data.quickNext) {
setTimeout(function() {
if ($('fieldset').hasClass('correct')) {
$('fieldset button').click();
}
}, 25);
}
});
$('#quick-next').click(function() {
$(this).toggleClass('active');
settings.data.quickNext = $(this).hasClass('active');
settings.save();
});
$('#priority, #priority2').click(function(e) {
var offset = $(this).offset();
var x = (e.pageX - offset.left);
var y = (e.pageY - offset.top);
var id = $(this).attr('id');
if (y > 50) {
if (x < 75) {
if (id === "priority") {
$(this).removeClass().addClass("level-heavy");
settings.data.typePriorityMode = "1";
sorter.reorder();
} else {
$(this).removeClass().addClass("reading-heavy");
settings.data.questionTypeMode = "1";
utilities.forceQuestionTypeUpdate();
}
} else {
if (id === "priority") {
$(this).removeClass().addClass("type-heavy");
settings.data.typePriorityMode = "2";
sorter.reorder();
} else {
$(this).removeClass().addClass("meaning-heavy");
settings.data.questionTypeMode = "2";
utilities.forceQuestionTypeUpdate();
}
}
} else {
if (id === "priority") {
$(this).removeClass().addClass("balance");
settings.data.typePriorityMode = "0";
sorter.reorder();
} else {
$(this).removeClass().addClass("balance2");
settings.data.questionTypeMode = "0";
utilities.forceQuestionTypeUpdate();
}
}
settings.save();
}).mousemove(function(e) {
var offset = $(this).offset();
var x = (e.pageX - offset.left);
var y = (e.pageY - offset.top);
var ele = $(this).find('#overlay');
if (y > 50) {
if (x < 75) {
ele.removeClass().addClass('left');
} else {
ele.removeClass().addClass('right');
}
} else {
ele.removeClass().addClass('top');
}
}).mouseleave(function(e) {
$(this).find('#overlay').removeClass();
});
activeLevels.forEach(function(ele) {
$('.ui #levels').append('<item id="level-' + ele + '">' + ele + '</item>');
});
$('span#reverse').click(function() {
var parent = $(this).parents('.sortable');
var items = parent.children('item');
parent.append(items.get().reverse());
sorter.reorder();
settings.save();
});
$('.icon-minus, .icon-plus').click(function() {
$('.ui, .ui-small').toggleClass('hidden');
});
$('#sort-types, #sort-levels').change(function() {
$(this).parents('.sortable').find('item').toggleClass('unsorted', !this.checked);
settings.data[$(this).attr('id').replace("-", "")] = this.checked;
settings.save();
});
if (dataset.quick === 'r') {
$('#mode').on('change', function() {
settings.data.onebyone = this.checked;
if (settings.data.questionTypeMode > 0) {
utilities.forceQuestionTypeUpdate();
}
settings.save();
});
$('#option-wrap-up').click(function() {
if ($(this).attr('class') === 'wrap-up-selected') {
var fullQueue = $.jStorage.get(dataset.active).concat($.jStorage.get(dataset.queue));
$.jStorage.set(dataset.active, fullQueue.splice(0, 10));
$.jStorage.set(dataset.queue, fullQueue);
} else {
if (ordered) {
sorter.reorder();
}
}
});
}
settings.load();
setup.ui.toggler();
$('#reorder').click(function() {
sorter.reorder();
});
$('body').on('contextmenu', '.ui', function(e) {
e.preventDefault();
});
$('item').mousedown(function(event) {
if (event.which === 3) {
var ele = $(this).addClass('hidden');
if (ele.attr('id')) {
sorter.removeLevel(parseInt(ele.text()));
} else {
sorter.removeType(ele.attr('class'));
}
}
});
$('.sortable').sortable({
items: ':not(div, button)'
}).bind('sortupdate', function() {
sorter.reorder();
settings.save();
});
});
},
toggler: function() {
if ($('.sortable').length) {
var fq = $.jStorage.get(dataset.queue).concat($.jStorage.get(dataset.active));
if (!fq.length) {
utilities.log("There are no options available... Removing UI.");
$('.ui').remove();
} else {
$('#types .radical').toggleClass('hidden', !sorter.filterType("rad", fq).length).prop('title', function() {
var filtered = sorter.filterType("rad", fq);
var text = "Total: " + filtered.length;
$.each(activeLevels, function() {
var amount = sorter.filterLevel(this, filtered).length;
if (amount) {
text += "\nLevel " + this + ": " + amount + "";
}
});
return text;
});
$('#types .kanji').toggleClass('hidden', !sorter.filterType("kan", fq).length).prop('title', function() {
var filtered = sorter.filterType("kan", fq);
var text = "Total: " + filtered.length;
$.each(activeLevels, function() {
var amount = sorter.filterLevel(this, filtered).length;
if (amount) {
text += "\nLevel " + this + ": " + amount + "";
}
});
return text;
});
$('#types .vocabulary').toggleClass('hidden', !sorter.filterType("voc", fq).length).prop('title', function() {
var filtered = sorter.filterType("voc", fq);
var text = "Total: " + filtered.length;
$.each(activeLevels, function() {
var amount = sorter.filterLevel(this, filtered).length;
if (amount) {
text += "\nLevel " + this + ": " + amount + "";
}
});
return text;
});
activeLevels.forEach(function(level) {
$('#level-' + level).toggleClass('hidden', !sorter.filterLevel(level, fq).length).prop('title', function() {
var filtered = sorter.filterLevel(level, fq);
var rad = sorter.filterType("rad", filtered).length;
var kan = sorter.filterType("kan", filtered).length;
var voc = sorter.filterType("voc", filtered).length;
var text = "Total: " + filtered.length;
if (rad) {
text += "\nRadicals: " + rad;
}
if (kan) {
text += "\nKanji: " + kan;
}
if (voc) {
text += "\nVocabulary: " + voc;
}
return text;
});
});
$('input[type="checkbox"]:disabled').removeAttr('disabled');
}
}
}
},
listeners: function() {
var lastCount = $.jStorage.get(dataset.active).length + $.jStorage.get(dataset.queue).length;
$.jStorage.listenKeyChange('currentItem', function() {
if (lastCount < ($.jStorage.get(dataset.active).length + $.jStorage.get(dataset.queue).length)) {
lastCount = $.jStorage.get(dataset.active).length + $.jStorage.get(dataset.queue).length; // will infinitely trigger if not here.
setup.init();
}
lastCount = $.jStorage.get(dataset.active).length + $.jStorage.get(dataset.queue).length;
});
$.jStorage.listenKeyChange('currentItem', utilities.forceQuestionTypeUpdate);
$.jStorage.listenKeyChange(dataset.active, setup.ui.toggler);
}
};
var sorter = {
filterLevel: function(level, list) {
if (!list) {
return [];
}
return list.filter(function(ele, ind) {
return ele.level == level;
});
},
filterType: function(type, list) {
if (!list) {
return [];
}
return list.filter(function(ele, ind) {
return ele[type.substr(0, 3)];
});
},
getHTMLElementPriority: function(a) {
return a.className === 'radical' ? settings.data.priority.rad : a.className === 'kanji' ? settings.data.priority.kan : settings.data.priority.voc;
},
getPriority: function(a) {
return a.rad ? settings.data.priority.rad : a.kan ? settings.data.priority.kan : settings.data.priority.voc;
},
randomize: function(list) {
return list.sort(function() {
return 0.5 - window.Math.randomB();
});
},
reorder: function() {
ordered = true;
sorter.setPriorities();
var fullQueue = $.jStorage.get(dataset.queue).concat($.jStorage.get(dataset.active));
fullQueue = sorter.randomize(fullQueue);
if (parseInt(settings.data.typePriorityMode) == 1) {
if (settings.data.sortlevels) {
$('#levels > item').each(function() {
var level = parseInt(this.innerHTML);
var sorted = sorter.filterLevel(level, fullQueue);
if (settings.data.sorttypes) {
sorted = sorter.sortByType(sorted);
} else {
sorted = sorter.randomize(sorted);
}
fullQueue = sorter.removeLevel(level, fullQueue);
fullQueue = fullQueue.concat(sorted);
});
}
}
if (parseInt(settings.data.typePriorityMode) == 2) {
if (settings.data.sorttypes) {
$('#types > item').each(function() {
var typeFilter = sorter.filterType(this.className, fullQueue);
if (settings.data.sortlevels) {
$('#levels > item').each(function() {
var level = parseInt(this.innerHTML);
var sorted = sorter.filterLevel(level, typeFilter);
typeFilter = sorter.removeLevel(level, typeFilter);
typeFilter = typeFilter.concat(sorted);
});
} else {
typeFilter = sorter.randomize(typeFilter);
}
fullQueue = sorter.removeType(this.className, fullQueue);
fullQueue = fullQueue.concat(typeFilter);
});
}
}
$.jStorage.set(dataset.active, (dataset.quick === 'r' ? fullQueue : fullQueue.splice(0, $.jStorage.get('l/batchSize'))));
$.jStorage.set(dataset.queue, (dataset.quick === 'r' ? [] : fullQueue));
dataset.updateVisual();
},
removeLevel: function(level, list) {
if (!list) {
var fullQueue = sorter.removeLevel(level, $.jStorage.get(dataset.queue));
var activeQueue = sorter.removeLevel(level, $.jStorage.get(dataset.active));
$.jStorage.set(dataset.queue, fullQueue);
$.jStorage.set(dataset.active, activeQueue);
sorter.reorder();
return;
}
return list.filter(function(ele, ind) {
return ele.level != level;
});
},
removeType: function(type, list) {
if (!list) {
var fullQueue = sorter.removeType(type, $.jStorage.get(dataset.queue));
var activeQueue = sorter.removeType(type, $.jStorage.get(dataset.active));
$.jStorage.set(dataset.queue, fullQueue);
$.jStorage.set(dataset.active, activeQueue);
sorter.reorder();
return;
}
return list.filter(function(ele, ind) {
return !ele[type.substr(0, 3)];
});
},
sortByType: function(list) {
return list.sort(function(a, b) {
return (sorter.getPriority(a) - sorter.getPriority(b));
});
},
setPriorities: function() {
settings.data.priority.rad = $('#types .radical').index();
settings.data.priority.kan = $('#types .kanji').index();
settings.data.priority.voc = $('#types .vocabulary').index();
}
};
var utilities = {
forceQuestionTypeUpdate: function() {
var current = $.jStorage.get("currentItem");
if (!current) {
return;
}
var type = $.jStorage.get("questionType");
if (current.rad) {
if (type !== "meaning") {
$.jStorage.set("questionType", "meaning");
$.jStorage.set("currentItem", current);
}
return;
}
var typeMethod = parseInt(settings.data.questionTypeMode);
var data = $.jStorage.get(utilities.toUID(current));
if (!typeMethod && (!data || (!data.mc && !data.rc))) {
if ((new Date().getTime() - lastUpdate) > 500) {
lastUpdate = new Date().getTime();
var nextRandType = ["reading", "meaning"][Math.round(window.Math.randomB())];
console.log(type, nextRandType);
if (type != nextRandType) {
$.jStorage.set('questionType', nextRandType);
$.jStorage.set("currentItem", current);
}
}
}
if (typeMethod === 1) {
if (!data || !data.rc) {
if (type !== "reading") {
$.jStorage.set("questionType", "reading");
$.jStorage.set("currentItem", current);
}
} else {
if (type !== "meaning") {
$.jStorage.set("questionType", "meaning");
$.jStorage.set("currentItem", current);
}
}
}
if (typeMethod === 2) {
if (!data || !data.mc) {
if (type !== "meaning") {
$.jStorage.set("questionType", "meaning");
$.jStorage.set("currentItem", current);
}
} else {
if (type !== "reading") {
$.jStorage.set("questionType", "reading");
$.jStorage.set("currentItem", current);
}
}
}
},
highestPriorityType: function() {
return $('#types item').not('.hidden').first().attr('class');
},
highestPriorityLevel: function() {
return parseInt($('#levels item').not('.hidden').first().text());
},
log: function(msg) {
console.dlog.push(msg);
console.debug(msg);
},
newRandom: function(fullVal) {
if (!settings.data.onebyone && ordered) {
var fullQueue = $.jStorage.get(dataset.active).concat($.jStorage.get(dataset.queue));
var fullLength = fullQueue.length;
if (settings.data.sortlevels && parseInt(settings.data.typePriorityMode) == 1) {
fullQueue = sorter.filterLevel(utilities.highestPriorityLevel(), fullQueue);
if (settings.data.sorttypes) {
fullQueue = sorter.filterType(utilities.toType(fullQueue[0]), fullQueue);
}
}
if (settings.data.sorttypes && parseInt(settings.data.typePriorityMode) == 2) {
fullQueue = sorter.filterType(utilities.highestPriorityType(), fullQueue);
if (settings.data.sortlevels) {
fullQueue = sorter.filterLevel(fullQueue[0].level, fullQueue);
}
}
return Math.floor(window.Math.randomB() * Math.min(10, fullQueue.length)) / (fullVal ? 1 : Math.max(fullLength, 1));
}
return settings.data.onebyone ? 0 : window.Math.randomB();
},
toType: function(item) {
return (item && item.rad) ? 'rad' : item.kan ? 'kan' : 'voc' || "-1";
},
toUID: function(item) {
return ((item && item.rad) ? 'r' : item.kan ? 'k' : 'v') + item.id || "-1";
},
settingsValueToClass: function(id, val) {
val = parseInt(val);
if (id === "priority") {
switch (val) {
case 0:
return "balance";
case 1:
return "level-heavy";
case 2:
return "type-heavy";
}
} else {
switch (val) {
case 0:
return "balance2";
case 1:
return "reading-heavy";
case 2:
return "meaning-heavy";
}
}
}
};
$('div[id*="loading"]:visible').on('hide', function() {
dataset = (location.pathname.match('review') ? reviewset : lessonset);
window.Math.randomB = window.Math.random;
if (dataset.quick === 'r') {
window.Math.random = utilities.newRandom;
}
setup.init();
setup.listeners();
});