Фильтры для ficbook.net

Позоляет убрать раздражающих авторов и прозведения. Фильтровать мелочь и фики с низком количеством лайков. Всё настраивается.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        Фильтры для ficbook.net
// @namespace   ficbook.net.uo1.net
// @description Позоляет убрать раздражающих авторов и прозведения. Фильтровать мелочь и фики с низком количеством лайков. Всё настраивается.
// @include     /^https?:\/\/ficbook.net(/.*|)$/
// @version     1.21
// @grant       none
// @require     https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @license     MIT
// ==/UserScript==

var personalInfoValues = null;
var banPrefix = "Убрал пейсателей: ";
var banDelim = ", фики: ";
var banRegex = /Убрал пейсателей: ((?:,?\d+)*)?, фики: ((?:,?\d+)*)?/;

var previouslySerialized = null;

function storeBansInProfile(authors, fics) {
    if (personalInfoValues === null) {
        console.error("Can't store bans in profile, it's not loaded");
        return;
    }

    if (!authors) {
        authors = localStorage.getItem('_userscript_banned_authors');
    }
    if (!fics) {
        fics = localStorage.getItem('_userscript_banned_fics');
    }

    if (typeof authors === 'object') {
        authors = authors.join(',');
    }

    if (typeof fics === 'object') {
        fics = fics.join(',');
    }

    var serialized = banPrefix + authors + banDelim + fics;

    if (previouslySerialized === serialized) {
        previouslySerialized = serialized;
        return;
    }

    if (banRegex.test(personalInfoValues.about_myself)) {
        personalInfoValues.about_myself = personalInfoValues.about_myself.replace(banRegex, serialized);
    }
    else {
        personalInfoValues.about_myself += serialized;
    }

    $.post('https://ficbook.net/home/personal_info', personalInfoValues);
}

