Sensible sized E-Hentai Automated Downloads

A modified version of E-Hentai Automated Downloads by etc. that selects between resized and uncompressed archives based on size and also ignores out of date torrents.

// ==UserScript==
// @name           Sensible sized E-Hentai Automated Downloads
// @description    A modified version of E-Hentai Automated Downloads by etc. that selects between resized and uncompressed archives based on size and also ignores out of date torrents.
// @namespace      https://greasyfork.org/users/212175-brozilian
// @author         brozilian
// @version        2.3
// @include        http://e-hentai.org/*
// @include        https://e-hentai.org/*
// @include        http://exhentai.org/*
// @include        https://exhentai.org/*
// @grant          GM_xmlhttpRequest
// @grant          GM.xmlHttpRequest
// @grant          GM_setValue
// @grant          GM_getValue
// @grant          GM.setValue
// @grant          GM.getValue
// @run-at         document-start
// ==/UserScript==
//
// Based on version 2.1.3 of E-Hentai Automated Downloads by etc see https://sleazyfork.org/en/scripts/1604-e-hentai-automated-downloads . Thanks to etc for the original.
//
//
// Settings can now be found at top of ehentai settings page and should persist through versions.
//



if (typeof(GM_getValue) !== 'undefined') {
	var imageSizeLimit = GM_getValue('imageSizeLimit', 1500);
	var imageLowSizeLimit = GM_getValue('imageLowSizeLimit', 20); //Smallest size per image in KB to reject small torrents
	var downloadIfNoTorrentFound = GM_getValue('downloadIfNoTorrentFound', true);
	var saveDownloadsAsVisits = GM_getValue('saveDownloadsAsVisits', false);
} else if (typeof(GM) !== 'undefined') {
	var imageSizeLimit = GM.getValue('imageSizeLimit', 1500);
	var imageLowSizeLimit = GM.getValue('imageLowSizeLimit', 20); //Smallest size per image in KB to reject small torrents
	var downloadIfNoTorrentFound = GM.getValue('downloadIfNoTorrentFound', true);
	var saveDownloadsAsVisits = GM.getValue('saveDownloadsAsVisits', false);
} else
	reject(new Error('GM methods not working'));

var apiurl = "https://" + window.location.host + "/api.php";

var storageName = "ehVisited"; //name of object, to avoid clash with old installs

function ehvStore(data) {
      	var sto = localStorage.getItem(storageName);
	    var vis = JSON.parse(sto);
		var ccc = data.galleryId + "." + data.galleryToken;
		vis["data"][ccc] = Date.now();
		localStorage.setItem(storageName, JSON.stringify(vis));
        return;
	}
if (localStorage.getItem(storageName) && saveDownloadsAsVisits) {
	var countDownloads = true;
 
} else
	var countDownloads = false;

if (typeof(Promise) === 'undefined') {
	console.warn('Browser does not support promises, aborting.');
	return;
}

/*-----------------------
Assets (icons and GIFs)
-----------------------*/

