// ==UserScript==// // @name IMDB works# // @description Shows number of credited works after names on IMDB site // @match *://*.imdb.com/* // @version 1.0.5 // @author wOxxOm // @namespace wOxxOm.scripts // @license MIT License // @grant GM_addStyle // @grant GM_xmlhttpRequest // @run-at document-start // @require https://greasyfork.org/scripts/12228/code/setMutationHandler.js // @downloadURL none // ==/UserScript== var SELECTOR = 'a[href^="/name/nm"]'; var CACHE_DURATION = 30 * 24 * 3600 * 1000; // 1 month 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.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, needsUpdate) { num = num.toString().replace(/\/(\d+)/, '$1'); if (link.nextElementSibling && link.nextElementSibling.matches('.number-of-works')) { link.nextElementSibling.innerHTML = num; delete link.nextElementSibling.dataset.updating; } 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_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); 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()); } } }