/*
* @title TOEFL Vocabulary List をフラッシュカード化
* @description http://www.examword.com/toefl で紹介されている TOEFL Vocabulary List をフラッシュカード化
* @include http://www.examword.com/toefl/practice.aspx
* @license MIT License
* @require
*/
(function(console){
var KEY_RETURN = 13;
var shuffle = function(a) {
var i = a.length, j, temp;
if ( i == 0 ) return;
while ( --i ) {
j = Math.floor( Math.random() * ( i + 1 ) );
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
};
var lcs = function (lcstest, lcstarget) {
var matchfound = 0;
var lsclen = lcstest.length;
for(var lcsi=0; lcsi<lcstest.length; lcsi++){
var lscos=0;
for(var lcsj=0; lcsj<lcsi+1; lcsj++){
var re = new RegExp("(?:.{" + lscos + "})(.{" + lsclen + "})", "i");
var temp = re.test(lcstest);
re = new RegExp("(" + RegExp.$1 + ")", "i");
if(re.test(lcstarget)){
matchfound=1;
var result = RegExp.$1;
break;
}
lscos = lscos + 1;
}
if(matchfound==1){return result; break;}
lsclen = lsclen - 1;
}
return "";
};
var map = function(ar, cb){
var l = ar.length;
var R = new Array(l);
for(var i=0;i<l;i++){
R[i] = cb(ar[i],i);
}
return R;
};
var googlingURL = function(word){
return (
"https://www.google.com/search?q="+
encodeURIComponent(word) +
"&oe=utf-8"
);
};
var sentenceURL = function(word){
return (
'http://sentence.yourdictionary.com/' +
encodeURIComponent(word)
);
};
var imgSearchURL = function(word){
return googlingURL(word) + '&tbm=isch';
};
var dictURL = function(word){
return googlingURL('define:' + word);
};
var translateURL = function(word){
return (
"http://translate.google.com/#en/ja/" +
encodeURIComponent(word)
);
};
var dumpData = function(data, title){
if(!title) title = data.substr(0,20);
var win = window.open();
var doc = win.document;
doc.open();
doc.write('<!DOCTYPE html><title>'+title+'<body>');
doc.close();
doc.body.appendChild(doc.createTextNode(data));
};
var createFlashCard = function(target, note){
var getWordFromElement = function(wordElem){
var content = wordElem.textContent;
return content.replace(/:$/, '');
};
var getExplain = function(wordElem){
for(var n = wordElem.nextSibling;n;n = n.nextSibling){
if(String(n.className) == 'listWordExplanation') return n;
}
return null;
};
var getBRs = function(exp){
var R = [];
for(var n = exp.nextSibling;n;n=n.nextSibling){
if(String(n.tagName).toLowerCase() == 'br')R.push(n);
if(String(n.tagName).toLowerCase() == 'span')break;
}
return R;
};
var exp = getExplain(target);
var card = {
text: getWordFromElement(target),
word: target,
explain: exp,
brs: getBRs(exp),
note: note,
container: null,
getScore: function(){
var ld = this.getLearningData();
return Math.min((ld.count || 0) / 15, 1);
},
hide: function(){
this.container.style.display = 'none';
},
show: function(){
this.container.style.display = null;
},
getExpires: function(){
var ld = this.getLearningData();
if(!ld.start) return null;
if(!ld.date){
return Date.now() - 3600000 * 24;
}
var R = ld.date + (10 * Math.pow(2, (ld.count || 0))) * 1000;
if(isNaN(R)) console.log(this.text,
ld.date,
Math.pow(1.5, (ld.count || 0)), ld);
return R;
},
getLearningData: function(){
return this.note.getLearningData(this.text);
},
init: function(){
var parent = this.word.parentNode;
parent.removeChild(this.word);
parent.removeChild(this.explain);
var brs = this.brs;
for(var i=0,l=brs.length;i<l;i++){
parent.removeChild(brs[i]);
}
var container = document.createElement('div');
this.container = container;
container.appendChild(this.word);
container.appendChild(this.explain);
},
explainList: function(){
var exp = this.explain.cloneNode(true);
exp.style.display = 'block';
var content = exp.textContent;
if(!content) return exp;
var all = content.split(/\s*;\s*/);
if(all.length < 2) return exp;
var ul = document.createElement('ul');
for(var i=0,l=all.length;i<l;i++){
var li = document.createElement('li');
li.appendChild(document.createTextNode(all[i]));
ul.appendChild(li);
}
return ul;
},
makeQuestion: function(wait){
var container = this.note.wordContainer;
var ld = this.getLearningData();
container.innerHTML = '';
if(ld.repeat){
container.style.background = '#FFC';
}
var input = document.createElement('input');
var self = this;
input.addEventListener(
'keyup',
function(ev){
if(ev.keyCode != KEY_RETURN) return;
self.checkAnswer(input.value);
}
);
if(ld.repeat == 1){
input.type = 'password';
}
var exp = this.explainList();
container.appendChild(input);
container.appendChild(exp);
input.style.fontSize = '2em';
input.style.opacity = '0';
input.disabled = true;
setTimeout(
function(){
input.style.opacity = null;
input.disabled = false;
input.focus();
},
(wait || 0));
},
showAnswer: function(answer, repeat){
var success = (answer === null || answer === undefined);
var container = this.note.wordContainer;
container.innerHTML = '';
container.style.background = success ? '#CFC' : '#FCC';
var correct = document.createElement('div');
correct.appendChild(document.createTextNode(
repeat == 1 ? this.text.replace(/./g,'*') : this.text
));
correct.style.fontSize = '2em';
container.appendChild(correct);
if(!success){
var youwrote = document.createElement('div');
youwrote.appendChild(document.createTextNode(answer));
youwrote.style.color = '#C99';
container.appendChild(youwrote);
}
var exp = this.explainList();
container.appendChild(exp);
var ul = document.createElement('ul');
ul.innerHTML =
[
'<li><a href="',dictURL(this.text),
'" target="_blank">search definition of "',
this.text,'".</a></li>',
'<li><a href="',imgSearchURL(this.text),
'" target="_blank">search images about "',
this.text, '".</a></li>',
'<li><a href="',sentenceURL(this.text),
'" target="_blank">sentences with "',
this.text, '".</a></li>',
'<li><a href="',translateURL(this.text),
'" target="_blank">Google translate: "',this.text,'"</a></li>'
].join('');
container.appendChild(ul);
ul.style.fontSize = '11px';
var button = document.createElement('div');
button.appendChild(document.createTextNode('NEXT TERM'));
button.style.cssText = [
'background: '+(success ? 'lime;' : 'red;'),
'color:black;',
'border-radius: 10px;',
'text-align: center;',
'padding: 5px;',
'margin: 5px;',
'cursor: pointer;',
''
].join('\n');
button.tabIndex = 0;
var self = this;
var wait = (repeat || -1) == 1 ? 10 * 1000 : 0;
var lstnr = function(){
container.style.background = null;
container.innerHTML = '';
self.note.nextQuestion(wait);
};
button.addEventListener('click',lstnr, false );
button.addEventListener(
'keyup',
function(ev){
if(ev.keyCode == KEY_RETURN) lstnr();
}, false
);
container.appendChild(button);
if(success && !repeat){
var ld = this.getLearningData();
var score = document.createElement('div');
score.appendChild(document.createTextNode(
'You succeed: ' + ld.count+ ' times'));
score.style.cssText = [
'text-align:right;',
'font-size: 11px;'
].join('\n');
container.appendChild(score);
}
button.focus();
this.note.updateScore();
},
checkAnswer: function(answer){
if(this.text == answer) return this.success();
return this.fail(answer);
},
learned: function(){
var slot = this.getLearningData();
var now = Date.now();
if(!slot.start){
console.log(this.text, "is already learned....");
slot.start = now;
slot.date = now - 3600000;
slot.count = 10;
}else{
if(slot.repeat){
slot.repeat--;
}else{
slot.date = now;
slot.count++;
}
}
this.note.saveToStorage();
},
success: function(){
var ld = this.getLearningData();
var rep = ld.repeat;
this.learned();
this.showAnswer(null, rep);
return true;
},
forget: function(){
var slot = this.getLearningData();
if(!slot.start) slot.start = Date.now();
slot.repeat = 2;
slot.count = 0;
this.note.saveToStorage();
},
fail: function(answer){
this.forget();
this.showAnswer(answer);
return false;
}
};
card.init();
return card;
};
var detectId = function(){
var getQuery = function(){
var src = location.search;
if(!src || src.length < 3) return {};
var pairs = src.substring(1).split("&");
var R = {};
for(var i=0,l=pairs.length;i<l;i++){
var kv = pairs[i].split("=");
var key = decodeURIComponent(kv[0]);
var val = decodeURIComponent(kv[1]);
R[key]=val;
}
return R;
};
var query = getQuery();
return query.id;
};
var createNote = function(){
var note = {
id: detectId(),
cards: null,
session: {memorised:{}},
container: document.querySelector('#core div.ewnormaltextPanel'),
infoContainer: null,
wordContainer: null,
currentCard: null,
getLearningData: function(word){
var slot = this.session.memorised[word];
if(slot && slot.start) return slot;
slot = {count: 0};
this.session.memorised[word] = slot;
return slot;
},
loadFromStorage: function(){
var stored = sessionStorage.getItem(note.id);
try{
if(!stored) throw new Exception();
var data = JSON.parse(stored);
if(!data.memorised) data.memorised = {};
this.session = data;
}catch(e){
this.session = {
memorised: {}
};
return;
}
},
saveToStorage: function(){
sessionStorage.setItem(note.id, JSON.stringify(this.session));
},
initContainer: function(){
var cont = this.container;
cont.style.width = '600px';
var frgn = document.createDocumentFragment();
while(cont.firstChild){
var e = cont.firstChild;
cont.removeChild(e);
frgn.appendChild(e);
}
this.savedFragment = frgn;
var ic = document.createElement('div');
var wc = document.createElement('div');
cont.appendChild(ic);
cont.appendChild(wc);
ic.style.fontSize = '11px';
ic.style.textAlign = 'right';
ic.style.height = '1em';
this.infoContainer = ic;
this.wordContainer = wc;
},
init: function(){
this.loadFromStorage();
this.initContainer();
var words = this.cards;
for(var w in words){
var s = words[w];
}
},
reset: function(){
this.session = {};
this.saveToStorage();
location.reload();
},
dump: function(){
dumpData(JSON.stringify(this.session));
},
getAllCards: function(){
var R = [];
var cards = this.cards;
for(var f in cards) R.push(cards[f]);
return R;
},
getCurrentCard: function(){
var cards = this.cards;
var R = null;
var minExp = null;
var noExp = null;
for(var f in cards){
var c = cards[f];
var e = c.getExpires();
if(e === null){
if(!noExp){
noExp = c;
}
}else if(!minExp ||
e < minExp){
R = c;
minExp = e;
}
}
if(!R) return noExp;
if(minExp > Date.now()) return noExp;
return (R || noExp);
},
nextQuestion: function(wait){
this.updateScore();
this.getCurrentCard().makeQuestion(wait);
},
updateScore: function(){
var round = function(n){
return Math.floor(n * 100) / 100;
};
var sc = this.getScore();
var ic = this.infoContainer;
ic.innerHTML = '';
ic.appendChild(document.createTextNode(
round(sc.score) + ' / ' +
sc.all + ' : ' +
round(sc.rate * 100) + '%'
));
},
getScore: function(){
var all = 0;
var score = 0;
var cards = this.cards;
for(var f in cards){
score += cards[f].getScore();
all++;
}
return {
score: score,
all: all,
rate: score / all
};
}
};
note.cards = (
function(note){
var R = {};
var words = document.querySelectorAll(".listWordWord");
if(!(words && words.length)){
words = document.querySelectorAll(".listWordWord2");
}
var cards=0;
for(var i=0,l=words.length;i<l;i++){
var target = createFlashCard(words[i], note);
R[target.text] = target;
cards++;
}
return R;
}
)(note);
return note;
};
var createControler = function(note){
var genButton = function(title, action){
var span = document.createElement('span');
span.appendChild(document.createTextNode(title));
span.addEventListener('click', action, false);
var csstext = Array.prototype.slice.apply(
arguments, [2]
).join("\n");
span.style.cssText = csstext;
span.style.display = 'inline-table';
span.style.margin = '2px';
span.style.padding = '1px 3px';
span.style.borderRadius = '3px';
span.style.cursor = 'pointer';
return span;
};
var initGUI = function(ctrl){
var container = document.querySelector('div#core td.rdtop_title');
var dump = genButton(
'dump', function(){ ctrl.dump(); },
'background-color: #CCC;',
'color: #666;'
);
container.appendChild(dump);
var reset = genButton(
'reset', function(){
if(confirm('really?')) ctrl.reset();
},
'background-color: red;',
'color: white;'
);
container.appendChild(reset);
};
var ctrl = {
note: note,
reset: function(){
this.note.reset();
},
dump: function(){
this.note.dump();
},
init: function(){
initGUI(this);
}
};
return ctrl;
};
var main = function(){
var note = createNote();
var ctrl = createControler(note);
note.init();
ctrl.init();
note.nextQuestion();
};
main();
})(window.console ? window.console : {log:function(){}});