NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==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://g.e-hentai.org/ // @include http://g.e-hentai.org/?page=* // @include http://g.e-hentai.org/?f_* // @include http://g.e-hentai.org/tag/* // @include http://g.e-hentai.org/uploader/* // @include http://g.e-hentai.org/favorites.php* // @include https://exhentai.org/ // @include https://exhentai.org/?page=* // @include https://exhentai.org/?f_* // @include https://exhentai.org/tag/* // @include https://exhentai.org/uploader/* // @include https://exhentai.org/favorites.php* // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @version 0.2.3 // ==/UserScript== var onFirefox = (window.chrome === undefined); var save = function(key,value) { if (onFirefox) GM_setValue(key,value); else localStorage.setItem(key,value); }; var load = function(key,def) { return onFirefox ? GM_getValue(key,def) : (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('http://g.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();