Greasy Fork is available in English.

Show Letterboxd rating

Show Letterboxd rating on imdb.com, metacritic.com, rottentomatoes.com, BoxOfficeMojo, Amazon, Google Play, allmovie.com, Wikipedia, themoviedb.org, movies.com

Verze ze dne 15. 06. 2019. Zobrazit nejnovější verzi.

// ==UserScript==
// @name        Show Letterboxd rating
// @description Show Letterboxd rating on imdb.com, metacritic.com, rottentomatoes.com, BoxOfficeMojo, Amazon, Google Play, allmovie.com, Wikipedia, themoviedb.org, movies.com
// @namespace   cuzi
// @grant       GM_xmlhttpRequest
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       unsafeWindow
// @grant       GM.xmlHttpRequest
// @grant       GM.setValue
// @grant       GM.getValue
// @require     http://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js
// @require     https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @license     GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt
// @version     1
// @connect     letterboxd.com
// @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://www.serienjunkies.de/*
// @include     https://www.serienjunkies.de/*
// @include     http://www.boxofficemojo.com/movies/*
// @include     https://www.boxofficemojo.com/movies/*
// @include     http://www.allmovie.com/movie/*
// @include     https://www.allmovie.com/movie/*
// @include     https://en.wikipedia.org/*
// @include     http://www.movies.com/*/m*
// @include     https://www.themoviedb.org/movie/*
// @include     http://www.rottentomatoes.com/m/*
// @include     https://www.rottentomatoes.com/m/*
// @include     http://www.metacritic.com/movie/*
// @include     https://www.metacritic.com/movie/*
// @include     https://www.nme.com/reviews/movie/*
// @include     https://itunes.apple.com/*/movie/*
// ==/UserScript==


var baseURL = "https://letterboxd.com"
var baseURL_search = baseURL + "/s/autocompletefilm?q={query}&limit=20&timestamp={timestamp}"
var baseURL_openTab = baseURL + "/search/{query}/";
var baseURL_ratingHistogram = baseURL + "/csi{url}rating-histogram/";

const cacheExpireAfterHours = 4;

function minutesSince(time) {
  let seconds = ((new Date()).getTime() - time.getTime()) / 1000;
  return seconds>60?parseInt(seconds/60)+" min ago":"now";
}

