Greasy Fork

Duolingo Wide

Make Duolingo wider and more minimalist, and add useful keyboard shortcuts for the main features of the site.

目前为 2017-10-12 提交的版本。查看 最新版本

// ==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
// ==/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();
})();