BishBashBosh+

Extend BishBashBosh's great interface with more review types

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Advertisement:

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

Advertisement:

// ==UserScript==
// @name         BishBashBosh+
// @namespace    http://tampermonkey.net/
// @version      0.17
// @description  Extend BishBashBosh's great interface with more review types
// @author       Ian Mason
// @match        http://jptools.s3-website-us-east-1.amazonaws.com/bishbashbosh.html
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    var styleSheet = document.createElement("style");
    styleSheet.type = "text/css";
    styleSheet.innerText = "#answer2 {font-size: 3em;width: 15em;text-align: center;} #config { margin-left: 40px; width: 50px; } #oneanddonelabel { margin-left: 40px; }";
    document.head.appendChild(styleSheet);

    var element = document.getElementById('contentSelectorForm');
    element.id = "contentSelectorForm2";
    var whitespace = "    ";
    element.innerHTML = element.innerHTML.concat(whitespace);

    {
        var p = document.createElement("b");
        p.innerHTML = "1&Done";
        p.id = "oneanddonelabel";
        element.appendChild(p);

        var oneanddone = document.createElement("Input");
        oneanddone.setAttribute("type", "checkbox");
        oneanddone.id = "oneanddone";

        element.appendChild(oneanddone);
    }
    var answer = document.getElementById('answer');
    var answer_parent = answer.parentNode;
    answer.style.display = "none";
    var cln = answer.cloneNode(false);
    answer_parent.appendChild(cln);

    var config = document.createElement("input");
    config.placeholder = "ALL";
    var idAttribute = document.createAttribute("id");
    idAttribute.value = "config";
    config.setAttributeNode(idAttribute);
    element.appendChild(config);


    cln.id = "answer";
    answer.id = "answer2";

    function getInputElement(name) {
        var input = document.createElement("Input");
        var idAttribute = document.createAttribute("id");
        idAttribute.value = name;
        var typeAttribute = document.createAttribute("type");
        typeAttribute.value = "radio";
        var nameAttribute = document.createAttribute("name");
        nameAttribute.value = "content";
        var valueAttribute = document.createAttribute("value");
        valueAttribute.value = name;
        input.setAttributeNode(idAttribute);
        input.setAttributeNode(typeAttribute);
        input.setAttributeNode(nameAttribute);
        input.setAttributeNode(valueAttribute);
        return input;
    }
    function getLabelElement(name, visibleName) {
        var label = document.createElement("Label");
        label.innerHTML = visibleName;//"ALL Ancient Gurus and Apprentice";
        var forAttribute = document.createAttribute("for");
        forAttribute.value = name;
        var idAttribute = document.createAttribute("id");
        idAttribute.value = name;
        label.setAttributeNode(forAttribute);
        label.setAttributeNode(idAttribute);
        return label;
    }

    var input = getInputElement("allOldGuruApprentice");
    var label = getLabelElement("allOldGuruApprentice", "ALL ancient guru and apprentice");

    var input2 = getInputElement("randomBurned");
    var label2 = getLabelElement("randomBurned", "ALL random burned");
    element.appendChild(input);
    element.appendChild(label);
    element.appendChild(input2);
    element.appendChild(label2);

    var one_and_done_mode = false;
