Let's panda!

A login, view, download tool for exhentai & e-hentai

2022/08/20のページです。最新版はこちら。

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
  1. // ==UserScript==
  2. // @name Let's panda!
  3. // @namespace https://github.com/Sean2525/Let-s-panda
  4. // @author sean2525, strong-Ting
  5. // @description A login, view, download tool for exhentai & e-hentai
  6. // @description:zh-tw 一個用於exhentai和e-hentai的登入、查看、下載的工具
  7. // @description:zh-cn 一个用于exhentai和e-hentai的登录、查看、下载的工具
  8. // @license MIT
  9. // @require https://code.jquery.com/jquery-3.2.1.slim.min.js
  10. // @include https://exhentai.org/
  11. // @include https://exhentai.org/g/*
  12. // @include https://e-hentai.org/g/*
  13. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.4/jszip.min.js
  14. // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js
  15. // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
  16. // @grant GM_xmlhttpRequest
  17. // @grant GM.xmlHttpRequest
  18. // @grant GM_setValue
  19. // @grant GM_getValue
  20. // @grant GM.setValue
  21. // @grant GM.getValue
  22. // @grant GM_notification
  23. // @grant GM.notification
  24. // @connect *
  25. // @run-at document-end
  26. // @version 0.2.10
  27. // ==/UserScript==
  28.  
  29. jQuery(function ($) {
  30. /**
  31. * Output extension
  32. * @type {String} zip
  33. * cbz
  34. *
  35. * Tips: Convert .zip to .cbz
  36. * Windows
  37. * $ ren *.zip *.cbz
  38. * Linux
  39. * $ rename 's/\.zip$/\.cbz/' *.zip
  40. */
  41. var outputExt = "zip"; // or 'cbz'
  42.  
  43. /**
  44. * Multithreading
  45. * @type {Number} [1 -> 32]
  46. */
  47. var threading = 8;
  48.  
  49. /**
  50. * Logging
  51. * @type {Boolean}
  52. */
  53. var debug = false;
  54.  
  55.  
  56. var viewed = false;
  57. const loginPage = () => {
  58. let div = document.createElement("div");
  59. div.className = "main";
  60. let username = document.createElement("input");
  61. let style = document.createElement("style");
  62. style.innerHTML = `
  63. body {
  64. background-color: #212121;
  65. }
  66. .main {
  67. display: -webkit-flex;
  68. display: flex;
  69. -webkit-flex-direction: column;
  70. flex-direction: column;
  71. -webkit-align-items: center;
  72. align-items: center;
  73. -webkit-justify-content: center;
  74. justify-content: center;
  75. height: ${window.innerHeight}px;
  76. }
  77. .flex-center{
  78. display: -webkit-flex;
  79. display: flex;
  80. -webkit-align-items: center;
  81. align-items: center;
  82. -webkit-justify-content: center;
  83. justify-content: center;
  84. }
  85. form {
  86. display: -webkit-flex;
  87. display: flex;
  88. -webkit-flex-direction: column;
  89. flex-direction: column;
  90. -webkit-align-items: center;
  91. align-items: center;
  92. -webkit-justify-content: center;
  93. justify-content: center;
  94. }
  95. .image {
  96. position: relative;
  97. margin: 0;
  98. }
  99. .input {
  100. margin-top: 10px;
  101. display: block;
  102. height: 34px;
  103. padding: 6px 12px;
  104. font-size: 14px;
  105. line-height: 1.42857143;
  106. color: #555;
  107. background-color: #fff;
  108. background-image: none;
  109. border: 1px solid #ccc;
  110. border-radius: 4px;
  111. -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
  112. box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
  113. -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
  114. -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
  115. transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
  116. }
  117. .btn {
  118. color: #fff;
  119. background-color: #5cb85c;
  120. border-color: #4cae4c;
  121. margin-top: 10px;
  122. display: inline-block;
  123. font-weight: 400;
  124. line-height: 1.25;
  125. text-align: center;
  126. white-space: nowrap;
  127. vertical-align: middle;
  128. -webkit-user-select: none;
  129. -moz-user-select: none;
  130. -ms-user-select: none;
  131. user-select: none;
  132. border: 1px solid transparent;
  133. padding: .5rem 1rem;
  134. font-size: 1rem;
  135. border-radius: .25rem;
  136. -webkit-transition: all .2s ease-in-out;
  137. -o-transition: all .2s ease-in-out;
  138. transition: all .2s ease-in-out;
  139. }
  140. .btn:hover {
  141. background-color: #4da64d;
  142. }
  143. .btn-blue {
  144. color: #fff;
  145. background-color: #3832dd;
  146. border-color: #3832dd;
  147. display: inline-block;
  148. font-weight: 400;
  149. line-height: 1.0;
  150. text-align: center;
  151. white-space: nowrap;
  152. vertical-align: middle;
  153. -webkit-user-select: none;
  154. -moz-user-select: none;
  155. -ms-user-select: none;
  156. user-select: none;
  157. border: 1px solid transparent;
  158. padding: .5rem 1rem;
  159. font-size: 1rem;
  160. border-radius: .25rem;
  161. -webkit-transition: all .2s ease-in-out;
  162. -o-transition: all .2s ease-in-out;
  163. transition: all .2s ease-in-out;
  164. }
  165. .btn-blue:hover {
  166. background-color: #1c15c8;
  167. }
  168. `;
  169. $("head").append(style);
  170. const setCookie = (headers) => {
  171. //
  172. try {
  173. headers
  174. .split("\r\n")
  175. .find((x) => x.match("cookie"))
  176. .replace("set-cookie: ", "")
  177. .split("\n")
  178. .map(
  179. (x) =>
  180. (document.cookie = x.replace(".e-hentai.org", ".exhentai.org") + " secure")
  181. );
  182. } catch (err) {
  183. if (debug) console.log(err);
  184. }
  185. document.cookie =
  186. "yay=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.exhentai.org; path=/; secure";
  187. setTimeout(function () { window.location.reload() }, 3000);
  188. };
  189. const clearCookie = () => {
  190. if (debug) console.log("Clearning cookies");
  191. document.cookie =
  192. "yay=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.exhentai.org; path=/; secure";
  193. window.location.reload();
  194. };
  195. let form = document.createElement("form");
  196. let login = document.createElement("button");
  197. let wrapper = document.createElement("div");
  198. let loadding = document.createElement("img");
  199. let password = document.createElement("input");
  200. username.placeholder = "Username"
  201. password.placeholder = "Password"
  202. let info = document.createElement("p");
  203. let error = document.createElement("p");
  204. info.innerHTML = `
  205. <center>
  206. If you can't log in, please visit the <a target="_blank" href="https://forums.e-hentai.org/index.php?act=Login&CODE=00" class='btn-blue'>Forums</a> and log in from there. <br >
  207. Please make sure you are logged in successfully and then click this <button class="clearCookie btn-blue">button</button>
  208. </center>
  209. `;
  210. info.style.color = "white";
  211. username.type = "text";
  212. username.className = "input";
  213. password.type = "password";
  214. password.className = "input";
  215. loadding.src =
  216. "data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==";
  217. loadding.style.position = "relative";
  218. info.hidden = true;
  219. loadding.hidden = true;
  220. login.addEventListener("click", () => {
  221. loadding.hidden = false;
  222. GM.xmlHttpRequest({
  223. method: "POST",
  224. url: "https://forums.e-hentai.org/index.php?act=Login&CODE=01",
  225. data: `referer=https://forums.e-hentai.org/index.php?&b=&bt=&UserName=${username.value}&PassWord=${password.value}&CookieDate=1"}`,
  226. headers: {
  227. "Content-Type": "application/x-www-form-urlencoded",
  228. },
  229. onload: function (response) {
  230. if (debug) console.log(response);
  231. if (/You are now logged/.exec(response.responseText)) {
  232. error.style = "color:green";
  233. error.innerText = "Login succeeded: you will be redirected to exhentai.org in 3 seconds, if you can't access exhentai, don't use private browsing. "
  234. GM.notification("You will be redirected to exhentai.org in 3 seconds; if you can't access exhentai, don't use private browsing", "Login succeeded");
  235. setCookie(response.responseHeaders);
  236. } else if (/IF YOU DO NOT SEE THE CAPTCHA/.exec(response.responseText)) {
  237. error.style = "color:red";
  238. error.innerText = "Login failed: Please visit the forums directly and log in from there; reCaptcha has been enabled."
  239. }
  240. else {
  241. error.style = "color:red";
  242. error.innerText = "Login failed: Please check that your username and password are correct.";
  243. }
  244. info.hidden = false;
  245. loadding.hidden = true;
  246. },
  247. onerror: function (err) {
  248. console.error(err);
  249. error.style = "color:red";
  250. error.innerText("Login got error: Please contact me at https://github.com/Sean2525/Let-s-panda/issues");
  251. loadding.hidden = true;
  252. },
  253. });
  254. });
  255. login.className = "btn";
  256. login.innerHTML = "Login";
  257. form.append(username);
  258. form.append(password);
  259. wrapper.className = "flex-center";
  260. wrapper.append(loadding);
  261. wrapper.append(login);
  262. form.append(wrapper);
  263. form.addEventListener("submit", (e) => {
  264. e.preventDefault();
  265. });
  266. var image = document.createElement("img");
  267. image.className = "image";
  268. image.src = "https://i.imgur.com/oX86mGf.png"
  269. div.append(image);
  270. div.append(form);
  271. div.append(error);
  272. div.append(info);
  273. $("body").append(div);
  274. $(".clearCookie").on("click", clearCookie);
  275. };
  276.  
  277. const downloadPage = () => {
  278. var zip = new JSZip(),
  279. doc = document,
  280. tit = doc.title,
  281. $win = $(window),
  282. loc = /.*\//.exec(doc.location.href)[0],
  283. prevZip = false,
  284. current = 0,
  285. images = [],
  286. total = 0,
  287. final = 0,
  288. failed = 0,
  289. hrefs = [],
  290. comicId = location.pathname.match(/\d+/)[0],
  291. download = document.createElement("p");
  292.  
  293. const dlImg = ({ index, url }, success, error) => {
  294. var filename = url.replace(/.*\//g, "");
  295. var extension = filename.split(".").pop();
  296. filename = ("0000" + index).slice(-4) + "." + extension;
  297. if (debug) console.log(filename, "progress");
  298. GM.xmlHttpRequest({
  299. method: "GET",
  300. url: url,
  301. responseType: "arraybuffer",
  302. onload: function (response) {
  303. final++;
  304. success(response, filename);
  305. },
  306. onerror: function (err) {
  307. final++;
  308. error(err, filename);
  309. },
  310. });
  311. };
  312.  
  313. const next = () => {
  314. download.innerHTML = `<img src="https://exhentai.org/img/mr.gif"> <a href="#"> Downloading ${final}/${total}</a>`;
  315. if (debug) console.log(final, current);
  316. if (final < current) return;
  317. final < total ? addZip() : genZip();
  318. };
  319.  
  320. const end = () => {
  321. $win.off("beforeunload");
  322. if(failed > 0){
  323. alert("Some pages download failed, please unzip and check!");
  324. }
  325. if (debug) console.timeEnd("eHentai");
  326. };
  327.  
  328. const genZip = () => {
  329. zip
  330. .generateAsync({
  331. type: "blob",
  332. })
  333. .then(function (blob) {
  334. var zipName =
  335. tit.replace(/\s/g, "_") + "." + comicId + "." + outputExt;
  336.  
  337. if (prevZip) window.URL.revokeObjectURL(prevZip);
  338. prevZip = blob;
  339.  
  340. saveAs(blob, zipName);
  341. if (debug) console.log("COMPLETE");
  342. download.innerHTML = `<img src="https://exhentai.org/img/mr.gif"> <a href="${window.URL.createObjectURL(
  343. prevZip
  344. )}" download="${zipName}"> Download completed!</a>`;
  345. end();
  346. });
  347. };
  348.  
  349. const addZip = () => {
  350. total = images.length;
  351. var max = current + threading;
  352. if (max > total) max = total;
  353. for (current; current < max; current++) {
  354. dlImg(
  355. images[current],
  356. function (response, filename) {
  357. zip.file(filename, response.response);
  358. if (debug) console.log(filename, "success");
  359. next();
  360. },
  361. function (err, filename) {
  362. final--;
  363. // retry for once
  364. dlImg(
  365. images[current],
  366. function (response, filename) {
  367. zip.file(filename, response.response);
  368. if (debug) console.log(filename, "success");
  369. next();
  370. },
  371. function (err, filename) {
  372. failed++;
  373. zip.file(
  374. filename + "_" + comicId + "_error.gif",
  375. "R0lGODdhBQAFAIACAAAAAP/eACwAAAAABQAFAAACCIwPkWerClIBADs=",
  376. {
  377. base64: true,
  378. }
  379. );
  380. if (debug) console.log(filename, "error");
  381. next();
  382. }
  383. );
  384. }
  385. );
  386. }
  387. };
  388.  
  389. /**
  390. * Update image download status.
  391. */
  392. const getImageNext = () => {
  393. download.innerHTML = `<img src="https://exhentai.org/img/mr.gif"> <a href="#">Getting images ${final}/${hrefs.length}</a>`;
  394. if (debug) console.log(final, current);
  395. if (final < current) return;
  396. final < hrefs.length
  397. ? getImage()
  398. : (() => {
  399. current = 0;
  400. final = 0;
  401. addZip();
  402. })();
  403. };
  404.  
  405. /**
  406. * Get all images from hrefs.
  407. */
  408. const getImage = () => {
  409. let max = current + threading;
  410. if (max > hrefs.length) max = hrefs.length;
  411. for (current; current < max; current++) {
  412. if (debug) console.log(hrefs[current]);
  413. GM.xmlHttpRequest({
  414. method: "GET",
  415. url: hrefs[current],
  416. onload: function (response) {
  417. let imgNo = parseInt(
  418. response.responseText.match("startpage=(\\d+)").pop()
  419. );
  420. let img = new DOMParser()
  421. .parseFromString(response.responseText, "text/html")
  422. .querySelector("#img");
  423. if (debug) console.log(imgNo, "success");
  424. images.push({
  425. index: imgNo,
  426. url: img.src,
  427. });
  428. final++;
  429. getImageNext();
  430. },
  431. onerror: function (err) {
  432. final++;
  433. getImageNext();
  434. if (debug) console.log(err);
  435. },
  436. });
  437. }
  438. };
  439.  
  440. /**
  441. * Get the href of all images from all pages.
  442. */
  443. const getHref = () => {
  444. childNodes = document.querySelector("table[class=ptt] tbody tr")
  445. .childNodes;
  446. let page = parseInt(
  447. childNodes[childNodes.length - 2].textContent.replace(",", "")
  448. );
  449. for (let i = 0; i < page; i++) {
  450. GM.xmlHttpRequest({
  451. method: "GET",
  452. url: `${loc}?p=${i}`,
  453. onload: function (response) {
  454. if (debug)
  455. console.log(`page ${i + 1} detect ${response.responseText}`);
  456. let imgs = [
  457. ...new DOMParser()
  458. .parseFromString(response.responseText, "text/html")
  459. .querySelectorAll(".gdtm a"),
  460. ];
  461. if (!imgs.length)
  462. imgs = [
  463. ...new DOMParser()
  464. .parseFromString(response.responseText, "text/html")
  465. .querySelectorAll(".gdtl a"),
  466. ];
  467. if (!imgs.length) {
  468. alert(
  469. "There are some issue in the script\nplease open an issue on Github\nhttps://github.com/Sean2525/Let-s-panda/issues"
  470. );
  471. }
  472. imgs.forEach((v) => {
  473. hrefs.push(v.href);
  474. });
  475. if (i == page - 1) {
  476. getImage();
  477. }
  478. },
  479. onerror: function (err) {
  480. download.innerHTML =
  481. '<img src="https://exhentai.org/img/mr.gif"> <a href="#">Get href failed</a>';
  482. if (i == page - 1) {
  483. getImage();
  484. }
  485. if (debug) console.log(err);
  486. },
  487. });
  488. }
  489. };
  490.  
  491. download.className = "g3";
  492. download.innerHTML = `<img src="https://exhentai.org/img/mr.gif"> <a class="panda_download" href="#">Download</a>`;
  493. $("#gd5").append(download);
  494. $(".panda_download").on("click", () => {
  495. if (threading < 1) threading = 1;
  496. if (threading > 32) threading = 32;
  497. if (debug) console.time("eHentai");
  498. $win.on("beforeunload", function () {
  499. return "Progress is running...";
  500. });
  501. download.innerHTML = `<img src="https://exhentai.org/img/mr.gif"> <a href="#">Start Download</a>`;
  502. getHref();
  503. });
  504. };
  505.  
  506. function view() {
  507. viewed = true;
  508. var gdt = document.querySelector("#gdt");
  509. var gdd = document.querySelector("#gdd");
  510. var gdo4 = document.querySelector("#gdo4");
  511. let childNodes = document.querySelector("table[class=ptt] tbody tr")
  512. .childNodes;
  513. let lpPage = parseInt(
  514. childNodes[childNodes.length - 2].textContent.replace(",", "")
  515. );
  516.  
  517. var data = document
  518. .querySelector("body div.gtb p.gpc")
  519. .textContent.split(" ");
  520.  
  521. var minPic = parseInt(data[1].replace(",", ""));
  522. var maxPic = parseInt(data[3].replace(",", ""));
  523.  
  524. var imgNum = parseInt(
  525. gdd
  526. .querySelector("#gdd tr:nth-child(n+6) td.gdt2")
  527. .textContent.split(" ")[0]
  528. );
  529.  
  530. var pagePic = maxPic - minPic + 1;
  531.  
  532. var status = "false";
  533. viewer(lpPage, imgNum, minPic, maxPic);
  534.  
  535. async function viewer(lpPage, imgNum, minPic, maxPic) {
  536. var Gallery = function (pageNum, imgNum, minPic, maxPic) {
  537. this.pageNum = pageNum || 0;
  538. this.imgNum = imgNum || 0;
  539. this.loc = /.*\//.exec(location.href)[0];
  540. };
  541. var viewAll = await GM.getValue("view_all", true);
  542. Gallery.prototype = {
  543. imgList: [],
  544.  
  545. checkFunctional: function () {
  546. return (this.imgNum > 41 && this.pageNum < 2) || this.imgNum !== 0;
  547. },
  548. loadPageUrls: function (element) {
  549. [].forEach.call(element.querySelectorAll("a[href]"), function (item) {
  550. console.log("load work");
  551. var ajax = new XMLHttpRequest();
  552. ajax.onreadystatechange = async function () {
  553. if (4 == ajax.readyState && 200 == ajax.status) {
  554. var imgNo = parseInt(
  555. ajax.responseText.match("startpage=(\\d+)").pop()
  556. );
  557. var imgDom = new DOMParser()
  558. .parseFromString(ajax.responseText, "text/html")
  559. .getElementById("img");
  560. var src =
  561. ajax.responseURL +
  562. "?nl=" +
  563. /nl\(\'(.*)\'\)/.exec(imgDom.attributes.onerror.value)[1];
  564. Gallery.prototype.imgList[imgNo - 1].setAttribute(
  565. "data-href",
  566. src
  567. );
  568. Gallery.prototype.imgList[imgNo - 1].childNodes[0].src =
  569. imgDom.src;
  570. $(Gallery.prototype.imgList[imgNo - 1].childNodes[0]).on(
  571. "error",
  572. function () {
  573. var ajax = new XMLHttpRequest();
  574. ajax.onreadystatechange = async function () {
  575. if (4 == ajax.readyState && 200 == ajax.status) {
  576. var imgNo = parseInt(
  577. ajax.responseText.match("startpage=(\\d+)").pop()
  578. );
  579. var imgDom = new DOMParser()
  580. .parseFromString(ajax.responseText, "text/html")
  581. .getElementById("img");
  582. Gallery.prototype.imgList[imgNo - 1].childNodes[0].src =
  583. imgDom.src;
  584.  
  585. if ((await GM.getValue("width")) == undefined) {
  586. GM.setValue("width", "0.7");
  587. console.log("set width:0.7");
  588. }
  589.  
  590. if ((await GM.getValue("mode")) == undefined) {
  591. GM.setValue("mode", "single");
  592. console.log("set mode:single");
  593. }
  594.  
  595. $("#gdt")
  596. .find("img")
  597. .css(
  598. "width",
  599. $(window).width() * (await GM.getValue("width"))
  600. );
  601. }
  602. };
  603. ajax.open("GET", src);
  604. ajax.send(null);
  605. }
  606. );
  607. if ((await GM.getValue("width")) == undefined) {
  608. GM.setValue("width", "0.7");
  609. console.log("set width:0.7");
  610. }
  611.  
  612. if ((await GM.getValue("mode")) == undefined) {
  613. GM.setValue("mode", "single");
  614. console.log("set mode:single");
  615. }
  616.  
  617. $("#gdt")
  618. .find("img")
  619. .css(
  620. "width",
  621. $(window).width() * (await GM.getValue("width"))
  622. );
  623. }
  624. };
  625. ajax.open("GET", item.href);
  626. ajax.send(null);
  627. });
  628. },
  629. getNextPage: function () {
  630. var LoadPageUrls = this.loadPageUrls;
  631. var download = this.download_file;
  632. for (var i = 0; i < this.pageNum; ++i) {
  633. var ajax = new XMLHttpRequest();
  634. ajax.onreadystatechange = function () {
  635. if (4 == this.readyState && 200 == this.status) {
  636. var dom = new DOMParser().parseFromString(
  637. this.responseText,
  638. "text/html"
  639. );
  640. LoadPageUrls(dom.getElementById("gdt"));
  641. }
  642. };
  643. ajax.open("GET", this.loc + "?p=" + i);
  644. ajax.send(null);
  645. }
  646. },
  647. claenGDT: function () {
  648. while (gdt.firstChild && gdt.firstChild.className)
  649. gdt.removeChild(gdt.firstChild);
  650. },
  651.  
  652. generateImg: function (callback) {
  653. for (var i = 0; i < this.imgNum; i++) {
  654. if (i < maxPic && i >= minPic - 1) {
  655. var img = document.createElement("img");
  656. var a = document.createElement("a");
  657. img.setAttribute("src", "http://ehgt.org/g/roller.gif");
  658. a.appendChild(img);
  659. this.imgList.push(a);
  660.  
  661. gdt.appendChild(a);
  662. } else {
  663. var img = document.createElement("img");
  664. var a = document.createElement("a");
  665.  
  666. img.setAttribute("src", "http://ehgt.org/g/roller.gif");
  667. a.appendChild(img);
  668.  
  669. this.imgList.push(a);
  670. if (viewAll) gdt.appendChild(a);
  671. }
  672. }
  673.  
  674. gdt.style.textAlign = "center";
  675. gdt.style.maxWidth = "100%";
  676.  
  677. gdo4.innerHTML = ""; //clear origin button(Normal Large)
  678.  
  679. var style = document.createElement("style");
  680. style.type = "text/css";
  681. style.innerHTML = `
  682. div#gdo4{
  683. position:fixed;
  684. width: 150px;
  685. height:32px;
  686. left:unset;
  687. right:10px;
  688. bottom:0px;
  689. top:unset;
  690. text-align:right;
  691. z-index:1;
  692. background:#34353b;
  693. border-radius:5%;
  694. }
  695.  
  696.  
  697.  
  698.  
  699. .double {
  700. font-weight: bold;
  701. // margin: 0 2px 4px 2px;
  702. float: left;
  703. border-radius: 5px;
  704. height:32px;
  705. width: 32px;
  706. //border: 1px solid #989898;
  707. //background: #4f535b;
  708. background-image: url(https://raw.githubusercontent.com/Sean2525/Let-s-panda/master/icons/2_32.png);
  709. }
  710.  
  711. .double:hover{
  712. background: #4f535b;
  713. background-image: url(https://raw.githubusercontent.com/Sean2525/Let-s-panda/master/icons/2_32.png);
  714. }
  715.  
  716. .single{
  717. font-weight: bold;
  718. // margin: 0 2px 4px 2px;
  719. float: left;
  720. border-radius: 5px;
  721. height:32px;
  722. width: 32px;
  723. //border: 1px solid #989898;
  724. // background: #4f535b;
  725. background-image: url(https://raw.githubusercontent.com/Sean2525/Let-s-panda/master/icons/1_32.png);
  726. }
  727.  
  728. .size_pic{
  729. font-weight: bold;
  730. // margin: 0 2px 4px 2px;
  731. float: left;
  732. border-radius: 2px;
  733. height:16px;
  734. width: 16px;
  735. //border: 1px solid #989898;
  736. // background: #4f535b;
  737. }
  738.  
  739. .single:hover{
  740. background: #4f535b;
  741. background-image: url(https://raw.githubusercontent.com/Sean2525/Let-s-panda/master/icons/1_32.png);
  742.  
  743. }
  744.  
  745. .size_btn {
  746. height: 32px;
  747. width: 32px;
  748. border-radius: 100%;
  749. //font-family: Arial;
  750. color: #ffffff;
  751. font-size: 16px;
  752. background: #4f535b;
  753. text-decoration: none;
  754. }
  755.  
  756. .size_btn:hover {
  757. background: #a9adb1;
  758. text-decoration: none;
  759. }
  760. `;
  761. document.getElementsByTagName("head")[0].appendChild(style);
  762.  
  763. //show
  764.  
  765. var single_pic = document.createElement("div"); //create single button
  766. single_pic.className = "single";
  767. single_pic.innerHTML += "";
  768. gdo4.appendChild(single_pic);
  769.  
  770. var double_pic = document.createElement("div"); //create double button
  771. double_pic.className = "double";
  772. double_pic.innerHTML = "";
  773. gdo4.appendChild(double_pic);
  774. var size_pic_reduce = document.createElement("button");
  775. size_pic_reduce.className = "size_btn";
  776. size_pic_reduce.innerHTML += "-";
  777. gdo4.appendChild(size_pic_reduce);
  778.  
  779. var size_pic_add = document.createElement("button");
  780. size_pic_add.className = "size_btn";
  781. size_pic_add.innerHTML += "+";
  782. gdo4.appendChild(size_pic_add);
  783.  
  784. /*
  785. const wrap =(width)=>{
  786. let img = $('#gdt').find('img');
  787.  
  788. for(let i = 0;i<img.length;i++){
  789. let wrap = document.createElement('wrap');
  790. wrap.innerHTML='<br>';
  791. if(width>0.5){
  792. gdt.insertBefore(wrap,img[i]);
  793. }
  794. else if(width<=0.5){
  795. if(i%2!==1){
  796. gdt.insertBefore(wrap,img[i]);
  797. }
  798.  
  799. }
  800. }
  801. }
  802. */
  803.  
  804. document
  805. .getElementById("gdo4")
  806. .children[0] //when single button click change value of width
  807. .addEventListener("click", async function (event) {
  808. GM.setValue("width", "0.7");
  809. GM.setValue("mode", "single");
  810. pic_width(await GM.getValue("width"));
  811. $("wrap").remove();
  812.  
  813. wrap(await GM.getValue("width"));
  814. });
  815.  
  816. document
  817. .getElementById("gdo4")
  818. .children[1] //when double button click change value of width
  819. .addEventListener("click", async function (event) {
  820. GM.setValue("width", "0.49");
  821. GM.setValue("mode", "double");
  822. let view_reverse = await GM.getValue("view_reverse", true);
  823. GM.setValue("view_reverse", !view_reverse);
  824. pic_width(await GM.getValue("width"));
  825. $("wrap").remove();
  826.  
  827. wrap(await GM.getValue("mode"));
  828. });
  829.  
  830. document
  831. .getElementById("gdo4")
  832. .children[3].addEventListener("click", async function (event) {
  833. var size_width = parseFloat(await GM.getValue("width"));
  834. if (size_width > 0.1 && size_width < 1.4) {
  835. size_width = size_width + 0.1;
  836. GM.setValue("width", size_width);
  837. }
  838. let _width = await GM.getValue("width");
  839. pic_width(_width);
  840. console.log(_width);
  841. });
  842.  
  843. document
  844. .getElementById("gdo4")
  845. .children[2].addEventListener("click", async function (event) {
  846. var size_width = parseFloat(await GM.getValue("width"));
  847. if (size_width > 0.2 && size_width < 1.5) {
  848. size_width = size_width - 0.1;
  849. GM.setValue("width", size_width);
  850. }
  851. let _width = await GM.getValue("width");
  852. pic_width(_width);
  853. console.log(_width);
  854. });
  855.  
  856. function pic_width(
  857. width //change width of pics
  858. ) {
  859. for (var i = maxPic - minPic + 1; i > 0; i--) {
  860. $("#gdt")
  861. .find("img")
  862. .css("width", $(window).width() * width);
  863. }
  864. }
  865.  
  866. callback && callback();
  867. },
  868. };
  869. var g = new Gallery(lpPage, imgNum, minPic, maxPic);
  870.  
  871. if (g.checkFunctional()) {
  872. var viewAll = await GM.getValue("view_all", true);
  873. g.generateImg(function () {
  874. g.loadPageUrls(gdt);
  875. g.claenGDT();
  876. if (g.pageNum && viewAll) g.getNextPage("load");
  877. });
  878.  
  879. wrap(await GM.getValue("mode"));
  880. } else {
  881. alert(
  882. "There are some issue in the script\nplease open an issue on Github"
  883. );
  884. //window.open("https://github.com/strong-Ting/Gentle-Viewer/issues");
  885. }
  886.  
  887. function download_files(lpPage, imgNum, minPic, maxPic) {
  888. console.log(lpPage, imgNum, minPic, maxPic);
  889. var download_obj = new Gallery(lpPage, imgNum, minPic, maxPic);
  890. download_obj.download();
  891. }
  892. }
  893. }
  894.  
  895. var switchWrap = false;
  896.  
  897. const wrap = async (width) => {
  898. let img = $("#gdt").find("a");
  899. let gdt = document.getElementById("gdt");
  900. if (switchWrap == true) {
  901. for (let i = 0; i < img.length; i++) {
  902. if (i % 2 !== 1) {
  903. gdt.insertBefore(img[i + 1], img[i]);
  904. }
  905. }
  906. switchWrap = false;
  907. }
  908. img = $("#gdt").find("a");
  909. let view_reverse = await GM.getValue("view_reverse", true);
  910. for (let i = 0; i < img.length; i++) {
  911. let wrap = document.createElement("wrap");
  912. wrap.innerHTML = "<br>";
  913. if ((await GM.getValue("mode")) == "single") {
  914. gdt.insertBefore(wrap, img[i]);
  915. } else if ((await GM.getValue("mode")) == "double") {
  916. if (i % 2 !== 1) {
  917. gdt.insertBefore(wrap, img[i]);
  918. if (view_reverse) {
  919. switchWrap = true;
  920. gdt.insertBefore(img[i + 1], img[i]);
  921. }
  922. }
  923. }
  924. }
  925. };
  926.  
  927. const adjustGmid = () => {
  928. var height = $("#gd5").outerHeight(true);
  929. height = height >= 330 ? height : 330;
  930. $("#gmid").height(height);
  931. $("#gd4").height(height);
  932. };
  933.  
  934. const viewAllMode = async () => {
  935. var view_all_btn = document.createElement("p");
  936. var view_all = await GM.getValue("view_all", true);
  937.  
  938. view_all_btn.className = "g3";
  939. view_all_btn.innerHTML = `<img src="https://exhentai.org/img/mr.gif"> <a class="panda_view_all" href="#">Viewer page(s): ${view_all ? "All" : "One"}</a>`;
  940. $("#gd5").append(view_all_btn);
  941.  
  942. $(".panda_view_all").on("click", async () => {
  943. view_all = await GM.getValue("view_all", true);
  944. GM.setValue("view_all", !view_all);
  945. $(".panda_view_all").html(
  946. `Viewer page(s): ${view_all ? "All" : "One"}`
  947. );
  948. window.location.reload(true);
  949. });
  950.  
  951. adjustGmid();
  952. };
  953. const viewMode = async () => {
  954. var view_mode = await GM.getValue("view_mode", true);
  955. var view_btn = document.createElement("p");
  956. view_btn.className = "g3";
  957. view_btn.innerHTML = `<img src="https://exhentai.org/img/mr.gif"> <a class="panda_view" href="#">Viewer ${view_mode ? "Enabled" : "Disabled"
  958. }</a>`;
  959.  
  960. $("#gd5").append(view_btn);
  961.  
  962. $(".panda_view").on("click", async () => {
  963. view_mode = await GM.getValue("view_mode", true);
  964. GM.setValue("view_mode", !view_mode);
  965. $(".panda_view").html(`Viewer ${!view_mode ? "Enabled" : "Disabled"}`);
  966. if (view_mode) {
  967. window.location.reload();
  968. }
  969. if (!view_mode && !viewed) {
  970. viewAllMode();
  971. view();
  972. }
  973. });
  974.  
  975. if (view_mode) {
  976. viewAllMode();
  977. }
  978.  
  979. adjustGmid();
  980. if (view_mode) {
  981. view();
  982. }
  983. };
  984.  
  985. if ((e = $("img")).length === 0 && (e = $("dev")).length === 0) {
  986. loginPage();
  987. } else if (window.location.href.match(/^https:\/\/e[x-]hentai\.org\/g/)) {
  988. downloadPage();
  989. viewMode();
  990. }
  991. });