var ASSETS = {

	downloadIcon: generateSvgIcon(1792, 'rgb(0,0,0)',
		'M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 ' +
		'45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28h-1472q-40 0-68-28t-28-68v-320q0-40 28-68t' +
		'68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-' +
		'18 19-45 19t-45-19l-448-448q-31-29-14-70 17-39 59-39h256v-448q0-26 19-45t45-19h256q26 0 45 19t19 45' +
		'v448h256q42 0 59 39z'),

	torrentIcon: generateSvgIcon(1792, 'rgb(0,0,0)',
		'M1216 928q0-14-9-23t-23-9h-224v-352q0-13-9.5-22.5t-22.5-9.5h-192q-13 0-22.5 9.5t-9.5 22.5v352h-224q' +
		'-13 0-22.5 9.5t-9.5 22.5q0 14 9 23l352 352q9 9 23 9t23-9l351-351q10-12 10-24zm640 224q0 159-112.5 2' +
		'71.5t-271.5 112.5h-1088q-185 0-316.5-131.5t-131.5-316.5q0-130 70-240t188-165q-2-30-2-43 0-212 150-3' +
		'62t362-150q156 0 285.5 87t188.5 231q71-62 166-62 106 0 181 75t75 181q0 76-41 138 130 31 213.5 135.5' +
		't83.5 238.5z'),

	pickerIcon: generateSvgIcon(1792, 'rgb(252,0,97)',
		'M1333 566q18 20 7 44l-540 1157q-13 25-42 25-4 0-14-2-17-5-25.5-19t-4.5-30l197-808-406 101q-4 1-12 1' +
		'-18 0-31-11-18-15-13-39l201-825q4-14 16-23t28-9h328q19 0 32 12.5t13 29.5q0 8-5 18l-171 463 396-98q8' +
		'-2 12-2 19 0 34 15z'),

	doneIcon: generateSvgIcon(1792, 'rgb(0,0,0)',
		'M1412 734q0-28-18-46l-91-90q-19-19-45-19t-45 19l-408 407-226-226q-19-19-45-19t-45 19l-91 90q-18 18-1' +
		'8 46 0 27 18 45l362 362q19 19 45 19 27 0 46-19l543-543q18-18 18-45zm252 162q0 209-103 385.5t-279.5 2' +
		'79.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5' +
		' 103 385.5z'),

	loadingGif: 'url(data:image/gif;base64,' +
	'R0lGODlhEgASAMQaAHl5d66urMXFw3l5dpSUk5WVlKOjoq+vrsbGw6Sko7u7uaWlpbm5t3h4doiIhtLSz4aGhJaWlsbGxNHRzrC' +
	'wr5SUkqKiobq6uNHRz4eHhf///wAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgAaACwAAAAAEgASAAAFaq' +
	'AmjmRplstyrkmbrCNFaUZtaFF0HvyhWRZNYVgwBY4BEmFJOB1NlYpJoYBpHI7RZXtZZb4ZEbd7AodFDIYVAjFJJCYA4ISoI0hyu' +
	'UnAF2geDxoDgwMnfBoYiRgaDQ1WiIqPJBMTkpYaIQAAIfkEBQoAGgAsAQABABAAEAAABWSgJo4aRZEoeaxHOiqKFsyBtizopV9y' +
	'nfwJ0o43MhgNKAYjZbGQJBLXKBLRIK4IaWFbEHgFUoKYoPFKRZUK6fFIORwojBxDytgzpDkdANDc8SQTExp8fBoQEGcDiwNnJA0' +
	'NLiEAACH5BAUKABoALAEAAQAQABAAAAVloCaOmqKQKHmtVzpKksa2FIUiOKIxjHb8B5JgKCAFjgHUMHUkPR6u0WKhwVgx0YQ2cc' +
	'W6DGCDZjKJiiwWEgCQikRQ6zWpQC+QBviBxuHQEP4EKA0NGhmGGRoVFWaHiGYjEBAuIQAAIfkEBQoAGgAsAQABABAAEAAABWSgJ' +
	'o6aJJEoiaxIOj6PJsyCpigopmNyff0X0o43AgZJk0mKwSABAK4RhaJ5PqOH7GHAHUQD4ICm0YiKwCSHI7VYoDLwDClBT5Di8khE' +
	'Y+gbUBAQGgWEBRoWFmYEiwRmJBUVLiEAACH5BAUKABoALAEAAQAQABAAAAVloCaO2vOQKImtWDoCgMa2koTCsDZNGuIjpIFwQBI' +
	'YBahGI2UkORyukUKhyVgz0Yv2csW6thcNBBIVMRikSCRFoaAK8ALpQD+QCHiCZrHQBP4BKBUVGgmGCX6BUQaMBmUkFhYuIQAAIf' +
	'kEBQoAGgAsAQABABAAEAAABWagJo4aAJAoaZrp6DjaIA/a86BZnmlNo2FADEm3GwWFJAgkNZmQIpHWSCLRFK4FKWKLIHgJUoFYo' +
	'KlUpCIxabFIKRSohDxButgvJIPeoKFQNHd4JBYWGgeHBxoMDGgBjgFoJI4tIQAAIfkEBQoAGgAsAQABABAAEAAABWSgJo6a45Ao' +
	'ma1ZOkaRxrYAgBZ4oUGQVtckgpBAGhgHqEol1WiQFgvX6PHQJK4JKWaLMXgNWq7GYpGKJhMShZKSSFCH+IGEqCNIgXxAo1BoBIA' +
	'CKHkaF4YXf4JSh4hmIwwMLiEAACH5BAUKABoALAEAAQAQABAAAAVloCaOWhSRKFmsRToui0bMhOY4aKInWlVpmWCGZCgaSMIhyW' +
	'JJQSAkCsU1AgA0h+yBarUGvgHqYDzQfKmiRoOkUKQeD9RlfiFh7hgSvS6RaPB5JAwMGgiGCBoTE2gCjQJoJI0uIQAAOw==)'

};

/*---------
Utilities
---------*/

