Greasy Fork is available in English.

MyAnimeList (MAL) Alternative History

Displays anime/manga series history based on RSS feed, instead of default episode/chapter history.

Versione datata 15/06/2015. Vedi la nuova versione l'ultima versione.

// ==UserScript==
// @name		MyAnimeList (MAL) Alternative History
// @namespace	https://greasyfork.org/users/7517
// @description	Displays anime/manga series history based on RSS feed, instead of default episode/chapter history.
// @icon		http://i.imgur.com/b7Fw8oH.png
// @version		3.3.5
// @author		akarin
// @include		/^http:\/\/myanimelist\.net\/history\//
// @grant		none
// @noframes
// ==/UserScript==

(function($) {
	
var mal = {
	debug: false,
	ajax: {
		//delay: 10,
		timeout: 5000 
	},
	covers: { 
		naLarge: 'na_series.gif', 
		naSmall: 'qm_50.gif', 
		url: 'http://cdn.myanimelist.net/images/' 
	},
	cache: { 
		version: '3.3',
		timeExp: 1000*60*60*24*14, 
		timeDlt: 1000*60*60*6,
		check: function() {
			if (mal.debug || (localStorage.getItem('version') || '') !== mal.cache.version) {
				localStorage.clear();
				localStorage.setItem('version', mal.cache.version);
			}
		}
	}
};

function main() {
	$.ajaxSetup({ timeout: mal.ajax.timeout });
	
	mal.cache.check();
	mal.date = Date.now();
	
	var entries = [],
		nickname = document.URL.match(/^http:\/\/myanimelist\.net\/history\/([^\/]+)/)[1],
		feedsCount = 0,
		feedsTotal = document.URL.match(/^http:\/\/myanimelist\.net\/history\/(?!.*\/.)/) ? 2 : 1,
		content = $('<div id="history_content" style="padding: 0 15px 10px 15px;">Loading history...</div>')
			.insertBefore($('#horiznav_nav + div').hide());
	
	if (feedsTotal > 1 || document.URL.match(/^http:\/\/myanimelist\.net\/history\/.+?\/anime/)) {
		load('/rss.php?type=rw&u=' + nickname, 'anime');
	}

	if (feedsTotal > 1 || document.URL.match(/^http:\/\/myanimelist\.net\/history\/.+?\/manga/)) {
		load('/rss.php?type=rm&u=' + nickname, 'manga');
	}
	
	function load(url, feed) {
		$.ajax(url).done(function(data) { 
			process(data, feed); 
		}).fail(function() {
			process('', feed); 
		});
	}
	
	function process(data, feed) {
		$('rss > channel > item', data).each(function() {				
			entries.push({
				gtype: feed,
				title: $('title', this).text().match(/^(.+)( - (?!.* - ))/)[1],
				type: $('title', this).text().match(/( - (?!.* - ))(.+)$/)[2],
				link: $('link', this).text().replace(/^http:\/\/127\.0\.0\.1:9001/, ''),
				id: $('link', this).text().match(/\/(\d+)\//)[1],
				status: $('description', this).text().match(/^(.+) - /)[1]
					.replace(/(Watching)/, feed === 'manga' ? 'Reading' : '$1')
					.replace(/(Plan to Watch)/, feed === 'manga' ? 'Plan to Read' : '$1'),
				progress: $('description', this).text().match(/ - (.+)$/)[1]
					.replace(/ (chapters|episodes)/, '')
					.replace(/ of /, '/')
					.replace(/^0\//, '-/')
					.replace(/\?/, '-'),
				date: new Date($('pubDate', this).text())
			});
		});
		
		if ((++feedsCount) < feedsTotal) {
			return;
		}
		
		entries.sort(function(a, b) {
			return b.date - a.date;
		});
		
		var table = $('<table id="history_table" width="100%" border="0" cellpadding="0" cellspacing="0"></table>')
			.appendTo(content.empty());
		
		$('<tr></tr>')
			.append('<td class="normal_header" colspan="2">Title</td>')
			.append('<td class="normal_header" width="70" align="center">Type</td>')
			.append('<td class="normal_header" width="90" align="center">Status</td>')
			.append('<td class="normal_header" width="70" align="center">Progress</td>')
			.append('<td class="normal_header" width="125" align="center">Date</td>')
			.appendTo(table);
		
		$.each(entries, function(index, entry) {
			var dateLast = (index < entries.length - 1) && (entry.date.getDate() != entries[index + 1].date.getDate()),
				dateFormat = entry.date.toLocaleTimeString('en-US', {
					year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', hour12: false, minute: '2-digit'
				});
			
			$('<tr' + (dateLast ? ' class="date_last"' : '') + '></tr>')
				.append($('<td width="50" valign="top"></td>')
					// Cover
					.append($('<div class="picSurround"></div>')
						.append($('<a href="' + entry.link + '"></a>')
							.append('<img id="cover_' + entry.gtype + '_' + entry.id + '" border="0" ' + 
								'src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" alt="" />')
						)
					)
				)
				.append($('<td valign="top"></td>')
					// Title
					.append($('<a href="' + entry.link + '"></a>').append('<strong>' + entry.title + '</strong>'))
					// Additional Info
					.append('<div id="info_' + entry.gtype + '_' + entry.id + '"></div>')
				)
				.append('<td width="70" align="center">' + entry.type + '</td>')
				.append('<td width="90" align="center">' + entry.status + '</td>')
				.append('<td width="70" align="center">' + entry.progress + '</td>')
				.append('<td width="125" align="center">' + dateFormat + '</td>')
				.appendTo(table);
			
			//setTimeout(function() {
				loadData(entry.id, entry.gtype);
			//}, mal.ajax.delay * index);
		});
	}
	
	function loadData(id, type) {
		if (!getInfo(id, type) || !getCover(id, type, true)	) {
			$.get('/' + type + '/' + id + '/_/pics', function(data) {
				setInfo(id, type, data);
				setCover(id, type, data, true);
			});
		}
	}

	function getInfo(id, type) {
		var infoData = localStorage.getItem('info_' + id);
		if (infoData !== null) {
			var info = infoData.split(';');
			if (parseInt(info[1]) + mal.cache.timeExp > mal.date) {
				applyInfo(id, type, info[0]);
				return true;
			}
		}
		return false;
	}

	function setInfo(id, type, data) {
		var infoRegExp = (type === 'anime'
			?
			/<h2>Information<\/h2>[\s\S]*?(<div><span class="dark_text">Producers:<\/span>[\s\S]*?>Genres:<\/span>[\s\S]*?<\/div>)[\s\S]*?<h2>Statistics<\/h2>[\s\S]*?(<div><span class="dark_text">Score:<\/span>[\s\S]*?>Popularity:<\/span>[\s\S]*?<\/div>)/
			:
			/<h2>Information<\/h2>[\s\S]*?(<div class="spaceit"><span class="dark_text">Genres:<\/span>[\s\S]*?>Authors:<\/span>[\s\S]*?<\/div>)[\s\S]*?<h2>Statistics<\/h2>[\s\S]*?(<div><span class="dark_text">Score:<\/span>[\s\S]*?>Popularity:<\/span>[\s\S]*?<\/div>)/
		);
		
		var infoData = data.match(infoRegExp),
			info = (infoData === null ? '' :
				infoData[1]
					.replace(/<sup>[\s\S]*?<\/sup>/g, '')
					.replace(/ class="spaceit"/g, '')
					.replace(/(<div><span class="dark_text">Producers:[\s\S]*?<\/div>)(<div><span class="dark_text">Genres:[\s\S]*?<\/div>)/, '$2$1') + 
			'<div>' + 
				infoData[2]
					.replace(/<sup>[\s\S]*?<\/sup>/g, '')
					.replace(/<[\/]*?div[\s\S]*?>/g, '') + 
			'</div>'
		);
		
		localStorage.setItem('info_' + id, info + ';' + (mal.date - mal.cache.timeExp + mal.cache.timeDlt));
		applyInfo(id, type, info);	
	}

	function getCover(id, type, thumbnail) {
		var coverData = localStorage.getItem('cover_' + id);
		if (coverData !== null) {
			var cover = coverData.split(';');
			if (parseInt(cover[1]) + mal.cache.timeExp > mal.date) {
				if (cover[0] !== mal.covers.naLarge) {
					cover = mal.covers.url + type + '/' + cover[0];
					if (thumbnail) {
						cover = cover.replace(/\.(\w+)$/, 't.$1');
					}
				} else {
					cover = mal.covers.url + (thumbnail ? mal.covers.naSmall : mal.covers.naLarge);
				}
				applyCover(id, type, cover);
				return true;
			}
		}
		return false;
	}

	function setCover(id, type, data, thumbnail) {
		var cover = $('#content > table td.borderClass img', data).prop('src');
			
		if (cover.length > 0 && cover.indexOf(mal.covers.naLarge) < 0) {
			var coverPrefix = mal.covers.url + type + '/';
			cover = cover.split(coverPrefix)[1];
			localStorage.setItem('cover_' + id, cover + ';' + mal.date);
			if (thumbnail) {
				cover = cover.replace(/\.(\w+)$/, 't.$1');
			}
			applyCover(id, type, coverPrefix + cover);
		}
		else {
			cover = thumbnail ? mal.covers.naSmall : mal.covers.naLarge;
			localStorage.setItem('cover_' + id, mal.covers.naLarge + ';' + (mal.date - mal.cache.timeExp + mal.cache.timeDlt));
			applyCover(id, type, mal.covers.url + cover);
		}
	}

	function applyInfo(id, type, data) {
		$('div[id="info_' + type + '_' + id + '"]', content).html(data);
	}

	function applyCover(id, type, data) {
		$('img[id="cover_' + type + '_' + id + '"]', content).attr('src', data);
	}
}

main();	

$('<style type="text/css" />').html(
	'#history_table { min-width: 100%; }' +
	'#history_table td { padding: 4px; border-bottom: 1px solid #ebebeb; }' +
	'#history_table tr.date_last td { border-bottom: 2px solid #dadada; }' +
	'#history_table td.normal_header { border-bottom: 1px solid #bebebe; }' +
	'#history_table td div[id^="info_"] { padding: 4px 0 2px; font-size: 10px; color: #666; }'
).appendTo('head');
	
})(jQuery);