Danbooru Ajax Interface

New interface to search images on Booru-style sites.

  1. // ==UserScript==
  2. // @name Danbooru Ajax Interface
  3. // @namespace http://danbooru.donmai.us
  4. // @description New interface to search images on Booru-style sites.
  5. // @match *://danbooru.donmai.us/
  6. // @match *://danbooru.donmai.us/#*
  7. // @match *://*.gelbooru.com/
  8. // @match *://*.gelbooru.com/#*
  9. // @match *://konachan.com/
  10. // @match *://konachan.com/#*
  11. // @match *://konachan.net/
  12. // @match *://konachan.net/#*
  13. // @match *://yande.re/post
  14. // @match *://yande.re/post#*
  15. // @version 4.6692016
  16. // @grant GM_deleteValue
  17. // ==/UserScript==
  18.  
  19. // NEW SITES CAN BE ADDED NOW
  20. var sites = [
  21. {
  22. name : "Danbooru", // Site name
  23. site : "donmai\.us", // Regular expression check on current url
  24. post : "/posts.xml", // Relative url to xml post API
  25. note : "/notes.xml", // Relative url to xml note API
  26. list : "/posts/", // Relative url to post listing
  27. page : "/posts/", // Relative url to post page
  28. query : function(tags, images, page, postid) { // Query passed to API
  29. return (postid ? "?limit=99999&search[is_active]=true&search[post_id]=" + postid : "?tags=" + tags + (page ? "&page=" + page + "&limit=" + images : ""));
  30. }
  31. },
  32. {
  33. name : "Gelbooru",
  34. site : "(www\.)?gelbooru\.",
  35. post : "/index.php?page=dapi&s=post&q=index",
  36. note : "/index.php?page=dapi&s=note&q=index&post_id=",
  37. list : "/index.php?page=post&s=list",
  38. page : "/index.php?page=post&s=view&id=",
  39. query : function(tags, images, page, postid) {
  40. return (postid ? postid : "&tags=" + tags + "&limit=" + images + "&pid=" + (page - 1));
  41. }
  42. },
  43. {
  44. name : "Konachan",
  45. site : "konachan\.",
  46. post : "/post.xml",
  47. note : "/note.xml?post_id=",
  48. list : "/post/",
  49. page : "/post/show/",
  50. query : function(tags, images, page, postid) {
  51. return (postid ? postid : "?tags=" + tags + "&limit=" + images + "&page=" + page);
  52. }
  53. },
  54. {
  55. name : "Yande.re",
  56. site : "yande\.re",
  57. post : "/post.xml",
  58. note : "/note.xml?post_id=",
  59. list : "/post/",
  60. page : "/post/show/",
  61. query : function(tags, images, page, postid) {
  62. return (postid ? postid : "?tags=" + tags + "&limit=" + images + "&page=" + page);
  63. }
  64. }
  65. ];
  66.  
  67. // CONSTANTS
  68. const ratio = ((1 + Math.sqrt(5)) / 2);
  69. const d = document;
  70. const cacheExpires = 1000 * 60 * 60 * 24 * 7; // cache expires in 7 days : (ms * s * m * h * d)
  71.  
  72. // PAGE CLEANING
  73. while(d.documentElement.firstChild)
  74. d.documentElement.removeChild(d.documentElement.firstChild);
  75.  
  76. // IMPORTANT VARIABLES
  77. var booru, storage, requestPost, requestNote, requestCount, requestCache, requestTag, reqTagTimer, tagTimer, noteclearTimer, content;
  78. var tags = "", images = 20, page = 1, rating = "s", sampleRate = 1, blacklist = "spoilers \nguro \nscat ";
  79. var cacheHide = [], cacheID = [], cacheTags = [];
  80. var domParser = new DOMParser();
  81. 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>";
  82.  
  83. for(var i = 0; i < sites.length; i++)
  84. if(new RegExp(sites[i].site).test(window.location.hostname))
  85. booru = sites[i];
  86.  
  87. if(checkStorage()) {
  88. getStorage();
  89. window.addEventListener("unload", setStorage); // save last search when page is changed/closed
  90. }
  91.  
  92. // SCRIPT STARTS HERE
  93. d.documentElement.appendChild(d.createElement("HEAD"));
  94. d.documentElement.appendChild(d.createElement("BODY"));
  95. d.documentElement.firstChild.appendChild(title = d.createElement("TITLE"));
  96. title.appendChild(d.createTextNode(booru.name));
  97.  
  98. var fa = d.createElement("LINK");
  99. fa.setAttribute("rel", "stylesheet");
  100. fa.setAttribute("href", "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css");
  101. d.head.appendChild(fa);
  102.  
  103. var dai_css = d.createElement("STYLE");
  104. dai_css.setAttribute("type", "text/css");
  105. 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; }"));
  106.  
  107. d.body.appendChild(searchTable = d.createElement("TABLE"));
  108. searchTable.appendChild(searchTr = d.createElement("TR"));
  109. searchTr.setAttribute("style", "vertical-align: top;");
  110. searchTr.appendChild(searchTd = d.createElement("TD"));
  111. searchTd.setAttribute("style", "text-align: center;");
  112.  
  113. searchForm = d.createElement("FORM");
  114. searchForm.addEventListener("submit", function(event) {
  115. tags = aTags.value;
  116. page = Math.max(1, parseInt(aPage.value, 10));
  117. images = Math.max(1, Math.min(parseInt(aImages.value, 10), 200));
  118. rating = (aRS.checked ? "s" : "") + (aRQ.checked ? "q" : "") + (aRE.checked ? "e" : "") + (aFIT.checked ? "f" : "") + (aSD && aSD.checked ? "d" : "");
  119. search(tags, page);
  120. event.preventDefault();
  121. }, false);
  122.  
  123. searchForm.appendChild(aLink = d.createElement("A"));
  124. aLink.setAttribute("href", booru.list);
  125. aLink.setAttribute("tabindex", "1");
  126. aLink.appendChild(d.createTextNode(booru.name));
  127.  
  128. searchForm.appendChild(d.createElement("BR"));
  129.  
  130. searchForm.appendChild(aTags = d.createElement("INPUT"));
  131. aTags.setAttribute("type", "text");
  132. aTags.setAttribute("style", "width: 80%");
  133. aTags.setAttribute("value", tags);
  134. aTags.setAttribute("tabindex", "2");
  135. aTags.setAttribute("list", "autocomplete");
  136. aTags.addEventListener("change", function(event) {
  137. aPage.value = 1;
  138. }, false);
  139.  
  140. searchForm.appendChild(aDatalist = d.createElement("DATALIST"));
  141. aDatalist.setAttribute("id", "autocomplete");
  142. if(cacheTags.length == 0) {
  143. popularTags = "touhou kantai_collection idolmaster vocaloid fate_(series) mahou_shoujo_madoka_magica".split(" ").reverse();
  144. for(var i in popularTags) {
  145. cacheTags.push({ tag : popularTags[i], expires : Date.now() + cacheExpires });
  146. }
  147. }
  148. for(var i = cacheTags.length - 1; i >= Math.max(cacheTags.length - 6, 0); i--) { // last 6 added values first
  149. var tagoption = d.createElement("OPTION");
  150. tagoption.setAttribute("value", cacheTags[i].tag + " ");
  151. aDatalist.appendChild(tagoption);
  152. }
  153.  
  154. aTags.addEventListener("input", function(e) {
  155. clearTimeout(tagTimer);
  156. clearTimeout(reqTagTimer);
  157. if(requestTag)
  158. requestTag.abort();
  159. tagTimer = setTimeout(function() {
  160. lookuptag = aTags.value.split(" ").pop();
  161. if(!lookuptag)
  162. return;
  163. taglist = aTags.value.split(" ");
  164. taglist.pop();
  165. taglist = taglist.join(" ");
  166. if(taglist.length > 0)
  167. taglist += " ";
  168.  
  169. while(aDatalist.hasChildNodes())
  170. aDatalist.removeChild(aDatalist.firstChild);
  171. aDL = d.createElement("DATALIST"); // adding new datalist to main datalist updates the main list contents
  172. for(var a = cacheTags.length - 1; a >= 0 && aDL.childNodes.length < 6; a--) {
  173. if(cacheTags[a] && cacheTags[a].tag.toLowerCase().startsWith(lookuptag.toLowerCase())) {
  174. aOptionTag = d.createElement("OPTION");
  175. aOptionTag.setAttribute("value", cacheTags[a].tag ? taglist + cacheTags[a].tag + " " : "");
  176. aDL.appendChild(aOptionTag);
  177. }
  178. }
  179. aDatalist.appendChild(aDL);
  180. aTags.focus();
  181. }, 10);
  182. if(booru.name != "Danbooru")
  183. return;
  184. reqTagTimer = setTimeout(function() {
  185. lookuptag = aTags.value.split(" ").pop();
  186. if(!lookuptag)
  187. return;
  188. requestTag = xmlhttpRequest({
  189. method : "GET",
  190. url : window.location.origin + "/tags.xml?search[hide_empty]=yes&search[order]=count&search[name_matches]=" + lookuptag + "*",
  191. headers : {
  192. "Accept" : "application/xml"
  193. },
  194. overrideMimeType : "application/xml; charset=utf-8",
  195. onload : function(response) {
  196. xmldoc = domParser.parseFromString(response.responseText, "application/xml");
  197. atags = xmldoc.evaluate("tags/tag/name", xmldoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  198.  
  199. taglist = aTags.value.split(" ");
  200. taglist.pop();
  201. taglist = taglist.join(" ");
  202. if(taglist.length > 0)
  203. taglist += " ";
  204. if(atags.snapshotItem(0))
  205. while(aDatalist.hasChildNodes())
  206. aDatalist.removeChild(aDatalist.firstChild);
  207. aDL = d.createElement("DATALIST"); // adding new datalist to main datalist updates the main list contents
  208. for(var a = 0; a < 6; a++) {
  209. atag = atags.snapshotItem(a);
  210. aOptionTag = d.createElement("OPTION");
  211. aOptionTag.setAttribute("value", atag ? taglist + atag.textContent + " " : "");
  212. aDL.appendChild(aOptionTag);
  213. }
  214. aDatalist.appendChild(aDL);
  215. aTags.focus();
  216. }
  217. });
  218. }, 3000);
  219. }, false);
  220.  
  221. searchForm.appendChild(d.createTextNode(" "));
  222. searchForm.appendChild(aBlacklink = d.createElement("A"));
  223. aBlacklink.setAttribute("href", "#");
  224. aBlacklink.setAttribute("style", "color: red; float: right");
  225. aBlacklink.setAttribute("title", "Blacklist");
  226. aBlacklink.appendChild(d.createTextNode("[B]"));
  227. aBlacklink.addEventListener("click", function(e) {
  228. if(aBlacklist.style.getPropertyValue("opacity") == "0") {
  229. aBlacklist.style.setProperty("height", "15em", "");
  230. aBlacklist.style.setProperty("opacity", "1", "");
  231. } else {
  232. aBlacklist.style.setProperty("height", "0em", "");
  233. aBlacklist.style.setProperty("opacity", "0", "");
  234. }
  235. }, false);
  236.  
  237. searchForm.appendChild(d.createElement("BR"));
  238. searchForm.appendChild(aBlacklist = d.createElement("TEXTAREA"));
  239. aBlacklist.setAttribute("style", "box-sizing: border-box; height: 0em; min-width: 100%; opacity: 0");
  240. aBlacklist.appendChild(d.createTextNode(blacklist));
  241.  
  242. searchForm.appendChild(d.createElement("BR"));
  243. searchForm.appendChild(aReply = d.createElement("SPAN"));
  244. aReply.appendChild(d.createTextNode("Nobody here but us chickens!"));
  245.  
  246. searchForm.appendChild(d.createElement("P"));
  247.  
  248. // Slider
  249. aTableBar = d.createElement("TABLE");
  250. aTableBar.appendChild(d.createElement("TR"));
  251. aTableBar.setAttribute("style", "border-collapse: collapse; border: 1px solid Black; width: 100%; padding: 0px");
  252. aLeftBar = d.createElement("TD");
  253. aLeftBar.setAttribute("style", "padding: 0px;");
  254. aTableBar.firstChild.appendChild(aLeftBar);
  255. aCenterBar = d.createElement("TD");
  256. aCenterBar.setAttribute("style", "border: 1px solid Black; padding: 1.5px 0px; background-color: WhiteSmoke; width: 25%; min-width: 1px;");
  257. aTableBar.firstChild.appendChild(aCenterBar);
  258. aRightBar = d.createElement("TD");
  259. aRightBar.setAttribute("style", "padding: 0px; width: 100%;");
  260. aTableBar.firstChild.appendChild(aRightBar);
  261. searchForm.appendChild(aTableBar);
  262.  
  263. // Search options
  264. aTable = d.createElement("TABLE");
  265. aTr1 = d.createElement("TR");
  266.  
  267. aTd1 = d.createElement("TD");
  268. aTd1.appendChild(d.createTextNode("Page:"));
  269. aTr1.appendChild(aTd1);
  270.  
  271. aTd2 = d.createElement("TD");
  272. aPage = d.createElement("INPUT");
  273. aPage.setAttribute("type", "text");
  274. aPage.setAttribute("size", "1");
  275. aPage.setAttribute("value", page);
  276. aPage.setAttribute("tabindex", "3");
  277. aTd2.appendChild(aPage);
  278. aTr1.appendChild(aTd2);
  279.  
  280. aTd3 = d.createElement("TD");
  281. aTd3.setAttribute("style", "text-align: left;");
  282. aTd3.setAttribute("rowspan", "3");
  283.  
  284. aRS = d.createElement("INPUT");
  285. aRS.setAttribute("type", "checkbox");
  286. if(/s/.test(rating))
  287. aRS.setAttribute("checked", "checked");
  288. aLS = d.createElement("LABEL");
  289. aLS.appendChild(aRS);
  290. aLS.appendChild(d.createTextNode("Safe"));
  291.  
  292. aTd3.appendChild(aLS);
  293. aTd3.appendChild(d.createElement("BR"));
  294.  
  295. aRQ = d.createElement("INPUT");
  296. aRQ.setAttribute("type", "checkbox");
  297. if(/q/.test(rating))
  298. aRQ.setAttribute("checked", "checked");
  299. aLQ = d.createElement("LABEL");
  300. aLQ.appendChild(aRQ);
  301. aLQ.appendChild(d.createTextNode("Questionable"));
  302.  
  303. aTd3.appendChild(aLQ);
  304. aTd3.appendChild(d.createElement("BR"));
  305.  
  306. aRE = d.createElement("INPUT");
  307. aRE.setAttribute("type", "checkbox");
  308. if(/e/.test(rating))
  309. aRE.setAttribute("checked", "checked");
  310. aLE = d.createElement("LABEL");
  311. aLE.appendChild(aRE);
  312. aLE.appendChild(d.createTextNode("Explicit"));
  313.  
  314. aTd3.appendChild(aLE);
  315. aTd3.appendChild(d.createElement("BR"));
  316.  
  317. aSD = d.createElement("INPUT");
  318. if(booru.name == "Danbooru") {
  319. aSD.setAttribute("type", "checkbox");
  320. if(/d/.test(rating))
  321. aSD.setAttribute("checked", "checked");
  322. aLD = d.createElement("LABEL");
  323. aLD.appendChild(aSD);
  324. aLD.appendChild(d.createTextNode("Show deleted"));
  325.  
  326. aTd3.appendChild(aLD);
  327. aTd3.appendChild(d.createElement("BR"));
  328.  
  329. aSD.addEventListener("change", function(event) {
  330. for(var i in cacheHide)
  331. content.insertBefore(cacheHide[i], content.childNodes[cacheHide[i].style.getPropertyValue("order")]);
  332.  
  333. setTimeout(function() { // transitions are stupid
  334. deletedlist = d.evaluate("//DIV[contains(@class, 'red')]", d, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  335. if(aSD.checked) {
  336. for(var n = 0, deleted = null; deleted = deletedlist.snapshotItem(n++); n) {
  337. deleted.style.setProperty("opacity", "1", "");
  338. deleted.style.setProperty("padding", "1px", "");
  339. deleted.style.setProperty("margin", "-1px 0px 0px -1px", "");
  340. deleted.style.setProperty("border-width", "1px", "");
  341. deleted.style.setProperty("min-width", "150px", "");
  342. deleted.style.setProperty("max-width", "150px", "");
  343. }
  344. } else {
  345. cacheHide.length = 0;
  346. for(var n = 0, deleted = null; deleted = deletedlist.snapshotItem(n++); n) {
  347. deleted.style.setProperty("opacity", "0", "");
  348. deleted.style.setProperty("padding", "0px", "");
  349. deleted.style.setProperty("margin", "0px", "");
  350. deleted.style.setProperty("border-width", "0px", "");
  351. deleted.style.setProperty("min-width", "0px", "");
  352. deleted.style.setProperty("max-width", "0px", "");
  353. cacheHide.push(deleted);
  354. }
  355. }
  356. }, 25);
  357. }, false);
  358. } else {
  359. rating.replace("d", "");
  360. }
  361. aTr1.appendChild(aTd3);
  362.  
  363. aTr2 = d.createElement("TR");
  364.  
  365. aTd4 = d.createElement("TD");
  366. aTd4.appendChild(d.createTextNode("Images:"));
  367. aTr2.appendChild(aTd4);
  368.  
  369. aTd5 = d.createElement("TD");
  370. aImages = d.createElement("INPUT");
  371. aImages.setAttribute("type", "text");
  372. aImages.setAttribute("size", "1");
  373. aImages.setAttribute("value", images);
  374. aImages.setAttribute("tabindex", "4");
  375. aTd5.appendChild(aImages);
  376. aTr2.appendChild(aTd5);
  377.  
  378. aTr3 = d.createElement("TR");
  379.  
  380. aTd6 = d.createElement("TD");
  381. aTd6.setAttribute("colspan", "2");
  382. aFIT = d.createElement("INPUT");
  383. aFIT.setAttribute("type", "checkbox");
  384. aFIT.setAttribute("tabindex", "5");
  385. if(/f/.test(rating))
  386. aFIT.setAttribute("checked", "checked");
  387. aLF = d.createElement("LABEL");
  388. aLF.appendChild(aFIT);
  389. aLF.appendChild(d.createTextNode("Fit width"));
  390.  
  391. aTd6.appendChild(aLF);
  392. aTr3.appendChild(aTd6);
  393.  
  394. aTable.appendChild(aTr1);
  395. aTable.appendChild(aTr2);
  396. aTable.appendChild(aTr3);
  397. searchForm.appendChild(aTable);
  398.  
  399. searchForm.appendChild(d.createElement("HR"));
  400.  
  401. searchForm.appendChild(aPrev = d.createElement("INPUT"));
  402. aPrev.setAttribute("type", "button");
  403. aPrev.setAttribute("value", "<");
  404. aPrev.setAttribute("disabled", "disabled");
  405. aPrev.addEventListener("click", function(event) {
  406. search(tags, --page);
  407. }, false);
  408.  
  409. searchForm.appendChild(aSearch = d.createElement("INPUT"));
  410. aSearch.setAttribute("type", "submit");
  411. aSearch.setAttribute("value", "Search");
  412.  
  413. searchForm.appendChild(aNext = d.createElement("INPUT"));
  414. aNext.setAttribute("type", "button");
  415. aNext.setAttribute("value", ">");
  416. aNext.setAttribute("disabled", "disabled");
  417. aNext.addEventListener("click", function(event) {
  418. search(tags, ++page);
  419. }, false);
  420.  
  421. searchForm.appendChild(d.createElement("HR"));
  422.  
  423. searchForm.appendChild(aTagsDisplay = d.createElement("DIV"));
  424. aTagsDisplay.setAttribute("style", "overflow-x: hidden; overflow-wrap: break-word;");
  425.  
  426. searchTd.appendChild(searchForm);
  427. searchTr.appendChild(imagesLayer = d.createElement("TD"));
  428.  
  429. fixsize = (searchTd.getBoundingClientRect().right - searchTd.getBoundingClientRect().left) + "px";
  430. searchTd.style.setProperty("min-width", fixsize, "important");
  431. searchTd.style.setProperty("max-width", fixsize, "important");
  432.  
  433. // "Lightbox"
  434. overlay = d.createElement("DIV");
  435. overlay.setAttribute("style", "display: none; position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; background-color: Black; opacity: 0.8;");
  436. innerDisplay = d.createElement("DIV");
  437. innerDisplay.setAttribute("id", "innerDisplay");
  438. innerDisplay.setAttribute("style", "background-color: White; display: inline-table; padding: 10px; min-width: 200px; min-height: 200px; border-radius: 10px; margin: auto;");
  439. display = d.createElement("DIV");
  440. display.setAttribute("style", "display: none; position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; text-align: center; justify-content: space-between; overflow: auto;");
  441. display.appendChild(innerDisplay);
  442.  
  443. prevImage = d.createElement("DIV");
  444. prevImage.setAttribute("id", "prevImage");
  445. 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;");
  446. prevImage.setAttribute("class", "fa fa-chevron-left");
  447.  
  448. nextImage = d.createElement("DIV");
  449. nextImage.setAttribute("id", "nextImage");
  450. 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;");
  451. nextImage.setAttribute("class", "fa fa-chevron-right");
  452.  
  453. d.body.insertBefore(display, d.body.firstChild);
  454. d.body.insertBefore(overlay, d.body.firstChild);
  455.  
  456. prevImage.addEventListener("click", function(e) {
  457. next = openimage.parentNode.previousElementSibling || openimage.parentNode.parentNode.lastChild;
  458. next.firstChild.firstChild.dispatchEvent(new MouseEvent("click", { "bubbles" : true, "cancelable": true }));
  459. }, false);
  460. prevImage.addEventListener("mouseover", function(e) {
  461. prevImage.style.setProperty("opacity", "1", "");
  462. }, false);
  463. prevImage.addEventListener("mouseout", function(e) {
  464. prevImage.style.setProperty("opacity", "0", "");
  465. }, false);
  466.  
  467. nextImage.addEventListener("click", function(e) {
  468. next = openimage.parentNode.nextElementSibling || openimage.parentNode.parentNode.firstChild;
  469. next.firstChild.firstChild.dispatchEvent(new MouseEvent("click", { "bubbles" : true, "cancelable": true }));
  470. }, false);
  471. nextImage.addEventListener("mouseover", function(e) {
  472. nextImage.style.setProperty("opacity", "1", "");
  473. }, false);
  474. nextImage.addEventListener("mouseout", function(e) {
  475. nextImage.style.setProperty("opacity", "0", "");
  476. }, false);
  477.  
  478. display.addEventListener("click", function(e) {
  479. if(e.target.id || d.evaluate("ancestor-or-self::div[contains(@id, 'bodynote')]", e.target, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue)
  480. return;
  481. d.body.style.setProperty("overflow", "auto", "");
  482. overlay.style.setProperty("display", "none", "");
  483. display.style.setProperty("display", "none", "");
  484. while(innerDisplay.hasChildNodes())
  485. innerDisplay.removeChild(innerDisplay.firstChild);
  486. }, false);
  487.  
  488. display.addEventListener("dblclick", function(e) {
  489. d.body.style.setProperty("overflow", "auto", "");
  490. overlay.style.setProperty("display", "none", "");
  491. display.style.setProperty("display", "none", "");
  492. while(innerDisplay.hasChildNodes())
  493. innerDisplay.removeChild(innerDisplay.firstChild);
  494. }, false);
  495.  
  496. // CTRL+S to save image
  497. var keylistener = function(event) {
  498. if(display.style.getPropertyValue("display") == "none")
  499. return
  500. else
  501. if(event.ctrlKey && event.keyCode == 83) { // CTRL+S
  502. event.preventDefault();
  503. event.stopPropagation();
  504. } else
  505. if(aFIT.checked && event.keyCode == 37) { // LEFT KEY
  506. next = openimage.parentNode.previousElementSibling || openimage.parentNode.parentNode.lastChild;
  507. next.firstChild.firstChild.dispatchEvent(new MouseEvent("click", { "bubbles" : true, "cancelable": true }));
  508. } else
  509. if(aFIT.checked && event.keyCode == 39) { // RIGHT KEY
  510. next = openimage.parentNode.nextElementSibling || openimage.parentNode.parentNode.firstChild;
  511. next.firstChild.firstChild.dispatchEvent(new MouseEvent("click", { "bubbles" : true, "cancelable": true }));
  512. }
  513. };
  514. var saveimage = function(event) {
  515. if(event.ctrlKey && event.keyCode == 83) { // CTRL+S
  516. var sauce = openimage.href.match(/[^\/]+$/)[0];
  517. if(/%/.test(sauce))
  518. sauce = decodeURIComponent(sauce);
  519. var imgdown = d.createElement("A");
  520. imgdown.setAttribute("download", sauce);
  521. imgdown.setAttribute("href", openimage.href);
  522. d.body.appendChild(imgdown);
  523. imgdown.dispatchEvent(new MouseEvent("click"));
  524. imgdown.remove();
  525. }
  526. };
  527. window.addEventListener("keydown", keylistener, false);
  528. window.addEventListener("keyup", saveimage, false);
  529.  
  530. if(!window.location.origin)
  531. window.location.origin = (window.location.protocol + "//" + window.location.host);
  532.  
  533. if(window.location.hash)
  534. search(window.location.hash.split("#")[1], 1);
  535.  
  536. function search(newtags, newpage) {
  537. if(requestPost)
  538. requestPost.abort();
  539. if(requestNote)
  540. requestNote.abort();
  541. if(requestCount)
  542. requestCount.abort();
  543. if(requestCache)
  544. requestCache.abort();
  545. if(requestTag)
  546. requestTag.abort();
  547.  
  548. clearTimeout(tagTimer);
  549. clearTimeout(reqTagTimer);
  550.  
  551. aTags.value = tags = newtags;
  552. aPage.value = page = newpage;
  553. aImages.value = images;
  554. blacklist = aBlacklist.value.trim();
  555.  
  556. aPrev.disabled = (newpage < 2);
  557. aRS.checked = /s/.test(rating);
  558. aRQ.checked = /q/.test(rating);
  559. aRE.checked = /e/.test(rating);
  560.  
  561. if(/^s(?!q|e)/.test(rating))
  562. newtags += " rating:safe";
  563. if(/^q(?!e)/.test(rating))
  564. newtags += " rating:questionable";
  565. if(/^e/.test(rating))
  566. newtags += " rating:explicit";
  567. if(/^qe/.test(rating))
  568. newtags += " -rating:safe";
  569. if(/^se/.test(rating))
  570. newtags += " -rating:questionable";
  571. if(/^sq(?!e)/.test(rating))
  572. newtags += " -rating:explicit";
  573.  
  574. if(imagesLayer.hasChildNodes())
  575. imagesLayer.removeChild(imagesLayer.firstChild);
  576. while(aDatalist.hasChildNodes())
  577. aDatalist.removeChild(aDatalist.firstChild);
  578.  
  579. imagesLayer.appendChild(d.createTextNode("Loading..."));
  580. requestPost = xmlhttpRequest({
  581. method : "GET",
  582. url : window.location.origin + booru.post + booru.query(encodeURIComponent(newtags.trim()), images, page),
  583. headers : {
  584. "Accept" : "application/xml"
  585. },
  586. overrideMimeType : "application/xml; charset=utf-8",
  587. onload : function(response) {
  588. getContent(domParser.parseFromString(response.responseText, "application/xml"), newtags);
  589. }
  590. });
  591. saveValues();
  592. }
  593.  
  594. function showContent(xmldoc) {
  595. if(imagesLayer.hasChildNodes())
  596. imagesLayer.removeChild(imagesLayer.firstChild);
  597.  
  598. aReply.textContent = "Nobody here but us chickens!";
  599.  
  600. var posts = xmldoc.evaluate("posts", xmldoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  601. if(!posts) {
  602. reason = xmldoc.evaluate("response/@reason | result/text()", xmldoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  603. if(reason)
  604. imagesLayer.textContent = reason.nodeValue;
  605. else
  606. imagesLayer.textContent = "Something broke.";
  607. return;
  608. }
  609. var post = xmldoc.evaluate("posts/post", xmldoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  610. if(((parseInt(posts.getAttribute("offset"), 10) + 1) > posts.getAttribute("count")) & posts.getAttribute("count") > 0) {
  611. search(tags, Math.ceil(parseInt(posts.getAttribute("count"), 10) / images));
  612. return;
  613. }
  614.  
  615. if(posts.getAttribute("count") > 0)
  616. 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)));
  617. aLeftBar.style.setProperty("width", (posts.getAttribute("offset") / posts.getAttribute("count") * 100) + "%");
  618. aCenterBar.style.setProperty("width", (images / posts.getAttribute("count") * 100) + "%");
  619.  
  620. aNext.disabled = (page >= Math.ceil(posts.getAttribute("count") / images));
  621.  
  622. imagesLayer.appendChild(content = d.createElement("DIV"));
  623. content.setAttribute("style", "display: flex; flex-flow: row wrap;");
  624. content.setAttribute("id", "content");
  625. cacheHide.length = 0;
  626.  
  627. for(var i = 0; i < images; i++) {
  628. data = post.snapshotItem(i);
  629. if(data) {
  630. if(!data.getAttribute("preview-file-url") && booru.name == "Danbooru")
  631. if(!checkCache(data))
  632. continue;
  633. content.appendChild(thumb = d.createElement("DIV"));
  634. if(!!data.getAttribute("last-noted-at") && data.getAttribute("last-noted-at") != "false")
  635. data.setAttribute("has_notes", "true");
  636. var image = d.createElement("IMG");
  637. image.setAttribute("src", data.getAttribute("preview_url") || data.getAttribute("preview-file-url"));
  638. image.setAttribute("id", data.getAttribute("id"));
  639. image.setAttribute("alt", data.getAttribute("tag-string") || domParser.parseFromString(data.getAttribute("tags").trim(), "text/html").documentElement.textContent);
  640. image.setAttribute("title", image.getAttribute("alt") + " rating:" + data.getAttribute("rating").replace(/^e$/, "Explicit").replace(/s$/, "Safe").replace(/q$/, "Questionable") + " score:" + data.getAttribute("score"));
  641.  
  642. if(/\.zip$/.test(data.getAttribute("file_url")) || /\.zip$/.test(data.getAttribute("file-url"))) {
  643. data.setAttribute("file_url", (data.getAttribute("preview_url") || data.getAttribute("file-url")).replace("preview/", "sample/sample-").replace("data/", "data/sample/sample-").replace(".zip", ".webm"));
  644. data.setAttribute("file-url", data.getAttribute("file_url"));
  645. }
  646. image.setAttribute("fullsize", data.getAttribute("file_url") || data.getAttribute("file-url"));
  647. image.setAttribute("fullwidth", data.getAttribute("width") || data.getAttribute("image-width"));
  648. image.setAttribute("fullheight", data.getAttribute("height") || data.getAttribute("image-height"));
  649. image.setAttribute("notes", data.getAttribute("has_notes"));
  650. if(data.getAttribute("last_noted_at"))
  651. image.setAttribute("notes", "true");
  652. image.setAttribute("md5", data.getAttribute("md5"));
  653.  
  654. // Show tags on sidebar
  655. image.addEventListener("click", function(event) {
  656. if(aTagsDisplay.hasChildNodes())
  657. aTagsDisplay.removeChild(aTagsDisplay.firstChild);
  658. aTagsDisplay.appendChild(d.createElement("DIV"));
  659.  
  660. var tagnames = this.getAttribute("alt").split(" ");
  661. for(var t = 0; t < tagnames.length; t++) {
  662. var taglink = d.createElement("A");
  663. taglink.appendChild(d.createTextNode(tagnames[t]));
  664. taglink.setAttribute("href", "#" + tagnames[t]);
  665. taglink.addEventListener("click", function(event) {
  666. aTags.value = event.target.textContent;
  667. aPage.value = 1;
  668. aSearch.dispatchEvent(new MouseEvent("click"));
  669. event.preventDefault();
  670. }, false);
  671. aTagsDisplay.firstChild.appendChild(taglink);
  672. aTagsDisplay.firstChild.appendChild(document.createElement("BR"));
  673. }
  674.  
  675. // CTRL + click to show only the tags
  676. if(event.ctrlKey)
  677. return;
  678.  
  679. // "Lightbox" by VIPPER ("How do I jQuery?")
  680. while(innerDisplay.hasChildNodes())
  681. innerDisplay.removeChild(innerDisplay.firstChild);
  682.  
  683. innerDisplay.appendChild(prevImage);
  684. innerDisplay.appendChild(nextImage);
  685.  
  686. if(/\.swf$/.test(this.getAttribute("fullsize")))
  687. fullsize = d.createElement("EMBED");
  688. else if(/\.webm$|\.zip$|\.mp4$/.test(this.getAttribute("fullsize"))) {
  689. fullsize = d.createElement("VIDEO");
  690. fullsize.setAttribute("controls", true);
  691. fullsize.setAttribute("loop", true);
  692. fullsize.setAttribute("autoplay", true);
  693. if(/\.zip$/.test(this.getAttribute("fullsize")))
  694. this.setAttribute("fullsize", this.getAttribute("fullsize").replace("data/", "data/sample/sample-").replace(".zip", ".webm"));
  695. } else {
  696. fullsize = d.createElement("IMG");
  697. fullsize.addEventListener("click", function(event) {
  698. noteDivs = d.evaluate("./DIV[starts-with(@id, 'note')]", innerDisplay, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  699. for(var n = 0, note = null; note = noteDivs.snapshotItem(n++); n) {
  700. if(note.style.getPropertyValue("visibility") == "visible")
  701. note.style.setProperty("visibility", "hidden", "");
  702. else
  703. note.style.setProperty("visibility", "visible", "");
  704. }
  705. }, false);
  706. }
  707. fullsize.setAttribute("src", this.getAttribute("fullsize"));
  708. fullsize.setAttribute("width", this.getAttribute("fullwidth"));
  709. fullsize.setAttribute("height", this.getAttribute("fullheight"));
  710. fullsize.setAttribute("id", this.getAttribute("id"));
  711. fullsize.setAttribute("notes", this.getAttribute("notes"));
  712. fullsize.setAttribute("md5", this.getAttribute("md5"));
  713. fullsize.style.setProperty("transition-property", "none", "");
  714.  
  715. var pagelink = d.createElement("A");
  716. pagelink.setAttribute("href", booru.page + fullsize.getAttribute("id") + "?tags=" + encodeURIComponent(tags.trim()));
  717. pagelink.appendChild(d.createTextNode(fullsize.getAttribute("id")));
  718.  
  719. innerDisplay.appendChild(fullsize);
  720. innerDisplay.appendChild(d.createElement("BR"));
  721. innerDisplay.appendChild(pagelink);
  722.  
  723. overlay.style.setProperty("display", "", "");
  724. display.style.setProperty("display", "flex", "");
  725. display.scrollTo(0, 0);
  726. d.body.style.setProperty("overflow", "hidden", "");
  727.  
  728. sampleRate = 1;
  729. clientH = parseInt(document.documentElement.clientHeight, 10);
  730. clientW = Math.min(parseInt(document.documentElement.clientWidth, 10), parseInt(document.body.clientWidth, 10)) - 20;
  731.  
  732. if(aFIT.checked && this.getAttribute("fullwidth") > clientW) {
  733. if(parseInt(this.getAttribute("fullheight"), 10) + 40 > clientH) {
  734. sampleRate = (document.documentElement.clientWidth - (window.outerWidth - window.innerWidth) - 21) / this.getAttribute("fullwidth");
  735. } else {
  736. sampleRate = (document.documentElement.clientWidth - 20) / this.getAttribute("fullwidth");
  737. }
  738. fullsize.setAttribute("width", this.getAttribute("fullwidth") * sampleRate + "px"); // 100%
  739. fullsize.setAttribute("height", this.getAttribute("fullheight") * sampleRate + "px"); // auto
  740. }
  741. nextImage.style.setProperty("right", (display.scrollHeight > clientH ? (window.outerWidth - window.innerWidth + 1) : 0) + "px");
  742.  
  743. if(this.getAttribute("notes") == "true")
  744. //fullsize.addEventListener("load", function(e) {
  745. requestNote = xmlhttpRequest({
  746. method : "GET",
  747. url : window.location.origin + booru.note + booru.query(null, null, null, fullsize.getAttribute("id")),
  748. headers : {
  749. "Accept" : "application/xml"
  750. },
  751. overrideMimeType : "application/xml; charset=utf-8",
  752. onload : function(response) {
  753. getNotes(domParser.parseFromString(response.responseText, "application/xml"), response.responseURL);
  754. }
  755. });
  756. //}, false);
  757. }, false);
  758.  
  759. var link = d.createElement("A");
  760. link.setAttribute("href", data.getAttribute("file_url") || data.getAttribute("file-url"));
  761. link.setAttribute("alt", data.getAttribute("id"));
  762. link.appendChild(image);
  763. link.addEventListener("click", function(event) {
  764. openimage = event.target.parentNode;
  765. // preload next images
  766. new Image().setAttribute("src", (openimage.parentNode.nextElementSibling || openimage.parentNode.parentNode.firstChild).firstChild.href);
  767. new Image().setAttribute("src", (openimage.parentNode.previousElementSibling || openimage.parentNode.parentNode.lastChild).firstChild.href);
  768. event.preventDefault();
  769. }, false);
  770.  
  771. thumb.classList.add("thumb");
  772. if(booru.name == "Danbooru")
  773. thumb.style.setProperty("order", i, "");
  774. thumb.appendChild(link);
  775. if(/true/.test(data.getAttribute("has_notes")))
  776. thumb.classList.add("yellow");
  777. if(/deleted/.test(data.getAttribute("status")) || /true/.test(data.getAttribute("is-deleted"))) {
  778. thumb.classList.add("red");
  779. thumb.addEventListener("transitionend", function() {
  780. if(!aSD.checked)
  781. this.remove();
  782. }, false);
  783. if(!aSD.checked) {
  784. thumb.style.setProperty("opacity", "0", "");
  785. thumb.style.setProperty("padding", "0", "");
  786. thumb.style.setProperty("margin", "0", "");
  787. thumb.style.setProperty("border-width", "0px", "");
  788. thumb.style.setProperty("min-width", "0px", "");
  789. thumb.style.setProperty("max-width", "0px", "");
  790. cacheHide.push(thumb);
  791. thumb.remove();
  792. }
  793. }
  794. taglist = image.getAttribute("alt") + " ";
  795. blacklisted = blacklist.split("\n");
  796. for(var b = 0; b < blacklisted.length; b++) {
  797. bLine = blacklisted[b].replace(/\s\s+/g, " ").trim().split(" "); // normalize
  798. var todelete = 0;
  799. for(var c = 0; c < bLine.length; c++) {
  800. if(bLine[c] == "")
  801. break;
  802. if(taglist.indexOf(bLine[c] + " ") >= 0)
  803. todelete++;
  804. }
  805. if(todelete == bLine.length)
  806. thumb.remove();
  807. }
  808. }
  809. }
  810. }
  811.  
  812. function getContent(xmldoc, newtags) {
  813. if(booru.name == "Danbooru") { // Inject the count where it should be by default...
  814. requestCount = xmlhttpRequest({
  815. method : "GET",
  816. url : window.location.origin + "/counts/posts.xml" + booru.query(encodeURIComponent(newtags)),
  817. headers : {
  818. "Accept" : "application/xml"
  819. },
  820. overrideMimeType : "application/xml; charset=utf-8",
  821. onload : function(response) {
  822. newxmldoc = domParser.parseFromString(response.responseText, "application/xml");
  823. count = newxmldoc.evaluate("counts/posts", newxmldoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  824. posts = xmldoc.evaluate("posts", xmldoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  825. if(posts) {
  826. posts.setAttribute("count", count ? count.textContent.trim() : "0");
  827. posts.setAttribute("offset", (page - 1) * images);
  828. // processing of new XML back to legacy format, how did I even learn this?
  829. var xsltProcessor = new XSLTProcessor();
  830. xsltProcessor.importStylesheet(domParser.parseFromString(xsltText, "text/xml"));
  831. xmldoc = xsltProcessor.transformToDocument(xmldoc);
  832. }
  833. showContent(xmldoc);
  834. }
  835. });
  836. } else {
  837. if(booru.name == "Gelbooru") { // Gelbooru has new a new xml format?
  838. var xsltProcessor = new XSLTProcessor();
  839. xsltProcessor.importStylesheet(domParser.parseFromString(xsltText, "text/xml"));
  840. xmldoc = xsltProcessor.transformToDocument(xmldoc);
  841. }
  842. showContent(xmldoc);
  843. }
  844. }
  845.  
  846. function checkCache(data) {
  847. var cached = false;
  848. for(var i in cacheID)
  849. if(data.getAttribute("id") == cacheID[i].id && cacheID[i].file != "null") { // insert the cached url directly to data
  850. cached = true;
  851. data.setAttribute("file-url", cacheID[i].file);
  852. data.setAttribute("preview-file-url", "/data/preview/" + cacheID[i].file.match(/[a-f0-9]{32}/)[0] + ".jpg");
  853. data.setAttribute("md5", cacheID[i].file.match(/[a-f0-9]{32}/)[0]);
  854. cacheID[i].expires = Math.abs(Date.now() + cacheExpires);
  855. break;
  856. }
  857. return cached;
  858. /*
  859. if(!cached)
  860. requestCache = xmlhttpRequest({
  861. method : "GET",
  862. url : window.location.origin + "/posts/" + data.getAttribute("id"),
  863. headers : {
  864. "Accept" : "text/html"
  865. },
  866. onload : function(response) {
  867. parsedPage = domParser.parseFromString(response.responseText, "text/html");
  868. parsed = parsedPage.getElementById("image-container");
  869. if(!parsed)
  870. return;
  871. id = parsed.getAttribute("data-id");
  872. file = parsed.getAttribute("data-file-url");
  873. if(/\.zip$/.test(file))
  874. file = parsed.getAttribute("data-large-file-url");
  875. cacheID.push({ id : id, file : file, expires : Math.abs(Date.now() + cacheExpires) });
  876. image = d.getElementById(id);
  877. if(!image) // check the deleted images too
  878. for(var i in cacheHide)
  879. if(cacheHide[i].firstChild.firstChild.getAttribute("id") == id)
  880. image = cacheHide[i].firstChild.firstChild;
  881. if(!image)
  882. return;
  883. image.setAttribute("fullsize", file);
  884. image.setAttribute("src", "/data/preview/" + file.match(/[a-f0-9]{32}/)[0] + ".jpg");
  885. image.setAttribute("md5", file.match(/[a-f0-9]{32}/)[0]);
  886. image.parentNode.setAttribute("href", file);
  887. }
  888. });
  889. */
  890. }
  891.  
  892. function showNotes(note, id) {
  893. var offsetx = Math.max(10, fullsize.getBoundingClientRect().left); //+ (d.documentElement.scrollLeft || d.body.scrollLeft);
  894. var offsety = Math.max(10, fullsize.getBoundingClientRect().top); //+ (d.documentElement.scrollTop || d.body.scrollTop);
  895. var vp_bottom = Math.max(window.innerHeight, innerDisplay.getBoundingClientRect().bottom - innerDisplay.getBoundingClientRect().top);
  896.  
  897. for(var i = 0, ndata = null; ndata = note.snapshotItem(i++); i) {
  898. if(id.match(/[0-9]+$/)[0] != fullsize.getAttribute("id"))
  899. continue;
  900. if(d.getElementById("note" + ndata.getAttribute("id")))
  901. continue;
  902.  
  903. var noteDiv = d.createElement("DIV");
  904. noteDiv.setAttribute("id", "note" + ndata.getAttribute("id"));
  905. noteDiv.setAttribute("class", "trans");
  906. noteDiv.setAttribute("style", "opacity: 0.4; transition: none !important;");
  907. noteDiv.style.setProperty("left", ndata.getAttribute("x") * sampleRate + offsetx + "px", "");
  908. noteDiv.style.setProperty("top", ndata.getAttribute("y") * sampleRate + offsety + "px", "");
  909. noteDiv.style.setProperty("width", ndata.getAttribute("width") * sampleRate + "px", "");
  910. noteDiv.style.setProperty("height", ndata.getAttribute("height") * sampleRate + "px", "");
  911. noteDiv.addEventListener("mouseover", function(event) {
  912. noteclearTimer = setTimeout(function() {
  913. d.getElementById("body" + this.getAttribute("id")).style.setProperty("display", "", "");
  914. }.bind(this), 100);
  915. }, false);
  916. noteDiv.addEventListener("mouseout", function(event) {
  917. noteclearTimer = setTimeout(function() {
  918. d.getElementById("body" + this.getAttribute("id")).style.setProperty("display", "none", "");
  919. }.bind(this), 200);
  920. }, false);
  921. innerDisplay.appendChild(noteDiv);
  922.  
  923. var noteBody = d.createElement("DIV");
  924. noteBody.innerHTML = ndata.getAttribute("body");
  925. noteBody.setAttribute("id", "bodynote" + ndata.getAttribute("id"));
  926. noteBody.setAttribute("class", "trans");
  927. noteBody.setAttribute("style", "color: Black; text-align: left; padding: 4px; z-index: 1" + i + ";");
  928. noteBody.addEventListener("mouseover", function(event) {
  929. clearTimeout(noteclearTimer);
  930. this.style.setProperty("display", "", "");
  931. }, false);
  932. noteBody.addEventListener("mouseout", function(event) {
  933. this.style.setProperty("display", "none", "");
  934. }, false);
  935. innerDisplay.appendChild(noteBody);
  936.  
  937. // this sucks, find another method!
  938. var w = ndata.getAttribute("width") * sampleRate;
  939. var h = ndata.getAttribute("height") * sampleRate;
  940. if(w < h) { // FUCK YEAH XOR SWAP
  941. w ^= h;
  942. h ^= w;
  943. w ^= h;
  944. }
  945. while(w / h > ratio) {
  946. w -= ratio;
  947. h += ratio;
  948. }
  949.  
  950. noteBody.style.setProperty("min-width", "-moz-min-content", "");
  951. noteBody.style.setProperty("min-width", "-webkit-min-content", "");
  952. noteBody.style.setProperty("max-width", w + "px", "");
  953.  
  954. ntop = (ndata.getAttribute("y") * sampleRate) + (ndata.getAttribute("height") * sampleRate) + offsety + 5;
  955. nheight = noteBody.getBoundingClientRect().bottom - noteBody.getBoundingClientRect().top;
  956. if(ntop + nheight > vp_bottom)
  957. noteBody.style.setProperty("top", vp_bottom - nheight + "px", "");
  958. else
  959. noteBody.style.setProperty("top", ntop + "px", "");
  960.  
  961. noteBody.style.setProperty("left", ndata.getAttribute("x") * sampleRate + offsetx + "px", "");
  962. noteBody.style.setProperty("display", "none", "");
  963. }
  964. }
  965.  
  966. function getNotes(xmldoc, id) {
  967. if(booru.name == "Danbooru") { // Parses the nodes as attributes for each note
  968. var xsltProcessor = new XSLTProcessor();
  969. xsltProcessor.importStylesheet(domParser.parseFromString(xsltText, "text/xml"));
  970. xmldoc = xsltProcessor.transformToDocument(xmldoc);
  971. showNotes(xmldoc.evaluate("notes/note", xmldoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null), id);
  972. } else {
  973. showNotes(xmldoc.evaluate("notes/note[@is_active = 'true']", xmldoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null), id);
  974. }
  975. }
  976.  
  977. function saveValues() { // sanitize and save as string
  978. tags = ("" + tags).replace(",", "");
  979. images = ("" + images).replace(",", "");
  980. page = ("" + page).replace(",", "");
  981. rating = ("" + rating).replace(",", "");
  982. blacklist = ("" + blacklist).replace(",", "");
  983. var cachedTags = tags.trim().split(" ");
  984. var cached = false;
  985. for(var i in cachedTags) {
  986. for(var j in cacheTags)
  987. if(cachedTags[i] == cacheTags[j].tag) {
  988. delete cacheTags[j];
  989. break;
  990. }
  991. cacheTags.push({ tag : cachedTags[i], expires : Date.now() + cacheExpires });
  992. }
  993. }
  994.  
  995. function setStorage() {
  996. saveValues(); // double sure that it was saved
  997. if(!checkStorage())
  998. return;
  999. storage.setItem("dai-tags", tags);
  1000. storage.setItem("dai-images", images);
  1001. storage.setItem("dai-page", page);
  1002. storage.setItem("dai-rating", rating);
  1003. storage.setItem("dai-blacklist", blacklist);
  1004. var cachedID = "";
  1005. for(var i in cacheID) // save as a shorter CSV string : id,file,expires,
  1006. cachedID += cacheID[i].id + "," + cacheID[i].file + "," + cacheID[i].expires + ",";
  1007. storage.setItem("dai-cacheID", cachedID);
  1008. var cachedTags = "";
  1009. for(var i in cacheTags) // save as a shorter CSV string : tag,expires,
  1010. cachedTags += cacheTags[i].tag ? cacheTags[i].tag + "," + cacheTags[i].expires + "," : "";
  1011. storage.setItem("dai-cacheTags", cachedTags);
  1012. }
  1013.  
  1014. function getStorage() { // load as string
  1015. tags = storage.getItem("dai-tags") || tags;
  1016. images = parseInt(storage.getItem("dai-images") || images, 10);
  1017. page = parseInt(storage.getItem("dai-page") || page, 10);
  1018. rating = storage.getItem("dai-rating") || rating;
  1019. blacklist = storage.getItem("dai-blacklist") || blacklist;
  1020. var cachedID = (storage.getItem("dai-cacheID") || "").split(",");
  1021. for(var i = 0; i < cachedID.length - 1; i += 3) // load the cached ids
  1022. if(cachedID[i+2] > Date.now()) // but only if not expired
  1023. cacheID.push({ id : cachedID[i], file : cachedID[i+1], expires : cachedID[i+2] });
  1024. var cachedTags = (storage.getItem("dai-cacheTags") || "").split(",");
  1025. for(var i = 0; i < cachedTags.length - 1; i += 2) // load the cached tags
  1026. if(cachedTags[i+1] > Date.now()) // but only if not expired
  1027. cacheTags.push({ tag : cachedTags[i], expires : cachedTags[i+1] });
  1028. }
  1029.  
  1030. function checkStorage() {
  1031. try {
  1032. storage = window.localStorage, test = "__storage_test__";
  1033. storage.setItem(test, test);
  1034. storage.removeItem(test);
  1035. return true;
  1036. } catch(e) {
  1037. console.log(e);
  1038. if(e == QUOTA_EXCEEDED_ERR) { // not my fault
  1039. storage.setItem("dai-cacheID", "");
  1040. storage.setItem("dai-cacheTags", "");
  1041. }
  1042. return false;
  1043. }
  1044. }
  1045.  
  1046. function xmlhttpRequest(request) {
  1047. var xReq = new XMLHttpRequest();
  1048. if(!!request.overrideMimeType)
  1049. xReq.overrideMimeType(request.overrideMimeType);
  1050. xReq.open(request.method, request.url, true);
  1051. if(!!request.headers)
  1052. Object.getOwnPropertyNames(request.headers).forEach(function(header) {
  1053. xReq.setRequestHeader(header, request.headers[header]);
  1054. });
  1055. xReq.onreadystatechange = function(e) {
  1056. if(xReq.readyState == 4)
  1057. if(xReq.status > 0)
  1058. request.onload(xReq);
  1059. };
  1060. xReq.send(null);
  1061. return xReq;
  1062. }
  1063.  
  1064. d.head.appendChild(dai_css);
  1065.  
  1066. // clear old GM values
  1067. GM_deleteValue("tags");
  1068. GM_deleteValue("images");
  1069. GM_deleteValue("page");
  1070. GM_deleteValue("rating");
  1071. GM_deleteValue("column");