|
// ==UserScript== |
|
// @name Jitai |
|
// @version 1.0.0 |
|
// @description Display WaniKani reviews in randomized fonts, for more varied reading training. |
|
// @author Samuel (@obskyr) |
|
// @namespace http://obskyr.io/ |
|
// @homepageURL https://gist.github.com/obskyr/9f3c77cf6bf663792c6e |
|
// @icon http://i.imgur.com/7uScPR4.png |
|
// @include /^https?://(www\.)?wanikani\.com/review/session/?$/ |
|
// @grant none |
|
// ==/UserScript== |
|
|
|
/* |
|
To control which fonts to choose from, edit this list. |
|
If you feel too many fonts of a certain type are showing |
|
up, remove a few of those from the list. If you've got |
|
fonts that aren't in the list that you'd like to be used, |
|
add their names and they'll be in the rotation. |
|
*/ |
|
|
|
var fonts = [ |
|
// Default Windows fonts |
|
"Meiryo", |
|
"MS Gothic", |
|
"MS Mincho", |
|
"Yu Gothic", |
|
"Yu Mincho", |
|
|
|
// Default OS X fonts |
|
"Hiragino Kaku Gothic Pro", |
|
"Hiragino Maru Gothic Pro", |
|
"Hiragino Mincho Pro", |
|
"Hannotate", |
|
"HanziPen", |
|
|
|
// Common Linux fonts |
|
"Takao Gothic, TakaoGothic", |
|
"Takao Mincho, TakaoMincho", |
|
"Sazanami Gothic", |
|
"Sazanami Mincho", |
|
"Kochi Gothic", |
|
"Kochi Mincho", |
|
"Dejima Mincho", |
|
"Ume Gothic", |
|
"Ume Mincho", |
|
|
|
// Other Japanese fonts people use. |
|
// You might want to try some of these! |
|
"EPSON 行書体M", |
|
"EPSON 正楷書体M", |
|
"EPSON 教科書体M", |
|
"EPSON 太明朝体B", |
|
"EPSON 太行書体B", |
|
"EPSON 丸ゴシック体M", |
|
"cinecaption", |
|
"nagayama_kai", |
|
"A-OTF Shin Maru Go Pro", |
|
"Hosofuwafont", |
|
"ChihayaGothic", |
|
"chifont", |
|
"'chifont+'", |
|
"darts font", |
|
"santyoume-font", |
|
"FC-Flower", |
|
"ArmedBanana", // This one is completely absurd. I recommend it. |
|
"HakusyuKaisyoExtraBold_kk", |
|
"aoyagireisyosimo2, AoyagiKouzanFont2OTF", |
|
"S2G Nagurigaki font, S2G Nagurigaki font-PRO, nagurigaki, nagurigaki-P", |
|
|
|
// Add your fonts here! |
|
"Fake font name that you can change", |
|
"Another fake font name", |
|
"Just add them like this!", |
|
"Quotes around the name, comma after." |
|
]; |
|
|
|
var existingFonts = []; |
|
for (var i = 0; i < fonts.length; i++) { |
|
var fontName = fonts[i]; |
|
if (fontExists(fontName)) { |
|
existingFonts.push(fontName); |
|
} |
|
} |
|
|
|
function fontExists(fontName) { |
|
// Approach from kirupa.com/html5/detect_whether_font_is_installed.htm - thanks! |
|
// Will return false for the browser's default monospace font, sadly. |
|
var canvas = document.createElement('canvas'); |
|
var context = canvas.getContext("2d"); |
|
var text = "wim-—l~ツ亻".repeat(100); // Characters with widths that often vary between fonts. |
|
|
|
context.font = "72px monospace"; |
|
var defaultWidth = context.measureText(text).width; |
|
|
|
context.font = "72px " + fontName + ", monospace"; |
|
var testWidth = context.measureText(text).width; |
|
|
|
delete canvas; |
|
|
|
return testWidth != defaultWidth; |
|
} |
|
|
|
function canRepresentGlyphs(fontName, glyphs) { |
|
var canvas = document.createElement('canvas'); |
|
canvas.width = 50; |
|
canvas.height = 50; |
|
var context = canvas.getContext("2d"); |
|
context.textBaseline = 'top'; |
|
|
|
var blank = document.createElement('canvas'); |
|
blank.width = canvas.width; |
|
blank.height = canvas.height; |
|
var blankDataUrl = blank.toDataURL(); |
|
|
|
context.font = "24px " + fontName; |
|
|
|
var result = true; |
|
for (var i = 0; i < glyphs.length; i++) { |
|
context.fillText(glyphs[i], 0, 0); |
|
if (canvas.toDataURL() === blankDataUrl) { |
|
result = false; |
|
break; |
|
} |
|
context.clearRect(0, 0, canvas.width, canvas.height); |
|
} |
|
|
|
delete canvas; |
|
delete blank; |
|
|
|
return result; |
|
} |
|
|
|
function shuffle(arr) { |
|
for (var i = arr.length;; i > 0) { |
|
var otherIndex = Math.floor(Math.random() * i); |
|
i--; |
|
|
|
var temp = arr[i]; |
|
arr[i] = arr[otherIndex]; |
|
arr[otherIndex] = temp; |
|
} |
|
return arr; |
|
} |
|
|
|
function shuffle(array) { |
|
let counter = array.length; |
|
|
|
// While there are elements in the array |
|
while (counter > 0) { |
|
// Pick a random index |
|
let index = Math.floor(Math.random() * counter); |
|
|
|
// Decrease counter by 1 |
|
counter--; |
|
|
|
// And swap the last element with it |
|
let temp = array[counter]; |
|
array[counter] = array[index]; |
|
array[index] = temp; |
|
} |
|
|
|
return array; |
|
} |
|
|
|
var $characterSpan; |
|
function setToRandomFont(glyphs) { |
|
// The font is set as a randomly shuffled list of the existing fonts |
|
// in order to always show a random font, even if the first one chosen |
|
// doesn't have a certain glyph being attempted to be displayed. |
|
var randomlyOrdered = shuffle(existingFonts.slice()); |
|
|
|
// Some fonts don't contain certain radicals, for example, so it's best |
|
// to check that the font used can represent all the glyphs. The reason |
|
// the browser can't switch automatically is that some fonts report that |
|
// they have a glyph, when in fact they just show up blank. |
|
if (glyphs) { |
|
var currentFont = []; |
|
for (var i = 0; i < randomlyOrdered.length; i++) { |
|
var fontName = randomlyOrdered[i] |
|
if (canRepresentGlyphs(fontName, glyphs)) { |
|
currentFont.push(fontName); |
|
} |
|
} |
|
} else { |
|
var currentFont = randomlyOrdered; |
|
} |
|
|
|
currentFont = currentFont.join(', '); |
|
$characterSpan.css('font-family', currentFont); |
|
} |
|
|
|
function setToDefaultFont(fontName) { |
|
$characterSpan.css('font-family', ''); |
|
} |
|
|
|
$(document).ready(function() { |
|
$characterSpan = $('#character span'); |
|
var defaultFont = $characterSpan.css('font-family') + ' !important'; |
|
|
|
// Add hover style, to allow hovering away weird fonts. |
|
var $hoverStyle = $('<style/>', {'type': 'text/css'}); |
|
$hoverStyle.text("#character span:hover {font-family: " + defaultFont + ";}"); |
|
$('head').append($hoverStyle); |
|
|
|
// Make sure page doesn't jump around on hover. |
|
$('#character').css('height', $('#character').css('line-height')); |
|
|
|
|
|
// answerChecker.evaluate is only called when checking the answer, which |
|
// is why we catch it, check for the "proceed to correct/incorrect display" |
|
// condition, and set the font back to default if it's a non-stopping answer. |
|
var oldEvaluate = answerChecker.evaluate; |
|
answerChecker.evaluate = function(questionType, answer) { |
|
var result = oldEvaluate.apply(this, [questionType, answer]); |
|
|
|
if (!result.exception) { |
|
setToDefaultFont(); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
// $.jStorage.set('currentItem') is only called right when switching to a |
|
// new question, which is why we hook into it to randomize the font at the |
|
// exact right time: when a new item shows up. |
|
var oldSet = $.jStorage.set; |
|
$.jStorage.set = function(key, value, options) { |
|
var ret = oldSet.apply(this, [key, value, options]); |
|
if (key === 'currentItem') { |
|
setToRandomFont(value.kan || value.voc || value.rad); |
|
} |
|
return ret; |
|
} |
|
|
|
setToRandomFont(); |
|
}); |