// ==UserScript== // @name Duolingo Vocab Search // @namespace http://tampermonkey.net/ // @version 1.0 // @description Search words from the language you are learning on Duolingo. // @author Nekosuki // @match https://www.duolingo.com/* // @grant GM_addStyle // @run-at document-end // @downloadURL none // ==/UserScript== GM_addStyle("#vocab-search{ position: fixed; top: 25%; left: 50%; z-index: 2000; width: 550px; margin: 0 0 0 -275px; background-color: #fff; border-radius: 10px; box-shadow: 0 2px 15px rgba(0, 0, 0, 0.3); transition: all 0.2s ease-in-out; opacity: 0 } #vocab-search input { width:100%; border: none; background: #fff; font: 500 24px/32px 'museo-sans-rounded', sans-serif; padding: 5px 10px; color: #4b4b4b; border-radius: 5px; box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.05); -webkit-font-smoothing: antialiased; text-align: left; line-height: 24px; padding: 12px 16px; } #vocab-search input::placeholder{ padding-top:3px; font-weight: 300}"); (function() { "use strict"; let modal, input, status; let searchInProgress = false; let drawModal = function() { modal = document.createElement("div"); modal.id = "vocab-search"; modal.innerHTML = ''; document.body.appendChild(modal); input = modal.querySelector("input"); status = modal.querySelector("span"); input.focus(); hideModal(); }; let showModal = function() { modal.style.opacity = "1"; modal.style.visibility = "visible"; setTimeout(function() { input.focus(); }, 50); }; let hideModal = function() { if(searchInProgress) return; modal.style.opacity = "0"; modal.style.visibility = "hidden"; setTimeout(function() { input.blur(); input.value = ""; status.textContent = ""; }, 250); }; let toggleModal = function() { if(modal.style.visibility == "visible") hideModal(); else showModal(); }; let getURLForWord = function(word, vocab) { let words = vocab.vocab_overview; let found = null; for(let i = 0; i < words.length; i++) { if(words[i].normalized_string.toLowerCase() == word.toLowerCase() || words[i].word_string.toLowerCase() == word.toLowerCase()) { found = words[i]; break; } } if(found !== null) { found = "https://www.duolingo.com/dictionary/" + vocab.language_string + "/" + found.normalized_string + "/" + found.lexeme_id; } return found; }; let requestVocab = function(word) { searchInProgress = true; status.textContent = "searching"; let xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if(this.readyState != 4 || this.status != 200) return; var obj = JSON.parse(this.responseText); let url = getURLForWord(word, obj); searchInProgress = false; if(url !== null) wordFound(url); else wordNotFound(); }; xhr.open("GET", "/vocabulary/overview", true); xhr.send(); }; let wordFound = function(url) { status.textContent = "redirecting"; setTimeout(function(){ window.location = url; }, 50); }; let wordNotFound = function() { status.textContent = "not found"; input.readOnly = false; }; let submitWord = function() { if(searchInProgress || document.activeElement != input) return; let word = input.value.trim(); if(!word || word.length === 0) return; input.readOnly = true; requestVocab(word); }; let init = function () { drawModal(); document.addEventListener("keypress", function(event) { switch(event.keyCode) { case 47: // forward slash toggleModal(); event.preventDefault(); break; case 27: // escape hideModal(); break; case 13: // enter submitWord(); } }); input.addEventListener("keydown", function(event) { if(!input.readOnly) status.textContent = ""; }); document.addEventListener("click", function(event) { if(!modal.contains(event.target)) hideModal(); }); }; init(); })();