Danbooru Ajax Interface

New interface to search images on Booru-style sites.

2017-07-05 या दिनांकाला. सर्वात नवीन आवृत्ती पाहा.

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