// ==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.8.3
// @author akarin
// @include http://myanimelist.net/history/*
// @require http://code.jquery.com/jquery-latest.min.js
// @run-at document-start
// @grant none
// @noframes
// ==/UserScript==
/*jslint fudge, maxerr: 10, browser, devel, this, white, for, single */
/*global jQuery */
(function($) {
'use strict';
var mal = {
ajax: { delay: 200, timeout: 10000 },
cache: { version: '3.8.3', time: 1000*60*60*36 },
entries: [],
nickname: document.URL.match(/^http:\/\/myanimelist\.net\/history\/([^\/]+)/)[1],
feedsCount: 0,
feedsTotal: document.URL.match(/^http:\/\/myanimelist\.net\/history\/(?!.*\/.)/) ? 2 : 1
};
mal.loadValue = function(key, value) {
try {
value = JSON.parse(localStorage.getItem(mal.cache.version + '#' + key)) || value;
}
finally {
return value;
}
};
mal.saveValue = function(key, value) {
localStorage.setItem(mal.cache.version + '#' + key, JSON.stringify(value));
};
mal.content = {
body: $('<div id="history_content">Loading history...</div>'),
setItem: function(id, type, item) {
if (item && item.hasOwnProperty('info') && item.info.length > 0 &&
item.hasOwnProperty('cover') && item.cover.length > 0) {
$('div[id="info_' + type + '_' + id + '"]', mal.content.body).html(item.info);
$('img[id="cover_' + type + '_' + id + '"]', mal.content.body).attr('src', item.cover);
return true;
}
return false;
},
loadItem: function(id, type) {
var item = mal.loadValue('item.data_' + type + '_' + id, {});
var date = mal.loadValue('item.time_' + type + '_' + id, 0);
return mal.content.setItem(id, type, item) ? (mal.date <= parseInt(date)) : false;
},
saveItem: function(id, type, data) {
var item = {
info: '', cover: ''
};
var info = {
first: $('span.dark_text:contains(' +
(type === 'anime' ? 'Studios' : 'Authors') + ':)', data).parent(),
genres: $('span.dark_text:contains(Genres:)', data).parent(),
score: $('span.dark_text:contains(Score:)', data).parent(),
rank: $('span.dark_text:contains(Ranked:)', data).parent(),
popularity: $('span.dark_text:contains(Popularity:)', data).parent()
};
$('meta, sup, .statistics-info', info.score).remove();
$('sup, .statistics-info', info.rank).remove();
item.info = info.first.html() + '<br>' + info.genres.html() + '<br>' +
info.score.html() + info.rank.html() + info.popularity.html();
var cover = $('img[itemprop="image"]', data);
if (cover.length > 0 && cover[0].hasAttribute('data-src')) {
item.cover = cover.attr('data-src').replace(/\.(\w+)$/, 't.$1');
} else if (cover.length > 0 && cover[0].hasAttribute('src')) {
item.cover = cover.attr('src').replace(/\.(\w+)$/, 't.$1');
} else {
item.cover = 'http://cdn.myanimelist.net/images/qm_50.gif';
}
mal.saveValue('item.data_' + type + '_' + id, item);
mal.saveValue('item.time_' + type + '_' + id, mal.date + mal.cache.time);
return mal.content.setItem(id, type, item);
}
};
mal.load = function(url, feed) {
$.ajax(url).done(function(data) {
mal.process(data, feed);
}).fail(function() {
mal.process('', feed);
});
};
mal.process = function(data, feed) {
$('rss > channel > item', data).each(function() {
mal.entries.push({
gtype: feed,
title: $('title', this).text().match(/^(.+)(\ -(?!.*\ -))/)[1],
type: $('title', this).text().match(/(\ -(?!.*\ -))\s?(.*)$/)[2].replace(/^$/, '-'),
link: $('link', this).text().replace(/^http:\/\/[^\/]+\//, '/'),
id: $('link', this).text().match(/\/(\d+)\//)[1],
status: $('description', this).text().match(/^(.+)\ -\ /)[1]
.replace(/(Watch)/, feed === 'manga' ? 'Read' : '$1'),
progress: $('description', this).text().match(/\ -\ (.+)$/)[1]
.replace(/\ (chapters|episodes)/, '')
.replace(/\ of\ /, '/')
.replace(/^0\//, '-/')
.replace(/\?/, '-'),
date: new Date($('pubDate', this).text())
});
});
mal.feedsCount += 1;
if (mal.feedsCount < mal.feedsTotal) {
return;
}
mal.entries.sort(function(a, b) {
return b.date - a.date;
});
var table = $('<table id="history_table" width="100%" border="0" cellpadding="0" cellspacing="0">').html('<tr>' +
'<td class="normal_header" width="50">Image</td>' +
'<td class="normal_header">Title</td>' +
'<td class="normal_header" width="70">Type</td>' +
'<td class="normal_header" width="90">Status</td>' +
'<td class="normal_header" width="70">Progress</td>' +
'<td class="normal_header" width="125">Date</td>' +
'</tr>').appendTo(mal.content.body.empty());
var index = 0;
$.each(mal.entries, function(i, entry) {
var dateLast = (i < mal.entries.length - 1) &&
(entry.date.getDate() !== mal.entries[i + 1].date.getDate());
$('<tr' + (dateLast ? ' class="date_last"' : '') + '>').html(
'<td valign="top"><div class="picSurround"><a href="' + entry.link + '">' +
'<img id="cover_' + entry.gtype + '_' + entry.id + '" src="/images/spacer.gif" /></a></div></td>' +
'<td valign="top"><a href="' + entry.link + '"><strong>' + entry.title + '</strong></a>' +
'<div id="info_' + entry.gtype + '_' + entry.id + '"></div></td>' +
'<td>' + entry.type + '</td>' +
'<td>' + entry.status + '</td>' +
'<td>' + entry.progress + '</td>' +
'<td>' + entry.date.toString()
.replace(/^\w+\s/, '')
.replace(/(\d+)/, '$1,')
.replace(/(\d{4})/, '$1,')
.replace(/(\d\d:\d\d).+$/, '$1')
+ '</td>'
).appendTo(table);
if (!mal.content.loadItem(entry.id, entry.gtype)) {
setTimeout(function() {
$.get('/' + entry.gtype + '/' + entry.id + '/_/pics', function(data) {
mal.content.saveItem(entry.id, entry.gtype, data);
});
}, mal.ajax.delay * index);
index += 1;
}
});
};
function main() {
$.ajaxSetup({ timeout: mal.ajax.timeout });
mal.date = Date.now();
var toggle = $('<a href="javascript:void(0);">Show Full History</a>').click(function() {
var el = $(this).next('div');
if (el.css('display') === 'none') {
$(this).text('Hide Full History');
el.show(200);
} else {
$(this).text('Show Full History');
el.hide(200);
}
});
$('#horiznav_nav + div').hide()
.before(mal.content.body)
.before(toggle.css({
'display': 'block',
'width': '100%',
'text-align': 'center'
})
);
if (mal.feedsTotal > 1 || document.URL.match(/^http:\/\/myanimelist\.net\/history\/[^\/]+\/anime/)) {
mal.load('/rss.php?type=rw&u=' + mal.nickname, 'anime');
}
if (mal.feedsTotal > 1 || document.URL.match(/^http:\/\/myanimelist\.net\/history\/[^\/]+\/manga/)) {
mal.load('/rss.php?type=rm&u=' + mal.nickname, 'manga');
}
$('<style type="text/css">').html(
'#history_content { padding: 0 15px 10px 15px; }' +
'#history_table { min-width: 100%; }' +
'#history_table td { padding: 4px; border-bottom: 1px solid #ebebeb; text-align: center; }' +
'#history_table tr.date_last td { border-bottom: 2px solid #dadada; }' +
'#history_table td.normal_header { border-bottom: 1px solid #bebebe; }' +
'#history_table td:nth-of-type(2) { text-align: left; }' +
'#history_table td div[id^="info_"] { padding: 4px 0 2px; font-size: 10px; color: #666; }'
).appendTo('head');
}
var style = $('<style type="text/css">').html(
'.page-common #content { display: none !important; }'
).appendTo('head');
$(function() {
try {
main();
}
finally {
style.remove();
}
});
}(jQuery));