function fixLetterboxdURLs(html) {
  return html.replace(/<a /g,'<a target="_blank" ').replace(/href="\//g,'href="'+baseURL+"/").replace(/src="\//g,'src="'+baseURL+"/");
}

function filterUniversalUrl(url) {
  try {
    url = url.match(/http.+/)[0];
  } catch(e) { }

  try {
    url = url.replace(/https?:\/\/(www.)?/,"");
  } catch(e) { }

  if(url.startsWith("imdb.com/") && url.match(/(imdb\.com\/\w+\/\w+\/)/)) {
     // Remove movie subpage from imdb url
     return url.match(/(imdb\.com\/\w+\/\w+\/)/)[1];
  } else if(url.startsWith("thetvdb.com/")) {
     // Do nothing with thetvdb.com urls
     return url;
  } else if(url.startsWith("boxofficemojo.com/")) {
     // Keep the important id= on
     try {
       var parts = url.split("?");
       var page = url[0] + "?";
       var idparam = url[1].match(/(id=.+?)(\.|&)/)[1];
       return page+idparam;
     } catch(e) {
       return url;
     }
  } else {
    // Default: Remove parameters
    return url.split("?")[0].split("&")[0];
  }
}

function parseLDJSON(keys, condition) {
  if(document.querySelector('script[type="application/ld+json"]')) {
    var data = [];
    var scripts = document.querySelectorAll('script[type="application/ld+json"]');
    for(let i = 0; i < scripts.length; i++) {
      try {
        var jsonld = JSON.parse(scripts[i].innerText);
      } catch(e) {
        continue;
      }
      if(jsonld) {
        if(Array.isArray(jsonld)) {
          data.push(...jsonld)
        } else {
          data.push(jsonld);
        }
      }
    }
    for(let i = 0; i < data.length; i++) {
      try {
        if(data[i] && data[i] && (typeof condition != 'function' || condition(data[i]))) {
          if(Array.isArray(keys)) {
            let r = [];
            for(let j = 0; j < keys.length; j++) {
              r.push(data[i][keys[j]]);
            }
            return r;
          } else if(keys) {
            return data[i][keys];
          } else if(typeof condition === 'function') {
            return data[i]; // Return whole object
          }
        }
      } catch(e) {
        continue;
      }
    }
    return data;
  }
  return null;
}

async function addToWhiteList(letterboxdUrl) {
  let whitelist = JSON.parse(await GM.getValue("whitelist","{}"));
  const docUrl = filterUniversalUrl(document.location.href)
  whitelist[docUrl] = letterboxdUrl
  await GM.setValue("whitelist", JSON.stringify(whitelist));
}

async function removeFromWhiteList() {
  let whitelist = JSON.parse(await GM.getValue("whitelist","{}"));
  const docUrl = filterUniversalUrl(document.location.href)
  if(docUrl in whitelist) {
    delete whitelist[docUrl]
    await GM.setValue("whitelist", JSON.stringify(whitelist));
  }
}

var current = {
  type : null,
  query : null,
  year : null
};


async function searchMovie(query, type, year, forceList) {
  // Load data from letterboxd search API or from cache

  current.type = type;
  current.query = query;
  current.year = year;
  
  
  let whitelist = JSON.parse(await GM.getValue("whitelist","{}"));

  if(forceList) {
    whitelist = {}
  }

  const docUrl = filterUniversalUrl(document.location.href)
  if(docUrl in whitelist) {
    return loadMovieRating({'url' : whitelist[docUrl]})
  }

  const url = baseURL_search.replace("{query}", encodeURIComponent(query)).replace("{timestamp}", encodeURIComponent(Date.now()));

  let cache = JSON.parse(await GM.getValue("cache","{}"));

  // Delete cached values, that are expired
  for(var prop in cache) {
    if((new Date()).getTime() - (new Date(cache[prop].time)).getTime() > cacheExpireAfterHours*60*60*1000) {
      delete cache[prop];
    }
  }

  // Check cache or request new content
  if(url in cache) {
    // Use cached response
    handleSearchResponse(cache[url], forceList);
  } else {
    GM.xmlHttpRequest({
      method: "GET",
      url: url,
      onload: function(response) {
        // Save to chache

        response.time = (new Date()).toJSON();

        // Chrome fix: Otherwise JSON.stringify(cache) omits responseText
        var newobj = {};
        for(var key in response) {
          newobj[key] = response[key];
        }
        newobj.responseText = response.responseText;


        cache[url] = newobj;


        GM.setValue("cache",JSON.stringify(cache));

        handleSearchResponse(response, forceList);
      },
      onerror: function(response) {
        console.log("GM.xmlHttpRequest Error: "+response.status+"\nURL: "+requestURL+"\nResponse:\n"+response.responseText);
      },
    });
  }
}

function handleSearchResponse(response, forceList) {
  // Handle GM.xmlHttpRequest response

  let result = JSON.parse(response.responseText);
  
  if(forceList && (result.result == false || !result.data)) {
    alert("No results for "+current.query);
  } else if(result.result == false || !result.data) {
    console.log("No results for "+current.query);
  } else if(!forceList && result.data.length == 1) {
    loadMovieRating(result.data[0]);
  } else {
    // Sort results by closest match
    function matchQuality(title, year, originalTitle) {
      if(title == current.query && year == current.year) {
        return 105 + year;
      }
      if(originalTitle && originalTitle == current.query && year == current.year) {
        return 104 + year;
      }
      if(title == current.query && current.year) {
        return 103 - Math.abs(year - current.year);
      }
      if(originalTitle && originalTitle == current.query && current.year) {
        return 102 - Math.abs(year - current.year);
      }
      if(title.replace(/\(.+\)/, "").trim() == current.query && current.year) {
        return 101 - Math.abs(year - current.year);
      }
      if(originalTitle && originalTitle.replace(/\(.+\)/, "").trim() == current.query && current.year) {
        return 100 - Math.abs(year - current.year);
      }
      if(title == current.query) {
        return 12;
      }
      if(originalTitle && originalTitle == current.query) {
        return 11;
      }
      if(title.replace(/\(.+\)/, "").trim() == current.query) {
        return 10;
      }
      if(originalTitle && originalTitle.replace(/\(.+\)/, "").trim() == current.query) {
        return 9;
      }
      if(title.startsWith(current.query)) {
        return 8;
      }
      if(originalTitle && originalTitle.startsWith(current.query)) {
        return 7;
      }
      if(current.query.indexOf(title) != -1) {
        return 6;
      }
      if(originalTitle && current.query.indexOf(originalTitle) != -1) {
        return 5;
      }
      if(title.indexOf(current.query) != -1) {
        return 4;
      }
      if(originalTitle && originalTitle.indexOf(current.query) != -1) {
        return 3;
      }
      if(current.query.toLowerCase().indexOf(title.toLowerCase()) != -1) {
        return 2;
      }
      if(title.toLowerCase().indexOf(current.query.toLowerCase()) != -1) {
        return 1;
      }
      return 0;
    }

    result.data.sort(function(a,b) {
      if(!a.hasOwnProperty('matchQuality')) {
        a.matchQuality = matchQuality(a.name, a.releaseYear, a.originalName);
      }
      if(!b.hasOwnProperty('matchQuality')) {
        b.matchQuality = matchQuality(b.name, b.releaseYear, b.originalName);
      }

      return b.matchQuality - a.matchQuality;
    });

    showMovieList(result.data, new Date(response.time));
  }
}


function showMovieList(arr, time) {
  // Show a small box in the right lower corner
  $("#mcdiv321letterboxd").remove();
  let main,div;
  div = main = $('<div id="mcdiv321letterboxd"></div>').appendTo(document.body);
  div.css({
    position:"fixed",
    bottom :0,
    right: 0,
    minWidth: 100,
    maxHeight: "95%",
    overflow: "auto",
    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",
    fontFamily : "Helvetica,Arial,sans-serif"
  });

  var imgFrame = function imgFrameFct(image125, scale) {
    if(!image125) {
      return
    }

    let html = '<iframe sandbox scrolling="no" src="' + baseURL + image125 + '" marginheight="0" marginwidth="0" style="vertical-align:middle; padding:0px; border:none; display:inline; max-width:125px; margin-top:'+(40.0*scale-40.0)+'%; margin-left:'+(40.0*scale-40.0)+'%; transform:scale('+scale+'); transform-origin:bottom right"></iframe> '
    html += '<div style="position:absolute;top:0px;left:0px;width:'+(180.0*scale-45.0)+'px;height:'+(180.0*scale-25)+'px"></div> '
    return html
  }

  // First result
  let first = $('<div style=""><a style="font-size:small; color:#136CB2; " href="' + baseURL + arr[0].url + '">' + imgFrame(arr[0].image125, 0.75) + arr[0].name + (arr[0].originalTitle?' ['+arr[0].originalTitle+']':'') +  " (" + arr[0].releaseYear + ")</a></div>").click(selectMovie).appendTo(main);
  first[0].dataset["movie"] = JSON.stringify(arr[0])

  // Shall the following results be collapsed by default?
  if((arr.length > 1 && arr[0].matchQuality > 10) || arr.length > 10) {
    let a = $('<span style="color:gray;font-size: x-small">More results...</span>').appendTo(main).click(function() { more.css("display", "block"); this.parentNode.removeChild(this); });
    let more = div = $("<div style=\"display:none\"></div>").appendTo(main);
  }

  // More results
  for(let i = 1; i < arr.length; i++) {
    let entry = $('<div style="position:relative"><a style="font-size:small; color:#136CB2; " href="' + baseURL + arr[i].url + '">' + imgFrame(arr[i].image125, 0.5) + arr[i].name + (arr[i].originalTitle?' ['+arr[i].originalTitle+']':'') + " (" + arr[i].releaseYear + ")</a></div>").click(selectMovie).appendTo(div);
    entry[0].dataset["movie"] = JSON.stringify(arr[i])
  }

  // Footer
  let sub = $("<div></div>").appendTo(main);
  $('<time style="color:#789; font-size: 11px;" datetime="'+time+'" title="'+time.toLocaleTimeString()+" "+time.toLocaleDateString()+'">'+minutesSince(time)+'</time>').appendTo(sub);
  $('<a style="color:#789; font-size: 11px;" target="_blank" href="' + baseURL_openTab.replace("{query}", encodeURIComponent(current.query)) + '" title="Open Letterboxd">@letterboxd.com</a>').appendTo(sub);
  $('<span title="Hide me" style="cursor:pointer; float:right; color:#789; font-size: 11px; padding-left:5px;padding-top:3px">&#10062;</span>').appendTo(sub).click(function() {
    document.body.removeChild(this.parentNode.parentNode);
  });

}

function selectMovie(ev) {
  ev.preventDefault()
  $("#mcdiv321letterboxd").html("Loading...")
  
  const data = JSON.parse(this.dataset.movie)

  loadMovieRating(data)

  addToWhiteList(data.url)
}

async function loadMovieRating(data) {
  // Load page from letterboxd

  if("name" in data) {
    current.query = data.name;
  }
  if("releaseYear" in data) {
    current.year = data.releaseYear;
  }

  const url = baseURL_ratingHistogram.replace("{url}", data.url)

  let cache = JSON.parse(await GM.getValue("cache","{}"));

  // Delete cached values, that are expired
  for(var prop in cache) {
    if((new Date()).getTime() - (new Date(cache[prop].time)).getTime() > cacheExpireAfterHours*60*60*1000) {
      delete cache[prop];
    }
  }

  // Check cache or request new content
  if(url in cache) {
    // Use cached response
    showMovieRating(cache[url], data.url);
  } else {
    GM.xmlHttpRequest({
      method: "GET",
      url: url,
      onload: function(response) {
        // Save to chache
        response.time = (new Date()).toJSON();

        // Chrome fix: Otherwise JSON.stringify(cache) omits responseText
        var newobj = {};
        for(var key in response) {
          newobj[key] = response[key];
        }
        newobj.responseText = response.responseText;


        cache[url] = newobj;

        GM.setValue("cache",JSON.stringify(cache));

        showMovieRating(newobj, data.url);
      },
      onerror: function(response) {
        console.log("GM.xmlHttpRequest Error: "+response.status+"\nURL: "+requestURL+"\nResponse:\n"+response.responseText);
      },
    });
  }
}

function showMovieRating(response, letterboxdUrl) {
  // Show a small box in the right lower corner
  const time = new Date(response.time)

  $("#mcdiv321letterboxd").remove();
  let main,div;
  div = main = $('<div id="mcdiv321letterboxd"></div>').appendTo(document.body);
  div.css({
    position:"fixed",
    bottom :0,
    right: 0,
    width: 230,
    minHeight: 44,
    color: "#789",
    padding:" 3px",
    zIndex: "5010001",
    fontFamily : "Helvetica,Arial,sans-serif"
  });
  
  const CSS = `<style>
.rating {
    display: inline-block;
    height: 16px;
    background: url(https://s.ltrbxd.com/static/img/sprite.18bffd5e.svg) no-repeat -290px -90px;
    background-position-x: -290px;
    background-position-y: -90px;
    text-indent: 110%;
    white-space: nowrap;
    overflow: hidden;
}
.rating-green .rating{
    background-position:-450px -50px
}
.rating-green .rated-0{
    width:0
}
.rating-green .rated-1{
    width:13px;
    background-position:-515px -50px
}
.rating-green .rated-2{
    width:12px
}
.rating-green .rated-3{
    width:26px;
    background-position:-502px -50px
}
.rating-green .rated-4{
    width:25px
}
.rating-green .rated-5{
    width:39px;
    background-position:-489px -50px
}
.rating-green .rated-6{
    width:38px
}
.rating-green .rated-7{
    width:52px;
    background-position:-476px -50px
}
.rating-green .rated-8{
    width:51px
}
.rating-green .rated-9{
    width:65px;
    background-position:-463px -50px
}
.rating-green .rated-10{
    width:64px
}
.rating-green-tiny .rating{
    background-position:-350px -380px;
    height:9px
}
.rating-green-tiny .rated-2{
    width:9px
}
.rating-green-tiny .rated-10{
    width:49px
}
.rating-histogram,.rating-histogram ul{
    height:44px
}
.rating-histogram .rating-1,.rating-histogram .rating-5{
    position:absolute;
    bottom:0;
    display:block;
    height:9px
}
.rating-histogram .rating-1 .rating,.rating-histogram .rating-5 .rating{
    display:block
}
.rating-histogram .rating-1{
    left:0
}
.rating-histogram .rating-5{
    right:0
}
.rating-histogram ul{
    width:200px;
    left:15px
}
.rating-histogram ul,.rating-histogram ul li{
    display:block;
    overflow:hidden;
    position:absolute;
    bottom:0
}
.rating-histogram ul li{
    height:1px;
    width:30px;
    height:100%;
    font-size:10px;
    line-height:1;
    text-indent:110%;
    white-space:nowrap;
    border-bottom:0px
}
#mcdiv321letterboxd:hover .rating-histogram ul li{
    border-bottom:2px solid green
}

.rating-histogram i{
    background:#456;
    border-top-right-radius:2px;
    border-top-left-radius:2px
}
.rating-histogram a,.rating-histogram i{
    width:100%;
    position:absolute;
    bottom:0;
    left:0
}
.rating-histogram a{
    display:block;
    top:0;
    right:0;
    background:none;
    padding:0;
		color:#789
}
.rating-histogram a:link,.rating-histogram a:visited{
    color:#789;
		text-decoration:none
}
.rating-histogram a:hover i{
    background-color:#678
}
.ratings-histogram-chart .section-heading{
    margin-bottom:15px
}
.ratings-histogram-chart .average-rating{
    position:absolute;
    top:8px;
    left:188px;
    z-index:1
}
.ratings-histogram-chart .average-rating .display-rating{
    display:block;
    font-size:20px;
    text-align:center;
    color:#789;
    margin-left:1px;
    line-height:40px;
    width:33px;
    height:33px;
    border-radius:20px;
    font-family:Graphik-Light-Web,sans-serif;
    font-weight:400
}
.rating-histogram {
    overflow:hidden;
    color:#9ab;
    display:block
    width: 230px;
    height: 44px;
    position: relative
}
.ratings-histogram-chart .all-link.more-link {
    font-size:10px;
		position:absolute;
    top:0;
    left:180px;
}

#mcdiv321letterboxd .footer {
    display:none;
}

#mcdiv321letterboxd:hover .footer {
    display:block;
}

#mcdiv321letterboxd {
    border:none;
    border-radius: 0px;
    background-color:transparent;
    transition:bottom 0.7s, background-color 0.5s, height 0.5s;
}

#mcdiv321letterboxd:hover {
    border-radius: 4px;
    background-color:rgb(44, 52, 64)
}

</style>`
  
  $(CSS).appendTo(main);
  let section = $(fixLetterboxdURLs(response.responseText)).appendTo(main)
  
  section.find("h2").remove();

  // Footer
  let sub = $('<div class="footer"></div>').appendTo(main);
  $('<span style="color:#789; font-size: 10px">' + current.query + (current.year?' ('+current.year+')':'') + '</span>').appendTo(sub)
  $('<br>').appendTo(sub)
  $('<time style="color:#789; font-size: 11px;" datetime="'+time+'" title="'+time.toLocaleTimeString()+" "+time.toLocaleDateString()+'">'+minutesSince(time)+'</time>').appendTo(sub);
  $('<a style="color:#789; font-size: 11px;" target="_blank" href="' + baseURL+letterboxdUrl + '" title="Open Letterboxd">@letterboxd.com</a>').appendTo(sub);
  $('<span title="Hide me" style="cursor:pointer; float:right; color:#789; font-size: 11px">&#10062;</span>').appendTo(sub).click(function() {
    document.getElementById('mcdiv321letterboxd').remove()
  });
  $('<span title="Wrong movie!" style="cursor:pointer; float:right; color:#789; font-size: 11px">&#128581;</span>').appendTo(sub).click(function() {
    removeFromWhiteList()
    searchMovie(current.query, current.type, current.year, true);
  });
  $('<span style="clear:right">').appendTo(sub)
}


var Always = () => true;
var sites = {
  'googleplay' : {
    host : ["play.google.com"],
    condition : Always,
    products : [
    {
      condition : () => ~document.location.href.indexOf("/movies/details/"),
      type : "movie",
      data : () => document.querySelector("*[itemprop=name]").textContent
    }
    ]
  },
  'imdb' : {
    host : ["imdb.com"],
    condition : () => !~document.location.pathname.indexOf("/mediaviewer") && !~document.location.pathname.indexOf("/mediaindex") && !~document.location.pathname.indexOf("/videoplayer"),
    products : [
    {
      condition : function() {
        let e = document.querySelector("meta[property='og:type']");
        if(e) {
          return e.content == "video.movie"
        }
        return false;
      },
      type : "movie",
      data : function() {
        var year = null;
        var name = null;
        var jsonld = null;
        if(document.querySelector("#titleYear")) {
          year = parseInt(document.querySelector("#titleYear a").firstChild.textContent);
        }
        if(document.querySelector('script[type="application/ld+json"]')) {
           jsonld = parseLDJSON(["name", "datePublished"]);
           name = jsonld[0];
           year = parseInt(jsonld[1].match(/\d{4}/)[0]);
        }
        if(document.querySelector(".originalTitle") && document.querySelector(".title_wrapper h1")) {
           return [document.querySelector(".title_wrapper h1").firstChild.data.trim(), year] // Use English title
        } else if(jsonld) {
          return [name, year]; // Use original title
        } else if(document.querySelector("h1[itemprop=name]")) { // Movie homepage (New design 2015-12)
          return [document.querySelector("h1[itemprop=name]").firstChild.textContent.trim(), year];
        } else if(document.querySelector("*[itemprop=name] a") && document.querySelector("*[itemprop=name] a").firstChild.data) { // Subpage of a move
          return [document.querySelector("*[itemprop=name] a").firstChild.data.trim(), year];
        } else if(document.querySelector(".title-extra[itemprop=name]")) { // Movie homepage: sub-/alternative-/original title
          return [document.querySelector(".title-extra[itemprop=name]").firstChild.textContent.replace(/\"/g,"").trim(), year];
        } else { // Movie homepage (old design)
          return document.querySelector("*[itemprop=name]").firstChild.textContent.trim();
        }
      }
    }
    ]
  },
  'metacritic' : {
    host : ["www.metacritic.com"],
    condition : () => document.querySelector("meta[property='og:type']"),
    products : [{
      condition : () => document.querySelector("meta[property='og:type']").content == "video.movie",
      type : "movie",
      data : function() {
        var year = null;
        if(document.querySelector(".release_year")) {
          year = parseInt(document.querySelector(".release_year").firstChild.textContent);
        } else if(document.querySelector(".release_data .data")) {
          year = document.querySelector(".release_data .data").textContent.match(/(\d{4})/)[1]
        }

        return [document.querySelector("meta[property='og:title']").content, year]
      }
    }]
  },
  'amazon' : {
    host : ["amazon."],
    condition : Always,
    products : [{
      condition : () => document.querySelector('[data-automation-id=title]'),
      type : "movie",
      data : () => document.querySelector('[data-automation-id=title]').textContent.trim()
    }]
  },
  'BoxOfficeMojo' : {
    host : ["boxofficemojo.com"],
    condition : () => ~document.location.search.indexOf("id="),
    products : [{
      condition : () => document.querySelector("#body table:nth-child(2) tr:first-child b"),
      type : "movie",
      data : function() {
        var year = null;
        try {
        var tds = document.querySelectorAll("#body table:nth-child(2) tr:first-child table table table td");
        for(var i = 0; i< tds.length; i++) {
          if(~tds[i].innerText.indexOf("Release Date")) {
            year = parseInt(tds[i].innerText.match(/\d{4}/)[0]);
            break;
          }
        }
        } catch(e) { }
        return [document.querySelector("#body table:nth-child(2) tr:first-child b").firstChild.data, year];
      }
    }]
  },
  'AllMovie' : {
    host : ["allmovie.com"],
    condition : () => document.querySelector("h2[itemprop=name].movie-title"),
    products : [{
      condition : () => document.querySelector("h2[itemprop=name].movie-title"),
      type : "movie",
      data : () => document.querySelector("h2[itemprop=name].movie-title").firstChild.data.trim()
    }]
  },
  'en.wikipedia' : {
    host : ["en.wikipedia.org"],
    condition : Always,
    products : [{
      condition : function() {
        if(!document.querySelector(".infobox .summary")) {
          return false;
        }
        var r = /\d\d\d\d films/;
        return $("#catlinks a").filter((i,e) => e.firstChild.data.match(r)).length;
      },
      type : "movie",
      data : () => document.querySelector(".infobox .summary").firstChild.data
    }]
  },
  'movies.com' : {
    host : ["movies.com"],
    condition : () => document.querySelector("meta[property='og:title']"),
    products : [{
      condition : Always,
      type : "movie",
      data : () => document.querySelector("meta[property='og:title']").content
    }]
  },
  'themoviedb' : {
    host : ["themoviedb.org"],
    condition : () => document.querySelector("meta[property='og:type']"),
    products : [{
      condition : () => document.querySelector("meta[property='og:type']").content == "movie",
      type : "movie",
      data : function() {
        var year = null;
        try {
          year = parseInt(document.querySelector(".release_date").innerText.match(/\d{4}/)[0]);
        } catch(e) {}

        return [document.querySelector("meta[property='og:title']").content, year]
      }
    }]
  },
  'rottentomatoes' : {
    host : ["www.rottentomatoes.com"],
    condition : Always,
    products : [{
      condition : () => document.location.pathname.startsWith("/m/"),
      type : "movie",
      data : () => document.querySelector("h1").firstChild.textContent
    }
    ]
  },
  'nme' : {
    host : ["nme.com"],
    condition : () => document.location.pathname.startsWith("/reviews/"),
    products : [    {
      condition : () => document.location.pathname.startsWith("/reviews/movie/"),
      type : "movie",
      data : function() {
        var year = null;
        try {
          year = parseInt(document.querySelector("*[itemprop=datePublished]").content.match(/\d{4}/)[0])
        } catch(e) {}

        try {
          return [ document.querySelector(".title-primary").textContent.match(/‘(.+?)’/)[1] , year ];
        } catch(e) {
          return [ document.querySelector("h1").textContent.match(/:\s*(.+)/)[1].trim() , year ];
        }
      }
    }]
  },
  'itunes' : {
    host : ["itunes.apple.com"],
    condition : Always,
    products : [{
      condition : () => ~document.location.href.indexOf("/movie/"),
      type : "movie",
      data : () => parseLDJSON("name", (j) => (j["@type"] == "Movie"))
    }]
  }

};


function main() {

  for(var name in sites) {
    var site = sites[name];
    if(site.host.some(function(e) {return ~this.indexOf(e)}, document.location.hostname) && site.condition()) {
      for(var i = 0; i < site.products.length; i++) {
        if(site.products[i].condition()) {
          // Try to retrieve item name from page
          var data;
          try {
            data = site.products[i].data();
          } catch(e) {
            data = false;
            console.log(e);
          }
          if(data) {
            if(Array.isArray(data) && data[1]) {
              searchMovie(data[0].trim(), site.products[i].type, parseInt(data[1]));
            } else {
              searchMovie(data.trim(), site.products[i].type);
            }
          }
          break;
        }
      }
      break;
    }
  }
}

async function adaptForRottentomatoesScript() {
  if(!document.getElementById('mcdiv321rotten') || !document.getElementById('mcdiv321letterboxd')) {
    return
  }
  const h = parseInt(document.getElementById('mcdiv321rotten').clientHeight) + 5
  if(document.getElementById('mcdiv321letterboxd').dataset.adapted && parseInt(document.getElementById('mcdiv321letterboxd').dataset.adapted) == h) {
    return
  }
  const letterboxd = document.getElementById('mcdiv321letterboxd')
  letterboxd.style.bottom = h + 'px'
  document.getElementById('mcdiv321letterboxd').dataset.adapted = h
}

(function() {

  main();
  var lastLoc = document.location.href;
  var lastContent = document.body.innerText;
  var lastCounter = 0;
  function newpage() {
    if(lastContent == document.body.innerText && lastCounter < 15) {
      window.setTimeout(newpage, 500);
      lastCounter++;
    } else {
      lastCounter = 0;
      main();
    }
  }
  window.setInterval(function() {
    adaptForRottentomatoesScript()
    if(document.location.href != lastLoc) {
      lastLoc = document.location.href;
      $("#mcdiv321letterboxd").remove();

      window.setTimeout(newpage,1000);
    }
  },500);

})();