nhentai Navigation Improvements

Clone search page navigation on top for mobile and options for faster tag filtering in top bar

От 09.06.2024. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name        nhentai Navigation Improvements
// @description Clone search page navigation on top for mobile and options for faster tag filtering in top bar
// @namespace   xspeed.net
// @license     MIT
// @version     3
// @icon        https://nhentai.net/favicon.ico
// @match       *://nhentai.net/*
// @grant       none
// ==/UserScript==

"use strict";

function showTextPrompt(msg) {
    const text = document.createElement("p");
    text.innerText = msg;

    const input = document.createElement("input");
    input.type = "search";

    const link = document.createElement("i");
    link.className = "fa fa-paste";

    const button = document.createElement("button");
    button.className = "btn btn-primary btn-square";
    button.type = "submit";
    button.appendChild(link);

    const form = document.createElement("form");
    form.className = "search";
    form.role = "search";
    form.method = "dialog";
    form.appendChild(input);
    form.appendChild(button);

    const dialog = document.createElement("dialog");
    dialog.appendChild(text);
    dialog.appendChild(form);

    const style = getComputedStyle(document.body);
    dialog.style.backgroundColor = style.getPropertyValue("background-color");
    dialog.style.color = style.getPropertyValue("color");

    document.body.appendChild(dialog);
    dialog.showModal();

    return new Promise((resolve, reject) => form.addEventListener('submit', () => {
        resolve(input.value);
        dialog.remove();
    }, { once: true }));
}

function stopEvent(event) {
    event.stopImmediatePropagation();
    event.preventDefault();
}

async function onChangeTags(event, tags) {
    stopEvent(event);

    const input = document.querySelector("input[type=search]");
    const str = tags ?? await showTextPrompt("Specify tags, separate by commas");

    if (str) {
        str.split(',').map(x => x.trim()).filter(x => x).forEach(x => adjustSearch("tag", x, false));

        document.querySelector("button[type=submit]").click();
    }
}

function adjustSearch(type, item, add) {
    const input = document.querySelector("input[type=search]");
    item = wrap(item);
    const act = " " + (add ? item : `-${type}:${item}`);
    const inv = " " + (add ? `-${type}:${item}` : item);

    if (input.value.indexOf(inv) != -1) input.value = input.value.replace(inv, act);
    else if (input.value.indexOf(act) == -1) input.value += act;

    input.value = input.value.replaceAll('  ', ' ');
    sessionStorage.setItem("lastSearch", input.value);
}

function wrap(txt) {
    return txt.indexOf(" ") == -1 ? txt : `"${txt}"`;
}

function clearSearch(event) {
    stopEvent(event);

    document.querySelector("input[type=search]").value = "";
    sessionStorage.removeItem("lastSearch");
}

function setupBtn(elem) {
    if (!elem) return;

    let item = elem.firstChild.cloneNode(true);

    let link = item.firstChild;
    link.href = "#";
    link.innerText = "Block default";
    link.addEventListener("click", e => onChangeTags(e, "bbm,netorare,vore,scat,guro"));

    elem.insertBefore(item, elem.firstChild);

    item = elem.firstChild.cloneNode(true);

    link = item.firstChild;
    link.href = "#";
    link.innerText = "Block tags";
    link.addEventListener("click", onChangeTags);

    elem.insertBefore(item, elem.firstChild);

    item = elem.firstChild.cloneNode(true);

    link = item.firstChild;
    link.href = "#";
    link.innerText = "Clear search";
    link.addEventListener("click", e => clearSearch(e));

    elem.insertBefore(item, elem.firstChild);
}

function onTagClick(event, elem, add) {
    stopEvent(event);

    const data = elem.pathname.split("/").filter(x => x);
    adjustSearch(data[0], data[1].replaceAll("-", " "), add);
}

function setupTag(elem) {
    if (!elem.querySelector(".count")) return document.createTextNode("");

    const container = document.createElement("span");
    container.addEventListener("click", e => stopEvent(e));
    container.className = "name";

    const add = document.createElement("a");
    add.href = "#";
    add.addEventListener("click", e => onTagClick(e, elem, true));
    add.className = "fa fa-plus";
    add.style.padding = "0.25em 0em";
    add.style.marginRight = "0.4em";

    const del = document.createElement("a");
    del.href = "#";
    del.addEventListener("click", e => onTagClick(e, elem));
    del.className = "fa fa-minus";
    del.style.padding = "0.25em 0em";

    container.appendChild(add);
    container.appendChild(del);
    return container;
}

(function() {
    const content = document.getElementById("content");
    const pagination = document.querySelector("section.pagination");
    const input = document.querySelector("input[type=search]");

    if (content && pagination) {
        const clone = pagination.cloneNode(true);

        const spacer = clone.querySelector(".ios-mobile-webkit-bottom-spacing");
        if (spacer) spacer.remove();

        content.insertBefore(clone, content.firstChild);
    }

    if (!input.value) {
        input.value = sessionStorage.getItem("lastSearch") ?? "english ";
    }

    setupBtn(document.querySelector("ul.menu"));
    setupBtn(document.querySelector("ul.dropdown-menu"));

    document.querySelectorAll(".tags>.tag").forEach(x => x.appendChild(setupTag(x)));
})();