// ==UserScript== // @name Infinite scroller // @namespace Violentmonkey Scripts // @match https://*.arca.live/* // @grant none // @version 1.0.3 // @author awk // @description 2021. 1. 6. 오전 11:17:30 // @downloadURL https://update.greasyfork.cloud/scripts/419731/Infinite%20scroller.user.js // @updateURL https://update.greasyfork.cloud/scripts/419731/Infinite%20scroller.meta.js // ==/UserScript== // bounce-back element size const bounceBackBuffer = 96; // last-checked page let scrolledPage = 1; let isLoading = false; // from Arca Refresher@LeKAKiD v2.6.8 function getTimeStr(datetime) { const date = new Date(datetime); let hh = date.getHours(); let mm = date.getMinutes(); if (hh.toString().length == 1) { hh = `0${hh}`; } if (mm.toString().length == 1) { mm = `0${mm}`; } return `${hh}:${mm}`; } function in24(datetime) { const target = new Date(datetime); const criteria = new Date(); criteria.setHours(criteria.getHours() - 24); if (target > criteria) return true; return false; } const bounceBack = () => { if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) { scrollBy({ 'top': -bounceBackBuffer, 'left': 0, 'behavior': 'smooth' }); } }; const getArticleId = elem => ~~elem.href.match(/\/b\/[^/]+\/(\d+)/)[1]; const loadMore = async () => { if(isLoading) return; isLoading = true; // true if scrolledPage value is reached to unread page let reached = false; const currentArticles = [...document.querySelectorAll('.vrow')]; const currentLastArticleId = getArticleId(currentArticles[currentArticles.length - 1]); // determine if current page doesn't include any article if(!currentLastArticleId) { isLoading = false; return; } while(!reached) { ++scrolledPage; // get next-page url const nextPage = (() => { // determine if current url includes page information if(location.href.match(/[&?]p=(\d+)/)) { return location.href.replace(/[&?]p=\d+/, pageString => { return pageString.replace(/\d+/, scrolledPage); }); } // determine if current url includes query string else if(location.href.match(/\?.+?=.+?/)){ return `${location.href}&p=${scrolledPage}`; } // it might be first-page of the board else { return `${location.href}?p=${scrolledPage}`; } })(); console.log(nextPage); // fetch and parse next-page DOM const nextPageDOM = await fetch(nextPage) .then(res => res.text()) .then(pageText => new DOMParser().parseFromString(pageText, 'text/html')); const nextPageArticles = [...nextPageDOM.querySelectorAll('.vrow:last-child')]; const nextPageLastArticle = nextPageArticles[nextPageArticles.length - 1]; // determine if reached to end if(!nextPageLastArticle || nextPageLastArticle.classList.length != 1) { // reached to end bounceBack(); break; } // determine if reached to unread page const nextPageLastArticleId = getArticleId(nextPageLastArticle); if(currentLastArticleId > nextPageLastArticleId) { // filter unread articles const unreadArticles = [...nextPageDOM.querySelectorAll('.vrow')].filter(article => { // filter out notices if(article.classList.length != 1) return false; return getArticleId(article) < currentLastArticleId; }); // attach unread articles with applying