Bunpro Toolbox

Adds various search options from Japanese resources

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

 // ==UserScript==
// @name         Bunpro Toolbox
// @version      1.6.0
// @description  Adds various search options from Japanese resources
// @author       Ambo100
// @match        *bunpro.jp/grammar_points/*
// @match        *bunpro.jp/learn*
// @grant        none
// @namespace    https://greasyfork.org/users/230700
// @changelog    Added button to open ChatGPT and copy a pre-made prompt to clipboard button (CAUTION: ChatGPT can be a helpful tool for language learning, but it's important to remember that its responses may contain inaccuracies or lack context.); Fixed Bunpro discussion links; Forced links to automatically reload when navigating through lessons using the page arrows;
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    var currentGrammarPoint;
    var grammarDiv;
    var toolboxDiv;
    var booksDivContainer;

    window.addEventListener('load', function() {
        initialize();
    }, false);

    function initialize() {
        if (!toolboxDiv) {
            addToolbox();
        }
        addLinks();
        fixBrokenLinks();
    }

    function addToolbox() {
        currentGrammarPoint = document.getElementsByClassName("bp-ddw undefined")[0].innerText;
        grammarDiv = document.getElementsByClassName("grid grid-cols-1 gap-16 md:grid-cols-6")[0];

        if (!grammarDiv) {
            console.error("Grammar Div not found.");
            return;
        }

        toolboxDiv = document.createElement("section");
        toolboxDiv.classList.add('rounded', 'border', 'border-rim', 'bg-secondary-bg', 'h-full', 'md:col-span-6', 'p-24', );
        grammarDiv.prepend(toolboxDiv);
    }
    
    function addLinks() {
        clearToolbox();

        //DELETE FOR PUBLIC UPLOAD//
        // AddPDFLink("AIAIJ","file:///C:/Users/ambo1/OneDrive/Documents2/Japanese/AIAIJ.pdf");
        // AddPDFLink("DAJG","file:///C:/Users/ambo1/OneDrive/Documents2/Japanese/DAJG.pdf");
        // AddPDFLink("DBJG","file:///C:/Users/ambo1/OneDrive/Documents2/Japanese/DBJG.pdf");
        // AddPDFLink("Genki I 2nd Edition","file:///C:/Users/ambo1/OneDrive/Documents2/Japanese/Genki/Genki%20-%20Elementary%20Japanese%20I.pdf", "zoom=120");
        // AddPDFLink("Genki II 2nd Edition","file:///C:/Users/ambo1/OneDrive/Documents2/Japanese/Genki/Genki%20-%20Elementary%20Japanese%20II.pdf", "zoom=120");

        // Dictionaries, References
        addLink("Jisho", "https://jisho.org/search/");
        addLink("Wikitionary", "https://en.wiktionary.org/wiki/","#Japanese");
        addLink("Eijirou", "https://eow.alc.co.jp/search?q=","");

        addDivider();

        //YouTube
        addLink("YouGlish", "https://youglish.com/pronounce/","/japanese?");
        addLink("YouTube", "https://www.youtube.com/results?search_query=","+Japanese");

        addDivider();

        //Q&A, Communities
        addLink("Stack Exchange", "https://japanese.stackexchange.com/search?q=");
        addLink("HiNative", "https://hinative.com/en-US/search/questions?language_id=45&q=");
        addLink("Reddit", "https://www.reddit.com/r/LearnJapanese/search?q=","&restrict_sr=on&sort=relevance&t=all");
        addLink("WK Forum", "https://community.wanikani.com/search?q=","%20category%3A17");
        addBunproLink("Discussion");
        
        addDivider();

        addAiPrompt("ChatGPT");

    }
    
    /**
     * @global
     * @description Creates and adds a link to the toolbox with common styling.
     * @param {string} linkName Link Name
     * @param {string} linkURL URL of the link
     */
    function addStyledLink(linkName, linkURL) {
        const link = document.createElement('a');

        link.textContent = linkName;
        link.href = linkURL;
        link.target = '_blank';
        link.style.margin = '5px';
        link.classList.add(
            'inline-flex',
            'items-center',
            'justify-center',
            'py-4',
            'px-12',
            'rounded',
            'text-center',
            'border',
            'transition-colors',
            'border-secondary-fg',
            'bg-transparent',
            'bg-secondary-fg',
            'text-secondary-fg'
        );

        toolboxDiv.appendChild(link);
    }

    /**
     * @global
     * @description Adds a link using the common styling for external resources.
     * @param {string} linkName Link Name
     * @param {string} urlPrefix URL Prefix
     * @param {string} [urlSuffix] Optional URL suffix
     */
    function addLink(linkName, urlPrefix = '', urlSuffix = '') {
        const linkURL = `${urlPrefix}${currentGrammarPoint}${urlSuffix}`;
        addStyledLink(linkName, linkURL);
    }

    function addAiPrompt(modalName) {
        const link = document.createElement('button');

        link.textContent = modalName;
        link.style.margin = '5px';
        link.classList.add(
            'inline-flex',
            'items-center',
            'justify-center',
            'py-4',
            'px-12',
            'rounded',
            'text-center',
            'border',
            'transition-colors',
            'border-secondary-fg',
            'bg-transparent',
            'bg-secondary-fg',
            'text-secondary-fg'
        );

        let modalTitle = "CAUTION: ChatGPT can be a helpful tool for language learning, but it's important to remember that its responses may contain inaccuracies or lack context. \n\n AI PROMPT ADDED TO CLIPBOARD: \n\n"

        let promptText = `Create a detailed grammar guide for the Japanese grammar '${currentGrammarPoint}'. ` +
          `### Usage: Explain how and when to use it, including specific contexts and nuances. ` +
          `### Structure: Describe grammatical structure.'. ` +
          `### Examples: Include only three example sentences to illustrate the usage in different sentence structures. ` +
          `Examples: Use the structure [JAPANESE] [NEW LINE] [ENGLISH].` +
          `Examples: Use emojis as bullet points that are relevant to the content they accompany, ensuring they match the tone and context of each section. ` +
          `Examples: DO NOT USE ROMAJI OR FURIGANA IN EXAMPLES.`;

        toolboxDiv.appendChild(link);

          link.addEventListener('click', async function() {
            try {
                await navigator.clipboard.writeText(promptText);
                alert(modalTitle + promptText);
                
                var win = window.open('https://chat.openai.com/', '_blank');
                if (win) {
                    win.focus();
                } else {
                    alert('Please allow popups for this website');
                }
            } catch (error) {
                console.error('Failed to copy to clipboard:', error);
            }
        });
    
    }

    /**
     * @global
     * @description Adds a Bunpro link using the common styling.
     * @param {string} linkName Link Name
     */
    function addBunproLink(linkName) {
        let bunproGrammarForumLink = document.querySelector('a[href^="https://community.bunpro.jp/t/grammar-discussion/"]').getAttribute('href');

        if (bunproGrammarForumLink) {
            addStyledLink(linkName, bunproGrammarForumLink);
        } else {
            console.error("Discussion anchor not found.");
        }
    }

    /**
     * @global
     * @description Adds a vertical divider between links with set spacing.
     * @param {number} [padding=10] Measured in pixels (px).
     * @param {string} [dividerSymbol='|'] The symbol used for the divider.
     * @example // Standard 10px divider
     * addDivider();
     * @example // 30px divider with the character "-"
     * addDivider(30, "-");
     */
    function addDivider(padding = 10, dividerSymbol = '') {
        const divider = document.createElement('span');
        divider.style.padding = `${padding}px`;
        divider.textContent = dividerSymbol;
        toolboxDiv.appendChild(divider);
    }
    
    /**
     * @global
     * @description Clears all default links and dividers from the toolbox.
     */
    function clearToolbox(){
        toolboxDiv.innerHTML = "";
    }

    /**
     * @global
     * @description This function has three parameters, a PDF title, local PDF URL and a parameter for optional PDF parameters. </br></br>
     * <b>Warning</b> </br> This feature is experimental, modern browsers will block links to local URLs for security purposes.
     * An browser extension, such as this <a href="https://chrome.google.com/webstore/detail/enable-local-file-links/nikfmfgobenbhmocjaaboihbeocackld?hl=en">extension</a> for Google Chrome can enable this feature.
     * @param {string} pdfTitle The name of the linK, this must match exactly with the names used on Bunpro (excludes non alphanumeric characters).
     * @param {string} pdfURL The local URL of your PDF file. Must be preceeded with 'file://'
     * @param {string} [pdfParameters] May be used for adding additional PDF parameters
     * @example //Adds a link to the PDF that matches 'Genki II 2nd Edition'.
     * AddPDFLink("Genki II 2nd Edition","file:///C:/Users/YOUR_USER/Documents/GenkiElementaryII.pdf");
     * @example //Adds a link to the PDF that matches 'DAJB'.
     * AddPDFLink("DAJG","file:///C:/Users/YOUR_USER/Documents/DAJG.pdf");
     * @example //Adds a link to the PDF that matches 'AIAIJ', includes optional query to set zoom level.
     * AddPDFLink("AIAIJ","file:///C:/Users/YOUR_USER/Documents/AIAIJ.pdf", "zoom=120");
     */
    function addPDFLink(pdfTitle = '', pdfURL = '', pdfParameters = ''){
        if (!booksDivContainer) {
            booksDivContainer = document.getElementsByClassName("grammar-point__container--resources-card-main-text-new");
        }
        var regexBookTitle = new RegExp('(.*' + pdfTitle + '.*)','i');
        var regexPageNumber = new RegExp('Page\\s(\\d+)','i');

        for (var i = 0; i < booksDivContainer.length; i++) {
            var result = regexBookTitle.exec(booksDivContainer[i].innerText);

            if (result != null) {
                var bookPageNumberDiv = booksDivContainer[i].parentElement.getElementsByTagName('div')[1].innerText;
                var bookPageNumber = regexPageNumber.exec(bookPageNumberDiv);

                //Replace book title with link
                var pageLink = document.createElement("a");
                pageLink.setAttribute('href', pdfURL + "#page=" + bookPageNumber[1] + "&" + pdfParameters);
                pageLink.innerText = booksDivContainer[i].innerText.trim();
                booksDivContainer[i].textContent = "";
                booksDivContainer[i].appendChild(pageLink);
                booksDivContainer[i].innerHTML += " ";

                //Add link icon
                var externalLink = document.createElement("i");
                externalLink.classList.add("fas", "fa-external-link-alt");
                booksDivContainer[i].appendChild(externalLink);
            }   
        }
    }

    function fixBrokenLinks() {
        var links = document.getElementsByTagName("a");
        var regex = /^(http?:\/\/)[^.]+\.(jgram|tanos|99bako)(.+)$/i;
        for (var i = 0, iMax = links.length; i < iMax; i++) {
            links[i].href = links[i].href.replace(regex, "https://web.archive.org/web/" + links[i].href);
        }
    }

    // Detect URL changes and reinitialize when the URL changes
    var currentUrl = window.location.href;
    setInterval(function() {
        if (window.location.href !== currentUrl) {
            currentUrl = window.location.href;
            initialize();
        }
    }, 1000);
}
)();