// ==UserScript== // @name Minor cleanups - asurascans.com // @namespace Itsnotlupus Industries // @match https://www.asurascans.com/* // @noframes // @version 1.0 // @author Itsnotlupus // @license MIT // @description Keyboard navigation, inertial drag, chapter preloading and chapter-tracking bookmarks // @require https://greasyfork.org/scripts/468394-itsnotlupus-tiny-utilities/code/utils.js // @downloadURL none // ==/UserScript== /* jshint esversion:11 */ addStyles(` /* remove ads and blank space between images were ads would have been */ [class^="ai-viewport"], .code-block, .blox, .kln, [id^="teaser"] { display: none !important; } /* disable builtin drag behavior to allow drag scrolling */ * { user-select: none; -webkit-user-drag: none; } body.drag { cursor: grabbing; } `); // keyboard navigation. good for long strips, which is apparently all this site has. const prev = () => $`.ch-prev-btn`?.click(); const next = () => $`.ch-next-btn`?.click(); addEventListener('keydown', e => ({ ArrowLeft: prev, ArrowRight: next, KeyA: prev, KeyD: next }[e.code]?.()), true); // inertial drag scrolling let [ delta, drag, dragged ] = [0, false, false]; events({ mousedown() { [ delta, drag, dragged ] = [0, true, false]; }, mousemove(e) { if (drag) { scrollBy(0, delta=-e.movementY); if (delta>3) { dragged = true; document.body.classList.add('drag'); } } }, mouseup(e) { if (drag) { drag=false; rAF((_, next) => Math.abs(delta*=0.95)>1 && next(scrollBy(0, delta))); } if (dragged) { dragged = false; document.body.classList.remove('drag'); const preventClick = e => { e.preventDefault(); e.stopPropagation(); removeEventListener('click', preventClick, true); }; addEventListener('click', preventClick, true); } } }); // don't be shy about loading an entire chapter $$`img[loading="lazy"]`.forEach(i=>i.loading="eager"); // and prefetch the next chapter for even less waiting. const nextURL = $`.ch-next-btn`?.href; if (nextURL) fetchHTML(nextURL).then( d => d.querySelectorAll`img[loading="lazy"]`.forEach( img => prefetch(img.src))); // have bookmarks track the last chapter you read const LAST_READ_CHAPTER_KEY = "lastReadChapter"; const lastReadChapters = JSON.parse(localStorage.getItem(LAST_READ_CHAPTER_KEY) ?? "{}"); function getLastReadChapter(post_id, defaultValue = {}) { return lastReadChapters[post_id] ?? defaultValue; } function setLastReadChapter(post_id, chapter_id, chapter_number) { lastReadChapters[post_id] = { id: chapter_id, number: chapter_number }; localStorage.setItem(LAST_READ_CHAPTER_KEY, JSON.stringify(lastReadChapters)); } const CHAPTER_REGEX = /\bChapter (?\d+)\b/; const chapterMatch = document.title.match(CHAPTER_REGEX); if (chapterMatch) { // We're on a chapter page. Save chapter number and id if greater than last saved chapter number. const chapter_number = +chapterMatch.groups.chapter; const { post_id, chapter_id } = window; const { number = 0 } = getLastReadChapter(post_id); if (number { // We're on a bookmark page. Wait for them to load, then tweak them to point to last read chapter, and gray out the ones that are fully read so far. await untilDOM("#bookmark-pool [data-id]"); const bookmarks = $$`#bookmark-pool [data-id]`; bookmarks.forEach(b => { const post_id = b.dataset.id; const latest_chapter = +b.querySelector('.epxs').textContent.match(CHAPTER_REGEX)?.groups.chapter; const lastReadChapter = getLastReadChapter(post_id); if (lastReadChapter.number) { if (lastReadChapter.number < latest_chapter) { // change link to last read chapter and move to front of the line. const a = b.querySelector`a`; a.href = '/?p=' + lastReadChapter.id; b.parentElement.parentElement.prepend(b.parentElement); } else { // nothing new to read here. gray it out. b.style = 'filter: grayscale(70%);opacity:.9'; } } else { // we don't have data on that series. leave it alone. } }); })();