// ==UserScript== // @name eze // @version 1.0.9.3 // @author dnsev-h // @namespace dnsev-h // @homepage https://dnsev-h.github.io/eze/ // @description Additional features for E*Hentai // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM.xmlHttpRequest // @grant GM.getValue // @grant GM.setValue // @grant GM.deleteValue // @run-at document-start // @include http://exhentai.org/* // @include https://exhentai.org/* // @include http://e-hentai.org/* // @include https://e-hentai.org/* // @connect exhentai.org // @connect e-hentai.org // @connect ehgt.org // @connect * // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAQMAAABtzGvEAAAABlBMVEUAAABmBhHV14kpAAAAAXRSTlMAQObYZgAAADFJREFUeAFjIB4w//9BLPWBgSLq//HH/w8QQYE18GOj6hgwKCBCpcDOZQaZQpgiGgAA0dhUnSJVLdEAAAAASUVORK5CYII= // @updateURL https://raw.githubusercontent.com/dnsev-h/eze/master/builds/eze.meta.js // @downloadURL https://raw.githubusercontent.com/dnsev-h/eze/master/builds/eze.user.js // ==/UserScript== // Main scope (function (window, _GM) { "use strict"; // Tampermonkey bug fix if (window.document === undefined) window = window.unsafeWindow; var document = window.document; // Greasemonkey 4 compatibility var to_promise = function (fn, self) { return function () { var args = arguments; return new Promise(function (resolve, reject) { try { resolve(fn.apply(self, args)); } catch (e) { reject(e); } }); }; }; var to_promise_immediate = function (fn, self) { return function () { var args = arguments; var exc = null; var result, p; try { result = fn.apply(self, args); } catch (e) { exc = e; } p = new Promise(function (resolve, reject) { p._result = result; if (exc === undefined) { resolve(result); } else { reject(exc); } }); p._result = result; return p; }; }; var GM = (function (GM) { if (GM !== null && typeof(GM) === "object") { return GM; } var mapping = [ [ "getValue", "GM_getValue", false ], [ "setValue", "GM_setValue", false ], [ "deleteValue", "GM_deleteValue", false ], [ "xmlHttpRequest", "GM_xmlhttpRequest", true ] ]; GM = {}; var m, i, ii; for (i = 0, ii = mapping.length; i < ii; ++i) { m = mapping[i]; GM[m[0]] = (m[2] ? to_promise_immediate : to_promise)(this[m[1]], this); } return GM; }).call(this, _GM); // Hash updating var Hash = (function () { var Hash = function () { var self = this; // Vars this.path = ""; this.path_array = []; this.vars = {}; this.vars_array = []; this.is_inited = false; // Change listeners this.onchange_listener = function () { trigger_change.call(self, "pop"); }; this.change_listeners = []; }; var ParsedHash = function (hash_part) { this.path = ""; if (hash_part === null) { // Init to empty this.path_array = []; this.vars = {}; this.vars_array = []; } else { // Don't init to objects this.path_array = null; this.vars = null; this.vars_array = null; parse_state.call(this, hash_part); } }; var hash_sep = "#!", re_decode_var = /^(.*?)(?:=(.*))?$/, re_parts = /^(.*?)(?:\?(.*?))?$/, re_hash_find = /#(.*)$/, re_remove_slashes = /^\/+|\/{2,}/g, re_encode_replacer = /\+|%20/ig, encode_map = { "+": "%2B", "%20": "+" }; var parse_state = function (h) { var i, m, v; // Normalize for (i = 0; i < hash_sep.length && h[i] === hash_sep[i]; ++i); if (i > 0) h = h.substr(i); // Match m = re_parts.exec(h); // Parse path this.path = m[1].replace(re_remove_slashes, ""); this.path_array = this.path.split("/"); for (i = 0; i < this.path_array.length; ++i) { this.path_array[i] = decodeURIComponent(this.path_array[i]); } // Parse vars v = Hash.decode_vars(m[2] || ""); this.vars = v[0]; this.vars_array = v[1]; }; var trigger_change = function (reason) { // Update state parse_state.call(this, window.location.hash); // Trigger a change event for (var i = 0; i < this.change_listeners.length; ++i) { this.change_listeners[i].call(null, this, reason); } }; Hash.prototype = { init: function () { if (this.is_inited) { return; } // Events window.addEventListener("popstate", this.onchange_listener, false); // Init trigger this.is_inited = true; trigger_change.call(this, "init"); }, call: function (callback) { callback.call(null, this, "call"); }, on_change: function (callback) { this.change_listeners.push(callback); }, off_change: function (callback) { for (var i = 0; i < this.change_listeners.length; ++i) { if (this.change_listeners[i] == callback) { this.change_listeners.splice(i, 1); return true; } } return false; }, }; Hash.sep = hash_sep; Hash.decode = function (url) { var m = re_hash_find.exec(url), obj; if (m !== null) { obj = new ParsedHash(m[0]); } else { obj = new ParsedHash(null); } return obj; }; Hash.encode = function (path, vars) { var str = hash_sep; if (typeof(path) == "string") { str += path; } else { str += path.join("/"); } if (vars) { str += "?"; str += Hash.encode_vars(vars); } return str; }; Hash.encode_component = function (c) { return encodeURIComponent(c).replace(re_encode_replacer, function (m) { return encode_map[m]; }); }; Hash.decode_component = function (c) { return decodeURIComponent(c.replace(/\+/g, " ")); }; Hash.encode_vars = function (vars) { var str = "", first = true, v; if (Array.isArray(vars)) { for (v = 0; v < vars.length; ++v) { if (v > 0) str += "&"; str += Hash.encode_component(vars[v][0]); if (vars[v][1] !== null) { str += "="; str += Hash.encode_component(vars[v][1]); } } } else { for (v in vars) { if (first) first = false; else str += "&"; str += Hash.encode_component(v); if (vars[v] !== null) { str += "="; str += Hash.encode_component(vars[v]); } } } return str; }; Hash.decode_vars = function (var_str) { var vars = {}, vars_array = [], m, k, v, s, i; s = var_str.split("&"); for (i = 0; i < s.length; ++i) { // Skip if (s[i].length === 0) continue; // Match m = re_decode_var.exec(s[i]); // Set the var k = Hash.decode_component(m[1]); v = (m[2] === undefined) ? null : Hash.decode_component(m[2]); vars[k] = v; vars_array.push([ k , v ]); } return [ vars , vars_array ]; }; return Hash; })(); // Generic classes/modules // Ready state var on_ready = (function () { // Vars var callbacks = [], check_interval = null, check_interval_time = 250; // Check if ready and run callbacks var callback_check = function () { if ( (document.readyState === "interactive" || document.readyState === "complete") && callbacks !== null ) { // Run callbacks var cbs = callbacks, cb_count = cbs.length, i; // Clear callbacks = null; for (i = 0; i < cb_count; ++i) { cbs[i].call(null); } // Clear events and checking interval window.removeEventListener("load", callback_check, false); window.removeEventListener("readystatechange", callback_check, false); if (check_interval !== null) { clearInterval(check_interval); check_interval = null; } // Okay return true; } // Not executed return false; }; // Listen window.addEventListener("load", callback_check, false); window.addEventListener("readystatechange", callback_check, false); // Callback adding function return function (cb) { if (callbacks === null) { // Ready to execute cb.call(null); } else { // Delay callbacks.push(cb); // Set a check interval if (check_interval === null && callback_check() !== true) { check_interval = setInterval(callback_check, check_interval_time); } } }; })(); // Date formatting var date_format = (function () { var months = [ "January" , "February" , "March" , "April" , "May" , "June" , "July" , "August" , "September" , "October" , "November" , "December" ], months_short = [ "Jan" , "Feb" , "Mar" , "Apr" , "May" , "Jun" , "Jul" , "Aug" , "Sep" , "Oct" , "Nov" , "Dec" ], days = [ "Sunday" , "Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" ], days_short = [ "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" ], ordinals = [ "th" , "st" , "nd" , "rd" ], formatter_keys = [], re_formatter, k, formatters; formatters = { d: function (date) { // Day of the month, 2 digits with leading zeros var s = date.getDate().toString(); if (s.length < 2) s = "0" + s; return s; }, j: function (date) { // Day of the month without leading zeros return date.getDate().toString(); }, l: function (date) { // A full textual representation of the day of the week return days[date.getDay()]; }, D: function (date) { // A textual representation of a day, three letters return days_short[date.getDay()]; }, S: function (date) { // English ordinal suffix for the day of the month, 2 characters var i = (date.getDate() - 1); // % 100 if ((i < 10 || i > 19) && (i = i % 10) <= 3) return ordinals[i]; return ordinals[0]; }, w: function (date) { // Numeric representation of the day of the week return date.getDay().toString(); }, F: function (date) { // A full textual representation of a month, such as January or March return months[date.getMonth()]; }, M: function (date) { // A short textual representation of a month, three letters return months_short[date.getMonth()]; }, m: function (date) { // Numeric representation of a month, with leading zeros var s = (date.getMonth() + 1).toString(); if (s.length < 2) s = "0" + s; return s; }, n: function (date) { // Numeric representation of a month, without leading zeros return (date.getMonth() + 1).toString(); }, y: function (date) { // Year, 2 digits return date.getFullYear().toString().substr(2); }, Y: function (date) { // A full numeric representation of a year, 4 digits return date.getFullYear().toString(); }, a: function (date) { // Lowercase Ante meridiem and Post meridiem return (date.getHours() >= 11 && date.getHours() <= 22 ? "pm" : "am"); }, A: function (date) { // Uppercase Ante meridiem and Post meridiem return (date.getHours() >= 11 && date.getHours() <= 22 ? "PM" : "AM"); }, g: function (date) { // 12-hour format of an hour without leading zeros return ((date.getHours() % 12) + 1).toString(); }, h: function (date) { // 12-hour format of an hour with leading zeros var s = ((date.getHours() % 12) + 1).toString(); if (s.length < 2) s = "0" + s; return s; }, G: function (date) { // 24-hour format of an hour without leading zeros return date.getHours().toString(); }, H: function (date) { // 24-hour format of an hour with leading zeros var s = date.getHours().toString(); if (s.length < 2) s = "0" + s; return s; }, i: function (date) { // Minutes with leading zeros var s = date.getMinutes().toString(); if (s.length < 2) s = "0" + s; return s; }, s: function (date) { // Seconds with leading zeros var s = date.getSeconds().toString(); if (s.length < 2) s = "0" + s; return s; }, u: function (date) { // Milliseconds (note: this is different from PHP) var s = date.getMilliseconds().toString(); if (s.length < 2) s = "00" + s; else if (s.length < 3) s = "0" + s; return s; }, }; for (k in formatters) formatter_keys.push(k); formatter_keys.sort(); re_formatter = new RegExp("(\\\\*)([" + formatter_keys.join("").replace(/([^a-zA-Z0-9])/g, "\\$1") + "])", "g"); // Final function return function (date, format) { // https://php.net/manual/en/function.date.php if (typeof(date) == "number") date = new Date(date); return format.replace(re_formatter, function (full, esc, fmt) { if (esc.length > 0) { if ((esc.length % 2) == 1) { // Escaped return esc.substr(0, (esc.length - 1) / 2) + fmt; } // Remove some escapes return esc.substr(0, esc.length / 2) + formatters[fmt](date); } // Not escaped return formatters[fmt](date); }); }; })(); // Zip creator var ZipCreator = (function () { var ZipCreator = function () { this.files = []; this.comment = ""; this.comment_data = string_to_array(this.comment, max_lengths.comment); this.date = new Date(); }; var max_lengths = { data: 0XFFFFFFFF, name: 0xFFFF, extra: 0xFFFF, comment: 0xFFFF, }; var signatures = { file: new Uint8Array([ 0x50 , 0x4B , 0x03 , 0x04 ]), cd: new Uint8Array([ 0x50 , 0x4B , 0x01 , 0x02 ]), footer: new Uint8Array([ 0x50 , 0x4B , 0x05 , 0x06 ]), }; var data_sizes = { file: 26, // 30 - 4 cd: 42, // 46 - 4 footer: 18, // 22 - 4 }; var ZIP_FLAG_UTF8 = 1 << 11; var typeof_str = typeof(""); var FileData = function (data, filename, extra, comment, date) { // Setup this.set_data(data); this.set_name(filename); this.set_extra(extra); this.set_comment(comment); this.set_date(date); // Other stuff this.offset = 0; this.crc = null; this.utf8 = true; }; FileData.prototype = { constructor: FileData, prepare: function (offset) { this.offset = offset; this.utf8 = ( this.name.length != this.name_data.length || this.extra.length != this.extra_data.length || this.comment.length != this.comment_data.length ); if (this.crc === null) { this.crc = crc32(this.data); } }, set_data: function (data) { if (typeof(data) == typeof_str) { // Convert to array this.data = string_to_array(data, max_lengths.data); } else { // Set and truncate if necessary this.data = data; if (this.data.length > max_lengths.data) { this.data = this.data.subarray(0, max_lengths.data); } } this.crc = null; }, set_name: function (filename) { this.name = filename; this.name_data = string_to_array(this.name, max_lengths.name); }, set_extra: function (extra) { this.extra = extra; this.extra_data = string_to_array(this.extra, max_lengths.extra); }, set_comment: function (comment) { this.comment = comment; this.comment_data = string_to_array(this.comment, max_lengths.comment); }, set_date: function (date) { this.date = date; this.date_zip = date_to_ziptime(this.date); }, }; var BufferWriter = function (buffer) { this.buffer = buffer; this.pos = 0; this.buffers = buffer; }; BufferWriter.prototype = { constructor: BufferWriter, reset: function (buffer) { this.buffer = buffer; this.pos = 0; }, get_pos: function () { return this.pos; }, write_ushort: function (value) { // Bound value = (value & 0x0000FFFF) >>> 0; // Write for (var i = 0; i < 2; ++i) { this.buffer[this.pos++] = value & 0xFF; value = value >>> 8; } }, write_uint: function (value) { // Bound value = (value & 0xFFFFFFFF) >>> 0; // Write for (var i = 0; i < 4; ++i) { this.buffer[this.pos++] = value & 0xFF; value = value >>> 8; } }, write_data: function (data) { // Write for (var i = 0, len = data.length; i < len; ++i) { this.buffer[this.pos++] = data[i]; } }, write_string: function (str) { // Write for (var i = 0, len = str.length; i < len; ++i) { this.buffer[this.pos++] = str.charCodeAt(i); } }, }; var MultiBufferWriter = function (total_length, max_segment_length) { max_segment_length = Math.max(1, max_segment_length); this.pos = 0; this.pos_offset = 0; this.remaining_length = total_length; this.max_segment_length = max_segment_length; this.buffer = new Uint8Array(Math.min(this.remaining_length, this.max_segment_length)); this.buffers = [ this.buffer ]; this.remaining_length -= this.buffer.length; }; MultiBufferWriter.prototype = { constructor: MultiBufferWriter, bounds_check: function () { if (this.pos >= this.buffer.length) { this.buffer = new Uint8Array(this.remaining_length <= 0 ? this.max_segment_length : Math.min(this.remaining_length, this.max_segment_length)); this.buffers.push(this.buffer); this.remaining_length -= this.buffer.length; this.pos_offset += this.pos; this.pos = 0; } }, get_pos: function () { return this.pos + this.pos_offset; }, write_ushort: function (value) { // Bound value = (value & 0x0000FFFF) >>> 0; // Write for (var i = 0; i < 2; ++i) { this.bounds_check(); this.buffer[this.pos++] = value & 0xFF; value = value >>> 8; } }, write_uint: function (value) { // Bound value = (value & 0xFFFFFFFF) >>> 0; // Write for (var i = 0; i < 4; ++i) { this.bounds_check(); this.buffer[this.pos++] = value & 0xFF; value = value >>> 8; } }, write_data: function (data) { // Write for (var i = 0, len = data.length; i < len; ++i) { this.bounds_check(); this.buffer[this.pos++] = data[i]; } }, write_string: function (str) { // Write for (var i = 0, len = str.length; i < len; ++i) { this.bounds_check(); this.buffer[this.pos++] = str.charCodeAt(i); } }, }; var crc32 = (function () { var crc_table = new Uint32Array([ //{ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, ]); //} return function (data) { var crc = (0 ^ (-1)), data_len = data.length, ct = crc_table, i; for (i = 0; i < data_len; ++i) { crc = (crc >>> 8) ^ ct[(crc ^ data[i]) & 0xFF]; } return (crc ^ (-1)) >>> 0; }; })(); var date_to_ziptime = function (date) { return [ (date.getDate()) | ((date.getMonth() + 1) << 5) | ((date.getFullYear() - 1980) << 9), // Date Math.floor(date.getSeconds() / 2) | (date.getMinutes() << 5) | (date.getHours() << 11) // Time ]; }; var array_to_string = function (arr) { // Copy var str = "", len = arr.length, chunk_size = 1024, i; for (i = 0; i < len; i += chunk_size) { str += String.fromCharCode.apply(null, arr.subarray(i, i + chunk_size)); } // Decode uft-8 return decodeURIComponent(escape(str)); }; var string_to_array = function (str, max_length) { // Encode to uft-8 str = unescape(encodeURIComponent(str)); // Copy var len = str.length, buffer, i; if (max_length !== undefined && len > max_length) len = max_length; buffer = new Uint8Array(len); for (i = 0; i < len; ++i) { buffer[i] = str.charCodeAt(i); } // Done return buffer; }; var write_file_header = function (writer, file_data) { // Write the file header writer.write_ushort(20); // Version writer.write_ushort(file_data.utf8 ? ZIP_FLAG_UTF8 : 0); // Flags writer.write_ushort(0); // Compression (store) writer.write_ushort(file_data.date_zip[1]); // Modified time writer.write_ushort(file_data.date_zip[0]); // Modified date writer.write_uint(file_data.crc); // CRC32 writer.write_uint(file_data.data.length); // Compressed size writer.write_uint(file_data.data.length); // Unompressed size writer.write_ushort(file_data.name_data.length); // Filename length writer.write_ushort(file_data.extra_data.length); // Extra length }; var write_central_directory_header = function (writer, file_data) { // Write the central directory header writer.write_ushort(20); // Version writer.write_ushort(20); // Version required writer.write_ushort(file_data.utf8 ? ZIP_FLAG_UTF8 : 0); // Flags writer.write_ushort(0); // Compression (store) writer.write_ushort(file_data.date_zip[1]); // Modified time writer.write_ushort(file_data.date_zip[0]); // Modified date writer.write_uint(file_data.crc); // CRC32 writer.write_uint(file_data.data.length); // Compressed size writer.write_uint(file_data.data.length); // Unompressed size writer.write_ushort(file_data.name_data.length); // Filename length writer.write_ushort(file_data.extra_data.length); // Extra length writer.write_ushort(file_data.comment_data.length); // Comment length writer.write_ushort(0); // Disk number start writer.write_ushort(0); // Internal attr writer.write_uint(0); // External attr writer.write_uint(file_data.offset); // Offset }; var write_footer = function (writer, file_count, pos_cd, pos_footer) { // Write file footer writer.write_ushort(0); // Disk number writer.write_ushort(0); // Disk number with central directory writer.write_ushort(file_count); // Disk entries writer.write_ushort(file_count); // Total entries writer.write_uint(pos_footer - pos_cd); // Central directory size writer.write_uint(pos_cd); // Central directory start writer.write_ushort(this.comment_data.length); // Comment length }; var arraybuffer_to_blob = function (arraybuffer) { return new Blob([ arraybuffer ], { type: "application/zip" }); }; var write_to_buffer = function (bw) { var total_size = this.calculate_size(), file_count = this.files.length, pos_cd, pos_footer, i, f; // Write file data for (i = 0; i < file_count; ++i) { f = this.files[i]; f.prepare(bw.get_pos()); // Header bw.write_data(signatures.file); // Signature write_file_header.call(this, bw, f); // Data bw.write_data(f.name_data); // Name bw.write_data(f.extra_data); // Extra bw.write_data(f.data); // Data } // Central directory pos_cd = bw.get_pos(); for (i = 0; i < file_count; ++i) { f = this.files[i]; // Entry bw.write_data(signatures.cd); // Signature write_central_directory_header.call(this, bw, f); // Data bw.write_data(f.name_data); // Name bw.write_data(f.extra_data); // Extra bw.write_data(f.comment_data); // Comment } // Footer pos_footer = bw.get_pos(); bw.write_data(signatures.footer); // Signature write_footer.call(this, bw, file_count, pos_cd, pos_footer); bw.write_data(this.comment_data); // Comment }; ZipCreator.array_to_string = array_to_string; ZipCreator.string_to_array = string_to_array; ZipCreator.arraybuffer_to_blob = arraybuffer_to_blob; ZipCreator.prototype = { constructor: ZipCreator, calculate_size: function () { var total_size = 0, file_count = this.files.length, i, f; // Calculate size for (i = 0; i < file_count; ++i) { f = this.files[i]; total_size += signatures.file.length + data_sizes.file + f.name_data.length + f.extra_data.length + f.data.length; // Entry total_size += signatures.cd.length + data_sizes.cd + f.name_data.length + f.extra_data.length + f.comment_data.length; // Central directory } // Footer length total_size += signatures.footer.length + data_sizes.footer + this.comment_data.length; // Done return total_size; }, set_date: function (date) { this.date = date; }, get_date: function () { return this.date; }, set_comment: function (str) { this.comment = str; this.comment_data = string_to_array(this.comment, max_lengths.comment); }, get_comment: function () { return this.comment; }, add_file: function (data, filename) { // Create var file_data = new FileData(data, filename, "", "", this.date); // Add this.files.push(file_data); // Done return this.files.length - 1; }, remove_file: function (index) { this.files.splice(index, 1); }, get_file_count: function () { return this.files.length; }, get_file_data: function (index) { return this.files[index].data; }, set_file_data: function (index, data) { this.files[index].set_data(data); }, get_file_name: function (index) { return this.files[index].name; }, set_file_name: function (index, name) { this.files[index].set_name(name); }, get_file_extra: function (index) { return this.files[index].extra; }, set_file_extra: function (index, extra) { this.files[index].set_extra(extra); }, get_file_comment: function (index) { return this.files[index].comment; }, set_file_comment: function (index, comment) { this.files[index].set_comment(comment); }, get_file_date: function (index) { return this.files[index].date; }, set_file_date: function (index, date) { this.files[index].set_date(date); }, to_buffer: function (segment_length) { var total_size = this.calculate_size(), bw = (segment_length === undefined) ? new BufferWriter(new Uint8Array(total_size)) : new MultiBufferWriter(total_size, segment_length); write_to_buffer.call(this, bw); return bw.buffers; }, to_blob: function () { var file_count = this.files.length, data_array = [], bw = new BufferWriter(), pos = 0, pos_cd, i, f; // Write file data for (i = 0; i < file_count; ++i) { f = this.files[i]; f.prepare(pos); // Header bw.reset(new Uint8Array(data_sizes.file)); write_file_header.call(this, bw, f); data_array.push(signatures.file); // Signature data_array.push(bw.buffer); // Data data_array.push(f.name_data); // Name data_array.push(f.extra_data); // Extra data_array.push(f.data); // Data // Pos update pos += signatures.file.length + bw.buffer.length + f.name_data.length + f.extra_data.length + f.data.length; } // Central directory pos_cd = pos; for (i = 0; i < file_count; ++i) { f = this.files[i]; // Header bw.reset(new Uint8Array(data_sizes.cd)); write_central_directory_header.call(this, bw, f); data_array.push(signatures.cd); // Signature data_array.push(bw.buffer); // Data data_array.push(f.name_data); // Name data_array.push(f.extra_data); // Extra data_array.push(f.comment_data); // Comment // Pos update pos += signatures.cd.length + bw.buffer.length + f.name_data.length + f.extra_data.length + f.comment_data.length; } // Footer bw.reset(new Uint8Array(data_sizes.footer)); write_footer.call(this, bw, file_count, pos_cd, pos); data_array.push(signatures.footer); // Signature data_array.push(bw.buffer); data_array.push(this.comment_data); // Comment // Create as a blob return new Blob(data_array, { type: "application/zip" }); }, }; return ZipCreator; })(); // Saving settings var Save = (function () { var localStorage = window.localStorage; var SaveLocal = { get: function (key, callback) { var okay = false, v; if (localStorage) { try { v = localStorage.getItem(key); if (v === null) { v = JSON.parse(v); okay = true; } else { v = undefined; } } catch (e) { v = undefined; } } if (typeof(callback) === "function") { callback.call(null, v, okay); } }, set: function (key, value, callback) { var okay = false; if (localStorage) { try { localStorage.setItem(key, JSON.stringify(value)); okay = true; } catch (e) {} } if (typeof(callback) === "function") { callback.call(null, okay); } }, del: function (key, callback) { var okay = false; if (localStorage) { try { localStorage.removeItem(key); okay = true; } catch (e) {} } if (typeof(callback) === "function") { callback.call(null, okay); } }, }; var Save = { get: function (key, callback) { try { GM.getValue(key) .then(function (value) { if (value === undefined) { SaveLocal.get(key, callback); } else if (typeof(callback) === "function") { callback.call(null, value, true); } }) .catch(function () { SaveLocal.get(key, callback); }); } catch (e) { SaveLocal.get(key, callback); } }, set: function (key, value, callback) { try { GM.setValue(key, value) .then(function () { if (typeof(callback) === "function") { callback.call(null, true); } }) .catch(function () { if (typeof(callback) === "function") { callback.call(null, false); } }); } catch (e) { if (typeof(callback) === "function") { callback.call(null, false); } } }, del: function (key, callback) { try { GM.deleteValue(key, value) .then(function () { if (typeof(callback) === "function") { callback.call(null, value, true); } }) .catch(function () { if (typeof(callback) === "function") { callback.call(null, false); } }); } catch (e) { if (typeof(callback) === "function") { callback.call(null, false); } } } }; return Save; })(); // Cookies var Cookies = (function () { var invalid_cookies = [ "expires" , "max-age" , "path" , "domain" , "secure" ]; return { get_all: function (cookie_str) { var cookie_parts = cookie_str.split(";"), cookies = {}, re_pattern = /^\s*([^=]*)=(.*)$/, i, m; for (i = 0; i < cookie_parts.length; ++i) { if ((m = re_pattern.exec(cookie_parts[i]))) { cookies[m[1]] = m[2]; } } return cookies; }, set: function (key, value, expire_date, path, domain, secure) { // Invalid if (invalid_cookies.indexOf(key) >= 0) return false; // Set var cookie_str = encodeURIComponent(key) + "=" + encodeURIComponent(value); // Expire if (expire_date) { if (typeof(expire_date) !== "string") { expire_date = expire_date.toUTCString(); } cookie_str += "; expires=" + expire_date; } // Domain if (domain) { cookie_str += "; domain=" + domain; } // Path if (path) { cookie_str += "; path=" + path; } // Secure if (secure) { cookie_str += "; secure"; } // Set document.cookie = cookie_str; return true; }, remove: function (key, path, domain) { // Remove a cookie this.set(key, "", new Date(0), path, domain); }, }; })(); // Ajax var Ajax = (function () { // Detect greasemonkey var can_use_gm = false; try { if (GM.xmlHttpRequest) { can_use_gm = true; } } catch (e) {} // GM data parsers var response_parsers = { json: function (text) { try { return JSON.parse(text); } catch (e) { return null; } }, document: function (text) { try { var parser = new DOMParser(), doc = parser.parseFromString(text, "text/html"); return doc; } catch (e) { return null; } }, arraybuffer: function (text) { var array = new Uint8Array(text.length), text_len = text.length, i = 0; for (; i < text_len; ++i) { array[i] = text.charCodeAt(i); } return array; }, }; var header_string_parse = function (header_str) { var lines = header_str.split("\r\n"), re_line = /^([^:]*):\s(.*)$/i, headers = {}, i, m; for (i = 0; i < lines.length; ++i) { if ((m = re_line.exec(lines[i]))) { headers[m[1]] = m[2]; } } return headers; }; var complete_extra = function (extra, response_headers) { extra.response_headers = (response_headers === null) ? {} : header_string_parse(response_headers); }; // Main function /** settings can be an object including: use_gm: Use the Greasemonkey xmlhttprequest version response_type: One of [ "text" , "json" , "arraybuffer" , "document" ] headers: An object of headers */ var Ajax = function (method, url, data, options, on_load, on_error, on_complete, on_progress) { var extra, xhr_data, xhr, parser, okay, v; extra = { url: url, response_headers: null, final_url: url }; // Values on_load = on_load || null; on_error = on_error || null; on_complete = on_complete || null; on_progress = on_progress || null; // Run if (options && options.use_gm && can_use_gm) { // Setup xhr args xhr_data = { method: method, url: url, }; parser = null; // Setup if ("response_type" in options) { v = options.response_type; if (v in response_parsers) { parser = response_parsers[v]; } if (v == "arraybuffer") { xhr_data.overrideMimeType = "text/plain; charset=x-user-defined"; } } if ("headers" in options) { xhr_data.headers = options.headers; } // Events if (on_load !== null || on_complete !== null) { xhr_data.onload = function (response) { // Complete extra complete_extra(extra, response.responseHeaders); extra.final_url = response.finalUrl; // Process response var res_data = response.responseText; if (parser !== null) res_data = parser.call(null, res_data); // Events if (on_load !== null) on_load.call(null, res_data, response.status, response.statusText, extra); if (on_complete !== null) on_complete.call(null, true, extra); }; } if (on_error !== null || on_complete !== null) { xhr_data.onerror = function () { // Complete extra complete_extra(extra, null); // Events if (on_error !== null) on_error.call(null, "error", extra); if (on_complete !== null) on_complete.call(null, false, extra); }; xhr_data.onabort = function () { // Complete extra complete_extra(extra, null); // Events if (on_error !== null) on_error.call(null, "abort", extra); if (on_complete !== null) on_complete.call(null, false, extra); }; } if (on_progress !== null) { xhr_data.onprogress = function (event) { // Compute progress var perc, total; if (event.lengthComputable) { perc = event.loaded / event.total; total = event.total; } else { perc = 0.0; total = null; } // Event if (on_progress !== null) on_progress.call(null, perc, event.loaded, total, extra); }; } // Send if (data !== null) xhr_data.data = data; var gmxhr = GM.xmlHttpRequest(xhr_data); // jshint ignore:line xhr = { abort: function () { // This is sort of hacky to support both GM4 and other userscript engines which had the .abort function available if (gmxhr && gmxhr._result && gmxhr._result.abort) { gmxhr._result.abort(); } } }; } else { // Create XHR xhr = new XMLHttpRequest(); okay = false; // Open xhr.open(method, url, true); // Setup if (options) { if ("response_type" in options) { v = options.response_type; xhr.responseType = v; if (v == "arraybuffer") { xhr.overrideMimeType("text/plain; charset=x-user-defined"); } } if ("headers" in options) { for (v in options.headers) { xhr.setRequestHeader(v, options.headers[v]); } } } // Events if (on_load !== null) { xhr.addEventListener("load", function () { // Complete extra complete_extra(extra, xhr.getAllResponseHeaders()); // Event okay = true; if (on_load !== null) on_load.call(null, xhr.response, xhr.status, xhr.statusText, extra); }, false); } if (on_error !== null) { xhr.addEventListener("error", function () { // Complete extra complete_extra(extra, null); // Event if (on_error !== null) on_error.call(null, "error", extra); }, false); xhr.addEventListener("abort", function () { // Complete extra complete_extra(extra, null); // Event if (on_error !== null) on_error.call(null, "abort", extra); }, false); } if (on_complete !== null) { xhr.addEventListener("loadend", function () { // Event if (on_complete !== null) on_complete.call(null, okay, extra); }, false); } if (on_progress !== null) { xhr.addEventListener("progress", function (event) { // Compute progress var perc, total; if (event.lengthComputable) { perc = event.loaded / event.total; total = event.total; } else { perc = 0.0; total = null; } // Event if (on_progress !== null) on_progress.call(null, perc, event.loaded, total, extra); }, false); } // Send if (data === null) { xhr.send(); } else { xhr.send(data); } } // Done this.abort = function () { if (xhr !== null) { // Complete extra complete_extra(extra, null); // Events if (on_error !== null) { on_error.call(null, "abort", extra); on_error = null; } if (on_complete !== null) { on_complete.call(null, false, extra); on_complete = null; } on_load = null; on_progress = null; // Abort var x = xhr; xhr = null; try { x.abort(); } catch (e) { // This happens using GM_xhr on FF36 due to some privilege crap } } }; }; // Done return Ajax; })(); // CSS formatting var CSS = (function () { var formatters = { color: function (val, alpha) { alpha = (alpha === undefined) ? val[3] : parseFloat(alpha); if (alpha < 1) { if (alpha < 0) alpha = 0; // RGBA color return "rgba(" + val[0] + "," + val[1] + "," + val[2] + "," + alpha.toFixed(4) + ")"; } else { // Hex color var rgb = "#", i, c; for (i = 0; i < 3; ++i) { c = val[i].toString(16); if (c.length < 2) c = "0" + c; rgb += c; } return rgb; } } }; return { color: function (str) { var c = [ 0 , 0 , 0 , 1 ], m; if ((m = /^#([0-9a-f]{6})$/i.exec(str))) { m = m[1]; c[0] = parseInt(m.substr(0, 2), 16); c[1] = parseInt(m.substr(2, 2), 16); c[2] = parseInt(m.substr(4, 2), 16); } else if ((m = /^#([0-9a-f]{3})$/i.exec(str))) { m = m[1]; c[0] = parseInt(m[0] + m[0], 16); c[1] = parseInt(m[1] + m[1], 16); c[2] = parseInt(m[2] + m[2], 16); } else if ((m = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i.exec(str))) { c[0] = parseInt(m[1], 10); c[1] = parseInt(m[2], 10); c[2] = parseInt(m[3], 10); } else if ((m = /^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\,\s*([\d\.]+)\s*\)$/i.exec(str))) { c[0] = parseInt(m[1], 10); c[1] = parseInt(m[2], 10); c[2] = parseInt(m[3], 10); c[3] = parseFloat(m[4]); if (isNaN(c[3]) || c[3] > 1) { c[3] = 1; } else if (c[3] < 0) { c[3] = 0; } } return c; }, format: function (str, vars) { str = str.replace(/\{\{(?:([^\}:]*?):)?([^\}]*?)\}\}/g, function (full, label, key) { var args = key.split(","); key = args[0]; if (key in vars) { args[0] = vars[key]; if (label) { if (label in formatters) { return formatters[label].apply(null, args); } } else { return args[0]; } } return ""; }); return str; }, computed: function (node, style) { return window.getComputedStyle(node).getPropertyValue(style); }, }; })(); // Page geometry var Geometry = (function () { return { get_window_rect: function () { var doc = document.documentElement, left = (window.pageXOffset || doc.scrollLeft || 0) - (doc.clientLeft || 0), top = (window.pageYOffset || doc.scrollTop || 0) - (doc.clientTop || 0), width = (doc.clientWidth || window.innerWidth || 0), height = (doc.clientHeight || window.innerHeight || 0); return { left: left, top: top, right: left + width, bottom: top + height, width: width, height: height, }; }, get_object_rect: function (obj) { var bounds = obj.getBoundingClientRect(), doc = document.documentElement, left = (window.pageXOffset || doc.scrollLeft || 0) - (doc.clientLeft || 0), top = (window.pageYOffset || doc.scrollTop || 0) - (doc.clientTop || 0); return { left: left + bounds.left, top: top + bounds.top, right: left + bounds.right, bottom: top + bounds.bottom, width: bounds.width, height: bounds.height, }; }, }; })(); // Node creation functions var $ = (function () { var typeof_str = typeof(""), typeof_object = typeof({}); var $ = function (tag) { var arg_count = arguments.length, node = document.createElement(tag), i = 1, a, j, k, t; // Done if (i >= arg_count) return node; // Classname t = typeof(a = arguments[i]); if (t == typeof_str) { // Use node.className = a; // Next if (++i >= arg_count) return node; t = typeof(a = arguments[i]); } // Children, attributes, text, custom while (true) { if (t == typeof_object) { // Children or attributes if (Array.isArray(a)) { // Children for (j = 0; j < a.length; ++j) { if (a[j] !== null) { node.appendChild(a[j]); } } } else if (a !== null) { // Attributes for (k in a) { node.setAttribute(k, a[k]); } } } else if (t == typeof_str) { // Text node.appendChild(document.createTextNode(a)); } else { // Number if (a == $.PARENT) { // Parent arguments[++i].appendChild(node); } else if (a == $.BEFORE) { // Insert before a node a = arguments[++i]; k = a.parentNode; if (k !== null) { k.insertBefore(node, a); } } else if (a == $.AFTER) { // Insert after a node a = arguments[++i]; k = a.parentNode; if (k !== null) { a = a.nextSibling; if (a !== null) { k.insertBefore(node, a); } else { k.appendChild(node); } } } else if (a == $.EVENT) { // Event a = arguments[++i]; k = a[1]; // function if (a.length >= 4) { // Bind k = bind_event_listener(node, k, a[3]); } node.addEventListener(a[0], k, a[2] || false); } else if (a == $.TEXT) { // Text node.appendChild(document.createTextNode(arguments[++i])); } else if (a == $.CHECKED) { // Checked node.checked = !!arguments[++i]; } } // Next if (++i >= arg_count) return node; t = typeof(a = arguments[i]); } }; $.A = $.AFTER = 1; $.B = $.BEFORE = 2; $.P = $.PARENT = 3; $.ON = $.EVENT = 4; $.T = $.TEXT = 5; $.CHECKED = 6; $.node = {}; $.text = function (str) { return document.createTextNode(str || ""); }; var bind_event_listener = function (node, callback, args) { // Bind if (args.length > 0) { var self_arg = $.node, new_args = [], self, a, i; // Form new array a = args[0]; self = (a === self_arg) ? node : a; for (i = 1; i < args.length; ++i) { a = args[i]; new_args.push((a === self_arg) ? node : a); } args = null; self_arg = null; a = null; // Return new callback return function () { var full_args = Array.prototype.slice.call(new_args); Array.prototype.push.apply(full_args, arguments); return callback.apply(self, full_args); }; } else { // No change return callback; } }; return $; })(); // Callback binding function var bind = function (callback, self) { if (arguments.length > 2) { var slice = Array.prototype.slice, push = Array.prototype.push, args = slice.call(arguments, 2); return function () { var full_args = slice.call(args); push.apply(full_args, arguments); return callback.apply(self, full_args); }; } else { return function () { return callback.apply(self, arguments); }; } }; // Event listeners var add_event_listener = function (list, node, event, callback, capture) { node.addEventListener(event, callback, capture); list.push([node, event, callback, capture]); }; var remove_event_listeners = function (list) { var list_len = list.length, i = 0, li; for (; i < list_len; ++i) { li = list[i]; li[0].removeEventListener(li[1], li[2], li[3]); } }; // setTimeout with a progress callback var set_timeout = (function () { var perf = window.performance, now = performance.now || performance.mozNow || performance.msNow || performance.oNow || performance.webkitNow, time_now = (perf && now) ? function () { return now.call(perf); } : function () { return new Date().getTime(); }; return function (callback, time, progress, progress_time) { var start = time_now(), timer = setTimeout(function () { if (progress !== null) { clearInterval(interval); progress.call(null, 1.0); } callback.call(null); timer = null; }, time), interval = 0; if (progress) { interval = setInterval(function () { var diff = time_now() - start; if (diff > time) diff = time; progress.call(null, diff / time, diff); }, progress_time); } else { progress = null; } return function () { if (timer !== null) { clearTimeout(timer); if (progress !== null) clearInterval(interval); timer = null; return true; } return false; }; }; })(); // Url parsing var URLParts = (function () { var URLParts = function (url_str) { var m, url_part; this.hash = ""; this.host = null; this.hostname = null; this.pathname = ""; this.protocol = null; this.search = ""; this.slashes = false; this.auth = null; this.port = null; if ((m = /^[a-z][a-z0-9\+\-\.]*:/i.exec(url_str)) !== null) { this.protocol = m[0].toLowerCase(); url_str = url_str.substr(m[0].length); } if ((m = /^\/{2}/.exec(url_str)) !== null) { this.slashes = true; url_str = url_str.substr(m[0].length); m = /([\/\?\#]|$)/.exec(url_str); url_part = url_str.substr(0, m.index); url_str = url_str.substr(m.index); if ((m = /^(.*)@/i.exec(url_part)) !== null) { this.auth = m[1]; url_part = url_part.substr(m[0].length); } if ((m = /:(\d*)$/i.exec(url_part)) !== null) { this.port = parseInt(m[1], 10); url_part = url_part.substr(0, m.index); } url_part = url_part.toLowerCase(); this.hostname = url_part; this.host = url_part; if (this.port !== null) this.host += ":" + this.port; } m = /([\?\#]|$)/.exec(url_str); this.pathname = url_str.substr(0, m.index); if (this.pathname.length === 0 && this.slashes) this.pathname = "/"; url_str = url_str.substr(m.index); m = /([\#]|$)/.exec(url_str); this.search = url_str.substr(0, m.index); url_str = url_str.substr(m.index); this.hash = url_str; }; URLParts.prototype = { constructor: URLParts, join: function () { var url_str = ""; if (this.protocol !== null) url_str += this.protocol; if (this.slashes) { url_str += "//"; if (this.auth !== null) url_str += this.auth + "@"; url_str += this.hostname; if (this.port !== null) url_str += ":" + this.port; } url_str += this.pathname; url_str += this.search; url_str += this.hash; return url_str; }, }; return URLParts; })(); // Generic event functions var Events = (function () { return { on: function (valid_events) { if (valid_events) { // Lite mode return function (event, callback) { if (this.events === null) this.events = {}; if (event in this.events) { this.events[event].push(callback); } else { if (!(event in valid_events)) return false; this.events[event] = [ callback ]; } return true; }; } else { // Normal mode return function (event, callback) { if (this.events !== null && event in this.events) { this.events[event].push(callback); return true; } return false; }; } }, off: function () { return function (event, callback) { if (this.events !== null && event in this.events) { for (var i = 0, list = this.events[event]; i < list.length; ++i) { if (list[i] === callback) { list.splice(i, 1); return true; } } } return false; }; }, trigger: function () { return function (event, data) { if (this.events !== null && event in this.events) { for (var i = 0, list = this.events[event]; i < list.length; ++i) { list[i].call(null, data, event); } } }; }, }; })(); // Site specific classes/modules // API var API = (function () { var size_label_to_bytes = function (number, label) { var i = [ "b" , "kb" , "mb" , "gb" ].indexOf(label.toLowerCase()); if (i < 0) i = 0; return Math.floor(parseFloat(number) * Math.pow(1024, i)); }; var create_blank_gallery_data = function () { return { gallery: { gid: 0, token: "", }, title: "", title_original: "", date_uploaded: 0, category: "", uploader: "", rating: { average: 0, count: 0, }, favorites: { category: -1, category_title: null, count: 0, }, parent: null, newer_versions: [], thumbnail: "", thumbnail_size: "normal", thumbnail_rows: 0, image_count: 0, images_resized: false, total_file_size_approx: 0, visible: true, visible_reason: "", language: "", translated: false, tags: {}, }; }; var create_blank_gallery_stub = function () { return { gallery: { gid: 0, token: "", }, title: "", date_uploaded: null, category: "", uploader: null, favorites: { category: -1, category_title: null, }, file_count: null, thumbnail: "", }; }; var create_blank_gallery_image_data = function () { return { url: "", filename: "", thumbnail: "", width: 0, height: 0, x_offset: 0, y_offset: 0, height_required: 0, index: 0, page: 0, }; }; var create_blank_image_data = function () { return { page: 0, page_count: 0, gallery: { gid: 0, token: "", title: "", page: 0, }, image: { filename: "", url: "", width: 0, height: 0, size_approx: 0, }, image_original: null, navigation: { api_key: null, key_current: null, key_next: null, key_previous: null, key_first: null, key_last: null, direct_id: null, }, }; }; var set_inner_html_no_loading = function (node, inner_html) { // Don't allow the creation of tags, as they will try to load immediately inner_html = inner_html.replace(/= 3) { // Good match = /(\d+)-(\d+)/.exec(path[2]); if (match) { data.gid = parseInt(match[1], 10); data.page = parseInt(match[2], 10) - 1; data.key = path[1]; } } // Done return data; } // Nothing return null; }, get_gallery_title_info: function (title) { var re_tag_pre_pattern = /^\s*(\(([^\)]*?)\)|\[([^\]]*?)\]|\{([^\}]*?)\})\s*/i, re_tag_post_pattern = /\s*(\(([^\)]*?)\)|\[([^\]]*?)\]|\{([^\}]*?)\})\s*$/i, title_split, m, i, info = { title: "", title_alt: null, tags: { before: [], after: [], }, }; // Tags while ((m = re_tag_pre_pattern.exec(title))) { title = title.substr(m.index + m[0].length); info.tags.before.push({ type: m[1][0], value: m[2] || m[3] || "", }); } while ((m = re_tag_post_pattern.exec(title))) { title = title.substr(0, m.index); info.tags.after.push({ type: m[1][0], value: m[2] || m[3] || "", }); } // Double name? title_split = title.split("|"); if (title_split.length > 1) { i = title_split.length - Math.floor(title_split.length / 2); info.title_alt = title_split.slice(0, i).join("|").trim(); info.title = title_split.slice(i).join("|").trim(); } else { info.title = title; } // Done return info; }, get_pages_info_from_html: function (html) { // Detect page groups var pages_container = html.querySelector(".ptt"), i, m, n, nodes, page_info = { current: 0, count: 0, items_on_page: 0, items_per_page: 0, items_total: 0, }; // Not found if (pages_container === null) { // Image viewing if ((n = html.querySelectorAll(".sn>div>span")).length >= 2) { page_info.current = (parseInt(n[0].textContent.trim(), 10) || 1) - 1; page_info.count = parseInt(n[1].textContent.trim(), 10) || 0; page_info.items_on_page = 1; page_info.items_per_page = 1; page_info.items_total = page_info.count; return page_info; } else { return null; } } // Max if ((nodes = pages_container.querySelectorAll("td")).length >= 2) { n = nodes[nodes.length - 2]; page_info.count = (parseInt(n.textContent.trim(), 10) || 1); } // Current if ((n = pages_container.querySelector("td.ptds")) !== null) { page_info.current = (parseInt(n.textContent.trim(), 10) || 1) - 1; } // Items if ((n = pages_container.parentNode.querySelector(".ip")) !== null && n.parentNode === pages_container.parentNode && (m = /([0-9,]+)\s*-\s*([0-9,]+)\s*of\s*([0-9,]+)/i.exec(n.textContent)) !== null) { i = parseInt(m[1].replace(/,/g, ""), 10); page_info.items_total = parseInt(m[3].replace(/,/g, ""), 10); page_info.items_on_page = Math.max(1, parseInt(m[2].replace(/,/g, ""), 10) - i + 1); page_info.items_per_page = (page_info.count <= 1 || page_info.current < page_info.count - 1) ? page_info.items_on_page : (i - 1) / page_info.current; } // Done return page_info; }, get_gallery_info_from_html: function (html) { // Vars var i, j, n, m, par, pattern, prev, info, namespace, tds, tag; // Data var data = create_blank_gallery_data(); // ID/token if ( (n = html.querySelector(".ptt td.ptds>a,.ptt td.ptdd>a")) !== null && (info = API.get_gallery_url_info(n.getAttribute("href") || "")) !== null ) { data.gallery.gid = info.gid; data.gallery.token = info.token; } // Gallery names if ((n = html.querySelector("#gn")) !== null) { data.title = n.textContent.trim(); } if ((n = html.querySelector("#gj")) !== null) { data.title_original = n.textContent.trim(); } // Thumbnail if ((n = html.querySelector("#gd1>img")) !== null) { data.thumbnail = n.getAttribute("src") || ""; } // Category pattern = /^.*?\/\/.+?\/(.*?)(\?.*?)?(#.*?)?$/; if ( (n = html.querySelector("#gdc>a")) !== null && (m = pattern.exec(n.getAttribute("href") || "")) ) { data.category = m[1]; } // Uploader if ( (n = html.querySelector("#gdn>a")) !== null && (m = pattern.exec(n.getAttribute("href") || "")) ) { data.uploader = m[1].split("/")[1] || ""; } if ((par = html.querySelectorAll("#gdd tr")).length >= 7) { // Date uploaded if ( (n = par[0].querySelector(".gdt2")) !== null && (m = API.get_date_uploaded_from_node(n)) !== null ) { data.date_uploaded = m; } // Parent if ( (n = par[1].querySelector(".gdt2>a")) !== null && (info = API.get_gallery_url_info(n.getAttribute("href") || "")) !== null ) { data.parent = { gid: info.gid, token: info.token, }; } // Visible pattern = /no\s+\((.+?)\)/i; if ( (n = par[2].querySelector(".gdt2")) && (m = pattern.exec(n.textContent)) ) { data.visible = false; data.visible_reason = m[1]; } // Language pattern = /(.+)(?:\s*\((.+?)\))/i; if ( (n = par[3].querySelector(".gdt2")) ) { data.language = (n.firstChild ? (n.firstChild.nodeValue || "").trim() : ""); if ( (n = n.querySelector(".halp")) !== null && (n.textContent.trim() == "TR") ) { data.translated = true; } } // Image size/resized pattern = /([0-9\.]+)\s*(\w+)/i; if ( (n = par[4].querySelector(".gdt2")) !== null && (m = pattern.exec(n.textContent)) ) { data.total_file_size_approx = size_label_to_bytes(m[1], m[2]); if ( (n = n.querySelector(".halp")) !== null && (n.textContent.trim() == "RES") ) { data.images_resized = true; } } // Image count pattern = /([0-9]+)\s*pages/i; if ( (n = par[5].querySelector(".gdt2")) !== null && (m = pattern.exec(n.textContent)) ) { data.image_count = parseInt(m[1], 10); } } // Rating if ((n = html.querySelector("#rating_count")) !== null) { data.rating.count = parseInt(n.textContent.trim(), 10); } pattern = /average:\s*([0-9\.]+)/i; if ( (n = html.querySelector("#rating_label")) !== null && (m = pattern.exec(n.textContent)) ) { data.rating.average = parseFloat(m[1]); } // Favorites if ( (n = html.querySelector("#favcount")) !== null && (m = /\s*([0-9]+)/.exec(n.textContent || "")) !== null ) { data.favorites.count = parseInt(m[1], 10); } if ((n = html.querySelector("#fav>div.i")) !== null) { info = API.get_favorite_icon_info_from_node(n); data.favorites.category = info[0]; data.favorites.category_title = info[1]; } // Thumbnail settings if ((n = html.querySelectorAll("#gdo4>.nosel")).length >= 2) { if (n[0].classList.contains("ths")) { data.thumbnail_size = "normal"; } else { data.thumbnail_size = "large"; } } if ((n = html.querySelectorAll("#gdo2>.nosel")).length >= 1) { for (i = 0; i < n.length; ++i) { if (n[i].classList.contains("ths")) { if ((m = /\s*([0-9]+)/.exec(n[i].textContent)) !== null) { data.thumbnail_rows = parseInt(m[1], 10); } break; } } } // Newer versions par = html.querySelectorAll("#gnd>a"); for (i = 0; i < par.length; ++i) { n = par[i]; if ( (info = API.get_gallery_url_info(n.getAttribute("href") || "")) !== null ) { // Create previous entry prev = { gallery: { gid: info.gid, token: info.token, }, name: n.textContent.trim(), date_uploaded: 0, }; // Date pattern = /added\s*([0-9]+)-([0-9]+)-([0-9]+)\s+([0-9]+):([0-9]+)/; if ( n.nextSibling && (m = pattern.exec(n.nextSibling.textContent)) ) { prev.date_uploaded = (new Date( parseInt(m[1], 10), // year parseInt(m[2], 10) - 1, // month parseInt(m[2], 10), // day parseInt(m[3], 10), // hours parseInt(m[4], 10), // minutes 0, // seconds 0 // milliseconds )).getTime(); } // Add data.newer_versions.push(prev); } } // Tags pattern = /(.+):/; par = html.querySelectorAll("#taglist tr"); for (i = 0; i < par.length; ++i) { // Class tds = par[i].querySelectorAll("td"); if (tds.length > 0) { // Namespace if ((m = pattern.exec(tds[0].textContent))) { namespace = m[1].trim(); } else { namespace = ""; } if (!(namespace in data.tags)) { data.tags[namespace] = []; } // Tags tds = tds[tds.length - 1].querySelectorAll("div"); for (j = 0; j < tds.length; ++j) { // Create tag if ((n = tds[j].querySelector("a")) !== null) { // Add tag tag = n.textContent.trim(); data.tags[namespace].push(tag); } } } } // Done return data; }, get_gallery_info_from_json: function (json) { var tag_list; // Data var data = create_blank_gallery_data(), div = document.createElement("div"); // Basic data data.gallery.gid = json.gid; data.gallery.token = json.token; set_inner_html_no_loading(div, json.title); // Replace special &#...; chars data.title = div.textContent; set_inner_html_no_loading(div, json.title_jpn); data.title_original = div.textContent; data.image_count = parseInt(json.filecount, 10); data.total_file_size_approx = json.filesize; data.visible = !json.expunged; data.rating.average = json.rating; data.date_uploaded = (new Date(json.posted * 1000)).getTime(); data.uploader = json.uploader; data.category = json.category.toLowerCase(); data.thumbnail = json.thumb; // Tags tag_list = []; Array.prototype.push.apply(tag_list, json.tags); data.tags["undefined"] = tag_list; // Done return data; }, get_gallery_images_from_html: function (html) { // Vars var re_image_url = /width:([0-9]+)px;\s*height:([0-9]+)px;\s*background:.+?url\((.+?)\)\s*-([0-9]+)/, re_required_height = /height:([0-9]+)/, images = [], page_id = 0, large, nodes, image, i, n, m, n2; // Find containers if ((n = html.querySelector("#gdt")) !== null) { // Image nodes nodes = n.querySelectorAll(".gdtm"); if ((large = (nodes.length === 0))) { nodes = n.querySelectorAll(".gdtl"); } // Current page if ((n = html.querySelector(".ptt td.ptds")) !== null) { page_id = (parseInt(n.textContent.trim(), 10) || 1) - 1; } // Scan all for (i = 0; i < nodes.length; ++i) { n = nodes[i]; // Create new image image = create_blank_gallery_image_data(); image.page = page_id; // Required size if ((m = re_required_height.exec(n.getAttribute("style") || ""))) { image.height_required = parseInt(m[1], 10); } if (large) { // Elements if ((n = n.querySelector("a")) !== null) { image.url = n.getAttribute("href") || ""; if ((n2 = n.querySelector("img")) !== null) { image.filename = n2.getAttribute("title") || ""; image.index = (parseInt(n2.getAttribute("alt") || "", 10) || 1) - 1; image.thumbnail = n2.getAttribute("src") || ""; } } } else { // Elements if ( (n = n.querySelector("div")) !== null && (n2 = n.querySelector("a")) !== null ) { image.url = n2.getAttribute("href") || ""; if ((n2 = n2.querySelector("img")) !== null) { image.filename = n2.getAttribute("title") || ""; image.index = (parseInt(n2.getAttribute("alt") || "", 10) || 1) - 1; } if ((m = re_image_url.exec(n.getAttribute("style") || ""))) { // Setup image image.thumbnail = m[3]; image.width = parseInt(m[1], 10); image.height = parseInt(m[2], 10); image.x_offset = parseInt(m[4], 10); } } } // Add image images.push(image); } } // Return return images; }, get_image_info_from_html: function (html) { var page_vars, nodes, info, okay, re, replacer, i, n, m; // Setup data var data = create_blank_image_data(); // Scripts if ((n = html.querySelectorAll("body>script:not([src])")).length > 0) { re = /var\s+(\w+)\s*=\s*(.+?);/g; okay = false; replacer = function (full, name, val) { try { page_vars[name] = JSON.parse(val); } catch (e) {} return ""; }; for (i = 0; i < n.length && !okay; ++i) { // Get vars page_vars = {}; n[i].textContent.replace(re, replacer); if ("startkey" in page_vars) { data.navigation.key_current = page_vars.startkey; okay = true; } if ("showkey" in page_vars) { data.navigation.api_key = page_vars.showkey; okay = true; } } } if (data.navigation.direct_id === null) { if ((n = html.querySelector("#loadfail[onclick]")) !== null) { data.navigation.direct_id = get_direct_id(n.getAttribute("onclick")); } } // Get title if ((n = html.querySelector("h1")) !== null) { data.gallery.title = n.textContent.trim(); } // Get image if ( (n = html.querySelector("#img")) !== null || (n = html.querySelector(".sni>a[href]>img[src]")) !== null ) { data.image.url = n.getAttribute("src") || ""; } else { return null; } // Get pages if ((n = html.querySelectorAll(".sn>div>span")).length >= 2) { data.page = (parseInt(n[0].textContent.trim(), 10) || 1) - 1; data.page_count = parseInt(n[1].textContent.trim(), 10) || 0; } else { return null; } // Get info re = /(.*?)\s*::\s*(\d+)\s*x\s*(\d+)\s*::\s*([\d\.]+)\s*(\w+)$/i; if ( ( (n = html.querySelector("#i4>div")) !== null || (n = html.querySelector(".sni>div:not([class])")) !== null ) && (m = re.exec(n.textContent.trim())) ) { data.image.filename = m[1]; data.image.width = parseInt(m[2], 10); data.image.height = parseInt(m[3], 10); data.image.size_approx = size_label_to_bytes(m[4], m[5]); } else { return null; } // Gallery if ( (n = html.querySelector(".sb>a")) !== null && (info = API.get_gallery_url_info(n.getAttribute("href") || "")) !== null ) { data.gallery.gid = info.gid; data.gallery.token = info.token; data.gallery.page = info.page; } else { return null; } // Next/prev nodes = html.querySelectorAll(".sn>a"); if ( ( (n = html.querySelector("#next")) !== null || (n = nodes[1]) ) && (info = API.get_gallery_image_url_info(n.getAttribute("href") || "")) !== null && info.key != data.navigation.key_current ) { data.navigation.key_next = info.key; } if ( ( (n = html.querySelector("#prev")) !== null || (n = nodes[2]) ) && (info = API.get_gallery_image_url_info(n.getAttribute("href") || "")) !== null && info.key != data.navigation.key_current ) { data.navigation.key_previous = info.key; } // First/last if ( (n = nodes[0]) && (info = API.get_gallery_image_url_info(n.getAttribute("href") || "")) !== null ) { data.navigation.key_first = info.key; } if ( (n = nodes[3]) && (info = API.get_gallery_image_url_info(n.getAttribute("href") || "")) !== null ) { data.navigation.key_last = info.key; } // Original image re = /(\d+)\s*x\s*(\d+)\s+([\d\.]+)\s*(\w+)/i; if ( ( (n = html.querySelector("#i7>a")) !== null || ( (nodes = html.querySelectorAll(".if")).length >= 2 && (n = nodes[1].querySelector("a")) !== null ) ) && (m = re.exec(n.textContent.trim())) ) { data.image_original = { url: n.getAttribute("href") || "", width: parseInt(m[1], 10), height: parseInt(m[2], 10), size_approx: size_label_to_bytes(m[3], m[4]), }; } // Invalid return data; }, get_image_info_from_json: function (json) { var div = document.createElement("div"), info, re, m, n, nodes; // Setup data var data = create_blank_image_data(); // Basic info data.page = (json.p || 1) - 1; data.navigation.key_current = json.k || ""; data.image.width = parseInt(json.x, 10) || 0; data.image.height = parseInt(json.y, 10) || 0; // Get info set_inner_html_no_loading(div, json.i || ""); re = /(.*?)\s*::\s*(\d+)\s*x\s*(\d+)\s*::\s*([\d\.]+)\s*(\w+)$/i; if ( (n = div.querySelector("div")) !== null && (m = re.exec(n.textContent.trim())) ) { data.image.filename = m[1]; data.image.size_approx = size_label_to_bytes(m[4], m[5]); } else { return null; } // Get image set_inner_html_no_loading(div, json.i3 || ""); if ((n = div.querySelector("#img")) !== null) { data.image.url = n.getAttribute("src") || ""; } else { return null; } // Get pages set_inner_html_no_loading(div, json.n); if ((n = div.querySelectorAll(".sn>div>span")).length >= 2) { data.page_count = parseInt(n[1].textContent.trim(), 10) || 0; } else { return null; } // Next/prev if ( (n = div.querySelector("#next")) !== null && (info = API.get_gallery_image_url_info(n.getAttribute("href") || "")) !== null && info.key != data.navigation.key_current ) { data.navigation.key_next = info.key; } if ( (n = div.querySelector("#prev")) !== null && (info = API.get_gallery_image_url_info(n.getAttribute("href") || "")) !== null && info.key != data.navigation.key_current ) { data.navigation.key_previous = info.key; } // First/last nodes = n.querySelectorAll(".sn>a"); if ( (n = nodes[0]) && (info = API.get_gallery_image_url_info(n.getAttribute("href") || "")) !== null ) { data.navigation.key_first = info.key; } if ( (n = nodes[3]) && (info = API.get_gallery_image_url_info(n.getAttribute("href") || "")) !== null ) { data.navigation.key_last = info.key; } // Gallery set_inner_html_no_loading(div, json.i5); if ( (n = div.querySelector(".sb>a")) !== null && (info = API.get_gallery_url_info(n.getAttribute("href") || "")) !== null ) { data.gallery.gid = info.gid; data.gallery.token = info.token; } else { return null; } // Original image if (json.i7) { set_inner_html_no_loading(div, json.i7); re = /(\d+)\s*x\s*(\d+)\s+([\d\.]+)\s*(\w+)/i; if ( (n = div.querySelector("a")) !== null && (m = re.exec(n.textContent.trim())) ) { data.image_original = { url: n.getAttribute("href") || "", width: parseInt(m[1], 10), height: parseInt(m[2], 10), size_approx: size_label_to_bytes(m[3], m[4]), }; } } // Direct ID data.navigation.direct_id = get_direct_id(json.i6 || ""); // Invalid return data; }, get_favorite_icon_info_from_node: function (node) { var title = node.getAttribute("title") || "", m = /background-position\s*:\s*\d+(?:px)?\s+(-?\d+)(?:px)/.exec(node.getAttribute("style") || ""), category = -1; if (m !== null) { category = Math.floor((Math.abs(parseInt(m[1], 10)) - 2) / 19); } return [ category , title ]; }, get_date_uploaded_from_node: function (node) { // Date uploaded var m = /([0-9]+)-([0-9]+)-([0-9]+)\s+([0-9]+):([0-9]+)/.exec(node.textContent); if (m === null) return null; return (new Date( parseInt(m[1], 10), // year parseInt(m[2], 10) - 1, // month parseInt(m[2], 10), // day parseInt(m[3], 10), // hours parseInt(m[4], 10), // minutes 0, // seconds 0 // milliseconds )).getTime(); }, get_image_info: function (gid, key, page, api_key, direct_id, callback, on_load, on_error, on_complete, on_progress) { if (api_key && direct_id === null) { // Request using JSON var req_data = { method: "showpage", gid: gid, page: page + 1, imgkey: key, showkey: api_key, }; return API.request_api(req_data, // On load function (response, status, status_text) { if (on_load) on_load.apply(this, arguments); if (status == 200) { var data = response ? API.get_image_info_from_json(response) : null; if (data !== null) { callback.call(null, API.OK, data, response); } else { callback.call(null, API.ERROR_PARSING, response); } } else { callback.call(null, API.ERROR_REQUEST_STATUS, [ response , status, status_text ]); } }, // On error function (event) { if (on_error) on_error.apply(this, arguments); callback.call(null, API.ERROR_REQUEST, event); }, on_complete, on_progress ); } else { // Request the page return API.request_document("/s/" + key + "/" + gid + "-" + (page + 1) + (direct_id !== null ? "?nl=" + direct_id : ""), // On load function (response, status, status_text) { if (on_load) on_load.apply(this, arguments); if (status == 200) { var data = API.get_image_info_from_html(response); if (data !== null) { callback.call(null, API.OK, data, response); } else { callback.call(null, API.ERROR_PARSING, response); } } else { callback.call(null, API.ERROR_REQUEST_STATUS, [ response , status, status_text ]); } }, // On error function (event) { if (on_error) on_error.apply(this, arguments); callback.call(null, API.ERROR_REQUEST, event); }, on_complete, on_progress ); } }, get_gallery: function (gid, token, page, use_api, callback, on_load, on_error, on_complete, on_progress) { // Request the page if (use_api) { // Request using JSON var req_data = { method: "gdata", gidlist: [ [ gid , token ], ], }; return API.request_api(req_data, // On load function (response, status, status_text) { if (on_load) on_load.apply(this, arguments); if (status == 200) { var data = (response && response.gmetadata && response.gmetadata[0]) ? API.get_gallery_info_from_json(response.gmetadata[0]) : null; if (data !== null) { callback.call(null, API.OK, data, null); } else { callback.call(null, API.ERROR_PARSING, response); } } else { callback.call(null, API.ERROR_REQUEST_STATUS, [ response , status, status_text ]); } }, // On error function (event) { if (on_error) on_error.apply(this, arguments); callback.call(null, API.ERROR_REQUEST, event); }, on_complete, on_progress ); } else { return API.request_document("/g/" + gid + "/" + token + "/" + (page > 0 ? "?p=" + page : ""), // On load function (response, status, status_text) { if (on_load) on_load.apply(this, arguments); if (status == 200) { var data = API.get_gallery_info_from_html(response); if (data !== null) { callback.call(null, API.OK, data, response); } else { callback.call(null, API.ERROR_PARSING, response); } } else { callback.call(null, API.ERROR_REQUEST_STATUS, [ response , status, status_text ]); } }, // On error function (event) { if (on_error) on_error.apply(this, arguments); callback.call(null, API.ERROR_REQUEST, event); }, on_complete, on_progress ); } }, get_gallery_images: function (gid, token, page, callback, on_load, on_error, on_complete, on_progress) { return API.request_document("/g/" + gid + "/" + token + "/" + (page > 0 ? "?p=" + page : ""), // On load function (response, status, status_text) { if (on_load) on_load.apply(this, arguments); if (status == 200) { var data = API.get_gallery_images_from_html(response); if (data !== null) { callback.call(null, API.OK, data, response); } else { callback.call(null, API.ERROR_PARSING, response); } } else { callback.call(null, API.ERROR_REQUEST_STATUS, [ response , status, status_text ]); } }, // On error function (event) { if (on_error) on_error.apply(this, arguments); callback.call(null, API.ERROR_REQUEST, event); }, on_complete, on_progress ); }, set_html_gallery_favorite_icon: function (html, fav_id, fav_title) { var par = html.querySelector("#fav"), img; if (par) { par.innerHTML = ""; if (fav_id >= 0 && fav_id < 10) { img = document.createElement("div"); img.className = "i"; if (fav_title) img.setAttribute("title", fav_title); img.style.backgroundImage = "url('https://st.exhentai.net/img/fav.png')"; img.style.backgroundPosition = "0px -" + (2 + 19 * fav_id) + "px"; par.appendChild(img); } } }, block_redirections: function (on_block) { // Block scripts var on_window_bse = function (event) { var remove = false; if (!event.target.getAttribute("src")) { if (/setTimeout\s*\(\s*(gotonext|"gotonext\(\)")\s*,\s*\d+\s*\)/.test(event.target.textContent)) { remove = true; } } if (remove) { // Stop event.preventDefault(); event.stopPropagation(); // Remove if (event.target.parentNode) { event.target.parentNode.removeChild(event.target); } // Event if (on_block) { on_block.call(null, event.target); } // Done return false; } // Okay return true; }; window.addEventListener("beforescriptexecute", on_window_bse, true); // If beforescriptexecute fails (chrome), simply remask the function on_ready(function () { API.inject_script(function () { if (window.gotonext) { window.gotonext = function () {}; } }); }); }, get_page_type_from_html: function (html) { var n; if (html.querySelector("#searchbox") !== null) { return "search"; } else if (html.querySelector("input[name='favcat']") !== null) { return "favorites"; } else if (html.querySelector("#i1>h1") !== null) { return "image"; } else if (html.querySelector(".gm h1#gn") !== null) { return "gallery"; } else if (html.querySelector("#profile_outer") !== null) { return "settings"; } else if ( (n = html.querySelector("body>.d>p")) !== null && /gallery\s+has\s+been\s+removed/i.exec(n.textContent.trim()) !== null ) { return "gallery_deleted"; } else if ((n = html.querySelector("img[src]")) !== null) { var p = window.location.pathname; if (n.getAttribute("src") === window.location.href && p.substr(0, 3) !== "/t/" && p.substr(0, 5) !== "/img/") { return "panda"; } } // Unknown return null; }, get_site: function () { return api_site; }, get_search_page_results: function (html) { var results = [], nodes = html.querySelectorAll("div.itg>.id1"), i, n, n1, node, m, data, info; if (nodes.length > 0) { for (i = 0; i < nodes.length; ++i) { node = nodes[i]; data = create_blank_gallery_stub(); results.push(data); // Title, ID/token if ((n = node.querySelector(".id2>a")) !== null) { data.title = n.textContent.trim(); if ((info = API.get_gallery_url_info(n.getAttribute("href") || "")) !== null) { data.gallery.gid = info.gid; data.gallery.token = info.token; } } // Thumbnail if ((n = node.querySelector(".id3>a>img[src]")) !== null) { data.thumbnail = n.getAttribute("src"); } if ((n1 = node.querySelector(".id4")) !== null) { // Category if ((n = n1.querySelector(".id41[title]")) !== null) { data.category = n.getAttribute("title").toLowerCase(); } // Files if ( (n = n1.querySelector(".id42")) !== null && (m = /\s*([0-9]+)\s*files?/i.exec(n.textContent)) !== null ) { data.file_count = parseInt(m[1], 10); } // Favorited if ((n = n1.querySelector(".id44 .i[title]")) !== null) { info = API.get_favorite_icon_info_from_node(n); data.favorites.category = info[0]; data.favorites.category_title = info[1]; } } } } else if ((nodes = html.querySelectorAll("table.itg .gtr0,table.itg .gtr1")).length > 0) { for (i = 0; i < nodes.length; ++i) { node = nodes[i]; data = create_blank_gallery_stub(); results.push(data); // Category if ((n = node.querySelector(".itdc>a>img[alt]")) !== null) { data.category = n.getAttribute("alt").toLowerCase(); } // Uploader if ((n = node.querySelector(".itu>div>a")) !== null) { data.uploader = n.textContent.trim(); } if ((n1 = node.querySelectorAll(".itd")).length >= 2) { // Date data.date_uploaded = API.get_date_uploaded_from_node(n1[0]); // Title, ID/token n1 = n1[1]; if ((n = n1.querySelector(".it5>a")) !== null) { data.title = n.textContent.trim(); if ((info = API.get_gallery_url_info(n.getAttribute("href") || "")) !== null) { data.gallery.gid = info.gid; data.gallery.token = info.token; } } // Thumbnail if ((n = n1.querySelector(".it2>img[src]")) !== null) { data.thumbnail = n.getAttribute("src"); } else if ((n = n1.querySelector(".it2")) !== null) { data.thumbnail = API.parse_init_image(n.textContent.trim()); } // Favorited if ((n = n1.querySelector(".it3 .i[title]")) !== null) { info = API.get_favorite_icon_info_from_node(n); data.favorites.category = info[0]; data.favorites.category_title = info[1]; } } } } return results; }, parse_init_image: function (text) { var m = /^init~([\w\-\.]+)~([^~]+)~(.*)$/.exec(text); if (m === null) return null; return window.location.protocol + "//" + m[1] + "/" + m[2]; }, form_page_url: function (html, page_id, include_hash) { var node = html.querySelector(".ptt td.ptds>a"), page_key = "page", page_part = page_key + "=" + page_id, found = false, m, search; if (node === null) return "?" + page_part; // Gallery if (html.querySelector(".gm h1#gn") !== null) { page_key = "p"; page_part = page_key + "=" + page_id; } // Match m = /^([^#\?]*)(\?[^#]*)?((?:#.*)?)$/.exec(node.getAttribute("href") || ""); // No search query found if (m[2] === undefined) { search = page_part; } else { search = m[2].substr(1).replace(new RegExp("(^|&)" + page_key + "=[^&]*", "g"), function (full, prefix) { found = true; return prefix + page_part; }); if (!found) { if (search.length > 0) page_part += "&"; search = page_part + search; } } // Complete return m[1] + "?" + search + (include_hash ? m[3] : ""); }, form_gallery_image_url: function (gid, key, page, direct_id) { var url = "/s/" + key + "/" + gid + "-" + (page + 1); if (direct_id) url += "?nl=" + direct_id; return url; }, modify_page_navigation_links: function (html, modify_fn) { var pages = html.querySelectorAll(".ptt td,.ptb td"), i, n; for (i = 0; i < pages.length; ++i) { if ((n = pages[i].querySelector("a")) !== null) { modify_fn.call(null, pages[i], n); } } }, add_header_link: function (node) { var par = document.getElementById("nb"), div; if (par === null) return false; div = document.createElement("div"); div.appendChild(node); par.appendChild(div); return true; }, get_front_page_links: function (html) { var nodes = [], n; if ((n = html.querySelector("#nb>a[href='https://exhentai.org/']")) !== null) nodes.push(n); if ((n = html.querySelector("#frontpage>a[href='https://exhentai.org/']")) !== null) nodes.push(n); if ((n = html.querySelector(".ip>a[href='https://exhentai.org/']")) !== null) nodes.push(n); return nodes; }, get_image_limits_from_html: function (html) { var nodes = html.querySelectorAll(".stuffbox>h2"), obj = null, i, n; for (i = 0; i < nodes.length; ++i) { n = nodes[i]; if (/^\s*Image\s+Limits\s*$/i.test(n.textContent)) { while ((n = n.nextSibling) !== null) { if (n.classList) { if (n.classList.contains("homebox") && (nodes = n.querySelectorAll("p>strong")).length >= 3) { // Found obj = { current: parseInt(nodes[0].textContent.trim(), 10) || 0, limit: parseInt(nodes[1].textContent.trim(), 10) || 0, increase: parseInt(nodes[2].textContent.trim(), 10) || 0, }; } break; } } break; } } // Not found return obj; }, get_image_limits: function (callback) { return API.request_document("http://e-hentai.org/home.php", // On load function (response, status, status_text) { var data = API.get_image_limits_from_html(response); if (status == 200) { if (data !== null) { callback.call(null, API.OK, data, response); } else { callback.call(null, API.ERROR_PARSING, response); } } else { callback.call(null, API.ERROR_REQUEST_STATUS, [ response , status, status_text ]); } }, // On error function (event) { callback.call(null, API.ERROR_REQUEST, event); }, null, null, { use_gm: true, } ); }, }; // Done return API; })(); // Menu dropdown var Menu = (function () { var Menu = function (flags) { // Settings this.options = []; this.events = { "select": [], }; this.close_timer = null; this.flags = (flags === undefined) ? (Menu.BELOW | Menu.LEFT | Menu.CENTER | Menu.VERTICAL) : flags; // Node this.container = $("div", "eze_menu"); // Events this.on_document_mousedown = bind(on_document_mousedown, this); document.addEventListener("mousedown", this.on_document_mousedown, false); this.on_menu_mousedown = bind(on_menu_mousedown, this); this.container.addEventListener("mousedown", this.on_menu_mousedown, false); // Add node document.body.appendChild(this.container); }; // Constants Menu.ABOVE = 0x1; Menu.BELOW = 0x2; Menu.LEFT = 0x4; Menu.RIGHT = 0x8; Menu.CENTER = 0x10; // For secondary axis Menu.MIDDLE = 0x20; // For secondary axis Menu.HORIZONTAL = 0x40; // Primary axis Menu.VERTICAL = 0x80; // Primary axis Menu.VERTICAL_LOCK = 0x100; Menu.HORIZONTAL_LOCK = 0x200; Menu.NO_FONT_SCALING = 0x800; var trigger = Events.trigger(); var on_option_click = function (menu, id, event) { // Skip if (event.which != 1) return; // Trigger event trigger.call(menu, "select", { menu: menu, option: this, id: id, }); // Stop event.preventDefault(); event.stopPropagation(); return false; }; var on_document_mousedown = function (event) { // Skip if (event.which != 1) return; // Timer var self = this; this.close_timer = setTimeout(function () { self.close(); }, 50); }; var on_menu_mousedown = function (event) { // Skip if (event.which != 1) return; // Cancel if (this.close_timer !== null) { clearTimeout(this.close_timer); this.close_timer = null; } // Stop event.preventDefault(); event.stopPropagation(); return false; }; var set_font_size = function (parent) { var fs1, fs2; // Reset this.container.style.fontSize = ""; // Set font size fs1 = parseFloat(CSS.computed(parent, "fontSize").replace(/[^\d\.]+/g, "")); fs2 = parseFloat(CSS.computed(this.container, "fontSize").replace(/[^\d\.]+/g, "")); this.container.style.fontSize = (fs1 / fs2).toFixed(2) + "em"; }; var set_position = function (par_rect) { // Setup vars var k_low = [ "left" , "top" ], k_high = [ "right" , "bottom" ], k_size = [ "width" , "height" ], k_aflags = [ [ Menu.CENTER , Menu.LEFT , Menu.RIGHT ], [ Menu.MIDDLE , Menu.TOP , Menu.BOTTOM ], ], position = [ 0 , 0 ], bounds = false, self_rect = Geometry.get_object_rect(this.container), view_rect = Geometry.get_window_rect(), i = ((this.flags & Menu.VERTICAL) !== 0 || (this.flags & Menu.HORIZONTAL) === 0) ? 1 : 0, flex = [ (this.flags & Menu.HORIZONTAL_LOCK) === 0, (this.flags & Menu.VERTICAL_LOCK) === 0, ], align = [ (this.flags & Menu.LEFT) === 0, // Is to the right (this.flags & Menu.ABOVE) === 0, // Is below ], low, high, size, aflags, p; // Primary axis low = k_low[i]; high = k_high[i]; size = k_size[i]; // Default position p = align[i] ? par_rect[high] : par_rect[low] - self_rect[size]; // View flexing if (flex[i]) { if (align[i]) { if ( p + self_rect[size] > view_rect[high] && // Goes below bottom of screen par_rect[low] - self_rect[size] >= view_rect[low] // Top must stay on screen after the change ) { // Move above p = par_rect[low] - self_rect[size]; } } else { if ( p < view_rect[low] && // Goes above top of screen par_rect[high] + self_rect[size] <= view_rect[high] // Bottom must stay on screen ) { // Move below p = par_rect[high]; } } } position[i] = p; // Secondary axis flexing i = 1 - i; low = k_low[i]; high = k_high[i]; size = k_size[i]; aflags = k_aflags[i]; // Default position if ((this.flags & aflags[0]) !== 0) { p = par_rect[low] + (par_rect[size] - self_rect[size]) / 2.0; // Left/right bounding if ((this.flags & aflags[1]) !== 0) { p = Math.max(p, par_rect[low]); } else if ((this.flags & aflags[2]) !== 0) { p = Math.min(p, par_rect[high] - self_rect[size]); } } else { p = align[i] ? par_rect[high] - self_rect[size] : par_rect[low]; } // View flexing if (flex[i]) { if (p + self_rect[size] > view_rect[high]) { // Shift to left p = view_rect[high] - self_rect[size]; bounds = true; } else if (p < view_rect[low]) { // Shift to right p = view_rect[low]; bounds = true; } if (bounds) { p = Math.min( Math.max(par_rect[high] - self_rect[size], par_rect[low]), // max value Math.max( Math.min(par_rect[low], par_rect[high] - self_rect[size]), // min value p ) ); } } position[i] = p; // Apply position this.container.style.left = position[0].toFixed(2) + "px"; this.container.style.top = position[1].toFixed(2) + "px"; }; Menu.prototype = { constructor: Menu, on: Events.on(), off: Events.off(), add_option: function (data, option_settings) { var tag_type = "a", tag_class = "eze_menu_option", is_label = false, opt, on_click; if (option_settings) { if (option_settings.label) { tag_type = "div"; tag_class = "eze_menu_label"; is_label = true; } } if (typeof(data) == "string") { // String opt = $(tag_type, tag_class, data); } else { // Assume a DOM node opt = $(tag_type, tag_class); opt.appendChild(data); } // Setup node if (!is_label) { on_click = bind(on_option_click, opt, this, this.options.length); opt.addEventListener("click", on_click, false); } this.container.appendChild(opt); this.options.push([ opt , on_click ]); return opt; }, show: function (parent, flags) { // Setup flags if (flags !== undefined) this.flags = flags; // Show this.container.classList.add("eze_menu_visible"); // Set font size if ((this.flags & Menu.NO_FONT_SCALING) === 0) { set_font_size.call(this, parent); } // Rects var par_rect = Geometry.get_object_rect(parent); set_position.call(this, par_rect); }, hide: function () { this.container.classList.remove("eze_menu_visible"); }, close: function () { // Remove events for (var i = 0; i < this.options.length; ++i) { this.options[i][0].removeEventListener("click", this.options[i][1], false); } document.removeEventListener("mousedown", this.on_document_mousedown, false); this.container.removeEventListener("mousedown", this.on_menu_mousedown, false); // Hide and remove this.hide(); if (this.container.parentNode !== null) { this.container.parentNode.removeChild(this.container); } // Clear events this.events = {}; this.options = []; // Cancel timer if (this.close_timer !== null) { clearTimeout(this.close_timer); this.close_timer = null; } }, is_visible: function () { return this.container.classList.contains("eze_menu_visible"); }, }; return Menu; })(); // Option box var OptionBox = (function () { var OptionBox = function (options, selected_value, callback) { var sel = null, i, n, name, val; this.callback = callback; this.node = $("div", "eze_option_box"); for (i = 0; i < options.length; ++i) { name = "" + options[i][0]; val = (options[i].length >= 2) ? options[i][1] : name; n = $("div", "eze_option_box_entry", name, { "data-eze-option-id": i, "data-eze-value": JSON.stringify(val), }, $.P, this.node); if (sel === null || val == selected_value) sel = n; } if (sel !== null) { sel.classList.add("eze_option_box_entry_selected"); } this.node.addEventListener("click", bind(on_click, this), false); }; var on_click = function (event) { // Skip if (event.which != 1) return; var sel = this.node.querySelector(".eze_option_box_entry_selected"), val; // Deselect if (sel !== null) { sel.classList.remove("eze_option_box_entry_selected"); sel = sel.nextSibling; if (sel === null) sel = this.node.firstChild; } else { sel = this.node.firstChild; } if (sel !== null) { // Select new sel.classList.add("eze_option_box_entry_selected"); // Value try { val = JSON.parse(sel.getAttribute("data-eze-value") || ""); } catch (e) { val = null; } if (this.callback) this.callback.call(null, val, sel); } // Stop event.preventDefault(); event.stopPropagation(); return false; }; OptionBox.prototype = { constructor: OptionBox, get: function () { var n = this.node.querySelector(".eze_option_box_entry_selected"); if (n === null) return null; try { return JSON.parse(n.getAttribute("data-eze-value") || ""); } catch (e) { return null; } }, set: function (target_value) { var opts = this.node.querySelectorAll(".eze_option_box_entry"), sel = null, i, value, okay; // Find correct value for (i = 0; i < opts.length; ++i) { try { value = JSON.parse(opts[i].getAttribute("data-eze-value")); okay = true; } catch (e) { okay = false; } if (okay && value === target_value) { sel = opts[i]; break; } } if (sel !== null) { // Deselect opts = this.node.querySelectorAll(".eze_option_box_entry_selected"); for (i = 0; i < opts.length; ++i) { opts[i].classList.remove("eze_option_box_entry_selected"); } // Select sel.classList.add("eze_option_box_entry_selected"); return true; } return false; }, }; return OptionBox; })(); // Gallery downloader var GalleryDownloader = (function () { var GalleryDownloader = function (gallery, thumb_loader) { // Vars this.events = { error: [], active_change: [], state_change: [], gallery_page_get: [], gallery_page_progress: [], image_page_get: [], image_page_progress: [], image_get: [], image_progress: [], image_timeout_progress: [], image_range_update: [], }; this.thumb_loader = thumb_loader; this.active = false; this.state = GalleryDownloader.NOT_STARTED; this.request_gallery = new Requester(this, request_gallery_page, request_gallery_page_done, 1.0, 3.0, 1.0, "gallery_page_get", null); this.request_image_page = new Requester(this, request_image_page, request_image_page_done, 1.0, 3.0, 1.0, "image_page_get", null); this.request_image = new Requester(this, request_image, request_image_done, 1.0, 3.0, 1.0, "image_get", "image_timeout_progress"); this.request_timeout = 30.0; this.request_image.retry_max = 2; this.max_filename_length = 200; this.use_full_images = true; this.allow_fallback_if_using_full_images = true; this.request_progress = 0.0; //Gallery data this.gallery = { gid: gallery.gid, token: gallery.token, }; this.gal_info = null; this.gal_title_info = null; this.page_count = 0; this.current = 0; this.image_id = 0; this.images = []; this.image_counts = [ 0 , 0 , 0 , 0 , 0 ]; this.image_total_bytes = [ 0 , 0 , 0 ]; this.image_total_bytes_loaded = 0; this.images_downloading = 0; this.image_ranges = []; }; var Requester = function (downloader, request_function, completion_check, delay_okay, delay_error, delay_abort, event_name_next, event_name_progress) { this.downloader = downloader; this.index = 0; this.progress = 0.0; this.progress_loaded = 0; this.progress_total = 0; this.retry_index = 0; this.retry_max = 0; this.delays = [ delay_okay * 1000 , delay_error * 1000 , delay_abort * 1000 ]; this.request = null; this.timeout_timer = null; this.delay_timer = null; this.completed = false; this.request_function = request_function; this.completion_check = completion_check; this.event_name_next = event_name_next; this.event_name_progress = event_name_progress; }; Requester.prototype = { constructor: Requester, stop: function (complete) { // Returns true if a request was active, false otherwise if (this.timeout_timer !== null) { this.timeout_timer(); this.timeout_timer = null; } if (this.request !== null) { if (complete) { this.request = null; } else { this.request.abort(); this.request = null; this.progress_reset(); } return true; } return false; }, resume: function (reset) { // Don't continue if a delay timeout is active if (this.downloader.active && this.request === null && this.delay_timer === null && !this.completed) { if (reset) this.retry_index = 0; this.progress_reset(); this.request_function.call(this.downloader, this); if (this.request !== null) { var on_progress = (this.event_name_progress === null) ? null : bind(this.on_timeout_progress, this); this.timeout_timer = set_timeout(bind(this.on_request_timeout, this), this.downloader.request_timeout * 1000, on_progress, 250); } } }, next: function () { // Next var i = this.index; ++this.index; // Event trigger.call(this.downloader, this.event_name_next, { downloader: this.downloader, index: i, }); // Check if complete if (!(this.completed = this.completion_check.call(this.downloader, this))) { // Delay next this.delay(GalleryDownloader.DELAY_OKAY, true); } }, delay: function (mode, reset) { var self = this, cb = function () { self.delay_timer = null; self.resume(reset); }; if (mode === GalleryDownloader.DELAY_NONE) { cb.call(null); } else { this.delay_timer = setTimeout(cb, this.delays[mode]); } }, retry: function () { // Increase retry if (this.retry_index + 1 > this.retry_max) { // Stop completely this.downloader.pause(); } else { // Delay for next ++this.retry_index; this.delay(GalleryDownloader.DELAY_ERROR, false); } }, progress_reset: function () { this.progress = 0.0; this.progress_loaded = 0; this.progress_total = 0; }, on_request_timeout: function () { // Stop timer this.timeout_timer = null; // Stop request this.stop(false); // Error trigger_error.call(this.downloader, "Request timed out"); // Retry this.retry(); }, on_timeout_progress: function (percent, time) { // Event trigger.call(this.downloader, this.event_name_progress, { downloader: this.downloader, percent: percent, time: time / 1000.0, }); }, }; GalleryDownloader.NOT_STARTED = 0; GalleryDownloader.REQUESTING_GALLERY_PAGES = 1; GalleryDownloader.REQUESTING_IMAGES = 2; GalleryDownloader.COMPLETED = 3; GalleryDownloader.DELAY_NONE = -1; GalleryDownloader.DELAY_OKAY = 0; GalleryDownloader.DELAY_ERROR = 1; GalleryDownloader.DELAY_ABORT = 2; GalleryDownloader.IMAGE_NOT_ACQUIRED = 0; GalleryDownloader.IMAGE_FULL = 1; GalleryDownloader.IMAGE_RESIZED = 2; GalleryDownloader.IMAGE_UNRESIZED = 3; GalleryDownloader.IMAGE_FALLBACK = 4; var trigger = Events.trigger(); var trigger_error = function (error_message) { trigger.call(this, "error", { downloader: this, error: error_message, }); }; var state_change = function (new_state) { // Change state this.state = new_state; trigger.call(this, "state_change", { downloader: this, state: this.state, }); state_resume.call(this); }; var state_resume = function () { // Resume state if (this.state == GalleryDownloader.NOT_STARTED) { state_change.call(this, GalleryDownloader.REQUESTING_GALLERY_PAGES); } else if (this.state == GalleryDownloader.REQUESTING_GALLERY_PAGES) { this.request_gallery.resume(true); } else if (this.state == GalleryDownloader.REQUESTING_IMAGES) { this.request_image_page.resume(true); this.request_image.resume(true); } }; var validate_image = function (image_id) { // All if (this.image_ranges.length === 0) return true; // Convert to 1-indexed ++image_id; // Check if it's in a range for (var i = 0, r; i < this.image_ranges.length; ++i) { r = this.image_ranges[i]; if (image_id >= r[0] && image_id <= r[1]) return true; } // Wasn't in any range return false; }; var approximate_total_size = function (size) { // Multiply the size by the % of images in the range(s) for an approximation return size * (this.images_downloading / this.gal_info.image_count); }; var update_image_ranges = function () { // Normalize var max = this.gal_info.image_count, i = 0; this.images_downloading = 0; for (i = 0; i < this.image_ranges.length; ++i) { if (this.image_ranges[i][0] > max) { // Remove remaining this.image_ranges.splice(i, this.image_ranges.length - i); break; } else if (this.image_ranges[i][1] === null || this.image_ranges[i][1] > max) { this.image_ranges[i][1] = max; } this.images_downloading += (this.image_ranges[i][1] - this.image_ranges[i][0]) + 1; } // Convert to all if (this.image_ranges.length === 1 && this.image_ranges[0][0] <= 1 && this.image_ranges[0][1] >= max) { this.image_ranges.splice(0, this.image_ranges.length); } if (this.image_ranges.length === 0) { this.images_downloading = max; } // Trigger event trigger.call(this, "image_range_update", { downloader: this, }); }; var request_gallery_page = function (req) { // Request req.request = API.get_gallery( this.gallery.gid, // gallery id this.gallery.token, // gallery token req.index, // page false, // use API bind(on_request_gallery_page_callback, this, req), // callback null, // on_load null, // on_error null, // on_complete bind(on_request_progress, this, req, "gallery_page_progress") // on_progress ); }; var request_gallery_page_done = function (req) { // Done check if (this.page_count > 0 && req.index >= this.page_count) { // State change state_change.call(this, GalleryDownloader.REQUESTING_IMAGES); return true; } return false; }; var on_request_gallery_page_callback = function (req, status, data, response) { // Stop timer req.stop(true); if (status == API.OK) { // Set info if (this.gal_info === null) { this.gal_info = data; this.gal_title_info = API.get_gallery_title_info(this.gal_info.title); update_image_ranges.call(this); this.image_total_bytes[GalleryDownloader.IMAGE_NOT_ACQUIRED] = approximate_total_size.call(this, this.gal_info.total_file_size_approx); } // Page count if (this.page_count === 0) { var page_info = API.get_pages_info_from_html(response); if (page_info !== null) { // Set count this.page_count = page_info.count; } else { // Error trigger_error.call(this, "Failed to read gallery page count"); // Stop req.retry(); return; } } // Get images and add var images = API.get_gallery_images_from_html(response), count = 0, i; for (i = 0; i < images.length; ++i) { if (validate_image.call(this, this.image_id)) { this.images.push({ info_from_gallery: images[i], info: null, info_fallback: null, byte_data: null, image_id: this.image_id, used: { method: GalleryDownloader.IMAGE_NOT_ACQUIRED, info: null, }, }); ++count; } ++this.image_id; } this.image_counts[GalleryDownloader.IMAGE_NOT_ACQUIRED] += count; // Update page this.thumb_loader.add_page_from_html(response, req.index); // Next req.next(); } else { // Error if (data[0] == API.ERROR_REQUEST) { trigger_error.call(this, "Request for gallery page failed"); } else if (data[0] == API.ERROR_REQUEST_STATUS) { trigger_error.call(this, "Request status for gallery page was not 200: " + data[1] + "; " + data[2]); } else { // if (data[0] == API.ERROR_PARSING) { trigger_error.call(this, "An error occured parsing the response for gallery page"); } // Stop req.retry(); } }; var request_image_page = function (req) { var api_key = null, gid, key, page, i; if (req.index > 0) { // Get previous info i = this.images[req.index - 1].info; // Set if (i.navigation.key_next && this.images[req.index].image_id === i.page + 1) { gid = i.gallery.gid; key = i.navigation.key_next; page = i.page + 1; api_key = this.images[0].info.navigation.api_key || null; } } if (api_key === null) { // Get URL info i = API.get_gallery_image_url_info(this.images[req.index].info_from_gallery.url); if (i === null) return; // Invalid gid = i.gid; key = i.key; page = i.page; api_key = null; } // Request req.request = API.get_image_info( gid, // gid key, // key page, // page api_key, // api_key null, // direct_id bind(on_request_image_page_callback, this, req), // callback null, // on_load null, // on_error null, // on_complete bind(on_request_progress, this, req, "image_page_progress") // on_progress ); }; var request_image_page_done = function (req) { // Done check return (req.index >= this.images.length); }; var on_request_image_page_callback = function (req, status, data) { req.stop(true); if (status == API.OK) { if (/\/509\.gif$/i.test(data.image.url)) { // Error trigger_error.call(this, "Image viewing limit has been exceeded"); // Stop this.pause(); return; } // Set info this.images[req.index].info = data; // Update size this.image_total_bytes[GalleryDownloader.IMAGE_FULL] += (data.image_original !== null) ? data.image_original.size_approx : data.image.size_approx; this.image_total_bytes[GalleryDownloader.IMAGE_RESIZED] += data.image.size_approx; // Next req.next(); // Continue the image requester this.request_image.resume(true); } else { // Error if (data[0] == API.ERROR_REQUEST) { trigger_error.call(this, "Request for image page failed"); } else if (data[0] == API.ERROR_REQUEST_STATUS) { trigger_error.call(this, "Request status for image page was not 200: " + data[1] + "; " + data[2]); } else { // if (data[0] == API.ERROR_PARSING) { trigger_error.call(this, "An error occured parsing the response for image page"); } // Retry req.retry(); } }; var request_image = function (req) { var image_data = this.images[req.index], use_full = this.use_full_images, try_index = req.retry_index, using_data = null, using_method = GalleryDownloader.IMAGE_NOT_ACQUIRED, url_info; // Can't start yet if (image_data.info === null) return; // Acquire method if ( try_index === 0 || ( use_full && !this.allow_fallback_if_using_full_images && image_data.info.image_original !== null ) ) { // Get the primary image if (use_full) { if (image_data.info.image_original === null) { using_data = image_data.info.image; using_method = GalleryDownloader.IMAGE_UNRESIZED; } else { using_data = image_data.info.image_original; using_method = GalleryDownloader.IMAGE_FULL; } } else { using_data = image_data.info.image; using_method = (image_data.info.image_original === null) ? GalleryDownloader.IMAGE_UNRESIZED : GalleryDownloader.IMAGE_RESIZED; } } else { if (use_full && try_index <= (image_data.info.image_original === null ? 0 : 1)) { // Get the resized image using_data = image_data.info.image; using_method = GalleryDownloader.IMAGE_RESIZED; } else if (image_data.info_fallback !== null) { // Get the fallback image using_data = image_data.info_fallback.image; using_method = GalleryDownloader.IMAGE_FALLBACK; } // Else, get the fallback page } // Get if (using_data !== null) { // Get image req.request = API.request_data( using_data.url, // url bind(on_request_image_load, this, req, using_data, using_method), // on_load bind(on_request_image_error, this, req), // on_error null, // on_complete bind(on_request_progress, this, req, "image_progress") // on_progress ); } else { // Get fallback page url_info = API.get_gallery_image_url_info(image_data.info_from_gallery.url); image_data = image_data.info; req.request = API.get_image_info( image_data.gallery.gid, // gid url_info && url_info.key ? url_info.key : image_data.navigation.key_current, // key image_data.page, // page null, // api_key image_data.navigation.direct_id, // direct_id bind(on_request_image_fallback_page_callback, this, req), // callback null, // on_load null, // on_error null, // on_complete null // on_progress ); } }; var request_image_done = function (req) { // Done check if (req.index >= this.images.length) { // Done state_change.call(this, GalleryDownloader.COMPLETED); return true; } return false; }; var on_request_image_load = function (req, using_data, using_method, response, status, status_text, extra) { req.stop(true); if (status != 200) { // Error trigger_error.call(this, "Request status for image was not 200: " + status + "; " + status_text); // Retry req.retry(); return; } // Check for invalid if (response.length <= 200) { var s = ZipCreator.array_to_string(response); if (/you\s+have\s+exceeded\s+your\s+image\s+viewing\s+limits/i.test(s)) { // Error trigger_error.call(this, "Image viewing limit has been exceeded"); // Stop this.pause(); return; } else if (/invalid\s+token/i.test(s)) { // Error trigger_error.call(this, "Invalid token error encountered"); // Stop this.pause(); return; } } // Set info var image_data = this.images[req.index], data = image_data.info; image_data.final_url = extra.final_url; this.image_total_bytes[GalleryDownloader.IMAGE_FULL] -= (data.image_original !== null) ? data.image_original.size_approx : data.image.size_approx; this.image_total_bytes[GalleryDownloader.IMAGE_FULL] += response.length; this.image_total_bytes[GalleryDownloader.IMAGE_RESIZED] -= data.image.size_approx; this.image_total_bytes[GalleryDownloader.IMAGE_RESIZED] += response.length; this.image_total_bytes_loaded += response.length; --this.image_counts[image_data.used.method]; ++this.image_counts[using_method]; image_data.used.method = using_method; image_data.used.data = using_data; image_data.byte_data = response; // Next req.next(); }; var on_request_image_error = function (req, event) { req.stop(true); if (event != "abort") { // Error trigger_error.call(this, "Request for image failed"); // Retry req.retry(); } }; var on_request_image_fallback_page_callback = function (req, status, data) { req.stop(true); if (status == API.OK) { // Set info this.images[req.index].info_fallback = data; // Delay next req.delay(GalleryDownloader.DELAY_OKAY, true); } else { // Error if (data[0] == API.ERROR_REQUEST) { trigger_error.call(this, "Request for image fallback page failed"); } else if (data[0] == API.ERROR_REQUEST_STATUS) { trigger_error.call(this, "Request status for image fallback page was not 200: " + data[1] + "; " + data[2]); } else { // if (data[0] == API.ERROR_PARSING) { trigger_error.call(this, "An error occured parsing the response for image fallback page"); } // Retry req.retry(); } }; var on_request_progress = function (req, progress_event, percent, loaded, total) { // Update req.progress = percent; req.progress_loaded = loaded; req.progress_total = total; // Event trigger.call(this, progress_event, { downloader: this, progress: percent, loaded: loaded, total: total, }); }; GalleryDownloader.prototype = { constructor: GalleryDownloader, pause: function () { // Already not active if (!this.active) return; // Stop state if (this.request_gallery.stop(false)) { this.request_gallery.delay(GalleryDownloader.DELAY_ABORT, true); } if (this.request_image_page.stop(false)) { this.request_image_page.delay(GalleryDownloader.DELAY_ABORT, true); } if (this.request_image.stop(false)) { this.request_image.delay(GalleryDownloader.DELAY_ABORT, true); } // Inactive this.active = false; trigger.call(this, "active_change", { downloader: this, active: this.active, }); }, resume: function () { // Already active or done if (this.active || this.state == GalleryDownloader.COMPLETED) return; // Active this.active = true; trigger.call(this, "active_change", { downloader: this, active: this.active, }); // Resume state state_resume.call(this); }, is_active: function () { return this.active; }, is_done: function () { return (this.state == GalleryDownloader.COMPLETED); }, get_request_timeout: function () { return this.request_timeout; }, set_request_timeout: function (timeout) { this.request_timeout = Math.max(1.0, timeout); }, get_image_max_retry_count: function () { return this.request_image.retry_max; }, set_image_max_retry_count: function (retry_count) { this.request_image.retry_max = Math.max(0, Math.floor(retry_count)); }, get_max_filename_length: function () { return this.max_filename_length; }, set_max_filename_length: function (max_length) { this.max_filename_length = Math.max(10, Math.floor(max_length)); }, get_use_full_images: function () { return this.use_full_images; }, set_use_full_images: function (use_full) { this.use_full_images = use_full; }, get_allow_fallback_if_using_full_images: function () { return this.allow_fallback_if_using_full_images; }, set_allow_fallback_if_using_full_images: function (allow) { this.allow_fallback_if_using_full_images = allow; }, get_state: function () { return this.state; }, get_gallery_page_count: function () { return this.page_count; }, get_image_count: function () { return this.images.length; }, get_image_type_counts: function () { return this.image_counts; }, get_image_total_bytes: function () { if (this.request_image_page.index >= this.images.length) { return this.image_total_bytes[this.use_full_images ? GalleryDownloader.IMAGE_FULL : GalleryDownloader.IMAGE_RESIZED]; } else { return this.image_total_bytes[GalleryDownloader.IMAGE_NOT_ACQUIRED]; } }, get_image_total_bytes_loaded: function () { return this.image_total_bytes_loaded; }, get_current_image_bytes_loaded: function () { return this.request_image.request === null ? 0 : this.request_image.progress_loaded; }, get_current_image_bytes_total: function () { return this.request_image.request === null ? 0 : this.request_image.progress_total; }, set_image_ranges: function (text) { if (this.gal_info !== null) return false; // Cannot be set once started // Parse var parts = text.split(","), ranges = [], re = /^\s*(?:([0-9]+)\s*(-\s*([0-9]+)?)?|(?:-\s*([0-9]+)?)|(all))\s*$/i, min_value = 1, i, m, v1, v2, vt, r; for (i = 0; i < parts.length; ++i) { if ((m = re.exec(parts[i])) === null) continue; if (m[1] !== undefined) { v1 = Math.max(min_value, parseInt(m[1], 10)); if (m[2] === undefined) { // "x" v2 = v1; } else if (m[3] === undefined) { // "x-" v2 = null; if (v1 <= min_value) { // "1-" is the same as "all" this.image_ranges = []; return true; } } else { // "x-y" v2 = Math.max(min_value, parseInt(m[3], 10)); if (v2 < v1) { // Flip vt = v1; v1 = v2; v2 = vt; } } } else if (m[4] !== undefined) { // "-x" v1 = 1; v2 = Math.max(min_value, parseInt(m[4], 10)); } else { // "all" | "-" this.image_ranges = []; return true; } ranges.push([ v1 , v2 ]); } // Sort ranges.sort(function (a, b) { if (a[0] === b[0]) return 0; return (a[0] < b[0]) ? -1 : 1; }); // Normalize this.image_ranges = [ (r = ranges[0]) ]; for (i = 1; i < ranges.length; ++i) { if (r[1] === null) break; if (ranges[i][0] <= r[1] + 1) { r[1] = ranges[i][1]; } else { this.image_ranges.push((r = ranges[i])); } } // Okay return true; }, get_image_ranges: function () { // All if (this.image_ranges.length === 0) return "all"; // Form string var s = "", i, v; for (i = 0; i < this.image_ranges.length; ++i) { v = this.image_ranges[i]; if (i > 0) s += ", "; s += v[0]; if (v[1] !== v[0]) { s += "-"; if (v[1] !== null) { s += v[1]; } } } return s; }, on: Events.on(), off: Events.off(), }; return GalleryDownloader; })(); // Gallery downloader (GUI/manager) var GalleryDownloadManager = (function () { var GalleryDownloadManager = function (gallery, container, thumb_loader) { // Vars var n0; // Create this.loader = new GalleryDownloader(gallery, thumb_loader); this.zip = new ZipCreator(); this.zip_blob_urls = []; this.zip_blob_script_urls = []; // Values this.filename_mode = constants.MAIN_NAME_FULL; this.filename_ext_mode = constants.FILE_EXTENSION_ZIP; this.image_naming_mode = constants.IMAGE_NAMING_SINGLE_NUMBER; this.numbering_mode = constants.NUMBERING_RENUMBER; this.zip_info_json_name = "info.json"; this.zip_info_json_mode = constants.JSON_READABLE_2SPACE; // Title this.node_title = document.documentElement.querySelector("title"); this.title_default = this.node_title ? this.node_title.textContent || "" : ""; this.title_started = false; // Nodes this.node_main_link = null; this.node_main_links_container = null; this.node_info_status = null; this.node_info_count = null; this.node_info_error = null; this.node_opts_filename_mode = null; this.node_opts_filename_ext_mode = null; this.node_opts_filename_length = null; this.node_opts_image_naming_mode = null; this.node_opts_zip_info_json_mode = null; this.node_opts_numbering_mode = null; this.node_progress_gallery = null; this.node_progress_gallery_text = null; this.node_progress_image_pages = null; this.node_progress_image_pages_text = null; this.node_progress_images = null; this.node_progress_images_text = null; this.node_progress_image_size = null; this.node_progress_image_size_text = null; this.node_progress_image_current = null; this.node_progress_image_current_text = null; this.node_progress_timeout = null; this.node_progress_timeout_indicator = null; this.node_zip_info_json_name = null; this.node_full_image_checkbox = null; this.node_non_full_image_fallback = null; this.node_failure_timeout = null; this.node_failure_retry_max = null; this.node_file_ranges = null; // Create nodes n0 = $("div", "eze_gallery_custom_container_inner", [ //{ $("div", "eze_dl_title", [ this.node_main_link = $("a", "eze_dl_link", null, "Begin downloading"), ]), this.node_main_links_container = $("div", "eze_dl_links eze_dl_links_hidden"), $("div", "eze_dl_info_container", [ this.node_info_status = $("div", "eze_dl_info eze_dl_info_visible", "Downloading has not started"), this.node_info_count = $("div", "eze_dl_info", ""), this.node_info_error = $("div", "eze_dl_info eze_dl_info_error", ""), ]), $("div", "eze_dl_progress_bar eze_dl_progress_bar_gallery", [ this.node_progress_gallery = $("div", "eze_dl_progress_bar_bg"), $("div", "eze_dl_progress_bar_text", [ $.text("Gallery progress: "), this.node_progress_gallery_text = $.text(), ]), ]), $("div", "eze_dl_progress_bar eze_dl_progress_bar_image_pages", [ this.node_progress_image_pages = $("div", "eze_dl_progress_bar_bg"), $("div", "eze_dl_progress_bar_text", [ $.text("Image page progress: "), this.node_progress_image_pages_text = $.text(), ]), ]), $("div", "eze_dl_progress_bar eze_dl_progress_bar_images", [ this.node_progress_images = $("div", "eze_dl_progress_bar_bg"), $("div", "eze_dl_progress_bar_text", [ $.text("Image progress: "), this.node_progress_images_text = $.text(), ]), ]), $("div", "eze_dl_progress_bar eze_dl_progress_bar_image_size", [ this.node_progress_image_size = $("div", "eze_dl_progress_bar_bg"), $("div", "eze_dl_progress_bar_text", [ $.text("Image byte progress: "), this.node_progress_image_size_text = $.text(), ]), ]), $("div", "eze_dl_progress_bar eze_dl_progress_bar_timeout", [ this.node_progress_timeout = $("div", "eze_dl_progress_bar_bg_light"), this.node_progress_image_current = $("div", "eze_dl_progress_bar_bg"), this.node_progress_timeout_indicator = $("div", "eze_dl_progress_bar_indicator eze_dl_progress_bar_indicator_hidden"), $("div", "eze_dl_progress_bar_text", [ $.text("Current image progress: "), this.node_progress_image_current_text = $.text(), ]), ]), $("div", "eze_dl_title eze_dl_title_pad_above", "Download settings"), $("div", [ $("div", "eze_dl_setting", [ $("div", "eze_dl_setting_row", [ $("div", "eze_dl_setting_cell", [ $("div", "eze_dl_setting_title", "Download ranges"), $("div", "eze_dl_setting_desc", [ $.text("Page range(s) to download; hover the following examples for more info:"), $("span", "eze_dl_setting_desc_tag", "all", { title: "Downloads all images; whitespace is ignored" }), $("span", "eze_dl_setting_desc_tag", "4 - 16, 18, 27 - 37", { title: "Downloads 25 images; image ranges are inclusive" }), $("span", "eze_dl_setting_desc_tag", "32 -", { title: "Downloads images 32 to the end; omitting a number defaults to first/last image" }), $("span", "eze_dl_setting_desc_tag", "- 31", { title: "Downloads images 1 - 31 inclusive; first number is omitted in this case" }), ]), ]), $("div", "eze_dl_setting_cell", [ $("div", [ this.node_file_ranges = $("input", "eze_dl_setting_input", { type: "text" }), ]), ]), ]), ]), $("div", "eze_dl_setting", [ $("div", "eze_dl_setting_row", [ $("div", "eze_dl_setting_cell", [ $("div", "eze_dl_setting_title", "Output filename"), $("div", "eze_dl_setting_desc", "Set the primary output .zip file's name"), ]), $("div", "eze_dl_setting_cell", [ $("div", [ (this.node_opts_filename_mode = new OptionBox([ [ "Full gallery name", constants.MAIN_NAME_FULL ], [ "Short gallery name", constants.MAIN_NAME_SHORT ], ], this.filename_mode, bind(on_option_filename_change, this))).node, (this.node_opts_filename_ext_mode = new OptionBox([ [ ".zip", constants.FILE_EXTENSION_ZIP ], [ ".cbz", constants.FILE_EXTENSION_CBZ ], ], this.filename_ext_mode, bind(on_option_filename_ext_change, this))).node, ]), $("div", [ $("span", null, "Max filename length: "), this.node_opts_filename_length = $("input", "eze_dl_setting_input eze_dl_setting_input_small", { type: "text", title: "Includes extension" }), ]), ]), ]), ]), $("div", "eze_dl_setting", [ $("div", "eze_dl_setting_row", [ $("div", "eze_dl_setting_cell", [ $("div", "eze_dl_setting_title", "Image filename"), $("div", "eze_dl_setting_desc", "Individual image naming method"), ]), $("div", "eze_dl_setting_cell", [ (this.node_opts_image_naming_mode = new OptionBox([ [ "Single number", constants.IMAGE_NAMING_SINGLE_NUMBER ], [ "Full gallery name + number", constants.IMAGE_NAMING_FULL_NAME ], [ "Short gallery name + number", constants.IMAGE_NAMING_SHORT_NAME ], [ "Original filenames", constants.IMAGE_NAMING_ORIGINAL_NAME ], [ "Number + original filename", constants.IMAGE_NAMING_NUMBERED_ORIGINAL ], ], this.image_naming_mode, bind(on_option_image_name_change, this))).node, ]), ]), ]), $("div", "eze_dl_setting", [ $("div", "eze_dl_setting_row", [ $("div", "eze_dl_setting_cell", [ $("div", "eze_dl_setting_title", "Image numbering"), $("div", "eze_dl_setting_desc", "How to set the image numbers if download ranges are used"), ]), $("div", "eze_dl_setting_cell", [ (this.node_opts_numbering_mode = new OptionBox([ [ "Re-number to start at 1", constants.NUMBERING_RENUMBER ], [ "Use the original numbers", constants.NUMBERING_ORIGINAL ], ], this.numbering_mode, bind(on_option_numbering_mode_change, this))).node, ]), ]), ]), $("div", "eze_dl_setting", [ $("div", "eze_dl_setting_row", [ $("div", "eze_dl_setting_cell", [ $("div", "eze_dl_setting_title", "JSON info file"), $("div", "eze_dl_setting_desc", "A data file containing information about the gallery, such as tags"), ]), $("div", "eze_dl_setting_cell", [ $("div", [ (this.node_opts_zip_info_json_mode = new OptionBox([ [ "Omit", constants.JSON_OMIT ], [ "Compressed", constants.JSON_COMPRESSED ], [ "Human readable (2 space indent)", constants.JSON_READABLE_2SPACE ], [ "Human readable (4 space indent)", constants.JSON_READABLE_4SPACE ], [ "Human readable (tab indent)", constants.JSON_READABLE_TABS ], ], this.zip_info_json_mode, bind(on_option_zip_info_json_mode_change, this))).node, ]), $("div", [ this.node_zip_info_json_name = $("input", "eze_dl_setting_input", { type: "text" }), ]), ]), ]), ]), $("div", "eze_dl_setting", [ $("div", "eze_dl_setting_row", [ $("div", "eze_dl_setting_cell", [ $("div", "eze_dl_setting_title", "Image size"), $("div", "eze_dl_setting_desc", "Preference for downloading full sized images"), ]), $("div", "eze_dl_setting_cell", [ $("label", "eze_dl_label", [ this.node_full_image_checkbox = $("input", { type: "checkbox" }), $("span", null, "Download full-sized images if available"), ]), $("br"), $("label", "eze_dl_label", [ this.node_non_full_image_fallback = $("input", { type: "checkbox" }), $("span", null, "Allow non-full-sized images to be downloaded if a timeout error occurs"), ]), ]), ]), ]), $("div", "eze_dl_setting", [ $("div", "eze_dl_setting_row", [ $("div", "eze_dl_setting_cell", [ $("div", "eze_dl_setting_title", "Failure"), $("div", "eze_dl_setting_desc", "What do do when things go wrong"), ]), $("div", "eze_dl_setting_cell", [ $("div", [ $("span", null, "Timeout for failure: "), this.node_failure_timeout = $("input", "eze_dl_setting_input eze_dl_setting_input_small", { type: "text", title: "Units: seconds" }), ]), $("div", [ $("span", null, "Maximum image retry count: "), this.node_failure_retry_max = $("input", "eze_dl_setting_input eze_dl_setting_input_small", { type: "text" }), ]), ]), ]), ]), ]), ]); //} // Setup values this.node_opts_filename_length.value = this.loader.get_max_filename_length(); this.node_failure_timeout.value = this.loader.get_request_timeout(); this.node_failure_retry_max.value = this.loader.get_image_max_retry_count(); this.node_full_image_checkbox.checked = this.loader.get_use_full_images(); this.node_non_full_image_fallback.checked = this.loader.get_allow_fallback_if_using_full_images(); this.node_zip_info_json_name.value = this.zip_info_json_name; this.node_file_ranges.value = this.loader.get_image_ranges(); // Setup events this.node_opts_filename_length.addEventListener("change", bind(on_option_max_filename_length_change, this)); this.node_main_link.addEventListener("click", bind(on_main_link_click, this)); this.node_full_image_checkbox.addEventListener("change", bind(on_option_full_images_change, this)); this.node_non_full_image_fallback.addEventListener("change", bind(on_option_non_full_image_fallback_change, this)); this.node_failure_timeout.addEventListener("change", bind(on_option_failure_timeout_change, this)); this.node_failure_retry_max.addEventListener("change", bind(on_option_failure_retry_max_change, this)); this.node_zip_info_json_name.addEventListener("change", bind(on_option_zip_info_json_name_change, this)); this.node_file_ranges.addEventListener("change", bind(on_option_file_ranges_change, this)); this.loader.on("error", bind(on_loader_error, this)); this.loader.on("active_change", bind(on_loader_active_change, this)); this.loader.on("state_change", bind(on_loader_state_change, this)); this.loader.on("gallery_page_get", bind(on_loader_gallery_page_get, this)); this.loader.on("image_page_get", bind(on_loader_image_page_get, this)); this.loader.on("image_get", bind(on_loader_image_get, this)); this.loader.on("image_progress", bind(on_loader_image_progress, this)); this.loader.on("image_timeout_progress", bind(on_loader_image_timeout_progress, this)); this.loader.on("image_range_update", bind(on_loader_image_range_update, this)); this.page_close_warning_cb = null; // Load settings load_values.call(this); // Update text update_main_link_text.call(this); update_info_status_text.call(this); update_progress_bars.call(this); update_timeout_progress_bar.call(this, 0, 0); // Show container.appendChild(n0); container.classList.add("eze_dl_container_visible"); }; var constants = { MAIN_NAME_FULL: 0, MAIN_NAME_SHORT: 1, FILE_EXTENSION_ZIP: 0, FILE_EXTENSION_CBZ: 1, IMAGE_NAMING_SINGLE_NUMBER: 0, IMAGE_NAMING_FULL_NAME: 1, IMAGE_NAMING_SHORT_NAME: 2, IMAGE_NAMING_ORIGINAL_NAME: 3, IMAGE_NAMING_NUMBERED_ORIGINAL: 4, NUMBERING_RENUMBER: 0, NUMBERING_ORIGINAL: 1, JSON_OMIT: 0, JSON_COMPRESSED: 1, JSON_READABLE_2SPACE: 2, JSON_READABLE_4SPACE: 3, JSON_READABLE_TABS: 4, }; var valid_extensions = { ".jpg": ".jpg", ".jpeg": ".jpg", ".png": ".png", ".gif": ".gif", "": ".jpg", }; var save_values = function () { // Save Save.set("eze_downloader", { filename_mode: this.filename_mode, filename_ext_mode: this.filename_ext_mode, image_naming_mode: this.image_naming_mode, numbering_mode: this.numbering_mode, zip_info_json_name: this.zip_info_json_name, zip_info_json_mode: this.zip_info_json_mode, use_full_images: this.loader.get_use_full_images(), allow_fallback_if_using_full_images: this.loader.get_allow_fallback_if_using_full_images(), request_timeout: this.loader.get_request_timeout(), image_max_retry_count: this.loader.get_image_max_retry_count(), max_filename_length: this.loader.get_max_filename_length(), }); }; var load_values = function () { // Get the value var self = this; Save.get("eze_downloader", function (value, okay) { if (okay && value) { // Update if (validate(value.filename_mode, [ constants.MAIN_NAME_FULL , constants.MAIN_NAME_SHORT ])) { self.filename_mode = value.filename_mode; self.node_opts_filename_mode.set(self.filename_mode); } if (validate(value.filename_ext_mode, [ constants.FILE_EXTENSION_ZIP , constants.FILE_EXTENSION_CBZ ])) { self.filename_ext_mode = value.filename_ext_mode; self.node_opts_filename_ext_mode.set(self.filename_ext_mode); } if (validate(value.image_naming_mode, [ constants.IMAGE_NAMING_SINGLE_NUMBER , constants.IMAGE_NAMING_FULL_NAME , constants.IMAGE_NAMING_SHORT_NAME , constants.IMAGE_NAMING_ORIGINAL_NAME , constants.IMAGE_NAMING_NUMBERED_ORIGINAL ])) { self.image_naming_mode = value.image_naming_mode; self.node_opts_image_naming_mode.set(self.image_naming_mode); } if (validate(value.numbering_mode, [ constants.NUMBERING_RENUMBER , constants.NUMBERING_ORIGINAL ])) { self.numbering_mode = value.numbering_mode; self.node_opts_numbering_mode.set(self.numbering_mode); } if (validate(value.zip_info_json_mode, [ constants.JSON_OMIT , constants.JSON_COMPRESSED , constants.JSON_READABLE_2SPACE , constants.JSON_READABLE_4SPACE , constants.JSON_READABLE_TABS ])) { self.zip_info_json_mode = value.zip_info_json_mode; self.node_opts_zip_info_json_mode.set(self.zip_info_json_mode); } if ("zip_info_json_name" in value) { self.zip_info_json_name = filename_normalize(value.zip_info_json_name || ""); if (self.zip_info_json_name.length === 0) self.zip_info_json_name = "info.json"; self.node_zip_info_json_name.value = self.zip_info_json_name; } if ("use_full_images" in value) { self.loader.set_use_full_images(!!value.use_full_images); self.node_full_image_checkbox.checked = self.loader.get_use_full_images(); } if ("allow_fallback_if_using_full_images" in value) { self.loader.set_allow_fallback_if_using_full_images(!!value.allow_fallback_if_using_full_images); self.node_non_full_image_fallback.checked = self.loader.get_allow_fallback_if_using_full_images(); } if ("request_timeout" in value) { self.loader.set_request_timeout(value.request_timeout); self.node_failure_timeout.value = self.loader.get_request_timeout(); } if ("image_max_retry_count" in value) { self.loader.set_image_max_retry_count(value.image_max_retry_count); self.node_failure_retry_max.value = self.loader.get_image_max_retry_count(); } if ("max_filename_length" in value) { self.loader.set_max_filename_length(value.max_filename_length); self.node_opts_filename_length.value = self.loader.get_max_filename_length(); } } }); }; var validate = function (value, okay_values) { return okay_values.indexOf(value) >= 0; }; var fraction_to_percent = function (f, decimals) { return (f * 100).toFixed(decimals) + "%"; }; var filename_normalize = (function () { var filename_normalize = function (name) { name = name.replace(/[\x00-\x1F]+/g, ""); name = name.replace(re_pattern, function (m) { return char_map[m]; }); return name; }; var char_map = { "\"": "\u201d", "<": "\uff1c", ">": "\uff1e", ":": "\uff1a", "|": "\uff5c", "?": "\uff1f", "*": "\uff0a", "/": "\uff0f", "\\": "\uff3c", }; var re_pattern = "[", k; for (k in char_map) { re_pattern += k.replace(/([^\w\s])/g, "\\$1"); } re_pattern += "]"; re_pattern = new RegExp(re_pattern, "g"); return filename_normalize; })(); var filename_get_ext = function (name) { if (typeof(name) === "string") { var m = /(\.[^#\?\\\/\.]*)(?:[#\?]|$)/.exec(name); if (m) return m[1]; } return ""; }; var filename_remove_ext = function (name) { return name.replace(/(\.[^\/\.]*)$/, ""); }; var update_main_link_text = function () { var state = this.loader.get_state(); if (state == GalleryDownloader.NOT_STARTED) { this.node_main_link.text = "Begin download"; } else if (state == GalleryDownloader.COMPLETED) { if (this.zip_blob_urls.length < 2) { this.node_main_link.text = "Click to download file"; } else { this.node_main_link.text = "Click the links below to download the zip archive"; } } else { if (this.loader.is_active()) { this.node_main_link.text = "Pause download"; } else { this.node_main_link.text = "Resume download"; } } }; var update_info_status_text = function () { var state = this.loader.get_state(); if (state == GalleryDownloader.NOT_STARTED) { this.node_info_status.textContent = "Downloading has not started"; } else if (state == GalleryDownloader.REQUESTING_GALLERY_PAGES) { this.node_info_status.textContent = "Step 1 / 2 : Downloading gallery info"; } else if (state == GalleryDownloader.REQUESTING_IMAGES) { this.node_info_status.textContent = "Step 2 / 2 : Downloading images"; } else if (state == GalleryDownloader.COMPLETED) { this.node_info_status.textContent = "Downloading complete"; } }; var update_progress_bars = function () { var state = this.loader.get_state(), gallery_p = 0.0, gallery_t = "none", image_page_p = 0.0, image_page_t = "none", images_p = 0.0, images_t = "none", num, dem; // Progress calculations if (state != GalleryDownloader.NOT_STARTED) { if (state == GalleryDownloader.REQUESTING_GALLERY_PAGES || state == GalleryDownloader.REQUESTING_IMAGES) { // Gallery progress num = this.loader.request_gallery.index; dem = this.loader.get_gallery_page_count(); if (dem > 0) { gallery_p = num / dem; } gallery_t = "" + num; if (dem > 0) { gallery_t += " / " + dem; } else { gallery_t += " / ?"; } gallery_t += " (" + fraction_to_percent(gallery_p, 0) + ")"; } if (state == GalleryDownloader.REQUESTING_IMAGES) { // Image page progress num = this.loader.request_image_page.index; dem = this.loader.get_image_count(); image_page_p = num / dem; image_page_t = "" + num + " / " + dem; image_page_t += " (" + fraction_to_percent(image_page_p, 0) + ")"; // Image progress num = this.loader.request_image.index; images_p = num / dem; images_t = "" + num + " / " + dem; images_t += " (" + fraction_to_percent(images_p, 0) + ")"; } else if (state == GalleryDownloader.COMPLETED) { gallery_p = 1.0; gallery_t = "done"; image_page_p = 1.0; image_page_t = "done"; images_p = 1.0; images_t = "done"; } } // Node updates this.node_progress_gallery.style.width = fraction_to_percent(gallery_p, 2); this.node_progress_gallery_text.nodeValue = gallery_t; this.node_progress_image_pages.style.width = fraction_to_percent(image_page_p, 2); this.node_progress_image_pages_text.nodeValue = image_page_t; this.node_progress_images.style.width = fraction_to_percent(images_p, 2); this.node_progress_images_text.nodeValue = images_t; update_byte_progress_bar.call(this); }; var update_byte_progress_bar = function () { var state = this.loader.get_state(), bytes_p = 0.0, bytes_t = "none", bytes_p2 = 0.0, bytes_t2 = "none", num, den; // Progress calculations if (state != GalleryDownloader.NOT_STARTED) { if (state == GalleryDownloader.REQUESTING_IMAGES) { // Image progress num = this.loader.get_current_image_bytes_loaded(); den = this.loader.get_current_image_bytes_total(); if (den === 0) { bytes_p2 = 0; bytes_t2 = "0%"; } else { bytes_p2 = num / den; bytes_t2 = "" + bytes_to_labeled_string(num) + " / " + bytes_to_labeled_string(den); bytes_t2 += " (" + fraction_to_percent(bytes_p2, 0) + ")"; } // Total image progress num += this.loader.get_image_total_bytes_loaded(); den = Math.max(num, this.loader.get_image_total_bytes()); bytes_p = num / den; bytes_t = "" + bytes_to_labeled_string(num) + " / " + bytes_to_labeled_string(den); bytes_t += " (" + fraction_to_percent(bytes_p, 0) + ")"; } else if (state == GalleryDownloader.COMPLETED) { bytes_p = 1.0; bytes_t = "done"; bytes_p2 = 1.0; bytes_t2 = "done"; } } // Node updates this.node_progress_image_size.style.width = fraction_to_percent(bytes_p, 2); this.node_progress_image_size_text.nodeValue = bytes_t; this.node_progress_image_current.style.width = fraction_to_percent(bytes_p2, 2); this.node_progress_image_current_text.nodeValue = bytes_t2; title_update.call(this, bytes_p); }; var update_timeout_progress_bar = function (percent) { var p = fraction_to_percent(percent, 2); this.node_progress_timeout.style.width = p; this.node_progress_timeout_indicator.style.width = p; if (percent <= 0) { this.node_progress_timeout_indicator.classList.add("eze_dl_progress_bar_indicator_hidden"); } else { this.node_progress_timeout_indicator.classList.remove("eze_dl_progress_bar_indicator_hidden"); } }; var update_image_counters = function () { // vars var type_counts = this.loader.get_image_type_counts(), t1 = type_counts[GalleryDownloader.IMAGE_FULL], t2 = type_counts[GalleryDownloader.IMAGE_UNRESIZED], t3 = type_counts[GalleryDownloader.IMAGE_RESIZED], t4 = type_counts[GalleryDownloader.IMAGE_FALLBACK], s = ""; if (t1 + t2 + t3 + t4 > 0) { if (t1 > 0) { s += t1; s += " full sized image"; if (t1 != 1) s += "s"; } if (t2 > 0) { if (s.length > 0) s += " | "; s += t2; s += " unresized image"; if (t2 != 1) s += "s"; } if (t3 > 0) { if (s.length > 0) s += " | "; s += t3; s += " resized image"; if (t3 != 1) s += "s"; } if (t4 > 0) { if (s.length > 0) s += " | "; s += t4; s += " fallback image"; if (t4 != 1) s += "s"; } } else { s = "No images loaded"; } this.node_info_count.textContent = s; }; var get_ext = function () { if (this.filename_ext_mode == constants.FILE_EXTENSION_ZIP) { return ".zip"; } else { // if (this.filename_ext_mode == constants.FILE_EXTENSION_CBZ) { return ".cbz"; } }; var get_final_filename = function () { // Get name var ext = get_ext.call(this), max_len, base_name; if (this.filename_mode == constants.MAIN_NAME_FULL) { base_name = filename_normalize.call(this, this.loader.gal_info.title); } else { // if (this.filename_mode == constants.MAIN_NAME_SHORT) { base_name = filename_normalize.call(this, this.loader.gal_title_info.title); } max_len = Math.max(0, this.loader.get_max_filename_length() - ext.length); if (base_name.length > max_len) { var ellipsis = ""; base_name = base_name.substr(0, Math.max(max_len - ellipsis.length)) + ellipsis; if (base_name.length > max_len) { base_name = base_name.substr(0, max_len); } } // Apply to link return base_name + ext; }; var update_final_filename = function () { if (this.zip_blob_urls.length > 0) { this.node_main_link.setAttribute("download", get_final_filename.call(this)); } }; var set_image_naming_mode = function (mode, number_mode) { // No change if (this.image_naming_mode == mode && this.numbering_mode == number_mode) return; // Update this.image_naming_mode = mode; this.numbering_mode = number_mode; // No change if (this.gal_info === null) return; // Update var base_name = get_zip_image_filename.call(this, null, null), changes = 0, file_count = Math.min(this.zip.get_file_count(), this.loader.get_image_count()), prev_name, new_name, i; for (i = 0; i < file_count; ++i) { // Update new_name = get_zip_image_filename.call(this, base_name, i); prev_name = this.zip.get_file_name(i); this.zip.set_file_name(i, new_name); if (new_name != prev_name) ++changes; } // Regenerate blob if (this.zip_blob_urls.length > 0) { create_zip.call(this); } }; var revoke_urls = function () { var i; for (i = 0; i < this.zip_blob_urls.length; ++i) { window.URL.revokeObjectURL(this.zip_blob_urls[i]); } this.zip_blob_urls = []; for (i = 0; i < this.zip_blob_script_urls.length; ++i) { window.URL.revokeObjectURL(this.zip_blob_script_urls[i]); } this.zip_blob_script_urls = []; }; var get_zip_image_filename = function (base_name, index) { // Get base name if (base_name === null) { if (this.loader.gal_info === null) { base_name = ""; } else if (this.image_naming_mode == constants.IMAGE_NAMING_FULL_NAME) { base_name = filename_normalize.call(this, this.loader.gal_info.title) + " - "; } else if (this.image_naming_mode == constants.IMAGE_NAMING_SHORT_NAME) { base_name = filename_normalize.call(this, this.loader.gal_title_info.title) + " - "; } else { base_name = ""; } if (index === null) return base_name; } // Settings var image_data = this.loader.images[index], digit_count = Math.max(3, ("" + this.loader.images.length).length), ext = filename_get_ext(image_data.final_url).toLowerCase(), n, number; if (ext.length === 0) { ext = filename_get_ext(image_data.info.image.filename).toLowerCase(); } if (Object.prototype.hasOwnProperty.call(valid_extensions, ext)) { ext = valid_extensions[ext]; } n = (this.numbering_mode == constants.NUMBERING_RENUMBER ? index : image_data.image_id); number = "" + (n + 1); while (number.length < digit_count) number = "0" + number; // New name if (this.image_naming_mode == constants.IMAGE_NAMING_ORIGINAL_NAME) { // Set filename base_name += filename_normalize(filename_remove_ext(image_data.info.image.filename)); } else if (this.image_naming_mode == constants.IMAGE_NAMING_NUMBERED_ORIGINAL) { // Set filename base_name += number; base_name += " - "; base_name += filename_normalize(filename_remove_ext(image_data.info.image.filename)); } else { // Append number base_name += number; } base_name += ext; // Update return base_name; }; var create_zip = function () { // Revoke var buffer, zip_blob; revoke_urls.call(this); this.node_main_links_container.innerHTML = ""; this.node_main_links_container.classList.add("eze_dl_links_hidden"); try { // Create // this.zip_blob = this.zip.to_blob(); // crashes firefox 49 buffer = this.zip.to_buffer(); zip_blob = ZipCreator.arraybuffer_to_blob(buffer); this.zip_blob_urls.push(window.URL.createObjectURL(zip_blob)); // Apply link this.node_main_link.setAttribute("href", this.zip_blob_urls[0]); } catch (e) { create_multi_zip.call(this); } // Main link update_main_link_text.call(this); // Update filename update_final_filename.call(this); }; var create_multi_zip = function () { // Revoke var i, buffers, zip_blob, n, n1, n2, fn, ext, names, filename; revoke_urls.call(this); try { // Create buffers = this.zip.to_buffer(1024 * 1024 * 100); // 100MB limit if (buffers.length > 1) { this.node_main_link.removeAttribute("href"); this.node_main_links_container.classList.remove("eze_dl_links_hidden"); ext = get_ext.call(this); names = []; filename = get_final_filename.call(this); for (i = 0; i < buffers.length; ++i) { zip_blob = ZipCreator.arraybuffer_to_blob(buffers[i]); this.zip_blob_urls.push(window.URL.createObjectURL(zip_blob)); fn = "part" + (i + 1) + ext; n = $("div", "eze_dl_links_inner", [ $("a", "eze_dl_links_single", fn, { href: this.zip_blob_urls[i], download: fn }), ]); names.push(fn); this.node_main_links_container.appendChild(n); } n = $("div", "eze_dl_links_scripts", [ $.text("File too large to download as a single file -- download all the parts and then combine them using one of the following scripts:"), $("div", [ $("strong", null, "Windows script: "), n1 = $("a", null, "download"), $.text(" | "), $("strong", null, "Linux, Mac, etc. script: "), n2 = $("a", null, "download"), ]), $("div", "eze_dl_links_script_name", [ $.text("Filename: "), $("input", "eze_dl_setting_input", { type: "text", value: filename }), ]) ]); this.node_main_links_container.appendChild(n); create_concat_scripts.call(this, names, ext, filename, n1, n2); } else { // Apply link zip_blob = ZipCreator.arraybuffer_to_blob(buffers[0]); this.zip_blob_urls.push(window.URL.createObjectURL(zip_blob)); this.node_main_link.setAttribute("href", this.zip_blob_urls[0]); } } catch (e) { console.log(e); } }; var update_zip_json = function () { // Skip if not done if (!this.loader.is_done()) return; // Remove if (this.zip.get_file_count() > this.loader.images.length) { this.zip.remove_file(this.zip.get_file_count() - 1); } // Skip if (this.zip_info_json_mode == constants.JSON_OMIT) return; // Settings var images = this.loader.images, gal_info = this.loader.gal_info, date = new Date(gal_info.date_uploaded), tab_mode, json_info, img_meta, img, ext, fn, obj, i; if (this.zip_info_json_mode == constants.JSON_READABLE_2SPACE) { tab_mode = 2; } else if (this.zip_info_json_mode == constants.JSON_READABLE_4SPACE) { tab_mode = 4; } else if (this.zip_info_json_mode == constants.JSON_READABLE_TABS) { tab_mode = "\t"; } // Create json_info = { gallery_info: { title: gal_info.title, title_original: gal_info.title_original, category: gal_info.category, tags: gal_info.tags, language: gal_info.language, translated: gal_info.translated, favorite_category: null, upload_date: [ date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), ], source: { site: API.get_site(), gid: gal_info.gallery.gid, token: gal_info.gallery.token, parent_gallery: gal_info.parent, newer_versions: gal_info.newer_versions, }, }, image_api_key: (images.length > 0 && images[0].info) ? images[0].info.navigation.api_key : null, image_info: [], }; // Set info if (gal_info.favorites.category >= 0 && gal_info.favorites.category_title !== null) { json_info.gallery_info.favorite_category = { id: gal_info.favorites.category, title: gal_info.favorites.category_title, }; } // Set images for (i = 0; i < images.length; ++i) { img_meta = images[i]; img = img_meta.info; fn = img.image.filename; ext = filename_get_ext(img_meta.final_url); if (ext.length > 0) { fn = filename_remove_ext(fn) + ext; } obj = { image_key: img.navigation.key_current, direct_id: img.navigation.direct_id, width: images[i].used.data.width, height: images[i].used.data.height, original_filename: fn, }; json_info.image_info.push(obj); } // Add to file this.zip.add_file(ZipCreator.string_to_array(JSON.stringify(json_info, null, tab_mode)), this.zip_info_json_name); // Done if (this.zip_blob_urls.length > 0) { create_zip.call(this); } }; var create_concat_scripts = function (files, ext, filename, win_link, gnu_linux_or_as_ive_recently_taken_to_calling_it_gnu_plus_linux_link) { var s, temp_name, i, blob, url; temp_name = "output_" + (("" + Math.random()).replace(/[^0-9]+/g, "")) + ext; filename = filename.replace(/[^\x20-\x7e]+|\"/g, ""); // Windows s = "copy "; for (i = 0; i < files.length; ++i) { if (i > 0) s += " + "; s += "/b \""; s += files[i]; s += "\""; } s += " \""; s += temp_name; s += "\" /b || goto :eof\r\n"; for (i = 0; i < files.length; ++i) { s += "\r\ndel \""; s += files[i]; s += "\""; } s += "\r\n\r\nren \""; s += temp_name; s += "\" \""; s += filename; s += "\"\r\n"; blob = new Blob([ ZipCreator.string_to_array(s) ], { type: "application/x-bat" }); url = window.URL.createObjectURL(blob); this.zip_blob_script_urls.push(url); win_link.setAttribute("href", url); win_link.setAttribute("download", "join_script.bat"); // Linux s = "#!/bin/sh\ncat"; for (i = 0; i < files.length; ++i) { s += " \""; s += files[i]; s += "\""; } s += " > \""; s += temp_name; s += "\" || exit 1\n"; for (i = 0; i < files.length; ++i) { s += "\nrm \""; s += files[i]; s += "\""; } s += "\n\nmv \""; s += temp_name; s += "\" \""; s += filename; s += "\"\n"; blob = new Blob([ ZipCreator.string_to_array(s) ], { type: "text/x-shellscript" }); url = window.URL.createObjectURL(blob); this.zip_blob_script_urls.push(url); gnu_linux_or_as_ive_recently_taken_to_calling_it_gnu_plus_linux_link.setAttribute("href", url); gnu_linux_or_as_ive_recently_taken_to_calling_it_gnu_plus_linux_link.setAttribute("download", "join_script.sh"); }; var title_init = function () { this.title_started = true; title_update.call(this, 0); }; var title_update = function (progress) { if (this.node_title === null || !this.title_started) return; var p = Math.floor(progress * 100), title = "[" + p.toFixed(0) + "%] " + this.title_default; this.node_title.textContent = title; }; var title_complete = function () { if (this.node_title === null) return; // Flash the title var flash_on = false, flash_count = 2, flash_id = flash_on ? -1 : 0, self = this, interval; interval = setInterval(function () { if (flash_on) { title_update.call(self, 1.0); if (++flash_id >= flash_count) { clearInterval(interval); return; } } else { self.node_title.textContent = "\xa0"; } flash_on = !flash_on; }, 500); }; var on_option_filename_change = function (value) { // Set this.filename_mode = value; update_final_filename.call(this); // Save save_values.call(this); }; var on_option_filename_ext_change = function (value) { // Set this.filename_ext_mode = value; update_final_filename.call(this); // Save save_values.call(this); }; var on_option_image_name_change = function (value) { // Update image naming mode set_image_naming_mode.call(this, value, this.numbering_mode); // Save save_values.call(this); }; var on_option_zip_info_json_mode_change = function (value) { // Update image naming mode this.zip_info_json_mode = value; // Set update_zip_json.call(this); // Save save_values.call(this); }; var on_option_max_filename_length_change = function () { var node = this.node_opts_filename_length, value, m; if ( (m = /^\s*([\d.]+)/.exec(node.value)) && !isNaN((value = parseFloat(m[1]))) ) { this.loader.set_max_filename_length(value); } // Update display value node.value = this.loader.get_max_filename_length(); update_final_filename.call(this); // Save save_values.call(this); }; var on_option_full_images_change = function () { var node = this.node_full_image_checkbox, value = node.checked; // Set this.loader.set_use_full_images(value); // Update node.checked = this.loader.get_use_full_images(); // Save save_values.call(this); }; var on_option_non_full_image_fallback_change = function () { var node = this.node_non_full_image_fallback, value = node.checked; // Set this.loader.set_allow_fallback_if_using_full_images(value); // Update node.checked = this.loader.get_allow_fallback_if_using_full_images(); // Save save_values.call(this); }; var on_option_failure_timeout_change = function () { var node = this.node_failure_timeout, value, m; if ( (m = /^\s*([\d.]+)/.exec(node.value)) && !isNaN((value = parseFloat(m[1]))) ) { this.loader.set_request_timeout(value); } // Update display value node.value = this.loader.get_request_timeout(); // Save save_values.call(this); }; var on_option_failure_retry_max_change = function () { var node = this.node_failure_retry_max, value, m; if ( (m = /^\s*(\d+)/.exec(node.value)) && !isNaN((value = parseInt(m[1], 10))) ) { this.loader.set_image_max_retry_count(value); } // Update display value node.value = this.loader.get_image_max_retry_count(); // Save save_values.call(this); }; var on_option_zip_info_json_name_change = function () { var node = this.node_zip_info_json_name, value = filename_normalize(node.value); if (value.length === 0) value = "info.json"; this.zip_info_json_name = value; update_zip_json.call(this); // Update display value node.value = value; // Save save_values.call(this); }; var on_option_numbering_mode_change = function (value) { // Update image naming mode set_image_naming_mode.call(this, this.image_naming_mode, value); // Save save_values.call(this); }; var on_option_file_ranges_change = function () { // Update var node = this.node_file_ranges; this.loader.set_image_ranges(node.value); node.value = this.loader.get_image_ranges(); }; var on_main_link_click = function (event) { // Remove page close warning if (this.loader.is_done() && this.page_close_warning_cb !== null) { window.removeEventListener("beforeunload", this.page_close_warning_cb, false); this.page_close_warning_cb = null; } // Skip if (event.which != 1) return; // Pause/resume if (this.loader.is_done()) { // Allow the link to be clicked return; } if (this.loader.is_active()) { this.loader.pause(); } else { this.loader.resume(); } // Stop event.preventDefault(); event.stopPropagation(); return false; }; var on_loader_image_progress = function () { // Update progress update_byte_progress_bar.call(this); }; var on_loader_error = function (event) { // Set text this.node_info_error.textContent = event.error; this.node_info_error.classList.add("eze_dl_info_visible"); }; var on_loader_active_change = function (event) { // Main link update_main_link_text.call(this); // Hide error if (event.active) { this.node_info_error.classList.remove("eze_dl_info_visible"); } else { // Progress bars update_progress_bars.call(this); update_timeout_progress_bar.call(this, 0, 0); } }; var on_loader_state_change = function () { // Update stuff update_main_link_text.call(this); update_info_status_text.call(this); update_progress_bars.call(this); if (this.loader.get_state() == GalleryDownloader.REQUESTING_IMAGES) { update_image_counters.call(this); this.node_info_count.classList.add("eze_dl_info_visible"); } if (this.loader.is_done()) { // Add json update_zip_json.call(this); // Create zip url create_zip.call(this); // Title title_complete.call(this); } else if (this.loader.get_state() !== GalleryDownloader.NOT_STARTED) { // Setup page close warning if (this.page_close_warning_cb === null) { this.page_close_warning_cb = function (event) { var msg = "A download is active, or the download link has not been clicked yet.\n\nExit page?"; event.returnValue = msg; return msg; }; window.addEventListener("beforeunload", this.page_close_warning_cb, false); } if (!this.title_started) { title_init.call(this); } } }; var on_loader_gallery_page_get = function () { // Update update_progress_bars.call(this); // Hide error this.node_info_error.classList.remove("eze_dl_info_visible"); }; var on_loader_image_page_get = function () { // Update update_progress_bars.call(this); }; var on_loader_image_get = function (event) { // Add image to zip this.zip.add_file(this.loader.images[event.index].byte_data, get_zip_image_filename.call(this, null, event.index)); // Update update_progress_bars.call(this); update_image_counters.call(this); update_timeout_progress_bar.call(this, 0, 0); // Hide error this.node_info_error.classList.remove("eze_dl_info_visible"); }; var on_loader_image_range_update = function () { // Update node this.node_file_ranges.value = this.loader.get_image_ranges(); }; var on_loader_image_timeout_progress = function (event) { update_timeout_progress_bar.call(this, event.percent, event.time); }; return GalleryDownloadManager; })(); // Gallery thumnail loader var ThumbnailLoader = (function () { var ThumbnailLoader = function (gid, token, page_info, checkbox, status_node) { // Vars this.pages = []; this.gallery = { gid: gid, token: token, }; this.count = page_info.count; this.start = Math.max(0, Math.min(this.count - 1, page_info.current)); this.completed = 1; this.index = this.start; this.checkbox = checkbox; this.status_node = status_node; this.delay = 1.0; this.request = null; this.timeout = null; this.container = $("div", "eze_gallery_page_container"); // Setup pages for (var i = 0; i < this.count; ++i) { this.pages.push(null); } // Start node var n = document.querySelector("#gdt"); if (n !== null) { // Set page this.pages[this.start] = stylize_page.call(this, n, this.start); // Container n.parentNode.insertBefore(this.container, n); this.container.appendChild(n); } // Events this.checkbox.addEventListener("change", bind(on_checkbox_change, this, this.checkbox), false); // Update update_checkbox.call(this); update_status.call(this); }; var begin_request = function () { var increase = (this.index >= this.start); if (increase) { while (true) { if (++this.index >= this.count) { this.index = this.start; increase = false; break; } if (this.pages[this.index] === null) break; } } if (!increase) { while (true) { // Done if (--this.index < 0) { this.index = 0; return; } if (this.pages[this.index] === null) break; } } this.request = API.request_document( "/g/" + this.gallery.gid + "/" + this.gallery.token + "?p=" + this.index, bind(on_response_load, this), bind(on_response_error, this) ); }; var on_response_load = function (response, status) { // Clear this.request = null; if (status == 200) { // Okay if (this.add_page_from_html(response, this.index)) { // Next var self = this; this.timeout = setTimeout(function () { begin_request.call(self); }, this.delay * 1000); return; } } // Stop update_checkbox.call(this); }; var on_response_error = function () { // Stop this.request = null; update_checkbox.call(this); }; var stylize_page = function (node, index) { // Style node.removeAttribute("id"); node.classList.add("eze_gallery_page"); node.setAttribute("data-eze-page-id", index); // Indicator $("a", "eze_gallery_page_indicator", { href: "/g/" + this.gallery.gid + "/" + this.gallery.token + (index <= 0 ? "" : "?p=" + index), target: "_blank", }, [ $("span", "eze_gallery_page_indicator_border_top"), $("span", "eze_gallery_page_indicator_border"), $("span", "eze_gallery_page_indicator_text", "Page " + (index + 1)), ], $.P, node); // Link styling if (settings.get("multiviewer_by_default")) { var nodes = node.querySelectorAll(".gdtm>div>a"), i, url; if (nodes.length === 0) { nodes = node.querySelectorAll(".gdtl>a"); } for (i = 0; i < nodes.length; ++i) { url = (nodes[i].getAttribute("href") || "").replace(/#.*$/, "") + Hash.sep + "eze/view"; nodes[i].setAttribute("href", url); } } // Done return node; }; var update_checkbox = function () { this.checkbox.checked = (this.completed >= this.count || this.request !== null || this.timeout !== null); }; var update_status = function () { this.status_node.textContent = "(" + this.completed + "/" + this.count + ")"; }; var on_checkbox_change = function (node) { if (node.checked) { this.resume(); } else { this.pause(); } update_checkbox.call(this); }; ThumbnailLoader.prototype = { constructor: ThumbnailLoader, resume: function () { // Done if (this.completed >= this.count || this.request !== null || this.timeout !== null) return; // Start begin_request.call(this); }, pause: function () { // Done if (this.completed >= this.count) return; // Stop if (this.request !== null) { this.request.abort(); this.request = null; } if (this.timeout !== null) { clearTimeout(this.timeout); this.timeout = null; } }, add_page_from_html: function (html, page_index) { if (page_index < 0 || page_index >= this.pages.length || this.pages[page_index] !== null) return false; // Find node var n = html.querySelector("#gdt"), rel = null, i; if (n !== null) { // Set this.pages[page_index] = stylize_page.call(this, n, page_index); // Find relative; for (i = page_index + 1; i < this.count; ++i) { if (this.pages[i] !== null) { rel = this.pages[i]; break; } } // Add if (rel === null || rel.parentNode !== this.container) { this.container.appendChild(this.pages[page_index]); } else { this.container.insertBefore(this.pages[page_index], rel); } // Done? if (++this.completed >= this.count) { update_checkbox.call(this); } update_status.call(this); // Okay return true; } // Error return false; }, }; return ThumbnailLoader; })(); // Script settings var Settings = (function () { var Settings = function () { // Values and descriptors this.values = { custom_search_front_page: false, custom_search_links: true, custom_search_form: true, custom_search_params: {}, multiviewer_by_default: false, show_deprecation_notice: true, }; this.validators = { custom_search_front_page: is_boolean, custom_search_links: is_boolean, custom_search_form: is_boolean, custom_search_params: is_object_not_null, multiviewer_by_default: is_boolean, show_deprecation_notice: is_boolean, }; this.uconfig = { categories: 0, }; // Events this.change_listeners = {}; // Load this.save_prefix = "eze_"; load_all.call(this); load_uconfig.call(this); // Sync var self = this; this.sync = true; this.sync_key = "eze_sync"; window.addEventListener("storage", function (event) { if (self.sync && event.key == self.sync_key && event.newValue !== null) { if (event.newValue in self.values) { load_value.call(self, event.newValue, "sync"); } } }, false); }; Settings.uconfig_categories = { misc: [ 1 , "f_misc" ], doujinshi: [ 2 , "f_doujinshi" ], manga: [ 4 , "f_manga" ], artistcg: [ 8 , "f_artistcg" ], gamecg: [ 16 , "f_gamecg" ], imageset: [ 32 , "f_imageset" ], cosplay: [ 64 , "f_cosplay" ], asianporn: [ 128 , "f_asianporn" ], "non-h": [ 256 , "f_non-h" ], western: [ 512 , "f_western" ], }; var is_boolean = function (obj) { return typeof(obj) == "boolean"; }; var is_object_not_null = function (obj) { return typeof(obj) == "object" && obj !== null; }; var string_to_int = function (str) { return parseInt(str, 10) || 0; }; var load_all = function () { for (var k in this.values) { load_value.call(this, k, null); } }; var load_value = function (name, reason) { var self = this; Save.get(this.save_prefix + name, function (v) { if (v !== undefined && self.validators[name].call(null, v) === true) { self.values[name] = v; if (reason !== null) trigger_change.call(self, name, reason); } }); }; var trigger_change = function (name, reason) { if (name in this.change_listeners) { var list = this.change_listeners[name], i = 0; for (; i < list.length; ++i) { list[i].call(null, settings, name, reason); } } }; var uconfig_remap = { cats: [ "categories", string_to_int ], }; var load_uconfig = function () { var cookie = Cookies.get_all(document.cookie), vars, i, s, k; if (!("uconfig" in cookie)) return; // Parse cookie = cookie.uconfig.split("-"); vars = {}; for (i = 0; i < cookie.length; ++i) { s = cookie[i].split("_"); k = s[0]; s.splice(0, 1); vars[k] = s.join("_"); } // Set for (k in vars) { if (k in uconfig_remap) { this.uconfig[uconfig_remap[k][0]] = uconfig_remap[k][1].call(null, vars[k]); } // else { this.uconfig[k] = vars[k]; } } }; Settings.prototype = { constructor: Settings, get: function (name) { // Get return this.values[name]; }, set: function (name, value, callback) { // Test if (!(name in this.values) || !this.validators[name].call(null, value)) { if (callback) callback.call(null, false); return false; } // Memory this.values[name] = value; // Storage Save.set(this.save_prefix + name, value, callback); // Sync if (this.sync) { window.localStorage.setItem(this.sync_key, name); window.localStorage.removeItem(this.sync_key); } // Event trigger_change.call(this, name, "set"); // Done return true; }, on_change: function (name, callback) { if (name in this.change_listeners) { this.change_listeners[name].push(callback); return true; } else if (name in this.values) { this.change_listeners[name] = [ callback ]; return true; } return false; }, off_change: function (name, callback) { if (!(name in this.change_listeners)) return false; var list = this.change_listeners[name], i = 0; for (; i < list.length; ++i) { if (list[i] === callback) { if (list.length <= 1) { delete this.change_listeners[name]; } else { list.splice(i, 1); } return true; } } return false; }, set_sync_enabled: function (enabled) { this.sync = enabled; }, }; return Settings; })(); // Main logic functions var insert_stylesheet = function () { // Styling var vars = {}, is_ex = (API.get_site() == "exhentai"), css; // Setup vars if (is_ex) { vars.gallery_item_hl = CSS.color("#080808"); vars.bg = CSS.color("#43464E"); vars.bg_dark = CSS.color("#34353B"); vars.bg_light = CSS.color("#4f535b"); vars.border = CSS.color("#000000"); vars.border_light = CSS.color("#989898"); vars.border_radius = "0"; vars.text = CSS.color("#F1F1F1"); vars.text_light = CSS.color("#B8B8B8"); vars.text_shadow = CSS.color("#080808"); vars.dl_bar_bg1 = CSS.color("#c00000"); vars.dl_bar_bg2 = CSS.color("#a07000"); vars.dl_bar_bg3 = CSS.color("#00a020"); vars.dl_bar_bg4 = CSS.color("#0060ff"); vars.dl_bar_bg5 = CSS.color("#8020ff"); vars.dl_bar_bg5_light = CSS.color("#5f1080"); vars.multiviewer_page_bg = CSS.color("#f0f0f0"); vars.multiviewer_page_hl = CSS.color("rgba(255,255,255,0.125)"); vars.multiviewer_page_selected = CSS.color("#0060ff"); vars.multiviewer_page_selected_hl = CSS.color("#0060ff"); } else { vars.gallery_item_hl = CSS.color("#f8f8f8"); vars.bg = CSS.color("#E3E0D1"); vars.bg_dark = CSS.color("#E3E0D1"); vars.bg_light = CSS.color("#EDEBDF"); vars.border = CSS.color("#5C0D12"); vars.border_light = CSS.color("#806769"); vars.border_radius = "9px"; vars.text = CSS.color("#5C0D11"); vars.text_light = CSS.color("#9F8687"); vars.text_shadow = CSS.color("#f8f8f8"); vars.dl_bar_bg1 = CSS.color("#ffaaaa"); vars.dl_bar_bg2 = CSS.color("#ffc0aa"); vars.dl_bar_bg3 = CSS.color("#88ffaa"); vars.dl_bar_bg4 = CSS.color("#88ccff"); vars.dl_bar_bg5 = CSS.color("#d088ff"); vars.dl_bar_bg5_light = CSS.color("#ffc0ff"); vars.multiviewer_page_bg = CSS.color("#f0f0f0"); vars.multiviewer_page_hl = CSS.color("rgba(255,255,255,0.125)"); vars.multiviewer_page_selected = CSS.color("#88ccff"); vars.multiviewer_page_selected_hl = CSS.color("#88ccff"); } // Create css css = CSS.format( ".id1:not(.e-Highlighted):not(.e-Filtered)>.id2{overflow:visible;position:relative;}.id1:not(.e-Highlighted):not(.e-Filtered):hover>.id2{z-index:1;}.id1:not(.e-Highlighted):not(.e-Filtered):hover>.id2>a{text-shadow:0px 0px 1px {{color:gallery_item_hl}},0px 0px 1px {{color:gallery_item_hl}},0px 0px 1px {{color:gallery_item_hl}},0px 0px 1px {{color:gallery_item_hl}};background:{{color:bg,0.75}};display:inline-block;padding-bottom:0.5em;}:root.eze_is_ex #nb{max-width:780px;}.eze_gallery_page_container{}.eze_gallery_page{background:{{color:bg_light}};border:1px solid {{color:border}};text-align:left;width:99%;min-width:950px;max-width:1200px;margin:0 auto;clear:both;padding:5px;border-radius:{{border_radius}};position:relative;border-top-left-radius:0;}.eze_gallery_page img{border:1px solid {{color:border}};margin:0;padding:0;}.eze_gallery_page a{text-decoration:none;}.eze_gallery_page+.eze_gallery_page{margin-top:0.5em;}.eze_gallery_page_indicator{display:inline-block;position:absolute;right:100%;top:0;background-color:{{color:bg_dark}};}.eze_gallery_page_indicator:hover{background-color:{{color:bg_light}};}.eze_gallery_page_indicator_border_top{position:absolute;bottom:100%;left:0;right:0;border-bottom:1px solid {{color:border}};}.eze_gallery_page_indicator_border{position:absolute;left:0;top:0;right:0;bottom:0;border-style:solid;border-width:0px 0px 1px 1px;border-color:{{color:border}};}.eze_gallery_page_indicator:not(:hover)>.ez_gallery_page_indicator_border{border-right:1px solid {{color:border}};}.eze_gallery_page_indicator_text{position:relative;display:inline-block;white-space:nowrap;padding:4px;}.eze_favorite_link{cursor:pointer;}div.eze_gallery_custom_container{font-size:1.25em;margin:0 auto 0.5em;padding:0.5em;}.eze_gallery_custom_container_inner{}.eze_gallery_custom{margin:0 auto;}.eze_gallery_link{text-decoration:none;font-weight:bold;cursor:pointer;font-weight:bold;}.eze_gallery_link>*{vertical-align:middle;}.eze_gallery_custom_table{display:table;table-layout:fixed;width:100%;}.eze_gallery_custom_row{display:table-row;}.eze_gallery_custom_cell{display:table-cell;text-align:left;}.eze_gallery_custom_cell+.eze_gallery_custom_cell{border-left:1px solid {{color:border}};padding-left:0.5em;}.eze_dl_container{margin-top:0.5em;padding-top:0.5em;border-top:1px solid {{color:border}};}.eze_dl_container:not(.eze_dl_container_visible){display:none;}.eze_dl_title{font-size:2em;font-weight:bold;margin-bottom:0.125em;}.eze_dl_title.eze_dl_title_pad_above{margin-top:0.125em;}.eze_dl_link{cursor:pointer;text-decoration:none;}.eze_dl_links{display:block;}.eze_dl_links:not(.eze_dl_links_hidden){margin-bottom:0.25em;}.eze_dl_links_scripts{margin-top:0.5em;}.eze_dl_links_script_name{margin-top:0.25em;}.eze_dl_info_container{margin-left:-0.5em;}.eze_dl_info{display:inline-block;margin-left:0.5em;border:1px solid {{color:border}};background-color:{{color:bg}};border-bottom:none;padding:0.25em;}.eze_dl_info.eze_dl_info_error{color:#f00000;text-shadow:1px 1px 0 {{color:border}};}.eze_dl_info:not(.eze_dl_info_visible){display:none;}.eze_dl_progress_bar{position:relative;width:100%;box-sizing:border-box;-moz-box-sizing:border-box;border:1px solid {{color:border}};background-color:{{color:bg_dark}};}.eze_dl_progress_bar+.eze_dl_progress_bar{border-top:none;}.eze_dl_progress_bar_bg,.eze_dl_progress_bar_bg_light{position:absolute;left:0;top:0;bottom:0;background-color:{{color:dl_bar_bg1}};}.eze_dl_progress_bar_indicator{position:absolute;left:0;top:0;bottom:0;border-right:1px solid {{color:border}};}.eze_dl_progress_bar_indicator.eze_dl_progress_bar_indicator_hidden{display:none;}.eze_dl_progress_bar.eze_dl_progress_bar_image_pages>.eze_dl_progress_bar_bg{background-color:{{color:dl_bar_bg2}};}.eze_dl_progress_bar.eze_dl_progress_bar_images>.eze_dl_progress_bar_bg{background-color:{{color:dl_bar_bg3}};}.eze_dl_progress_bar.eze_dl_progress_bar_image_size>.eze_dl_progress_bar_bg{background-color:{{color:dl_bar_bg4}};}.eze_dl_progress_bar.eze_dl_progress_bar_timeout>.eze_dl_progress_bar_bg{background-color:{{color:dl_bar_bg5}};}.eze_dl_progress_bar.eze_dl_progress_bar_timeout>.eze_dl_progress_bar_bg_light{background-color:{{color:dl_bar_bg5_light}};}.eze_dl_progress_bar_text{position:relative;height:1.25em;padding:0.25em;line-height:1.25em;color:{{color:text}};text-shadow:1px 1px 0 {{color:text_shadow}};}.eze_dl_setting{display:table;width:100%;padding:0.5em;box-sizing:border-box;-moz-box-sizing:border-box;}.eze_dl_setting:nth-of-type(2n){background-color:{{color:bg}};}.eze_dl_setting_row{display:table-row;}.eze_dl_setting_cell{display:table-cell;width:100%;vertical-align:top;}.eze_dl_setting_cell:first-of-type{width:0;white-space:nowrap;}.eze_dl_setting_cell:not(:first-of-type){text-align:right;}.eze_dl_setting_cell:not(:first-of-type)>div+div{margin-top:0.125em;}.eze_dl_setting_title{font-size:1.25em;font-weight:bold;}.eze_dl_setting_desc{}.eze_dl_setting_desc_tag{background-color:{{color:bg}};padding:0.125em 0.375em;display:inline-block;border-radius:0.5em;margin-left:0.5em;}.eze_dl_setting:nth-of-type(2n) .eze_dl_setting_desc_tag{background-color:{{color:bg_light}};}.eze_dl_setting_input{border:1px solid {{color:border}};background-color:{{color:bg_dark}};color:{{color:text}};padding:0.125em;line-height:1.25em;width:10em;font-family:inherit;}.eze_dl_setting_input.eze_dl_setting_input_small{width:4em;}.eze_option_box{display:inline-block;border:1px solid {{color:border}};background-color:{{color:bg_dark}};line-height:1.5em;padding:0 0.25em;height:1.5em;overflow:hidden;text-align:center;cursor:pointer;}.eze_option_box+.eze_option_box{margin-left:0.5em;}.eze_option_box_entry{display:block;height:1.5em;}.eze_option_box_entry:not(.eze_option_box_entry_selected){height:0;overflow:hidden;visibility:hidden;}.eze_dl_label>*{vertical-align:middle;}.eze_dgallery_table{display:table;width:100%;}.eze_dgallery_table.eze_dgallery_table_spaced{margin:1em 0;}.eze_dgallery_row{display:table-row;}.eze_dgallery_cell{display:table-cell;width:0;vertical-align:top;}.eze_dgallery_cell.eze_dgallery_cell_full{width:100%;}.eze_dgallery_cell.eze_dgallery_cell_nowhite{white-space:nowrap;}.eze_dgallery_cell.eze_dgallery_cell_pre_border{padding-right:0.5em;}.eze_dgallery_cell.eze_dgallery_cell_border{border-left:1px solid {{color:border}};padding-left:0.5em;}.eze_dgallery_image{border:1px solid {{color:border}};min-width:200px;height:auto;margin-right:1em;}.eze_dgallery_title{font-size:1.5em;margin:0.25em 0;}.eze_dgallery_title_alt{font-size:1.25em;margin:0.25em 0;color:{{color:text_light}};}.eze_dgallery_tag_container{margin:-0.5em 0 0 -1em;}.eze_dgallery_tag{display:inline-block;margin:0.5em 0 0 1em;padding:0.25em;font-weight:bold;white-space:nowrap;border-radius:0.5em;border:1px solid #989898;background:{{color:bg_light}};cursor:pointer;text-decoration:none;}.eze_dgallery_info_label{font-weight:bold;}.eze_dgallery_extra_container{border-top:1px solid {{color:border}};padding-top:0.5em;}.eze_main_container{position:absolute;left:0;top:0;bottom:0;right:0;white-space:nowrap;line-height:0;text-align:center;}.eze_main_container:before{content:\"\";display:inline-block;width:0;height:100%;vertical-align:middle;}.eze_main{display:inline-block;width:950px;vertical-align:top;white-space:normal;line-height:normal;text-align:left;margin:2em 0;}.eze_main.eze_main_middle{vertical-align:middle;}.eze_main_box{border:1px solid {{color:border}};background-color:{{color:bg}};padding:0.5em 1em;box-sizing:border-box;-moz-box-sizing:border-box;}.eze_menu{display:block;position:absolute;left:0;top:0;background-color:{{color:bg}};text-align:left;white-space:nowrap;border:1px solid {{color:border}};z-index:100;}.eze_menu_option,.eze_menu_label{display:block;padding:0.25em;text-decoration:none;}.eze_menu_option{cursor:pointer;}.eze_menu_label{cursor:default;}.eze_menu_option:hover{background-color:{{color:bg_dark}};}.eze_settings_container:not(.eze_settings_container_visible){display:none;}.eze_settings_container.eze_settings_container_visible~.stuffbox{display:none;}.eze_settings_box{font-size:1.2em;text-align:center;margin:5px auto;width:700px;padding:5px;text-align:left;border:1px solid {{color:border}};background-color:{{color:bg_light}};}.eze_settings_header{margin:0 0 0.25em;padding:0;font-size:2em;}.eze_settings_entry_container{}.eze_settings_entry{padding:0.25em;}.eze_settings_entry:nth-of-type(2n){background-color:{{color:bg}};}.eze_settings_label>*{vertical-align:middle;}.eze_settings_pad_left{margin-left:0.5em;}.eze_cv_container{position:absolute;left:0;top:0;bottom:0;right:0;overflow:hidden;}.eze_cv_offset_size{position:absolute;left:50%;top:50%;width:0;height:0;}.eze_cv_offset{width:100%;height:100%;transform:translate(0%,0%);}.eze_cv_content_container{display:inline-block;transform:translate(-50%,-50%);}.eze_cv_content{position:relative;}.eze_cv_container.eze_cv_container_transition_scale>.eze_cv_offset_size{transition:width 0.25s ease-in-out 0s,height 0.25s ease-in-out 0s;}.eze_cv_container.eze_cv_container_transition_scale>*>*>*>.eze_cv_content{transition:transform 0.25s ease-in-out 0s;}.eze_cv_container.eze_cv_container_transition_position>*>.eze_cv_offset{transition:transform 0.25s ease-in-out 0s;}body.eze_multiviewer_visible{overflow:hidden;}.eze_multiviewer{position:absolute;left:0;top:0;bottom:0;right:0;font-size:16px;background-color:{{color:bg_dark}};}.eze_multiviewer:not(.eze_multiviewer_visible){display:none;}.eze_multiviewer.eze_multiviewer_visible~*{display:none;}.eze_multiviewer_sidebar{position:absolute;right:0;top:0;bottom:0;background-color:{{color:bg_light}};border-left:1px solid {{color:border}};overflow-x:hidden;overflow-y:scroll;}.eze_multiviewer_sidebar:not(.eze_multiviewer_sidebar_visible){display:none;}.eze_multiviewer_sidebar.eze_multiviewer_sidebar_left{right:auto;left:0;border-left:none;border-right:1px solid {{color:border}};}.eze_multiviewer_sidebar_size{}.eze_multiviewer_content{position:fixed;left:0;top:0;bottom:0;right:0;}.eze_multiviewer_content_size{position:absolute;left:0;top:0;bottom:0;right:0;}.eze_multiviewer_topbar{position:fixed;left:0;top:0;right:0;line-height:1.25em;background-color:{{color:bg_light}};border-bottom:1px solid {{color:border}};}.eze_multiviewer_topbar:not(.eze_multiviewer_topbar_visible){display:none;}.eze_multiviewer_scrollbars{position:absolute;left:0;top:0;right:0;bottom:0;overflow-x:hidden;overflow-y:hidden;}.eze_multiviewer_scrollbars.eze_multiviewer_scrollbars_visible_h{position:absolute;right:0;top:0;bottom:0;right:0;overflow-x:scroll;}.eze_multiviewer_scrollbars.eze_multiviewer_scrollbars_visible_v{position:absolute;right:0;top:0;bottom:0;right:0;overflow-y:scroll;}.eze_multiviewer_scrollbars:not(.eze_multiviewer_scrollbars_visible){display:none;}.eze_multiviewer_scrollbars_range{width:100%;height:100%;}.eze_multiviewer_scrollbars_size{width:100%;height:100%;}.eze_multiviewer_image_container{padding:0.5em;}.eze_multiviewer_image_size{position:relative;background-color:{{color:multiviewer_page_bg}};}.eze_multiviewer_image_container.eze_multiviewer_image_container_no_bg>.eze_multiviewer_image_size{background-color:transparent;}.eze_multiviewer_image{vertical-align:middle;-position:absolute;left:0;top:0;width:100%;height:100%;}.eze_multiviewer_thumb_container{}.eze_multiviewer_thumb_sep{display:block;cursor:default;border-top:0.125em solid {{color:text_light}};border-bottom:0.125em solid {{color:text_light}};height:0.125em;margin:0.5em 1em;}.eze_multiviewer_thumb_sep:not(.eze_multiviewer_thumb_sep_visible){display:none;}.eze_multiviewer_thumb{text-align:center;padding:0.5em 1em;cursor:pointer;transition:background-color 0.25s ease-in-out 0s;display:block;text-decoration:none;}.eze_multiviewer_thumb:not(.eze_multiviewer_thumb_visible){display:none;}.eze_multiviewer_thumb:hover{background-color:{{color:multiviewer_page_hl}};}.eze_multiviewer_thumb.eze_multiviewer_thumb_viewing{background-color:{{color:multiviewer_page_selected}};}.eze_multiviewer_thumb.eze_multiviewer_thumb_viewing:hover{background-color:{{color:multiviewer_page_selected_hl}};}.eze_multiviewer_thumb_sizer1{display:inline-block;vertical-align:middle;width:100%;}.eze_multiviewer_thumb_sizer2{position:relative;width:100%;}.eze_multiviewer_thumb_sizer3{position:relative;width:100%;height:100%;white-space:nowrap;line-height:0;}.eze_multiviewer_thumb_aligner{display:inline-block;vertical-align:middle;width:0;height:0;padding-top:50%;}.eze_multiviewer_thumb_size{display:inline-block;vertical-align:middle;white-space:normal;line-height:normal;width:100%;padding-top:100%;position:relative;background-color:{{color:multiviewer_page_bg}};overflow:hidden;}.eze_multiviewer_thumb_image{position:absolute;left:0;top:0;bottom:0;right:0;background-color:transparent;background-repeat:no-repeat;background-position:center center;background-size:auto;transform-origin:0 0;opacity:0.25;transition:opacity 0.25s ease-in-out 0s;}.eze_multiviewer_thumb[data-eze-state='0']>*>*>*>*>.eze_multiviewer_thumb_image{opacity:0.25;}.eze_multiviewer_thumb[data-eze-state='1']>*>*>*>*>.eze_multiviewer_thumb_image{opacity:0.25;}.eze_multiviewer_thumb[data-eze-state='2']>*>*>*>*>.eze_multiviewer_thumb_image{opacity:0.25;}.eze_multiviewer_thumb[data-eze-state='3']>*>*>*>*>.eze_multiviewer_thumb_image{opacity:0.5;}.eze_multiviewer_thumb[data-eze-state='4']>*>*>*>*>.eze_multiviewer_thumb_image{opacity:1;}.eze_multiviewer_thumb[data-eze-state='5']>*>*>*>*>.eze_multiviewer_thumb_image{opacity:0.5;}.eze_multiviewer_thumb.eze_multiviewer_thumb_viewing[data-eze-state]>*>*>*>*>.eze_multiviewer_thumb_image{opacity:1;}.eze_multiviewer_thumb[data-eze-state]:hover>*>*>*>*>.eze_multiviewer_thumb_image{opacity:1;}", vars ); // Insert document.documentElement.classList.toggle("eze_is_ex", is_ex); API.inject_style(css); }; var bytes_to_labeled_string = (function () { var suffixes = [ "B" , "KiB" , "MiB" , "GiB" ], division_levels = [ 1024.0 , 1024.0 , 1024.0 ]; return function (bytes) { var i = 0; while (i + 1 < suffixes.length && bytes / division_levels[i] >= 1) { bytes /= division_levels[i]; ++i; } return bytes.toFixed(2) + " " + suffixes[i]; }; })(); var create_custom_search_links = (function () { var links = [ { title: "Search by gallery name", primary: 0, links: [ { title: "Search by name", url: "/?f_doujinshi=1&f_manga=1&f_artistcg=1&f_gamecg=1&f_western=1&f_non-h=1&f_imageset=1&f_cosplay=1&f_asianporn=1&f_misc=1&f_search=\"{short}\"", }, { title: "Search by exact name", url: "/?f_doujinshi=1&f_manga=1&f_artistcg=1&f_gamecg=1&f_western=1&f_non-h=1&f_imageset=1&f_cosplay=1&f_asianporn=1&f_misc=1&f_search=\"{full}\"", }, ], }, { title: "Search on external websites", primary: 0, links: [ { title: "nhentai.net - by name", url: "//nhentai.net/search/?q=\"{short}\"", }, { title: "nhentai.net - by exact name", url: "//nhentai.net/search/?q=\"{full}\"", }, { title: "hitomi.la - by name", url: "https://hitomi.la/search.html?{short}", }, ], }, ]; var create_custom_search_links = function (gal_info) { var title_info = API.get_gallery_title_info(gal_info.title), nodes = [], replacers = {}, re_replacer = /\{(\w+)\}/g, replacer_fn, node_links, i, j, n, li, url; // Replacers replacers.full = encodeURIComponent(gal_info.title.replace(/,/g, " ")); replacers.short = encodeURIComponent(title_info.title.replace(/,/g, " ")); replacer_fn = function (full, key) { return (key in replacers) ? replacers[key] : full; }; // Create for (i = 0; i < links.length; ++i) { li = links[i]; // Setup links node_links = []; for (j = 0; j < li.links.length; ++j) { url = li.links[j].url.replace(re_replacer, replacer_fn); if (settings.get("custom_search_links")) { url = modify_search_url(url); } node_links.push(li.links[j].title); node_links.push(url); } // Create node n = $("a", "eze_gallery_link", li.title || "", { href: node_links[li.primary * 2 + 1], target: "_blank" }); if (li.links.length > 1) { n.addEventListener("click", bind(on_search_link_click, n, node_links), false); } nodes.push(n); } // Done return nodes; }; var on_search_link_click = function (node_links, event) { // Skip if (event.which != 1) return; // Create menu var menu = new Menu(), opt, i; for (i = 0; i < node_links.length; i += 2) { opt = menu.add_option(node_links[i]); opt.setAttribute("href", node_links[i + 1]); opt.setAttribute("target", "_blank"); } menu.on("select", function (event) { // Follow link window.location.href = event.option.getAttribute("href"); event.menu.close(); }); menu.show(this, Menu.BELOW | Menu.LEFT | Menu.CENTER | Menu.VERTICAL); // Stop event.preventDefault(); event.stopPropagation(); return false; }; return create_custom_search_links; })(); var modify_search_url = function (url) { var parts = new URLParts(url), param_count = 0, vars, params, i, k, k2, cats; // External url, no change if (parts.hostname !== null && parts.hostname !== window.location.hostname) return url; // Update vars vars = parts.search ? Hash.decode_vars(parts.search.substr(1))[1] : []; params = settings.get("custom_search_params"); for (k in params) { for (i = 0; i < vars.length; ++i) { if (vars[i][0] == k) { vars[i][1] = params[k]; break; } } if (i == vars.length) { vars.push([ k , params[k] ]); } ++param_count; } if (param_count > 0) { // Add default search categories (doesn't overwrite if they already exist) // Warning: This no longer works as of about 2018-02-09 because cookies aren't used for all settings anymore // https://forums.e-hentai.org/index.php?showtopic=213374 /* cats = settings.uconfig.categories; for (k in Settings.uconfig_categories) { if ((cats & Settings.uconfig_categories[k][0]) === 0) continue; k2 = Settings.uconfig_categories[k][1]; for (i = 0; i < vars.length && vars[i][0] != k2; ++i); if (i == vars.length) vars.push([ k2 , "1" ]); } */ // Add search apply vars.push([ "f_apply", "Apply Filter" ]); } // Encode parts.search = Hash.encode_vars(vars); if (parts.search.length > 0) parts.search = "?" + parts.search; return parts.join(); }; var setup_before_ready = function () { //API.block_redirections(); if (settings.get("custom_search_front_page")) { // Check for auto-search redirect var key = "custom_search_front_page_redirected"; if (window.sessionStorage) { if (!window.sessionStorage[key]) { if (window.location.pathname == "/" && window.location.search === "" && window.location.hash === "") { window.sessionStorage[key] = true; if (window.sessionStorage[key]) { var url = modify_search_url(window.location.href); if (url !== window.location.href) { window.stop(); window.history.replaceState(null, "", url); window.location.reload(); } } } } } } }; var setup_modified_titles = function () { var re_pattern = /\b(exhentai|e-hentai)/i, nodes, i; nodes = document.querySelectorAll("title"); for (i = 0; i < nodes.length; ++i) { nodes[i].textContent = nodes[i].textContent.trim().replace(re_pattern, "Ez+$1"); } }; var setup_custom_settings_link = function () { var nodes, i, url; // Custom link API.add_header_link($("a", null, "Ez-Settings", { href: "/uconfig.php#!eze/settings" })); // Modify homepage links if (settings.get("custom_search_front_page")) { nodes = API.get_front_page_links(document.documentElement); url = modify_search_url("http://exhentai.org/"); for (i = 0; i < nodes.length; ++i) { nodes[i].setAttribute("href", url); } } }; var setup_search = (function () { var setup_search = function (page_type) { var re_pattern = /\b(exhentai|e-hentai)/i, nodes, i, n1, n2, m; nodes = document.querySelectorAll("h1.ih"); for (i = 0; i < nodes.length; ++i) { nodes[i].textContent = nodes[i].textContent.trim().replace(re_pattern, "Ez+$1"); } // Add a random button if ((n1 = document.querySelector("p+table.ptt")) !== null) { n1 = n1.previousSibling; n2 = document.createElement("span"); n2.textContent = " | "; n1.appendChild(n2); n2 = document.createElement("a"); n2.setAttribute("href", "#!eze?random"); n2.textContent = "random"; n1.appendChild(n2); } // Make favorites classes clickable (WHY: onclick="document.location='http://exhentai.org/favorites.php?favcat='") if (page_type === "favorites") { nodes = document.querySelectorAll("div.fp"); for (i = 0; i < nodes.length; ++i) { n1 = nodes[i]; if ((m = /document\s*\.\s*location\s*=\s*'([^']*)'/.exec(n1.getAttribute("onclick") || "")) !== null) { // Replace n2 = document.createElement("a"); n2.setAttribute("href", m[1]); n2.style.textDecoration = "none"; n1.parentNode.insertBefore(n2, n1); n2.appendChild(n1); // Remove onclick n1.removeAttribute("onclick"); } } } // Setup custom search setup_custom_search(); // Setup navigation h_nav.on_change(on_hash_change); }; var on_hash_change = function (h) { if (h.path_array[0] !== "eze") return; if ("random" in h.vars) { // Search for the page counters var doc = document.documentElement, pages = API.get_pages_info_from_html(doc); if (pages !== null && pages.count > 0) { // Go to a random page var p_id = Math.floor(Math.random() * pages.count); window.location.href = API.form_page_url(doc, p_id, false) + Hash.sep + "eze?random-gallery"; } } else if ("random-gallery" in h.vars) { var results = API.get_search_page_results(document.documentElement); if (results.length > 0) { // Go to a random gallery var g_id = Math.floor(Math.random() * results.length), r = results[g_id]; window.location.href = "/g/" + r.gallery.gid + "/" + r.gallery.token; } } }; var setup_custom_search = function () { if (settings.get("custom_search_form")) { var params = settings.get("custom_search_params"); if (params !== null && typeof(params) === "object") { var n = document.querySelector("#advdiv"); if (n !== null) { var n2 = null; var ns = document.querySelectorAll(".nopm>a[onclick]"); for (var i = 0; i < ns.length; ++i) { if (ns[i].getAttribute("onclick").indexOf("toggle_advsearch_pane") >= 0) { n2 = ns[i]; break; } } if (n2 !== null && n.style.display === "none") { var s = n2.textContent; n2.click(); n2.textContent = s; s = n2.getAttribute("onclick"); n2.removeAttribute("onclick"); n2.addEventListener("click", function (event) { var n = document.querySelector("#advdiv"); if (n.innerHTML.length > 0 && n.style.display === "none") { this.textContent = "Hide Advanced Options"; n.style.display = ""; event.preventDefault(); event.stopImmediatePropagation(); return false; } }, false); n2.setAttribute("onclick", s); n.style.display = "none"; for (var k in params) { n2 = n.querySelector("[name='" + k + "']"); if (n2 !== null) { if (n2.tagName === "INPUT") { if (n2.type === "checkbox") { n2.checked = (params[k] === "on"); } } else if (n2.tagName === "SELECT") { n2.value = params[k]; } } } } } } } }; return setup_search; })(); var setup_gallery = (function () { var setup_gallery = function () { var gal_info = API.get_gallery_info_from_html(document.documentElement); //fix_tags(); if (gal_info !== null) { insert_custom(gal_info); recreate_favorite_link(gal_info.gallery); } }; var fix_tags = function () { // Modify tag links var nodes, i, s; nodes = document.querySelectorAll("#taglist td>div>a[href]"); for (i = 0; i < nodes.length; ++i) { s = nodes[i].getAttribute("href"); s = s.replace(/^(.*)\/([^\/]*)$/, "$1/\"$2\""); nodes[i].setAttribute("href", s); } }; var insert_custom = function (gal_info) { var par, rel, n; // Parent/relative rel = document.querySelector(".gm"); if (rel === null) return; par = rel.parentNode; rel = rel.nextSibling; // Create n = create_custom_gallery_bar(gal_info); // Add if (rel !== null) { par.insertBefore(n, rel); } else { par.appendChild(n); } }; var recreate_favorite_link = function (gallery) { var link = document.querySelector("#favoritelink"); if (link) { link.setAttribute("href", "https://exhentai.org/gallerypopups.php?gid=" + gallery.gid + "&t=" + gallery.token + "&act=addfav"); /* // Create n = $("a", "eze_favorite_link", link.textContent, { href: link.getAttribute("href") }, $.ON, [ "click", on_favorite_link_click, false, [ $.node, gallery.gid, gallery.token ] ] ); // Replace link.parentNode.insertBefore(n, link); link.parentNode.removeChild(link); */ } }; var create_custom_gallery_bar = function (gal_info) { var page_info = API.get_pages_info_from_html(document.documentElement), search_links = create_custom_search_links(gal_info), thumb_loader, i, n0, n1, n2, n3, n4, n5; // DOM create n0 = $("div", "gm eze_gallery_custom_container", [ $("div", "eze_gallery_custom", [ $("div", "eze_gallery_custom_table", [ $("div", "eze_gallery_custom_row", [ $("div", "eze_gallery_custom_cell", [ $("p", [ $("label", "eze_gallery_link", [ $("span", null, "Load all thumbnails "), n1 = $("span"), n2 = $("input", { type: "checkbox" }), ]), ]), $("p", [ n3 = $("a", "eze_gallery_link", "Download gallery"), ]), ]), n4 = $("div", "eze_gallery_custom_cell"), ]), ]), ]), n5 = $("div", "eze_dl_container"), ]); // Search links for (i = 0; i < search_links.length; ++i) { $("p", [ search_links[i] ], $.P, n4); } // Thumbnail loader thumb_loader = new ThumbnailLoader(gal_info.gallery.gid, gal_info.gallery.token, page_info, n2, n1); // Download link n3.addEventListener("click", bind(on_download_gallery_click, n3, { gallery: { gid: gal_info.gallery.gid, token: gal_info.gallery.token, }, loader: null, thumb_loader: thumb_loader, container: n5, }), false); // Done return n0; }; /* var on_favorite_link_click = function (gid, token, event) { // Skip if (event.which != 1) return; // Create menu var menu = new Menu(); menu.add_option("Select a category", { label: true }); menu.on("select", function (event) { event.menu.close(); }); menu.show(this, Menu.BELOW | Menu.LEFT | Menu.CENTER | Menu.VERTICAL); // Stop event.preventDefault(); event.stopPropagation(); return false; }; */ var on_download_gallery_click = function (data, event) { // Skip if (event.which != 1) return; if (data.loader === null) { // Create loader data.loader = new GalleryDownloadManager(data.gallery, data.container, data.thumb_loader); delete data.gallery; delete data.thumb_loader; } else { // Toggle visibility data.container.classList.toggle("eze_dl_container_visible"); } // Stop event.preventDefault(); event.stopPropagation(); return false; }; return setup_gallery; })(); var setup_gallery_deleted = (function () { var setup_gallery_deleted = function () { var url_info = API.get_gallery_url_info(window.location.href), n; // Invalid if (url_info === null) return; // Hide if ((n = document.querySelector("div.d")) !== null) { n.parentNode.removeChild(n); } // Get API.get_gallery(url_info.gid, url_info.token, 0, true, on_gallery_acquire); }; var on_gallery_acquire = function (status, gal_info) { if (status != API.OK) return; // An error occured var search_links = create_custom_search_links(gal_info), n0, n1, n2, i, k; // Create DOM n0 = $("div", "eze_main_container", [ //{ $("div", "eze_main eze_main_middle", [ $("div", "eze_main_box", [ // Main table $("div", "eze_dgallery_table", [ $("div", "eze_dgallery_row", [ $("div", "eze_dgallery_cell", [ // Image $("img", "eze_dgallery_image", { src: gal_info.thumbnail, alt: "", }), ]), $("div", "eze_dgallery_cell eze_dgallery_cell_full", [ // Body table $("div", "eze_dgallery_table", [ $("div", "eze_dgallery_row", [ $("div", "eze_dgallery_cell", [ // Title content $("h1", "eze_dgallery_title", gal_info.title), gal_info.title_original ? $("h2", "eze_dgallery_title_alt", gal_info.title_original) : null, ]), ]), $("div", "eze_dgallery_row", [ $("div", "eze_dgallery_cell", [ // Info table $("div", "eze_dgallery_table eze_dgallery_table_spaced", [ $("div", "eze_dgallery_row", [ $("div", "eze_dgallery_cell eze_dgallery_cell_nowhite eze_dgallery_cell_pre_border", [ // Body content // Category $("p", [ $("a", { href: "/" + gal_info.category }, [ $("img", "ic", { src: "https://exhentai.net/img/c/" + gal_info.category + ".png", title: gal_info.category, }), ]), ]), // Uploader $("p", [ $("span", "eze_dgallery_info_label", "Uploader: "), $("a", null, gal_info.uploader, { href: "/uploader/" + gal_info.uploader }), ]), // Uploaded $("p", [ $("span", "eze_dgallery_info_label", "Uploaded: "), $.text(date_format(gal_info.date_uploaded, "Y-m-d H:i")), ]), // Image count $("p", [ $("span", "eze_dgallery_info_label", "Images: "), $.text(gal_info.image_count + " @ " + bytes_to_labeled_string(gal_info.total_file_size_approx)), ]), ]), $("div", "eze_dgallery_cell eze_dgallery_cell_full eze_dgallery_cell_border", [ // Tags n1 = $("div", "eze_dgallery_tag_container"), ]), ]), ]), ]), ]), $("div", "eze_dgallery_row", [ $("div", "eze_dgallery_extra_container", [ // Extra stuff n2 = $("p", [ $("a", "eze_gallery_link", "Modify favorite", { href: "https://exhentai.org/gallerypopups.php?gid=" + gal_info.gallery.gid + "&t=" + gal_info.gallery.token + "&act=addfav", target: "_blank", }, $.ON, [ "click", on_favorite_edit_click ]), ]), $("p", [ $("a", "eze_gallery_link", "Return to front page", { href: "/" }), ]), ]), ]), ]), ]), ]), ]), ]), ]), ]); //} // Search links for (i = 0; i < search_links.length; ++i) { $("p", [ search_links[i] ], $.B, n2); } // Add tags for (k in gal_info.tags) { for (i = 0; i < gal_info.tags[k].length; ++i) { $("a", "eze_dgallery_tag", gal_info.tags[k][i], { href: "/tag/\"" + encodeURIComponent(gal_info.tags[k][i]) + "\"" }, $.P, n1); } } // Done document.body.innerHTML = ""; document.body.appendChild(n0); }; var on_favorite_edit_click = function (event) { // Skip if (event.which != 1) return; // Pop-up var window_id = "_pu_ezec" + (Math.random() + "").replace(/0\./, ""), window_width = 675, window_height = 415, screen_width = 0, screen_height = 0, window_settings; try { screen_width = screen.width; screen_height = screen.height; } catch (e) {} window_settings = "toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=" + window_width + ",height=" + window_height + ",left=" + ((screen_width - window_width) / 2) + ",top=" + ((screen_height - window_height) / 2); window.open(this.getAttribute("href"), window_id, window_settings); // Stop event.preventDefault(); event.stopPropagation(); return false; }; return setup_gallery_deleted; })(); var setup_panda = (function () { // Primary setup function var setup_panda = function () { // Setup login set_doctype(); setup_document(); setup_title(); setup_stylesheet(); setup_dom(); }; var set_doctype = function () { var new_doctype = document.implementation.createDocumentType( "html", "-//W3C//DTD XHTML 1.0 Transitional//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtdd" ); if (document.doctype) { document.replaceChild(new_doctype, document.doctype); } else { document.insertBefore(new_doctype, document.firstChild); } }; var setup_document = function () { var doc_el = document.documentElement, head = doc_el.querySelector("head") || document.head || null, body = doc_el.querySelector("body") || document.body || null, meta_charset = $("meta", { charset: "UTF-8" }), n; // Create head if (head !== null) { head.innerHTML = ""; } else { head = $("head"); n = doc_el.firstChild; if (n !== null) { doc_el.insertBefore(head, n); } else { doc_el.appendChild(head); } } // Create body if (body === null) { body = $("body"); doc_el.appendChild(body); } // Setup head head.appendChild(meta_charset); }; var setup_title = function () { var doc_el = document.documentElement, title = $("title"), head = doc_el.querySelector("head"), i, n; // Remove titles n = document.querySelectorAll("title"); for (i = 0; i < n.length; ++i) { n[i].parentNode.removeChild(n[i]); } // Add title head.appendChild(title); // Update title update_title(title); // Observe var MutationObserver = (window.MutationObserver || window.WebKitMutationObserver); if (MutationObserver) { new MutationObserver(function () { update_title(title); }) .observe(title, { childList: true }); } }; var update_title = function (title) { var text = title.textContent, text_target = "Login Helper"; // Update title if (text != text_target) { title.textContent = text_target; } }; var setup_stylesheet = function () { // Styling var vars = { border: CSS.color("#000000"), bg: CSS.color("#43464e"), bg_dark: CSS.color("#34353b"), bg_light: CSS.color("#4f535b"), text: CSS.color("#f1f1f1"), text_link: CSS.color("#dddddd"), text_link_hover: CSS.color("#fffbdb"), text_error: CSS.color("#ff8080"), text_success: CSS.color("#80ff80"), }; // Create css var css = CSS.format( "body{font-size:10pt;font-family:arial,helvetica,sans-serif;color:{{color:text}};background:{{color:bg_dark}};padding:0;margin:0;}a{color:{{color:text_link}};text-decoration:underline;cursor:pointer;}a:hover{color:{{color:text_link_hover}};}.eze_main_container{position:absolute;left:0;top:0;bottom:0;right:0;white-space:nowrap;line-height:0;text-align:center;}.eze_main_container:before{content:\"\";display:inline-block;width:0;height:100%;vertical-align:middle;}.eze_main{display:inline-block;vertical-align:top;white-space:normal;line-height:normal;text-align:left;margin:2em 0;}.eze_main.eze_main_middle{vertical-align:middle;}.eze_main_box{border:1px solid {{color:border}};background-color:{{color:bg}};padding:1em;box-sizing:border-box;-moz-box-sizing:border-box;}.eze_input_line{display:block;text-align:left;width:20em;box-sizing:border-box;-moz-box-sizing:border-box;}.eze_input_line+.eze_input_line{margin-top:0.5em;}.eze_input_line.eze_input_center{text-align:center;}.eze_input_line_label_text{font-size:1.5em;font-weight:bold;}", vars ); // Insert API.inject_style(css); }; var setup_dom = function () { var doc_el = document.documentElement, body = document.body, image = doc_el.querySelector("img"), loc = window.location.href, site = "" + window.location.hostname.replace(/\.[^\.]*$/, ""), n0, image_new, link1_container, link1, link2, link3; // Find image url if (image !== null) { loc = image.getAttribute("src"); } image_new = $("img", "eze_login_image", { src: loc, alt: "", }); // Create DOM n0 = $("div", "eze_main_container", [ //{ $("div", "eze_main eze_main_middle", [ $("div", "eze_main_box", [ $("div", "eze_input_line eze_input_center", [ image_new, ]), $("div", "eze_input_line", [ $("span", "eze_input_line_label_text", "Login Instructions:"), ]), $("div", "eze_input_line", [ link1_container = $("span", "eze_instruction eze_instruction_ready", [ $.text("Delete all e-hentai and "), link1 = $("a", null, site, { title: "Click to delete " + site + " cookies" }), $.text(" cookies"), ]), ]), $("div", "eze_input_line", [ link2 = $("a", "eze_instruction", "Go to the homepage and log in", { href: "https://e-hentai.org/", target: "_blank", rel: "noreferrer nofollow" }), ]), $("div", "eze_input_line", [ link3 = $("a", "eze_instruction", "Refresh the page", { href: "" }), ]), ]), ]), ]); //} var remove_cookies = function () { Cookies.remove("yay", "/", "." + window.location.hostname); }; // Events link1.addEventListener("click", function (event) { if (event.which !== 1) return; remove_cookies(); link1_container.classList.add("eze_instruction_complete"); link2.classList.add("eze_instruction_ready"); link3.classList.add("eze_instruction_ready"); event.preventDefault(); event.stopPropagation(); return false; }, false); link3.addEventListener("click", function (event) { if (event.which !== 1) return; remove_cookies(); // just in case link2.classList.add("eze_instruction_complete"); link3.classList.add("eze_instruction_complete"); event.preventDefault(); event.stopPropagation(); window.location.reload(); return false; }, false); // Replace body.innerHTML = ""; body.appendChild(n0); }; return setup_panda; })(); var setup_settings = (function () { var setup_settings = function () { // Setup navigation h_nav.on_change(on_hash_change); if (h_nav.is_inited) { h_nav.call(on_hash_change); } }; var on_hash_change = function (h) { if (h.path_array.length < 2 || h.path_array[0] !== "eze" || h.path_array[1] !== "settings") { hide_settings_panel(); } else { if (!show_settings_panel()) create_settings_panel(); } }; var hide_settings_panel = function () { var n = document.querySelector(".eze_settings_container"); if (n !== null) { n.classList.remove("eze_settings_container_visible"); return true; } return false; }; var show_settings_panel = function () { var n = document.querySelector(".eze_settings_container"); if (n !== null) { n.classList.add("eze_settings_container_visible"); return true; } return false; }; var create_settings_panel = function () { // Create var rel = document.querySelector(".stuffbox"), container = $("div", "eze_settings_container eze_settings_container_visible"), params = settings.get("custom_search_params"), n_min_rating, box; n_min_rating = new OptionBox([ ["2"] , ["3"] , ["4"] , ["5"] ], ("f_srdd" in params ? params.f_srdd : "2"), function (value) { var v = settings.get("custom_search_params"); if ("f_sr" in v) { v.f_srdd = value; settings.set("custom_search_params", v); } }); // Primary settings box = $("div", "eze_settings_box", [ $("h1", "eze_settings_header", "Search link settings"), $("div", "eze_settings_entry_container", [ $("div", "eze_settings_entry", [ $("label", "eze_settings_label", [ $("input", { type: "checkbox" }, $.ON, [ "change", on_checkbox_change, false, [ $.node, "show_deprecation_notice" ] ], $.CHECKED, settings.get("show_deprecation_notice")), $("strong", null, "Show deprecation notice"), ]), ]), $("div", "eze_settings_entry", [ $("label", "eze_settings_label", [ $("input", { type: "checkbox" }, $.ON, [ "change", on_checkbox_change, false, [ $.node, "custom_search_front_page" ] ], $.CHECKED, settings.get("custom_search_front_page")), $("strong", null, "Apply these settings to the front page"), ]), ]), $("div", "eze_settings_entry", [ $("label", "eze_settings_label", [ $("input", { type: "checkbox" }, $.ON, [ "change", on_checkbox_change, false, [ $.node, "custom_search_links" ] ], $.CHECKED, settings.get("custom_search_links")), $("strong", null, "Apply these settings to custom search links"), ]), ]), $("div", "eze_settings_entry", [ $("label", "eze_settings_label", [ $("input", { type: "checkbox" }, $.ON, [ "change", on_checkbox_change, false, [ $.node, "custom_search_form" ] ], $.CHECKED, settings.get("custom_search_form")), $("strong", null, "Apply these settings to the search form"), ]), ]), create_search_setting("Search gallery descriptions", "f_sdesc", "on"), create_search_setting("Search low-power tags", "f_sdt1", "on"), create_search_setting("Search downvoted tags", "f_sdt2", "on"), create_search_setting("Search torrent filenames", "f_storr", "on"), create_search_setting("Show exclusively galleries with torrents", "f_sto", "on"), create_search_setting("Show expunged galleries", "f_sh", "on"), $("div", "eze_settings_entry", [ $("label", "eze_settings_label", [ $("input", { type: "checkbox" }, $.ON, [ "change", on_search_setting2_change, false, [ $.node, "f_sr", "on", "f_srdd", n_min_rating ] ], $.CHECKED, "f_sr" in params), $("span", null, "Minimum rating"), n_min_rating.node, ]), ]), $("div", "eze_settings_entry", { style: "padding-right:0;" }, [ $("div", "eze_settings_entry", "Categories", { style: "font-weight:bold;" }), $("div", { style: "margin-left:1em;" }, [ $("div"), create_search_setting("Doujinshi", "f_doujinshi", "1"), create_search_setting("Manga", "f_manga", "1"), create_search_setting("Artist CG", "f_artistcg", "1"), create_search_setting("Game CG", "f_gamecg", "1"), create_search_setting("Western", "f_western", "1"), create_search_setting("Non-H", "f_non-h", "1"), create_search_setting("Image Set", "f_imageset", "1"), create_search_setting("Cosplay", "f_cosplay", "1"), create_search_setting("Asian Porn", "f_asianporn", "1"), create_search_setting("Misc", "f_misc", "1"), ]), ]), ]), // minimum rating ], $.P, container); n_min_rating.node.classList.add("eze_settings_pad_left"); // Add if (rel !== null) { rel.parentNode.insertBefore(container, rel); } else { document.body.appendChild(container); } }; var create_search_setting = function (name, var_name, var_value) { var checked = (var_name in settings.get("custom_search_params")), n1; n1 = $("div", "eze_settings_entry", [ $("label", "eze_settings_label", [ $("input", { type: "checkbox" }, $.ON, [ "change", on_search_setting_change, false, [ $.node, var_name, var_value ] ], $.CHECKED, checked), $("span", null, name), ]), ]); return n1; }; var on_search_setting_change = function (var_name, var_value) { var v = settings.get("custom_search_params"); if (this.checked) { // Set v[var_name] = var_value; settings.set("custom_search_params", v); } else { // Remove if (var_name in v) { delete v[var_name]; settings.set("custom_search_params", v); } } }; var on_search_setting2_change = function (var_name, var_value, var_name2, option_box) { var v = settings.get("custom_search_params"); if (this.checked) { // Set v[var_name] = var_value; v[var_name2] = option_box.get(); settings.set("custom_search_params", v); } else { // Remove var changed = false; if (var_name in v) { delete v[var_name]; changed = true; } if (var_name2 in v) { delete v[var_name2]; changed = true; } if (changed) { settings.set("custom_search_params", v); } } }; var on_checkbox_change = function (var_name) { settings.set(var_name, this.checked); }; return setup_settings; })(); var deprecation_notice = (function () { var display = function () { if (settings.get("show_deprecation_notice") === false) { return; } var n = document.querySelector("#toppane"); if (n === null) { return; } var notice = document.createElement("div"); var notice_content = document.createElement("div"); notice_content.className = "idi"; notice_content.style.textAlign = "left"; var header = document.createElement("h2"); header.style.textAlign = "center"; header.style.margin = "0"; header.textContent = "eze Deprecation Notice"; notice_content.appendChild(header); var p = document.createElement("p"); p.textContent = "The eze userscript is no longer undergoing active development.\nSeveral features have already stopped working, and more features\nare likely to break in the future."; notice_content.appendChild(p); p = document.createElement("p"); p.textContent = "Several features have already been converted to standalone userscripts\nthat will be easier to maintain in the future:"; notice_content.appendChild(p); var li; var ul = document.createElement("ul"); ul.style.padding = "0 0 0 1.5em"; ul.style.margin = "3px 1px"; li = document.createElement("li"); li.textContent = "Random gallery"; ul.appendChild(li); li = document.createElement("li"); li.textContent = "Gallery thumbnail loader"; ul.appendChild(li); li = document.createElement("li"); li.textContent = "Removed gallery info recovery"; ul.appendChild(li); li = document.createElement("li"); li.textContent = "Full title readability on search results"; ul.appendChild(li); notice_content.appendChild(ul); p = document.createElement("p"); p.textContent = "These features can be found in the collection of userscripts at "; var a = document.createElement("a"); a.href = "https://dnsev-h.github.io/x/"; a.target = "_blank"; a.rel = "noreferrer"; a.textContent = a.href; p.appendChild(a); p.appendChild(document.createTextNode(".")); notice_content.appendChild(p); p = document.createElement("p"); a = document.createElement("a"); a.href = "#"; a.target = "_blank"; a.rel = "noreferrer"; a.textContent = "Click here to hide this notice."; a.addEventListener("click", function (e) { settings.set("show_deprecation_notice", false); notice.style.display = "none"; e.preventDefault(); e.stopPropagation(); return false; }, false); p.appendChild(a); notice_content.appendChild(p); notice.appendChild(notice_content); n.parentNode.insertBefore(notice, n); }; return function () { // Delay for settings to be loaded setTimeout(function () { display(); }, 100); }; })(); // Init var settings = new Settings(); var h_nav = new Hash(); setup_before_ready(); on_ready(function () { // Page check var page_type = API.get_page_type_from_html(document.documentElement); if (page_type === null) return; // Don't do anything if (page_type == "panda") { // Sad panda setup_panda(); } else { // Styling insert_stylesheet(); setup_modified_titles(); setup_custom_settings_link(); if (page_type == "search" || page_type == "favorites") { setup_search(page_type); } else if (page_type == "gallery") { setup_gallery(); } else if (page_type == "gallery_deleted") { // setup_gallery_deleted(); } else if (page_type == "settings") { setTimeout(function () { setup_settings(); }, 100); } deprecation_notice(); } // Init hash navigation h_nav.init(); }); }).call(this, window, (function () { try { return GM; } catch (e) { return undefined; } })());