您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
manage your favorite tags, enhance searching, improve comic page
当前为
// ==UserScript== // @name ExHentai Viewer // @namespace Violentmonkey Scripts // @description manage your favorite tags, enhance searching, improve comic page // @match *://exhentai.org/* // @match *://e-hentai.org/* // @grant GM_setValue // @grant GM_getValue // @version 0.53 // ==/UserScript== const currentVersion = '0.53'; const $ = selector => document.querySelector(selector); const $$ = selector => document.querySelectorAll(selector); const create = tag => document.createElement(tag); let data = { custom_filter: GM_getValue('custom_filter', {}), script_config: GM_getValue('script_config', {}) } init(); function init() { // upgrade user data if not compatible upgradeScriptData(); // router const url = window.location.href; if (url.includes('/s/')) { // comic page EHViewer('s'); } else if (url.includes('/mpv')) { // multi page mode EHViewer('mpv'); } else if (url.includes('/g/')) { // gallery page handleGallery(); } else if ($('#searchbox')) { // add panel below the searchbox handleSearchBox(); } } function upgradeScriptData() { if (data.script_config.currentVersion) { // correct wrong naming style in 0.41 data.script_config.current_version = data.script_config.currentVersion; delete data.script_config.currentVersion; } switch (data.script_config.current_version) { // don't break, just go through the flow case undefined: if (data.custom_filter.length === undefined) { // just install } else { // 0.37 or ealier // backup old data data.script_config.old_version_data = [{ 'version': '0.37-', 'data': data.custom_filter }]; // migrate custom filters let new_custom_filter = {}; for (let item of data.custom_filter) { if (item && item.name && item.tag) { new_custom_filter[item.name] = item.tag.split('+'); } } data.custom_filter = new_custom_filter; } default: data.script_config.current_version = currentVersion; GM_setValue('custom_filter', data.custom_filter); GM_setValue('script_config', data.script_config); } } function changeStyle(css, mode, id = 'ehv-style') { let cssEl = $('#' + id); if (!cssEl) { cssEl = create('style'); cssEl.type = 'text/css'; cssEl.id = id; document.head.append(cssEl); } switch (mode) { case 'add': cssEl.innerHTML += css; break; case 'replace': cssEl.innerHTML = css; break; } } function addBtnContainer() { let btnStyle = ` #ehv-btn-c{ text-align: center; list-style: none; position: fixed; bottom: 30px; right: 30px; z-index: 999; } .ehv-btn { line-height: 32px; font-size: 16px; padding: 2px; margin: 5px; color: #233; position: relative; width: 32px; height: 32px; border: none; border-radius: 50%; box-shadow: 0 0 3px 0 #0006; cursor: pointer; user-select: none; outline: none; background-color: #fff; background-repeat: no-repeat; background-position: center; } .ehv-btn:hover{ box-shadow: 0 0 3px 1px #0005!important; } .ehv-btn:active { top: 1px; box-shadow: 0 0 1px 1px #0004!important; }`; let btnContainer = create('ul'); btnContainer.id = 'ehv-btn-c'; document.body.append(btnContainer); changeStyle(btnStyle, 'add', 'ehv-btn-c-style'); return btnContainer; } function addBtn(btn, container) { let btnEl = create('li'); if (btn.icon.startsWith('data:image')) { btnEl.style.backgroundImage = "url('" + btn.icon + "')"; } else { btnEl.innerHTML = btn.icon; } btnEl.classList.add('ehv-btn'); btnEl.addEventListener(btn.event, btn.cb); container.append(btnEl); return btnEl; } function EHViewer(mode) { const host = window.location.host; const svgIcons = { zoomIn: 'data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="rgb(120,120,120)" d="M14 7H9V2a1 1 0 0 0-2 0v5H2a1 1 0 0 0 0 2h5v5a1 1 0 0 0 2 0V9h5a1 1 0 0 0 0-2z"></path></svg>', zoomOut: 'data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect fill="rgb(120,120,120)" x="2" y="7" width="12" height="2" rx="1"></rect></svg>', prevPage: 'data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="rgb(120,120,120)" d="M15 7H3.414l4.293-4.293a1 1 0 0 0-1.414-1.414l-6 6a1 1 0 0 0 0 1.414l6 6a1 1 0 0 0 1.414-1.414L3.414 9H15a1 1 0 0 0 0-2z"></path></svg>', nextPage: 'data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="rgb(120,120,120)" d="M15.707 7.293l-6-6a1 1 0 0 0-1.414 1.414L12.586 7H1a1 1 0 0 0 0 2h11.586l-4.293 4.293a1 1 0 1 0 1.414 1.414l6-6a1 1 0 0 0 0-1.414z"></path></svg>', reload: 'data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="rgb(120,120,120)" d="M15 1a1 1 0 0 0-1 1v2.418A6.995 6.995 0 1 0 8 15a6.954 6.954 0 0 0 4.95-2.05 1 1 0 0 0-1.414-1.414A5.019 5.019 0 1 1 12.549 6H10a1 1 0 0 0 0 2h5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1z"></path></svg>', gallery: 'data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><g fill="rgb(120,120,120)"><rect x="1" y="1" width="6" height="6" rx="1" ry="1"></rect><rect x="9" y="1" width="6" height="6" rx="1" ry="1"></rect><rect x="1" y="9" width="6" height="6" rx="1" ry="1"></rect><rect x="9" y="9" width="6" height="6" rx="1" ry="1"></rect></g></svg>' } let css = ''; let btnContainer = addBtnContainer(); let currentScale; switch (mode) { case 's': currentScale = 0.45; css += ` #i1{ width:98%!important; max-width:98%!important; min-width:800px; background-color: inherit!important; border: none!important } #img{ max-width:none!important; max-height:none!important; background-color: #4f535b!important; padding: 8px; border: 1px solid #000; border-radius: 2px; } h1, #i2, #i5, #i6, #i7, .ip, .sn{ display:none!important; } ::-webkit-scrollbar{ display:none; } html{ scrollbar-width: none; }`; // add btns [ { icon: svgIcons.zoomIn, event: 'mousedown', cb: e => zoomer(zoomInS) }, { icon: svgIcons.zoomOut, event: 'mousedown', cb: e => zoomer(zoomOutS) }, { icon: svgIcons.prevPage, event: 'click', cb: prevPage }, { icon: svgIcons.nextPage, event: 'click', cb: nextPage }, { icon: svgIcons.reload, event: 'click', cb: e => $('#loadfail').click() }, { icon: svgIcons.gallery, event: 'click', cb: e => $('div.sb>a').click() } ].forEach(v => addBtn(v, btnContainer)); // handle key events document.body.addEventListener('keydown', e => { switch (e.key) { case "=": zoomInS(0.1); break; case "-": zoomOutS(0.1); break; case ",": window.scroll(0, 0); break; case ".": window.scroll(0, window.innerHeight); break; case "[": window.scrollBy(0, -window.innerHeight * 0.3); break; case "]": window.scrollBy(0, window.innerHeight * 0.3); break; } }); // set scale on page load setNewPage(); // set scale on img load let observer = new MutationObserver(setNewPage); observer.observe($('#i3'), { attributes: false, childList: true, subtree: false }); break; case 'mpv': currentScale = 1; css += '#pane_images_inner>div{margin:auto;}'; // add btns [ { icon: svgIcons.zoomIn, event: 'mousedown', cb: e => zoomer(zoomInMpv) }, { icon: svgIcons.zoomOut, event: 'mousedown', cb: e => zoomer(zoomOutMpv) } ].forEach(v => addBtn(v, btnContainer)); document.body.addEventListener('keydown', e => { switch (e.key) { case "=": zoomInMpv(0.1); break; case "-": zoomOutMpv(0.1); break; } }); break; } // add style if(data.script_config.hide_button){ css += ` #ehv-btn-c{ padding: 80px 30px 30px 80px; bottom: 0!important; right: -80px!important; transition-duration: 300ms; } #ehv-btn-c:hover{ padding: 80px 30px 30px 20px!important; right: 0!important; }`; } css += '.ehv-btn{background-color: #44454B!important;}'; if (host === 'e-hentai.org') { // btn color css = css.replaceAll('#44454B', '#D3D0D1'); // page bg & border (for mode 's') css = css.replace('#4f535b', '#EDEBDF').replace('1px solid #000', '1px solid #5C0D12'); } changeStyle(css, 'replace', 'ehv-style'); // === functions === function zoomer(cb) { // long press to trigger continuous scaling(zoomTimeout > zoomInterval) let zoomInterval = -1; let zoomTimeout = setTimeout(function () { zoomInterval = setInterval(cb, 100); }, 800); document.addEventListener('mouseup', e=>{ if (zoomInterval === -1) { cb(0.1); clearTimeout(zoomTimeout); } else { clearInterval(zoomInterval); } }, {once: true}); } function prevPage() { const currentPage = $('.sn>span:first-child').innerText currentPage !== '1' ? $('#prev').click() : alert('This is first page (⊙_⊙)'); } function nextPage() { const currentPage = $('.sn span:first-child').innerText const lastPage = $('.sn span:last-child').innerText; currentPage !== lastPage ? $('#next').click() : alert('This is last page (⊙ω⊙)'); } function setNewPage() { // add page indicator let footer = $('#i4 > div:first-child'); const currentPage = $('.sn span:first-child').innerText; const totalPage = $('.sn span:last-child').innerText; footer.innerHTML = currentPage + 'P / ' + totalPage + 'P :: ' + footer.innerText + ' :: '; // add origin source let imgLink = $('#i7>a'); imgLink ? footer.append(imgLink) : footer.innerHTML += 'No download'; // inherit the zoom scale of previous page zoom4S(); } function zoomInS(pace = 0.02) { const targetScale = currentScale * (1 + pace); currentScale = targetScale > 1 ? 1 : targetScale; zoom4S(); } function zoomOutS(pace = 0.02) { const img = $('#img'); const targetScale = currentScale * (1 - pace); currentScale = img.height > window.innerHeight ? targetScale : currentScale; zoom4S(); } function zoom4S() { const img = $('#img'); img.style.width = currentScale * 100 + '%'; img.style.height = 'auto'; } function zoomInMpv(pace = 0.02) { const maxWidth = Number($('#pane_images').style.width.replace("px", "")) - 20; const originalWidth = Number($('#image_1').style.maxWidth.replace("px", "")); currentScale = originalWidth * (1 + pace) < maxWidth ? currentScale + pace : currentScale; zoom4Mpv(originalWidth * currentScale); } function zoomOutMpv(pace = 0.02) { const minWidth = 200; const originalWidth = Number($('#image_1').style.maxWidth.replace("px", "")); currentScale = originalWidth * (currentScale - pace) > minWidth ? currentScale - pace : currentScale; zoom4Mpv(originalWidth * currentScale); } function zoom4Mpv(width) { let mpvStyle = 'img[id^="imgsrc"], div[id^="image"]{width:mpvWidth!important;height:auto!important; max-width:100%!important;min-width:200px!important}"'; mpvStyle = mpvStyle.replace('mpvWidth', width + 'px'); changeStyle(mpvStyle, 'replace', 'custom-width'); } } function handleGallery() { // add searchbox let searchBox = create('form'); searchBox.innerHTML = ` <p class="nopm"> <input type="text" id="f_search" name="f_search" placeholder="Search Keywords" value="" size="60"> <input type="submit" name="f_apply" value="Apply Filter"> </p>`; searchBox.setAttribute('style', 'display: none; width: 600px; margin: 10px auto; border: 2px ridge black; padding: 10px;'); searchBox.setAttribute('action', 'https://' + window.location.host + '/'); searchBox.setAttribute('method', 'get'); $('.gm').before(searchBox); // add btn to show/hide searchbox let tbody = $('#taglist > table > tbody'); tbody.innerHTML += ` <tr> <td class="tc">EHV:</td> <td> <div id="ehv-panel-btn" class="gt" style="cursor:pointer">show panel</div> </td> </tr>`; $('#ehv-panel-btn').addEventListener('click', e => { const t = e.target; if (t.innerText == 'show panel') { searchBox.style.display = "block"; t.innerText = 'hide panel'; } else { searchBox.style.display = "none"; t.innerText = 'show panel'; } }); // add panel setPanel(); // add gallery tag to searchbox by right-click tbody.addEventListener('contextmenu', applyGalleryTag); function applyGalleryTag(e) { const t = e.target; if (t.tagName === 'A') { e.preventDefault(); const searchInput = $('#f_search'); let tag = t.href.split('/').pop(); tag = `"${tag}"`; searchInput.value.includes(tag) ? searchInput.value = searchInput.value.replace(tag, '') : searchInput.value += tag; } } } function handleSearchBox() { setPanel(); const ehvPanel = $('#ehv-panel'); if (data.script_config.hide_panel) { ehvPanel.style.display = 'none'; const a = create('a'); a.id = 'ehv-panel-btn'; a.innerText = 'Show EHV Panel'; a.setAttribute('href', '#'); a.setAttribute('style', 'margin-left: 1em;'); a.addEventListener('click', function () { if (ehvPanel.style.display == 'none') { ehvPanel.style.display = 'block'; a.innerText = 'Hide EHV Panel'; } else { ehvPanel.style.display = 'none'; a.innerText = 'Show EHV Panel'; } }); $$('p.nopm')[1].append(a); }else{ $('#ehv-panel-btn') && $('#ehv-panel-btn').remove(); } } function setPanel() { const filters = data.custom_filter; const container = $('.nopm'); const searchInput = $('#f_search'); let ehvPanel = $('#ehv-panel'); if (ehvPanel) ehvPanel.remove(); ehvPanel = document.createElement('div'); ehvPanel.setAttribute('class', 'nopm'); ehvPanel.setAttribute('id', 'ehv-panel'); ehvPanel.setAttribute('style', 'padding-top:8px;'); container.append(ehvPanel); // filter buttons for (let key in filters) { let filterBtn = create('input'); filterBtn.setAttribute('type', 'button'); filterBtn.setAttribute('value', key); filterBtn.setAttribute('title', filters[key].toString()); filterBtn.addEventListener('click', applyFilter); filterBtn.addEventListener('contextmenu', removeFilter); if (isExist(filters[key])) { filterBtn.setAttribute('style', 'filter: invert(20%);'); } else { filterBtn.removeAttribute('style'); } ehvPanel.append(filterBtn); } //new filter button let addFilterBtn = create('input'); addFilterBtn.setAttribute('type', 'button'); addFilterBtn.setAttribute('value', '+'); addFilterBtn.addEventListener('click', addFilter); addFilterBtn.addEventListener('contextmenu', ehvSetting); ehvPanel.append(addFilterBtn); // // === function === function isExist(values) { const inputValue = searchInput.value; return values.every(v => inputValue.includes(v)); } function applyFilter(e) { let t = e.target; let key = t.value; let values = filters[key]; if (isExist(values)) { values.forEach(v => searchInput.value = searchInput.value.replaceAll(`"${v}"`, '')); t.removeAttribute('style'); } else { values.forEach(v => searchInput.value += `"${v}"`); t.setAttribute('style', 'filter: invert(20%);'); } } function addFilter() { const input = prompt('Add filter like format below', '[tag] or [name@tag] or [name@tag+tag+tag+tag]'); const filterStr = input ? input.split('@') : ''; let key, value; if(filterStr.length === 2){ key = filterStr[0]; value = filterStr[1].split('+'); filters[key] = value; }else if(filterStr.length === 1 && filterStr[0] !== ''){ key = value = filterStr[0]; filters[key] = [value]; }else{ alert('Invalid input...:('); return; } GM_setValue('custom_filter', filters); setPanel(); } function ehvSetting(e){ e.preventDefault(); data.script_config.hide_panel = confirm('hide EHV panel by default?'); data.script_config.hide_button = confirm('hide comic page buttons by default? (hover lower right corner to show)'); GM_setValue('script_config', data.script_config); handleSearchBox(); } function removeFilter(e){ e.preventDefault(); if(confirm('Delete this tag?')){ let key = e.target.value; delete filters[key]; GM_setValue('custom_filter', filters); setPanel(); } } }