// ==UserScript==
// @name Show Metacritic.com ratings
// @description Show metacritic metascore and user ratings on: Bandcamp, Apple Itunes (Music), Amazon (Music,Movies,TV Shows), IMDb (Movies), Google Play (Music, Movies), TV.com, Steam, Gamespot (PS4, XONE, PC), Rotten Tomatoes, Serienjunkies, BoxOfficeMojo, allmovie.com, movie.com, Wikipedia (en), themoviedb.org, letterboxd, TVmaze, TVGuide, followshows.com, TheTVDB.com
// @namespace cuzi
// @oujs:author cuzi
// @grant GM_xmlhttpRequest
// @grant GM_getResourceURL
// @grant GM_setValue
// @grant GM_getValue
// @grant unsafeWindow
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @resource global.min.css http://www.metacritic.com/css/global.min.1446760484.css
// @resource base.min.css http://www.metacritic.com/css/search/base.min.1446760407.css
// @license GNUGPL
// @version 6
// @include https://*.bandcamp.com/*
// @include https://itunes.apple.com/*/album/*
// @include https://play.google.com/store/music/album/*
// @include https://play.google.com/store/movies/details/*
// @include http://www.amazon.com/*
// @include https://www.amazon.com/*
// @include http://www.amazon.co.uk/*
// @include https://www.amazon.co.uk/*
// @include http://www.amazon.fr/*
// @include https://www.amazon.fr/*
// @include http://www.amazon.de/*
// @include https://www.amazon.de/*
// @include http://www.amazon.es/*
// @include https://www.amazon.es/*
// @include http://www.amazon.ca/*
// @include https://www.amazon.ca/*
// @include http://www.amazon.in/*
// @include https://www.amazon.in/*
// @include http://www.amazon.it/*
// @include https://www.amazon.it/*
// @include http://www.amazon.co.jp/*
// @include https://www.amazon.co.jp/*
// @include http://www.amazon.com.mx/*
// @include https://www.amazon.com.mx/*
// @include http://www.amazon.com.au/*
// @include https://www.amazon.com.au/*
// @include http://www.imdb.com/title/*
// @include https://www.imdb.com/title/*
// @include http://store.steampowered.com/app/*
// @include https://store.steampowered.com/app/*
// @include http://www.gamespot.com/*
// @include https://www.gamespot.com/*
// @include http://www.serienjunkies.de/*
// @include https://www.serienjunkies.de/*
// @include http://www.tv.com/shows/*
// @include http://www.rottentomatoes.com/m/*
// @include https://www.rottentomatoes.com/m/*
// @include http://www.rottentomatoes.com/tv/*
// @include https://www.rottentomatoes.com/tv/*
// @include http://www.boxofficemojo.com/movies/*
// @include http://www.allmovie.com/movie/*
// @include https://en.wikipedia.org/*
// @include http://www.movies.com/*/m*
// @include https://www.themoviedb.org/movie/*
// @include https://www.themoviedb.org/tv/*
// @include http://letterboxd.com/film/*
// @include https://letterboxd.com/film/*
// @include http://www.tvmaze.com/shows/*
// @include http://www.tvguide.com/tvshows/*
// @include https://www.tvguide.com/tvshows/*
// @include http://followshows.com/show/*
// @include https://followshows.com/show/*
// @include http://thetvdb.com/*tab=series*
// @include https://thetvdb.com/*tab=series*
// @downloadURL none
// ==/UserScript==
var baseURL = "http://www.metacritic.com/";
var baseURL_music = "http://www.metacritic.com/music/";
var baseURL_movie = "http://www.metacritic.com/movie/";
var baseURL_pcgame = "http://www.metacritic.com/game/pc/";
var baseURL_ps4 = "http://www.metacritic.com/game/playstation-4/";
var baseURL_xone = "http://www.metacritic.com/game/xbox-one/";
var baseURL_tv = "http://www.metacritic.com/tv/";
var baseURL_search = "http://www.metacritic.com/search/{type}/{query}/results";
var baseURL_autosearch = "http://www.metacritic.com/autosearch";
var mybrowser = "other";
if(~navigator.userAgent.indexOf("Chrome")) {
mybrowser = "chrome";
}
// http://www.designcouch.com/home/why/2013/05/23/dead-simple-pure-css-loading-spinner/
var CSS = "#mcdiv123 .grespinner{height:16px;width:16px;margin:0 auto;position:relative;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgba(0,174,239,.8);border-radius:100%}@-webkit-keyframes rotation{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{from{-moz-transform:rotate(0)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{from{-o-transform:rotate(0)}to{-o-transform:rotate(359deg)}}@keyframes rotation{from{transform:rotate(0)}to{transform:rotate(359deg)}}#mcdiv123searchresults .result{font:12px arial,helvetica,serif;border-top-width:1px;border-top-color:#ccc;border-top-style:solid;padding:5px}#mcdiv123searchresults .result .result_type{display:inline}#mcdiv123searchresults .result .result_wrap{float:left;width:100%}#mcdiv123searchresults .result .has_score{padding-left:42px}#mcdiv123searchresults .result .basic_stats{height:1%;overflow:hidden}#mcdiv123searchresults .result h3{font-size:14px;font-weight:700}#mcdiv123searchresults .result a{color:#09f;font-weight:700;text-decoration:none}#mcdiv123searchresults .metascore_w.game.seventyfive,#mcdiv123searchresults .metascore_w.positive,#mcdiv123searchresults .metascore_w.score_favorable,#mcdiv123searchresults .metascore_w.score_outstanding,#mcdiv123searchresults .metascore_w.sixtyone{background-color:#6c3}#mcdiv123searchresults .metascore_w.forty,#mcdiv123searchresults .metascore_w.game.fifty,#mcdiv123searchresults .metascore_w.mixed,#mcdiv123searchresults .metascore_w.score_mixed{background-color:#fc3}#mcdiv123searchresults .metascore_w.negative,#mcdiv123searchresults .metascore_w.score_terrible,#mcdiv123searchresults .metascore_w.score_unfavorable{background-color:red}#mcdiv123searchresults a.metascore_w,#mcdiv123searchresults span.metascore_w{display:inline-block}#mcdiv123searchresults .result .metascore_w{color:#fff!important;font-family:Arial,Helvetica,sans-serif;font-size:17px;font-style:normal!important;font-weight:700!important;height:2em;line-height:2em;text-align:center;vertical-align:middle;width:2em;float:left;margin:0 0 0 -42px}#mcdiv123searchresults .result .more_stats{font-size:10px;color:#444}#mcdiv123searchresults .result .release_date .data{font-weight:700;color:#000}#mcdiv123searchresults ol,#mcdiv123searchresults ul{list-style:none}#mcdiv123searchresults .result li.stat{background:0 0;display:inline;float:left;margin:0;padding:0 6px 0 0;white-space:nowrap}#mcdiv123searchresults .result .deck{margin:3px 0 0}#mcdiv123searchresults .result .basic_stat{display:inline;float:right;overflow:hidden;width:100%}";
function name2metacritic(s) {
return s.normalize('NFKD').replace(/\//g,"").replace(/[\u0300-\u036F]/g, '').replace(/&/g,"and").replace(/\W+/g, " ").toLowerCase().trim().replace(/\W+/g,"-");
}
function minutesSince(time) {
var seconds = ((new Date()).getTime() - time.getTime()) / 1000;
return seconds>60?parseInt(seconds/60)+" min ago":"now";
}
function randomStringId() {
var id10 = () => Math.floor((1 + Math.random()) * 0x10000000000).toString(16).substring(1);
return id10()+id10()+id10()+id10()+id10()+id10();
}
function fixMetacriticURLs(html) {
return html.replace(/= 75) {
fg = "white";
bg = "#6c3";
t = parseInt(score);
} else if(score < 40) {
fg = "white";
bg = "#f00";
t = parseInt(score);
} else {
fg = "white";
bg = "#fc3";
t = parseInt(score);
}
return ''+t+'';
}
function filterUniversalUrl(url) {
url = url.match(/http.+/)[0].replace(/https?:\/\/(www.)?/,"");
if(url.startsWith("somehost")) {// TODO
return url; // Do not remove parameters
} else {
return url.split("?")[0].split("&")[0]; // Remove parameters
}
}
function addToMap(url, metaurl) {
var data = JSON.parse(GM_getValue("map","{}"));
var url = filterUniversalUrl(url);
var metaurl = metaurl.replace(/^http:\/\/(www.)?metacritic\.com\//,"");
data[url] = metaurl;
GM_setValue("map", JSON.stringify(data));
(new Image()).src = "http://123.net23.net/whitelist.php?docurl="+encodeURIComponent(url)+"&metaurl="+encodeURIComponent(metaurl)+"&ref="+encodeURIComponent(randomStringId());
}
function addToBlacklist(url, metaurl) {
var data = JSON.parse(GM_getValue("black","{}"));
var url = filterUniversalUrl(url);
var metaurl = metaurl.replace(/^http:\/\/(www.)?metacritic\.com\//,"");
data[url] = metaurl;
GM_setValue("black", JSON.stringify(data));
(new Image()).src = "http://123.net23.net/blacklist.php?docurl="+encodeURIComponent(url)+"&metaurl="+encodeURIComponent(metaurl)+"&ref="+encodeURIComponent(randomStringId());
}
function isBlacklistedUrl(docurl, metaurl) {
docurl = docurl.replace(/https?:\/\/(www.)?/,"");
metaurl = metaurl.replace(/^http:\/\/(www.)?metacritic\.com\//,"");
metaurl = metaurl.replace(/\/\//g,"/").replace(/\/\//g,"/");; // remove double slash
var data = JSON.parse(GM_getValue("black","{}"));
if(docurl in data) {
if(data[docurl] == metaurl) {
return true;
}
}
return false;
}
function isBlacklisted(metaurl) {
return isBlacklistedUrl("" + document.location.host.replace(/^www\./,"") + document.location.pathname + document.location.search, metaurl);
}
function listenForHotkeys(code, cb) {
// Call cb() as soon as the code sequence was typed
var i = 0;
$(document).bind("keydown.listenForHotkeys",function(ev) {
if(document.activeElement == document.body) {
if(ev.key != code[i]) {
i = 0;
} else {
i++;
if(i == code.length) {
ev.preventDefault();
$(document).unbind("keydown.listenForHotkeys");
cb();
}
}
}
});
}
function metacritic_hoverInfo(url, cb, errorcb) {
// Get the metacritic hover info. Requests are cached.
var handleresponse = function(response, cached) {
if(response.status == 200 && cb) {
if(~response.responseText.indexOf('"jsonRedirect"')) { // {"viewer":{},"mixpanelToken":"6e219fd....","mixpanelDistinctId":"255.255.255.255","omnitureDebug":0,"jsonRedirect":"\/movie\/national-lampoons-vacation"}
var j = JSON.parse(response.responseText);
current.url = baseURL + j["jsonRedirect"];
metacritic_hoverInfo(baseURL + j["jsonRedirect"], cb, errorcb);
} else {
cb(response.responseText, new Date(response.time));
}
} else if(response.status != 200 && errorcb) {
errorcb(response.responseText, new Date(response.time));
if(!cached)
console.log("Show metacritic ratings: Error:"+response.status+"\n"+url);
}
};
var cache = JSON.parse(GM_getValue("hovercache","{}"));
for(var prop in cache) {
// Delete cached values, that are older than 2 hours
if((new Date()).getTime() - (new Date(cache[prop].time)).getTime() > 2*60*60*1000) {
delete cache[prop];
}
}
if(url in cache) {
handleresponse(cache[url], true);
} else {
GM_xmlhttpRequest({
method: "POST",
url: url,
data: "hoverinfo=1",
headers: {
"Referer" : url,
"Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
"Host" : "www.metacritic.com",
"User-Agent" : "MetacriticUserscript "+navigator.userAgent,
"X-Requested-With" : "XMLHttpRequest"
},
onload: function(response) {
response.time = (new Date()).toJSON();
cache[url] = response;
GM_setValue("hovercache",JSON.stringify(cache));
handleresponse(response, false);
}
});
}
}
function metacritic_searchResults(url, cb, errorcb) {
// Get metacritic search results. Requests are cached.
var handleresponse = function(response, cached) {
if(response.results.length && cb) {
cb(response.results, new Date(response.time));
} else if(response.results.length == 0 && errorcb) {
errorcb(response.results, new Date(response.time));
}
};
var cache = JSON.parse(GM_getValue("searchcache","{}"));
for(var prop in cache) {
// Delete cached values, that are older than 2 hours
if((new Date()).getTime() - (new Date(cache[prop].time)).getTime() > 2*60*60*1000) {
delete cache[prop];
}
}
if(url in cache) {
handleresponse(cache[url], true);
} else {
GM_xmlhttpRequest({
method: "GET",
url: url,
headers: {
"Referer" : url,
"Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
"Host" : "www.metacritic.com",
"User-Agent" : "MetacriticUserscript "+navigator.userAgent,
},
onload: function(response) {
var results = [];
if(!~response.responseText.indexOf("No search results found.")) {
var d = $('').html(response.responseText);
d.find("ul.search_results.module .result").each(function() {
results.push(this.innerHTML);
});
}
response = {
time : (new Date()).toJSON(),
results : results,
};
cache[url] = response;
GM_setValue("searchcache",JSON.stringify(cache));
handleresponse(response, false);
},
onerror: function(response) {
alert(response.responseText);
console.log("Show metacritic ratings: Search error: "+response.status+"\n"+url);
handleresponse({
time : (new Date()).toJSON(),
results : [],
}, false);
}
});
}
}
function metacritic_showHoverInfo(url) {
if(!url) {
return;
}
metacritic_hoverInfo(url,
// On Success
function(html, time) {
$("#mcdiv123").remove();
var div = $('').appendTo(document.body);
div.css({
position:"fixed",
bottom :0,
left: 0,
minWidth: 300,
backgroundColor: "#fff",
border: "2px solid #bbb",
borderRadius:" 6px",
boxShadow: "0 0 3px 3px rgba(100, 100, 100, 0.2)",
color: "#000",
padding:" 3px",
zIndex: "5010001",
});
// Functions for communication between page and iframe
// Mozilla can access parent.document
// Chrome can use postMessage()
var functions = {
"other" : {
"parent": function() {},
"frame" : function sizecorrection() {
var f = parent.document.getElementById('mciframe123');
for(var i =0; f.clientHeight < document.body.scrollHeight && i < 100; i++) {
f.style.width = parseInt(f.style.width)+10+"px";
}
if(f.clientHeight < document.body.scrollHeight) {
f.style.height = parseInt(f.style.height)+15+"px";
f.style.width = "300px";
sizecorrection();
}
}
},
"chrome" : {
"parent" : function() {
var f = parent.document.getElementById('mciframe123');
window.addEventListener("message", function(e){
if("mcimessage1" in e.data) {
f.style.width = parseInt(f.style.width)+10+"px";
} else if("mcimessage2" in e.data) {
f.style.height = parseInt(f.style.height)+15+"px";
f.style.width = "300px";
} else {
return;
}
f.contentWindow.postMessage({
"mcimessage3" : true,
"mciframe123_clientHeight" : f.clientHeight,
"mciframe123_clientWidth" : f.clientWidth,
},'*');
});
},
"frame" : function() {
var i = 0;
window.addEventListener("message", function(e){
if(!("mcimessage3" in e.data)) return;
if(e.data.mciframe123_clientHeight < document.body.scrollHeight && i < 100) {
parent.postMessage({"mcimessage1":1},'*');
i++;
}
if(i >= 100) {
parent.postMessage({"mcimessage2":1},'*')
i = 0;
}
});
parent.postMessage({"mcimessage1":1},'*');
}
}
};
var framesrc = 'data:text/html,';
framesrc += encodeURIComponent('\
\
\
\