您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
New interface to search images on Booru-style sites.
// ==UserScript== // @name Danbooru Ajax Interface // @namespace http://danbooru.donmai.us // @description New interface to search images on Booru-style sites. // @match *://danbooru.donmai.us/ // @match *://danbooru.donmai.us/#* // @match *://*.gelbooru.com/ // @match *://*.gelbooru.com/#* // @match *://konachan.com/ // @match *://konachan.com/#* // @match *://konachan.net/ // @match *://konachan.net/#* // @match *://yande.re/post // @match *://yande.re/post#* // @version 4.6692016 // @grant GM_deleteValue // ==/UserScript== // NEW SITES CAN BE ADDED NOW var sites = [ { name : "Danbooru", // Site name site : "donmai\.us", // Regular expression check on current url post : "/posts.xml", // Relative url to xml post API note : "/notes.xml", // Relative url to xml note API list : "/posts/", // Relative url to post listing page : "/posts/", // Relative url to post page query : function(tags, images, page, postid) { // Query passed to API return (postid ? "?limit=99999&search[is_active]=true&search[post_id]=" + postid : "?tags=" + tags + (page ? "&page=" + page + "&limit=" + images : "")); } }, { name : "Gelbooru", site : "(www\.)?gelbooru\.", post : "/index.php?page=dapi&s=post&q=index", note : "/index.php?page=dapi&s=note&q=index&post_id=", list : "/index.php?page=post&s=list", page : "/index.php?page=post&s=view&id=", query : function(tags, images, page, postid) { return (postid ? postid : "&tags=" + tags + "&limit=" + images + "&pid=" + (page - 1)); } }, { name : "Konachan", site : "konachan\.", post : "/post.xml", note : "/note.xml?post_id=", list : "/post/", page : "/post/show/", query : function(tags, images, page, postid) { return (postid ? postid : "?tags=" + tags + "&limit=" + images + "&page=" + page); } }, { name : "Yande.re", site : "yande\.re", post : "/post.xml", note : "/note.xml?post_id=", list : "/post/", page : "/post/show/", query : function(tags, images, page, postid) { return (postid ? postid : "?tags=" + tags + "&limit=" + images + "&page=" + page); } } ]; // CONSTANTS const ratio = ((1 + Math.sqrt(5)) / 2); const d = document; const cacheExpires = 1000 * 60 * 60 * 24 * 7; // cache expires in 7 days : (ms * s * m * h * d) // PAGE CLEANING while(d.documentElement.firstChild) d.documentElement.removeChild(d.documentElement.firstChild); // IMPORTANT VARIABLES var booru, storage, requestPost, requestNote, requestCount, requestCache, requestTag, reqTagTimer, tagTimer, noteclearTimer, content; var tags = "", images = 20, page = 1, rating = "s", sampleRate = 1, blacklist = "spoilers \nguro \nscat "; var cacheHide = [], cacheID = [], cacheTags = []; var domParser = new DOMParser(); var xsltText = "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'><xsl:template match='*'><xsl:copy><xsl:for-each select='*|@*'><xsl:copy><xsl:for-each select='*|@*'><xsl:attribute name='{name()}'><xsl:value-of select='.|text()'/><xsl:if test='@nil'>false</xsl:if></xsl:attribute></xsl:for-each></xsl:copy></xsl:for-each></xsl:copy></xsl:template></xsl:stylesheet>"; for(var i = 0; i < sites.length; i++) if(new RegExp(sites[i].site).test(window.location.hostname)) booru = sites[i]; if(checkStorage()) { getStorage(); window.addEventListener("unload", setStorage); // save last search when page is changed/closed } // SCRIPT STARTS HERE d.documentElement.appendChild(d.createElement("HEAD")); d.documentElement.appendChild(d.createElement("BODY")); d.documentElement.firstChild.appendChild(title = d.createElement("TITLE")); title.appendChild(d.createTextNode(booru.name)); var fa = d.createElement("LINK"); fa.setAttribute("rel", "stylesheet"); fa.setAttribute("href", "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"); d.head.appendChild(fa); var dai_css = d.createElement("STYLE"); dai_css.setAttribute("type", "text/css"); dai_css.appendChild(d.createTextNode("body { margin: 4px; } a { text-decoration: none; color: #0000EE; } img { border: 0px; } .thumb { border: 1px solid WhiteSmoke; min-width: 150px; min-height: 150px; max-width: 150px; max-height: 150px; margin: -1px 0px 0px -1px; padding: 1px; display: flex; justify-content: center; align-items: center; font-size: small; overflow: hidden; } .yellow { background-color: LightYellow; } .red { background-color: MistyRose; } .trans { border: 1px solid Black; background-color: LightYellow; position: absolute; } *:not(body) { transition: all 2s ease-in-out 0s; }")); d.body.appendChild(searchTable = d.createElement("TABLE")); searchTable.appendChild(searchTr = d.createElement("TR")); searchTr.setAttribute("style", "vertical-align: top;"); searchTr.appendChild(searchTd = d.createElement("TD")); searchTd.setAttribute("style", "text-align: center;"); searchForm = d.createElement("FORM"); searchForm.addEventListener("submit", function(event) { tags = aTags.value; page = Math.max(1, parseInt(aPage.value, 10)); images = Math.max(1, Math.min(parseInt(aImages.value, 10), 200)); rating = (aRS.checked ? "s" : "") + (aRQ.checked ? "q" : "") + (aRE.checked ? "e" : "") + (aFIT.checked ? "f" : "") + (aSD && aSD.checked ? "d" : ""); search(tags, page); event.preventDefault(); }, false); searchForm.appendChild(aLink = d.createElement("A")); aLink.setAttribute("href", booru.list); aLink.setAttribute("tabindex", "1"); aLink.appendChild(d.createTextNode(booru.name)); searchForm.appendChild(d.createElement("BR")); searchForm.appendChild(aTags = d.createElement("INPUT")); aTags.setAttribute("type", "text"); aTags.setAttribute("style", "width: 80%"); aTags.setAttribute("value", tags); aTags.setAttribute("tabindex", "2"); aTags.setAttribute("list", "autocomplete"); aTags.addEventListener("change", function(event) { aPage.value = 1; }, false); searchForm.appendChild(aDatalist = d.createElement("DATALIST")); aDatalist.setAttribute("id", "autocomplete"); if(cacheTags.length == 0) { popularTags = "touhou kantai_collection idolmaster vocaloid fate_(series) mahou_shoujo_madoka_magica".split(" ").reverse(); for(var i in popularTags) { cacheTags.push({ tag : popularTags[i], expires : Date.now() + cacheExpires }); } } for(var i = cacheTags.length - 1; i >= Math.max(cacheTags.length - 6, 0); i--) { // last 6 added values first var tagoption = d.createElement("OPTION"); tagoption.setAttribute("value", cacheTags[i].tag + " "); aDatalist.appendChild(tagoption); } aTags.addEventListener("input", function(e) { clearTimeout(tagTimer); clearTimeout(reqTagTimer); if(requestTag) requestTag.abort(); tagTimer = setTimeout(function() { lookuptag = aTags.value.split(" ").pop(); if(!lookuptag) return; taglist = aTags.value.split(" "); taglist.pop(); taglist = taglist.join(" "); if(taglist.length > 0) taglist += " "; while(aDatalist.hasChildNodes()) aDatalist.removeChild(aDatalist.firstChild); aDL = d.createElement("DATALIST"); // adding new datalist to main datalist updates the main list contents for(var a = cacheTags.length - 1; a >= 0 && aDL.childNodes.length < 6; a--) { if(cacheTags[a] && cacheTags[a].tag.toLowerCase().startsWith(lookuptag.toLowerCase())) { aOptionTag = d.createElement("OPTION"); aOptionTag.setAttribute("value", cacheTags[a].tag ? taglist + cacheTags[a].tag + " " : ""); aDL.appendChild(aOptionTag); } } aDatalist.appendChild(aDL); aTags.focus(); }, 10); if(booru.name != "Danbooru") return; reqTagTimer = setTimeout(function() { lookuptag = aTags.value.split(" ").pop(); if(!lookuptag) return; requestTag = xmlhttpRequest({ method : "GET", url : window.location.origin + "/tags.xml?search[hide_empty]=yes&search[order]=count&search[name_matches]=" + lookuptag + "*", headers : { "Accept" : "application/xml" }, overrideMimeType : "application/xml; charset=utf-8", onload : function(response) { xmldoc = domParser.parseFromString(response.responseText, "application/xml"); atags = xmldoc.evaluate("tags/tag/name", xmldoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); taglist = aTags.value.split(" "); taglist.pop(); taglist = taglist.join(" "); if(taglist.length > 0) taglist += " "; if(atags.snapshotItem(0)) while(aDatalist.hasChildNodes()) aDatalist.removeChild(aDatalist.firstChild); aDL = d.createElement("DATALIST"); // adding new datalist to main datalist updates the main list contents for(var a = 0; a < 6; a++) { atag = atags.snapshotItem(a); aOptionTag = d.createElement("OPTION"); aOptionTag.setAttribute("value", atag ? taglist + atag.textContent + " " : ""); aDL.appendChild(aOptionTag); } aDatalist.appendChild(aDL); aTags.focus(); } }); }, 3000); }, false); searchForm.appendChild(d.createTextNode(" ")); searchForm.appendChild(aBlacklink = d.createElement("A")); aBlacklink.setAttribute("href", "#"); aBlacklink.setAttribute("style", "color: red; float: right"); aBlacklink.setAttribute("title", "Blacklist"); aBlacklink.appendChild(d.createTextNode("[B]")); aBlacklink.addEventListener("click", function(e) { if(aBlacklist.style.getPropertyValue("opacity") == "0") { aBlacklist.style.setProperty("height", "15em", ""); aBlacklist.style.setProperty("opacity", "1", ""); } else { aBlacklist.style.setProperty("height", "0em", ""); aBlacklist.style.setProperty("opacity", "0", ""); } }, false); searchForm.appendChild(d.createElement("BR")); searchForm.appendChild(aBlacklist = d.createElement("TEXTAREA")); aBlacklist.setAttribute("style", "box-sizing: border-box; height: 0em; min-width: 100%; opacity: 0"); aBlacklist.appendChild(d.createTextNode(blacklist)); searchForm.appendChild(d.createElement("BR")); searchForm.appendChild(aReply = d.createElement("SPAN")); aReply.appendChild(d.createTextNode("Nobody here but us chickens!")); searchForm.appendChild(d.createElement("P")); // Slider aTableBar = d.createElement("TABLE"); aTableBar.appendChild(d.createElement("TR")); aTableBar.setAttribute("style", "border-collapse: collapse; border: 1px solid Black; width: 100%; padding: 0px"); aLeftBar = d.createElement("TD"); aLeftBar.setAttribute("style", "padding: 0px;"); aTableBar.firstChild.appendChild(aLeftBar); aCenterBar = d.createElement("TD"); aCenterBar.setAttribute("style", "border: 1px solid Black; padding: 1.5px 0px; background-color: WhiteSmoke; width: 25%; min-width: 1px;"); aTableBar.firstChild.appendChild(aCenterBar); aRightBar = d.createElement("TD"); aRightBar.setAttribute("style", "padding: 0px; width: 100%;"); aTableBar.firstChild.appendChild(aRightBar); searchForm.appendChild(aTableBar); // Search options aTable = d.createElement("TABLE"); aTr1 = d.createElement("TR"); aTd1 = d.createElement("TD"); aTd1.appendChild(d.createTextNode("Page:")); aTr1.appendChild(aTd1); aTd2 = d.createElement("TD"); aPage = d.createElement("INPUT"); aPage.setAttribute("type", "text"); aPage.setAttribute("size", "1"); aPage.setAttribute("value", page); aPage.setAttribute("tabindex", "3"); aTd2.appendChild(aPage); aTr1.appendChild(aTd2); aTd3 = d.createElement("TD"); aTd3.setAttribute("style", "text-align: left;"); aTd3.setAttribute("rowspan", "3"); aRS = d.createElement("INPUT"); aRS.setAttribute("type", "checkbox"); if(/s/.test(rating)) aRS.setAttribute("checked", "checked"); aLS = d.createElement("LABEL"); aLS.appendChild(aRS); aLS.appendChild(d.createTextNode("Safe")); aTd3.appendChild(aLS); aTd3.appendChild(d.createElement("BR")); aRQ = d.createElement("INPUT"); aRQ.setAttribute("type", "checkbox"); if(/q/.test(rating)) aRQ.setAttribute("checked", "checked"); aLQ = d.createElement("LABEL"); aLQ.appendChild(aRQ); aLQ.appendChild(d.createTextNode("Questionable")); aTd3.appendChild(aLQ); aTd3.appendChild(d.createElement("BR")); aRE = d.createElement("INPUT"); aRE.setAttribute("type", "checkbox"); if(/e/.test(rating)) aRE.setAttribute("checked", "checked"); aLE = d.createElement("LABEL"); aLE.appendChild(aRE); aLE.appendChild(d.createTextNode("Explicit")); aTd3.appendChild(aLE); aTd3.appendChild(d.createElement("BR")); aSD = d.createElement("INPUT"); if(booru.name == "Danbooru") { aSD.setAttribute("type", "checkbox"); if(/d/.test(rating)) aSD.setAttribute("checked", "checked"); aLD = d.createElement("LABEL"); aLD.appendChild(aSD); aLD.appendChild(d.createTextNode("Show deleted")); aTd3.appendChild(aLD); aTd3.appendChild(d.createElement("BR")); aSD.addEventListener("change", function(event) { for(var i in cacheHide) content.insertBefore(cacheHide[i], content.childNodes[cacheHide[i].style.getPropertyValue("order")]); setTimeout(function() { // transitions are stupid deletedlist = d.evaluate("//DIV[contains(@class, 'red')]", d, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); if(aSD.checked) { for(var n = 0, deleted = null; deleted = deletedlist.snapshotItem(n++); n) { deleted.style.setProperty("opacity", "1", ""); deleted.style.setProperty("padding", "1px", ""); deleted.style.setProperty("margin", "-1px 0px 0px -1px", ""); deleted.style.setProperty("border-width", "1px", ""); deleted.style.setProperty("min-width", "150px", ""); deleted.style.setProperty("max-width", "150px", ""); } } else { cacheHide.length = 0; for(var n = 0, deleted = null; deleted = deletedlist.snapshotItem(n++); n) { deleted.style.setProperty("opacity", "0", ""); deleted.style.setProperty("padding", "0px", ""); deleted.style.setProperty("margin", "0px", ""); deleted.style.setProperty("border-width", "0px", ""); deleted.style.setProperty("min-width", "0px", ""); deleted.style.setProperty("max-width", "0px", ""); cacheHide.push(deleted); } } }, 25); }, false); } else { rating.replace("d", ""); } aTr1.appendChild(aTd3); aTr2 = d.createElement("TR"); aTd4 = d.createElement("TD"); aTd4.appendChild(d.createTextNode("Images:")); aTr2.appendChild(aTd4); aTd5 = d.createElement("TD"); aImages = d.createElement("INPUT"); aImages.setAttribute("type", "text"); aImages.setAttribute("size", "1"); aImages.setAttribute("value", images); aImages.setAttribute("tabindex", "4"); aTd5.appendChild(aImages); aTr2.appendChild(aTd5); aTr3 = d.createElement("TR"); aTd6 = d.createElement("TD"); aTd6.setAttribute("colspan", "2"); aFIT = d.createElement("INPUT"); aFIT.setAttribute("type", "checkbox"); aFIT.setAttribute("tabindex", "5"); if(/f/.test(rating)) aFIT.setAttribute("checked", "checked"); aLF = d.createElement("LABEL"); aLF.appendChild(aFIT); aLF.appendChild(d.createTextNode("Fit width")); aTd6.appendChild(aLF); aTr3.appendChild(aTd6); aTable.appendChild(aTr1); aTable.appendChild(aTr2); aTable.appendChild(aTr3); searchForm.appendChild(aTable); searchForm.appendChild(d.createElement("HR")); searchForm.appendChild(aPrev = d.createElement("INPUT")); aPrev.setAttribute("type", "button"); aPrev.setAttribute("value", "<"); aPrev.setAttribute("disabled", "disabled"); aPrev.addEventListener("click", function(event) { search(tags, --page); }, false); searchForm.appendChild(aSearch = d.createElement("INPUT")); aSearch.setAttribute("type", "submit"); aSearch.setAttribute("value", "Search"); searchForm.appendChild(aNext = d.createElement("INPUT")); aNext.setAttribute("type", "button"); aNext.setAttribute("value", ">"); aNext.setAttribute("disabled", "disabled"); aNext.addEventListener("click", function(event) { search(tags, ++page); }, false); searchForm.appendChild(d.createElement("HR")); searchForm.appendChild(aTagsDisplay = d.createElement("DIV")); aTagsDisplay.setAttribute("style", "overflow-x: hidden; overflow-wrap: break-word;"); searchTd.appendChild(searchForm); searchTr.appendChild(imagesLayer = d.createElement("TD")); fixsize = (searchTd.getBoundingClientRect().right - searchTd.getBoundingClientRect().left) + "px"; searchTd.style.setProperty("min-width", fixsize, "important"); searchTd.style.setProperty("max-width", fixsize, "important"); // "Lightbox" overlay = d.createElement("DIV"); overlay.setAttribute("style", "display: none; position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; background-color: Black; opacity: 0.8;"); innerDisplay = d.createElement("DIV"); innerDisplay.setAttribute("id", "innerDisplay"); innerDisplay.setAttribute("style", "background-color: White; display: inline-table; padding: 10px; min-width: 200px; min-height: 200px; border-radius: 10px; margin: auto;"); display = d.createElement("DIV"); display.setAttribute("style", "display: none; position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; text-align: center; justify-content: space-between; overflow: auto;"); display.appendChild(innerDisplay); prevImage = d.createElement("DIV"); prevImage.setAttribute("id", "prevImage"); prevImage.setAttribute("style", "display: flex; justify-content: center; align-items: center; position: fixed; top: 0px; left: 0px; background-image: linear-gradient(to right, Black, Transparent); color: White; font-size: xx-large; width: 30px; height: 100%; opacity: 0;"); prevImage.setAttribute("class", "fa fa-chevron-left"); nextImage = d.createElement("DIV"); nextImage.setAttribute("id", "nextImage"); nextImage.setAttribute("style", "display: flex; justify-content: center; align-items: center; position: fixed; top: 0px; right: 0px; background-image: linear-gradient(to left, Black, Transparent); color: White; font-size: xx-large; width: 30px; height: 100%; opacity: 0;"); nextImage.setAttribute("class", "fa fa-chevron-right"); d.body.insertBefore(display, d.body.firstChild); d.body.insertBefore(overlay, d.body.firstChild); prevImage.addEventListener("click", function(e) { next = openimage.parentNode.previousElementSibling || openimage.parentNode.parentNode.lastChild; next.firstChild.firstChild.dispatchEvent(new MouseEvent("click", { "bubbles" : true, "cancelable": true })); }, false); prevImage.addEventListener("mouseover", function(e) { prevImage.style.setProperty("opacity", "1", ""); }, false); prevImage.addEventListener("mouseout", function(e) { prevImage.style.setProperty("opacity", "0", ""); }, false); nextImage.addEventListener("click", function(e) { next = openimage.parentNode.nextElementSibling || openimage.parentNode.parentNode.firstChild; next.firstChild.firstChild.dispatchEvent(new MouseEvent("click", { "bubbles" : true, "cancelable": true })); }, false); nextImage.addEventListener("mouseover", function(e) { nextImage.style.setProperty("opacity", "1", ""); }, false); nextImage.addEventListener("mouseout", function(e) { nextImage.style.setProperty("opacity", "0", ""); }, false); display.addEventListener("click", function(e) { if(e.target.id || d.evaluate("ancestor-or-self::div[contains(@id, 'bodynote')]", e.target, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue) return; d.body.style.setProperty("overflow", "auto", ""); overlay.style.setProperty("display", "none", ""); display.style.setProperty("display", "none", ""); while(innerDisplay.hasChildNodes()) innerDisplay.removeChild(innerDisplay.firstChild); }, false); display.addEventListener("dblclick", function(e) { d.body.style.setProperty("overflow", "auto", ""); overlay.style.setProperty("display", "none", ""); display.style.setProperty("display", "none", ""); while(innerDisplay.hasChildNodes()) innerDisplay.removeChild(innerDisplay.firstChild); }, false); // CTRL+S to save image var keylistener = function(event) { if(display.style.getPropertyValue("display") == "none") return else if(event.ctrlKey && event.keyCode == 83) { // CTRL+S event.preventDefault(); event.stopPropagation(); } else if(aFIT.checked && event.keyCode == 37) { // LEFT KEY next = openimage.parentNode.previousElementSibling || openimage.parentNode.parentNode.lastChild; next.firstChild.firstChild.dispatchEvent(new MouseEvent("click", { "bubbles" : true, "cancelable": true })); } else if(aFIT.checked && event.keyCode == 39) { // RIGHT KEY next = openimage.parentNode.nextElementSibling || openimage.parentNode.parentNode.firstChild; next.firstChild.firstChild.dispatchEvent(new MouseEvent("click", { "bubbles" : true, "cancelable": true })); } }; var saveimage = function(event) { if(event.ctrlKey && event.keyCode == 83) { // CTRL+S var sauce = openimage.href.match(/[^\/]+$/)[0]; if(/%/.test(sauce)) sauce = decodeURIComponent(sauce); var imgdown = d.createElement("A"); imgdown.setAttribute("download", sauce); imgdown.setAttribute("href", openimage.href); d.body.appendChild(imgdown); imgdown.dispatchEvent(new MouseEvent("click")); imgdown.remove(); } }; window.addEventListener("keydown", keylistener, false); window.addEventListener("keyup", saveimage, false); if(!window.location.origin) window.location.origin = (window.location.protocol + "//" + window.location.host); if(window.location.hash) search(window.location.hash.split("#")[1], 1); function search(newtags, newpage) { if(requestPost) requestPost.abort(); if(requestNote) requestNote.abort(); if(requestCount) requestCount.abort(); if(requestCache) requestCache.abort(); if(requestTag) requestTag.abort(); clearTimeout(tagTimer); clearTimeout(reqTagTimer); aTags.value = tags = newtags; aPage.value = page = newpage; aImages.value = images; blacklist = aBlacklist.value.trim(); aPrev.disabled = (newpage < 2); aRS.checked = /s/.test(rating); aRQ.checked = /q/.test(rating); aRE.checked = /e/.test(rating); if(/^s(?!q|e)/.test(rating)) newtags += " rating:safe"; if(/^q(?!e)/.test(rating)) newtags += " rating:questionable"; if(/^e/.test(rating)) newtags += " rating:explicit"; if(/^qe/.test(rating)) newtags += " -rating:safe"; if(/^se/.test(rating)) newtags += " -rating:questionable"; if(/^sq(?!e)/.test(rating)) newtags += " -rating:explicit"; if(imagesLayer.hasChildNodes()) imagesLayer.removeChild(imagesLayer.firstChild); while(aDatalist.hasChildNodes()) aDatalist.removeChild(aDatalist.firstChild); imagesLayer.appendChild(d.createTextNode("Loading...")); requestPost = xmlhttpRequest({ method : "GET", url : window.location.origin + booru.post + booru.query(encodeURIComponent(newtags.trim()), images, page), headers : { "Accept" : "application/xml" }, overrideMimeType : "application/xml; charset=utf-8", onload : function(response) { getContent(domParser.parseFromString(response.responseText, "application/xml"), newtags); } }); saveValues(); } function showContent(xmldoc) { if(imagesLayer.hasChildNodes()) imagesLayer.removeChild(imagesLayer.firstChild); aReply.textContent = "Nobody here but us chickens!"; var posts = xmldoc.evaluate("posts", xmldoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if(!posts) { reason = xmldoc.evaluate("response/@reason | result/text()", xmldoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if(reason) imagesLayer.textContent = reason.nodeValue; else imagesLayer.textContent = "Something broke."; return; } var post = xmldoc.evaluate("posts/post", xmldoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if(((parseInt(posts.getAttribute("offset"), 10) + 1) > posts.getAttribute("count")) & posts.getAttribute("count") > 0) { search(tags, Math.ceil(parseInt(posts.getAttribute("count"), 10) / images)); return; } if(posts.getAttribute("count") > 0) aReply.textContent = "Found " + posts.getAttribute("count") + ", showing " + (parseInt(posts.getAttribute("offset"), 10) + 1) + "-" + (Math.min(posts.getAttribute("count"), parseInt(posts.getAttribute("offset"), 10) + parseInt(images, 10))); aLeftBar.style.setProperty("width", (posts.getAttribute("offset") / posts.getAttribute("count") * 100) + "%"); aCenterBar.style.setProperty("width", (images / posts.getAttribute("count") * 100) + "%"); aNext.disabled = (page >= Math.ceil(posts.getAttribute("count") / images)); imagesLayer.appendChild(content = d.createElement("DIV")); content.setAttribute("style", "display: flex; flex-flow: row wrap;"); content.setAttribute("id", "content"); cacheHide.length = 0; for(var i = 0; i < images; i++) { data = post.snapshotItem(i); if(data) { if(!data.getAttribute("preview-file-url") && booru.name == "Danbooru") if(!checkCache(data)) continue; content.appendChild(thumb = d.createElement("DIV")); if(!!data.getAttribute("last-noted-at") && data.getAttribute("last-noted-at") != "false") data.setAttribute("has_notes", "true"); var image = d.createElement("IMG"); image.setAttribute("src", data.getAttribute("preview_url") || data.getAttribute("preview-file-url")); image.setAttribute("id", data.getAttribute("id")); image.setAttribute("alt", data.getAttribute("tag-string") || domParser.parseFromString(data.getAttribute("tags").trim(), "text/html").documentElement.textContent); image.setAttribute("title", image.getAttribute("alt") + " rating:" + data.getAttribute("rating").replace(/^e$/, "Explicit").replace(/s$/, "Safe").replace(/q$/, "Questionable") + " score:" + data.getAttribute("score")); if(/\.zip$/.test(data.getAttribute("file_url")) || /\.zip$/.test(data.getAttribute("file-url"))) { data.setAttribute("file_url", (data.getAttribute("preview_url") || data.getAttribute("file-url")).replace("preview/", "sample/sample-").replace("data/", "data/sample/sample-").replace(".zip", ".webm")); data.setAttribute("file-url", data.getAttribute("file_url")); } image.setAttribute("fullsize", data.getAttribute("file_url") || data.getAttribute("file-url")); image.setAttribute("fullwidth", data.getAttribute("width") || data.getAttribute("image-width")); image.setAttribute("fullheight", data.getAttribute("height") || data.getAttribute("image-height")); image.setAttribute("notes", data.getAttribute("has_notes")); if(data.getAttribute("last_noted_at")) image.setAttribute("notes", "true"); image.setAttribute("md5", data.getAttribute("md5")); // Show tags on sidebar image.addEventListener("click", function(event) { if(aTagsDisplay.hasChildNodes()) aTagsDisplay.removeChild(aTagsDisplay.firstChild); aTagsDisplay.appendChild(d.createElement("DIV")); var tagnames = this.getAttribute("alt").split(" "); for(var t = 0; t < tagnames.length; t++) { var taglink = d.createElement("A"); taglink.appendChild(d.createTextNode(tagnames[t])); taglink.setAttribute("href", "#" + tagnames[t]); taglink.addEventListener("click", function(event) { aTags.value = event.target.textContent; aPage.value = 1; aSearch.dispatchEvent(new MouseEvent("click")); event.preventDefault(); }, false); aTagsDisplay.firstChild.appendChild(taglink); aTagsDisplay.firstChild.appendChild(document.createElement("BR")); } // CTRL + click to show only the tags if(event.ctrlKey) return; // "Lightbox" by VIPPER ("How do I jQuery?") while(innerDisplay.hasChildNodes()) innerDisplay.removeChild(innerDisplay.firstChild); innerDisplay.appendChild(prevImage); innerDisplay.appendChild(nextImage); if(/\.swf$/.test(this.getAttribute("fullsize"))) fullsize = d.createElement("EMBED"); else if(/\.webm$|\.zip$|\.mp4$/.test(this.getAttribute("fullsize"))) { fullsize = d.createElement("VIDEO"); fullsize.setAttribute("controls", true); fullsize.setAttribute("loop", true); fullsize.setAttribute("autoplay", true); if(/\.zip$/.test(this.getAttribute("fullsize"))) this.setAttribute("fullsize", this.getAttribute("fullsize").replace("data/", "data/sample/sample-").replace(".zip", ".webm")); } else { fullsize = d.createElement("IMG"); fullsize.addEventListener("click", function(event) { noteDivs = d.evaluate("./DIV[starts-with(@id, 'note')]", innerDisplay, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); for(var n = 0, note = null; note = noteDivs.snapshotItem(n++); n) { if(note.style.getPropertyValue("visibility") == "visible") note.style.setProperty("visibility", "hidden", ""); else note.style.setProperty("visibility", "visible", ""); } }, false); } fullsize.setAttribute("src", this.getAttribute("fullsize")); fullsize.setAttribute("width", this.getAttribute("fullwidth")); fullsize.setAttribute("height", this.getAttribute("fullheight")); fullsize.setAttribute("id", this.getAttribute("id")); fullsize.setAttribute("notes", this.getAttribute("notes")); fullsize.setAttribute("md5", this.getAttribute("md5")); fullsize.style.setProperty("transition-property", "none", ""); var pagelink = d.createElement("A"); pagelink.setAttribute("href", booru.page + fullsize.getAttribute("id") + "?tags=" + encodeURIComponent(tags.trim())); pagelink.appendChild(d.createTextNode(fullsize.getAttribute("id"))); innerDisplay.appendChild(fullsize); innerDisplay.appendChild(d.createElement("BR")); innerDisplay.appendChild(pagelink); overlay.style.setProperty("display", "", ""); display.style.setProperty("display", "flex", ""); display.scrollTo(0, 0); d.body.style.setProperty("overflow", "hidden", ""); sampleRate = 1; clientH = parseInt(document.documentElement.clientHeight, 10); clientW = Math.min(parseInt(document.documentElement.clientWidth, 10), parseInt(document.body.clientWidth, 10)) - 20; if(aFIT.checked && this.getAttribute("fullwidth") > clientW) { if(parseInt(this.getAttribute("fullheight"), 10) + 40 > clientH) { sampleRate = (document.documentElement.clientWidth - (window.outerWidth - window.innerWidth) - 21) / this.getAttribute("fullwidth"); } else { sampleRate = (document.documentElement.clientWidth - 20) / this.getAttribute("fullwidth"); } fullsize.setAttribute("width", this.getAttribute("fullwidth") * sampleRate + "px"); // 100% fullsize.setAttribute("height", this.getAttribute("fullheight") * sampleRate + "px"); // auto } nextImage.style.setProperty("right", (display.scrollHeight > clientH ? (window.outerWidth - window.innerWidth + 1) : 0) + "px"); if(this.getAttribute("notes") == "true") //fullsize.addEventListener("load", function(e) { requestNote = xmlhttpRequest({ method : "GET", url : window.location.origin + booru.note + booru.query(null, null, null, fullsize.getAttribute("id")), headers : { "Accept" : "application/xml" }, overrideMimeType : "application/xml; charset=utf-8", onload : function(response) { getNotes(domParser.parseFromString(response.responseText, "application/xml"), response.responseURL); } }); //}, false); }, false); var link = d.createElement("A"); link.setAttribute("href", data.getAttribute("file_url") || data.getAttribute("file-url")); link.setAttribute("alt", data.getAttribute("id")); link.appendChild(image); link.addEventListener("click", function(event) { openimage = event.target.parentNode; // preload next images new Image().setAttribute("src", (openimage.parentNode.nextElementSibling || openimage.parentNode.parentNode.firstChild).firstChild.href); new Image().setAttribute("src", (openimage.parentNode.previousElementSibling || openimage.parentNode.parentNode.lastChild).firstChild.href); event.preventDefault(); }, false); thumb.classList.add("thumb"); if(booru.name == "Danbooru") thumb.style.setProperty("order", i, ""); thumb.appendChild(link); if(/true/.test(data.getAttribute("has_notes"))) thumb.classList.add("yellow"); if(/deleted/.test(data.getAttribute("status")) || /true/.test(data.getAttribute("is-deleted"))) { thumb.classList.add("red"); thumb.addEventListener("transitionend", function() { if(!aSD.checked) this.remove(); }, false); if(!aSD.checked) { thumb.style.setProperty("opacity", "0", ""); thumb.style.setProperty("padding", "0", ""); thumb.style.setProperty("margin", "0", ""); thumb.style.setProperty("border-width", "0px", ""); thumb.style.setProperty("min-width", "0px", ""); thumb.style.setProperty("max-width", "0px", ""); cacheHide.push(thumb); thumb.remove(); } } taglist = image.getAttribute("alt") + " "; blacklisted = blacklist.split("\n"); for(var b = 0; b < blacklisted.length; b++) { bLine = blacklisted[b].replace(/\s\s+/g, " ").trim().split(" "); // normalize var todelete = 0; for(var c = 0; c < bLine.length; c++) { if(bLine[c] == "") break; if(taglist.indexOf(bLine[c] + " ") >= 0) todelete++; } if(todelete == bLine.length) thumb.remove(); } } } } function getContent(xmldoc, newtags) { if(booru.name == "Danbooru") { // Inject the count where it should be by default... requestCount = xmlhttpRequest({ method : "GET", url : window.location.origin + "/counts/posts.xml" + booru.query(encodeURIComponent(newtags)), headers : { "Accept" : "application/xml" }, overrideMimeType : "application/xml; charset=utf-8", onload : function(response) { newxmldoc = domParser.parseFromString(response.responseText, "application/xml"); count = newxmldoc.evaluate("counts/posts", newxmldoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; posts = xmldoc.evaluate("posts", xmldoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if(posts) { posts.setAttribute("count", count ? count.textContent.trim() : "0"); posts.setAttribute("offset", (page - 1) * images); // processing of new XML back to legacy format, how did I even learn this? var xsltProcessor = new XSLTProcessor(); xsltProcessor.importStylesheet(domParser.parseFromString(xsltText, "text/xml")); xmldoc = xsltProcessor.transformToDocument(xmldoc); } showContent(xmldoc); } }); } else { if(booru.name == "Gelbooru") { // Gelbooru has new a new xml format? var xsltProcessor = new XSLTProcessor(); xsltProcessor.importStylesheet(domParser.parseFromString(xsltText, "text/xml")); xmldoc = xsltProcessor.transformToDocument(xmldoc); } showContent(xmldoc); } } function checkCache(data) { var cached = false; for(var i in cacheID) if(data.getAttribute("id") == cacheID[i].id && cacheID[i].file != "null") { // insert the cached url directly to data cached = true; data.setAttribute("file-url", cacheID[i].file); data.setAttribute("preview-file-url", "/data/preview/" + cacheID[i].file.match(/[a-f0-9]{32}/)[0] + ".jpg"); data.setAttribute("md5", cacheID[i].file.match(/[a-f0-9]{32}/)[0]); cacheID[i].expires = Math.abs(Date.now() + cacheExpires); break; } return cached; /* if(!cached) requestCache = xmlhttpRequest({ method : "GET", url : window.location.origin + "/posts/" + data.getAttribute("id"), headers : { "Accept" : "text/html" }, onload : function(response) { parsedPage = domParser.parseFromString(response.responseText, "text/html"); parsed = parsedPage.getElementById("image-container"); if(!parsed) return; id = parsed.getAttribute("data-id"); file = parsed.getAttribute("data-file-url"); if(/\.zip$/.test(file)) file = parsed.getAttribute("data-large-file-url"); cacheID.push({ id : id, file : file, expires : Math.abs(Date.now() + cacheExpires) }); image = d.getElementById(id); if(!image) // check the deleted images too for(var i in cacheHide) if(cacheHide[i].firstChild.firstChild.getAttribute("id") == id) image = cacheHide[i].firstChild.firstChild; if(!image) return; image.setAttribute("fullsize", file); image.setAttribute("src", "/data/preview/" + file.match(/[a-f0-9]{32}/)[0] + ".jpg"); image.setAttribute("md5", file.match(/[a-f0-9]{32}/)[0]); image.parentNode.setAttribute("href", file); } }); */ } function showNotes(note, id) { var offsetx = Math.max(10, fullsize.getBoundingClientRect().left); //+ (d.documentElement.scrollLeft || d.body.scrollLeft); var offsety = Math.max(10, fullsize.getBoundingClientRect().top); //+ (d.documentElement.scrollTop || d.body.scrollTop); var vp_bottom = Math.max(window.innerHeight, innerDisplay.getBoundingClientRect().bottom - innerDisplay.getBoundingClientRect().top); for(var i = 0, ndata = null; ndata = note.snapshotItem(i++); i) { if(id.match(/[0-9]+$/)[0] != fullsize.getAttribute("id")) continue; if(d.getElementById("note" + ndata.getAttribute("id"))) continue; var noteDiv = d.createElement("DIV"); noteDiv.setAttribute("id", "note" + ndata.getAttribute("id")); noteDiv.setAttribute("class", "trans"); noteDiv.setAttribute("style", "opacity: 0.4; transition: none !important;"); noteDiv.style.setProperty("left", ndata.getAttribute("x") * sampleRate + offsetx + "px", ""); noteDiv.style.setProperty("top", ndata.getAttribute("y") * sampleRate + offsety + "px", ""); noteDiv.style.setProperty("width", ndata.getAttribute("width") * sampleRate + "px", ""); noteDiv.style.setProperty("height", ndata.getAttribute("height") * sampleRate + "px", ""); noteDiv.addEventListener("mouseover", function(event) { noteclearTimer = setTimeout(function() { d.getElementById("body" + this.getAttribute("id")).style.setProperty("display", "", ""); }.bind(this), 100); }, false); noteDiv.addEventListener("mouseout", function(event) { noteclearTimer = setTimeout(function() { d.getElementById("body" + this.getAttribute("id")).style.setProperty("display", "none", ""); }.bind(this), 200); }, false); innerDisplay.appendChild(noteDiv); var noteBody = d.createElement("DIV"); noteBody.innerHTML = ndata.getAttribute("body"); noteBody.setAttribute("id", "bodynote" + ndata.getAttribute("id")); noteBody.setAttribute("class", "trans"); noteBody.setAttribute("style", "color: Black; text-align: left; padding: 4px; z-index: 1" + i + ";"); noteBody.addEventListener("mouseover", function(event) { clearTimeout(noteclearTimer); this.style.setProperty("display", "", ""); }, false); noteBody.addEventListener("mouseout", function(event) { this.style.setProperty("display", "none", ""); }, false); innerDisplay.appendChild(noteBody); // this sucks, find another method! var w = ndata.getAttribute("width") * sampleRate; var h = ndata.getAttribute("height") * sampleRate; if(w < h) { // FUCK YEAH XOR SWAP w ^= h; h ^= w; w ^= h; } while(w / h > ratio) { w -= ratio; h += ratio; } noteBody.style.setProperty("min-width", "-moz-min-content", ""); noteBody.style.setProperty("min-width", "-webkit-min-content", ""); noteBody.style.setProperty("max-width", w + "px", ""); ntop = (ndata.getAttribute("y") * sampleRate) + (ndata.getAttribute("height") * sampleRate) + offsety + 5; nheight = noteBody.getBoundingClientRect().bottom - noteBody.getBoundingClientRect().top; if(ntop + nheight > vp_bottom) noteBody.style.setProperty("top", vp_bottom - nheight + "px", ""); else noteBody.style.setProperty("top", ntop + "px", ""); noteBody.style.setProperty("left", ndata.getAttribute("x") * sampleRate + offsetx + "px", ""); noteBody.style.setProperty("display", "none", ""); } } function getNotes(xmldoc, id) { if(booru.name == "Danbooru") { // Parses the nodes as attributes for each note var xsltProcessor = new XSLTProcessor(); xsltProcessor.importStylesheet(domParser.parseFromString(xsltText, "text/xml")); xmldoc = xsltProcessor.transformToDocument(xmldoc); showNotes(xmldoc.evaluate("notes/note", xmldoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null), id); } else { showNotes(xmldoc.evaluate("notes/note[@is_active = 'true']", xmldoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null), id); } } function saveValues() { // sanitize and save as string tags = ("" + tags).replace(",", ""); images = ("" + images).replace(",", ""); page = ("" + page).replace(",", ""); rating = ("" + rating).replace(",", ""); blacklist = ("" + blacklist).replace(",", ""); var cachedTags = tags.trim().split(" "); var cached = false; for(var i in cachedTags) { for(var j in cacheTags) if(cachedTags[i] == cacheTags[j].tag) { delete cacheTags[j]; break; } cacheTags.push({ tag : cachedTags[i], expires : Date.now() + cacheExpires }); } } function setStorage() { saveValues(); // double sure that it was saved if(!checkStorage()) return; storage.setItem("dai-tags", tags); storage.setItem("dai-images", images); storage.setItem("dai-page", page); storage.setItem("dai-rating", rating); storage.setItem("dai-blacklist", blacklist); var cachedID = ""; for(var i in cacheID) // save as a shorter CSV string : id,file,expires, cachedID += cacheID[i].id + "," + cacheID[i].file + "," + cacheID[i].expires + ","; storage.setItem("dai-cacheID", cachedID); var cachedTags = ""; for(var i in cacheTags) // save as a shorter CSV string : tag,expires, cachedTags += cacheTags[i].tag ? cacheTags[i].tag + "," + cacheTags[i].expires + "," : ""; storage.setItem("dai-cacheTags", cachedTags); } function getStorage() { // load as string tags = storage.getItem("dai-tags") || tags; images = parseInt(storage.getItem("dai-images") || images, 10); page = parseInt(storage.getItem("dai-page") || page, 10); rating = storage.getItem("dai-rating") || rating; blacklist = storage.getItem("dai-blacklist") || blacklist; var cachedID = (storage.getItem("dai-cacheID") || "").split(","); for(var i = 0; i < cachedID.length - 1; i += 3) // load the cached ids if(cachedID[i+2] > Date.now()) // but only if not expired cacheID.push({ id : cachedID[i], file : cachedID[i+1], expires : cachedID[i+2] }); var cachedTags = (storage.getItem("dai-cacheTags") || "").split(","); for(var i = 0; i < cachedTags.length - 1; i += 2) // load the cached tags if(cachedTags[i+1] > Date.now()) // but only if not expired cacheTags.push({ tag : cachedTags[i], expires : cachedTags[i+1] }); } function checkStorage() { try { storage = window.localStorage, test = "__storage_test__"; storage.setItem(test, test); storage.removeItem(test); return true; } catch(e) { console.log(e); if(e == QUOTA_EXCEEDED_ERR) { // not my fault storage.setItem("dai-cacheID", ""); storage.setItem("dai-cacheTags", ""); } return false; } } function xmlhttpRequest(request) { var xReq = new XMLHttpRequest(); if(!!request.overrideMimeType) xReq.overrideMimeType(request.overrideMimeType); xReq.open(request.method, request.url, true); if(!!request.headers) Object.getOwnPropertyNames(request.headers).forEach(function(header) { xReq.setRequestHeader(header, request.headers[header]); }); xReq.onreadystatechange = function(e) { if(xReq.readyState == 4) if(xReq.status > 0) request.onload(xReq); }; xReq.send(null); return xReq; } d.head.appendChild(dai_css); // clear old GM values GM_deleteValue("tags"); GM_deleteValue("images"); GM_deleteValue("page"); GM_deleteValue("rating"); GM_deleteValue("column");