// ==UserScript==
// @name Clip Studio Reader Downloader
// @namespace http://tampermonkey.net/
// @version 1.9
// @description Download books from the browser version of Clip Studio Reader
// @author mrcoconuat
// @supportURL https://github.com/MrCocoNuat/clip-studio-reader-downloader/issues
// @match *://*/*
// @require https://unpkg.com/[email protected]/dist/jszip.js
// @require https://unpkg.com/[email protected]/dist/FileSaver.js
// @icon https://www.google.com/s2/favicons?sz=64&domain=mobilebook.jp
// @license MIT
// @grant none
// ==/UserScript==
'use strict';
// Site-blind Clip Studio Reader integration support:
//------------------------------
const downloadButtonId = "download-button";
const errorMessageId = "error-message";
const ELEMENT = {
SCREEN_CONTROLLER: 0, // used to flip pages
CURRENT_PAGE_COUNTER: 1, // duh
TOTAL_PAGE_COUNTER: 2, // duh
LOADER_SPINNER: 3, // used to detect if the reader is loading a page
MENU: 4, // used to detect if the menu must be raised since it contains the page scroller
PAGE_SPREAD: 5, // contains the actual pages, and is checked to see if the reader as a whole has loaded
PAGE_SLIDER: 7 // duh
}
const DOWNLOAD_MODE = {
PAGE_BY_PAGE: 0, // each page must be descrambled and rendered individually. Usually these are paid books
DIRECT: 1 // links to each page are accessible right from the start. Usually these are free samples or outright free books
}
// Data distribution for site-specific integrations:
//------------------------------
const siteSupport = {
"mbj-bs.pf.mobilebook.jp": {
mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
ids: {
[ELEMENT.SCREEN_CONTROLLER]: "screen_surface",
[ELEMENT.CURRENT_PAGE_COUNTER]: "paging_slider_nombre_current",
[ELEMENT.TOTAL_PAGE_COUNTER]: "paging_slider_nombre_total",
[ELEMENT.LOADER_SPINNER]: "loading_spinner_layer",
[ELEMENT.MENU]: "menu_layer",
[ELEMENT.PAGE_SPREAD]: "spread_a",
[ELEMENT.PAGE_SLIDER]: "paging_slider"
},
// sometimes the necessary element does not have an id, which sucks
classes: {}
},
"api.distribution.mediadotech.com": {
mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
ids: {
[ELEMENT.SCREEN_CONTROLLER]: "screen_control_pad",
[ELEMENT.CURRENT_PAGE_COUNTER]: "menu_nombre_current",
[ELEMENT.TOTAL_PAGE_COUNTER]: "menu_nombre_total",
[ELEMENT.LOADER_SPINNER]: "screen_loading_spinner_layer",
[ELEMENT.MENU]: "menu_container",
[ELEMENT.PAGE_SPREAD]: "screen_layer"
},
},
"comic-viewer.iowl.jp": {
mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
ids: {
[ELEMENT.SCREEN_CONTROLLER]: "screen_control_pad",
[ELEMENT.CURRENT_PAGE_COUNTER]: "menu_nombre_current",
[ELEMENT.TOTAL_PAGE_COUNTER]: "menu_nombre_total",
[ELEMENT.LOADER_SPINNER]: "screen_loading_spinner_layer",
[ELEMENT.MENU]: "menu_footer",
[ELEMENT.PAGE_SPREAD]: "screen_layer",
},
},
"comic.pixiv.net": {
// readers can have extra conditions tacked on
condition: () => document.location.pathname.substring(0, 7) === "/viewer",
mode: DOWNLOAD_MODE.DIRECT,
href: (pageNumber) => document.getElementById(`page-${pageNumber}`)?.style["background-image"].match(/url\("(.*)"\)/)?.[1], // TODO: these urls are not all loaded at start. So clicking it too fast will lose some ending pages
classes: {
[ELEMENT.PAGE_SPREAD]: "h-screen",
},
},
"bs.comicdc.jp": {
mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
ids: {
[ELEMENT.SCREEN_CONTROLLER]: "screen_control_pad",
[ELEMENT.CURRENT_PAGE_COUNTER]: "menu_nombre_current",
[ELEMENT.TOTAL_PAGE_COUNTER]: "menu_nombre_total",
[ELEMENT.LOADER_SPINNER]: "screen_loading_spinner_layer",
[ELEMENT.MENU]: "menu_container",
[ELEMENT.PAGE_SPREAD]: "screen_layer"
},
}
}
// try by ID if given, then by class
const getCSRElement = (elementEnum) => {
return document.getElementById(siteSupport[window.location.hostname].ids?.[elementEnum])
?? document.getElementsByClassName(siteSupport[window.location.hostname].classes?.[elementEnum])[0];
}
// returns a list of direct image links
function getCSRPageHrefs() {
// start from page 0, and go until null
const result = [];
for (let i = 0; /*blank*/; i++) {
const href = siteSupport[window.location.hostname].href(i);
if (href === undefined) {
break;
}
result.push(href);
}
return result;
}
function siteIsSupported() {
return !!siteSupport[document.location.hostname] && (siteSupport[document.location.hostname].condition?.() ?? true);
}
function downloadMode() {
return siteSupport[document.location.hostname].mode;
}
init();
// SVG Handling
// Convert SVG to image (JPEG, PNG, etc.) in the browser
// Thanks, Thom Kiesewetter
// https://stackoverflow.com/a/58142441
//------------------------------
const downloadSvg = `<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path id="download-a" d="M4.29289322,1.70710678 C3.90236893,1.31658249 3.90236893,0.683417511 4.29289322,0.292893219 C4.68341751,-0.0976310729 5.31658249,-0.0976310729 5.70710678,0.292893219 L7.70710678,2.29289322 C8.09763107,2.68341751 8.09763107,3.31658249 7.70710678,3.70710678 C7.31658249,4.09763107 6.68341751,4.09763107 6.29289322,3.70710678 L4.29289322,1.70710678 Z M0,8 L16,8 L16,10 L0,10 L0,8 Z"/>
<path id="download-c" d="M11,9.58578644 L13.2928932,7.29289322 C13.6834175,6.90236893 14.3165825,6.90236893 14.7071068,7.29289322 C15.0976311,7.68341751 15.0976311,8.31658249 14.7071068,8.70710678 L10.7071068,12.7071068 C10.3165825,13.0976311 9.68341751,13.0976311 9.29289322,12.7071068 L5.29289322,8.70710678 C4.90236893,8.31658249 4.90236893,7.68341751 5.29289322,7.29289322 C5.68341751,6.90236893 6.31658249,6.90236893 6.70710678,7.29289322 L9,9.58578644 L9,0.998529185 C9,0.447056744 9.44771525,-7.95978809e-15 10,-7.99360578e-15 C10.5522847,-8.02742346e-15 11,0.447056744 11,0.998529185 L11,9.58578644 Z M18,16 L18,10 C18,9.44771525 18.4477153,9 19,9 C19.5522847,9 20,9.44771525 20,10 L20,17 C20,17.5522847 19.5522847,18 19,18 L1,18 C0.44771525,18 0,17.5522847 0,17 L0,10 C0,9.44771525 0.44771525,9 1,9 C1.55228475,9 2,9.44771525 2,10 L2,16 L18,16 Z"/>
</defs>
<g fill="none" fill-rule="evenodd" transform="translate(2 3)">
<g transform="translate(2 6)">
<mask id="download-b" fill="#ffffff">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAMgCAYAAADbcAZoAAAgAElEQVR4XuzdCbQdVZkv8C8BQggQCFOHhBkEEQEVxLZRZEyQyW5QmWx9igiIgowtSveLovJkFhDDZKutgOKE2BKQEFAGlUEJLSozAcI8hoQQSPJOGPoiJDf7nqGqdtXPtVyre7mr9q7f/mpz/nz33Dso/IcAAQIECBAgQIAAAQIFCQwqaB7TECBAgAABAgQIECBAIAQQRUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAEC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swAigPS+ysxAgACB3gkIIL2zdWcCBFoC0y666Iwln356v0Fz5gyuAsicRRed/cyIEceO2HHHf6/CetpZgwAigLRTN64hQIBAVQQEkKrshHUQqJnAfddeu84KDzxwydDp07vy5fJu8zy3zDLXPzFq1PtXede7Hu/2vXt9PwFEAOl1jbk/AQIEeikggPRS170JNFTg0YkT9xzx4INnt75gvmSVCV4cMuTxp0aN+tCKW245qcrrfP3aBBABJKd6tVYCBAi8XkAAURMECHRV4LErrjhyxP33f23wnDmLdPXGPbpZ60eyZj0xcuRBK2677Zk9mqLrtxVABJCuF5UbEiBAoEABAaRAbFMRqLvA07/85b8Pf+yxL+f2nHMXWWTus8st92/Dd9zx+BzWLoAIIDnUqTUSIEBgQQICiNogQKArAo9NnHjscvfd9/lcD5XWX1OPp0aO/OZy22//ma6A9PAmAogA0sPycmsCBAj0XCDXzwo9hzEBAQLpAq3wccByDzxwelV+01X6yv9+5LwQ8vSoUaePGDPms+3eo4jrBBABpIg6MwcBAgR6JSCA9ErWfQk0RODRq6/earm77rq09Z2PxeryyE9WvBMigAggdXnXPAcBAs0UEECaue+emkBXBO69994RK1933d8Wmzlzxa7csCI3qXonRAARQCryqlgGAQIE2hIQQNpicxEBAvMEnvnv/75y6UcffV8dNaocQgQQAaSO75xnIkCgOQICSHP22pMS6KrA1Kuv3n/kHXd8q86HyEshZNVVTxmxzTaHdBWvw5sJIAJIhyXkcgIECJQqUOfPDqXCmpxA3QVmXnDBo4vPnLlC3Z8zBg2a2/qL6d9YfrvtKhNCBBABpPbvnQckQKDWAgJIrbfXwxHojUDr732c0fp7Hwf05u7VvOuTK6982nJjxx5UhdUJIAJIFerQGggQINCugADSrpzrCDRU4Nprr11is7vvfmyRF14Y1iiCCnVCBBABpFHvnoclQKB2AgJI7bbUAxHorcATEyacMeKhhxrV/fhf0YqEEAFEAOntW+7uBAgQ6K2AANJbX3cnUDuBWT/60TOLzZixdO0ebAAP9OwKKxy99E47fXUAl3R1qAAigHS1oNyMAAECBQsIIAWDm45AzgIPT5q030r33js+52foytpL7oQIIAJIV+rYTQgQIFCSgABSErxpCeQoMO3nP79mqaee+qcc1971NZcYQgQQAaTr9eyGBAgQKFBAACkQ21QEchd48Qc/mNn68vniuT9HN9f/1KhRp44YM+bgbt5zYfcSQASQhdWI/50AAQJVFhBAqrw71kagQgJPXn75Py97//0/q9CSqrGUVifksZEjv7Hi2LGF/Z0QAUQAqUbxWwUBAgTaE4hC/i4AACAASURBVBBA2nNzFYHGCTz9q199f/gjj+zduAdPeeCCQ4gAIoCklKUxBAgQqKqAAFLVnbEuAhUTmP7Tn9457Jln1qrYsqqznAJDiAAigFSn8K2EAAECAxcQQAZu5goCjRR4/oILZg6ZOdP3P/rb/YJCiAAigDTyEPLQBAjURkAAqc1WehACvROYO3fu4Lnf+96Lg+bOdWYsjLmAECKACCALK0P/OwECBKos4MNElXfH2ghUROCpyy7bdJmpU6+vyHKqv4yXf0XvKctvt92hvVisACKA9KKu3JMAAQJFCQggRUmbh0DGAs9ccsnHln744e9k/AilLP2p0aNPGbHddl3/7VgCiABSSkGblAABAl0SEEC6BOk2BOos8OQllxy97MMPH1PnZ+zJs/WoEyKACCA9qVc3JUCAQEECAkhB0KYhkLPAkxMmnL7sQw8dmPMzlLb2HoQQAUQAKa2eTUyAAIEuCAggXUB0CwJ1ENgyxq2ySIz48JBYYs1FYujKg2PoCovFsOUXiyWXWWrxoSstvsTziy869NkYtvSMGLbUrFh6+KxYbsTsePM6c+Odbx1SB4KePsPTyy9/1LI77/z/ujHJ2Bh/4saxX0++X9KN9RV5j5vjzJMujf0PK3LOps+1VRy3Q+tc2GzRGNo6M4b+wyKx+HKt/3+FIbHk8Nb/PWRWzJj+Qkx/+oWY8ficmPnYizFz6gvx3D0z48kLrolxU5vu5/kJEIgQQFQBgQYLbBsnHzA81t5vuVhvveVi3aHtUjw3+MGYO+J/4i1vvy8O+Mgi7d6m3te9/NuxTmn9xfSOg4MA0lcqAkgxr83745yzlo213798vGXUsFhpcLuzPh5/nflk3PaXp+POb0yMQ7/b7n1cR4BA3gICSN77Z/UEBiwwJsattGisfvKo+KddOwkdC5p45uDHYthaV8ZH9ngyNlhHZ+TvnOb9ONbKK5+8/JgxHf0bewFEABnwi9/GBVvHsVuPiLecODq22HhoLNv1zwuPx59nTI0/XPBcTDnsyhj3VBtLdAkBApkKdP1AydTBsgnUXmCb+Pp2y8b6J6wa79toSAzv/fMOmhPPLvOb2HLs7fGh9wsirwV/KYR00AnxHZA+zclx1skT/Dha197ncTFu8LWx4n+sFO84cFS8a4UiflCi9aNZc++Pq254Mv5yyKT4wjVdexg3IkCgsgICSGW3xsIIdEdg6/jK6svHJpeuGWPXK+LDxPxWPW2JP8bHP319vHNDf0j9JZ9WJ+Tx0aOPX2Hbbf+tnV0WQASQdupmYddsFcfvtXbsfG7rRzLb/nHMhc3R3/8+N2bHnfHfNz0Rfxzb6og81sm9XEuAQLUFBJBq74/VEehI4P1x9rfWiV33WyKWq8S7PnidC+PYo2d09Ex1uXjuvB/HajOE+BGsvirwHZDO34jNY9yoUbHZ5WvG+9fv/G6d32F6PDjn9vjZMZfFgeM6v5s7ECBQRYFKfCipIow1EchZYJv4wj/8Q2z/x1XivStX7TmeXey2+ORhV8Xb3+zHsubtzbSVVvq34TvscNxA9kkHpE/Lj2ANpHLeOHbrOO5D68We5y0dqyza2Z26f/Vd8atbp8f1774kxj3T/bu7IwECZQoIIGXqm5tADwS2iRP2Xjd2/04VP1C8+rgvDHo21h/7w9h3j8p95unBjvR/y3Y6ITogfaY6IO2X7E7xvR+/Jf51t/bv0PsrW78x6/k746c7XBFHXdH72cxAgEBRAgJIUdLmIVCAwLZx6hEbxb7HtX4/fwGzdT7F4utfEF/+t+c7v1HmdxhoCNEB6dtwHZC2in/QB+OXt6wVO27Q1tUFXzTvS+r/E/+53xVx2NkFT206AgR6JCCA9AjWbQkULbB9nPXNDeMTnx4Uef0djudWvCyO/8r9sfjiea27F/v7xKhRJ6X8il4BRABpt/7eHYcssVb8y12j470j271HGdfNjufjf+I7/uhkGfjmJNADAQGkB6huSaBogbFx5mkbx76fKeu3XHX6vM8ufU2ceMJtMXTxtv++WadLqMT1qZ0QAUQAaadgx8ThS64Uu90zKv6x9et1c/zP3Lg5zv3WpbHvp3NcvTUTINAnIICoBgKZC2wbp33+7XHAsbl1Pl7PLoS8LJISQgQQAWSgx1b+4eOV96P1q3r/GN86+vL47FcHamA8AQLVERBAqrMXVkJgwAJbxYkffWcc+t0BX1jRC6YNvybOOPWOiq6u2GVNW3HFI4fvuOPx85vVl9D7VHwJPa0uPxLXPjIq3r1i2ujqj7ohTtzrijj8/Oqv1AoJEJifgACiLghkKvDe+MLKb40DplT5t121Q6sT8sq/6e3n74TogOiApL5bdel8vP55n4kpL/45xo/6bRz7aKqFcQQIVEdAAKnOXlgJgQEJ7BVXTV0ltqjc3/kY0EMsYLAQ0n8IEUAEkJT3rK7h49Vnvzsuvf3C2H7dFAtjCBColoAAUq39sBoCSQLbx/jjN4r9Dk8anOmgacN/2/pxrLsyXX13lz3vL6Yvv912R756Vz+C1efrR7AWXGt7xzUPj45/Wqm71Vitu/0pzhjX+ovpX6rWqqyGAIGFCQggCxPyvxOomMC8v3K+fhw8dVisVPtfGaUTMv9OiA6IDkh/x1LdOx+vffZnY+rsm+MbI66J46ZV7Ki2HAIE+hEQQJQHgcwEdouL/rh27PK2zJbd9nKFkDeGEAFEAFnQC9Wk8PGqwW3xk2t/Hh/cvO1DxoUECBQuIIAUTm5CAu0LbBFffvtmcdRNg2PR9m+S4ZVCSN+mPb3CCkfssdMDozaKTx2S4VZ2fcn+EnofaRPDx7ynnx2z4sY4ds0rY9w9XS8wNyRAoCcCAkhPWN2UQG8Edo2LblwndnlHb+5e7bsKIS/vz7y/E3L0+EVuePG6vd9Z7R0rZnUCyMvOTQ0fr1bZX+PCq34RH96ymKozCwECnQoIIJ0Kup5AQQLzfu3uO+PoqYvGEgXNWL1phJCX9+SsC2bHnRM+Ub0NKmFFAojwMa/sZsZTc/8aX136sjhhegllaEoCBAYoIIAMEMxwAmUJ7BjfvXCD+OgHy5q/KvMKIa0Acv6Lceel+1RlS0pdR9MDSNM7H68tvlvinPGXxL4HlFqQJidAIElAAEliMohA+QKfiMnTVogNlyp/JeWvoOkhRADpq8EmBxDh4+/Poqlx3aPfr/mvHS7/9LUCAt0REEC64+guBHoqsFV87X3vjKOu7Okkmd28ySFEABFAhI83HlhzW19Hvz6+4svomZ3llttMAQGkmfvuqTMT2Dm+//P1Y+8PZLbsni+3qSFEAGl2ABE+Fny0+DGsnh+7JiDQFQEBpCuMbkKgtwJ7xlUPrBpbjOrtLHnevYkhRABpbgARPvo/p+6KX93649hxgzxPM6sm0BwBAaQ5e+1JMxY4KJ6YMzRGeF8XsIdNCyECSDMDiPCx8EP8ibht5jmxXnN/VeDCiYwgUAkBH2gqsQ0WQWDBAu+Lr737XXHUtYz6F5g2/Ldxxql3NYLp7AtejDsm+C1Y8zb75jjzpEtj/8OasPF7xzUPj/Yl64Vu9c3xpeUvjXFPLHSgAQQIlCYggJRGb2ICaQJj4vSj3xYHHpM2utmjmtIJ0QFpVgdE52Ng59r1ccLuk+KIHw3sKqMJEChSQAApUttcBNoQ2Cn+62dviY/8cxuXNvKSeSHklJNui8UWG1zb5xdAmhNAhI+Bv8ZN/tXMA9dyBYFyBASQctzNSiBZYLe46I9rxy5vS77AwJi+7FVx+in31FbCj2D1bW3dfwTLj10N/DX+S5z3y4tj750HfqUrCBAoSkAAKUraPATaFPhQXHrbmjHmTW1e3tjLnl3mqjjlhLtq2QnRAal/B0Tno/2j669x4VW/iA9v2f4dXEmAQK8FBJBeC7s/gQ4F9oxJ960aW67S4W0aeXldQ4gAUu8AInx0dlzdET+//qfxL5t1dhdXEyDQSwEBpJe67k2gCwJ7x9Wt33yz+UpduFUjbzEvhHzzG/fU6tn9CFbfdtbxR7D82FVnr+ud8ctbfhI7b9TZXVxNgEAvBQSQXuq6N4EuCOwRk+5fLbYc3YVbNfYWdeuE6IDUswPy/vjs4svFh+8ZHe8Z2diXtQsPfnv87Pqfxa46IF2wdAsCvRIQQHol674EuiTw4bj0jjVizNpdul1jb1OnECKA1C+ACB/dO5r+Fj+adFHsvnX37uhOBAh0W0AA6bao+xHossBucfHktWOnDbt820beri6/HcuPYPWVb11+BGvv+M2Do+O9Oh9dOJluje///Jfxr//ShVu5BQECPRIQQHoE67YEuiWwS5w34c2x59hu3a/p96lDJ0QHpD4dEJ2P7p9Ik+PMEybE/kd0/87uSIBAtwQEkG5Jug+BHgmMjfEnbhz7Hdqj2zfytrmHEAGkHgFE+OjN8XNjnPihiXH4j3tzd3clQKAbAgJINxTdg0APBbaJE3ffJA69oIdTNPLWOYcQAST/ACJ89O7YuSb+bfg1cdy03s3gzgQIdCoggHQq6HoCPRaY90Fl/Thu5qIxtMczNe/2uYYQASTvACJ89O6seTLumHV2vGnx3s3gzgQIdENAAOmGonsQ6LHAx+KGJ/8hNlm2x9M08vY5hhABJN8AInz09pi5K/77lh/HTv4GSG+Z3Z1AxwICSMeEbkCg9wIfaP1ayfXiQ1v2fqZmzpBbCBFA8gwgwkfvzxdfQO+9sRkIdENAAOmGonsQ6LHA1nHCnpvGYef1eJpG3z6nECKA5BdAhI/eHy8vxsy4OY4ZOTG+9nDvZzMDAQKdCAggnei5lkCBAvvF3bOWiTUWK3DKxk2VSwgRQPIKIMJHMUfJlJh03wWx9WrFzGYWAgQ6ERBAOtFzLYECBT4QF1y+Xuy+TYFTNnKqHEKIAJJPABE+ijtGbo4zjrk0DvyP4mY0EwEC7QoIIO3KuY5AwQLvjXFveVd88c+DY9GCZ27edFUPIQJIHgFE+Cju7JgeD815JA4acmFcOLu4Wc1EgEC7AgJIu3KuI1CCwO5x2Z2rx3ZrlTB146ascggRQKofQISPYo+MP8d//fS/46O7FTur2QgQaFdAAGlXznUEShDYMo7dfrP4/CUlTN3IKasaQgSQagcQ4aPY42JmPDX3pvj68lfH/3uy2JnNRoBAuwICSLtyriNQksAeMen+1WLL0SVN37hpqxhCBJDqBhDho/gj4i9x3iUXx947FD+zGQkQaFdAAGlXznUEShLYKr62+SZx+NWDwy/EKmoLqhZCBJBqBhDho6g3sm+eaXH/7D/FSUtfFyc/V/zsZiRAoF0BAaRdOdcRKFFg17joxnVil3eUuITGTV2lECKAVC+ACB/lHAk3x1mnXBr7HVLO7GYlQKBdAQGkXTnXEShRYJs4avm1Y5+py8baQ0pcRuOmrkoIEUCqFUCEj3KOgvviN1PPj/f5cdRy+M1KoCMBAaQjPhcTKE/gffHVrd4RB12xWCxV3iIaOHMVQogAUp0AInyUcwg8FXfOujPOHTUxjn28nBWYlQCBTgQEkE70XEugZIEx8c1xb4tP/9+Sl9G46aePuDJOP/ne0p777AtejDsm7FPa/FWa+OY486RLY//DylrTXq1/C79KvHflsuZv4ryz4pn4U5yx1ZVx1JVNfH7PTKAOAgJIHXbRMzRaYIf49n+9NT7+kUYjlPDwZXZCdEDK74DofJTw0r005dy4KU4/8vI46PiyVmBeAgQ6FxBAOjd0BwKlC7S+lH5T60vpby99IQ1bQFkhRAApN4AIH+W96H+LH026KHbfurwVmJkAgW4ICCDdUHQPAhUQ2D1+fdfqse2aFVhKo5ZQxo9j+RGsvhIr40ew9oqrWj92tYUfuyr4Tb83Lr/7h7HdWgVPazoCBHogIID0ANUtCZQkMGj3mHjP6rH1aiXN39hpi+6E6ICU0wHR+SjvFb83rpjyw9hmjdYK5pa3CjMTINAtAQGkW5LuQ6AaAkJISfswL4R846S7Y9FFen+sCiDFBxDho6QXqzWt8FGevZkJ9Eqg9/+k7NXK3ZcAgQUJCCEl1cb05a6IU46f0vMQIoAUG0CEj5JeKOGjPHgzE+ixgADSY2C3J1CSgBBSEnwRIUQAKS6ACB8lvUjCR3nwZiZQgIAAUgCyKQiUJCCElATf6xAigBQTQISPkl4g4aM8eDMTKEhAACkI2jQEShIQQkqC72UIEUB6H0CEj5JeHOGjPHgzEyhQQAApENtUBEoSEEJKgu9VCBFAehtAhI+SXhjhozx4MxMoWEAAKRjcdARKEhBCSoLvRQgRQHoXQISPkl4U4aM8eDMTKEFAACkB3ZQEShIQQkqC73YIEUB6E0CEj5JeEOGjPHgzEyhJQAApGH6LOGbNIbHsXkNj2U2HxnLrDosVRrb+/2GLx9JDFo8Rg+ct5/l4cs7zMW3WrHhqxox49MGZ8eRtM+OpP8yMx8+7OsZNKXjJpquZQOsvpt/Z+ovp/ppwwfs6ffmJcfqJ93dlVn8JvY+xm38Jfc+48v5V432ju7JJbpIs0PoL53e1/sL52skXGEhgPgI+X+VVFgJIj/dryxi37JAY+YVlYtWdRsR6ay8baw/pZMon4/bnn4jbbp8WU35+WXz63zu5l2ubK7B7XH736i//VWH/KVBgxnIT47STOg8hAkj3A8hecdXUVWKLlQssB1O1BO6Niff8MLZdEwaBgQr4fDVQsWqNF0B6tB/bxqmfWyE2PHzl2Gz0YjGsJ7O8ENNjalx39yMxedykOOx7PZnETWsrIISUs7XdCCECSHcDiPBRzrsgfJTjnvusPl/lvoMvr18A6fI+bh0nf3KVeM9xI2PTEV2+db+3eyCufmhq/P7ASXH4T4uc11xZC/hOSEnb1+l3QnwHpG/jJsdZJ0+I/Q5tZyt956Mdte5cc29cMeWHL3dh53bnju5SdwGfr+q1wwJIl/ZzqzhmvZXi3RPK/bGWuXFX/OqWB+OG7a+JcVO79GhuU28BIaSk/e0khAggnQeQTeJTi705/nXK6HjPyJJKoLHTCh+N3fq2Htznq7bYKn+RANKFLdo+zjl73dh1n6ExohKe0+PBObfFz8b9Og48pguP5xYNEPDF9HI2ud0vpvsRrL79avdL6HvGpPtWjS1XKWfnmzurL5w3d+/beXKfr9pRy+OaSnxgzoPqjatsfQHqzavEmN+MinevWMVnuDsuu/3RuG7rK2Nc5996reIDWlM3BXRCuqk5gHu10wnRAWm/AzKv8/Gm2PPu1WJLv+1qAHXajaE6H91QbMY9fL6q/z4LIG3u8Zg4/eg3x95fbv063UobTo+H5twa533cl9Tb3OiGXeaL6eVs+EC/mK4D0n4HZI+YdL/wUXydtzofd7d+1a5f/108fXYz+nyV3Za1teBKf3hu64kKuGhsjD9xw9jn0MGxaAGzdT7FvN+W1fqi5icnxqHndn43d6i5gE5ISRs8kE6IDsjAOyA6HyUVdmtanY/y7HOb2eer3Has/fUKIAO02yHOPeet8Yl9BnhZ6cNfjOdaIeScQy6Pg04pfTEWUHkB3wkpZ4tmLP/rOO3Ehf/+CB2QgXdAfOejnJq+Jy6780cxdp1yZjdrTgI+X+W0W52vVQAZgGHrd08f8fY48LhB8dIfLM/uP/M6IX+KMz44KY78SXaLt+CiBXRCihZ/Zb6UTogOSHoHROejpELW+SgPPsOZfb7KcNM6XLIAkgi4VXx917fFgT9ZLJZMvKKaw2bEI3Mmx5lv+038xy3VXKFVVUhACClpMxYWQgSQtAAifJRUwMJHefAZzuzzVYab1oUlCyAJiFvHMWuvH5+8bckYmWfr43XP+Fjc+ty3Y4Pe/Hn2BE9D8hL4cFx6xxoxZu28Vp3/amcsf3nrx7EemO+D+BGsPpb+fg2vH7sq5z3wY1fluOc4q89XOe5ad9YsgCzE8UPxoUWGxecf/4d4xzLdIa/GXVp/sPDWH8eOG1RjNVZRcQGdkJI2aEGdEB2Q/jsgOh8lFazOR3nwGc7s81WGm9bFJQsgC8H8QPz46vVit827aF6ZW02Os0+bEJ86qDILspBKC+iElLM98/tiug5I/x2Q1q/andL6VburlrNjzZ1V56O5e9/Ok/t81Y5afa4RQPrZy3k/l7hpHPaTQbFIfXb8NU/yfDw998Y4ZY2rY9yUWj6gh+q2gE5It0UT7/f6TogOyPw7IDofiQXVg2F+1W4PUGt8S5+vary5iY8mgPQD9Ym4ZfoK8dZaf1fi7rjkLxfGDm9JrBfDCAghJdXAa0OIAPLGACJ8lFSYrWmFj/Lsc53Z56tcd6576xZAFmCZ6++jbqc0bogT97oiDj+/nWtd00yB1t8JuWv12HbNZj59eU/96hfT/QhW3x68+iV0Xzgvpy79hfNy3HOe1eernHeve2sXQOZjOSYOX3KdOOTppWJUPX/26nXPPDWuffT7sflK3Ssrd2qCwO4x8d7VY+vVmvCsVXrGeZ2QDTe5O+76dXZ/D7UnjJPjrFOXjXV3a33nY3RPJnDTBQq80vlYHRGBVAGfr1Kl6j9OAJnPHu8Y3zl/g/jYHvXf/r4nvDFO/j8T49DvNumZPWvnAr6Y3rlhO3eYsciUGDZb9ptnNy3un710rNKIf1nUTq306prWF87vaP2F8zf16v7uW08Bn6/qua/tPJUAMh+1A+K+F5v2D7T74sr7z4+t/NaYdt6ihl8jhDS8ADx+4wSEj8Ztedce2OerrlFmfyMB5HVbuE2cst8mcfD47Hd2gA8wO2bFH+JLo34bX3twgJcaTsAX09UAgYYI+MJ5Qza6B4/p85XPV68tKwHkdS/ZbnHx5LVjpw178O5V/pa3xDnjL4l9D6j8Qi2wigJCSBV3xZoIdFFA+OgiZgNv5fOVz1cCSD8v/mfikTnDYsVGBrMH4uqHfhDvXbmB56JH7pKA347VJUi3IVAxgdZvu7rrh7Hd2hVbluVkJODzlc9XAsgCXtht4oS9N4nDvp/R+9zVpb4Q0+OvcdTQS+K057t6YzdrkoBOSJN227M2QkDnoxHb3NOH9PnK56vXF1gj/03/gt6yneP8S9ePPcb09C2s+M1vilMPujwOPq3iy7S8agsIIdXeH6sjkCwgfCRTGdiPgM9XET5f/X2BCCCv8fho/P7xkbHZck0+Rf4WF155UXx4qyYbePbuCLR+HOvO1h8rXKs7d3MXAgSKFvBjV0WL13c+n68ifL4SQBb4hh8YD85eMkYOru8RsPAnmxKT7rvAH5dbOJQRKQI6ISlKxhCooIDORwU3JeMl+XwV4fOVADLfV3jzOHLpzePrz2T8fndl6Y/Frc99OzYY1pWbuQmBCCFEFRDITED4yGzDKr5cn69e3iCfrwSQ+b6qW8cJe24ah51X8fe458ubFc/EKbGMH83ruXSjJhBCGrXdHjZngXvj163fdjVmndYzzM35Oay9OgI+X728Fz5fCSDzfSu3jdO++I74zFeq88qWt5Ib4qtrXBFH31veCsxcQwEhpIab6pHqJSB81Gs/q/I0Pl/17YTPV30W/k33KxZj4oxj3hYHHF2VF7bMdVwfx+84KY78VZlrMHctBYSQWm6rh6qDgPBRh12s5jP4fNW3Lz5fCSBveEu3j/HHbxT7HV7N17fYVd0Ypx42MQ4+qdhZzdYQASGkIRvtMfMRED7y2ascV+rzVd+u+XwlgLzhHR4b40/cOPY7NMeXu9trnhxnnjAh9j+i2/d1PwKvCnw4Lr19jZd/ztx/CBAoUeDu1rt4YWy/bolLMHXNBXy+6ttgn68EkPl0QM48aaP41CE1PweSHm9ynHXyBGEsycqgtgV0QtqmcyGB7gjofHTH0V36F9g+fL56VcjnKwFEAOnnvPCC+MdJQQJCSEHQpiHwegHhQ00UJSCAvLYD4l/wvqrhS+ivSHhBvCBFHcbm+TsBIURBEChYQPgoGLzh0/l85fPV/F4BAUQAeUNd6IA0/J8WxT9+K4Rcdsfqsd1axU9tRgLNEhA+mrXfVXhaAUQAEUD6eRO9IF6QKhzUDV6DENLgzffoxQgIH8U4m+XvBXy+8vlKABFAks5FHZAkJoO6LyCEdN/UHQm8JCB8KISyBAQQAUQAEUCSzh8BJInJoN4ICCG9cXXXBgsIHw3e/Ao8ugAigAggAkjSUSSAJDEZ1DsBIaR3tu7cMAHho2EbXsHHFUAEEAFEAEk6mgSQJCaDeisghPTW190bICB8NGCTM3hEAUQAEUAEkKSjSgBJYjKo9wJCSO+NzVBTAeGjphub4WMJIAKIACKAJB1dAkgSk0HFCAghxTibpUYCwkeNNrMGjyKACCACiACSdJQJIElMBhUnIIQUZ22mzAWEj8w3sIbLF0AEEAFEAEk62gSQJCaDihUQQor1NluGAsJHhpvWgCULIAKIACKAJB11AkgSk0HFCwghxZubMRMB4SOTjWrgMgUQAUQAEUCSjj4BJInJoHIEhJBy3M1aYQHho8KbY2khgAggAogAknQUCiBJTAaVJyCElGdv5ooJCB8V2xDLeYOAACKACCACSNLRKIAkMRlUroAQUq6/2SsgIHxUYBMsYaECAogAIoAIIAs9KOYNEECSmAwqX0AIKX8PrKAkAeGjJHjTDlhAABFABBABJOngEECSmAyqhoAQUo19sIoCBYSPArFN1bGAACKACCACSNJBIoAkMRlUHQEhpDp7YSU9FhA+egzs9l0XEEAEEAFEAEk6WASQJCaDqiUghFRrP6ymBwLCRw9Q3bLnAgKIACKACCBJB40AksRkUPUEhJDq7YkVdUlA+OgSpNsULiCACCACiACSdPAIIElMBlVTQAip5r5YVQcCwkcHeC4tXUAAEUAEEAEk6SASQJKYDKqugBBS3b2xsgEKCB8DBDO8cgICiAAigAggSQeTAJLEZFC1BYSQau+P1SUICB8JSIZUXkAAEUAEEAEk6aASQJKYEM46PgAAIABJREFUDKq+gBBS/T2ywgUICB9Koy4CAogAIoAIIEnnmQCSxGRQHgJCSB77ZJWvERA+lEOdBAQQAUQAEUCSzjQBJInJoHwEhJB89qrxKxU+Gl8CtQMQQAQQAUQASTrYBJAkJoPyEhBC8tqvRq5W+Gjkttf+oQUQAUQAEUCSDjoBJInJoPwEhJD89qwxKxY+GrPVjXtQAUQAEUAEkKSDTwBJYjIoTwEhJM99q/WqhY9ab2/jH04AEUAEEAEk6SAUQJKYDMpXQAjJd+9qt3Lho3Zb6oFeJyCACCACiACSdDAKIElMBuUtIITkvX+1WL3wUYtt9BALERBABBABRABJOigFkCQmg/IXEELy38Nsn0D4yHbrLHyAAgKIACKACCBJx4YAksRkUD0EhJB67GNWTyF8ZLVdFtuhgAAigAggAkjSMSKAJDEZVB8BIaQ+e1n5JxE+Kr9FFthlAQFEABFABJCkY0UASWIyqF4CQki99rOSTyN8VHJbLKrHAgKIACKACCBJx4wAksRkUP0EhJD67Wllnkj4qMxWWEjBAgKIACKACCBJx44AksRkUD0FhJB67mupTyV8lMpv8pIFBBABRAARQJKOIQEkicmg+goIIfXd28Kf7K6Y8Ncfx/vf0pp4buGTm5BABQQEEAFEABFAko4iASSJyaB6Cwgh9d7fQp7ulfCxfiGTmYRARQUEEAFEABFAko4nASSJyaD6Cwgh9d/jnj2h8NEzWjfOTEAAEUAEEAEk6dgSQJKYDGqGgBDSjH3u6lMKH13ldLPMBQQQAUQAEUCSjjEBJInJoOYICCHN2euOn1T46JjQDWomIIAIIAKIAJJ0rAkgSUwGNUtACGnWfrf1tMJHW2wuqrmAACKACCACSNIxJ4AkMRnUPAEhpHl7nvzEwkcylYENExBABBABRABJOvYEkCQmg5opIIQ0c9/7fWrhQ1EQWLCAACKACCACSNIZKYAkMRnUXAEhpLl7/4YnFz4UA4H+BQQQAUQAEUCSzkkBJInJoGYLCCHN3v+Xnl74UAQEFi4ggAggAogAsvCTojVCAEliMoiAENLgGhA+Grz5Hn1AAgKIACKACCBJh4YAksRkEIF5AkJIA+tA+GjgpnvktgUEEAFEABFAkg4QASSJySACrwoIIQ2qBeGjQZvtUbsiIIAIIAKIAJJ0mAggSUwGEXitgBDSgHoQPhqwyR6x6wICiAAigAggSQeLAJLEZBCB1wsIITWuCeGjxpvr0XoqIIAIIAKIAJJ0yAggSUwGEZjvmbp7XHbH6rHdWnjqIyB81GcvPUnxAgKIACKACCBJJ48AksRkEIEFCeiE1Kg2hI8abaZHKUVAABFABBABJOnwEUCSmAwi0J+AEFKD+hA+arCJHqF0AQFEABFABJCkg0gASWIyiMDCBAZ9MC65da3Y/s0LG+h/r56A8FG9PbGiPAUEEAFEABFAkk4vASSJySACSQKtEPIXISSJqjKDhI/KbIWF1EBAABFABBABJOkoE0CSmAwikCwghCRTlT5Q+Ch9CyygZgICiAAigAggSceaAJLEZBCBAQkIIQPiKmWw8FEKu0lrLiCACCACiACSdMwJIElMBhEYsIAQMmCywi4QPgqjNlHDBAQQAUQAEUCSjj0BJInJIAJtCQghbbH19CLho6e8bt5wAQFEABFABJCkY1AASWIyiEDbAkJI23Rdv1D46DqpGxL4OwEBRAARQASQpGNRAEliMohARwJCSEd8XblY+OgKo5sQ6FdAABFABBABJOmYFECSmAwi0LGAENIxYds3ED7apnMhgQEJCCACiAAigCQdGgJIEpNBBLoiIIR0hXFANxE+BsRlMIGOBAQQAUQAEUCSDhEBJInJIAJdExBCuka50BsJHwslMoBAVwUEEAFEABFAkg4VASSJySACXRUQQrrKOd+bCR+9NzYDgdcLCCACiAAigCSdjAJIEpNBBLouIIR0nfR/byh89M7WnQn0JyCACCACiACSdEoKIElMBhHoiYAQ0n1W4aP7pu5IIFVAABFABBABJOm8EECSmAwi0DMBIaR7tMJH9yzdiUA7AgKIACKACCBJZ4cAksRkEIGeCgghnfMKH50bugOBTgUEEAFEABFAks4RASSJySACPRcQQtonFj7at3MlgW4KCCACiAAigCSdKQJIEpNBBAoREEIGzix8DNzMFQR6JSCACCACiACSdL4IIElMBhEoTEAISacWPtKtjCRQhIAAIoAIIAJI0lkjgCQxGUSgUAEhZOHcwsfCjYwgULSAACKACCACSNK5I4AkMRlEoHABIWTB5MJH4eVoQgJJAgKIACKACCBJh4UAksRkEIFSBISQN7ILH6WUokkJJAkIIAKIACKAJB0WAkgSk0EEShMQQvrohY/SytDEBJIEBBABRAARQJIOCwEkickgAqUKCCERwkepJWhyAkkCAogAIoAIIEmHhQCSxGQQgdIFmhxChI/Sy88CCCQJCCACiAAigCQdFgJIEpNBBCoh0MQQInxUovQsgkCSgAAigAggAkjSYSGAJDEZRKAyAk0KIcJHZcrOQggkCQggAogAIoAkHRYCSBKTQQQqJdCEEHJn/PKWn8TOG1UK3mIIEOhXQAARQAQQASTpmBRAkpgMIlA5gTqHEOGjcuVmQQSSBAQQAUQAEUCSDgsBJInJIAKVFKhjCBE+KllqFkUgSUAAEUAEEAEk6bAQQJKYDCJQWYE6hRDho7JlZmEEkgQEEAFEABFAkg4LASSJySAClRaoQwgRPipdYhZHIElAABFABJB+XpWxMf7EjWO/Q5PeppoPujnOPOnS2P+wmj+mxyNQe4Fd4xd/Wid23jjHB709LrrxZ/HPm+a4dmsmQKBPwOerPgufr/osBnlJXhaQ0CV07wKBOgrsHD/4xfqx584RuRz3c+Mvcf7FF8feu9RxPzwTgaYJ+Hzl85UOiA5I0rknoScxGUQgG4Ht46xTN4p9P5vDgv0IaA67ZI0E0gV0QHRABJB+3hcJXUJPP06NJJCfwFbx9V3Wi91/MjxWX7SKq38q7pp1W/zoA1fGUROquD5rIkCgPQGfr3y+EkB0QJJODx2QJCaDCGQnsGWMGzky3vO71WKb1au0+Ltjwt8ejd//45Ux7qkqrctaCBDoXEAHRAdEANEBSTpJ/AhEEpNBBLIV2Ca+cei6sevXl45VSu2GPBP3vnhH/PyQy+Nzp2eLaeEECPQroAOiAyKA6IAkHZM6IElMBhHIXmCX+OHE1WLrrYbFCoV+Q31GPDpnSlz+61/EXttnj+gBCBDoV0AHRAdEABFAko5JASSJySACtREYG2efMTre9a8rxIZL9fKhHo6bnnkorj/Hr/nupbJ7E6iWgAAigAgg/byTWoRahNU6sq2GQPECW8UJu64Qb/nKKrHF+ovFkl1ZwPPx9Nz74jeTn4i/fv7KONIXzLui6iYE8hHw+crnKwFEByTpxNIBSWIyiECtBbaLbxy8RIzcZXisttESsdzSi8eyiw2LlQb399DT46E5rcAx67l47OlpMeVP0+ORn02Mz51ZaygPR4BAvwI6IDogAogAknRMCiBJTAYRaKTAe2LcakNi8XUGx9A15sTcuXPi+bsjnr+j9Rus7m8kiIcmQEAASawBn6/6oAr94mHi/pQyTItQi7CUwjMpAQIECBCosYDPVz5f6YD084J7QbwgNT7/PRoBAgQIEChFwOcrn68EEAEk6fDxd0CSmAwiQIAAAQIEFiIggAggAogAknRQCiBJTAYRIECAAAECAkhyDfh81UflOyCvWEjoEnryCWIgAQIECBAgkCTg85XPVzogOiBJh4WEnsRkEAECBAgQIKADklwDPl/pgLyhWCR0CT35BDGQAAECBAgQSBLw+crnKx0QHZCkw0JCT2IyiAABAgQIENABSa4Bn690QHRA+nldvCDJZ4mBBAgQIECAgH/Bm1QDPl8JIAKIAJJ0WBhEgAABAgQItC/gR7D67AQQAUQAEUDaP01dSYAAAQIECCQJCCACyPwKxa/hfUXFC+IFSTpJDSJAgAABAgSSBXy+8vlKAOnndfGCeEGST1MDCRAgQIAAgSQBn698vhJABJCkw8LPKCYxGUSAAAECBAgsREAAEUAEEAEk6aAUQJKYDCJAgAABAgQEkOQa8Pmqj8p3QF6xkNAl9OQTxEACBAgQIEAgScDnK5+vdEB0QJIOCwk9ickgAgQIECBAQAckuQZ8vtIBeUOxSOgSevIJYiABAgQIECCQJODzlc9XOiA6IEmHhYSexGQQAQIECBAgoAOSXAM+X+mA6ID087p4QZLPEgMJECBAgAAB/4I3qQZ8vhJABBABJOmwMIgAAQIECBBoX8CPYPXZCSACiAAigLR/mrqSAAECBAgQSBIQQASQ+RWKX8P7iooXxAuSdJIaRIAAAQIECCQL+Hzl85UA0s/r4gXxgiSfpgYSIECAAAECSQI+X/l8JYAIIEmHhZ9RTGIyiAABAgQIEFiIgAAigAggAkjSQSmAJDEZRIAAAQIECAggyTXg81Ufle+AvGIhoUvoySeIgQQIECBAgECSgM9XPl/pgOiAJB0WEnoSk0EECBAgQICADkhyDfh8pQPyhmKR0CX05BPEQAIECBAgQCBJwOcrn690QHRAkg4LCT2JySACBAgQIEBAByS5Bny+0gHRAenndfGCJJ8lBhIgQIAAAQL+BW9SDfh8JYAIIAJI0mFhEAECBAgQINC+gB/B6rMTQAQQAaSfs2Taut/95VdOWWli+8eNKwkQIECAAAECEV886OEth9/xfz7AIkIAEUAEkH5OgtE7fv+5gz40ewmHBQECBAgQIECgE4FTzhv01IOXfXTZTu5Rl2sFEAFEABFA6nKeeQ4CBAgQIFBZAQGkb2sEEAFEABFAKntYWxgBAgQIEKiLgAAigMyvlv0l9FdUfEmqrzz8CFZdjn3PQYAAAQIEyhUQQAQQAaSfd1AAEUDKPaLNToAAAQIE6icggAggAogAknSy6YAkMRlEgAABAgQILERAABFABBABJOmgFECSmAwiQIAAAQIEBJDkGvAl9D4q3wF5xcKPYPUVhQCSfJYYSIAAAQIECPQjoAOiA6IDogOSdEgKIElMBhEgQIAAAQI6IMk1oAOiA/KGYtEB0QFJPkEMJECAAAECBJIEdEB0QHRAdECSDgsdkCQmgwgQIECAAAEdkOQa0AHRAdEB6ed1EUCSzxIDCRAgQIAAgX4EdEB0QHRAdECSDkkBJInJIAIECBAgQEAHJLkGdEB0QHRAdECSDwwDCRAgQIAAgfYEdEB0QHRAdECSTg8dkCQmgwgQIECAAAEdkOQa0AHRAdEB0QFJPjAMJECAAAECBNoT0AHRAdEB0QFJOj10QJKYDCJAgAABAgR0QJJrQAdEB0QHRAck+cAwkAABAgQIEGhPQAdEB0QHRAck6fTQAUliMogAAQIECBDQAUmuAR0QHRAdEB2Q5APDQAIECBAgQKA9AR0QHRAdEB2QpNNDBySJySACBAgQIEBAByS5BnRAdEB0QHRAkg8MAwkQIECAAIH2BHRAdEB0QHRAkk4PHZAkJoMIECBAgAABHZDkGtAB0QHRAdEBST4wDCRAgAABAgTaE9AB0QHRAdEBSTo9dECSmAwiQIAAAQIEdECSa0AHRAdEB0QHJPnAMJAAAQIECBBoT0AHRAdEB0QHJOn00AFJYjKIAAECBAgQ0AFJrgEdEB0QHRAdkOQDw0ACBAgQIECgPQEdEB0QHRAdkKTTQwckickgAgQIECBAQAckuQZ0QHRAdEB0QJIPDAMJECBAgACB9gR0QHRAdEB0QJJODx2QJCaDCBAgQIAAAR2Q5BrQAdEB0QHRAUk+MAwkQIAAAQIE2hPQAdEB0QHRAUk6PXRAkpgMIkCAAAECBHRAkmtAB0QHRAdEByT5wDCQAAECBAgQaE9AB0QHRAdEByTp9NABSWIyiAABAgQIENABSa4BHRAdEB0QHZDkA8NAAgQIECBAoD0BHRAdEB0QHZCk00MHJInJIAIECBAgQEAHJLkGdEB0QHRAdECSDwwDCRAgQIAAgfYEdEB0QHRAdECSTg8dkCQmgwgQIECAAAEdkOQa0AHRAdEB0QFJPjAMJECAAAECBNoT0AHRAdEB0QFJOj10QJKYDCJAgAABAgR0QJJrQAdEB0QHRAck+cAwkAABAgQIEGhPQAdEB0QHRAck6fTQAUliMogAAQIECBDQAUmuAR0QHRAdEB2Q5APDQAIECBAgQKA9AR0QHRAdEB2QpNNDBySJySACBAgQIEBAByS5BnRAdEB0QHRAkg8MAwkQIECAAIH2BHRAdEB0QHRAkk4PHZAkJoMIECBAgAABHZDkGtAB0QHRAdEBST4wDCRAgAABAgTaE9AB0QHRAdEBSTo9dECSmAwiQIAAAQIEdECSa0AHRAdEB0QHJPnAMJAAAQIECBBoT0AHRAdEB0QHJOn00AFJYjKIAAECBAgQ0AFJrgEdEB0QHRAdkOQDw0ACBAgQIECgPQEdEB0QHRAdkKTTQwckickgAgQIECBAQAckuQZ0QHRAdEB0QJIPDAMJECBAgACB9gR0QHRAdEB0QJJODx2QJCaDCBAgQIAAAR2Q5BrQAdEB0QHRAUk+MAwkQIAAAQIE2hPQAdEB0QHRAUk6PXRAkpgMIkCAAAECBHRAkmtAB0QHRAdEByT5wDCQAAECBAgQaE9AB0QHRAdEByTp9NABSWIyiAABAgQIENABSa4BHRAdEB0QHZDkA8NAAgQIECBAoD0BHRAdEB0QHZCk00MHJInJIAIECBAgQEAHJLkGdEB0QHRAdECSDwwDCRAgQIAAgfYEdEB0QHRAdECSTg8dkCQmgwgQIECAAAEdkOQa0AHRAdEB0QFJPjAMJECAAAECBNoT0AHRAdEB0QFJOj10QJKYDCJAgAABAgR0QJJrQAdEB0QHRAck+cAwkAABAgQIEGhPQAdEB0QHpJ93Z2yMP3Hj2O/Q9l6vel31zODbX3xh0Wdm1+upPA0BAgQIECBQtMBiLw5fZPicNy1a9LxVnO/mOPOkS2P/w6q4tqLXNKjoCas63/atotgoPnVIVddnXQQIECBAgAABAvkK+BGsvr0TQF6xEEDyfaGtnAABAgQIECBQdQEBRAB5Q40KIFV/ba2PAAECBAgQIJCvgAAigAgg+b6/Vk6AAAECBAgQyE5AABFABJDsXlsLJkCAAAECBAjkKyCACCACSL7vr5UTIECAAAECBLITEEAEEAEku9fWggkQIECAAAEC+QoIIAKIAJLv+2vlBAgQIECAAIHsBAQQAUQAye61tWACBAgQIECAQL4CAogAIoDk+/5aOQECBAgQIEAgOwEBRAARQLJ7bS2YAAECBAgQIJCvgAAigAgg+b6/Vk6AAAECBAgQyE5AABFABJDsXlsLJkCAAAECBAjkKyCACCACSL7vr5UTIECAAAECBLITEEAEEAEku9fWggkQIECAAAEC+QoIIAKIAJLv+2vlBAgQIECAAIHsBAQQAUQAye61tWACBAgQIECAQL4CAogAIoDk+/5aOQECBAgQIEAgOwEBRAARQLJ7bS2YAAECBAgQIJCvgAAigAgg+b6/Vk6AAAECBAgQyE5AABFABJDsXlsLJkCAAAECBAjkKyCACCACSL7vr5UTIECAAAECBLITEEAEEAEku9fWggkQIECAAAEC+QoIIAKIAJLv+2vlBAgQIECAAIHsBAQQAUQAye61tWACBAgQIECAQL4CAogAIoDk+/5aOQECBAgQIEAgOwEBRAARQLJ7bS2YAAECBAgQIJCvgAAigAgg+b6/Vk6AAAECBAgQyE5AABFABJDsXlsLJkCAAAECBAjkKyCACCACSL7vr5UTIECAAAECBLITEEAEEAEku9fWggkQIECAAAEC+QoIIAKIAJLv+2vlBAgQIECAAIHsBAQQAWQ+AWT88RvFfodnV80WTIAAAQIECBAgUHmByXHmCRNi/yMqv9ACFjiogDmymGJMfHPc2+LT/zeLxVokAQIECBAgQIBAVgJ/ijPGXRYHfimrRfdosQLIK7DbxTcOfnscdEqPnN2WAAECBAgQIECgwQI3xTc+e3l87vQGE/zvowsgr1BsEyfsvUkc9n1FQYAAAQIECBAgQKDbAtfHCbtPiiN+1O375ng/AeSVXdsqvvaud8ZRv8txE62ZAAECBAgQIECg2gI3xtc2nhhfnFztVRazOgHkNc6Hxcy5i8TixcibhQABAgQIECBAoBECL8T0ODmW8rn7ld0G8Zqy3yf+8tzy8eahjXgTPCQBAgQIECBAgEAhAo/GLdP/MzZaqpDJMphEAHnNJu0Rl9+zWmyzegb7ZokECBAgQIAAAQKZCNwdl95+YWy/bibL7fkyBZDXEO8Y3zl/g/jYHj1XNwEBAgQIECBAgEBjBCbHuedMiE/u25gHXsiDCiCvAdoyjttpszjiYsVBgAABAgQIECBAoFsC18WXNvhtjLu1W/fL/T4CyOt28DPx8OxhsdLg3DfW+gkQIECAAAECBMoXeDrueeHMWHNI+SupzgoEkNftxW7xi5vXjp03qs4WWQkBAgQIECBAgECuArfFT679eXxw81zX34t1CyCvU90mTvzIJnHof/UC2z0JECBAgAABAgSaJfD7OHaLq+ILv23WU/f/tALIfHz2jdtmjog3+YMg3hQCBAgQIECAAIG2BR6OPz7z3XjHMm3foKYXCiDz2dgd4tvffWt8/KM13XOPRYAAAQIECBAgUIDA5DjzhAmx/xEFTJXVFALIfLZrTBy+5Lpx5LRhsSKfrMrZYgkQIECAAAEC1RB4Ou5qffl87Xk/UTO3Giuqzip8wF7AXuwcP7h4/dhrp+pslZUQIECAAAECBAjkInBL/Oe3L4lP7JPLeotcpwCyAO3N48ilN46Dn1wqRi1S5IaYiwABAgQIECBAIG+BJ1rfJz4n1hum+zH/fRRA+qnv7eL0/3h7HPilvF8BqydAgAABAgQIEChOYG7cECd99Io43G9VXQC6ALKQatwjJt2/Wmw5uriiNRMBAgQIECBAgECuAnfGxZN/ErtsnOv6i1i3ALIQ5ffGF1Z+axwwZelYZdEiNsQcBAgQIECAAAECeQrM+9GrP8f45a6Lk5/L8wmKWbUAkuC8dRz3oXfE5340OBZLGG0IAQIECBAgQIBA0wSej6fj5jj93VfG0b9r2rMP9HkFkESxMXHG8RvHpw4fFL6TnkhmGAECBAgQIECgEQIvxIxo/c2PT06MQ89txAN3+JACyAAAx8aZp20c+34mAtsA2AwlQIAAAQIECNRWYHbManU+vvXZy+Nzp9f2Ibv8YD5JDxB0hzj3nLf6nc4DVDOcAAECBAgQIFBPgZvi9KMvj89+tZ5P15unEkDacB0b40/cMPY5dHD4XnobfC4hQIAAAQIECGQv8EJMb/3Y1dmfmRiHfDP7hyn4AQSQNsFb3wk5ZoP42NGLxby/MeM/BAgQIECAAAECTRF4Lh6fe2t87+Ot73x8tynP3M3nFEA60Nwyjt12vdj9V8vEmn49VgeOLiVAgAABAgQI5CLwSNw87a74xea/if+4JZc1V22dAkiHOzImxq20TPzj1WvG2Dd1eCuXEyBAgAABAgQIVFjgtvjpdT+P3f6pwkvMYmkCSJe2acs4bqdV4r3/OSr+cYUu3dJtCBAgQIAAAQIEShaYG3NiSky8a2pc/6HfxhdvKnk5tZheAOnyNm4bJx+wamx93Iqx0VJdvrXbESBAgAABAgQIFCYwN+6L3059OP6w/xVxxMWFTduAiQSQHm3y2Pjml1eIjfYdHe8Z2aMp3JYAAQIECBAgQKDLArPimXggfnf7Y3HL5yfF4T/t8u3driUggPS4DOZ9R2RwrPLlEbHOTiNj09GLxZI9ntHtCRAgQIAAAQIEBiIw77daPRi/u/WZuOeCy+IzXxnItcYOXEAAGbhZR1fM64wsGaO2HxYrrbFUjB4xPFb3x0Q6EnUxAQIECBAgQCBd4MWY2epxTJk5PR58fHo8csf0mHr+xPjcmel3MLJTAQGkU0HXEyBAgAABAgQIECCQLCCAJFMZSIAAAQIECBAgQIBApwICSKeCridAgAABAgQIECBAIFlAAEmmMpAAAQIECBAgQIAAgU4FBJBOBV1PgAABAgQIECBAgECygACSTGUgAQIECBAgQIAAAQKdCgggnQq6ngABAgQIECBAgACBZAEBJJnKQAIECBAgQIAAAQIEOhUQQDoVdD0BAgQIECBAgAABAskCAkgylYEECBAgQIAAAQIECHQqIIB0Kuh6AgQIECBAgAABAgSSBQSQZCoDCRAgQIAAAQIECBDoVEAA6VTQ9QQIECBAgAABAgQIJAsIIMlUBhIgQIAAAQIECBAg0KmAANKpoOsJECBAgAABAgQIEEgWEECSqQwkQIAAAQIECBAgQKBTAQGkU0HXEyBAgAABAgQIECCQLCCAJFMZSIAAAQIECBAgQIBApwICSKeCridAgAABAgQIECBAIFlAAEmmMpAAAQIECBAgQIAAgU4FBJBOBV1PgAABAgQIECBAgECygACSTGUgAQIECBAgQIAAAQKdCgggnQq6ngABAgQIECBAgACBZAEBJJnKQAIECBAgQIAAAQIEOhUQQDoVdD0BAgQIECBAgAABAskCAkgylYEECBAgQIAAAQIECHQqIIB0Kuh6AgQIECBAgAABAgSSBQSQZCoDCRAgQIAAAQIECBDoVEAA6VTQ9QQIECBAgAABAgQIJAsIIMlUBhIgQIAAAQIECBAg0KmAANKpoOsJECBAgAABAgQIEEgWEECSqQwkQIAAAQIECBAgQKBTAQGkU0HXEyBAgAABAgQIECCQLCCAJFMZSIAAAQIECBAgQIBApwICSKeCridAgAABAgQIECBAIFlAAEmmMpAAAQIECBAgQIAAgU4FBJAdyRc2AAAEpUlEQVROBV1PgAABAgQIECBAgECygACSTGUgAQIECBAgQIAAAQKdCgggnQq6ngABAgQIECBAgACBZAEBJJnKQAIECBAgQIAAAQIEOhUQQDoVdD0BAgQIECBAgAABAskCAkgylYEECBAgQIAAAQIECHQqIIB0Kuh6AgQIECBAgAABAgSSBQSQZCoDCRAgQIAAAQIECBDoVEAA6VTQ9QQIECBAgAABAgQIJAsIIMlUBhIgQIAAAQIECBAg0KmAANKpoOsJECBAgAABAgQIEEgWEECSqQwkQIAAAQIECBAgQKBTAQGkU0HXEyBAgAABAgQIECCQLCCAJFMZSIAAAQIECBAgQIBApwICSKeCridAgAABAgQIECBAIFlAAEmmMpAAAQIECBAgQIAAgU4FBJBOBV1PgAABAgQIECBAgECygACSTGUgAQIECBAgQIAAAQKdCgggnQq6ngABAgQIECBAgACBZAEBJJnKQAIECBAgQIAAAQIEOhUQQDoVdD0BAgQIECBAgAABAskCAkgylYEECBAgQIAAAQIECHQqIIB0Kuh6AgQIECBAgAABAgSSBQSQZCoDCRAgQIAAAQIECBDoVEAA6VTQ9QQIECBAgAABAgQIJAsIIMlUBhIgQIAAAQIECBAg0KmAANKpoOsJECBAgAABAgQIEEgWEECSqQwkQIAAAQIECBAgQKBTAQGkU0HXEyBAgAABAgQIECCQLCCAJFMZSIAAAQIECBAgQIBApwICSKeCridAgAABAgQIECBAIFlAAEmmMpAAAQIECBAgQIAAgU4FBJBOBV1PgAABAgQIECBAgECygACSTGUgAQIECBAgQIAAAQKdCgggnQq6ngABAgQIECBAgACBZAEBJJnKQAIECBAgQIAAAQIEOhUQQDoVdD0BAgQIECBAgAABAskCAkgylYEECBAgQIAAAQIECHQqIIB0Kuh6AgQIECBAgAABAgSSBQSQZCoDCRAgQIAAAQIECBDoVEAA6VTQ9QQIECBAgAABAgQIJAsIIMlUBhIgQIAAAQIECBAg0KmAANKpoOsJECBAgAABAgQIEEgWEECSqQwkQIAAAQIECBAgQKBTAQGkU0HXEyBAgAABAgQIECCQLCCAJFMZSIAAAQIECBAgQIBApwICSKeCridAgAABAgQIECBAIFlAAEmmMpAAAQIECBAgQIAAgf/ffh3aAAAAIAz7/2teIJmtB1O3KiBAqqA/AQIECBAgQIAAAQK3gAC5qQwJECBAgAABAgQIEKgCAqQK+hMgQIAAAQIECBAgcAsIkJvKkAABAgQIECBAgACBKiBAqqA/AQIECBAgQIAAAQK3gAC5qQwJECBAgAABAgQIEKgCAqQK+hMgQIAAAQIECBAgcAsIkJvKkAABAgQIECBAgACBKiBAqqA/AQIECBAgQIAAAQK3gAC5qQwJECBAgAABAgQIEKgCAqQK+hMgQIAAAQIECBAgcAsIkJvKkAABAgQIECBAgACBKiBAqqA/AQIECBAgQIAAAQK3gAC5qQwJECBAgAABAgQIEKgCAqQK+hMgQIAAAQIECBAgcAsIkJvKkAABAgQIECBAgACBKjBXjy7ykJetlAAAAABJRU5ErkJggg==
<use xlink:href="#download-a"/>
</mask>
<use fill="#D8D8D8" fill-rule="nonzero" xlink:href="#download-a"/>
<g fill="#FFA0A0" mask="url(#download-b)">
<rect width="24" height="24" transform="translate(-4 -9)"/>
</g>
</g>
<mask id="download-d" fill="#ffffff">
<use xlink:href="#download-c"/>
</mask>
<use fill="#000000" fill-rule="nonzero" xlink:href="#download-c"/>
<g fill="#7600FF" mask="url(#download-d)">
<rect width="24" height="24" transform="translate(-2 -3)"/>
</g>
</g>
</svg>`
function svgToPng(svg, callback) {
const url = getSvgUrl(svg);
svgUrlToPng(url, (imgData) => {
callback(imgData);
URL.revokeObjectURL(url);
});
}
function getSvgUrl(svg) {
return URL.createObjectURL(new Blob([svg], {type: 'image/svg+xml'}));
}
function svgUrlToPng(svgUrl, callback) {
const svgImage = document.createElement('img');
// can't be display none, but also don't take up space
svgImage.style.position = "fixed";
svgImage.style.visibility = "hidden";
document.body.appendChild(svgImage);
svgImage.onload = function () {
const canvas = document.createElement('canvas');
canvas.width = svgImage.clientWidth;
canvas.height = svgImage.clientHeight;
const canvasCtx = canvas.getContext('2d');
canvasCtx.drawImage(svgImage, 0, 0);
const imgData = canvas.toDataURL('image/png');
callback(imgData);
// document.body.removeChild(imgPreview);
};
svgImage.src = svgUrl;
}
// Utility
//------------------------------
function error(str) {
console.error(`[CSRD]: ${str}`);
}
function log(str) {
console.log(`[CSRD]: ${str}`);
}
function info(str) {
console.info(`[CSRD]: ${str}`);
}
function debug(str) {
console.debug(`[CSRD]: ${str}`);
}
async function sleep(ms) {
await new Promise(r => setTimeout(r, ms)); // give the browser a break - idle wait
}
// How to get the browser viewport dimensions?
// Thanks, ryanve
// https://stackoverflow.com/a/8876069
function viewportX() {
return Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
}
function viewportY() {
return Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
}
function digitCount(num) {
return num.toString().length;
}
function dataUrlToData(base64Url) {
return base64Url.substr(base64Url.indexOf(',') + 1);
}
// Reader Utility
//------------------------------
// positive flips forward (increase page number), negative flips backwards (decrease page number), 0 is to open menu
function flipPage(direction) {
const screen = getCSRElement(ELEMENT.SCREEN_CONTROLLER);
// click on the left, middle, or right of the screen depending on arg
const x = (-direction + 1) * viewportX() / 2;
screen.dispatchEvent(new PointerEvent("pointerdown", {buttons: 1, clientX: x, clientY: 100, bubbles: true}));
screen.dispatchEvent(new PointerEvent("pointerup", {buttons: 0, clientX: x, clientY: 100, bubbles: true}));
}
async function waitForPageLoad() {
while (isLoadingPage()) {
await sleep(100);
}
}
function currentPage() {
return +getCSRElement(ELEMENT.CURRENT_PAGE_COUNTER).textContent;
}
function totalPageCount() {
return +getCSRElement(ELEMENT.TOTAL_PAGE_COUNTER).textContent;
}
function isLoadingPage() {
return getCSRElement(ELEMENT.LOADER_SPINNER).classList.contains("onstage");
}
function isMenuOpen() {
return getCSRElement(ELEMENT.MENU).style.display !== "none" && getCSRElement(ELEMENT.MENU).classList.contains("onstage");
}
async function flipToFirstPage() {
const slider = getCSRElement(ELEMENT.PAGE_SLIDER);
if (slider === undefined && currentPage() !== 1) {
throw Error("This reader's automatic page slider is not supported, please move to page 1 manually and click the download button again");
}
if (currentPage() === 1) {
info(`already on first page`);
return;
}
info(`flipping to first page`);
if (!isMenuOpen()) {
debug("opening menu to load scroller");
flipPage(0); // open menu
await sleep(200);
}
// click the very right of the page slider - page 1
slider.dispatchEvent(new PointerEvent("pointerdown", {
buttons: 1,
clientX: viewportX() - 25,
clientY: viewportY() - 70,
bubbles: true
}));
slider.dispatchEvent(new PointerEvent("pointerup", {
buttons: 0,
clientX: viewportX() - 25,
clientY: viewportY() - 70,
bubbles: true
}));
await sleep(100);
await waitForPageLoad();
}
// Main
//------------------------------
async function generatePageByPageZip() {
const jsZip = new JSZip();
let totalPages = totalPageCount();
if (totalPages === 0) {
throw Error("The total number of pages reported by the reader is 0, the page slider seems to have been opened incorrectly");
}
info(`there are ${totalPages} pages in total to save`);
const zeroPads = digitCount(totalPages);
let pageNumber = 1;
let lastReportedReaderPageNumber; // this helps distinguish a 1 page spread on 2 canvases, vs the reader always rendering 1 page only because the viewport is too narrow
// any time the spread is not 2 canvases, this will no longer match pageNumber. But that is ok, we prioritize continuity of pageNumber instead (e.g. page 01, 02, 04 is bad!)
let expectedPagesInSpread = 2; // a 2 page spread is normal. But we have to handle when the viewport is narrow enough that only 1 is shown
log("==== downloading pages: ====");
while (true) {
const element_spread = getCSRElement(ELEMENT.PAGE_SPREAD);
// Right to left means reverse the array
for (const canvas of [...element_spread.children].toReversed()) {
if (canvas.style.visibility === "hidden" || canvas.style.display === "none") {
debug(`page before ${pageNumber} is a hidden or junk page, skipping it`);
//possibly the unseen half of a 1 page spread on 2 canvases. Need to check after this canvas spread is completed and the page is flipped, what to do
expectedPagesInSpread--;
continue;
}
info(`saving page ${pageNumber}`);
jsZip.file(`${pageNumber.toString().padStart(zeroPads, "0")}.png`, dataUrlToData(canvas.toDataURL()), {base64: true});
pageNumber++;
}
if (pageNumber > totalPages) {
break;
}
// Need to check after this canvas spread is completed and the page is flipped, what to do
lastReportedReaderPageNumber = currentPage();
await sleep(100);
flipPage(1);
await sleep(100);
await waitForPageLoad();
debug(`currentPage() - lastReportedReaderPageNumber = ${currentPage()} - ${lastReportedReaderPageNumber}`);
if (currentPage() - lastReportedReaderPageNumber > expectedPagesInSpread) {
// there are less pages than we thought! - the reader reports that currentPage() - lastDownloadedPageNumber passed (usually 2)
// but we only saw expectedPages and incremented pageNumber by that much (usually 1 when this branch is entered)! decrement totalPages so we don't run over the totalPages with pageNumber
totalPages -= (currentPage() - lastReportedReaderPageNumber) - expectedPagesInSpread;
debug(`cutting ${(currentPage() - lastReportedReaderPageNumber) - expectedPagesInSpread} from total pages, now ${totalPages}`)
}
expectedPagesInSpread = 2; // reset this
await sleep(100);
}
return jsZip;
}
async function generateDirectZip() {
const jsZip = new JSZip();
const pageHrefs = getCSRPageHrefs();
const totalPages = pageHrefs.length;
info(`there are ${totalPages} pages in total to save`);
const zeroPads = digitCount(totalPages);
log("==== downloading pages: ====");
for (const [i, href] of pageHrefs.entries()) {
jsZip.file(`${i.toString().padStart(zeroPads, "0")}.png`, await (await fetch(href)).blob());
}
return jsZip;
}
function injectErrorMessage(message) {
if (document.getElementById(errorMessageId) !== null) {
document.getElementById(errorMessageId).remove();
}
const div = document.createElement("div");
div.id = errorMessageId;
div.style["position"] = "fixed";
div.style["border-radius"] = "5%";
div.style["z-index"] = 900;
div.style["bottom"] = "150px";
div.style["right"] = "32px";
div.style["background"] = "black";
div.style["border"] = "3px solid purple";
div.style["padding"] = "8px";
div.style["cursor"] = "pointer";
div.style["color"] = "red";
document.body.appendChild(div);
const text = document.createTextNode(message);
div.appendChild(text);
}
async function downloadBookAsZip() {
let jsZip;
try {
switch (downloadMode()) {
case DOWNLOAD_MODE.PAGE_BY_PAGE:
if (!isMenuOpen()) {
info("opening menu to load scroller");
flipPage(0); // open menu
await sleep(200);
}
await flipToFirstPage();
jsZip = await generatePageByPageZip();
break;
case DOWNLOAD_MODE.DIRECT:
jsZip = await generateDirectZip();
break;
}
} catch (exception) {
injectErrorMessage(exception.message);
throw exception;
}
log("generating zip file, rename it however you like - this script cannot figure out the book's name");
await jsZip.generateAsync({type: "blob"}).then(blob => saveAs(blob, "Clip_Studio_Reader_Downloader_RENAME_ME.zip"));
log("==== all done! Enjoy your book ^_^ ====");
}
function injectDownloadButton() {
log("reader is loaded, download button injected");
const parent = document.body;
const div = document.createElement("div");
parent.appendChild(div);
div.id = downloadButtonId;
// not all sites use tailwind
div.style["position"] = "fixed";
div.style["border-radius"] = "50%";
div.style["z-index"] = 900;
div.style["bottom"] = "32px";
div.style["right"] = "32px";
div.style["background"] = "black";
div.style["border"] = "3px solid purple";
div.style["padding"] = "8px";
div.style["cursor"] = "pointer";
div.addEventListener("pointerdown", downloadBookAsZip);
svgToPng(downloadSvg, (imgData) => {
const image = document.createElement('img');
image.style.height = "64px";
div.appendChild(image);
image.src = imgData;
});
}
function checkReaderLoad(observer, timeoutId) {
if (currentPage() !== 0) {
observer.disconnect();
// stop the 30 second timeout
clearTimeout(timeoutId);
injectDownloadButton();
}
}
// Userscript to wait for page to load before executing code techniques?
// Thanks, goweon
// https://stackoverflow.com/a/47406751
function checkPageLoad(observer) {
if (getCSRElement(ELEMENT.PAGE_SPREAD)) {
observer.disconnect();
log("==== Clip Studio Reader Downloader ====");
log("https://github.com/MrCocoNuat/clip-studio-reader-downloader");
log("waiting up to 30 seconds for reader to load");
switch (downloadMode()) {
case DOWNLOAD_MODE.PAGE_BY_PAGE:
let readerObserver;
const timeoutId = setTimeout(async () => {
error("ERR: reader load timeout. the reader seems to have started incorrectly or the reader may have taken too long to load - do you need to reopen the book?");
readerObserver.disconnect();
}, 30000);
readerObserver = new MutationObserver((changes, innerObserver) => checkReaderLoad(innerObserver, timeoutId));
readerObserver.observe(getCSRElement(ELEMENT.CURRENT_PAGE_COUNTER), {childList: true, subtree: true});
break;
case DOWNLOAD_MODE.DIRECT:
injectDownloadButton(); // not much to wait for
break;
}
}
}
function init() {
if (siteIsSupported()) {
(new MutationObserver((changes, observer) => checkPageLoad(observer))).observe(document, {
childList: true,
subtree: true
});
} else {
log("No instance of Clip Studio Reader found on the current page");
}
}