您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
解锁 Hath Perks 及增加一些小工具
// ==UserScript== // @name Unlock Hath Perks // @name:zh-TW 解鎖 Hath Perks // @name:zh-CN 解锁 Hath Perks // @description Unlock Hath Perks and add other helpers // @description:zh-TW 解鎖 Hath Perks 及增加一些小工具 // @description:zh-CN 解锁 Hath Perks 及增加一些小工具 // @namespace https://flandre.in/github // @version 2.2.3 // @match https://e-hentai.org/* // @match https://exhentai.org/* // @require https://unpkg.com/[email protected]/dist/vue.min.js // @icon https://i.imgur.com/JsU0vTd.png // @grant GM_getValue // @grant GM.getValue // @grant GM_setValue // @grant GM.setValue // @noframes // @author FlandreDaisuki // @supportURL https://github.com/FlandreDaisuki/My-Browser-Extensions/issues // @homepageURL https://github.com/FlandreDaisuki/My-Browser-Extensions/blob/master/userscripts/UnlockHathPerks/README.md // @license MPLv2 // ==/UserScript== (function () { 'use strict'; const noop = () => {}; const $find = (el, selectors) => el.querySelector(selectors); const $ = (selectors) => document.querySelector(selectors); const $el = (tag, attr = {}, cb = noop) => { const el = document.createElement(tag); if (typeof(attr) === 'string') { el.textContent = attr; } else { Object.assign(el, attr); } cb(el); return el; }; const $style = (stylesheet) => $el('style', stylesheet, (el) => document.head.appendChild(el)); const throttle = (fn, timeout = 1000) => { let locked = false; return (...args) => { if (!locked){ locked = true; fn(...args); setTimeout(() => { locked = false; }, timeout); } }; }; /* cSpell:ignore navdiv navbtn exhentai adsbyjuicy searchbox favcat searchnav favform */ /* cSpell:ignoreRegExp \b\.\w+\b */ /* eslint-disable no-console */ /* global Vue */ // #region easy DOM // nav const nb = $('#nb'); const navdiv = $el('div'); const navbtn = $el('a', { id: 'uhp-btn', textContent: 'Unlock Hath Perks', }); navbtn.addEventListener('click', () => { $('#uhp-panel-container').classList.remove('hidden'); }); nb.appendChild(navdiv); navdiv.appendChild(navbtn); // panel container const uhpPanelContainer = $el('div', { className: 'hidden', id: 'uhp-panel-container', }); uhpPanelContainer.addEventListener('click', () => { uhpPanelContainer.classList.add('hidden'); }); document.body.appendChild(uhpPanelContainer); // panel const uhpPanel = $el('div', { id: 'uhp-panel' }, (el) => { if (location.host === 'exhentai.org') { el.classList.add('dark'); } el.addEventListener('click', (ev) => ev.stopPropagation()); }); uhpPanelContainer.appendChild(uhpPanel); // #endregion easy DOM // #region configs and events const uhpConfig = { abg: true, mt: true, pe: true, }; Object.assign(uhpConfig, GM_getValue('uhp', uhpConfig)); GM_setValue('uhp', uhpConfig); if (uhpConfig.abg) { Object.defineProperty(window, 'adsbyjuicy', { configurable: false, enumerable: false, writable: false, value: Object.create(null), }); } // More Thumbs code block if (location.pathname.startsWith('/g/')) { (async() => { const getGalleryPageState = async(url, selectors) => { const result = { elements: [], nextURL: null, }; if (!url) { return result; } const resp = await fetch(url, { credentials: 'same-origin', }); if (resp.ok) { const html = await resp.text(); const docEl = (new DOMParser()) .parseFromString(html, 'text/html') .documentElement; result.elements = [...$find(docEl, selectors.parent)?.children ?? []]; const nextEl = $find(docEl, selectors.np); result.nextURL = nextEl ? (nextEl.href || null) : null; } console.log(result); return result; }; const selectors = { np: '.ptt td:last-child > a', parent: '#gdt', }; const pageState = { parent: $(selectors.parent), locked: false, nextURL: null, }; const thisPage = await getGalleryPageState(location.href, selectors); while (pageState.parent.firstChild) { pageState.parent.firstChild.remove(); } thisPage.elements .filter((el) => !el.classList.contains('c')) .forEach((el) => pageState.parent.appendChild(el)); pageState.nextURL = thisPage.nextURL; if (!pageState.nextURL) { return; } if (uhpConfig.mt) { // search page found results document.addEventListener('scroll', throttle(async() => { const anchorTop = $('table.ptb').getBoundingClientRect().top; const vh = window.innerHeight; if (anchorTop < vh * 2 && !pageState.lock && pageState.nextURL) { pageState.lock = true; const nextPage = await getGalleryPageState(pageState.nextURL, selectors); nextPage.elements .filter((el) => !el.classList.contains('c')) .forEach((el) => pageState.parent.appendChild(el)); pageState.nextURL = nextPage.nextURL; pageState.lock = false; } })); } })(); } // Page Enlargement code block if ($('input[name="f_search"]') && $('.itg')) { (async() => { const getPageState = async(url, selectors) => { const result = { elements: [], nextURL: null, }; if (!url) { return result; } const resp = await fetch(url, { credentials: 'same-origin', }); if (resp.ok) { const html = await resp.text(); const docEl = (new DOMParser()) .parseFromString(html, 'text/html') .documentElement; result.elements = [...$find(docEl, selectors.parent)?.children ?? []]; const nextEl = $find(docEl, selectors.np); result.nextURL = nextEl ? (nextEl.href || null) : null; } console.log(result); return result; }; const isTableLayout = Boolean($('table.itg')); const status = $el('h1', { textContent: 'Loading...', id: 'uhp-status' }); const selectors = { np: '.ptt td:last-child > a, .searchnav a[href*="next="]', parent: isTableLayout ? 'table.itg > tbody' : 'div.itg', }; const pageState = { parent: $(selectors.parent), locked: false, nextURL: null, }; const thisPage = await getPageState(location.href, selectors); while (pageState.parent.firstChild) { pageState.parent.firstChild.remove(); } thisPage.elements.forEach((el) => pageState.parent.appendChild(el)); pageState.nextURL = thisPage.nextURL; if (!pageState.nextURL) { status.textContent = 'End'; } if (uhpConfig.pe) { $('table.ptb, .itg + .searchnav, #favform + .searchnav').replaceWith(status); // search page found results document.addEventListener('scroll', async() => { const anchorTop = status.getBoundingClientRect().top; const vh = window.innerHeight; if (anchorTop < vh * 2 && !pageState.lock && pageState.nextURL) { pageState.lock = true; const nextPage = await getPageState(pageState.nextURL, selectors); nextPage.elements.forEach((el) => pageState.parent.appendChild(el)); pageState.nextURL = nextPage.nextURL; if (!pageState.nextURL) { status.textContent = 'End'; } pageState.lock = false; } }); } })(); } // #endregion configs and events const uhpPanelTemplate = ` <div id="uhp-panel" :class="{ dark: isExH }" @click.stop> <h1>Hath Perks</h1> <div> <div v-for="d in HathPerks" class="option-grid"> <div class="material-switch"> <input :id="getConfId(d.abbr)" type="checkbox" v-model="conf[d.abbr]" @change="save" /> <label :for="getConfId(d.abbr)"></label> </div> <span class="uhp-conf-title">{{d.title}}</span> <span class="uhp-conf-desc">{{d.desc}}</span> </div> </div> </div> `; // eslint-disable-next-line no-new new Vue({ el: '#uhp-panel', template: uhpPanelTemplate, data: { conf: uhpConfig, HathPerks: [{ abbr: 'abg', title: 'Ads-Be-Gone', desc: 'Remove ads. You can use it with adblock webextensions.', }, { abbr: 'mt', title: 'More Thumbs', desc: 'Scroll infinitely in gallery pages.', }, { abbr: 'pe', title: 'Paging Enlargement', desc: 'Scroll infinitely in search results pages.', }], Others: [], }, computed: { isExH() { return location.host === 'exhentai.org'; }, }, methods: { save() { GM_setValue('uhp', uhpConfig); }, getConfId(id) { return `ubp-conf-${ id }`; }, }, }); $style(` /* nav bar */ #nb { width: initial; max-width: initial; max-height: initial; justify-content: center; } /* search input */ table.itc + p.nopm { display: flex; flex-flow: row wrap; justify-content: center; } input[name="f_search"] { width: 100%; } /* /favorites.php */ input[name="favcat"] + div { display: flex; flex-flow: row wrap; justify-content: center; gap: 8px; } /* gallery grid */ .gl1t { display: flex; flex-flow: column; } .gl1t > .gl3t { flex: 1; } .gl1t > .gl3t > a { display: flex; align-items: center; justify-content: center; height: 100%; }`); $style(` /* uhp */ #uhp-btn { cursor: pointer; } #uhp-panel-container { position: fixed; top: 0; height: 100vh; width: 100vw; background-color: rgba(200, 200, 200, 0.7); z-index: 2; display: flex; align-items: center; justify-content: center; } #uhp-panel-container.hidden { visibility: hidden; opacity: 0; } #uhp-panel { padding: 1.2rem; background-color: floralwhite; border-radius: 1rem; font-size: 1rem; color: darkred; max-width: 650px; } #uhp-panel.dark { background-color: dimgray; color: ghostwhite; } #uhp-panel .option-grid { display: grid; grid-template-columns: max-content 120px 1fr; grid-gap: 0.5rem 1rem; margin: 0.5rem 1rem; } #uhp-panel .option-grid > * { display: flex; justify-content: center; align-items: center; } #uhp-full-width-container.fullwidth, #uhp-full-width-container.fullwidth div.itg { max-width: none; } #uhp-full-width-container div.itg { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); grid-gap: 2px; } #uhp-full-width-container div.itg.uhp-tpf-dense { grid-auto-flow: dense; } #uhp-full-width-container div.id1 { height: 345px; float: none; display: flex; flex-direction: column; margin: 3px auto; padding: 4px 0; } #uhp-full-width-container div.id2 { overflow: visible; height: initial; margin: 4px auto; } #uhp-full-width-container div.id3 { flex: 1; display: flex; justify-content: center; align-items: center; } .uhp-list-parent-eh tr:nth-of-type(2n+1) { background-color: #EDEBDF; } .uhp-list-parent-eh tr:nth-of-type(2n+2) { background-color: #F2F0E4; } .uhp-list-parent-exh tr:nth-of-type(2n+1) { background-color: #363940; } .uhp-list-parent-exh tr:nth-of-type(2n+2) { background-color: #4F535B; } #uhp-status { text-align: center; font-size: 3rem; clear: both; padding: 2rem 0; } /* https://bootsnipp.com/snippets/featured/material-design-switch */ .material-switch { display: inline-block; } .material-switch > input[type="checkbox"] { display: none; } .material-switch > input[type="checkbox"] + label { display: inline-block; position: relative; margin: 6px; border-radius: 8px; width: 40px; height: 16px; opacity: 0.3; background-color: rgb(0, 0, 0); box-shadow: inset 0px 0px 10px rgba(0, 0, 0, 0.5); transition: all 0.4s ease-in-out; } .material-switch > input[type="checkbox"] + label::after { position: absolute; top: -4px; left: -4px; border-radius: 16px; width: 24px; height: 24px; content: ""; background-color: rgb(255, 255, 255); box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3); transition: all 0.3s ease-in-out; } .material-switch > input[type="checkbox"]:checked + label { background-color: #0e0; opacity: 0.7; } .material-switch > input[type="checkbox"]:checked + label::after { background-color: inherit; left: 20px; } .material-switch > input[type="checkbox"]:disabled + label::after { content: "\\f023"; line-height: 24px; font-size: 0.8em; font-family: FontAwesome; color: initial; }`); $el('link', { href: 'https://use.fontawesome.com/releases/v5.8.0/css/all.css', rel: 'stylesheet', integrity: 'sha384-Mmxa0mLqhmOeaE8vgOSbKacftZcsNYDjQzuCOm6D02luYSzBG8vpaOykv9lFQ51Y', crossOrigin: 'anonymous', }, (el) => document.head.appendChild(el)); })();