Created
February 7, 2015 20:26
-
-
Save sheodox/5daa04083fe73f06d691 to your computer and use it in GitHub Desktop.
WaniKani override, fixed for Greasemonkey
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Wanikani Override | |
// @namespace wkoverride | |
// @description Adds an "Ignore Answer" button during reviews that makes WaniKani ignore the current answer (useful if, for example, you made a stupid typo) | |
// @include http://www.wanikani.com/review/session* | |
// @include https://www.wanikani.com/review/session* | |
// @version 1.1.3 | |
// @author Rui Pinheiro | |
// @grant GM_addStyle | |
// @grant unsafeWindow | |
// @require http://code.jquery.com/jquery-1.11.2.min.js | |
// @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html | |
// ==/UserScript== | |
/* | |
* ==== Wanikani Override ==== | |
* == by Rui Pinheiro == | |
* | |
* One of my biggest peeves with Wanikani is how once you get something wrong, | |
* you can't change it. If you type too fast and make a typo, or if the spell checker | |
* doesn't like you, you're out of luck. It's really frustrating to know an answer but | |
* have Wanikani deny it to you because it wants the '-ing' form of the verb, but you | |
* typed the infinitive. | |
* Sometimes you get lucky, and the spell checker is lenient enough to mark the answer | |
* correctly. Other times, that's not the case. | |
* Then, there's the other type of mistakes. The ones where you type a reading but were | |
* supposed to type a meaning, or the opposite. Those are really annoying, since most | |
* times you actually knew the correct reading or meaning respectively. | |
* | |
* | |
* My problem is that the decision was completely out of my hands. My objective is to | |
* learn Kanji, and I'm not going to cheat since that wouldn't bring me closer to where | |
* I want to be, but Wanikani decides that I can't be trusted and does not let me correct it. | |
* | |
* But now, with the client-side reviews update, we can finally do something about all this! | |
* | |
* | |
* | |
* This userscript fixes this problem, by adding an "Ignore Answer" button to the footer | |
* during reviews. This button can be clicked anytime an answer has been marked as wrong by | |
* Wanikani. | |
* | |
* Once clicked, the current answer will be changed from "incorrect" (red background) | |
* to "ignored" (yellow background), and Wanikani will forget you even answered it. | |
* This way, the item will appear again later on during the review cycle, effectively | |
* giving you a second chance to get it right without stupid typos or the spell checker | |
* getting in the way. | |
* | |
* | |
* DISCLAIMER: | |
* I am not responsible for any problems caused by this script. | |
* This script was developed on Firefox 25.0.1 with Greasemonkey 1.12. | |
* It was also tested on Chrome 31.0.1650.57 with Tampermonkey 3.5.3630.77. | |
* Because I'm just one person, I can't guarantee the script works anywhere else. | |
* | |
* Also, anyone using this script is responsible for using it correctly. | |
* This should be used only if you make an honest mistake but actually knew the correct | |
* answer. Using it in any other way will harm your Kanji learning process, | |
* cheating will only make learning Japanese harder and you'll end up harming only yourselves! | |
*/ | |
/* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
/* | |
* === Changelog === | |
* | |
* 1.1.2 (11 March 2014) | |
* - Relicensed under the GPLv3. | |
* | |
* 1.1.1 (23 January 2014) | |
* - Now supports the HTTPS protocol. | |
* | |
* 1.1.0 (24 November 2013) | |
* - Previously ignored answers can be unignored by pressing the 'Ignore' button again before skipping to the next question. | |
* - Code cleaned up, added debug functionality and better error logging (like all my other Wanikani scripts) | |
* | |
* 1.0.5 (7 November 2013) | |
* - Ignores '~' hotkey if pressed while editing an item's notes. | |
* | |
* 1.0.4 (14 August 2013) | |
* - Fixed the '~' hotkey in Chrome. | |
* | |
* 1.0.3 (5 August 2013) | |
* - Wanikani update changed the review URL. While the script worked, it also affected the reviews summary page. This has been fixed | |
* | |
* 1.0.2 (29 July 2013) | |
* - Made '~' a hotkey, as requested by some users of the Wanikani forums. | |
* - Script now updates the question count and error count correctly. | |
* | |
* 1.0.1 (24 July 2013) | |
* - Fixed a bug that would cause "Null" to appear instead of "Reading" or "Meaning" after ignoring a certain Vocabulary. | |
* - Tested the script on Google Chrome with Tampermonkey (the script does not work natively). | |
* | |
* 1.0.0 (23 July 2013) | |
* - First release. | |
* | |
*/ | |
/* | |
* Debug Settings | |
*/ | |
var debugLogEnabled = false; | |
var scriptShortName = 'WKO'; | |
scriptLog = debugLogEnabled ? function (msg) { | |
if (typeof msg === 'string') { | |
console.log(scriptShortName + ': ' + msg); | |
} else { | |
console.log(msg); | |
} | |
} | |
: function () { | |
}; | |
/* | |
* Other settings | |
*/ | |
var prefAllowUnignore = true; | |
/* | |
* "Ignore Answer" Button Click | |
*/ | |
var ActionEnum = Object.freeze({ | |
ignore: 0, | |
unignore: 1 | |
}); | |
function WKO_ignoreAnswer() | |
{ | |
try | |
{ | |
/* Check if the current item was answered incorrectly */ | |
var elmnts = document.getElementsByClassName('incorrect'); | |
var elmnts2 = document.getElementsByClassName('WKO_ignored'); | |
var curAction; | |
if (!isEmpty(elmnts[0])) // Current answer is wrong | |
curAction = ActionEnum.ignore; | |
else if (prefAllowUnignore && !isEmpty(elmnts2[0])) // Current answer is ignored | |
curAction = ActionEnum.unignore; | |
else | |
// Either there is no current answer, or it's correct | |
{ | |
alert('WKO: Current item wasn\'t answered incorrectly, nor ignored previously!'); | |
return false; | |
} | |
/* Grab information about current question */ | |
var curItem = $.jStorage.get('currentItem'); | |
var questionType = $.jStorage.get('questionType'); | |
/* Build item name */ | |
var itemName; | |
if (curItem.rad) | |
itemName = 'r'; | |
else if (curItem.kan) | |
itemName = 'k'; | |
else | |
itemName = 'v'; | |
itemName += curItem.id; | |
scriptLog(itemName); | |
/* Grab item from jStorage. | |
* | |
* item.rc and item.mc => Reading/Meaning Completed (if answered the item correctly) | |
* item.ri and item.mi => Reading/Meaning Invalid (number of mistakes before answering correctly) | |
*/ | |
var item = $.jStorage.get(itemName) || { | |
}; | |
/* Update the item data */ | |
if (questionType === 'meaning') | |
{ | |
if (!('mi' in item) || isEmpty(item.mi)) | |
{ | |
throw Error('item.mi undefined'); | |
return false; | |
} | |
else if (item.mi < 0 || (item.mi == 0 && curAction == ActionEnum.ignore)) | |
{ | |
throw Error('item.mi too small'); | |
return false; | |
} | |
if (curAction == ActionEnum.ignore) | |
item.mi -= 1; | |
else | |
item.mi += 1; | |
delete item.mc; | |
} | |
else | |
{ | |
if (!('ri' in item) || isEmpty(item.ri)) | |
{ | |
throw Error('item.ri undefined'); | |
return false; | |
} | |
else if (item.ri < 0 || (item.ri == 0 && curAction == ActionEnum.ignore)) | |
{ | |
throw Error('i.ri too small'); | |
return false; | |
} | |
if (curAction == ActionEnum.ignore) | |
item.ri -= 1; | |
else | |
item.ri += 1; | |
delete item.rc; | |
} | |
/* Save the new state back into jStorage */ | |
$.jStorage.set(itemName, item); | |
/* Modify the questions counter and wrong counter and change the style of the answer field */ | |
var wrongCount = $.jStorage.get('wrongCount'); | |
var questionCount = $.jStorage.get('questionCount'); | |
if (curAction == ActionEnum.ignore) | |
{ | |
$.jStorage.set('wrongCount', wrongCount - 1); | |
$.jStorage.set('questionCount', questionCount - 1); | |
$('#answer-form fieldset').removeClass('incorrect'); | |
$('#answer-form fieldset').addClass('WKO_ignored'); | |
} | |
else | |
{ | |
$.jStorage.set('wrongCount', wrongCount + 1); | |
$.jStorage.set('questionCount', questionCount + 1); | |
$('#answer-form fieldset').removeClass('WKO_ignored'); | |
$('#answer-form fieldset').addClass('incorrect'); | |
} | |
return true; | |
} | |
catch (err) { | |
logError(err); | |
} | |
} | |
/* | |
* Bind '~' as a hotkey | |
*/ | |
function bindHotkey() | |
{ | |
jQuery(document).on('keydown.reviewScreen', function (event) | |
{ | |
if ($('#reviews').is(':visible') && !$('*:focus').is('textarea, input')) | |
{ | |
//alert('keycode: ' + event.keyCode); | |
switch (event.keyCode) { | |
case 176: //Firefox '~' | |
case 192: //Chrome '~' | |
event.stopPropagation(); | |
event.preventDefault(); | |
if ($('#user-response').is(':disabled')) | |
WKO_ignoreAnswer(); | |
return false; | |
break; | |
} | |
} | |
}); | |
} | |
/* | |
* Inject Ignore Button | |
*/ | |
function addIgnoreAnswerBtn() | |
{ | |
var footer = document.getElementsByTagName('footer'), | |
$btn = jQuery('<div id="WKO_button" title="Ignore Answer">Ignore Answer</div>').on('click', WKO_ignoreAnswer); | |
jQuery(footer[0]).prepend($btn); | |
} | |
/* | |
* Prepares the script | |
*/ | |
function scriptInit() | |
{ | |
// Add global CSS styles | |
GM_addStyle('#WKO_button {background-color: #CC0000; color: #FFFFFF; cursor: pointer; display: inline-block; font-size: 0.8125em; padding: 10px; vertical-align: bottom;}'); | |
GM_addStyle('#answer-form fieldset.WKO_ignored input[type="text"]:-moz-placeholder, #answer-form fieldset.WKO_ignored input[type="text"]:-moz-placeholder {color: #FFFFFF; font-family: "Source Sans Pro",sans-serif; font-weight: 300; text-shadow: none; transition: color 0.15s linear 0s; } #answer-form fieldset.WKO_ignored button, #answer-form fieldset.WKO_ignored input[type="text"], #answer-form fieldset.WKO_ignored input[type="text"]:disabled { background-color: #FFCC00 !important; }'); | |
scriptLog('loaded'); | |
// Set up hooks | |
try | |
{ | |
addIgnoreAnswerBtn(); | |
bindHotkey(); | |
} | |
catch (err) { | |
logError(err); | |
} | |
} | |
/* | |
* Helper Functions/Variables | |
*/ | |
//use 'jQuery' for greasemonkey's version, $ is WK's jQuery | |
$ = unsafeWindow.$; | |
function isEmpty(value) { | |
return (typeof value === 'undefined' || value === null); | |
} | |
/* | |
* Error handling | |
* Can use 'error.stack', not cross-browser (though it should work on Firefox and Chrome) | |
*/ | |
function logError(error) | |
{ | |
var stackMessage = ''; | |
if ('stack' in error) | |
stackMessage = '\n\tStack: ' + error.stack; | |
console.error(scriptShortName + ' Error: ' + error.name + '\n\tMessage: ' + error.message + stackMessage); | |
} | |
/* | |
* Start the script | |
*/ | |
scriptInit(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you