Cpt_mathix / MyAnimeList(MAL) - Anime/Manga Entries Compare

// ==UserScript==
// @name           MyAnimeList(MAL) - Anime/Manga Entries Compare
// @description    This script compares anime and manga entries from your userlists with entries from anime detail page and bold similar ones
// @include        /^https?:\/\/myanimelist\.net\/(anime|manga)\/\d+/
// @include        *://myanimelist.net/(anime|manga).php?id=*
// @exclude        /^https?:\/\/myanimelist\.net\/(anime|manga)\/[^0-9]+/
// @exclude        /^https?:\/\/myanimelist\.net\/(anime|manga)\/\d+\/.+\/.+/
// @version        1.1.7
// @author         Cpt_mathix (this script is inspired on the script of Bastvera)
// @licence        GPL-2.0+; http://www.gnu.org/licenses/gpl-2.0.txt
// @grant          GM_getValue
// @grant          GM_setValue
// @namespace https://greasyfork.org/users/16080
// ==/UserScript==

// get user
var user = document.getElementsByClassName('header-profile-link')[0];
if (user) {
	user = user.textContent;
	init();
} else {
	console.log('Not logged in (Anime/Manga Entries Compare)');
}

var version = "1.1.7";

function init() {
	// get header (shameless copy of bastvera's script)
	var AnchorLink;
	var allTextareas = document.getElementsByTagName('H2');
	for(var element in allTextareas){
		if(/EditRelated/.test(allTextareas[element].textContent))
			AnchorLink = allTextareas[element];
	}
	var feedbackElement;
	if (AnchorLink) {
		feedbackElement = document.createElement('BR');
		AnchorLink.appendChild(feedbackElement);
		feedbackElement = document.createElement('label');
		feedbackElement.setAttribute('for','firstName');
		feedbackElement.appendChild(document.createTextNode('Loading script. Please wait...'));
		AnchorLink.appendChild(feedbackElement);
		feedbackElement.style.fontWeight="normal";
		feedbackElement.style.fontSize="10px";

		activateScript(feedbackElement);
	}
}

function activateScript(feedbackElement) {
	// check if we have/need to refresh our userlist
	var animelist, mangalist;

	if(isListUpToDate(user, ["manga","rm"])) {
		feedbackElement.textcontent = "Processing cached mangalist...";
		console.log('Processing cached mangalist...');
		mangalist = getUserList("manga");
	} else {
		feedbackElement.textcontent = "Downloading mangalist...";
		console.log('Downloading mangalist...');
		mangalist = loadUserList(user, "manga");
	}

	if(isListUpToDate(user, ["anime","rw"])) {
		feedbackElement.textcontent = "Processing cached animelist...";
		console.log('Processing cached animelist...');
		animelist = getUserList("anime");
	} else {
		feedbackElement.textcontent = "Downloading animelist...";
		console.log('Downloading animelist...');
		animelist = loadUserList(user, "anime");
	}

	console.log('Running script: Anime/Manga Entries Compare');

	// get Anime/Manga Entries on current page
	var allElements;
	allElements = document.evaluate(
		'//*[contains(@class, "anime_detail_related_anime")]//a[@href]',
		document,
		null,
		XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
		null);

	for (var i = 0; i < allElements.snapshotLength; i++){
		var linkEl= allElements.snapshotItem(i);
		var href = linkEl.href;

		feedbackElement.textContent = "Comparing entries...";
		// compare Anime Entries with your list
		var self;
		if (/http.*:\/\/myanimelist\.net\/anime\/\d+/.test(href)) {
			var animeid = href.match(/\d+/g)[0];
			self = animeid.search(document.location.href.match(/\d+/g)[0]);
			if (self == -1 && haveListHit(animelist, animeid, "anime")) {
				linkEl.style.fontWeight="bold";
			}
		}

		// compare Manga Entries with your list
		if (/http.*:\/\/myanimelist\.net\/manga\/\d+/.test(href)) {
			var mangaid = href.match(/\d+/g)[0];
			self = mangaid.search(document.location.href.match(/\d+/g)[0]);
			if (self == -1 && haveListHit(mangalist, mangaid, "manga")) {
				linkEl.style.fontWeight= "bold";
			}
		}
		feedbackElement.style.display = "none";
	}
}

function isListUpToDate(user, type) {
	var rss = loadRSS(user, type[1]);

	var lastUpdate, cachedDate;
	if (rss === null) {
		console.log(type[0] + ' RSS feed failed to load');
		lastUpdate = new Date();
		cachedDate = new Date(getCacheDate(type[0] + "list"));
		if (lastUpdate - cachedDate < 86400000 && lastUpdate - cachedDate > 0)
			return true;
		else {
			console.log('Cached ' + type[0] + ' data not up to date!');
			return false;
		}
	} else {
		lastUpdate = rss.getElementsByTagName('pubDate')[0].textContent;
		cachedDate = getCacheDate(type[0] + "rss");
		if (lastUpdate == cachedDate)
			return true;
		else {
			console.log('Cached ' + type[0] + ' data not up to date!');
			setCacheDate(type[0] + "rss", lastUpdate);
			return false;
		}
	}
}

function getUserList(type) {
	var object = GM_getValue('MAL' + type + 'list');
	if (object)
		return JSON.parse(object);
	else
		console.log("failed to get lists");
}

function setUserList(type, list) {
	GM_setValue('MAL' + type + 'list', JSON.stringify(list));
}

function getCacheDate(type) {
	var object = GM_getValue('CacheDate' + type + version);
	if (object)
		return JSON.parse(object);
	else
		return null;
}

function setCacheDate(type, value) {
	GM_setValue('CacheDate' + type + version, JSON.stringify(value));
}

function loadUserList(user, type) {
	// get userlist
	var xhr = new XMLHttpRequest();
	var url = '/malappinfo.php?u=' + user + '&status=all&type=' + type + '';
	xhr.open("GET", url, false);
	xhr.setRequestHeader('Content-Type', 'text/xml');
	xhr.send();
	var xmlDocument = xhr.responseXML;

	if (xmlDocument === null) {
		console.log("failed to load list, trying to grab cached list");
		return getUserList(type);
	}

	// create a list that I can cache
	var rawList = xmlDocument.getElementsByTagName('series_' + type + 'db_id');
	var statusList = xmlDocument.getElementsByTagName('my_status');
	var list = [];
	for (var i = 0; i < rawList.length; i++) {
		list.push(rawList[i].textContent);
	}

	setCacheDate(type + "list", new Date());
	setUserList(type, list);
	return list;
}

function loadRSS(user, type) {
	var xhr = new XMLHttpRequest();
	var url = '/rss.php?type=' + type + '&u=' + user;
	xhr.open("GET", url, false);
	xhr.setRequestHeader('Content-Type', 'text/xml');
	xhr.send();
	var xmlDocument = xhr.responseXML;
	return xmlDocument;
}

function haveListHit(list, animeid) {
	for(var k = 0; k < list.length; k++) {
		if (list[k] == animeid) {
			return true;
		}
	}
	return false;
}