Gelbooru Endless Scroll

Adds endless scroll function to Gelbooru

Versão de: 05/06/2017. Veja: a última versão.

  1. // ==UserScript==
  2. // @id gelbooru-endless-scroll
  3. // @name Gelbooru Endless Scroll
  4. // @version 1.6.7
  5. // @namespace intermission
  6. // @author intermission
  7. // @license WTFPL; http://www.wtfpl.net/about/
  8. // @description Adds endless scroll function to Gelbooru
  9. // @include http://gelbooru.com/index.php?*
  10. // @include https://gelbooru.com/index.php?*
  11. // @include http://rule34.xxx/index.php?*
  12. // @include https://rule34.xxx/index.php?*
  13. // @include http://*.booru.org/index.php?*
  14. // @include https://*.booru.org/index.php?*
  15. // @include http://e621.net/post/index/*
  16. // @include https://e621.net/post/index/*
  17. // @include http://rule34.paheal.net/post/list/*
  18. // @include https://rule34.paheal.net/post/list/*
  19. // @run-at document-end
  20. // @grant none
  21. // ==/UserScript==
  22.  
  23. /* This program is free software. It comes without any warranty, to
  24. * the extent permitted by applicable law. You can redistribute it
  25. * and/or modify it under the terms of the Do What The Fuck You Want
  26. * To Public License, Version 2, as published by Sam Hocevar. See
  27. * http://www.wtfpl.net/ for more details. */
  28.  
  29. (function(){
  30. "use strict";
  31. var d = document, url, host = location.hostname.match(/[^\.]+\.[^\.]+$/)[0],
  32. v = ".thumb",
  33. vis = function() {
  34. var rect = target().getBoundingClientRect();
  35. return rect.x === 0 && rect.y === 0 ? false :
  36. rect.top + rect.height >= 0 &&
  37. document.documentElement.clientHeight - rect.bottom + rect.height >= 0;
  38. }, total,
  39. page = function(doc) {
  40. try {
  41. var images, pageNo = d.createElement("paheal.net" === host ? "div" : "span"), frag = d.createDocumentFragment(), container,
  42. fn = (e) => {
  43. (e = e.target.parentNode.parentNode).style.overflow = "";
  44. if (!e.getAttribute("style")) e.removeAttribute("style");
  45. };
  46. images = [...doc.querySelectorAll(host === "yande.re" ? "li[id^='p'][class*='creator-id-']" : v)];
  47. if (images.length === 0)
  48. throw Error("API error");
  49. pageNo.innerHTML = '<span style="height:' + ("paheal.net" === host ? "180px" : "inherit") + ';display:flex;flex-direction:column;">Page ' + url.index + '~<span style="margin:auto 0 30px">out of ' + total + "</span></span>";
  50. pageNo.className = "thumb";
  51. if ("paheal.net" === host)
  52. pageNo.setAttribute("style", "transform: translateY(-180px); margin-bottom: -180px");
  53. frag.appendChild(pageNo);
  54. for (let a of images) {
  55. let img = a.querySelector("img");
  56. a.style.overflow = "hidden";
  57. img.addEventListener("load", fn, { once : true });
  58. frag.appendChild(a);
  59. }
  60. container = d.querySelector(v).parentNode;
  61. if (container.lastElementChild.tagName === "SPAN") container.appendChild(frag);
  62. else container.insertBefore(frag, container.querySelector(v + ":last-of-type + *"));
  63. target().classList.remove("loadingu");
  64. if (paginator.go) paginator();
  65. if (list.length > 0) {
  66. events(true);
  67. process();
  68. } return;
  69. } catch(err) { console.error(err); }
  70. }, req = _ => fetch(url.href).then(
  71. x => x.text().then(
  72. text => page((new DOMParser()).parseFromString(text, "text/html"))
  73. )
  74. ).catch(err => {
  75. target().classList.remove("loadingu");
  76. if (typeof err !== "undefined") {
  77. if (_.attempt < 10) {
  78. console.error(`An error occured, ${~_.attempt + 11} retries left\n`, err.message, err.stack);
  79. ++_.attempt;
  80. setTimeout(req, 5000, _);
  81. } else
  82. console.error("Maximum number of retries reached\n", err.message, err.stack);
  83. }
  84. }), process = function(_override) {
  85. if (vis() || (typeof _override === "boolean" ? _override : false)) {
  86. target().classList.add("loadingu");
  87. events();
  88. url = list.shift();
  89. return req({attempt: 0});
  90. }
  91. }, events = function(_on) {
  92. var name = _on ? "addEventListener" : "removeEventListener", obj = { passive : true };
  93. ["scroll", "resize", "visibilitychange"].forEach(evt => window[name](evt, process, obj));
  94. }, list = [], r = /(page=|pid=|index\/)([0-9]+)|(list\/(?:[^\/]+\/)?)([0-9]+)$/, paginator = function() {
  95. var el = target(), rect1 = d.querySelector(".thumb:last-of-type"),
  96. el2 = d.querySelector(".sidebar"), rect2;
  97. if (el.classList.contains("pagination")) el = el.parentNode;
  98. rect1 = rect1.offsetTop + rect1.offsetHeight;
  99. rect2 = el2.offsetTop + el2.offsetHeight - 9;
  100. if (rect2 >= rect1) {
  101. el.style.position = "absolute";
  102. el.style.top = rect1 + "px";
  103. el.style.left = "calc(50vw + " + el2.getBoundingClientRect().width / 2 + "px)";
  104. el.style.transform = 'translateX(-50%)';
  105. } else {
  106. el.style.position = "";
  107. el.style.top = "";
  108. el.style.left = "";
  109. el.style.transform = "";
  110. paginator.go = false;
  111. }
  112. };
  113. const target = () => d.querySelector("div.pagination") || d.querySelector("div#paginator") || d.querySelector("section#paginator");
  114. {
  115. let style = d.createElement("style");
  116. style.appendChild(d.createTextNode(`.loadingu {
  117. position: relative;
  118. }
  119. .loadingu::after {
  120. content: "" ! important;
  121. display: block;
  122. position: absolute;
  123. top: 0;
  124. left: 0;
  125. width: 100%;
  126. height: 100%;
  127. min-width: 34px;
  128. min-height: 34px;
  129. background: rgba(0,0,0,.5) no-repeat center center url(data:image/svg+xml,%3Csvg%20width%3D%2234px%22%20height%3D%2234px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%20100%22%20preserveAspectRatio%3D%22xMidYMid%22%20class%3D%22uil-ring-alt%22%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%22100%22%20height%3D%22100%22%20fill%3D%22none%22%20class%3D%22bk%22%3E%3C%2Frect%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2240%22%20stroke%3D%22%23b4b197%22%20fill%3D%22none%22%20stroke-width%3D%2210%22%20stroke-linecap%3D%22round%22%3E%3C%2Fcircle%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2240%22%20stroke%3D%22%23f4efcc%22%20fill%3D%22none%22%20stroke-width%3D%226%22%20stroke-linecap%3D%22round%22%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20from%3D%220%22%20to%3D%22502%22%3E%3C%2Fanimate%3E%3Canimate%20attributeName%3D%22stroke-dasharray%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20values%3D%22150.6%20100.4%3B1%20250%3B150.6%20100.4%22%3E%3C%2Fanimate%3E%3C%2Fcircle%3E%3C%2Fsvg%3E);
  130. }`));
  131. d.head.appendChild(style);
  132. }
  133. if (target() && /\/post\/?|page=post/.test(location.href)) {
  134. let pid = location.href.match(r),
  135. p = ((pid && pid[1] === "index/" || host === "yande.re") ? target().lastElementChild.previousElementSibling : target().lastElementChild).href,
  136. n = d.querySelectorAll(v).length,
  137. start_index, end_index, increment = 1, index = 0;
  138. if (host === "paheal.net") {
  139. let ayy = [...target().children[0].children];
  140. p = ayy[ayy.indexOf(ayy.find(el => !!~el.textContent.indexOf("Random"))) + 2].href;
  141. }
  142. if (!p) return;
  143. switch(host) {
  144. case "gelbooru.com": case "rule34.xxx":
  145. start_index = pid ? pid[2] / n : 0;
  146. increment = n;
  147. end_index = p.match(r)[2] / n;
  148. index = 1;
  149. paginator.go = true;
  150. break;
  151. case "booru.org":
  152. start_index = pid ? pid[2] / n : 0;
  153. increment = n;
  154. end_index = p.match(r)[2] / n;
  155. index = 1;
  156. break;
  157. case "e621.net":
  158. start_index = pid ? +pid[2] : 2;
  159. end_index = +p.match(r)[2];
  160. break;
  161. case "yande.re":
  162. start_index = pid ? +pid[2] : 1;
  163. end_index = +p.match(r)[2];
  164. break;
  165. case "paheal.net":
  166. start_index = pid ? +pid[4] : 1;
  167. end_index = +p.match(r)[4];
  168. break;
  169. }
  170. while(start_index < end_index)
  171. list.push({
  172. href: p.replace(r, (host === "paheal.net" ? "$3" : "$1") + ++start_index * increment),
  173. index: start_index + index
  174. });
  175. total = end_index + index;
  176. } else
  177. throw Error("*shrug*");
  178. if (paginator.go) paginator();
  179. d.addEventListener("gelbooru-slide", e => {
  180. if (list.length > 0)
  181. events(e.detail);
  182. }, false);
  183. d.addEventListener("gelbooru-slide-next", e => {
  184. if (list.length > 0) {
  185. clearTimeout(e.detail);
  186. d.querySelector(".loadingu").classList.remove("loadingu");
  187. process(true);
  188. }
  189. }, false);
  190. events(true);
  191. if (vis()) process();
  192. }());