/*
    var input = document.createElement("Input");
    var idAttribute = document.createAttribute("id");
    idAttribute.value = "test";
    var typeAttribute = document.createAttribute("type");
    typeAttribute.value = "radio";
    var nameAttribute = document.createAttribute("name");
    nameAttribute.value = "content";
    var valueAttribute = document.createAttribute("value");
    valueAttribute.value = "test";

    input.setAttributeNode(idAttribute);
    input.setAttributeNode(typeAttribute);
    input.setAttributeNode(nameAttribute);
    input.setAttributeNode(valueAttribute);
    element.appendChild(input);

    var label = document.createElement("Label");
    label.innerHTML = "ALL Ancient Gurus and Apprentice";
    var forAttribute = document.createAttribute("for");
    forAttribute.value = "test";
    label.setAttributeNode(forAttribute);

    element.appendChild(label);
*/
    console.log('/// Testing');

    //function fetchStudyMaterials() {
    //    console.log("Make useless");
    //    return [];
    //}

    //function fetchSubjects() {
    //    console.log("FetchSubjects overridden");
    //    return [];
    //}

    function fetchAncientThings() {
        // 4 random gurus older than 10 weeks and not seen in in the past 2 days,
        // picked from the oldest 20
        const ageCutoff = new Date(Date.now() - 1000 * 60 * 60 * 24 * 7 * 10);
        const recentCorrectCutoff = new Date(Date.now() - 1000 * 60 * 60 * 24 * 2);
        return pullWholeCollection("assignments?srs_stages=1,2,3,4,5,6").then(list => {
            const output = _(list)
            .sortBy(["data.created_at"])
            .filter(x => new Date(x.data_updated_at) < recentCorrectCutoff)
            .filter(x => ageCutoff > new Date(x.data.started_at))
            .shuffle()
            .take(isNaN(config.value) || config.value == "" ? 99999 : config.value)
            .value();
            console.log(output);
            return output;
        });
    }

    function fetchRandomBurned() {
        return pullWholeCollection("assignments?burned=true").then(list => {
            const output = _(list)
            .shuffle()
            .take(isNaN(config.value) || config.value == "" ? 99999 : config.value).value();
            console.log(output);
            return output;
        });
    }

    function play2(assignments, subjects) {
        const answerField_orig = document.querySelector("#answer");
        answerField_orig.style.display = "none";
        const answerField = document.querySelector("#answer2");
        const questionDiv = document.querySelector("#question");
        const whatToPut = document.querySelector("#whattoput");
        const solution = document.querySelector("#solution");
        const queue = document.querySelector("#queue");
        const result = document.querySelector("#result");
        const tpl = _.template(
            '<span class="label"><%= label %></span><br>' +
            '<span class="kind" style="border-color:<%= colours[object] %>"><%= object %></span>'
        );
        const solTpl = _.template('<h2><%= title %></h2><%= sub.length > 1 ? "<h4>" + sub.join(", ") + "</h4>" : "" %><p><%= body %></p>');

        const PH_MEANING = 1;
        const PH_READING = 2;

        const now = new Date();
        const deck2 = _(assignments).map(a => ({
            score: startingScore,
            perfect: true,
            type: a.data.subject_type,
            subject: subjects[a.data.subject_id],
            availNow: new Date(a.data.available_at) <= now
        })).shuffle().orderBy(["availNow"],["desc"]).value();
        const hand2 = deck2.splice(0, Math.min(initialHandSize, deck2.length));
        console.log(hand2);
        const pile2 = [];
        subjects = null; // free some ram

        let phase = PH_MEANING;
        let hadError = false;
        let curAudio = null;

        function check2() {
            if (phase === PH_READING && !wanakana.isKana(answerField.value)) {
                // catch unfinished n's etc
                answerField.value = wanakana.toKana(answerField.value);
            }
            const card2 = hand2[0], subj2 = card2.subject;
            console.log(subj2);
            const answer2 = _.trim(answerField.value).toLowerCase();
            console.log(answer2);

            if (answer2 === "") { return; }
            if (TESTMODE && answer2==="1" || answer2==="2") {
                return answer2 === "1" ? success2() : fail2();
            }
            if (phase === PH_MEANING) {
                const found = _.find(subj2.accepted_meanings,
                                     m => levenshtein(m, answer2) <= Math.floor(1 + m.length/5));
                return found ? success2() : fail2();
            } else {
                if (subj2.accepted_readings.includes(answer2)) {
                    return success2();
                } else if (subj2.bad_readings.includes(answer2)) {
                    return notQuite2();
                } else {
                    const rom = wanakana.toRomaji(answer2);
                    const foundClose = _.find(subj2.accepted_readings,
                                              m => levenshtein(wanakana.toRomaji(m), rom) <= (m.length < 2 ? 0 : 1));
                    if (foundClose) {
                        return notQuite2();
                    }
                }
                return fail2();
            }
        };
        function maybeAddFronDeck2() {
            const lowestHandScore = _(hand2).map("score").min();
            if (lowestHandScore >= increaseHandThreshold && hand2.length < maxHandSize && deck2.length) {
                hand2.unshift(deck2.pop());
            }
        }
        function animateResult2(isCorrect, score) {
            result.innerHTML = (isCorrect ? goodResults : badResults)[score];
            result.classList.remove("animate");
            void result.offsetWidth; // interesting hack
            result.classList.add("animate");
            result.classList.remove("right");
            result.classList.remove("wrong");
            result.classList.add(isCorrect ? "right" : "wrong");
        }
        function success2() {
            const card = hand2[0], subj = card.subject;
            animateResult2(true, card.score);
            if (subj.object === "radical" || phase === PH_READING) {
                if (curAudio) { curAudio.play(); }
                if (hadError) {
                    hand2.splice(1, 0, hand2.shift());
                } else {
                    card.score += card.perfect ? one_and_done_mode ? discardScore : 2 : 1;
                    const enoughStillInPlay = deck2.length > 0 || hand2.length > initialHandSize;
                    const allDone = _(hand2).map("score").min() >= discardScore;
                    if (card.score >= discardScore) {
                        if (!enoughStillInPlay) {
                            if (_(hand2).map("score").min() >= discardScore) {
                                // discard all
                                while (hand2.length) {
                                    pile2.push(hand2.shift());
                                }
                            } else {
                                // rotate to back
                                hand2.push(hand2.shift());
                            }
                        } else {
                            // discard
                            pile2.push(hand2.shift());
                        }
                    } else {
                        // move back in hand
                        hand2.splice(Math.min(hand2.length-1, card.score*3), 0, hand2.shift());
                    }
                    maybeAddFronDeck2();
                }
                hadError = false;
                phase = PH_MEANING;
            } else {
                phase = PH_READING;
            }
            display2();
        }
        function notQuite2() {
            const card = hand2[0];
            card.perfect = false;
            solution.innerHTML = "<h2>Not quite...</h2>";
            solution.style.display = "block";
            answerField.value = "";
        }
        function fail2() {
            const card2 = hand2[0], subj2 = card2.subject;
            animateResult2(false, card2.score);
            if (phase === PH_MEANING) {
                solution.innerHTML = solTpl({title: subj2.primary_meanings, sub: subj2.accepted_meanings, body: subj2.meaning_mnemonic});
            } else {
                if (curAudio) { curAudio.play(); };
                solution.innerHTML = solTpl({title: subj2.primary_readings, sub: subj2.accepted_readings, body: subj2.reading_mnemonic});
            }
            if (!hadError) {
                card2.score = Math.max(0, card2.score - 2);
                card2.perfect = false;
            }
            solution.style.display = "block";
            answerField.value = "";
            answerField.placeholder = "Nope :(    Try again...";
            hadError = true;
        }
        function display2() {
            const card = hand2[0];
            if (!card) {
                answerField.style.display = "none";
                whatToPut.style.display = "none";
                questionDiv.innerHTML = tpl({label:"BOSH!",object:""});
                document.querySelector("#info").style.display = "block";
                redrawQueue2();
                return;
            }
            questionDiv.innerHTML = tpl(card.subject);
            switchInputMode2(phase === PH_READING);
            whatToPut.innerHTML = phase === PH_READING ? "読み方" : "MEANING";
            answerField.value = "";
            answerField.placeholder = "";
            answerField.style.display = "inline-block";
            whatToPut.style.display = "block";
            solution.style.display = "none";
            curAudio = _.get(card, "subject.audio.length", 0) ? new Audio(_.sample(card.subject.audio)) : null;
            redrawQueue2();
            answerField.focus();
        }
        function redrawQueue2() {
            const style = score => {
                const offset = Math.round(score / discardScore * 55);
                return 'style="background-color: rgb(' + (255-offset) + ',' + (200+offset) + ',200);"';
            };
            queue.innerHTML = '<strong style="color:#2c2;">' + pile2.length + "</strong> + " +
                _.map(hand2, c => "<span " + style(c.score) + ">" + c.subject.label + "</span>").join("") +
                ' + <strong style="color:#f22;">' + deck2.length + "</strong>";
        }

        function switchInputMode2(isKana) {
            try {
                wanakana.unbind(answerField);
            } catch (err) {
                // fails if not bound already and I'm lazy
            }
            if (isKana) {
                wanakana.bind(answerField, {
                    IMEMode: true
                });
            }
        }

        answerField.addEventListener("keydown", ev => {
            if (ev.keyCode !== 13) { return; }
            check2();
        });

        display2();
    }

    document.querySelector("#contentSelectorForm2").addEventListener("change", ev => {
        if(ev.target.id == "oneanddone")
        {
            one_and_done_mode = !one_and_done_mode;
            return;
        }
        if(ev.target.id == "config")
        {
            if(ev.target.value == "")
            {
                label.innerHTML = "ALL ancient guru and apprentice";
                label2.innerHTML = "ALL random burned";
            }
            else
            {
                label.innerHTML = ev.target.value.toString() + " ancient guru and apprentice";
                label2.innerHTML = ev.target.value.toString() + " random burned";
            }
            return;
        }
        const content = usableFormData("contentSelectorForm2").content;
        storeLocal("content", content);
        document.querySelector("#loading").style.display = "block";
        document.querySelector("#main").style.display = "none";

        isLoading = true;

        const selectedContent = () => {
            switch (content) {
                case "apprentice1":
                    var lvl = queryStr().srsLevelOverride || "1";
                    return pullWholeCollection("assignments?srs_stages=" + lvl);
                case "recentlyFailed":
                    return fetchRecentlyFailed();
                case "oldestApprentices":
                    return fetchTenOldestApprentices();
                case "allThatStuff":
                    return fetchAllThatStuff();
                case "plusGurus":
                    return fetchAllThatStuffPlusGurus();
                case "allOldGuruApprentice":
                    return fetchAncientThings();
                case "randomBurned":
                    return fetchRandomBurned();
            }
        }

        Promise.all([selectedContent(), fetchSubjects(), fetchStudyMaterials()]).then(
            ([assignments, subjects, sm]) => {
                console.log(assignments);
                console.log(subjects);
                console.log(sm);
                subjects.forEach(s => {
                    const extra = sm[s.id];
                    if (extra) {
                        s.accepted_meanings = s.accepted_meanings.concat(extra.accepted_meanings);
                        s.meaning_mnemonic = extra.meaning_mnemonic + s.meaning_mnemonic;
                        s.reading_mnemonic = extra.reading_mnemonic + s.reading_mnemonic;
                    }
                });

                document.querySelector("#loading").style.display = "none";
                isLoading = false;

                if (!assignments || assignments.length < 1) {
                    window.alert("Nothing found to cram!");
                    document.querySelector("#info").style.display = "block";
                    return;
                }

                document.querySelector("#main").style.display = "block";
                console.log("PLAY");
                play2(assignments, subjects);
            }
        );
    });

})();