nhentai Navigation Improvements

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

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например 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     4
// @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 ";
    }
    else if (input.value.trim() != "english") {
        sessionStorage.setItem("lastSearch", input.value);
    }

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

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