function applyFicBookFiltering() {
    function eraseCookie(name) {
        var expires = '';

        var date = new Date();
        date.setTime(date.getTime() + (10 * 365 * 24 * 60 * 60 * 1000));
        expires = '; expires=' + date.toGMTString();

        document.cookie = name + '=' + expires + '; path=/';
    }

    function writeStorage(name, value, days) {

        if (typeof window.localStorage !== 'undefined') {
            localStorage.setItem(name, value);

            eraseCookie(name);

            storeBansInProfile();

            return;
        }

        var expires;

        if (days) {
            var date = new Date();
            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
            expires = '; expires=' + date.toGMTString();
        } else {
            expires = '';
        }
        document.cookie = name + '=' + value + expires + '; path=/';
    }

    function createStorageWriteCode(name, value, days) {
        var expires;

        if (days) {
            var date = new Date();
            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
            expires = '; expires=' + date.toGMTString();
        } else {
            expires = '';
        }

        var code;

        code = "{\n"
            + "\tk = " + JSON.stringify(name) + ";\n"
            + "\tv = " + JSON.stringify(value) + ";\n"
            + "\tif(typeof window.localStorage !== 'undefined') {\n"
            + "\t\tlocalStorage.setItem(k, v);\n"
            + "\t} else {\n"
            + "\t\tdocument.cookie = k + '=' + v + '" + expires + "; path=/';\n"
            + "\t}\n"
            + "}\n";

        return code;
    }

    function readStorage(name) {

        if (typeof window.localStorage !== 'undefined') {
            var value = localStorage.getItem(name);

            if (value !== null) {
                return value;
            }
        }

        var nameEQ = name + '=';
        var ca = document.cookie.split(';');
        for (var value = 0; value < ca.length; value++) {
            var c = ca[value];
            while (c.charAt(0) === ' ')
                c = c.substring(1, c.length);
            if (c.indexOf(nameEQ) === 0)
                return c.substring(nameEQ.length, c.length);
        }
        return null;
    }

    var cookiePrefix = "_userscript_";

    var bannedAuthorsCookieName = cookiePrefix + "banned_authors";
    //eraseCookie(bannedAuthorsCookieName);
    var bannedAuthors = {};
    var bannedAuthorsCookie = readStorage(bannedAuthorsCookieName);

    if (bannedAuthorsCookie !== null) {
        bannedAuthorsCookie.split(",").forEach(function (id) {
            bannedAuthors[id] = true;
        });
    }

    console.log("Banned authors:", bannedAuthors);

    var bannedFicsCookieName = cookiePrefix + "banned_fics";
    //eraseCookie(bannedFicsCookieName);
    var bannedFics = {};
    var bannedFicsCookie = readStorage(bannedFicsCookieName);

    if (bannedFicsCookie !== null) {
        bannedFicsCookie.split(",").forEach(function (id) {
            bannedFics[id] = true;
        });
    }

    console.log("Banned fics:", bannedFics);

    function banAuthor(id) {
        if (bannedAuthors[id]) {
            return;
        }

        bannedAuthors[id] = true;
        if (bannedAuthorsCookie === null) {
            bannedAuthorsCookie = id;
        } else {
            bannedAuthorsCookie += "," + id;
        }

        writeStorage(bannedAuthorsCookieName, bannedAuthorsCookie, 365 * 20);
    }

    function unbanAuthor(id) {
        if (!bannedAuthors[id]) {
            return;
        }

        delete bannedAuthors[id];
        if (bannedAuthorsCookie !== null) {
            bannedAuthorsCookie = Object.keys(bannedAuthors).join(',');
        }

        writeStorage(bannedAuthorsCookieName, bannedAuthorsCookie, 365 * 20);
    }

    function banFic(id) {
        if (bannedFics[id]) {
            return;
        }

        bannedFics[id] = true;
        if (bannedFicsCookie === null) {
            bannedFicsCookie = id;
        } else {
            bannedFicsCookie += "," + id;
        }

        writeStorage(bannedFicsCookieName, bannedFicsCookie, 365 * 20);
    }

    function exportCookieData() {
        return createStorageWriteCode(bannedAuthorsCookieName, bannedAuthorsCookie, 365 * 20) + "\n" + createStorageWriteCode(bannedFicsCookieName, bannedFicsCookie, 365 * 20) + "\n";
    }

    var keepVisible = false;

    if (location.pathname.startsWith("/authors/") || "/home/collections" === location.pathname) {
        keepVisible = true;
    }

    function findBlockRoot(element) {
        while (element !== null && element !== document) {
            if (element.classList.contains('fanfic-inline')) {
                if (element.parentNode === null) {
                    console.log("Orphan node found:", element);
                    return element;
                }

                if (element.parentNode.tagName === 'DIV' && element.parentNode.classList.contains('top-item-row')) {
                    return element.parentNode;
                }

                if (element.parentNode.tagName === 'SECTION')
                {
                    if (element.parentNode.parentNode.tagName === 'TD') {
                        //console.log("FOUND: ", element, "P:", element.parentNode, "PP:", element.parentNode.parentNode); return null;
                        return element.parentNode.parentNode.parentNode;
                    }

                    if (element.parentNode.querySelectorAll(".fanfic-inline").length > 1) {
                        return element;
                    }

                    return element.parentNode;
                }

                if (element.parentNode.tagName === "TD") {
                    return element.parentNode.parentNode;
                }

                return element;
            }

            element = element.parentNode;
        }

        return null;
    }

    var deleted = 0;

    function deleteBanned(element) {
        if (keepVisible) {
            return;
        }

        console.log("Removing HTML section for", element);
        var rootElement = findBlockRoot(element);

        if (rootElement === null) {
            console.log("Can't delete section for ", element);
            return false;
        }

        console.log("Root:", rootElement);
        $(rootElement).remove();
        console.log("Block Removed:", rootElement);

        deleted++;

        return true;
    }

    var nextId = 0;

    function isFilteredPage() {
        return location.pathname !== "/home/favourites" && location.pathname !== "/home/collections" && location.pathname !== "/home/liked_fanfics" && !/^\/collections\/\d+$/.test(location.pathname);
    }

    function htmlApplyBans(addButtons) {
        //var links = document.links;
        var links = document.querySelectorAll("A[HREF]");

        for (var i = 0; i < links.length; i++) {
            var link = links[i];
            var href = link.getAttribute("href");
            if (/^\/authors\/\d+$/.test(href)) {
                if (link.innerText === "Мой профиль") {
                    continue;
                }

                var authorId = href.substring("/authors/".length);

                if (bannedAuthors[authorId]) {
                    console.log("Banned:", authorId);
                    if (isFilteredPage()) {
                        if (deleteBanned(link, authorId)) {
                            continue;
                        }
                    }
                }

                if (addButtons) {
                    if (link.children.length > 0 && link.children.item(0).nodeType === Node.ELEMENT_NODE && link.children.item(0).tagName === "IMG") {
                        continue;
                    }

                    if (link.buttons) {
                        link.buttons.remove();
                    }

                    if (bannedAuthors[authorId]) {
                        link.buttons = $("<span> </span><small>(в топке)</small>").insertAfter($(link)).click({ author: authorId, link: link }, function (event) {
                            if (confirm("Точно вернуть?")) {
                                unbanAuthor(event.data.author);
                                location.reload();
                            }
                            event.preventDefault();
                            return false;
                        }
                        );
                    }
                    else {
                        link.buttons = $("<span> </span><small>(<a href=\"#\">в топку</a>)</small>")
                            .click({ author: authorId, link: link }, function (event) {
                                if (confirm("Точно?")) {
                                    banAuthor(event.data.author);
                                    //deleteBanned(ev.data.link);
                                    htmlApplyBans(false);
                                }
                                event.preventDefault();
                                return false;
                            })
                            .insertAfter($(link));
                    }

                    //console.log("Author ban link added: " + authorId);
                }
            }
            else if (/^\/readfic\/\d+$/.test(href)) {
                var ficId = href.substring("/readfic/".length);

                if (bannedFics[ficId]) {
                    if (isFilteredPage()) {
                        console.log("Banned fic:", ficId);
                        deleteBanned(link);
                        continue;
                    }
                }

                if (addButtons) {

                    var id = "_usid_" + ++nextId;

                    if (link.buttons) {
                        link.buttons.remove();
                    }

                    link.buttons = $("<span> </span><small>(<span id='" + id + "'></span>)</small>").insertAfter($(link));

                    $("<a href=\"#\">в топку</a>")
                        .click({ fic: ficId, link: link }, function (ev) {
                            if (confirm("Точно?")) {
                                banFic(ev.data.fic);
                                //deleteBanned(ev.data.link);
                                htmlApplyBans(false);
                            }
                            ev.preventDefault();
                            return false;
                        })
                        .appendTo($("#" + id));

                    //                    $("<span> </span>").appendTo($("#" + id));
                    //                    $("<a href=\"#\">задвинуть</a>")
                    //                    .click({fic: ficId, link: link}, function (ev) {
                    //                        deleteBanned(ev.data.link);
                    //                        ev.preventDefault();
                    //                    })
                    //                    .appendTo($("#" + id));

                    console.log("Fic ban link added:", link);
                }

            }
        }

    }
    htmlApplyBans(true);

    var searchMinPages = document.querySelector("input[name='pages_min']");
    var searchMinLikes = document.querySelector("input[name='likes_min']");

    var minSizeCookieName = cookiePrefix + "filterMinSize";
    var minLikesCookieName = cookiePrefix + "filterMinLikes";

    if (searchMinPages !== null && searchMinPages.value !== '') {
        writeStorage(minSizeCookieName, searchMinPages.value);
    }
    if (searchMinLikes !== null && searchMinLikes.value !== '') {
        writeStorage(minLikesCookieName, searchMinLikes.value);
    }

    var minSize = parseInt(readStorage(minSizeCookieName));
    if (isNaN(minSize)) {
        minSize = 0;
    }

    var minLikes = parseInt(readStorage(minLikesCookieName));
    if (isNaN(minLikes)) {
        minLikes = 0;
    }

    var filtered = 0;
    var onFiltersApplied = function () { };

    var left = 0;

    function applyFilters() {
        if (!isFilteredPage()) {
            return;
        }

        var links = document.querySelectorAll("A[HREF]");

        var visible = 0, hidden = 0, insertAfter = null, insertIntoNode = null;

        for (var i = 0; i < links.length; i++) {
            var link = links[i];
            var href = link.getAttribute("href");

            if (/^\/readfic\/\d+$/.test(href)) {
                var block = findBlockRoot(link);

                if (block === null) {
                    continue;
                }

                if (insertAfter === null) {
                    insertAfter = $(block).prev();
                    insertIntoNode = block.parentNode;
                }

                if (minLikes > 0) {
                    var countEl = block.querySelector(".count");
                    if (countEl !== null) {
                        var count = countEl.innerText;
                        if (count.substring(0, 1) === "+") {
                            count = count.substring(1);
                        }
                        try {
                            count = parseInt(count);

                            if (count < minLikes) {
                                $(block).hide();
                                hidden++;
                                continue;
                            } else {
                                $(block).show();
                            }
                        } catch (e) {
                            console.log("Error:", e);
                        }
                    }
                }

                if (minSize > 0) {
                    var text = block.innerText;
                    var matches = /[ \t\r\n](\d+) страниц/.exec(text);

                    if (matches !== null && matches.length > 0) {
                        var pages = parseInt(matches[1]);

                        console.log(pages);

                        if (pages < minSize) {
                            $(block).hide();
                            hidden++;
                            continue;
                        } else {
                            $(block).show();
                        }

                    } else {
                        console.log("No page count in text", text);
                    }
                }

                visible++;
            }
        }

        if (hidden > 0 && visible === 0 && insertAfter !== null) {
            if ($("#_userscript_allfiltered").length === 0) {
                var htmlSel = $("<div class='block' style='color:red' id='_userscript_allfiltered'>Всё зафильтровано. Может на другой странице найдётся что-то подходящее...</div>");
                if (insertAfter.length === 0) {
                    htmlSel.appendTo(insertIntoNode);
                }
                else {
                    htmlSel.insertAfter(insertAfter);
                }
            }
        }
        else {
            $("#_userscript_allfiltered").remove();
        }

        left = visible;

        filtered = hidden;
        onFiltersApplied();
    }

    var cpDiv = $("<div><h2>Дополнительные фильтры</h2></div>");
    /*
    $("<A STYLE='display:block; text-decoration: none; float:right; background-color: black; color: white; border-radius: 0.25em; width: 1.85em; height: 1.85em; text-align: center' HREF='#'>x</A>").appendTo($(cpDiv)).click(function (event) {
        $(cpDiv).hide();
        event.preventDefault();
    });
    */
    $("<BR>").appendTo($(cpDiv));
    var statsDiv = $("<DIV></DIV>");
    statsDiv.appendTo(cpDiv);

    var loadingSearchPage = false;
    var loadingUrl = null;

    function loadSearchPage() {
        if (loadingSearchPage) {
            console.log('loading page already');
            return;
        }

        if (location.pathname !== '/find') {
            console.log('not a serp');
            return;
        }

        var ib = document.querySelector('#yandex_rtb_2');
        if (ib === null) {
            var ah = document.querySelectorAll('.pagination-holder');
            ib = ah.length > 0 ? ah[ah.length - 1] : null;
        }

        if (ib === null) {
            console.log('unable to find insert location');
        }

        var a_ = document.querySelectorAll("li:not(.disabled) a>.icon-arrow-right");
        var a = a_[0];
        if (a === null) {
            console.log('no next page');
        }
        a = a.parentNode;
        if (a.tagName === 'A' && a.hasAttribute("href")) {
            var nextLink = a.href;

            if (loadingUrl === nextLink) {
                return;
            }

            for (var i = 0; i < a_.length; i++) {
                var a = a_[i].parentNode;
                a.setAttribute('original-href', a.getAttribute('href'));
                a.removeAttribute('href');
            }

            console.log('Loading extra blocks from', nextLink);

            loadingUrl = nextLink;

            $.get(nextLink, null, function (data, textStatus, jqXHR) {
                var d = new DOMParser().parseFromString(data, "text/html");

                console.log('insert before:', ib, 'in:', ib.parentNode);

                var bs = d.querySelectorAll('.fanfic-thumb-block');

                console.log('blocks on page:', bs.length);

                var scrollY = window.scrollY;

                try {
                    var cc = document.createElement('div');
                    for (var i = 0; i < bs.length; i++) {
                        var b = bs[i];
                        cc.innerHTML = b.outerHTML;
                        var b = cc.childNodes[0];
                        cc.removeChild(b);
                        $(b.outerHTML).insertBefore($(ib));
                        //var n = ib.parentNode.insertBefore(b, ib);
                        console.log('Block added');
                    }

                    window.scrollY = scrollY;
                } catch (e) {
                    loadingSearchPage = false;
                    console.error(e);
                    throw e;
                }

                var nextLink = d.querySelector('li:not(.disabled) a>.icon-arrow-right');
                if (nextLink !== null) {
                    nextLink = nextLink.parentNode;
                }

                a_ = document.querySelectorAll('li:not(.disabled) a>.icon-arrow-right');
                if (a_ !== null && a_.length > 0 && nextLink !== null) {
                    for (var i = 0; i < a_.length; i++) {
                        var a = a_[i].parentNode;
                        a.setAttribute('href', nextLink.getAttribute('href'));
                    }
                }
                else {
                    console.log('Last page');
                    a_ = document.querySelectorAll('li > a > .icon-arrow-right');
                    if (nextLink !== null) {
                        for (var i = 0; i < a_.length; i++) {
                            var a = a_[i].parentNode;
                            nextLink.parentNode.parentNode.setAttribute('class', 'disabled');
                        }
                    }
                }

                htmlApplyBans(true);
                applyFilters();
            });
        }
        loadingSearchPage = false;
    }

    statsDiv.html("-");
    onFiltersApplied = function () {
        statsDiv.html("<div style='padding: 0.5em 0.5em 1.5em 0.5em'>Топка: " + deleted + "<br>Фильтр: " + filtered);

        if (left < 15 && location.pathname === '/find' && document.querySelector('.fanfic-thumb-block') !== null) {
            loadSearchPage();
        }
    };

    var filtersNode = null;

    function addFilterInput(initialValue, onValueChanged, textLabel) {
        if (filtersNode === null) {
            filtersNode = $("<div style='border-left: 5px solid #cab39e; background-color: #eae2d1; padding: 5px 15px;'>");
            filtersNode.appendTo(cpDiv);
        }
        var group = $("<div class='form-group form-group-sm'><label>" + textLabel + "</label><div class='form-inline'><input style='width: 5em' class='form-control short-number-input' id='minLinksFilter' type=number value='" + initialValue + "'>");
        var input = group.find('input');
        //$("<label for='minLinksFilter'>" + textLabel + ":</label>").appendTo(cpDiv);
        group.appendTo(filtersNode);
        input.on("input", function (event) {
            var value;
            try {
                value = parseInt(input.val());

                if (isNaN(value)) {
                    value = 0;
                }
            } catch (e) {
                value = 0;
            }

            onValueChanged(value);

            applyFilters();
        });

        return input;
    }

    var showPanel = !/^\/readfic\/\d+(?:\/\d+)?$/.test(location.pathname);

    if (showPanel && "/home/collections" === location.pathname) {
        showPanel = false;
    }

    if (showPanel) {

        var searchMinPages = document.querySelector("input[name='pages_min']");
        var searchMinLikes = document.querySelector("input[name='likes_min']");

        if (searchMinPages !== null && searchMinLikes !== null) {

            searchMinPages.addEventListener('input', function () {
                minSize = parseInt(searchMinPages.value);
                writeStorage(minSizeCookieName, minSize, 365 * 10);
                applyFilters();
            });

            searchMinLikes.addEventListener('input', function () {
                minLikes = parseInt(searchMinLikes.value);
                writeStorage(minLikesCookieName, minLikes, 365 * 10);
                applyFilters();
            });

        }
        else {
            addFilterInput(minSize, function (value) {
                minSize = value;
                writeStorage(minSizeCookieName, value, 365 * 10);
            }, "Размер");
            addFilterInput(minLikes, function (value) {
                minLikes = value;
                writeStorage(minLikesCookieName, value, 365 * 10);
            }, "Лайки");
        }

        var exportBtn = $("<input type='button' class='btn btn-default btn-block' value='Экспорт'>");
        exportBtn.click(function () {
            exportCookieData();
            exportBtn.hide();
            $('<textarea readonly></textarea>').insertAfter(exportBtn).text(exportCookieData());
        });
        cpDiv.append("<br>");
        exportBtn.appendTo(cpDiv);

        //cpDiv.appendTo("body");
        //cpDiv.attr('style', 'position: fixed; top: 0; right: 0; z-index: 999; background-color: #fff; padding: 0.5em 0.5em 0.5em 0.5em; border-radius: 1em');
        cpDiv.appendTo("#main .content-section");

        /*
        if (location.pathname === "/find") {
            cpDiv.append($("<button>Load</button>").click(function() {
                loadSearchPage();
            }));
        }
        */

    }

    var atBottom = null;
    var bottomResistance = 0;

    if (location.pathname === "/find") {
        window.addEventListener('scroll', function (ev) {
            atBottom = ((window.innerHeight + window.scrollY) >= document.body.offsetHeight);
            if (!atBottom) {
                bottomResistance = 5;
            }
        });

        window.addEventListener('mousewheel', function (ev) {
            //console.log('wheel:', ev);

            if (ev.deltaY <= 0) {
                bottomResistance = 5;
            }

            if (atBottom) {
                bottomResistance--;

                if (bottomResistance <= 0) {
                    bottomResistance = 5;
                    loadSearchPage();
                }
            }
        });
    }

    $('.fanfic-thumb-block-premium').hide();


    applyFilters();

    console.log('My ficbook.net userscript executed, f:', isFilteredPage());
}

/*$.get("https://ficbook.net/home/personal_info", null, function (data, textStatus, jqXHR) {
    var d = new DOMParser().parseFromString(data, "text/html");

    personalInfoValues = {
        'about_myself': d.querySelector('#aboutInput').value,
        'do_save': 'Сохранить+изменения',
        'www': d.querySelector('#wwwInput').value,
        'email': d.querySelector('#emailInput').value,
        'show_email': 'on',
        'icq': d.querySelector('#icqInput').value,
        'support_me': d.querySelector('#supportInput').value,
        'skype': d.querySelector('#skypeInput').value,
    };

    if (!d.querySelector("input[name='show_email']").checked) {
        delete personalInfoValues['show_email'];
    }

    var matches = banRegex.exec(personalInfoValues['about_myself']);

    if (matches) {
        localStorage.setItem('_userscript_banned_authors', matches[1]);
        localStorage.setItem('_userscript_banned_fics', matches[2]);
    }

    console.log('Профиль: ', personalInfoValues);

    setTimeout(applyFicBookFiltering, 1);
}, "text");*/

applyFicBookFiltering();