NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Фильтры для ficbook.net // @namespace ficbook.net.uo1.net // @description Позоляет убрать раздражающих авторов и прозведения. Фильтровать мелочь и фики с низком количеством лайков. Всё настраивается. // @include /^https?:\/\/ficbook.net(/.*|)$/ // @version 1.21 // @grant none // @updateURL https://openuserjs.org/meta/azazar/Фильтры_для_ficbook.net.meta.js // @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();