// ==UserScript== // @name Let's panda! // @namespace https://github.com/Sean2525/Let-s-panda // @author sean2525, strong-Ting // @description A login, view, download tool for exhentai & e-hentai // @description:zh-tw 一個用於exhentai和e-hentai的登入、查看、下載的工具 // @description:zh-cn 一个用于exhentai和e-hentai的登录、查看、下载的工具 // @license MIT // @require https://code.jquery.com/jquery-3.2.1.slim.min.js // @include https://exhentai.org/ // @include https://exhentai.org/g/* // @include https://e-hentai.org/g/* // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.4/jszip.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js // @grant GM_xmlhttpRequest // @grant GM.xmlHttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM.setValue // @grant GM.getValue // @grant GM_notification // @grant GM.notification // @connect * // @run-at document-end // @version 0.2.17 // ==/UserScript== jQuery(function ($) { /** * Output extension * @type {String} zip * cbz * * Tips: Convert .zip to .cbz * Windows * $ ren *.zip *.cbz * Linux * $ rename 's/\.zip$/\.cbz/' *.zip */ var outputExt = "zip"; // or 'cbz' /** * Multithreading * @type {Number} [1 -> 32] */ var threading = 8; /** * Logging * @type {Boolean} */ var debug = false; var viewed = false; const loginPage = () => { let div = document.createElement("div"); div.className = "main"; let username = document.createElement("input"); let style = document.createElement("style"); style.innerHTML = ` body { background-color: #212121; } .main { display: -webkit-flex; display: flex; -webkit-flex-direction: column; flex-direction: column; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; height: ${window.innerHeight}px; } .flex-center{ display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; } form { display: -webkit-flex; display: flex; -webkit-flex-direction: column; flex-direction: column; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; } .image { position: relative; margin: 0; } .input { margin-top: 10px; display: block; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; color: #555; background-color: #fff; background-image: none; border: 1px solid #ccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); box-shadow: inset 0 1px 1px rgba(0,0,0,.075); -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; } .btn { color: #fff; background-color: #5cb85c; border-color: #4cae4c; margin-top: 10px; display: inline-block; font-weight: 400; line-height: 1.25; text-align: center; white-space: nowrap; vertical-align: middle; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; border: 1px solid transparent; padding: .5rem 1rem; font-size: 1rem; border-radius: .25rem; -webkit-transition: all .2s ease-in-out; -o-transition: all .2s ease-in-out; transition: all .2s ease-in-out; } .btn:hover { background-color: #4da64d; } .btn-blue { color: #fff; background-color: #3832dd; border-color: #3832dd; display: inline-block; font-weight: 400; line-height: 1.0; text-align: center; white-space: nowrap; vertical-align: middle; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; border: 1px solid transparent; padding: .5rem 1rem; font-size: 1rem; border-radius: .25rem; -webkit-transition: all .2s ease-in-out; -o-transition: all .2s ease-in-out; transition: all .2s ease-in-out; } .btn-blue:hover { background-color: #1c15c8; } `; $("head").append(style); const setCookie = (headers) => { // try { headers .split("\r\n") .find((x) => x.match("cookie")) .replace("set-cookie: ", "") .split("\n") .map( (x) => (document.cookie = x.replace(".e-hentai.org", ".exhentai.org") + " secure") ); } catch (err) { if (debug) console.log(err); } document.cookie = "yay=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.exhentai.org; path=/; secure"; setTimeout(function () { window.location.reload() }, 3000); }; const clearCookie = () => { if (debug) console.log("Clearning cookies"); document.cookie = "yay=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.exhentai.org; path=/; secure"; window.location.reload(); }; let form = document.createElement("form"); let login = document.createElement("button"); let wrapper = document.createElement("div"); let loadding = document.createElement("img"); let password = document.createElement("input"); username.placeholder = "Username" password.placeholder = "Password" let info = document.createElement("p"); let error = document.createElement("p"); info.innerHTML = `
If you can't log in, please visit the Forums and log in from there.
Please make sure you are logged in successfully and then click this
`; info.style.color = "white"; username.type = "text"; username.className = "input"; password.type = "password"; password.className = "input"; loadding.src = "data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA=="; loadding.style.position = "relative"; info.hidden = true; loadding.hidden = true; login.addEventListener("click", () => { loadding.hidden = false; GM.xmlHttpRequest({ method: "POST", url: "https://forums.e-hentai.org/index.php?act=Login&CODE=01", data: `referer=https://forums.e-hentai.org/index.php?&b=&bt=&UserName=${username.value}&PassWord=${password.value}&CookieDate=1"}`, headers: { "Content-Type": "application/x-www-form-urlencoded", }, onload: function (response) { if (debug) console.log(response); if (/You are now logged/.exec(response.responseText)) { error.style = "color:green"; 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. " 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"); setCookie(response.responseHeaders); } else if (/IF YOU DO NOT SEE THE CAPTCHA/.exec(response.responseText)) { error.style = "color:red"; error.innerText = "Login failed: Please visit the forums directly and log in from there; reCaptcha has been enabled." } else { error.style = "color:red"; error.innerText = "Login failed: Please check that your username and password are correct."; } info.hidden = false; loadding.hidden = true; }, onerror: function (err) { console.error(err); error.style = "color:red"; error.innerText("Login got error: Please contact me at https://github.com/MinoLiu/Let-s-panda/issues"); loadding.hidden = true; }, }); }); login.className = "btn"; login.innerHTML = "Login"; form.append(username); form.append(password); wrapper.className = "flex-center"; wrapper.append(loadding); wrapper.append(login); form.append(wrapper); form.addEventListener("submit", (e) => { e.preventDefault(); }); var image = document.createElement("img"); image.className = "image"; image.src = "https://i.imgur.com/oX86mGf.png" div.append(image); div.append(form); div.append(error); div.append(info); $("body").append(div); $(".clearCookie").on("click", clearCookie); }; const downloadPage = () => { var zip = new JSZip(), doc = document, tit = doc.title, $win = $(window), loc = /https?:\/\/e[x-]hentai\.org\/g\/\d+\/\w+/.exec(doc.location.href)[0], prevZip = false, current = 0, images = [], total = 0, final = 0, failed = 0, hrefs = [], comicId = location.pathname.match(/\d+/)[0], download = document.createElement("p"); const dlImg = ({ index, url, _ }, success, error) => { var filename = url.replace(/.*\//g, ""); var extension = filename.split(".").pop(); filename = ("0000" + index).slice(-4) + "." + extension; if (debug) console.log(filename, "progress"); GM.xmlHttpRequest({ method: "GET", url: url, responseType: "arraybuffer", onload: function (response) { final++; success(response, filename); }, onerror: function (err) { final++; error(err, filename); }, }); }; const next = () => { download.innerHTML = ` Downloading ${final}/${total}`; if (debug) console.log(final, current); if (final < current) return; final < total ? addZip() : genZip(); }; const end = () => { $win.off("beforeunload"); if (failed > 0) { alert("Some pages download failed, please unzip and check!"); } if (debug) console.timeEnd("eHentai"); }; const genZip = () => { zip .generateAsync({ type: "blob", }) .then(function (blob) { var zipName = tit.replace(/\s/g, "_") + "." + comicId + "." + outputExt; if (prevZip) window.URL.revokeObjectURL(prevZip); prevZip = blob; saveAs(blob, zipName); if (debug) console.log("COMPLETE"); download.innerHTML = ` Download completed!`; end(); }); }; const addZip = () => { total = images.length; var max = current + threading; if (max > total) max = total; for (current; current < max; current++) { let _href = images[current]; dlImg( _href, function (response, filename) { zip.file(filename, response.response); if (debug) console.log(filename, "image success"); next(); }, function (err, filename) { final--; // retry backupUrl for once GM.xmlHttpRequest({ method: "GET", url: _href.backupUrl, onload: function (response) { let imgNo = parseInt( response.responseText.match("startpage=(\\d+)").pop() ); let img = new DOMParser() .parseFromString(response.responseText, "text/html") .querySelector("#img"); if (debug) console.log(imgNo, "backupUrl success"); _href.url = img.src; dlImg( _href, function (response, filename) { zip.file(filename, response.response); if (debug) console.log(filename, "backupUrl image success"); next(); }, function (err, filename) { failed++; zip.file( filename + "_" + comicId + "_error.gif", "R0lGODdhBQAFAIACAAAAAP/eACwAAAAABQAFAAACCIwPkWerClIBADs=", { base64: true, } ); if (debug) console.log(filename, "backupUrl image error"); next(); } ); }, onerror: function (err, filename) { dlImg( _href, function (response, filename) { zip.file(filename, response.response); if (debug) console.log(filename, "retry image success"); next(); }, function (err, filename) { failed++; zip.file( filename + "_" + comicId + "_error.gif", "R0lGODdhBQAFAIACAAAAAP/eACwAAAAABQAFAAACCIwPkWerClIBADs=", { base64: true, } ); if (debug) console.log(filename, "retry url error"); next(); } ); } }); } ); } }; /** * Update image download status. */ const getImageNext = () => { download.innerHTML = ` Getting images ${final}/${hrefs.length}`; if (debug) console.log(final, current); if (final < current) return; final < hrefs.length ? getImage() : (() => { current = 0; final = 0; addZip(); })(); }; /** * Get all images from hrefs. */ const getImage = () => { let max = current + threading; if (max > hrefs.length) max = hrefs.length; for (current; current < max; current++) { if (debug) console.log(hrefs[current]); let href = hrefs[current]; GM.xmlHttpRequest({ method: "GET", url: hrefs[current], onload: function (response) { let imgNo = parseInt( response.responseText.match("startpage=(\\d+)").pop() ); let img = new DOMParser() .parseFromString(response.responseText, "text/html") .querySelector("#img"); if (debug) console.log(imgNo, "url success"); let src = href + "?nl=" + /nl\(\'(.*)\'\)/.exec(img.attributes.onerror.value)[1]; images.push({ index: imgNo, url: img.src, backupUrl: src, }); final++; getImageNext(); }, onerror: function (err) { final++; getImageNext(); if (debug) console.log(err); }, }); } }; /** * Get the href of all images from all pages. */ const getHref = () => { childNodes = document.querySelector("table[class=ptt] tbody tr") .childNodes; let page = parseInt( childNodes[childNodes.length - 2].textContent.replace(",", "") ); for (let i = 0; i < page; i++) { GM.xmlHttpRequest({ method: "GET", url: `${loc}?p=${i}`, onload: function (response) { if (debug) console.log(`page ${loc}?p=${i} detect ${response.responseText}`); let imgs = [ ...new DOMParser() .parseFromString(response.responseText, "text/html") .querySelectorAll(".gdtm a"), ]; if (!imgs.length) imgs = [ ...new DOMParser() .parseFromString(response.responseText, "text/html") .querySelectorAll(".gdtl a"), ]; if (!imgs.length) { alert( "There are some issue in the script\nplease open an issue on Github\nhttps://github.com/MinoLiu/Let-s-panda/issues" ); } imgs.forEach((v) => { hrefs.push(v.href); }); if (i == page - 1) { getImage(); } }, onerror: function (err) { download.innerHTML = ' Get href failed'; if (i == page - 1) { getImage(); } if (debug) console.log(err); }, }); } }; download.className = "g3"; download.innerHTML = ` Download`; $("#gd5").append(download); $(".panda_download").on("click", () => { if (threading < 1) threading = 1; if (threading > 32) threading = 32; if (debug) console.time("eHentai"); $win.on("beforeunload", function () { return "Progress is running..."; }); download.innerHTML = ` Start Download`; getHref(); }); }; function view() { viewed = true; if (threading < 1) threading = 1; if (threading > 32) threading = 32; var gdt = document.querySelector("#gdt"); var gdd = document.querySelector("#gdd"); var gdo4 = document.createElement("div"); gdo4.setAttribute("id", "gdo4"); $("body").append(gdo4); let childNodes = document.querySelector("table[class=ptt] tbody tr") .childNodes; let lpPage = parseInt( childNodes[childNodes.length - 2].textContent.replace(",", "") ); var data = document .querySelector("body div.gtb p.gpc") .textContent.split(" "); var minPic = parseInt(data[1].replace(",", "")); var maxPic = parseInt(data[3].replace(",", "")); var imgNum = parseInt( gdd .querySelector("#gdd tr:nth-child(n+6) td.gdt2") .textContent.split(" ")[0] ); viewer(lpPage, imgNum, minPic, maxPic); async function viewer(lpPage, imgNum, minPic, maxPic) { var Gallery = function (pageNum, imgNum, minPic, maxPic) { this.pageNum = pageNum || 0; this.imgNum = imgNum || 0; this.loc = /https?:\/\/e[x-]hentai\.org\/g\/\d+\/\w+/.exec(location.href)[0]; this.padding = false; this.current = 0; this.final = 0; }; var viewAll = await GM.getValue("view_all", true); Gallery.prototype = { imgHref: [], imgList: [], retry: 0, getAllHref: function (nextID) { if (nextID >= this.pageNum) { this.loadNextImage(); return; } var that = this; GM.xmlHttpRequest({ method: "GET", url: `${this.loc}?p=${nextID}`, onload: function (response) { if (debug) console.log(`page ${that.loc}?p=${nextID} detect ${response.responseText}`); let imgs = [ ...new DOMParser() .parseFromString(response.responseText, "text/html") .querySelectorAll(".gdtm a"), ]; if (!imgs.length) imgs = [ ...new DOMParser() .parseFromString(response.responseText, "text/html") .querySelectorAll(".gdtl a"), ]; if (!imgs.length) { alert( "There are some issue in the script\nplease open an issue on Github\nhttps://github.com/MinoLiu/Let-s-panda/issues" ); } imgs.forEach((v) => { that.imgHref.push(v.href); }); that.getAllHref(nextID + 1); }, onerror: function (err) { if (debug) console.log(err); that.retry++; if (that.retry > 2) { alert(`Page number ${nextID + 1} load failed for 3 times.`); that.getAllHref(nextID + 1); } else { that.getAllHref(nextID); } }, }); }, getHref: function (pageID) { var that = this; GM.xmlHttpRequest({ method: "GET", url: `${this.loc}?p=${pageID}`, onload: function (response) { if (debug) console.log(`page ${that.loc}?p=${pageID} detect ${response.responseText}`); let imgs = [ ...new DOMParser() .parseFromString(response.responseText, "text/html") .querySelectorAll(".gdtm a"), ]; if (!imgs.length) imgs = [ ...new DOMParser() .parseFromString(response.responseText, "text/html") .querySelectorAll(".gdtl a"), ]; if (!imgs.length) { alert( "There are some issue in the script\nplease open an issue on Github\nhttps://github.com/MinoLiu/Let-s-panda/issues" ); } imgs.forEach((v) => { that.imgHref.push(v.href); }); that.loadNextImage(); }, onerror: function (err) { if (debug) console.log(err); that.retry++; if (that.retry > 2) { alert(`Page number ${nextID + 1} load failed for 3 times.`); that.loadNextImage(); } else { that.getHref(nextID); } }, }); }, checkFunctional: function () { return (this.imgNum > 41 && this.pageNum < 2) || this.imgNum !== 0; }, loadNextImage: function () { if (this.final < this.current) { return; } this.loadPageUrls(); }, onSucceed: async function (response, href) { let imgNo = parseInt( response.responseText.match("startpage=(\\d+)").pop() ); let img = new DOMParser() .parseFromString(response.responseText, "text/html") .querySelector("#img"); if (debug) console.log(imgNo, "success"); let src = href + "?nl=" + /nl\(\'(.*)\'\)/.exec(img.attributes.onerror.value)[1]; Gallery.prototype.imgList[imgNo - 1].setAttribute( "data-href", src ); let timeoutId; let timeoutDuration = 10000; // 10s timeoutId = setTimeout(function () { // timeout trigger error Gallery.prototype.imgList[imgNo - 1].childNodes[0].dispatchEvent(new Event('error')); }, timeoutDuration); $(Gallery.prototype.imgList[imgNo - 1].childNodes[0]).on("load", function () { // success clear timeoutId clearTimeout(timeoutId); }); $(Gallery.prototype.imgList[imgNo - 1].childNodes[0]).on( "error", function () { var ajax = new XMLHttpRequest(); ajax.onreadystatechange = async function () { if (debug) { console.log(`Failed load ${Number(imgNo)}, getting backup image from ${src}.`); } if (4 == ajax.readyState && 200 == ajax.status) { var _imgNo = parseInt( ajax.responseText.match("startpage=(\\d+)").pop() ); var imgDom = new DOMParser() .parseFromString(ajax.responseText, "text/html") .getElementById("img"); Gallery.prototype.imgList[_imgNo - 1].childNodes[0].src = imgDom.src; } }; ajax.open("GET", src); ajax.send(null); } ); Gallery.prototype.imgList[imgNo - 1].childNodes[0].src = img.src; this.loadNextImage(); }, onFailed: function (err, href) { GM.xmlHttpRequest({ method: "GET", url: href, responseType: "document", onload: function (response) { that.onSucceed(response, href); }, onerror: function (err) { if (debug) console.log(err); this.loadNextImage(); }, }); }, loadPageUrls: function () { if (debug) { console.log("load work"); } let max = threading + this.current > this.imgHref.length ? this.imgHref.length : threading + this.current; for (this.current; this.current < max; this.current++) { let that = this; let href = this.imgHref[this.current]; GM.xmlHttpRequest({ method: "GET", url: href, responseType: "document", onload: function (response) { that.final++; that.onSucceed(response, href); }, onerror: function (err) { if (debug) console.log(err); that.final++; that.onFailed(err, href); }, }); } }, cleanGDT: function () { while (gdt.firstChild && gdt.firstChild.className) gdt.removeChild(gdt.firstChild); }, generateImg: function (callback) { for (var i = 0; i < this.imgNum; i++) { if (i < maxPic && i >= minPic - 1) { var img = document.createElement("img"); var a = document.createElement("a"); img.setAttribute("src", "https://ehgt.org/g/roller.gif"); img.setAttribute("loadding", "lazy"); a.appendChild(img); this.imgList.push(a); gdt.appendChild(a); } else { var img = document.createElement("img"); var a = document.createElement("a"); img.setAttribute("src", "https://ehgt.org/g/roller.gif"); img.setAttribute("loadding", "lazy"); a.appendChild(img); this.imgList.push(a); if (viewAll) gdt.appendChild(a); } } gdt.style.textAlign = "center"; gdt.style.maxWidth = "100%"; gdo4.innerHTML = ""; //clear origin button(Normal Large) var style = document.createElement("style"); style.type = "text/css"; style.innerHTML = ` div#gdo4{ position:fixed; width: 212px; height:32px; left:unset; right:10px; bottom:0px; top:unset; text-align:right; z-index:1; background:#34353b; border-radius:5%; } .double { font-weight: bold; // margin: 0 2px 4px 2px; float: left; border-radius: 5px; height:32px; width: 32px; //border: 1px solid #989898; //background: #4f535b; background-image: url(https://raw.githubusercontent.com/MinoLiu/Let-s-panda/master/icons/2_32.png); } .double:hover{ background: #4f535b; background-image: url(https://raw.githubusercontent.com/MinoLiu/Let-s-panda/master/icons/2_32.png); } .single{ font-weight: bold; // margin: 0 2px 4px 2px; float: left; border-radius: 5px; height:32px; width: 32px; //border: 1px solid #989898; // background: #4f535b; background-image: url(https://raw.githubusercontent.com/MinoLiu/Let-s-panda/master/icons/1_32.png); } .size_pic{ font-weight: bold; // margin: 0 2px 4px 2px; float: left; border-radius: 2px; height:16px; width: 16px; //border: 1px solid #989898; // background: #4f535b; } .single:hover{ background: #4f535b; background-image: url(https://raw.githubusercontent.com/MinoLiu/Let-s-panda/master/icons/1_32.png); } .size_btn { height: 32px; width: 32px; border-radius: 100%; //font-family: Arial; color: #ffffff; font-size: 16px; background: #4f535b; text-decoration: none; } .pad_pic { height: 32px; width: 32px; border-radius: 100%; //font-family: Arial; color: #ffffff; font-size: 16px; background: #4f535b; text-decoration: none; } .size_btn:hover { background: #a9adb1; text-decoration: none; } `; document.getElementsByTagName("head")[0].appendChild(style); //show var single_pic = document.createElement("div"); //create single button single_pic.className = "single"; single_pic.innerHTML += ""; gdo4.appendChild(single_pic); var double_pic = document.createElement("div"); //create double button double_pic.className = "double"; double_pic.innerHTML = ""; gdo4.appendChild(double_pic); var pad_pic = document.createElement("button"); pad_pic.className = "pad_pic"; pad_pic.innerHTML += "p"; gdo4.appendChild(pad_pic); var full_pic = document.createElement("button"); full_pic.className = "pad_pic"; full_pic.innerHTML += "f"; gdo4.appendChild(full_pic); var size_pic_reduce = document.createElement("button"); size_pic_reduce.className = "size_btn"; size_pic_reduce.innerHTML += "-"; gdo4.appendChild(size_pic_reduce); var size_pic_add = document.createElement("button"); size_pic_add.className = "size_btn"; size_pic_add.innerHTML += "+"; gdo4.appendChild(size_pic_add); document .getElementById("gdo4") .children[0] //when single button click change value of width .addEventListener("click", async function (event) { await GM.setValue("width", "0.7"); await GM.setValue("mode", "single"); await pic_width(await GM.getValue("width")); $("wrap").remove(); wrap(await GM.getValue("width")); }); document .getElementById("gdo4") .children[1] //when double button click change value of width .addEventListener("click", async function (event) { await GM.setValue("width", "0.49"); await GM.setValue("mode", "double"); let view_reverse = await GM.getValue("view_reverse", true); GM.setValue("view_reverse", !view_reverse); await pic_width(await GM.getValue("width")); $("wrap").remove(); wrap(await GM.getValue("mode")); }); var pad_img = document.createElement("img"); var pad_a = document.createElement("a"); pad_a.appendChild(pad_img); document .getElementById("gdo4") .children[2].addEventListener("click", async (event) => { this.padding = !this.padding; const view_reverse = await GM.getValue("view_reverse", true); await GM.setValue("view_reverse", false); $("wrap").remove(); await wrap(await GM.getValue("mode")); $("wrap").remove(); if (this.padding) { this.imgList.unshift(pad_a); gdt.insertBefore(pad_a, gdt.firstChild); } else { this.imgList.shift(); gdt.removeChild(pad_a); } await GM.setValue("view_reverse", view_reverse); await wrap(await GM.getValue("mode")); }); document .getElementById("gdo4") .children[3].addEventListener("click", async function (event) { await GM.setValue("full_image", true); await pic_width(0); }); document .getElementById("gdo4") .children[4].addEventListener("click", async function (event) { await GM.setValue("full_image", false); var size_width = parseFloat(await GM.getValue("width")); if (size_width > 0.2 && size_width < 1.5) { size_width = size_width - 0.1; GM.setValue("width", size_width); } let _width = await GM.getValue("width"); await pic_width(_width); console.log(_width); }); document .getElementById("gdo4") .children[5].addEventListener("click", async function (event) { await GM.setValue("full_image", false); var size_width = parseFloat(await GM.getValue("width")); if (size_width > 0.1 && size_width < 1.4) { size_width = size_width + 0.1; GM.setValue("width", size_width); } let _width = await GM.getValue("width"); await pic_width(_width); console.log(_width); }); async function pic_width( width //change width of pics ) { for (var i = maxPic - minPic + 1; i > 0; i--) { await resizeImg(width); } } callback && callback(); }, }; var g = new Gallery(lpPage, imgNum, minPic, maxPic); if (g.checkFunctional()) { var viewAll = await GM.getValue("view_all", true); g.generateImg(function () { if (g.pageNum && viewAll) { g.getAllHref(0); } else { g.getHref(Number(document.querySelector("td.ptds").childNodes[0].text) - 1); } g.cleanGDT(); }); document.addEventListener("keydown", (e) => { let nextImg = null; if (e.code === "ArrowUp") { for (let i = g.imgList.length - 1; i >= 0; i--) { const img = g.imgList[i].childNodes[0]; const rect = img.getBoundingClientRect(); if (rect.top < -1) { nextImg = img; break; } } } if (e.code === "ArrowDown") { for (let i = 0; i < g.imgList.length; i++) { const img = g.imgList[i].childNodes[0]; const rect = img.getBoundingClientRect(); if (rect.top > 1) { nextImg = img; break; } } } if (nextImg !== null) { e.preventDefault(); window.scrollTo({ top: nextImg.offsetTop, }); } }) await wrap(await GM.getValue("mode")); } else { alert( "There are some issue in the script\nplease open an issue on Github\nhttps://github.com/MinoLiu/Let-s-panda/issues" ); } } } var switchWrap = false; const wrap = async (width) => { let img = $("#gdt").find("a"); let gdt = document.getElementById("gdt"); if (switchWrap == true) { for (let i = 0; i < img.length - 1; i++) { if (i % 2 !== 1) { gdt.insertBefore(img[i + 1], img[i]); } } switchWrap = false; } if ((await GM.getValue("width")) == undefined) { await GM.setValue("width", "0.49"); console.log("set width:0.49"); } if ((await GM.getValue("mode")) == undefined) { await GM.setValue("mode", "double"); console.log("set mode:double"); } if ((await GM.getValue("view_reverse")) == undefined) { await GM.setValue("view_reverse", true); console.log("set view_reverse:true"); } img = $("#gdt").find("a"); let view_reverse = (await GM.getValue("view_reverse", true)); for (let i = 0; i < img.length; i++) { let wrap = document.createElement("wrap"); wrap.innerHTML = "
"; if ((await GM.getValue("mode")) == "single") { gdt.insertBefore(wrap, img[i]); } else if ((await GM.getValue("mode")) == "double") { if (i % 2 !== 1) { gdt.insertBefore(wrap, img[i]); if (view_reverse && i != img.length - 1) { switchWrap = true; gdt.insertBefore(img[i + 1], img[i]); } } } } await resizeImg(await GM.getValue("width")); }; const resizeImg = async (width) => { const full_image = (await GM.getValue("full_image")); if (full_image == true) { $("#gdt") .find("img") .css({ "height": "100vh", "width": "auto" }); } else { $("#gdt") .find("img") .css({ "height": "auto", "width": $(window).width() * width }); } } const adjustGmid = () => { var height = $("#gd5").outerHeight(true); height = height >= 330 ? height : 330; $("#gmid").height(height); $("#gd4").height(height); }; const viewAllMode = async () => { var view_all_btn = document.createElement("p"); var view_all = await GM.getValue("view_all", true); view_all_btn.className = "g3"; view_all_btn.innerHTML = ` Viewer page(s): ${view_all ? "All" : "One"}`; $("#gd5").append(view_all_btn); $(".panda_view_all").on("click", async () => { view_all = await GM.getValue("view_all", true); GM.setValue("view_all", !view_all); $(".panda_view_all").html( `Viewer page(s): ${view_all ? "All" : "One"}` ); window.location.reload(true); }); adjustGmid(); }; const viewMode = async () => { var view_mode = await GM.getValue("view_mode", true); var view_btn = document.createElement("p"); view_btn.className = "g3"; view_btn.innerHTML = ` Viewer ${view_mode ? "Enabled" : "Disabled" }`; $("#gd5").append(view_btn); $(".panda_view").on("click", async () => { view_mode = await GM.getValue("view_mode", true); GM.setValue("view_mode", !view_mode); $(".panda_view").html(`Viewer ${!view_mode ? "Enabled" : "Disabled"}`); if (view_mode) { window.location.reload(); } if (!view_mode && !viewed) { viewAllMode(); view(); } }); if (view_mode) { viewAllMode(); } adjustGmid(); if (view_mode) { // Stop image loadding for thumbnails. var imageToStop = document.querySelector("#gdt").querySelectorAll("img"); imageToStop.forEach((img, key) => { // Only load the first thumbnail. if (key == 0) { return; } img.src = ""; }) view(); } }; if ((e = $("img")).length === 0 && (e = $("dev")).length === 0) { loginPage(); } else if (window.location.href.match(/^https:\/\/e[x-]hentai\.org\/g/)) { downloadPage(); viewMode(); } });