Last active
May 25, 2017 02:55
-
-
Save sheodox/2fba30197f47795b2bf5009c3634d5d9 to your computer and use it in GitHub Desktop.
Memrise Add Wizard
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 Memrise Add Wizard | |
// @namespace http://tampermonkey.net/ | |
// @version 0.1 | |
// @description Wizard for adding words to a course | |
// @author sheodox | |
// @match https://www.memrise.com/course/*/edit/* | |
// @grant GM_getValue | |
// @grant GM_setValue | |
// @grant GM_xmlhttpRequest | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
const $styles = $(` | |
<style> | |
.control-panel { | |
position: fixed; | |
z-index: 10000000; | |
padding: 3rem; | |
background: #434c59; | |
color: white; | |
top: 50%; | |
left: 50%; | |
transform: translateX(-50%) translateY(-50%); | |
} | |
.control-panel label { | |
display: inline-block; | |
margin: 0 2px; | |
} | |
.control-panel input { | |
background-color: #23282f; | |
border: none; | |
} | |
.control-panel .columns .one-half { | |
width: 400px; | |
} | |
.control-panel .columns > :first-child { | |
float: left; | |
} | |
.control-panel .columns > :last-child { | |
margin-left: 2rem; | |
float: right; | |
} | |
.control-panel textarea#working-definition { | |
resize: both; | |
width: 400px; | |
height: 150px; | |
} | |
.control-panel #goo-definition > ol { | |
list-style: none; | |
} | |
.control-panel .supplemental_info { | |
display: block; | |
font-size: 75%; | |
} | |
</style>`).appendTo('head'); | |
function createDialog() { | |
return $('<div class="control-panel"></div>').appendTo('body'); | |
} | |
let $row, | |
wordFields; | |
function showWizard() { | |
wordFields = {}; | |
getContext() | |
.then(getWord) | |
.then(lookupWord) | |
.then(commit); | |
} | |
//grab the necessary piece of the document before parsing with jQuery to avoid CSRF issues | |
function parseAndSelect(html, selector) { | |
let tmp = document.implementation.createHTMLDocument(); | |
tmp.body.innerHTML = html; | |
return $($.parseHTML(tmp.querySelector(selector).outerHTML)); | |
} | |
function getContext() { | |
return new Promise(res => { | |
const $dialog = createDialog(); | |
$dialog.append(` | |
<label for="context-sentence">Context Sentence</label> | |
<input id="context-sentence"> | |
`); | |
$dialog.find('#context-sentence') | |
.focus() | |
.on('paste', function(e) { | |
wordFields.context = e.originalEvent.clipboardData.getData('text'); | |
$dialog.remove(); | |
res(); | |
}); | |
}); | |
} | |
function searchGoo(encoded) { | |
return new Promise((resolve, reject) => { | |
get(`https://dictionary.goo.ne.jp/srch/all/${encoded}/m1u/`) | |
.then(result => { | |
const $goo = parseAndSelect(result, '#NR-wrapper a'); | |
get(`https://dictionary.goo.ne.jp${$goo.attr('href')}`) | |
.then(resolve, reject); | |
}) | |
}); | |
} | |
function getWord() { | |
return new Promise(res => { | |
const $dialog = createDialog(); | |
$dialog.append(` | |
<p>Please select the word you want to study.</p> | |
<p id="context-sentence"></p> | |
`); | |
$dialog.find('#context-sentence') | |
.text(wordFields.context) | |
.on('mouseup', function() { | |
wordFields.common = window.getSelection().toString(); | |
res(); | |
$dialog.remove(); | |
}); | |
}); | |
} | |
function get(url) { | |
return new Promise((res, reject) => { | |
GM_xmlhttpRequest({ | |
method: 'GET', | |
url: url, | |
onload: function(response) { | |
res(response.responseText); | |
} | |
}); | |
}); | |
} | |
function lookupWord() { | |
return new Promise(res => { | |
const $dialog = createDialog(), | |
encoded = encodeURIComponent(wordFields.common); | |
get(`http://jisho.org/search/${encoded}`) | |
.then(html => { | |
const $jishoInfo = parseAndSelect(html, '.concept_light'); | |
//try to parse accurate kana | |
const $furigana = $jishoInfo.find('.furigana').find('span'), //each character is represented by a span, empty span means it's not above a kanji | |
common = $jishoInfo.find('.concept_light-representation .text').text().trim(); | |
wordFields.kana = [].reduce.call(common, function(done, next, index) { | |
const correspondingFurigana = $furigana.eq(index).text().trim(); | |
return done + (correspondingFurigana ? correspondingFurigana : next); | |
}, ''); | |
wordFields.common = common; | |
$jishoInfo.find('.sentences').remove(); | |
$dialog.find('#jisho-definition').html($jishoInfo.find('.meanings-wrapper').html()); | |
searchGoo(encodeURIComponent(common)) | |
.then(html => { | |
const $gooInfo = parseAndSelect(html, '.explanation'); | |
$dialog.find('#goo-definition').html($gooInfo.html()); | |
}); | |
}); | |
$dialog.append(` | |
<label for="working-definition">Definition:</label> | |
<textarea id="working-definition"></textarea> | |
<br> | |
<button id="submit-definition">Definition Complete!</button> | |
<br> | |
<div class="columns" id="definition-selection"> | |
<div class="column one-half"> | |
<p>Jisho</p> | |
<div id="jisho-definition"></div> | |
</div> | |
<div class="column one-half goo"> | |
<p>Goo</p> | |
<div id="goo-definition"></div> | |
</div> | |
</div> | |
`); | |
const $definition = $('#working-definition'); | |
$dialog.find('#definition-selection') | |
.on('mouseup', function() { | |
const selection = window.getSelection().toString(), | |
def = $definition.val().trim() + ' ' + selection.trim(); | |
$definition.val(def); | |
wordFields.definition = def; | |
}); | |
$dialog.find('#submit-definition').on('click', function() { | |
res(); | |
$dialog.remove(); | |
}); | |
}); | |
} | |
function commit() { | |
function setInput(columnNumber, val) { | |
$row.find(`td[data-key="${columnNumber}"] input`).val(val); | |
} | |
setInput('1', wordFields.common); | |
setInput('2', wordFields.definition); | |
setInput('3', wordFields.kana); | |
setInput('4', wordFields.context); | |
$row.find('input:first').focus(); | |
} | |
//listen for Alt + W on an input to show the wizard | |
$('body') | |
.on('keydown', 'tbody.adding td input', function(e) { | |
if (e.which === 87 && e.altKey) { | |
$row = $(this).parents('tr'); | |
showWizard(); | |
} | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment