// ==UserScript==//
// @name IMDB works#
// @description Shows number of credited works after names on IMDB site
// @match *://*.imdb.com/*
// @version 1.0.6
// @author wOxxOm
// @namespace wOxxOm.scripts
// @license MIT License
// @grant GM_addStyle
// @run-at document-start
// @require https://greasyfork.org/scripts/12228/code/setMutationHandler.js
// @downloadURL none
// ==/UserScript==
var SELECTOR = 'a[href^="/name/nm"]';
var CACHE_FRESH_DURATION = 30 * 24 * 3600 * 1000; // 1 month
var CACHE_STALE_DURATION = CACHE_FRESH_DURATION * 2; // keep stale data another 2 months, then kill
GM_addStyle(
'.number-of-works, .number-of-works span { opacity: 0.5; transition: opacity .25s ease-in-out .25s; }' +
'.number-of-works span:before { content: "/"; }' +
'.number-of-works:hover { opacity: 1.0; }' +
'.number-of-works:hover span { opacity: 1.0; }' +
'.number-of-works:before { content: " ["; opacity: 0.5; }' +
'.number-of-works:after { content: "]"; opacity: 0.5; }'
);
process(document.querySelectorAll(SELECTOR));
setMutationHandler(document, SELECTOR, process);
function process(links) {
var now = Date.now();
for (var link, i = 0; (link = links[i++]); ) {
if (link.querySelector('img') ||
!link.textContent.trim() ||
link.children[0] && link.textContent.trim() != link.children[0].textContent.trim() ||
link.nextElementSibling && link.nextElementSibling.matches('.number-of-works'))
continue;
var id = (link.pathname.match(/\/name\/nm(\d+)\/?$/) || [])[1];
if (!id)
continue;
var cacheKey = 'works#' + id;
var cache = (localStorage[cacheKey] || '').split('\t');
if (cache.length == 2 && +cache[0] && cache[1]) {
showWorksNum(link, cache[1]);
var isFresh = +cache[0] > now;
if (isFresh)
continue;
}
doXHR({
url: link.pathname,
link: link,
cacheKey: cacheKey,
onload: parseNamePage,
});
}
}
function showWorksNum(link, num) {
num = num.toString().replace(/\/(\d+)/, '$1');
if (link.nextElementSibling && link.nextElementSibling.matches('.number-of-works')) {
link.nextElementSibling.innerHTML = num;
} else {
link.insertAdjacentHTML('afterend', '' + num + '');
}
}
function parseNamePage(doc, options) {
var credits = [].map.call(doc.querySelectorAll('#filmography .head'), function(e) {
return +(e.textContent.match(/\((\d+) credits?\)/i) || [])[1];
});
if (!credits.length)
return;
var creditsSum = credits.reduce(function(sum, e) { return sum + e; }, 0);
var worksNum = credits[0] + (credits.length > 1 ? '/' + creditsSum : '');
showWorksNum(options.link, worksNum);
localStorage[options.cacheKey] = '' + (Date.now() + CACHE_FRESH_DURATION) + '\t' + worksNum;
}
function doXHR(options) {
if (document.readyState == 'complete') {
sendRequest(options);
return;
}
if (!doXHR.queue)
initQueue();
if (!isDupe()) {
doXHR.queue.push(options);
doXHR.queuedUrl[options.url] = options;
}
function sendRequest(options) {
var xhr = new XMLHttpRequest();
xhr.open('GET', options.url);
xhr.responseType = 'document';
xhr.onload = function(e) {
options.onload(xhr.response, options);
doXHR.activeRequests--;
poolQueue();
};
doXHR.activeRequests++;
xhr.send();
}
function initQueue() {
doXHR.queue = [];
doXHR.queuedUrl = {};
doXHR.activeRequests = 0;
document.addEventListener('DOMContentLoaded', function docLoaded() {
document.removeEventListener('DOMContentLoaded', docLoaded);
cleanupStorage();
poolQueue();
});
}
function isDupe() {
var dupe = doXHR.queuedUrl[options.url];
if (!dupe)
return false;
if (dupe.link == options.link)
return true;
// this request's link element will use the will-be-cached data from the earlier queued request
options.url = '';
var _onload = dupe.onload;
dupe.onload = function() {
_onload.apply(null, arguments);
showWorksNum(options.link, localStorage[options.cacheKey].split('\t')[1]);
};
return true;
}
function poolQueue() {
while (doXHR.queue.length && doXHR.activeRequests < 16) {
sendRequest(doXHR.queue.shift());
}
}
}
function cleanupStorage() {
setTimeout(function doCleanup() {
var now = Date.now(), i = 0;
for (var k in localStorage) {
if (k.lastIndexOf('works#', 0) === 0 && (+localStorage[k].split('\t')[0]) + CACHE_STALE_DURATION < now) {
delete localStorage[k];
}
if (++i % 100 === 0 && Date.now() - now > 10) {
setTimeout(doCleanup, 1000);
return;
}
}
}, 1000);
}