Greasy Fork

Licensed (in English)

Show if manga is licensed in English.

目前为 2023-11-26 提交的版本。查看 最新版本

// ==UserScript==
// @name         Licensed (in English)
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Show if manga is licensed in English.
// @author       Santeri Hetekivi
// @match        https://mangadex.org/title/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=mangadex.org
// @grant        GM.xmlHttpRequest
// ==/UserScript==

(function() {
    'use strict';
    // ID for result element.
    const ID_RESULT = "licensedInEnglishResult"

    // Colors for different results.
    const COLOR_ERROR = "yellow"
    const COLOR_LICENSED = "red"
    const COLOR_UNLICENSED = "green"

    // Milliseconds to sleep multiplied by try counter.
    const MS_SLEEP = 1000

    // Start for Manga Updates series url.
    const URL_START_MANGAUPDATES = "https://www.mangaupdates.com/series.html?id="

    // Querys for elements.
    // Parent to store result element in.
    const QUERY_PARENT = ".title"
    // Query for getting link for Manga Updates.
    const QUERY_LINK_MANGAUPDATES = "a[href^='"+URL_START_MANGAUPDATES+"']"
    // Query for getting elements that can contain TEXT_LICENSED_IN_ENGLISH text.
    const QUERY_MANGAUPDATES_LICENSED_IN_ENGLISH = ".sCat b"

    // Texts.
    const TEXT_LICENSED_IN_ENGLISH = "Licensed (in English)"
    const TEXT_LICENSED_POSITIVE = "Yes"
    const TEXT_LICENSED_NEGATIVE = "No"

    // Update result element.
    const updateLicensedInEnglishText = (_elementResult, _text, _color = "yellow") => {
        _elementResult.style.color = _color
        _elementResult.textContent = _text;
    }

    // Select node list with given _query.
    const selectNodeList = (_query, _document = document) => {
        const nodeList = _document.querySelectorAll(_query);
        if(!(nodeList instanceof NodeList))
        {
            throw "nodeList for query '"+_query+"' was not instance of NodeList!"
        }
        return nodeList
    }

    // Select single element.
    const selectElement = (_query) => {
        // Get node list.
        const nodeList = selectNodeList(_query);

        // Check that got only single result.
        const lengthNodeList = nodeList.length
        if(lengthNodeList !== 1)
        {
            throw "Found "+lengthNodeList+" nodes for '"+_query+"'!"
        }

        // Check that single element is instance of Element.
        const element = nodeList[0]
        if(!(element instanceof Element))
        {
            throw "Element for query "+_query+" was not instance of Element!"
        }

        // Return gotten element.
        return element
    }

    // Get element for result.
    const getElementResult = () => {
        return document.getElementById(ID_RESULT)
    }

    // Output given _error to console.
    const consoleError = (_error) => {
        console.error("Licensed (in English)", _error)
    }

    // Handle given _error.
    const handleError = (_error) => {
        // Output to console.
        consoleError(_error)

        // Get element for result.
        const elementResult = getElementResult()

        // If element result found.
        if(elementResult instanceof Element)
        {
            // Update error to result element.
            updateLicensedInEnglishText(
                elementResult,
                _error,
                COLOR_ERROR
            )
        }
    }

    // Check that given _element is instance of Element.
    const checkIsElement = (_element, _name) => {
        if(!(_element instanceof Element))
        {
            throw _name+" was not instance of Element!"
        }
        return _element
    }

    // Get element with TEXT_LICENSED_IN_ENGLISH text.
    const getElementLicensedInEnglish = (_document) => {
        // Loop node list.
        const nodeList = selectNodeList(QUERY_MANGAUPDATES_LICENSED_IN_ENGLISH, _document)
        let element = null
        for (let i = 0; i < nodeList.length; i++)
        {
            // Get element.
            element = checkIsElement(nodeList[i], "nodeList["+i+"]")
            // If text matches
            if (element.textContent == TEXT_LICENSED_IN_ENGLISH)
            {
                // return element.
                return element
            }
        }

        // No element found.
        throw "No element with text '"+TEXT_LICENSED_IN_ENGLISH+"' found!"
    }

    // Get text answer for lisenced in english.
    const getTextLisencedInEnglish = (_document) => {
        // Get text.
        const text = checkIsElement(
            checkIsElement(
                getElementLicensedInEnglish(_document).parentElement ?? null,
                "getElementLicensedInEnglish.parentElement"
            ).nextElementSibling ?? null,
            "getElementLicensedInEnglish.parentElement.nextElementSibling"
        ).textContent.trim()

        // Check gotten text.
        if(
            text !== TEXT_LICENSED_POSITIVE
            &&
            text !== TEXT_LICENSED_NEGATIVE
        )
        {
            throw "Invalid text: "+text
        }

        // Return text.
        return text
    }

    // Handle response.
    const handleResponse = (_response) => {
        // Get answer for lisenced in english.
        const lisencedInEnglish = getTextLisencedInEnglish(
            (
                new DOMParser()
            ).parseFromString(
                _response.responseText,
                'text/html'
            )
        )

        // Update result element.
        updateLicensedInEnglishText(
            checkIsElement(
                getElementResult(),
                "getElementResult"
            ),
            lisencedInEnglish,
            (
                (lisencedInEnglish === TEXT_LICENSED_NEGATIVE) ?
                    COLOR_UNLICENSED :
                    (
                        (lisencedInEnglish === TEXT_LICENSED_POSITIVE) ?
                            COLOR_LICENSED :
                            COLOR_ERROR
                    )
            )
        )
    }

    // Get url for Manga Updates.
    const getURLMangaUpdates = () => {
        // Init urls array
        const urls = []

        // Loop node list.
        const nodeList = selectNodeList(QUERY_LINK_MANGAUPDATES)
        for (let i = 0; i < nodeList.length; i++)
        {
            // Get href
            let href = checkIsElement(nodeList[i], "nodeList["+i+"]").href ?? null
            // If got href as string
            if (typeof href === "string")
            {
                // Trim href.
                href = href.trim()
                // If href not already in urls
                if(!urls.includes(href))
                {
                    // add href to urls.
                    urls.push(href)
                }
            }
        }

        // Check that has only one url.
        const lengthUrls = urls.length
        if(lengthUrls !== 1)
        {
            throw "Found "+lengthUrls+" Manga Updates urls: "+urls.join(",")
        }

        // Return url.
        return urls[0]
    }

    // Run logic.
    const run = (_counter = 0) => {
        // Get parent elements.
        const parentElementsLength = selectNodeList(QUERY_PARENT).length

        // If
        if(
            // no parent values found
            parentElementsLength === 0
            &&
            // and call counter is under 10.
            _counter < 10
        )
        {
            // Increment counter.
            _counter++

            // Call after timeout.
            setTimeout(
                () => {
                    try
                    {
                        run(_counter)
                    }
                    catch(_error)
                    {
                        handleError(_error)
                    }
                },
                MS_SLEEP*_counter
            );

            // Return.
            return
        }
        // Throw error if found other than 0 or 1 elements.
        else if(parentElementsLength !== 1)
        {
            throw "Found "+parentElementsLength+" nodes for '"+QUERY_PARENT+"'!"
        }

        // Get result element.
        let elementResult = getElementResult(ID_RESULT)

        // If no result element found.
        if(!(elementResult instanceof Element))
        {
            // Create result element.
            elementResult = document.createElement("p")
            elementResult.id = ID_RESULT
            selectElement(QUERY_PARENT).appendChild(elementResult);
        }

        // Update result to unknown.
        updateLicensedInEnglishText(
            elementResult,
            "Unknown",
            COLOR_ERROR
        )

        // Get Manga Updates page.
        GM.xmlHttpRequest(
            {
                method: "GET",
                url: getURLMangaUpdates(),
                headers: {
                    "Accept": "text/html"
                },
                onload: function(_response) {
                    // Handle response.
                    try
                    {
                        handleResponse(_response)
                    }
                    catch(_error)
                    {
                        handleError(_error)
                    }
                },
                onerror: (_response) => {
                    consoleError(_response)
                    handleError(_response.error ?? "Unknown error!")
                },
                ontimeout: (_response) => {
                    consoleError(_response)
                    handleError(_response.error ?? "Unknown timeout!")
                },
                onabort: (_response) => {
                    consoleError(_response)
                    handleError(_response.error ?? "Unknown timeout!")
                }
            }
        );
    }

    // Try to start running.
    try
    {
        // Make sure that document is instance of HTMLDocument.
        if(!(document instanceof HTMLDocument))
        {
            throw "document was not instance of HTMLDocument!"
        }
        run()
    }
    catch(_error)
    {
        handleError(_error)
    }
})();