Apkawa / Kinopoisk subtitle petition

// ==UserScript==
// @name         Kinopoisk subtitle petition
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  Helper for subtitle petition
// @author       Apkawa
// @license      MIT
// @match        https://forms.yandex.ru/surveys/10022784.8ae29888f3224e212d4a904160b6baf0a05acd37/*
// @match        https://hd.kinopoisk.ru/*
// @match        https://kinopoisk.ru/*
// @match        https://www.kinopoisk.ru/*
// @icon         https://www.google.com/s2/favicons?domain=kinopoisk.ru
// @require      https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @grant        none
// @homepage     https://github.com/Apkawa/userscripts
// @homepageUrl  https://github.com/Apkawa/userscripts
// @supportUrl   https://github.com/Apkawa/userscripts/issues
// @downloadUrl  https://github.com/Apkawa/userscripts/raw/master/dist/hd.kinopoisk.ru/kp_subtitle_petition.user.js
// @updateUrl    https://github.com/Apkawa/userscripts/raw/master/dist/hd.kinopoisk.ru/kp_subtitle_petition.user.js
// ==/UserScript==
(function() {
    "use strict";
    function getElementByXpath(xpath, root = document) {
        const e = document.evaluate(xpath, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        return e && e;
    }
    function getElementsByXpath(xpath, root = document) {
        const iterator = document.evaluate(xpath, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
        const result = [];
        let el = iterator.iterateNext();
        while (el) {
            result.push(el);
            el = iterator.iterateNext();
        }
        return result;
    }
    function markElementHandled(wrapFn, attrName = "_handled") {
        return function(el) {
            if (el.getAttribute(attrName)) return;
            el.setAttribute(attrName, "1");
            wrapFn(el);
        };
    }
    function waitElement(match, callback, root = document.body) {
        const observer = new MutationObserver((mutations => {
            let matchFlag = false;
            mutations.forEach((mutation => {
                if (!mutation.addedNodes) return;
                for (let i = 0; i < mutation.addedNodes.length; i++) {
                    const node = mutation.addedNodes[i];
                    matchFlag = match(node);
                }
            }));
            if (matchFlag) {
                _stop();
                callback();
                _start();
            }
        }));
        let isStarted = false;
        function _start() {
            if (isStarted) return;
            observer.observe(root || document.body, {
                childList: true,
                subtree: true,
                attributes: false,
                characterData: false
            });
            isStarted = true;
        }
        function _stop() {
            observer.disconnect();
            isStarted = false;
        }
        _start();
        return () => {
            _stop();
        };
    }
    function E(tag, attributes = {}, ...children) {
        const element = document.createElement(tag);
        for (const [k, v] of Object.entries(attributes)) element.setAttribute(k, v);
        const fragment = document.createDocumentFragment();
        children.forEach((child => {
            if ("string" === typeof child) child = document.createTextNode(child);
            fragment.appendChild(child);
        }));
        element.appendChild(fragment);
        return element;
    }
    function mapLocation(map) {
        const s = document.location.hostname + document.location.pathname;
        for (const [k, v] of Object.entries(map)) if (RegExp(k).test(s)) v();
    }
    function parseSearch() {
        return Object.fromEntries(new URLSearchParams(window.location.search).entries());
    }
    Object.keys;
    Object.entries;
    Object.values;
    const FORM_URL = "https://forms.yandex.ru/surveys/10022784.8ae29888f3224e212d4a904160b6baf0a05acd37/";
    function radioByText(text) {
        const xpath = `//p[text() = '${text}']/../..`;
        const matchingElement = getElementByXpath(xpath);
        null === matchingElement || void 0 === matchingElement ? void 0 : matchingElement.click();
    }
    function selectByLabel(label, option) {
        const xpath = `//p[text() = '${label}']/ancestor::tbody//select`;
        const select = getElementByXpath(xpath);
        const selected = getElementByXpath(`//option[@selected]`, select);
        selected.removeAttribute("selected");
        const opt = getElementByXpath(`//option[text() = '${option}']`, select);
        select.value = null === opt || void 0 === opt ? void 0 : opt.value;
        opt.setAttribute("selected", "1");
        return select;
    }
    function fillInputByLabel(label, text) {
        const xpath = `//p[text() = '${label}']/ancestor::tbody//input`;
        const el = getElementByXpath(xpath);
        el.value = text;
        return el;
    }
    function getUserName() {
        const el = getElementByXpath(`//span[@class='user__name']`);
        return (null === el || void 0 === el ? void 0 : el.innerText) || "";
    }
    function normalizeKPLink(s) {
        return s.replace(/www\./, "");
    }
    function renderHdKinopoiskRequestLink(sub) {
        const filmContainer = getElementByXpath("ancestor::section", sub);
        if (!filmContainer) return;
        const subNames = [].slice.call(sub.getElementsByTagName("li")).map((el => el.innerText));
        const audio = getElementByXpath(`//div[text() = 'Аудиодорожки']/../ul`);
        const audioNames = [].slice.call(null === audio || void 0 === audio ? void 0 : audio.getElementsByTagName("li")).map((el => el.innerText));
        function getInfo() {
            const link = getElementByXpath(`//a[text() ='Подробнее на КиноПоиске']`, filmContainer).href.toString();
            const type = getElementByXpath(`//button[text() = 'О сериале']`) ? "series" : "film";
            return {
                link: normalizeKPLink(link),
                type: type
            };
        }
        if (!audioNames.includes("Английский")) {
            const info = Object.assign(Object.assign({}, getInfo()), {
                mode: "audio"
            });
            const formUrl = FORM_URL + "?" + new URLSearchParams(info).toString();
            const li = E("li", {}, E("a", {
                href: formUrl,
                target: "_blank"
            }, "Запросить оригинальную озвучку"));
            null === audio || void 0 === audio ? void 0 : audio.appendChild(li);
        }
        if (!subNames.includes("Русские")) {
            const info = getInfo();
            const formUrl = FORM_URL + "?" + new URLSearchParams(info).toString();
            const li = E("li", {}, E("a", {
                href: formUrl,
                target: "_blank"
            }, "Запросить русские сабы"));
            sub.appendChild(li);
        }
    }
    function renderKinopoiskRequestLink(sub) {
        var _a, _b;
        const filmContainer = getElementByXpath("ancestor::body", sub);
        if (!filmContainer) return;
        const country = null === (_a = getElementByXpath(`//div[text() = 'Страна']/following-sibling::div`, filmContainer)) || void 0 === _a ? void 0 : _a.innerText;
        const audio = getElementByXpath(`//div[text() = 'Аудиодорожки']/following-sibling::div`, filmContainer);
        const audioNames = null === audio || void 0 === audio ? void 0 : audio.innerText.split(", ");
        const subNames = sub.innerText.split(", ");
        const info = {
            link: normalizeKPLink(window.location.href),
            type: (null === (_b = /https:\/\/(?:www.|)kinopoisk.ru\/(series|film)\//.exec(window.location.href)) || void 0 === _b ? void 0 : _b[1]) || ""
        };
        if (!(null === audioNames || void 0 === audioNames ? void 0 : audioNames.includes("Английский")) && "Россия" !== country) {
            const formUrl = FORM_URL + "?" + new URLSearchParams(Object.assign(Object.assign({}, info), {
                mode: "audio"
            })).toString();
            const li = E("a", {
                href: formUrl,
                target: "_blank"
            }, "Запросить оригинальную озвучку");
            null === audio || void 0 === audio ? void 0 : audio.appendChild(li);
        }
        if (!subNames.includes("Русские")) {
            const formUrl = FORM_URL + "?" + new URLSearchParams(info).toString();
            const li = E("a", {
                href: formUrl,
                target: "_blank"
            }, "Запросить русские сабы");
            sub.appendChild(li);
        }
    }
    (function() {
        console.warn("DEPRECATED");
        mapLocation({
            "^forms.yandex.ru/": () => {
                const params = parseSearch();
                if ("film" === params.type) radioByText("Фильм"); else radioByText("Сериал");
                if ("audio" === params.mode) {
                    radioByText("Аудиодорожку/озвучку");
                    selectByLabel("Какую аудиодорожку добавить?", "Оригинальную");
                } else {
                    radioByText("Субтитры");
                    const el = fillInputByLabel("Выберите субтитры:", params.lang || "Русский");
                    const hiddenEl = getElementByXpath(`ancestor::table//input[@type='hidden']`, el);
                    null === hiddenEl || void 0 === hiddenEl ? void 0 : hiddenEl.setAttribute("value", "30010730");
                }
                fillInputByLabel("Ссылка на фильм/сериал на КиноПоиске", params.link || "");
                fillInputByLabel("Ваша почта", `${getUserName()}@yandex.ru`);
            },
            "^hd.kinopoisk.ru/": () => {
                const xpath = `//div[text() = 'Субтитры']/../ul`;
                waitElement((el => Boolean(getElementByXpath(xpath, el))), (() => {
                    const subtitles = getElementsByXpath(xpath);
                    for (const el of subtitles) markElementHandled(renderHdKinopoiskRequestLink)(el);
                }));
            },
            "(www.|)kinopoisk.ru/(series|film)": () => {
                const xpath = `//div[text() = 'Субтитры']/following-sibling::div`;
                waitElement((el => Boolean(getElementByXpath(xpath, el))), (() => {
                    const subtitles = getElementsByXpath(xpath);
                    for (const el of subtitles) markElementHandled(renderKinopoiskRequestLink)(el);
                }));
            }
        });
    })();
})();