// ==UserScript==
// @name E-Hentai Info on Hover
// @description Displays additional gallery information when hovering over thumbnails. Only works in thumbnail mode.
// @namespace http://userscripts.org/users/106844
// @include http://e-hentai.org/*
// @include https://e-hentai.org/*
// @include http://g.e-hentai.org/*
// @include https://g.e-hentai.org/*
// @include http://exhentai.org/*
// @include https://exhentai.org/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @version 0.2.6
// ==/UserScript==
var save = function(key,value) {
if (typeof(GM_setValue) !== 'undefined') GM_setValue(key,value);
else localStorage.setItem(key,value);
};
var load = function(key,def) {
if (typeof(GM_getValue) !== 'undefined') return GM_getValue(key,def);
else return (localStorage.getItem(key) || def);
};
var doRequest = function(url, method, data, callback) {
if (typeof(GM_xmlhttpRequest) === 'undefined') {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function() { callback(this); };
xhr.send(data);
} else {
GM_xmlhttpRequest({
url: url,
method: method,
data: data,
onload: callback
});
}
};
/* * * * * * * * * */
var stringify = function(data) {
var volatile = (new Date().valueOf() - data.posted*1000) < 1000*60*60*2;
return data.gid + ':' + (volatile*1) + ':' + data.filesize + ':' + data.tags.join(',') + ':' + data.uploader;
};
var parse = function(data) {
if (data && data[0] == '@') data = data.slice(1);
var tokens = data.split(/:/);
if (!tokens || tokens.length < 5) return null;
return { gid: parseInt(tokens[0],10), volatile: parseInt(tokens[1],10) == 1,
size: parseInt(tokens[2],10), tags: tokens[3].split(','), uploader: tokens[4] };
};
var getRegex = function(gid,flags) {
return new RegExp('(@|^)' + gid + ':[^@]+',flags);
};
/* * * * * * * * * */
var checkStorage = function(gid) {
var data = load('g.cache',null);
if (data === null) return null;
var match = data.match(getRegex(gid));
return match === null ? null : parse(match[0]);
};
var cleanStorage = function() {
var data = load('g.cache',null), lastClean = load('g.lastClean',null);
if (data === null) return;
var now = new Date().valueOf(), hours = 1000*60*60;
if (lastClean !== null && now - parseInt(lastClean,10) < 6*hours) return;
data = data.split(/@/).filter(function(x) { var parsed = parse(x); return parsed && !parsed.volatile; });
save('g.cache',data.join('@'));
save('g.lastClean',now);
};
/* * * * * * * * * */
var startApiRequest = function(target) {
apiBusy = true;
var request = [ [ target.gid, target.token ] ], data = load('g.cache','');
var temp = targets.filter(function(x) { return x.gid != target.gid && !getRegex(x.gid).test(data); });
temp = temp.slice(0,24).map(function(x) { return [ x.gid, x.token ]; });
request = request.concat(temp);
request = JSON.stringify({ method: 'gdata', gidlist: request });
doRequest('https://e-hentai.org/api.php', 'POST', request, function(data) { onApiLoad(target,data.responseText); });
};
var onApiLoad = function(target,response) {
var data = load('g.cache',null);
data = data === null ? [ ] : data.split(/@/);
response = JSON.parse(response);
response.gmetadata.forEach(function(x) { data.push(stringify(x)); });
save('g.cache',data.slice(-1000).join('@'));
apiBusy = false;
if (timeout !== null) showData(target,checkStorage(target.gid));
};
/* * * * * * * * * */
var mouseOver = function(target) {
if (apiBusy) return;
if (timeout !== null) clearTimeout(timeout);
timeout = setTimeout(function() { hoverTimeout(target); },500);
};
var mouseLeave = function(target) {
if (timeout !== null) clearTimeout(timeout);
timeout = null;
target.target.classList.remove('gShow');
};
var hoverTimeout = function(target) {
if (target.target.querySelector('.gData') !== null) {
target.target.classList.add('gShow');
return;
}
var data = checkStorage(target.gid);
if (data !== null) showData(target,data);
else startApiRequest(target);
};
/* * * * * * * * * */
var showData = function(target,data) {
if (data === null) return;
var div = document.createElement('div');
var size = (Math.round(data.size/1024/1024*100)/100) + 'MB';
div.appendChild(document.createElement('div')).innerHTML = size;
if (data.uploader) {
var uploader = document.createElement('a');
uploader.href = window.location.origin + '/uploader/' + data.uploader;
uploader.textContent = data.uploader;
var uploaderContainer = document.createElement('span');
uploaderContainer.appendChild(uploader);
div.appendChild(uploaderContainer);
}
var tags = div.appendChild(document.createElement('div'));
data.tags.forEach(function(x,n) {
var a = tags.appendChild(document.createElement('a'));
a.href = '/?f_search=' + x.replace(/\s/g,'+');
a.innerHTML = x;
if (n < data.tags.length-1) tags.appendChild(document.createTextNode(', '));
});
div.className = 'gData id1';
target.target.appendChild(div);
setTimeout(function() { target.target.classList.add('gShow'); },10);
};
/* * * * * * * * * */
var onPanda = (window.location.href.indexOf('exhentai') != -1);
var style = document.createElement('style');
style.innerHTML =
'.gData { position: absolute; top: 0px; left: 0px; text-align: left; padding: 5px;' +
'font-weight: bold; height: 100%; width: 95% !important; z-index: 1; font-size: 10px; margin: 0 !important;' +
'border: none !important; border-radius: 0 !important; transition: left .5s; left: -300px;' +
'color: ' + (onPanda ? 'white' : 'black') + '}' +
'.gShow > .gData { left: 0px !important; }' +
'.gData > :first-child:before { content: "Size: " }' +
'.gData > :nth-child(2):not(:last-child):before { content: "Uploader: " }' +
'.gData > :last-child:before { content: "Tags: " }' +
'.gData > :last-child > a { color: ' + (onPanda ? 'white' : 'black') + ' !important; }' +
'.gData > :last-child > a:hover { background: red !important; }' +
'.automatedButton { z-index: 2; }';
document.head.appendChild(style);
/* * * * * * * * * */
var timeout = null, apiBusy = false;
var targets = Array.prototype.slice.call(document.querySelectorAll('.id3 > a'),0);
targets = targets.map(function(x) {
var tokens = x.href.match(/g\/(\d+)\/([0-9a-f]{10,10})/);
if (!tokens) return null;
return { target: x, gid: parseInt(tokens[1],10), token: tokens[2] };
});
targets = targets.filter(function(x) { return x !== null; });
targets.forEach(function(x) {
x.target.addEventListener('mouseover',function() { mouseOver(x); },false);
x.target.addEventListener('mouseleave',function() { mouseLeave(x); },false);
});
cleanStorage();