function generateSvgIcon(size, color, data) {
	return format('url("data:image/svg+xml,' +
		'<svg width=\'{0}\' height=\'{0}\' viewBox=\'0 0 {0} {0}\' xmlns=\'http://www.w3.org/2000/svg\'>' +
		'<path fill=\'{1}\' d=\'{2}\'/></svg>")', size, color, data);
}

function createButton(data) {
	var result = document.createElement(data.hasOwnProperty('type') ? data.type : 'a');
	if (data.hasOwnProperty('className'))
		result.className = data.className;
	if (data.hasOwnProperty('title'))
		result.title = data.title;
	if (data.hasOwnProperty('onClick')) {
		result.addEventListener('mouseup', data.onClick, false);
		result.addEventListener('click', function (e) {
			e.preventDefault();
		}, false);
		result.addEventListener('contextmenu', function (e) {
			e.preventDefault();
		}, false);
	}
	if (data.hasOwnProperty('parent'))
		data.parent.appendChild(result);
	if (data.hasOwnProperty('target'))
		result.setAttribute('target', data.target);
	if (data.hasOwnProperty('style'))
		result.style.cssText = Object.keys(data.style).map(function (x) {
				return x + ': ' + data.style[x] + 'px';
			}).join('; ');
	return result;
}

function format(varargs) {
	var pattern = arguments[0];
	for (var i = 1; i < arguments.length; ++i)
		pattern = pattern.replace(new RegExp('\\{' + (i - 1) + '\\}', 'g'), arguments[i]);
	return pattern;
}

function xhr(data) {
	return new Promise(function (resolve, reject) {
		var request = {
			method: data.method,
			url: data.url,
			onload: function () {
				resolve.apply(this, arguments);
			},
			onerror: function () {
				reject.apply(this, arguments);
			}
		};
		if (data.headers)
			request.headers = data.headers;
		if (data.body && data.body.constructor == String)
			request.data = data.body;
		else if (data.body)
			request.data = JSON.stringify(data.body);
		if (typeof(GM_xmlhttpRequest) !== 'undefined')
			GM_xmlhttpRequest(request);
		else if (typeof(GM) !== 'undefined')
			GM.xmlHttpRequest(request);
		else
			reject(new Error('Could not submit XHR request'));
	});
}

function parseHTML(html) {
	var div = document.createElement('div');
	div.innerHTML = html.replace(/src=/g, 'no-src=');
	return div;
}

function updateUI(data) {
	if (!data || data.error)
		return;
	var temp = (data.isTorrent ? torrentQueue[data.galleryId] : archiveQueue[data.galleryId]);
	temp.button.className = temp.button.className.replace(/\s*working/, '') + ' requested';

	if (countDownloads){
      ehvStore(data);}
}

function handleFailure(data) {
	if (!data)
		return;
	var temp = (data.isTorrent ? torrentQueue[data.galleryId] : archiveQueue[data.galleryId]);
	temp.button.className = temp.button.className.replace(/\s*working/, '');
	if (data.error == 'could not find any suitable torrent' && downloadIfNoTorrentFound) {

		temp.button.previousSibling.dispatchEvent(new MouseEvent("mouseup"));

	} else if (data.error !== 'aborted')
		alert('Could not complete operation.\nReason: ' + (data.error || 'unknown'));
}

function xpathFind(root, nodeType, text) {
	return document.evaluate('.//' + (nodeType || '*') + '[contains(text(), "' + text + '")]', root, null, 9, null).singleNodeValue;
}

function pickTorrent(candidates, lastUpdateDate) {
	var currentScore = 0,
	currentCandidate = null;
	// Get max values
	var maxSeeds = candidates.reduce(function (p, n) {
			return Math.max(p, n.seeds);
		}, 0);
	var maxSize = candidates.reduce(function (p, n) {
			return Math.max(p, n.size);
		}, 0);
	// Calculate scores
	candidates.forEach(function (candidate) {
		var seedScore = candidate.seeds / maxSeeds;
		var sizeScore = candidate.size / maxSize;
		// Total score
		var score = seedScore * sizeScore;
		if (currentScore >= score)
			return;
		currentScore = score;
		currentCandidate = candidate;
	});
	return currentCandidate;
}

/*--------------
Download Steps
--------------*/

