ProxyFiend / Hentai Cafe Tweaks

// ==UserScript==
// @name            Hentai Cafe Tweaks
// @namespace       ProxyFiend.HentaiCafeTweaks
// @prefix          HCT
// @author          ProxyFiend
// @homepage        https://openuserjs.org/scripts/ProxyFiend/Hentai_Cafe_Tweaks
// @supportURL      https://openuserjs.org/scripts/ProxyFiend/Hentai_Cafe_Tweaks/issues
// @copyright       2020, ProxyFiend (https://openuserjs.org//users/ProxyFiend)
// @license         MIT
// @version         1.4
// @description     Tweaks Hentai Cafe, making it easier to use, and easier to save doujin for offline enjoyment.
// @include         https://hentai.cafe/*
// @updateURL       https://openuserjs.org/meta/ProxyFiend/Hentai_Cafe_Tweaks.meta.js
// @require         https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js
// @require         https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js
// @require         https://cdnjs.cloudflare.com/ajax/libs/jquery-scrollTo/1.4.2/jquery.scrollTo.min.js
// @require         https://openuserjs.org/src/libs/ProxyFiend/jQuery_Binary_Ajax.js
// @require         https://openuserjs.org/src/libs/ProxyFiend/ProxyFiendLib.js
// @require         https://openuserjs.org/src/libs/sizzle/GM_config.js
// @require         https://kit.fontawesome.com/86ff082c1b.js
// @resource        configCSS
// @grant           GM_getValue
// @grant           GM_setValue
// @grant           GM_registerMenuCommand
// @grant           GM_notification
// ==/UserScript==

/*
 *   DO NOT CHANGE ANYTHING BEYOND HERE, UNLESS YOU KNOW WHAT YOU'RE DOING.
 */

