// ==UserScript== // @name Duolingo Wide // @namespace http://tampermonkey.net/ // @version 1.8.5 // @description Make Duolingo wider and more minimalist, and add useful keyboard shortcuts for the main features of the site. // @author Nekosuki // @match https://www.duolingo.com/* // @grant GM_addStyle // @run-at document-start // @downloadURL none // ==/UserScript== GM_addStyle("div.a5SW0,div._2_lzu{display:none}div._3MT-S{width:100%}div.kHldG{width:20%}"); GM_addStyle("div._3BXBQ > div._17OUG.ZZ-xe{width:100%} div._3BXBQ > div.ouWo7.ZZ-xe{display:none}"); GM_addStyle(".__duowide_achievs > :last-child > ul {max-height:none} .__duowide_friends a+div {position:absolute}"); (function() { "use strict"; let addStrengthenButton = function() { let c = document.querySelector("div.mAsUf"); if(c === null || c.firstElementChild.dataset.test != "lingot-store-button") return; let a = document.querySelector("a[data-test='global-practice']"); a.className = "_3LN9C _3QG2_ _1vaUe _3IS_q _1XnsG _1vaUe _3IS_q"; a.innerHTML = "Practice"; let p = a.cloneNode(true); p.onclick = function() { a.click(); return false; }; c.replaceChild(p, c.firstChild); }; let addAchievementsInProfile = function() { let d = document.querySelector("div.a5SW0"); if(d === null || d.firstChild.textContent != "Achievements") return; let e = d.nextElementSibling; let c = document.querySelector("div._3MT-S").firstChild; d.className = "_2hEQd _1E3L7 __duowide_achievs"; e.className = "_2hEQd _1E3L7 __duowide_friends"; c.insertBefore(e, c.lastChild); c.insertBefore(d, c.lastChild); }; let keyEventListener = function(event) { if(['input', 'select', 'textarea'].indexOf(document.activeElement.tagName.toLowerCase()) !== -1) return; if(window.location.pathname === "/practice" || window.location.pathname.match(/^\/skill\/[^/]+\/[^/]+\//)) { switch(event.keyCode) { case 68: let discussSentence = document.querySelector("div.WiNlI > button:last-child"); if(discussSentence !== null) discussSentence.click(); return; case 81: let quitButton = document.querySelector("div.Mlxjr > a._38taa._2Zfkq.cCL9P"); if(quitButton !== null) quitButton.click(); return; } } let tag = null; switch(event.keyCode) { case 78: /* N */ newLearningSession(); return; case 72: /* H */ tag = "home-nav"; break; case 87: /* W */ tag = "vocab-nav"; break; case 68: /* D */ tag = "discussion-nav"; break; case 76: /* L */ tag = "labs-nav"; break; case 80: /* P */ tag = "global-practice"; break; case 89: /* Y */ tag = "user-profile"; break; case 83: /* S */ tag = "sound-settings"; break; } let elem = document.querySelector("a[data-test='" + tag + "']"); if(elem !== null) elem.click(); else if(window.location.pathname === "/practice") { let practiceButton = document.querySelector("button[data-test='secondary-button']"); let timedPracticeButton = document.querySelector("button[data-test='player-next']"); if(event.keyCode == 80) practiceButton.click(); else if(event.keyCode == 84) timedPracticeButton.click(); } }; let checkForSettings = function() { let m = document.querySelector("div._3MT-S"); if(m === null) return; let s = document.querySelector("div._2_lzu"); if(window.location.pathname.startsWith("/settings")) { m.style.width = "auto"; s.style.display = "block"; } else { m.style.width = "100%"; s.style.display = "none"; } }; let init = function() { document.addEventListener("keyup", keyEventListener); }; let check = function() { addAchievementsInProfile(); addStrengthenButton(); checkForSettings(); }; let newLearningSession = function() { if(window.location.pathname.startsWith("/skill/")) { let next = document.querySelector("a[data-test=begin-session-button]"); if(next !== null) next.click(); return; } let skills = Array.prototype.slice.call(document.querySelectorAll("a[data-test~=skill-tree-link]")); let toLearn = skills.filter(a => !a.dataset.test.includes("gold") && !a.dataset.test.includes("purple") && a.firstElementChild.childElementCount == 1); if(toLearn.length === 0) return; toLearn[0].click(); }; new MutationObserver(check).observe(document, {childList: true, subtree: true}); init(); })();