function obtainArchiverKey(data) {
	return xhr({
		method: 'GET',
		url: format('{0}//{1}/g/{2}/{3}',
			window.location.protocol, window.location.host, data.galleryId, data.galleryToken)
	})
	.then(function (response) {
		var div = parseHTML(response.responseText);
		var target = div.querySelector('[onclick*="archiver.php"]');
		if (target) {
			var tokens = target.getAttribute('onclick').match(/or=([^'"]+)/);
			if (tokens)
				data.archiverKey = tokens[1];
		}
		return data;
	}).then(function (data) {
		if (!data.archiverKey) {
			return xhr({
				method: 'POST',
				url: apiurl,
				body: JSON.stringify({
					'method': 'gdata',
					'gidlist': [[data.galleryId, data.galleryToken]]
				}),
				headers: {
					'Content-Type': 'application/x-www-form-urlencoded'
				},
			}).then(function (response) {
				data.archiverKey = JSON.parse(response.responseText).gmetadata[0].archiver_key;
				return data;
			})
		} else
			return data;
	}).then(function (data) {
		if (!data.archiverKey)
			data.error = 'could not resolve archiver key';
		if (data.error)
			return Promise.reject(data);
		else
			return data;
	});
}

function obtainTorrentFile(data) {
	return xhr({
		method: 'GET',
		url: format('{0}//{1}/gallerytorrents.php?gid={2}&t={3}',
			window.location.protocol, window.location.host, data.galleryId, data.galleryToken)
	})
	.then(function (response) {
		var div = parseHTML(response.responseText);
		var forms = div.querySelectorAll('form'),
		candidates = [];
		var findValue = function (text) {
			var target = xpathFind(forms[i], 'span', text);
			return (target ? target.nextSibling.textContent.trim() : null);
		};
		for (var i = 0; i < forms.length; ++i) {
			var link = forms[i].querySelector('a');
			if (!link)
				continue;
			// Gather torrent data
			var posted = new Date(findValue('Posted')),
			size = findValue('Size'),
			seeds = parseInt(findValue('Seeds'), 10) || 0;
			size = parseFloat(size, 10) * (/MB/i.test(size) ? 1024 : (/GB/i.test(size) ? 1024 * 1024 : 1));
			// Ignore torrents with invalid sizes or no seeds or older than newest update
			if (size === 0 || size < (imageLowSizeLimit * data.length) || size > (data.size * 1.05) || size > (imageSizeLimit * data.length) || seeds === 0 || (posted < data.date))
				continue;
			candidates.push({
				link: link.href,
				date: posted,
				size: size,
				seeds: seeds
			});
		}
		if (candidates.length === 0)
			data.error = 'could not find any suitable torrent';
		else
			data.fileUrl = pickTorrent(candidates, data.date).link
				if (data.error)
					return Promise.reject(data);
				else
					return data;
	});
}

function confirmDownloadRequest(data) {
	return xhr({
		method: 'GET',
		url: format('{0}//{1}/archiver.php?gid={2}&token={3}&or={4}',
			window.location.protocol, window.location.host, data.galleryId, data.galleryToken,
			data.archiverKey.replace(/--/, '-'))
	})
	.then(function (response) {
		var div = parseHTML(response.responseText);
		var costLabel = xpathFind(div, '*', 'Download Cost:');
		var sizeLabel = xpathFind(div, '*', 'Estimated Size:');
		if (!costLabel || !sizeLabel)
			return data;
		var cost = costLabel.textContent.replace(/^.+:/, '').trim();
		var size = sizeLabel.textContent.replace(/^.+:/, '').trim();
		var proceed = confirm(format('Size: {0}\nCost: {1}\n\nProceed?', size, cost));
		if (proceed)
			return data;
		data.error = 'aborted';
		return Promise.reject(data);
	});
}

function confirmDownloadRequestResized(data) {
	return xhr({
		method: 'GET',
		url: format('{0}//{1}/archiver.php?gid={2}&token={3}&or={4}',
			window.location.protocol, window.location.host, data.galleryId, data.galleryToken,
			data.archiverKey.replace(/--/, '-'))
	})
	.then(function (response) {
		var div = parseHTML(response.responseText);
		var cost = document.evaluate("/div[1]/div[1]/div[2]/div/strong", div, null, 9, null).singleNodeValue.textContent.trim();
		var size = document.evaluate("/div[1]/div[1]/div[2]/p/strong", div, null, 9, null).singleNodeValue.textContent.trim();
		var proceed = confirm(format('Size: {0}\nCost: {1}\n\nProceed?', size, cost));
		if (proceed)
			return data;
		data.error = 'aborted';
		return Promise.reject(data);
	});
}

function submitDownloadRequest(data) {
	return xhr({
		method: 'POST',
		url: format('{0}//{1}/archiver.php?gid={2}&token={3}&or={4}',
			window.location.protocol, window.location.host, data.galleryId, data.galleryToken,
			data.archiverKey.replace(/--/, '-')),
		headers: {
			'Content-Type': 'application/x-www-form-urlencoded'
		},
		body: 'dltype=org&dlcheck=Download+Original+Archive',
	})
	.then(function (response) {
		var div = parseHTML(response.responseText);
		var target = div.querySelector('#continue > a');
		if (target)
			url = target.href;
		else {
			var targets = div.querySelectorAll('script');
			for (var i = 0; i < targets.length; ++i) {
				var match = targets[i].textContent.match(/location\s*=\s*"(.+?)"/);
				if (!match)
					continue;
				url = match[1];
				break;
			}
		}
		if (url)
			data.archiverUrl = url;
		else
			data.error = 'could not resolve archiver URL';
		if (data.error)
			return Promise.reject(data);
		else
			return data;
	});
}
function submitDownloadRequestResized(data) {
	return xhr({
		method: 'POST',
		url: format('{0}//{1}/archiver.php?gid={2}&token={3}&or={4}',
			window.location.protocol, window.location.host, data.galleryId, data.galleryToken,
			data.archiverKey.replace(/--/, '-')),
		headers: {
			'Content-Type': 'application/x-www-form-urlencoded'
		},
		body: 'dltype=res&dlcheck=Download+Resample+Archive',
	})
	.then(function (response) {
		var div = parseHTML(response.responseText);
		var target = div.querySelector('#continue > a');
		if (target)
			url = target.href;
		else {
			var targets = div.querySelectorAll('script');
			for (var i = 0; i < targets.length; ++i) {
				var match = targets[i].textContent.match(/location\s*=\s*"(.+?)"/);
				if (!match)
					continue;
				url = match[1];
				break;
			}
		}
		if (url)
			data.archiverUrl = url;
		else
			data.error = 'could not resolve archiver URL';
		if (data.error)
			return Promise.reject(data);
		else
			return data;
	});
}

function waitForDownloadLink(data) {
	return xhr({
		method: 'GET',
		url: data.archiverUrl
	})
	.then(function (response) {
		if (/The file was successfully prepared/i.test(response.responseText)) {
			var div = parseHTML(response.responseText);
			var target = div.querySelector('#db a');
			if (target) {
				var archiverUrl = new URL(data.archiverUrl);
				data.fileUrl = archiverUrl.protocol + '//' + archiverUrl.host + target.getAttribute('href');
			} else
				data.error = 'could not resolve file URL';
		} else
			data.error = 'archiver did not provide file URL';
		if (data.error)
			return Promise.reject(data);
		else
			return data;
	})
	.catch(function () {
		if (data.error)
			return Promise.reject(data);
		data.error = 'could not contact archiver';
		if (/https/.test(window.location.protocol)) {
			data.error += '; this is most likely caused by mixed-content security policies enforced by the' +
			' browser that need to be disabled by the user. If you have no clue how to do that, you' +
			' should probably Google "how to disable mixed-content blocking".';
		} else {
			data.error += '; please check whether your browser is not blocking XHR requests towards' +
			' 3rd-party URLs';
		}
		return Promise.reject(data);
	});
}

function downloadFile(data) {
	var a = document.createElement('a');
	a.href = data.fileUrl;
	document.body.appendChild(a);
	a.click();
	document.body.removeChild(a);
	document.body.appendChild(a);
	return Promise.resolve(data);
}

function getGalleryData(target) {
	return xhr({
		method: 'GET',
		url: target
	}).then(function (response) {
		var div = parseHTML(response.responseText);
		gallerySize = xpathFind(div, 'td', 'File Size:').nextSibling.textContent.trim();
		if (gallerySize)
			gallerySize = parseFloat(gallerySize, 10) * (/MB/i.test(gallerySize) ? 1024 : (/GB/i.test(gallerySize) ? 1024 * 1024 : 1)); //in KB
		var galleryLength = xpathFind(div, 'td', 'Length:').nextSibling.textContent.trim().split(' ')[0];
		var galleryDate = new Date(xpathFind(div, 'td', 'Posted:').nextSibling.textContent.trim());
		return [galleryDate, gallerySize, galleryLength];
	});
}

/*----------------
State Management
----------------*/

var archiveQueue = {}, torrentQueue = {};

function requestDownload(e) {
	var isTorrent = /torrentLink/.test(e.target.className);
	if (/working|requested/.test(e.target.className))
		return;
	if (e.which !== 1 && (e.which !== 3 || isTorrent))
		return;
	e.preventDefault();
	e.stopPropagation();
	e.target.className += ' working';
	var tokens = e.target.getAttribute('target').match(/\/g\/(\d+)\/([0-9a-z]+)/i);
	var galleryId = parseInt(tokens[1], 10),
	galleryToken = tokens[2];
	var askConfirmation = (!isTorrent && e.which === 3);
	if (isTorrent) {
		// Try to find out gallery's last update date if possible
		var galleryDate = xpathFind(document, 'td', 'Posted:'); // gallery page
		if (galleryDate)
			galleryDate = galleryDate.nextSibling;
		else // thumbnail mode
			galleryDate = document.evaluate('./ancestor::tr/td[@class="itd"]', e.target, null, 9, null).singleNodeValue;
		if (galleryDate !== null)
			galleryDate = new Date(galleryDate.textContent.trim());
		// Gather data
		torrentQueue[galleryId] = {
			token: galleryToken,
			button: e.target
		};
		obtainTorrentFile({
			galleryId: galleryId,
			galleryToken: galleryToken,
			isTorrent: true,
			date: galleryDate
		})
		.then(downloadFile)
		.then(updateUI)
		.catch(handleFailure);

	}
	if (!isTorrent) {
		archiveQueue[galleryId] = {
			token: galleryToken,
			button: e.target
		};
		var promise = obtainArchiverKey({
				galleryId: galleryId,
				galleryToken: galleryToken,
				isTorrent: false
			});
		if (askConfirmation)
			promise = promise.then(confirmDownloadRequest);
		promise
		.then(submitDownloadRequest)
		.then(waitForDownloadLink)
		.then(downloadFile)
		.then(updateUI)
		.catch(handleFailure);
	}
	return false;
}

async function requestDownloadResized(e) {
	var isTorrent = /torrentLink/.test(e.target.className);
	if (/working|requested/.test(e.target.className))
		return;
	if (e.which !== 1 && (e.which !== 3 || isTorrent))
		return;
	e.preventDefault();
	e.stopPropagation();
	e.target.className += ' working';
	var tokens = e.target.getAttribute('target').match(/\/g\/(\d+)\/([0-9a-z]+)/i);
	var galleryId = parseInt(tokens[1], 10),
	galleryToken = tokens[2];
	var askConfirmation = (!isTorrent && e.which === 3);
	if (window.location.href == e.target.getAttribute('target')) {
		var gallerySize = xpathFind(document, 'td', 'File Size:').nextSibling.textContent.trim();
		if (gallerySize)
			gallerySize = parseFloat(gallerySize, 10) * (/MB/i.test(gallerySize) ? 1024 : (/GB/i.test(gallerySize) ? 1024 * 1024 : 1)); //in KB
		var galleryLength = xpathFind(document, 'td', 'Length:').nextSibling.textContent.trim().split(' ')[0];
		var galleryDate = new Date(xpathFind(document, 'td', 'Posted:').nextSibling.textContent.trim());
	} else {
		var[galleryDate, gallerySize, galleryLength] = await getGalleryData(e.target.getAttribute('target'));
	}
	if (isTorrent) {
		// Gather data
		torrentQueue[galleryId] = {
			token: galleryToken,
			button: e.target
		};
		obtainTorrentFile({
			galleryId: galleryId,
			galleryToken: galleryToken,
			isTorrent: true,
			date: galleryDate,
			size: gallerySize,
			length: galleryLength
		})
		.then(downloadFile)
		.then(updateUI)
		.catch(handleFailure);
	}
	if (!isTorrent) {
		if ((gallerySize / galleryLength) < imageSizeLimit) {
			archiveQueue[galleryId] = {
				token: galleryToken,
				button: e.target
			};
			var promise = obtainArchiverKey({
					galleryId: galleryId,
					galleryToken: galleryToken,
					isTorrent: false
				});
			if (askConfirmation)
				promise = promise.then(confirmDownloadRequest);
			promise
			.then(submitDownloadRequest)
			.then(waitForDownloadLink)
			.then(downloadFile)
			.then(updateUI)
			.catch(handleFailure);
		} else {
			archiveQueue[galleryId] = {
				token: galleryToken,
				button: e.target
			};
			var promise = obtainArchiverKey({
					galleryId: galleryId,
					galleryToken: galleryToken,
					isTorrent: false
				});
			if (askConfirmation)
				promise = promise.then(confirmDownloadRequestResized);
			promise
			.then(submitDownloadRequestResized)
			.then(waitForDownloadLink)
			.then(downloadFile)
			.then(updateUI)
			.catch(handleFailure);
		}
	}
	return false;
}

/*--------
UI Setup
--------*/

window.addEventListener('load', function () {

	// document.querySelectorAll('.gl3m, .gl3c, .gl1e').forEach((button) => {
	// 	button.onclick = null
	// });

	// button generation (thumbnail)
	var thumbnails = document.querySelectorAll('.gl3t'),
	n = thumbnails.length;
	while (n-- > 0) {
		createButton({
			title: 'Automated download',
			target: thumbnails[n].querySelector('a').href,
			className: 'automatedButton downloadLink',
			onClick: requestDownloadResized,
			style: {
				bottom: 0,
				right: -2
			},
			parent: thumbnails[n]
		});
		createButton({
			title: 'Torrent download',
			target: thumbnails[n].querySelector('a').href,
			className: 'automatedButton torrentLink',
			onClick: requestDownloadResized,
			style: {
				bottom: 0,
				left: -1
			},
			parent: thumbnails[n]
		});
	}

	// button generation (extended)
	var erows = document.querySelectorAll('.gl1e > div > a'),
	n = erows.length;
	while (n-- > 0) {
		createButton({
			type: 'div',
			title: 'Automated Resized download',
			target: erows[n].href,
			className: 'automatedInline downloadLink',
			onClick: requestDownloadResized,
			style: {
				'margin-left': 33
			},
			parent: erows[n].parentNode.parentNode.nextSibling.firstChild.firstChild.lastChild
		});
		createButton({
			type: 'div',
			title: 'Torrent download',
			target: erows[n].href,
			className: 'automatedInline torrentLink',
			onClick: requestDownloadResized,
			style: {
				'margin-left': 10
			},
			parent: erows[n].parentNode.parentNode.nextSibling.firstChild.firstChild.lastChild
		});

	}

	// button generation (compact)
	var crows = document.querySelectorAll('.gl3c  > a'),
	n = crows.length;
	while (n-- > 0) {
		createButton({
			type: 'div',
			title: 'Automated Resized download',
			target: crows[n].href,
			className: 'automatedInline downloadLink',
			onClick: requestDownloadResized,
			style: {
				bottom: 0,
				right: -1
			},
			parent: crows[n].parentNode
		});
		createButton({
			type: 'div',
			title: 'Torrent download',
			target: crows[n].href,
			className: 'automatedInline torrentLink',
			onClick: requestDownloadResized,
			style: {
				bottom: 23,
				right: -1
			},
			parent: crows[n].parentNode
		});

	}

	//button generation (minimal and minimal+)
	var rows = document.querySelectorAll('.gl3m  > a'),
	n = rows.length;
	while (n-- > 0) {
		createButton({
			type: 'div',
			title: 'Automated Resized download',
			target: rows[n].href,
			className: 'automatedInline downloadLink',
			onClick: requestDownloadResized,
			style: {
				bottom: 0,
				right: 0
			},
			parent: rows[n].parentNode
		});
		createButton({
			type: 'div',
			title: 'Torrent download',
			target: rows[n].href,
			className: 'automatedInline torrentLink',
			onClick: requestDownloadResized,
			style: {
				bottom: 0,
				right: 23
			},
			parent: rows[n].parentNode
		});

	}

	// button generation (gallery)
	var krows = document.querySelectorAll('#gd5'),
	n = krows.length;
	while (n-- > 0) {
		createButton({
			type: 'div',
			title: 'Automated resized download',
			target: window.location.href,
			className: 'automatedInline downloadLink',
			onClick: requestDownloadResized,
			style: {
				left: 23
			},
			parent: krows[n]
		});
		createButton({
			type: 'div',
			title: 'Torrent download',
			target: window.location.href,
			className: 'automatedInline torrentLink',
			onClick: requestDownloadResized,
			style: {
				left: 0
			},
			parent: krows[n]
		});
	}

	// document style
	var style = document.createElement('style');
	style.innerHTML =
		// Icons and colors
		'.downloadLink  { background-image: ' + ASSETS.downloadIcon + '; background-color: rgb(220,98,98); }' +
		'.torrentLink  { background-image: ' + ASSETS.torrentIcon + '; background-color: rgb(98,182,210); }' +
		'.requested  { background-image: ' + ASSETS.doneIcon + '; }' +
		'.requested, .working { background-color: rgba(128,226,126,1); }' +
		'.working { background-image: ' + ASSETS.loadingGif + ' !important; background-repeat: no-repeat; }' +
		'.automatedPicker { background-image: ' + ASSETS.pickerIcon + '; }' +
		'.automatedButton:hover, .automatedInline:hover { background-color: rgba(255,199,139,1) }' +
		// Positioning
		'#gd1 > div, .gl3t, .gl1e > div { position: relative; }' +
		'div.it4 { position: absolute!important; right: 0px!important; }' + //compensating for buttons
		'div.it5 { position: absolute!important; left: 48px!important; height: 14px !important;}' +
		'div.i {  margin-left: -16px!important; }' +
		'div.in { margin-left: -42px!important; margin-top: 3px!important; background: black!important; }' +
		'div.in:hover { opacity: 0!important;}' +
		'div.it3 { margin-top: -6px!important; }' +
		'td.itu {overflow: hidden !important; position: absolute !important; height: 14px !important;}' +
		// Backgrounds
		'.automatedButton { background-size: 20px 20px; background-position: 5px 5px; background-repeat: no-repeat; }' +
		'.automatedPicker { background-size: 12px 12px; background-position: 2px 2px; background-repeat: no-repeat; }' +
		'.automatedInline { background-size: 13px 13px; background-position: 5px 5px; background-repeat: no-repeat; }' +
		// Others (thumbnail mode)
		'.automatedButton { display: none; position: absolute; text-align: left; cursor: pointer;' +
		'color: white; margin-right: 1px; font-size: 20px; line-height: 11px; width: 28px; height: 28px; }' +
		'.automatedButton.downloadLink  { border-radius: 0 0 5px 0 !important; }' +
		'.automatedButton.torrentLink  { border-radius: 0 0 0 5px !important; }' +
		'#gd1 > div > .automatedButton { border-radius: 0 0 0 0 !important; }' +
		'.automatedButton.working { font-size: 0px; }' +
		'#gd1 > div:hover .automatedButton, .gl3t:hover .automatedButton, .gl1e > div:hover .automatedButton,' +
		' .automatedButton.working, .automatedButton.requested { display: block !important; }' +
		//  ' .gl3e > .gldown > a {left: -30px; position: absolute;}' +
		// Others (list mode)
		'.automatedPicker { width: 16px; height: 16px; float: left; cursor: pointer; }' +
		'.automatedPicker > div { display: none; z-index: 100; position: absolute; top: -4px; text-align: center; }' +
		'.automatedPicker:hover > div, .automatedPicker > div:hover { display: block; }' +
		'.automatedInline { position: absolute; z-index: 100; border: 1px solid black; width: 23px; height: 23px; display: inline-block; }' +
		'.automatedInline:first-child { border-right: none !important; }';
	document.head.appendChild(style);

	//store value for gallery size threshold
	if (window.location.pathname == "/uconfig.php") {
		var sdcheckstate = '';
		if (downloadIfNoTorrentFound) {
			sdcheckstate = 'checked="true"';
		}
		var ehcheckstate = '';
		if (saveDownloadsAsVisits) {
			ehcheckstate = 'checked="true"';
		}
		var settingsdiv = document.createElement('div');
		var ehvisitedsetting = '<span>Count downloaded galleries in E-H Visited script (requires https://sleazyfork.org/en/scripts/377945-e-h-visited )</span><input id="ehvisitedcountdownloads" type="checkbox" ' + ehcheckstate + '><br>';

		settingsdiv.innerHTML = '<h2>Sensible sized E-Hentai Automated Downloads settings</h2><br><span>Image size limit in KB. Default is 1500 i.e. 1.5MB </span>' +
			'<input id="imagesizeconfig" type="text" value=' + imageSizeLimit + ' ><br><span>Start a direct download if no appropriate torrent is ' +
			'available </span><input id="autodownload" type="checkbox" ' + sdcheckstate + '><br>' + ehvisitedsetting + '<input type="button" id="savescriptsettings" value="Save">';

		document.getElementById("outer").insertBefore(settingsdiv, document.getElementById("profile_outer"));

		document.getElementById("savescriptsettings").addEventListener("click", function () {
			if (isNaN(document.getElementById("imagesizeconfig").value)) {
				alert('Needs to be a number');
			} else if (typeof(GM_setValue) !== 'undefined') {
				GM_setValue('downloadIfNoTorrentFound', document.getElementById("autodownload").checked);
				GM_setValue('saveDownloadsAsVisits', document.getElementById("ehvisitedcountdownloads").checked);
				GM_setValue('imageSizeLimit', document.getElementById("imagesizeconfig").value);
			} else if (typeof(GM) !== 'undefined') {
				GM.setValue('downloadIfNoTorrentFound', document.getElementById("autodownload").checked);
				GM.setValue('saveDownloadsAsVisits', document.getElementById("ehvisitedcountdownloads").checked);
				GM.setValue('imageSizeLimit', document.getElementById("imagesizeconfig").value);
			} else
				reject(new Error('GM methods not working'));
		})

	}

}, false);