(() => {
	'use strict';

	var zip = new JSZip();
	var timer;
	var deferreds = [];
	var completed = [];
	var progress = 0;
	var path = window.location.pathname.substring(1);
	path = path.split("/")

	initConfig();

	if (typeof path[0] !== "undefined") {
		switch (path[0]) {
			case "manga":
				MangaPageScript();
				break;
			case "hc.fyi":
				if (!isNaN(path[1])) {
					DownloadPageScript();
				}
				break;
		}
	}

	function MangaPageScript() {
		var key_flag = false;
		var key_scrolltimer;

		jQuery(".inner>a").attr("onclick", "").unbind("click");
		// Unbind events

		jQuery(document).unbind("keydown");
		jQuery(document).unbind("keyup");

		jQuery(document).keydown((e) => {
			if (!jQuery("input").is(":focus")) {
				if (e.keyCode == 37 || e.keyCode == 65) {
					e.preventDefault();
					if (!key_flag) {
						key_flag = true;
						window.scrollTo({
							top: 0,
							left: 0,
							behavior: "smooth"
						});
						prevPage();
					}
				}
				if (e.keyCode == 39 || e.keyCode == 68) {
					e.preventDefault();
					if (!key_flag) {
						key_flag = true;
						window.scrollTo({
							top: 0,
							left: 0,
							behavior: "smooth"
						});
						nextPage();
					}
				}

				if (e.keyCode == 40 || e.keyCode == 83) {
					e.preventDefault();
					GM_debug("Triggered scroll down.");
					if (!key_flag) {
						key_flag = true;
						key_scrolltimer = setInterval(() => {
							window.scrollBy({
								top: 13,
								left: 0,
								behavior: "instant"
							});
						}, 20);
					}
				}

				if (e.keyCode == 38 || e.keyCode == 87) {
					e.preventDefault();
					if (!key_flag) {
						key_flag = true;
						key_scrolltimer = setInterval(() => {
							window.scrollBy({
								top: -13,
								left: 0,
								behavior: "instant"
							});
						}, 20);
					}
				}
			}
		});

		jQuery(document).keyup((e) => {
			key_scrolltimer = window.clearInterval(key_scrolltimer);
			key_flag = false;
		});

		jQuery(".inner>a").click((e) => {
			e.preventDefault();
			window.scrollTo({
				top: 0,
				left: 0,
				behavior: "smooth"
			});
			nextPage();
		});

		var scriptElement = document.createElement("script");
		scriptElement.text = "function changePage(e,r,t){return e=parseInt(e),(!initialized||e!=current_page)&&(initialized=!0,e>pages.length-1?(location.href=\"#\",!1):(e<0&&(current_page=0,e=0),preload(e),current_page=e,next=parseInt(e+1),jQuery(\"html, body\").stop(!0,!0),r||jQuery(\".panel\")[0].scrollIntoView(),!0!==pages[e].loaded?(jQuery(\"#page .inner img.open\").css({opacity:\"0\"}),jQuery(\"#page .inner img.open\").attr(\"src\",pages[e].url)):(jQuery(\"#page .inner img.open\").css({opacity:\"1\"}),jQuery(\"#page .inner img.open\").attr(\"src\",pages[e].url)),resizePage(e),t||History.pushState(null,null,base_url+\"page\/\"+(current_page+1)),jQuery(document).prop(\"title\",gt_page+\" \"+(current_page+1)+\" :: \"+title),update_numberPanel(),jQuery(\"#pagelist .current\").removeClass(\"current\"),jQuery(\"#ads_top_banner.iframe iframe\").attr(\"src\",site_url+\"content\/ads\/ads_top.html\"),jQuery(\"#ads_bottom_banner.iframe iframe\").attr(\"src\",site_url+\"content\/ads\/ads_bottom.html\"),!1))}";
		document.body.appendChild(scriptElement);
	};

	function DownloadPageScript() {
		jQuery("div.entry-content>div.last>p").append(
			jQuery("<a/>").addClass("x-btn x-btn-flat x-btn-rounded x-btn-large").attr("title", "Download").append(
				jQuery("<i/>").addClass("x-icon x-icon-download").attr("data-x-icon", "")).append(
				jQuery("<span/>").text("Download")).css("height", "53.9844px")
			.click(() => {
				if (progress === 0) {
					downloadDoujin();
					jQuery("a[title='Download'] span").animate({
						opacity: 0
					}).queue((next) => {
						jQuery("a[title='Download'] span").text("Downloading");
						next();
					}).animate({
						width: "115px"
					}).animate({
						opacity: 1
					});
				}
			}));

		jQuery("body").append(
			jQuery("<div/>").addClass("progress-bar").css({
				position: "fixed",
				bottom: "-10px",
				height: "10px",
				width: "100%",
				margin: "0px",
				background: "#121212"
			}).append(
				jQuery("<div/>").addClass("progress").css({
					width: "0%",
					height: "100%",
					margin: "0px",
					background: "#1e73be"
				})
			)
		);

		function deferredAddZip(url, filename, zip) {
			var deferred = jQuery.Deferred();
			jQuery.ajax({
				url: url,
				type: "GET",
				dataType: "binary",
				processData: false,
				success: (result) => {
					completed.push(filename);
					zip.file(filename, result, {
						binary: true
					});
					GM_debug("Loaded page: " + filename);
					deferred.resolve(result);
				},
				error: (result) => {
					deferred.reject(result);
				}
			});
			return deferred;
		};

		function downloadDoujin() {
			progress = 1;

			jQuery("div.progress-bar").animate({
				bottom: "0px"
			}, {
				duration: 200,
				easing: "linear"
			});

			jQuery.ajax({
				url: jQuery("a.x-btn[title='Read']").attr("href").replace("<br />", ""),
				dataType: "html",
				success: (data) => {
					var obj = jQuery("<div/>").append(jQuery.parseHTML(data, true));
					jQuery.globalEval(obj.find("#content>script")[0].innerText.substr(0, obj.find("#content>script")[0].innerText.indexOf("var next_chapter")));
					GM_debug("Loaded page data.");

					pages.forEach((page) => {
						deferreds.push(deferredAddZip(page.url.replace("cdn.", ""), page.filename, zip));
						GM_debug("Added page: " + page.filename);
					});

					timer = setInterval(() => {
						var total = 0;
						var loaded = 0;
						var percentage = 0;

						total = pages.length;
						loaded = completed.length;
						percentage = ((loaded / total) * 100);

						GM_debug("Total: " + total + "; Loaded: " + loaded + "; Percentage: " + percentage);

						jQuery("div.progress-bar>div.progress").animate({
							width: percentage + "%"
						}, {
							duration: 200
						});
					}, 250);

					jQuery.when.apply(jQuery, deferreds).done(() => {
						clearInterval(timer);
						jQuery("div.progress-bar>div.progress").animate({
							width: "100%"
						}, {
							duration: 200
						});
						zip.generateAsync({
								type: "blob"
							})
							.then((blob) => {
								saveAs(blob, jQuery("div.entry-content>div.last>h3").text() + "." + GM_config.get("format").toLowerCase());

								jQuery("a[title='Download'] span").animate({
									opacity: 0
								}).queue((next) => {
									jQuery("a[title='Download'] span").text("Completed");
                                    next();
								}).animate({
									width: "95px"
								}).animate({
									opacity: 1
								});

								setTimeout(() => {
									jQuery("div.progress-bar").animate({
										bottom: "-10px"
									}, {
										duration: 200,
										easing: "linear"
									});
								}, 2000);
								GM_notification({
									title: "Hentai Cafe Downloader",
									text: jQuery("div.entry-content>div.last>h3").text() + " has finished downloading. Enjoy!",
									timeout: 2000
								});
							});
					});
				}
			});
		};
	};

	function initConfig() {
		jQuery("body").append(
			jQuery("<div/>").addClass("modal")
		);

		GM_registerMenuCommand("Hentai Cafe Config", () => {
			GM_config.open();
		});

		GM_config.init({
			"id": "hct-config",
			"title": "Hentai Cafe Tweaks",
			"fields": {
				"format": {
					"label": "Format",
					"type": "select",
					"options": ["ZIP", "CBZ"],
					"default": "ZIP"
				}
			},
			"frame": jQuery("div.modal")[0],
			"css": "#hct-config *{font-family:arial,tahoma,myriad pro,sans-serif}#hct-config{background:#fff}#hct-config input[type=radio]{margin-right:8px}#hct-config .indent40{margin-left:40%}#hct-config .field_label{font-size:12px;font-weight:700;margin-right:6px}#hct-config .radio_label{font-size:12px}#hct-config .block{display:block}#hct-config .saveclose_buttons{margin:16px 10px 10px;padding:2px 12px}#hct-config .reset,#hct-config .reset a,#hct-config_buttons_holder{color:#000;text-align:right}#hct-config .config_header{font-size:20pt;margin:0}#hct-config .config_desc,#hct-config .reset,#hct-config .section_desc{font-size:9pt}#hct-config .center{text-align:center}#hct-config .section_header_holder{margin-top:8px}#hct-config .config_var{margin:0 0 4px}#hct-config .section_header{background:#414141;border:1px solid #000;color:#fff;font-size:13pt;margin:0}#hct-config .section_desc{background:#efefef;border:1px solid #ccc;color:#575757;font-size:9pt;margin:0 0 6px}.blur{filter:blur(5px) grayscale(.5);overflow:hidden}#hct-config *{font-family:inherit}#hct-config{z-index:9999;display:none;position:fixed;left:0;top:0;width:100%;height:100%;overflow:hidden;font-family:Lato,sans-serif;font-style:normal;font-weight:700;letter-spacing:.085em;background:0 0}#hct-config_wrapper{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);min-width:30%;max-width:80%;max-height:80%;border:1px solid #999;background-color:#202020}#hct-config .config_header{background-color:#121212;font-size:inherit}#hct-config .config_var{margin:0 10px 4px}#hct-config input{margin:auto;width:100%}#hct-config select{color:#999;width:100%;appearance:none;-moz-appearance:none;-webkit-appearance:none;background-image:url(\"data:image\/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-prefix='fas' data-icon='caret-down' class='svg-inline--fa fa-caret-down fa-w-10' role='img' xmlns='http:\/\/www.w3.org\/2000\/svg' viewBox='0 0 320 512'%3E%3Cpath fill='rgb(153, 153, 153)' d='M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z'%3E%3C\/path%3E%3C\/svg%3E\");background-origin:content-box;background-repeat:no-repeat;background-position:right -.1rem center;background-size:12px}#hct-config #hct-config_buttons_holder{position:absolute;top:0;right:0;line-height:0}#hct-config .saveclose_buttons{border:none;background:0 0;color:#999;padding:0;margin:6px 4px;line-height:0}.animate-button{animation:.5s ease-in-out alternate saveAnim}@keyframes saveAnim{0%{transform:scale(1)}50%{transform:scale(1.2)}100%{transform:scale(1)}}#hct-config .reset_holder{display:none}.fa,.fas{font-family:\"Font Awesome 5 Free\"!important}",
			"events": {
				"open": () => {
					GM_config.frame.setAttribute("style", "");
					jQuery(".site").addClass("blur");
					jQuery("#hct-config_saveBtn").html(jQuery("<i/>").addClass("fas fa-save"));
					jQuery("#hct-config_closeBtn").html(jQuery("<i/>").addClass("fas fa-times-circle"))
					jQuery(".config_var>input").attr("size", "")
				},
				"close": () => {
					jQuery(".site").removeClass("blur");
				},
				"save": () => {
					jQuery("#hct-config_saveBtn").addClass("animate-button")
				}
			}
		})

		jQuery("body").on("webkitAnimationEnd oanimationend msAnimationEnd animationend", () => {
			jQuery("#hct-config_saveBtn").removeClass("animate-button");
		});
	};
})();