TOEFL Vocabulary List をフラッシュカード化

  • /*
     * @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 &quot;',
                 this.text,'&quot;.</a></li>',
                 '<li><a href="',imgSearchURL(this.text),
                 '" target="_blank">search images about &quot;',
                 this.text, '&quot;.</a></li>',
                 '<li><a href="',sentenceURL(this.text),
                 '" target="_blank">sentences with &quot;',
                 this.text, '&quot;.</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(){}});
    
    
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。

History

  1. 2014/05/01 15:44:42 - 2014-05-01
  2. 2014/05/01 10:55:20 - 2014-05-01
  3. 2014/04/30 23:03:09 - 2014-04-30