您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Keyboard navigation, inertial drag scrolling, chapter preloading and chapter-tracking bookmarks
当前为
// ==UserScript== // @name Minor cleanups - asurascans.com // @namespace Itsnotlupus Industries // @match https://www.asurascans.com/* // @noframes // @version 1.2 // @author Itsnotlupus // @license MIT // @description Keyboard navigation, inertial drag scrolling, chapter preloading and chapter-tracking bookmarks // @require https://gf.qytechs.cn/scripts/468394-itsnotlupus-tiny-utilities/code/utils.js // ==/UserScript== /* jshint esversion:11 */ // yin yang SVG derived from https://icons8.com/preloaders/en/filtered-search/all/free;svg/ const loading_svg = 'data:image/svg+xml;base64,'+btoa` <svg xmlns="http://www.w3.org/2000/svg" width="64" height="128" viewBox="0 -128 128 256"> <circle cx="64" cy="64" r="63.31" fill="#fff"/> <g> <path d="M3.13 44.22a64 64 0 1 0 80.65-41.1 64 64 0 0 0-80.65 41.1zm34.15-4.83a10.63 10.63 0 1 1-13.4 6.8 10.63 10.63 0 0 1 13.4-6.8zm7.85 82.66A61.06 61.06 0 0 1 5.7 45.86 30.53 30.53 0 0 0 64 64a30.53 30.53 0 0 1 58.3 18.12l.35-1.14-.58 1.9a61.06 61.06 0 0 1-76.94 39.2zM106.9 73.2A10.63 10.63 0 1 0 93.5 80a10.63 10.63 0 0 0 13.4-6.8z"/> <animateTransform attributeName="transform" dur="1200ms" from="0 64 64" repeatCount="indefinite" to="-360 64 64" type="rotate"/> </g> </svg>`; 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; } /* hide various header and footer content. */ .socialts, .chdesc, .chaptertags, .postarea >#comments { display: none; } /* style a custom button to expand collapsed footer areas */ button.expand { float: right; border: 0; border-radius: 20px; padding: 2px 15px; font-size: 13px; line-height: 25px; background: #333; color: #888; font-weight: bold; cursor: pointer; } button.expand:hover { background: #444; } /* disable builtin drag behavior to allow drag scrolling */ * { user-select: none; -webkit-user-drag: none; } body.drag { cursor: grabbing; } /* add a loading state to the bookmark page so that it doesn't look broken. */ #bookmark-pool { /* add a loading animation to avoid image jumps. */ min-height: 180px; background: no-repeat center url('${loading_svg}'); } #bookmark-pool.loaded { background: none; } `); // 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 (Math.abs(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(img => img.loading="eager"); // retry loading broken images const imgBackoff = new Map(); const imgNextRetry = new Map(); const retryImage = img => { const now = Date.now(); const nextRetry = imgNextRetry.has(img) ? imgNextRetry.get(img) : (imgNextRetry.set(img, now),now); if (nextRetry <= now) { // exponential backoff between retries: 0ms, 250ms, 500ms, 1s, 2s, 4s, 8s, 10s, 10s, ... imgBackoff.set(img, Math.min(10000,(imgBackoff.get(img)??125)*2)); imgNextRetry.set(img, now + imgBackoff.get(img)); img.src=img.src; } else { setTimeout(()=>retryImage(img), nextRetry - now); } } observeDOM(() => { [...document.images].filter(img=>img.complete && !img.naturalHeight).forEach(retryImage); }); // and prefetch the next chapter's images for even less waiting. const nextURL = $`.ch-next-btn`?.href; if (nextURL) fetchHTML(nextURL).then(d => [...d.images].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)); } function makeCollapsedFooter({ label, section }) { const elt = crel('div', { className: 'bixbox', style: 'padding: 8px 15px' }, crel('button', { className: 'expand', textContent: label, onclick() { section.style.display = 'block'; elt.style.display = 'none'; } })); section.parentElement.insertBefore(elt, section); } const CHAPTER_REGEX = /\bChapter (?<chapter>\d+)\b/i; 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<chapter_number) { setLastReadChapter(post_id, chapter_id, chapter_number); } // Tweak footer content: // 2. collapse related series. const related = $`.bixbox > .releases`.parentElement; makeCollapsedFooter({label: 'Show Related Series', section: related}); related.style.display = 'none'; // 3. collapse comments. const comments = $`#comments`; makeCollapsedFooter({label: 'Show Comments', section: comments}); } if (location.pathname == '/bookmark/') (async () => { // 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. setTimeout(()=> { if (!$`#bookmark-pool [data-id]`) { // no data seen from bookmark API. try a fallback. $`#bookmark-pool`.innerHTML = localStorage.bookmarkHTML; } }, 5000); await untilDOM("#bookmark-pool [data-id]"); // bookmarks' ajax API is flaky (/aggressively rate-limited) - mitigate. localStorage.bookmarkHTML = $`#bookmark-pool`.innerHTML; // stop loading animation $`#bookmark-pool`.classList.add('loaded'); $$`#bookmark-pool [data-id]`.forEach(b => { const post_id = b.dataset.id; const latest_chapter = +$('.epxs',b).textContent.match(CHAPTER_REGEX)?.groups.chapter; const { number, id } = getLastReadChapter(post_id); if (id) { if (number < latest_chapter) { // change link to last read chapter and move to front of the line. const a = $('a',b); a.href = '/?p=' + id; const holder = b.parentElement; holder.parentElement.prepend(holder); } 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. } }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址