- //{
- // Common functions for working on lists of titles, loading them, highlighting
- // titles based on these lists.
- //
- // Copyright (c) 2019, Guido Villa
- // Most of the code is taken from IMDb 'My Movies' enhancer:
- // Copyright (c) 2008-2018, Ricardo Mendonça Ferreira (ric@mpcnet.com.br)
- // Released under the GPL license - http://www.gnu.org/copyleft/gpl.html
- //
- // --------------------------------------------------------------------
- //
- // ==UserScript==
- // @name TitleList
- // @description Common functions for working on lists of titles
- // @namespace https://greasyfork.org/en/scripts/390248-titlelist
- // @updateURL about:blank
- // @homepageURL https://greasyfork.org/en/scripts/390248-titlelist
- // @copyright 2019, Guido Villa
- // @license GPL-3.0-or-later
- // @oujs:author Guido
- // @date 18.09.2019
- // @version 0.1
- // ==/UserScript==
- //
- // To-do (priority: [H]igh, [M]edium, [L]ow):
- // - [H] Everything: extract functions from Netflix Hide Titles script and insert them below
- //
- // History:
- // --------
- // 2019.09.18 [0.1] First test version, private use only
- //
- //}
-
- /*jshint esversion: 6 */
-
- const TITLELIST_Version = '0.1';
-
- // FUNCTIONS *************************************************************************************************************
-
- function TL() {
- 'use strict';
- var mainContext;
-
- /* XXX var private_stuff = function() { // Only visible inside Restaurant()
- myPrivateVar = "I can set this here!";
- }*/
-
- this.getLoggedUser = function(ctx) {
- //
- // Return name of user currently logged on <ctx> site
- // Return last saved value and log error if no user is found
- //
- var user = ctx.getUser();
-
- if (!user) {
- console.error(ctx.name + ": user not logged in (or couldn't get user info) on URL " + document.URL);
- user = GM_getValue(ctx.name + '-lastUser', '');
- console.error("Using last user: " + user);
- }
- GM_setValue(ctx.name + '-lastUser', user);
- ctx.user = user;
- return user;
- };
-
-
- function loadSavedList(listName) {
- //
- // Load a single saved lists
- //
- var list;
- var userData = GM_getValue(listName, null);
- if (userData) {
- try {
- list = JSON.parse(userData);
- } catch(err) {
- alert("Error loading saved list named '" + listName + "'!\n" + err.message);
- }
- }
- return list;
- }
-
-
- this.loadSavedLists = function(ctx) {
- //
- // Load lists saved for the current user
- //
- var lists = {};
-
- var listNames = loadSavedList('TitleLists-' + ctx.user);
- if (!listNames) return lists;
-
- for (var listName in listNames) {
- lists[listName] = loadSavedList('TitleList-' + ctx.user + '-' + listName);
- }
- return lists;
- };
-
-
- this.saveList = function(ctx, list, name) {
- //
- // Save single list for the current user
- //
- var listNames = loadSavedList('TitleLists-' + ctx.user);
- if (!listNames) listNames = {};
-
- listNames[name] = 1;
- var userData = JSON.stringify(listNames);
- GM_setValue('TitleLists-' + ctx.user, userData);
-
- userData = JSON.stringify(list);
- GM_setValue('TitleList-' + ctx.user + '-' + name, userData);
- };
-
-
- this.manageTitlePage = function(ctx) {
- var we_are_in_a_title_page = ctx.isTitlePage(document);
- if (!we_are_in_a_title_page) return;
-
- // find current logged in user, or quit script
- if (!getLoggedUser(ctx)) return;
-
- mainContext = ctx;
-
- // Load lists data for this user from local storage
- ctx.allLists = loadSavedLists(dest);
-
- // start the title processing function
- processTitles(ctx);
- if (ctx.interval >= 100) {
- ctx.timer = setInterval(function() {processTitles(ctx);}, ctx.interval);
- }
- };
-
-
- this.inLists = function(ctx, tt, entry) {
- //
- // Receives a title (and corresponding entry) and finds all lists title is in.
- // Argument "entry" is for "virtual" lists determined by attributes in the DOM
- //
- var lists = ( ctx.getListsFromEntry && ctx.getListsFromEntry(tt, entry) || {} );
-
- for (var list in ctx.allLists) {
- if (ctx.allLists[list][tt.id]) lists[list] = true;
- }
-
- return lists;
- };
-
-
- this.processTitles = function(ctx) {
- //
- // Process all title cards in current page
- //
- var entries = ctx.getTitleEntries(document);
- if (!entries) return;
-
- var entry, tt, lists, processingType;
- for (var i = 0; i < entries.length; i++) {
- entry = entries[i];
-
- // if entry has already been previously processed, skip it
- if (entry.TLProcessed) continue;
-
- tt = ctx.getIdFromEntry(entry);
- if (!tt) continue;
-
- ctx.modifyEntry(entry);
- lists = inLists(tt, entry);
-
- processingType = ctx.determineType(lists, tt, entry);
-
- if (processingType) {
- ctx.processItem(entry, tt, processingType);
- entry.TLProcessingType = processingType;
- }
-
- entry.TLProcessed = true; // set to "true" after processing (so we skip it on next pass)
- }
- };
-
-
- this.toggleTitle = function(evt) {
- var data = evt.target.dataset;
-
- // get title entry
- var entry = evt.target;
- if (Number.isInteger(Number(data.howToFindEntry))) {
- for (var i = 0; i < Number(data.howToFindEntry); i++) entry = entry.parentNode;
- } else {
- entry = entry.closest(data.howToFindEntry);
- }
-
- var tt = mainContext.getIdFromEntry(entry);
- if (!tt) return;
-
- // check if item is in list
- var list = mainContext.allLists[data.toggleList];
- if (list[tt.id]) {
- delete list[tt.id];
- mainContext.unProcessItem(entry, tt, data.toggleType);
- entry.TLProcessingType = "-" + data.toggleType;
- } else {
- list[tt.id] = tt.title;
- mainContext.processItem(entry, tt, data.toggleType);
- entry.TLProcessingType = data.toggleType;
- }
- saveList(mainContext, list, data.toggleList);
- };
-
-
-
- this.addToggleEventOnClick = function(button, toggleType, toggleList, howToFindEntry) {
- button.dataset.toggleType = toggleType;
- button.dataset.toggleList = toggleList;
- button.dataset.howToFindEntry = howToFindEntry;
- button.addEventListener('click', toggleTitle, false);
- };
-
-
- }
-
-
-
-
-
-
-
-
-
- function xxx() {
- var WATCHLIST = "watchlist";
- var RATINGLIST = "ratings";
- var CHECKINS = "checkins";
-
- var TITLES = "Titles";
- var PEOPLE = "People";
- var IMAGES = "Images";
- // Lists can be about Titles, People & Images (no Characters lists anymore?)
- // Comment out a list type to disable highlighting for it.
- var listTypes = {};
- listTypes[TITLES] = true;
- listTypes[PEOPLE] = true;
- //listTypes[IMAGES] = true; // To-do: highlight images using colored borders?
-
- var listOrderIdx = [];
-
- var myLists = [];
- var neededLists = {}; //GUIDO NF
-
-
- // Modified version of Michael Leigeber's code, from:
- // http://sixrevisions.com/tutorials/javascript_tutorial/create_lightweight_javascript_tooltip/
- // http://userscripts.org/scripts/review/91851 & others
- var injectJs = 'function tooltipClass(msg) {this.msg = msg;this.id = "tt";this.top = 3;this.left = 15;this.maxw = 500;this.speed = 10;this.timer = 20;this.endalpha = 95;this.alpha = 0;this.tt == null;this.c;this.h = 0;this.moveFunc = null;this.fade = function (d) {var a = this.alpha;if (a != this.endalpha && d == 1 || a != 0 && d == -1) {var i = this.speed;if (this.endalpha - a < this.speed && d == 1) {i = this.endalpha - a;} else if (this.alpha < this.speed && d == -1) {i = a;}this.alpha = a + i * d;this.tt.style.opacity = this.alpha * 0.01;} else {clearInterval(this.tt.timer);if (d == -1) {this.tt.style.display="none";document.removeEventListener("mousemove", this.moveFunc, false);this.tt = null;}}};this.pos = function (e, inst) {inst.tt.style.top = e.pageY - inst.h + "px";inst.tt.style.left = e.pageX + inst.left + "px";};this.show = function (msg) {if (this.tt == null) {this.tt = document.createElement("div");this.tt.setAttribute("id", this.id);c = document.createElement("div");c.setAttribute("id", this.id + "cont");this.tt.appendChild(c);document.body.appendChild(this.tt);this.tt.style.opacity = 0; this.tt.style.zIndex=100000; var inst = this;this.moveFunc = function (e) {inst.pos(e, inst);};document.addEventListener("mousemove", this.moveFunc, false);}this.tt.style.display = "block";c.innerHTML = msg || this.msg;this.tt.style.width = "auto";if (this.tt.offsetWidth > this.maxw) {this.tt.style.width = this.maxw + "px";}h = parseInt(this.tt.offsetHeight) + this.top;clearInterval(this.tt.timer);var inst = this;this.tt.timer = setInterval(function () {inst.fade(1);}, this.timer);};this.hide = function () {if (this.tt) {clearInterval(this.tt.timer);var inst = this;this.tt.timer = setInterval(function () {inst.fade(-1);}, this.timer);}};} tooltip = new tooltipClass("default txt");';
-
- var newJs = document.createElement('script');
- newJs.setAttribute('type', 'text/javascript');
- newJs.innerHTML = injectJs;
- document.getElementsByTagName('head')[0].appendChild(newJs);
-
- var myName = 'Netflix hide titles'; // Name & version of this script
- var user = ''; // Current user name/alias
- var IMDbUser = '';
- var interval = 1000; // Interval (in ms, >= 100) to re-scan links in the DOM
- // Won't re-scan if < 100
- // (I might consider using MutationObserver in the future, instead)
-
- function getCurrentNetflixUser() {
- //
- // Return name of user currently logged on IMDb (log on console if failed)
- //
- var loggedUser = null;
-
- var account = document.querySelector('div.account-menu-item div.account-dropdown-button > a');
- if (account) {
- var accountString = account.getAttribute("aria-label");
- if (accountString) {
- loggedUser = accountString.replace(/ - Account & Settings$/, '');
- if (loggedUser == accountString) loggedUser == null;
- }
- }
- if (!loggedUser) {
- console.error(document.URL + "\nUser not logged in (or couldn't get user info)"); // responseDetails.responseText
- loggedUser = GM_getValue('NetflixHide_lastUser', '');
- console.error("Using last user: " + loggedUser);
- }
- GM_setValue("NetflixHide_lastUser", loggedUser);
- return loggedUser;
- }
-
- function getCurrentIMDbUser() {
- //
- // Return name of user currently logged on IMDb (log on console if failed)
- //
- var loggedIn = '';
- var account = document.getElementById('consumer_user_nav') ||
- document.getElementById('nbpersonalize');
- if (account) {
- var result = account.getElementsByTagName('strong');
- if (!result.length) result = account.getElementsByClassName("navCategory");
- if (!result.length) result = account.getElementsByClassName("singleLine");
- if (!result.length) result = account.getElementsByTagName("p");
- if (result)
- loggedIn = result[0].textContent.trim();
- }
- if (!loggedIn)
- console.error(document.URL + "\nUser not logged in (or couldn't get user info)"); // responseDetails.responseText
- return loggedIn;
- }
-
- var myLocalList = {};
- var myNetflixList = {};
-
- function loadMyLocalList() {
- //
- // Load data for the current user
- //
- var userData = GM_getValue("NetflixHideList-"+user, null);
- if (userData) {
- try {
- myLocalList = JSON.parse(userData);
- return true;
- } catch(err) {
- alert("Error loading Netflix local data!\n" + err.message);
- }
- }
- }
-
- function loadMyNetflixList() {
- //
- // Load data for the current user
- //
- var userData = GM_getValue("NetflixMyList-"+user, null);
- if (userData) {
- try {
- myNetflixList = JSON.parse(userData);
- return true;
- } catch(err) {
- alert("Error loading Netflix My List data!\n" + err.message);
- }
- }
-
- return false;
- }
-
- function getMyIMDbLists() {
- //
- // Get all lists (name & id) for current user into myLists array
- // and set default colors for them (if not previously defined)
- //
-
- // You can customize your lists colors.
- // See also the listOrder variable below.
- // After any change in the code: save the script, reload the lists page,
- // clear the highlight data and refresh the highlight data!
- var customColors = [];
- customColors["Your Watchlist"] = "DarkGoldenRod";
- customColors["Your ratings" ] = "Green";
- customColors["Your check-ins"] = "DarkGreen";
- //GUIDO customColors["DefaultColor" ] = "DarkCyan";
- customColors["DefaultColor" ] = "Maroon";
- customColors["DefaultPeople" ] = "DarkMagenta";
- //GUIDO customColors["Filmes Netflix Brasil"] = "Red";
- customColors["Visti"] = "seagreen";
- customColors["Parzialmente visti"] = "yellowgreen";
- customColors["no"] = "darkgrey";
-
- // You can set the search order for the highlight color when a title is in multiple lists.
- // The script will choose the color of the the first list found in the variable below.
- // Uncomment the line below and enter the names of any lists you want to give preference over the others.
- var listOrder = ["Your Watchlist", "Your ratings"];
-
- myLists.length = 0; // Clear arrays and insert the two defaults
- myLists.push({"name":"Your Watchlist", "id":WATCHLIST, "color":customColors["Your Watchlist"] || "", "ids":{}, "type":TITLES });
- myLists.push({"name":"Your ratings", "id":RATINGLIST, "color":customColors["Your ratings"] || "", "ids":{}, "type":TITLES });
- myLists.push({"name":"Your check-ins", "id":CHECKINS, "color":customColors["Your check-ins"] || "", "ids":{}, "type":TITLES });
- var lists = document.getElementsByClassName('user-list');
- if (!lists || lists.length < 1) {
- console.error("Error getting lists (or no lists exist)!");
- return false;
- }
- for (var i = 0; i < lists.length; i++) {
- var listType = lists[i].getAttribute("data-list-type");
- if (listType in listTypes) {
- var tmp = lists[i].getElementsByClassName("list-name");
- if (!tmp) {
- console.error("Error reading information from list #"+i);
- continue;
- }
- tmp = tmp[0]; // <a class="list-name" href="/list/ls003658871/">Filmes Netflix Brasil</a>
- var name = tmp.text;
- var id = tmp.href.match(/\/list\/([^\/\?]+)\/?/)[1];
- var colorType = listType == PEOPLE ? "DefaultPeople" : "DefaultColor";
- var color = customColors[name] || customColors[colorType] || "";
- myLists.push({"name":name, "id":id, "color":color, "ids":{}, "type":listType });
- }
- }
- setListOrder(listOrder);
- return true;
- }
-
- function loadMyIMDbLists() {
- //
- // Load data for the current user
- //
- // var userData = localStorage.getItem("myMovies-"+user); // GUIDO NF
- var userData = GM_getValue("myIMDbMovies-"+user, null); // GUIDO NF
- if (userData) {
- try {
- myLists = JSON.parse(userData);
- if ("myLists" in myLists) {
- listOrderIdx = myLists["listOrder"];
- myLists = myLists["myLists" ];
-
- // GUIDO NF
- for (var i = 0; i < myLists.length; i++) {
- if (myLists[i].type != TITLES) continue;
- switch (myLists[i].name) {
- case 'no': neededLists.no = i; break;
- case 'Visti': neededLists.visti = i; break;
- case 'Your Watchlist': neededLists.watch = i; break;
- case 'tbd': neededLists.tbd = i; break;
- }
- }
- // FINE GUIDO NF
- }
- return true;
- } catch(err) {
- alert("Error loading previous data!\n" + err.message);
- }
- }
- return false;
- }
-
- function saveMyLocalList() {
- //
- // Save data for the current user
- //
- var userData = JSON.stringify(myLocalList);
- GM_setValue("NetflixHideList-"+user, userData);
- }
-
- function saveMyIMDbLists() {
- //
- // Save data for the current user
- //
- var userData = JSON.stringify(myLocalList);
- GM_setValue("NetflixHideList-"+user, userData);
-
- userData = {"listOrder": listOrderIdx, "myLists": myLists};
- userData = JSON.stringify(userData);
- GM_setValue("myIMDbMovies-"+user, userData);
- }
-
- function getIdFromDiv(div) {
- var a = div.querySelector('a[href^="/watch/"]');
- var tt = null;
- if (a) {
- tt = a.href.match(/\/watch\/([^/?&]+)[/?&]/);
- if (tt && tt.length >= 2) tt = tt[1];
- }
- if (!tt) console.error('Could not determine title id :-(');
- return tt;
- }
-
- function toggleTitle(evt) {
- // get title id
- var div = evt.target.parentNode.parentNode;
- var tt = getIdFromDiv(div);
-
- // check if item is in list
- if (myLocalList[tt]) {
- delete myLocalList[tt];
- showItem(div, tt);
- } else {
- var movie = div.querySelector("div.fallback-text");
- var movieTitle = '';
- if (movie) movieTitle = movie.innerText;
- if (!movieTitle) movieTitle = tt;
- myLocalList[tt] = movieTitle;
- hideItem(div, tt, movieTitle);
- }
- saveMyLocalList();
- console.log('TOGGLE: ' + tt + ', t: ' + movieTitle);
- }
-
-
- var hideTypes = {
- "H": { "name": 'Hidden', "colour": 'white' },
- "D": { "name": 'Disliked', "colour": 'black' },
- "W": { "name": 'Watchlist', "colour": 'darkgoldenrod', "visible": true },
- "T": { "name": 'TBD', "colour": 'Maroon', "visible": true },
- "S": { "name": 'Watched', "colour": 'seagreen' },
- "N": { "name": 'NO', "colour": 'darkgrey' },
- "M": { "name": 'My list', "colour": 'yellow' },
- "MISSING": { "name": 'Hide type not known', "colour": 'red' },
- };
-
-
- function hideItem(div, id, title, hideType) {
- //console.log('hideItem', id, title, hideType);
- if (!hideType) hideType = 'H';
-
- if (!hideTypes[hideType]) hideType = 'MISSING';
- var triangle = document.createElement('div');
- triangle.className = 'NHT-triangle'
- triangle.style.cssText =
- 'border-right: 20px solid ' + hideTypes[hideType].colour + '; ' +
- 'border-bottom: 20px solid transparent;' +
- 'height: 0; ' +
- 'width: 0; ' +
- 'position: absolute; ' +
- 'top: 0; ' +
- 'right: 0; ' +
- 'z-index: 2;'
- triangle.title = hideTypes[hideType].name;
- div.parentNode.appendChild(triangle);
-
- if (!hideTypes[hideType].visible) div.parentNode.style.opacity = .1;
- /*
- var parent = div.parentNode;
- parent.parentNode.style.width = '5%';
-
- var field = parent.querySelector('fieldset#hideTitle' + id);
- if (!field) {
- field = document.createElement('fieldset');
- field.id = 'hideTitle' + id;
- field.style.border = 0;
- field.appendChild(document.createTextNode(title));
- parent.appendChild(field);
- } else {
- field.style.display = 'block';
- }
- */
- }
-
- function showItem(div, id) {
- div.parentNode.style.opacity = 1;
- var triangle = div.parentNode.querySelector('.NHT-triangle');
- if (triangle) triangle.parentNode.removeChild(triangle);
- /*
- div.parentNode.parentNode.style.width = null;
- div.parentNode.querySelector('fieldset#hideTitle' + id).style.display = 'none';
- */
- }
-
- /* FROM IMDB MY MOVIES ENHANCER */
- function eraseMyData() {
- //
- // Erase just the movies and lists information for the user
- //
- // localStorage.removeItem("myMovies-"+user); // GUIDO NF
- GM_deleteValue("myMovies-"+user); // GUIDO NF
- for (var i = 0; i < myLists.length; i++)
- myLists[i].ids = {};
- }
-
- function parseCSV(str) {
- // Simple CSV parsing function, by Trevor Dixon:
- // https://stackoverflow.com/questions/1293147/javascript-code-to-parse-csv-data
- var arr = [];
- var quote = false; // true means we're inside a quoted field
-
- // iterate over each character, keep track of current row and column (of the returned array)
- var row, col, c;
- for (row = col = c = 0; c < str.length; c++) {
- var cc = str[c], nc = str[c+1]; // current character, next character
- arr[row] = arr[row] || []; // create a new row if necessary
- arr[row][col] = arr[row][col] || ''; // create a new column (start with empty string) if necessary
-
- // If the current character is a quotation mark, and we're inside a
- // quoted field, and the next character is also a quotation mark,
- // add a quotation mark to the current column and skip the next character
- if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; }
-
- // If it's just one quotation mark, begin/end quoted field
- if (cc == '"') { quote = !quote; continue; }
-
- // If it's a comma and we're not in a quoted field, move on to the next column
- if (cc == ',' && !quote) { ++col; continue; }
-
- // If it's a newline (CRLF) and we're not in a quoted field, skip the next character
- // and move on to the next row and move to column 0 of that new row
- if (cc == '\r' && nc == '\n' && !quote) { ++row; col = 0; ++c; continue; }
-
- // If it's a newline (LF or CR) and we're not in a quoted field,
- // move on to the next row and move to column 0 of that new row
- if (cc == '\n' && !quote) { ++row; col = 0; continue; }
- if (cc == '\r' && !quote) { ++row; col = 0; continue; }
-
- // Otherwise, append the current character to the current column
- arr[row][col] += cc;
- }
- return arr;
- }
-
- var downloadedLists = 0;
- var listsNotDownloaded = [];
-
-
- function advanceProgressBar() {
- //
- // Update progress bar
- //
- downloadedLists += 1;
- var total = myLists.length;
- var p = Math.round(downloadedLists*(100/total));
- updateProgressBar(p, "Loaded "+downloadedLists+"/"+total);
- if (downloadedLists >= total) {
- updateProgressBar(0, "");
- if (listsNotDownloaded.length > 0) {
- var msg = "Done, but could not load list(s):";
- listsNotDownloaded.forEach(function(l) { msg += "\n * " + l;} );
- msg += "\n\nThis script can only read public lists.";
- alert(msg);
- } else
- alert("OK, we're done!");
- }
- }
-
- function downloadOK(idx, request, link) {
- //
- // Process a downloaded list
- //
- if (request.status != 200) {
- console.error("Error "+request.status+" downloading "+link+": " + request.statusText);
- } else
- if (request.responseText.indexOf("<!DOCTYPE html") >= 0) {
- console.error("Received HTML instead of CSV file from "+link);
- } else {
- var data = parseCSV(request.responseText);
- var res, entryCode;
- var fields = {};
- var type = myLists[idx].type;
- for (var i=1; i < data.length; i++) {
- if (type == TITLES) {
- // ___0___ _____1_____ ____2_____ ___3____ _____4_____ ____5_____ _____6_____ ______7_______ _____8_____ _______9______ ____10___ _____11_____ ___12____ _____13_____ ____14____
- // ratings : Const, Your Rating, Date Added, Title, URL, Title Type, IMDb Rating, Runtime (mins), Year, Genres, Num Votes, Release Date, Directors
- // others : Position, Const, Created, Modified, Description, Title, URL, Title Type, IMDb Rating, Runtime (mins), Year, Genres, Num Votes, Release Date, Directors
- for (var f=0; f < data[0].length; f++)
- { fields[data[0][f]] = data[i][f]; }
- // var tt = fields["Const"]; // GUIDO NF
- var tt = fields["Title"]; // GUIDO NF
- var ratingMine = fields["Your Rating"];
- var ratingIMDb = fields["IMDb Rating"];
- if (typeof tt === "undefined") console.error("Error processing line "+i+" of "+idx);
- // else if (tt.substr(0,2) != 'tt') console.error("Error getting IMDb const from: "+data[i]); // GUIDO NF
- else {
- // var ttNum = parseInt(tt.substr(2)); // GUIDO NF
- // Encode the movie number with "base 36" to save memory
- // entryCode = ttNum.toString(36); // GUIDO NF
- entryCode = tt; // GUIDO NF
- myLists[idx].ids[entryCode] = {m:ratingMine, i:ratingIMDb};
- }
- } else if (type == PEOPLE) {
- // ___0___ __1__ ___2___ ___3____ _____4_____ __5__ ____6____ ____7_____
- // Position, Const, Created, Modified, Description, Name, Known For, Birth Date
- for (var f=0; f < data[0].length; f++)
- { fields[data[0][f]] = data[i][f]; }
- var nm = fields["Const"];
- //var name = fields["Name"];
- if (typeof nm === "undefined") console.error("Error processing line "+i+" of "+idx);
- else if (nm.substr(0,2) != 'nm') console.error("Error getting IMDb const from: "+data[i]);
- else {
- var nmNum = parseInt(nm.substr(2));
- // Encode the entry with "base 36" to save memory
- entryCode = nmNum.toString(36);
- //myLists[idx].ids[entryCode] = {n: name};
- myLists[idx].ids[entryCode] = {};
- }
- } else if (type == IMAGES) {
- // Do nothing for now
- }
- }
- // Save data into browser
- saveMyIMDbLists();
- }
-
- advanceProgressBar() ;
-
- // Try to free some memory
- delete request.responseText;
- }
-
- var createFunction = function( func, p1, p2, p3 ) {
- return function() {
- func(p1, p2, p3);
- };
- };
-
- function downloadError(name, request, link) {
- //
- // Alert user about a download error
- //
- var msg = "Error downloading your list "+name+":\n"+
- "Status: " +request.status + " - " + request.statusText +":\n"+
- "Source: " +link +"\n" +
- "Headers: " +request.getAllResponseHeaders();
- alert(msg);
- console.error(msg);
- updateProgressBar(0, "");
- }
-
- function downloadAsync(name, idx, exportLink) {
- var request = new XMLHttpRequest();
- request.onload = createFunction(downloadOK, idx, request, exportLink);
- request.onerror = createFunction(downloadError, name, request, exportLink);
- request.open("GET", exportLink, true);
- //request.setRequestHeader("Accept-Encoding","gzip"); // Browser does this already? (I get 'Refused to set unsafe header "Accept-Encoding"')...
- request.send();
- }
-
- function downloadAsyncWatchlist(name, idx, url) {
- var request = new XMLHttpRequest();
- request.onload = function() {
- var exportLink;
- var id = request.responseText.match('<meta property="pageId" content="(ls.+?)"/>');
- if (id && id.length > 1)
- exportLink = document.location.protocol + "//www.imdb.com/list/"+id[1]+"/export";
- else {
- id = request.responseText.match('"list":{"id":"(ls.+?)"');
- if (id && id.length > 1)
- exportLink = document.location.protocol + "//www.imdb.com/list/"+id[1]+"/export";
- }
- if (exportLink)
- downloadAsync(name, idx, exportLink);
- else {
- console.error("Could not find id of the '"+name+"' list! Try to make it public (you can make it private again right after).");
- listsNotDownloaded.push(name);
- advanceProgressBar();
- }
- };
- request.onerror = createFunction(downloadError, name, request, url);
- request.open("GET", url, true);
- request.send();
- }
-
- function downloadList(idx) {
- //
- // Download a list
- //
- var ur = document.location.pathname.match(/\/(ur\d+)/);
- if (ur && ur[1])
- ur = ur[1];
- else {
- alert("Sorry, but I could not find your user ID (required to download your lists). :(");
- return;
- }
-
- var name = myLists[idx].name;
- var id = myLists[idx].id;
- // Watchlist & check-ins are not easily available (requires another fetch to find export link)
- // http://www.imdb.com/user/ur???????/watchlist/export | shows old HTML format
- // http://www.imdb.com/list/export?list_id=watchlist&author_id=ur??????? | 404 error
- // http://www.imdb.com/user/ur???????/watchlist | HTML page w/ "export link" at the bottom
- if (id == WATCHLIST || id == CHECKINS) {
- var url = document.location.protocol + "//www.imdb.com/user/"+ur+"/"+id;
- downloadAsyncWatchlist(name, idx, url);
- } else {
- var exportLink;
- if (id == RATINGLIST)
- exportLink = document.location.protocol + "//www.imdb.com/user/"+ur+"/"+id+"/export";
- else exportLink = document.location.protocol + "//www.imdb.com/list/"+id+"/export";
- downloadAsync(name, idx, exportLink);
- }
- }
-
- function downloadLists() {
- //
- // Begin to download all user lists at once (asynchronously)
- //
- downloadedLists = 0;
- for (var idx=0; idx < myLists.length; idx++)
- downloadList(idx);
- // With 10.000 items in 5 lists, the approx. time to download them (on Chrome 29) was:
- // - synchronously: 1:50s
- // - asynchronously: 30s
- // Results might vary - a lot! - depending on number of lists and browser
- // Connections per hostname seems to be around 6: http://www.browserscope.org/?category=network&v=top
- }
-
- // Really simple progress bar...
- var pb;
- var pbBox;
- var pbTxt;
-
- function createProgressBar(p, msg) {
- var top_ = Math.round(window.innerHeight / 2) -15;
- var left = Math.round(window.innerWidth / 2) -100;
- pbBox = document.createElement('div');
- pbBox.style.cssText = "background-color: white; border: 2px solid black; "+
- "position: fixed; height: 30px; width: 200px; top: "+top_+"px; left: "+left+"px;";
- document.body.appendChild(pbBox);
-
- pb = document.createElement('div');
- pb.style.cssText = "background-color: green; border: none; height: 100%; width: "+p+"%;";
- pbBox.appendChild(pb);
-
- pbTxt = document.createElement('div');
- pbTxt.textContent = msg;
- pbTxt.style.cssText = "text-align: center; margin-top: -25px; font-family: verdana,sans-serif;";
- pbBox.appendChild(pbTxt);
- }
-
- function updateProgressBar(p, msg) {
- if (p <= 0) {
- pbBox.style.display = "none";
- return;
- }
- pbTxt.textContent = msg;
- pb.style.width = p+"%";
- }
-
- function setListOrder(listOrder) {
- //
- // Set color highlight order using lists indices, after variable listOrder (containing lists names).
- //
- if (typeof listOrder == "undefined")
- listOrder = []; // array of lists names
-
- listOrderIdx = []; // array of lists indices
-
- // First add indices set by user in listOrder
- for (var j = 0; j < listOrder.length; j++)
- for (var i = 0; i < myLists.length; i++)
- if (myLists[i].name == listOrder[j]) {
- listOrderIdx.push(i);
- break;
- }
- // Add remaining indices
- for (var i = 0; i < myLists.length; i++)
- if (!listOrderIdx.includes(i))
- listOrderIdx.push(i);
- }
-
-
-
-
- function inLists(num, type) {
- //
- // Receives an IMDb code and return the names of lists containing it.
- // Argument "num" : entry number encoded in base 36
- // Argument "type": optional, if set, limits search to a specific type of list
- //
- var num_l = 0;
- var lists = "";
- var pos = -1;
- var rated = false;
- var imdbRating = "";
- var header = "";
- var movie, name;
- for (var i = 0; i < myLists.length; i++) {
- if (type && myLists[i].type != type)
- continue;
- movie = myLists[i].ids[num];
- if (movie) {
- if (num_l)
- lists += "<br>";
- name = myLists[i].name;
- imdbRating = movie.i;
- if (imdbRating && name == "Your ratings") {
- name = "Your ratings: " + movie.m + " (IMDb: " + imdbRating + ")";
- rated = true;
- }
- lists += name;
- num_l += 1;
- }
- }
- if (imdbRating && !rated)
- imdbRating = "IMDb rating: " + imdbRating + "<br>";
- else imdbRating = "";
- if (num_l == 1)
- header = "<b>In your list:</b><br>";
- else header = "<b>In "+num_l+" of your lists:</b><br>";
-
- return imdbRating + header + '<div style="margin-left: 15px">' + lists + '</div>';
- }
-
- /* END */
-
-
- function addHideBtn(div, func, txt, help) {
- var b = document.createElement('a');
- b.className = "nf-svg-button simpleround";
- b.textContent = txt;
- b.title = help;
- var d = document.createElement('div');
- d.className = "nf-svg-button-wrapper";
- d.style.cssText = 'bottom: 0; position: absolute; z-index: 10';
- d.appendChild(b);
- b.addEventListener('click', func, false);
- div.appendChild(d);
- return d;
- }
-
- function refreshMovieData() {
- alert(myName+"\n\n"+IMDbUser+", I'll get some info from IMDb to be able to highlight your movies,\nplease click [OK] and wait a bit...");
- eraseMyData();
- createProgressBar(0, "Loading 1/"+myLists.length+"...");
- downloadLists();
- }
-
- function eraseNetflixMyListData() {
- GM_deleteValue("NetflixMyList-"+user);
- myNetflixList = {};
- }
-
- function refreshNetflixMyListData() {
- eraseNetflixMyListData();
- var list = {};
-
- var gallery = document.querySelector('div.mainView div.gallery');
- var titles = gallery.querySelectorAll('div.title-card');
-
- for (var i=0; i < titles.length; i++) {
- var a = titles[i];
-
- var tt = getIdFromDiv(a);
- var movie = a.querySelector("div.fallback-text");
- var movieTitle = '';
- if (movie) movieTitle = movie.innerText;
- if (!movieTitle) movieTitle = tt;
- list[tt] = movieTitle;
- }
-
- var userData = JSON.stringify(list);
- GM_setValue("NetflixMyList-"+user, userData);
- alert('Netflix My list saved');
- }
-
- //IMDb
- var btn1; // refresh
- var btn2; // clear
- var btn4; // help
- //Netflix
- var btn8; // refresh
- var btn16; // clear
-
- function btnRefresh() {
- refreshMovieData();
- }
-
- function btnClear() {
- eraseMyData();
- alert(myName+"\n\nDone! Information cleared, so highlighting is now disabled.");
- window.location.reload();
- }
-
- function btnNFRefresh() {
- refreshNetflixMyListData();
- }
-
- function btnNFClear() {
- eraseNetflixMyListData();
- alert(myName+"\n\nDone! Information cleared.");
- window.location.reload();
- }
-
- function btnHelp () {
- alert(myName+"\n\nThis is a user script that:\n"+
- " • highlights links for entries in your lists (e.g., movies, series & people)\n"+
- " • shows in which of your lists an entry is (in a tooltip)\n"+
- "\nIn order to highlight the entries "+
- "in all IMDb pages as fast as possible, we need to download "+
- "the data from your lists into your browser. Unfortunately " +
- "this can be slow, so it is not done automatically. I suggest "+
- "you to update this information at most once a day.\n\n" +
- "[Refresh highlight data] updates the data in your browser.\n" +
- "[Clear highlight data] disables color highlighting.\n"
- );
- }
-
- function addBtn(div, func, txt, help, style) {
- var b = document.createElement('button');
- b.className = "btn";
- if (!style) style = "margin-right: 10px; font-size: 11px;"
- b.style.cssText = style;
- // b.textContent = txt; // GUIDO NF
- b.textContent = 'NF ' + txt; // GUIDO NF
- b.title = help;
- b.addEventListener('click', func, false);
- div.appendChild(b);
- return b;
- }
-
- function addIMDbButtons() {
- var main = document.getElementById("main");
- if (!main)
- console.error('Could not find "main <div>" to insert buttons!');
- else {
- var h1 = main.getElementsByTagName("h1");
- if (h1) {
- var div = document.createElement('div');
- div.className = "aux-content-widget-2";
- div.style.cssText = "margin-top: 10px;";
- btn1 = addBtn(div, btnRefresh, "Refresh highlight data", "Reload information from your lists - might take a few seconds");
- btn2 = addBtn(div, btnClear, "Clear highlight data", "Disable color highlighting of your lists");
- btn4 = addBtn(div, btnHelp, "What's this?", "Click for help on these buttons");
- h1[0].appendChild(div);
- } else console.error('Could not find "<h1>Your Lists</h1>" to insert buttons!');
- }
- }
- function addNetflixButtons() {
- var main = document.querySelector('div.mainView');
- if (!main)
- console.error('Could not find "main <div>" to insert buttons!');
- else {
- var div = document.createElement('div');
- var btnStyle = 'margin-left: 20px; margin-bottom: 20px; font-size: 13px; padding: .5em; background: 0 0; color: grey; border: solid 1px grey;';
- btn8 = addBtn(div, btnNFRefresh, "Refresh My List data", "Reload information from your list - might take a few seconds", btnStyle);
- btn16 = addBtn(div, btnNFClear, "Clear My List data", "Empty the data", btnStyle);
- main.appendChild(div);
- }
- }
-
- /*
- function addButtons() {
- var div = document.querySelector(".jawbone-actions");
- if (!div)
- console.error('Could not find "button <div>" to insert buttons!');
- else {
- btn1 = addBtn(div, toggleTitle, "HIDE", "Hide this title");
- }
- }
- */
-
- //-------- "main" --------
- var we_are_in_a_title_page = false;
- var we_are_in_the_imdb_list_page = false;
- var we_are_in_the_netflix_list_page = false;
-
- if (document.location.href.match(/\.netflix\..{2,3}\//)) {
- we_are_in_a_title_page = true;
- }
-
- if (document.location.href == 'https://www.netflix.com/browse/my-list') {
- we_are_in_the_netflix_list_page = true;
- }
- if (document.location.href.match(/\.imdb\..{2,3}\/user\/[^\/]+\/lists/)) {
- we_are_in_the_imdb_list_page = true;
- getMyIMDbLists();
- }
-
- // Find current logged in user, or quit script
- user = getCurrentNetflixUser();
- if (!user) return;
-
-
- // Allow user to manually update his/her lists
- if (we_are_in_the_imdb_list_page) {
- // Find current logged in user, or quit script
- IMDbUser = getCurrentIMDbUser();
- if (!IMDbUser) return; // FIX-ME: to support external sites: set/get LAST user to/from browser storage
-
- addIMDbButtons();
- return; // Nothing else to do on the lists page - goodbye!
- }
- if (we_are_in_the_netflix_list_page) {
- addNetflixButtons();
- }
-
- if (we_are_in_a_title_page) {
- // Load lists data for this user from localStorage
- loadMyLocalList();
- loadMyNetflixList();
- loadMyIMDbLists();
- }
-
-
-
- // THIS IS THE NEW PART
-
-
- function hideTitleCards() {
- // console.log('waitnlp',we_are_in_the_netflix_list_page);
- //
- // Highlight all title cards in the current Netflix page
- //
-
- var num, color, lists, movie;
- var anchors = document.querySelectorAll('div.title-card');
-
- for (var i=0; i < anchors.length; i++) {
- var a = anchors[i];
- if (!a.GVhide) {
- addHideBtn(a, toggleTitle, 'H', 'Hide/show this title');
-
- var tt = getIdFromDiv(a);
- var title, movieTitle;
- var hideType = null;
- if (a.className.indexOf('is-disliked') != -1) hideType = 'D';
- else {
- movie = a.querySelector(".fallback-text");
- if (movie) movieTitle = movie.innerText;
-
- if (movieTitle) {
- num = movieTitle;
- //lists = inLists(num, TITLES);
- if (myLists[neededLists.watch].ids[num]) {
- hideType = 'W';
- } else if (myLists[neededLists.tbd].ids[num]) {
- hideType = 'T';
- } else if (myLists[neededLists.visti].ids[num]) {
- hideType = 'S';
- } else if (myLists[neededLists.no].ids[num]) {
- hideType = 'N';
- }
- }
- }
- if (!hideType && (title = myLocalList[tt])) hideType = 'H';
- if ((!hideType || hideType == 'W') && !we_are_in_the_netflix_list_page && (title = myNetflixList[tt])) {
- // console.log('ht',hideType,'tt',tt,'mt',movieTitle);
- var row = a.closest('div.lolomoRow');
- if (!row || (row.dataset.listContext != 'queue' && row.dataset.listContext != 'continueWatching')) hideType = 'M';
- // console.log('rownull',!row,'lt',row.dataset.listContext);
- }
-
- if (hideType) hideItem(a, tt, title, hideType);
- a.GVhide = true; // set to "true" when "enhanced" (so we skip it on next pass)
- }
- }
- }
-
-
-
- // start the hiding title function
- // if (myLists.length) {
- hideTitleCards();
- if (interval >= 100) setInterval(hideTitleCards, interval);
- // }
-
- }