NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name rrl-hide-improved // @namespace mataamad // @version 0.3 // @description allows hiding of royal road fictions, reducing their visibility, and enables infinite scroll // @author fsoc, mataamad // @license MIT // @copyright 2019, lw (https://fsoc.space), mataamad // @match https://www.royalroad.com/fictions/* // @grant GM_setValue // @grant GM_getValue // ==/UserScript== // ==OpenUserJS== // @author mataamad // ==/OpenUserJS== (function () { // medium priority // the button states aren't intuitive - use a checkbox or something // add 'maybehide' // tidy up the code a bit // allow defining a couple of tags (read / dropped / sounds bad / hiatus etc.) and allow enabling or disabling the tags // at the moment if I hide something I might never see it again // low priority: // support the search page // style 'loading' indicator // can use tapermonkey 'fetch' instead of XMLHttpRequest for simplicity I think // add number next to stars because it's hard to tell the star amounts apart // add the ability to add notes to fictions const styles = document.createElement('style'); styles.innerHTML = ` .fiction-list-item.rr-hider-ignore div div { display: none; } .fiction-list-item.rr-hider-ignore figure { display: none; } .fiction-list-item.rr-hider-ignore { padding: 3px 0; } .fiction-list-item.rr-hider-ignore .fiction-title { margin: 0; } .fiction-list-item.rr-hider-hide-title { display: none; } .rr-hider-hide { display: none; } .rr-hider-full-hide-button { margin-bottom: 10px; } .rr-hider-full-hide-button.off { background-color: #444 !important; color: #777 !important; border: 1px solid #444 !important; } .rr-hider-full-hide-button.enabled { } .rr-hider-autoload { margin-left: 8px !important; } .rr-hider-sort-loaded { margin-left: 8px !important; } .rr-hider-autoload.off { background-color: #444 !important; color: #777 !important; border: 1px solid #444 !important; } .rr-hider-autoload.enabled { } .rr-hider-page-number-indicator { border-bottom: 1px solid hsla(0,0%,100%,.1) } .rr-hider-loading { } .fiction-list-item.rr-hider-ignore .rr-hider-hide-fiction { display: none; } .fiction-list-item.rr-hider-show .rr-hider-show-fiction { display: none; } `; document.head.appendChild(styles); // const { localStorage } = window; const localStorage = { getItem: GM_getValue, setItem: GM_setValue, } /*for (key of Object.keys(window.localStorage).filter(key => key.startsWith('ign_')).filter(key => window.localStorage[key] === "true")) { localStorage.setItem(key, "true"); }*/ let showHiddenTitles = localStorage.getItem('rr-hider-show-hidden-titles', "true") === "true"; let autoLoad = localStorage.getItem('rr-hider-auto-load', "true") === "true"; let currentPage = 1; let loading = false; const setClass = (element, cssClass, enable = true) => { if (enable) { element.classList.add(cssClass); } else { element.classList.remove(cssClass); } }; const updateHideStatuses = () => { const fictions = document.querySelectorAll('.fiction-list-item'); for (const fiction of fictions) { const id = "ign_" + fiction.getElementsByClassName('fiction-title').item(0).getElementsByTagName('a')[0].getAttribute('href').split('/')[2]; const ignore = localStorage.getItem(id) === "true"; setClass(fiction, 'rr-hider-show', !ignore); setClass(fiction, 'rr-hider-ignore', ignore); setClass(fiction, 'rr-hider-hide-title', ignore && !showHiddenTitles); } } const addHideButtons = (fictions) => { for (const fiction of fictions) { const id = "ign_" + fiction.getElementsByClassName('fiction-title').item(0).getElementsByTagName('a')[0] .getAttribute('href').split('/')[2]; const hideLink = document.createElement('a'); hideLink.setAttribute('class', 'rr-hider-hide-fiction font-red-sunglo'); hideLink.insertAdjacentText('afterbegin', 'Hide'); const showLink = document.createElement('a'); showLink.setAttribute('class', 'rr-hider-show-fiction font-red-sunglo'); showLink.insertAdjacentText('afterbegin', 'Show'); hideLink.onclick = () => { const status = localStorage.getItem(id) === "true"; localStorage.setItem(id, (!status).toString()); updateHideStatuses(); }; showLink.onclick = hideLink.onclick; const container = document.createElement('small'); container.append(hideLink); container.append(showLink); const heading = fiction.getElementsByClassName('fiction-title').item(0); heading.append(container); } } const sortLoadedByRating = () => { const fictionList = document.querySelector('.fiction-list') const fictions = fictionList.querySelectorAll('.fiction-list-item'); /*for (var fiction of fictions) { fictionList.removeChild(fiction); }*/ const getRating = (fiction) => parseFloat(fiction.querySelector('.star').getAttribute('title')); const sorted = [...fictions].sort((a, b) => { return getRating(b) - getRating(a); }); fictionList.append(...sorted); }; const fictionList = document.querySelector('.fiction-list'); if (!fictionList) { return; } const fictions = document.querySelectorAll('.fiction-list-item'); addHideButtons(fictions); updateHideStatuses(); const loadingElement = document.createElement('div'); loadingElement.insertAdjacentText('afterbegin', 'loading more stories...'); loadingElement.setAttribute('class', 'rr-hider-loading font-red-sunglo'); fictionList.parentNode.insertBefore(loadingElement, fictionList.nextSibling); const userscriptButtons = document.createElement('div'); userscriptButtons.setAttribute('class', 'btn-group'); const fullHide = document.createElement('a'); fullHide.setAttribute('class', 'rr-hider-full-hide-button btn blue-dark'); fullHide.insertAdjacentText('afterbegin', 'Fully Hide Ignored'); setClass(fullHide, 'enabled', !showHiddenTitles); setClass(fullHide, 'off', showHiddenTitles); fullHide.onclick = () => { showHiddenTitles = !showHiddenTitles; localStorage.setItem('rr-hider-show-hidden-titles', showHiddenTitles.toString()) setClass(fullHide, 'enabled', !showHiddenTitles); setClass(fullHide, 'off', showHiddenTitles); updateHideStatuses(); } userscriptButtons.appendChild(fullHide); fictionList.parentNode.insertBefore(userscriptButtons, fictionList); const currentLocation = window.location.href; let pagingUrl = ''; const validLazyLoadUrls = [ 'https://www.royalroad.com/fictions/active-popular', 'https://www.royalroad.com/fictions/best-rated', 'https://www.royalroad.com/fictions/complete', 'https://www.royalroad.com/fictions/weekly-popular', 'https://www.royalroad.com/fictionsr/latest-updates', 'https://www.royalroad.com/fictionsr/new-releases', // https://www.royalroad.com/fictions/search, // to allow lazy loading search I'd need to preserver the URL parameters // 'https://www.royalroad.com/fictions/trending', // trending is limited to 50 pages ]; if (validLazyLoadUrls.includes(currentLocation)) { pagingUrl = currentLocation + '?page='; } if (pagingUrl) { const autoLoadElement = document.createElement('a'); autoLoadElement.setAttribute('class', 'rr-hider-autoload btn blue-dark'); autoLoadElement.insertAdjacentText('afterbegin', 'Infinite Scroll'); setClass(autoLoadElement, 'enabled', autoLoad); setClass(autoLoadElement, 'off', !autoLoad); autoLoadElement.onclick = () => { autoLoad = !autoLoad; localStorage.setItem('rr-hider-auto-load', autoLoad.toString()) setClass(autoLoadElement, 'enabled', autoLoad); setClass(autoLoadElement, 'off', !autoLoad); updateHideStatuses(); updateShowPagination(); } userscriptButtons.appendChild(autoLoadElement); } const sortLoaded = document.createElement('a'); sortLoaded.setAttribute('class', 'rr-hider-sort-loaded btn blue-dark'); sortLoaded.insertAdjacentText('afterbegin', 'Sort Loaded By Rating'); sortLoaded.onclick = sortLoadedByRating; userscriptButtons.appendChild(sortLoaded); const updateLoadingIndicator = () => { setClass(loadingElement, 'rr-hider-hide', !loading); } const updateShowPagination = () => { const pagination = document.querySelector('.pagination'); if (pagination) { setClass(pagination, 'rr-hider-hide', autoLoad && pagingUrl); } } updateLoadingIndicator(); updateShowPagination(); const tryLoadNextPage = () => { const closeToEnd = window.scrollY + window.innerHeight * 1.5 > fictionList.offsetTop + fictionList.clientHeight; if (loading || !autoLoad || !closeToEnd || !pagingUrl) { return; } loading = true; currentPage++; updateLoadingIndicator(); let xhr = new XMLHttpRequest(); xhr.open('GET', pagingUrl + currentPage); xhr.onload = function () { const fictionList = document.querySelector('.fiction-list') var pageNumberIndicator = document.createElement('h1'); pageNumberIndicator.insertAdjacentText('afterbegin', 'Page ' + currentPage); pageNumberIndicator.setAttribute('class', 'bold uppercase font-red-sunglo rr-hider-page-number-indicator') fictionList.appendChild(pageNumberIndicator); var res = document.createElement('div'); res.insertAdjacentHTML('afterbegin', xhr.responseText); var nextPageFictions = res.querySelectorAll('.fiction-list .fiction-list-item') addHideButtons(nextPageFictions); fictionList.append(...nextPageFictions); updateHideStatuses(); loading = false; updateLoadingIndicator(); tryLoadNextPage(); } xhr.send(); } window.addEventListener('scroll', tryLoadNextPage); tryLoadNextPage(); })();