您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Refining yande.re
当前为
// ==UserScript== // @name yande.re refine // @namespace https://greasyfork.org/scripts/397612-yande-re-refine // @description Refining yande.re // @include *://behoimi.org/* // @include *://www.behoimi.org/* // @include *://*.donmai.us/* // @include *://konachan.tld/* // @include *://yande.re/* // @include *://chan.sankakucomplex.com/* // @version 2020.03.10b // @grant none // ==/UserScript== //If true, each added page retains its paginator. If false, elements are smoothly joined together. var pageBreak = false //Minimum amount of window left to scroll, maintained by loading more pages. var scrollBuffer = 600 //Time (in ms) the script will wait for a response from the next page before attempting to fetch the page again. If the script gets trapped in a loop trying to load the next page, increase this value. var timeToFailure = 15000 var previewLocked = false //============================================================================ //=========================Script initialization============================== //============================================================================ var nextPage, mainTable, mainParent, pending, timeout, iframe let previewIframe, previewImage, previewImageDiv, previewDialog let imagesList = [] let currentImage = 0 if (typeof customF != 'undefined') customF() injectGlobalStyle() initialize() function initialize() { //Stop if inside an iframe if (window != window.top || scrollBuffer == 0) return //Stop if no "table" mainTable = getMainTable(document) if (!mainTable) return injectStyle() processItems() //Stop if no paginator var paginator = getPaginator(document) if (!paginator) return //Stop if no more pages nextPage = getNextPage(paginator) if (!nextPage) return //Hide the blacklist sidebar, since this script breaks the tag totals and post unhiding. var sidebar = document.getElementById('blacklisted-sidebar') if (sidebar) sidebar.style.display = 'none' //Other important variables: scrollBuffer += window.innerHeight mainParent = mainTable.parentNode pending = false iframe = document.createElement('iframe') iframe.width = iframe.height = 0 iframe.style.visibility = 'hidden' document.body.appendChild(iframe) //Slight delay so that Danbooru's initialize_edit_links() has time to hide all the edit boxes on the Comment index iframe.addEventListener( 'load', function(e) { setTimeout(appendNewContent, 100) }, false, ) //Stop if empty page if ( /<p>(Nothing to display.|Nobody here but us chickens!)<.p>/.test( mainTable.innerHTML, ) ) return //Add copy of paginator to the top mainParent.insertBefore(paginator.cloneNode(true), mainParent.firstChild) if (!pageBreak) paginator.style.display = 'none' //Hide bottom paginator else { //Reposition bottom paginator and add horizontal break mainTable.parentNode.insertBefore( document.createElement('hr'), mainTable.nextSibling, ) mainTable.parentNode.insertBefore(paginator, mainTable.nextSibling) } //Listen for scroll events window.addEventListener('scroll', testScrollPosition, false) testScrollPosition() } //============================================================================ //============================Script functions================================ //============================================================================ //Some pages match multiple "tables", so order is important. function getMainTable(source) { //Special case: Sankaku post index with Auto Paging enabled if ( /sankaku/.test(location.host) && /auto_page=1/.test(document.cookie) && /^(post(\/|\/index\/?)?|\/)$/.test(location.pathname) ) return null var xpath = [ ".//div[@id='c-favorites']//div[@id='posts']", // Danbooru (/favorites) ".//div[@id='posts']/div", // Danbooru; don't want to fall through to the wrong xpath if no posts ("<article>") on first page. ".//div[@id='c-pools']//section/article/..", // Danbooru (/pools/####) ".//div[@id='a-index']/table[not(contains(@class,'search'))]", // Danbooru (/forum_topics, ...), take care that this doesn't catch comments containing tables ".//div[@id='a-index']", // Danbooru (/comments, ...) ".//table[contains(@class,'highlight')]", // large number of pages ".//div[@id='content']/div/div/div/div/span[@class='author']/../../../..", // Sankaku: note search ".//div[contains(@id,'comment-list')]/div/..", // comment index ".//*[not(contains(@id,'popular'))]/span[contains(@class,'thumb')]/a/../..", // post/index, pool/show, note/index ".//li/div/a[contains(@class,'thumb')]/../../..", // post/index, note/index ".//div[@id='content']//table/tbody/tr[@class='even']/../..", // user/index, wiki/history ".//div[@id='content']/div/table", // 3dbooru user records ".//div[@id='forum']", // forum/show ] for (var i = 0; i < xpath.length; i++) { getMainTable = (function(query) { return function(source) { var mTable = new XPathEvaluator().evaluate( query, source, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null, ).singleNodeValue if (!mTable || !pageBreak) return mTable //Special case: Danbooru's /favorites lacks the extra DIV that /posts has, which causes issues with the paginator/page break. var xDiv = document.createElement('div') xDiv.style.overflow = 'hidden' mTable.parentNode.insertBefore(xDiv, mTable) xDiv.appendChild(mTable) return xDiv } })(xpath[i]) var result = getMainTable(source) if (result) { //alert("UPW main table query: "+xpath[i]+"\n\n"+location.pathname); return result } } return null } function getPaginator(source) { var pager = new XPathEvaluator().evaluate( "descendant-or-self::div[@id='paginator' or @class='paginator' or @id='paginater']", source, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null, ).singleNodeValue // Need clear:none to prevent the 2nd page from being pushed to below the sidebar on the Post index... but we don't want this when viewing a specific pool, // because then the paginator is shoved to the right of the last images on a page. Other sites have issues with clear:none as well, like //yande.re/post. if ( pager && location.host.indexOf('donmai.') >= 0 && document.getElementById('sidebar') ) pager.style.clear = 'none' return pager } function getNextPage(source) { let page = getPaginator(source) if (page) page = new XPathEvaluator().evaluate( ".//a[@alt='next' or @rel='next' or contains(text(),'>') or contains(text(),'Next')]", page, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null, ).singleNodeValue return page && page.href } function testScrollPosition() { if (!nextPage) testScrollPosition = function() {} //Take the max of the two heights for browser compatibility else if ( !pending && window.pageYOffset + scrollBuffer > Math.max( document.documentElement.scrollHeight, document.documentElement.offsetHeight, ) ) { pending = true timeout = setTimeout(function() { pending = false testScrollPosition() }, timeToFailure) iframe.contentDocument.location.replace(nextPage) } } function appendNewContent() { //Make sure page is correct. Using 'indexOf' instead of '!=' because links like "https://danbooru.donmai.us/pools?page=2&search%5Border%5D=" become "https://danbooru.donmai.us/pools?page=2" in the iframe href. clearTimeout(timeout) if (nextPage.indexOf(iframe.contentDocument.location.href) < 0) { setTimeout(function() { pending = false }, 1000) return } //Copy content from retrived page to current page, but leave off certain headers, labels, etc... var sourcePaginator = document.adoptNode(getPaginator(iframe.contentDocument)) var nextElem, deleteMe, source = document.adoptNode(getMainTable(iframe.contentDocument)) if ( /<p>(Nothing to display.|Nobody here but us chickens!)<.p>/.test( source.innerHTML, ) ) nextPage = null else { nextPage = getNextPage(sourcePaginator) if (pageBreak) mainParent.appendChild(source) else { //Hide elements separating one table from the next (h1 is used for user names on comment index) var rems = source.querySelectorAll('h2, h3, h4, thead, tfood') for (var i = 0; i < rems.length; i++) rems[i].style.display = 'none' //Move contents of next table into current one var fragment = document.createDocumentFragment() while ((nextElem = source.firstChild)) fragment.appendChild(nextElem) mainTable.appendChild(fragment) } } //Add the paginator at the bottom if needed. if (!nextPage || pageBreak) mainParent.appendChild(sourcePaginator) if (pageBreak && nextPage) mainParent.appendChild(document.createElement('hr')) //Clear the pending request marker and check position again processItems() pending = false testScrollPosition() } function injectGlobalStyle() { const s = document.createElement('style') s.innerHTML = ` body { padding: 0; } #title { display: none !important; } #header { margin: 0 !important; text-align: center; } #header ul { float: none !important; display: inline-block;} #content > div:first-child > div.sidebar { position: fixed; left: 0; top: 0; bottom: 0; overflow: auto !important; width: 200px !important; transform: translateX(-210px); background: #171717dd; transition: all .2s ease-out; float: none !important; padding: 15px; } #content > div:first-child > div.sidebar:hover { transform: translateX(0); } div.content { width: 100vw; text-align: center; } ` document.body.appendChild(s) } function injectStyle() { const s = document.createElement('style') s.innerHTML = ` a.directlink { display: none !important; } ul#post-list-posts { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; } ul#post-list-posts li { width: auto !important; margin: auto; padding: 5px; transition: .3s ease-out; overflow: visible; } ul#post-list-posts li img, ul#post-list-posts li .inner { width: auto !important; height: auto !important; } ul#post-list-posts li img { border-radius: 5px; } ul#post-list-posts li a { transition: .3s ease-out; } ul#post-list-posts li a.liked img { border: 2px solid pink; } ul#post-list-posts li:hover { transform: scale(1.3); z-index: 1; } ul#post-list-posts li:hover a { opacity: 1; box-shadow: 6px 6px 20px 0px rgba(50, 50, 50, 0.75); } .hidden { display: none !important; } .preivew-dialog { position: fixed; top: 0; left: 0; height: 100vh; width: 100vw; background: rgba(0,0,0,0.7); z-index: 100; } .preivew-dialog iframe { position: absolute; height: 90vh; width: 80vw; top: 50%; left: 50%; transform: translate(-50%, -50%); background: grey; border: none; border-radius: 5px; overflow: hidden; } .preivew-dialog .image-host { position: fixed; top: 0; left: 0; height: 100vh; width: 100vw; overflow: auto; text-align: center; } .preivew-dialog .image-host img { margin: auto; } .preivew-dialog .image-host.full { overflow: hidden } .preivew-dialog .image-host.full img { max-width: 100vw; max-height: 100vh; } ` document.body.appendChild(s) } function initPreviewIframe() { previewDialog = document.createElement('div') previewDialog.addClassName('preivew-dialog hidden') previewDialog.onclick = (e) => { if (e.target === previewDialog) previewDialog.classList.toggle('hidden', true) } window.onkeydown = (e) => { console.log(e) if (!previewDialog.classList.contains('hidden')) { if (e.key === 'ArrowLeft') { currentImage = Math.max(0, currentImage - 1) openImage(currentImage) e.preventDefault() } if (e.key === 'ArrowRight') { currentImage = Math.min(imagesList.length - 1, currentImage + 1) openImage(currentImage) e.preventDefault() } if (e.key === 'Escape') { previewDialog.classList.toggle('hidden', true) e.preventDefault() } if (e.key === 'Tab') { openImage(currentImage, 'page') e.preventDefault() } if (e.code === 'Space') { previewImageDiv.classList.toggle('full') e.preventDefault() } if (e.code === 'KeyL') { like(currentImage, 3) e.preventDefault() } if (e.code === 'KeyU') { unlike(currentImage, 2) e.preventDefault() } } } previewIframe = document.createElement('iframe') previewImageDiv = document.createElement('div') previewImageDiv.className = 'image-host full' previewImage = document.createElement('img') previewImage.onclick = (e) => { previewDialog.classList.toggle('hidden', true) } previewDialog.appendChild(previewIframe) previewImageDiv.appendChild(previewImage) previewDialog.appendChild(previewImageDiv) document.body.appendChild(previewDialog) } function processItems() { document .querySelectorAll( 'ul#post-list-posts li:not([_init]), ul#post-list-posts li:not([_init])', ) .forEach((li) => { li.setAttribute('_init', true) const a = li.querySelector('a.thumb') const url = a.href a.href = 'javascript:;' if (!url) return const large = (li.querySelector('a.largeimg') || {}).href const id = url.split('/').slice(-1)[0] const image = { url, large, id, a } let index = imagesList.length imagesList.push(image) let lastClicked = -Infinity let timer = null a.onclick = (e) => { e.preventDefault() // double click if (Date.now() - lastClicked < 300) { if (image.liked) unlike(index) else like(index) clearTimeout(timer) // click } else { lastClicked = +Date.now() timer = setTimeout(() => openImage(index), 400) } return false } }) } function openImage(idx, type = 'image') { currentImage = idx const { url, large, id } = imagesList[idx] console.log({ url, large, id }) if (!previewIframe) initPreviewIframe() if (!large) type = 'page' if (type === 'image') { previewImage.src = large previewIframe.classList.toggle('hidden', true) previewImageDiv.classList.toggle('hidden', false) } else { previewIframe.onload = () => { if (previewIframe.contentWindow.location.href !== url) { location.href = previewIframe.contentWindow.location.href previewDialog.classList.toggle('hidden', true) previewIframe.onload = null } } previewIframe.src = url previewIframe.classList.toggle('hidden', false) previewImageDiv.classList.toggle('hidden', true) } previewDialog.classList.toggle('hidden', false) } async function vote(id, score) { let body = new FormData() body.append('id', id) body.append('score', score) const rawResponse = await fetch('https://yande.re/post/vote.json', { method: 'POST', headers: { 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]').attributes .content.value, }, body, }) const content = await rawResponse.json() console.log(content) } function like(idx) { let image = imagesList[idx] vote(image.id, 3) image.liked = true image.a.classList.toggle('liked', image.liked) } function unlike(idx) { let image = imagesList[idx] vote(image.id, 2) image.liked = false image.a.classList.toggle('liked', image.liked) }