Greasy Fork is available in English.

MyAnimeList (MAL) Tags Updater

Adds type, genres and other info to entries tags. Can also delete all current tags.

As of 26.04.2015. See ბოლო ვერსია.

// ==UserScript==
// @name         MyAnimeList (MAL) Tags Updater
// @namespace    https://greasyfork.org/users/7517
// @description  Adds type, genres and other info to entries tags. Can also delete all current tags.
// @icon         http://i.imgur.com/b7Fw8oH.png
// @version      2.4.2
// @author       akarin
// @include      /^http:\/\/myanimelist\.net\/(anime|manga)list/
// @include      /^http:\/\/myanimelist\.net\/panel\.php\?go=(add|edit)/
// @include      /^http:\/\/myanimelist\.net\/editlist\.php\?type=(anime|manga&id=)/
// @grant        none
// ==/UserScript==

(function($) {  

var modes = {
	M_NONE: -1, M_LIST: 1, M_POPUP: 2, M_EXTRA: 3
};

var mal = {
	mode: modes.M_NONE,
	page: {},
	tagsUrl: '',
	ajax: { delay: 300, timeout: 5000, type: 'GET', url: 'http://myanimelist.net/' },
	list: { anime: 'anime', manga: 'manga', type: '', status: '' },
	entries: { total: 0, calc: 0, fail: 0 },
	content: { done: $(), fail: $() }
};

mal.main = function() {	
	if ($('#malLogin').length > 0) {
		return;
	}
	
	$.ajaxSetup({ type: mal.ajax.type, timeout: mal.ajax.timeout });
	
	if (document.URL.match(/\/(anime|manga)list\//)) {	
		mal.mode = modes.M_LIST;
		mal.page.list();
	}
	else if (document.URL.match(/\?(type=anime|go=editmanga)$/)) {
		mal.mode = modes.M_EXTRA;
		mal.page.extra();
	}
	else {
		mal.mode = modes.M_POPUP;
		mal.page.popup();
	}
	
	mal.tagsUrl = 'includes/ajax.inc.php?' + (mal.list.type === mal.list.anime ? 't=22&aid=' : 't=30&mid=');
};

mal.page.list = function() {	
	// exit if this is another user's list
	if ($('#mal_cs_otherlinks div:first strong').text().match(/^You are viewing your/) === null) {
		return;
	}
	
	mal.list.type = document.URL.match(/\/animelist\//) ? mal.list.anime : mal.list.manga;
	mal.list.status = $('.status_selected a').prop('href').match(/&status=\d/)[0];
			
	$('<span id="tu_links"></span>')
		.append('<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>')
		.append($('<a href="javascript:void(0);">Update Tags</a>').click(function() { 
			if (true === confirm('Are you sure you want to update all tags?')) {
				mal.updateAllTags(false);
			}
		}))
		.append('&nbsp;&nbsp;')
		.append($('<a href="javascript:void(0);">Clear Tags</a>').click(function() { 
			if (true === confirm('Are you sure you want to clear all tags?')) {
				mal.updateAllTags(true);
			}
		}))
		.appendTo('#mal_cs_otherlinks div:last');
	
	$('#list_surround .animetitle').each(function() {
		var id = $(this).attr('href').match(/\d+/)[0];
		$(this).prev('div[style="float: right;"]').find('small')
			.prepend('<span id="tu_info_' + id + '"></span> - ')
			.prepend(
				$('<a id="tu_link_' + id + '" href="javascript:void(0);">Upd</a>').click(function() { 
					mal.updateTags(id, false);
				})
			);				
	});
};

mal.page.popup = function() {
	mal.list.type = document.URL.match(/(\?go=(add|edit)&|\?type=anime&)/) ? mal.list.anime : mal.list.manga;
	
	var id = $('strong + a:contains(Info)').prop('href').match(/\d+/)[0];
	$('td.borderClass:contains(Tags)').append(
		$('<a href="javascript:void(0)"></a>').click(function() { 
			mal.updateTags(id, false);
		})
		.append('&nbsp;<small>update</small>')
	);
};

mal.page.extra = function() {
	mal.list.type = document.URL.match(/\?type=anime$/) ? mal.list.anime : mal.list.manga;
	mal.content.done = $('<span id="tu_status_done" style="color: green;"></span>');
	mal.content.fail = $('<span id="tu_status_fail" style="color: #c32;"></span>');
	
	$('<span id="tu_links"></span>')
		.append('<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>')
		.append($('<a href="javascript:void(0);">Update Tags</a>').click(function() { 
			if (true === confirm('Are you sure you want to update all tags?')) {
				mal.updateAllTags(false);
			}
		}))
		.append('&nbsp;&nbsp;')
		.append($('<a href="javascript:void(0);">Clear Tags</a>').click(function() { 
			if (true === confirm('Are you sure you want to clear all tags?')) {
				mal.updateAllTags(true);
			}
		}))
		.append('&nbsp;&nbsp;').append(mal.content.done)
		.append('&nbsp;').append(mal.content.fail)
		.appendTo('#content > div:first');
};

mal.updateAllTags = function(clear) {
	var links = $();
	if (mal.mode === modes.M_LIST) {
		$('#list_surround td[style="border-left-width: 0"] span[id^=tu_info_]').empty();
		links = $('#list_surround .animetitle');
	}
	else if (mal.mode === modes.M_EXTRA) {
		links = $('#content strong + a');
		mal.content.done.text('');
		mal.content.fail.text('');
		$('#content span[class^="tu_status_id_"]').empty();
	}
	else {
		return;
	}
	
	mal.entries.total = links.length;
	mal.entries.calc = mal.entries.total;
	mal.entries.fail = 0;
	
	if (links.length === 0) {
		return;
	}
	
	links.each(function(index) {
		var id = $(this).attr('href').match(/\d+/)[0];
		if (mal.mode === modes.M_EXTRA) {
			var span = $(this).parent().find('span[class="tu_status_id_' + id + '"]');
			if (span.length === 0) {
				span = $('<span class="tu_status_id_' + id + '"></span>').appendTo($(this).parent());
			}
		}
		setTimeout(function() { 
			mal.updateTags(id, clear);
		}, mal.ajax.delay * index);
	});
};

mal.updateTags = function(id, clear) {
	if (clear) {
		mal.setTags(id, '');
		if (mal.mode === modes.M_EXTRA) {
			$('#content span[class="tu_status_id_' + id + '"]').html('<small style="color: green;">done</small>');
			mal.content.done.text('done: ' + (mal.entries.total - (--mal.entries.calc)) + '/' + mal.entries.total);
		}
		return;
	}		
	$.ajax(mal.ajax.url + mal.list.type + '/' + id + '/_/pics')
		.done(function(data) {				
			mal.setTags(id, mal.list.type === mal.list.anime ? mal.getAnimeTags(data) : mal.getMangaTags(data));
			if (mal.mode === modes.M_EXTRA) {
				$('#content span[class="tu_status_id_' + id + '"]').html('<small style="color: green;">done</small>');
				mal.content.done.text('done: ' + (mal.entries.total - (--mal.entries.calc)) + '/' + mal.entries.total);
			}
		})
		.fail(function(data) {
			mal.setTags(id, '');
			if (mal.mode === modes.M_EXTRA) {
				$('#content span[class="tu_status_id_' + id + '"]').html('<small style="color: red;">fail</small>');
				mal.content.fail.text('fail: ' + (++mal.entries.fail));
			}
		});
};

mal.setTags = function(id, tags) {
	// normalize commas
	tags = tags.replace(/[,\s]{2,}/g, ',').replace(/(,(?!\s))|(,(\s{2,}))/g, ', ');
	if (mal.mode === modes.M_POPUP) {
		$('#tagtext').prop('value', tags);
		return;
	}
	// mal displays ' as \', so remove all '
	tags = tags.replace(/'/g, '');
	$.get(mal.ajax.url + mal.tagsUrl + id + '&tags=' + encodeURIComponent(tags), function(data) {
		if (mal.mode === modes.M_LIST) {
			if ($('#list_surround .table_header[width="125"]').length > 0) {
				data = data.replace(/&status=\d/g, '').replace(/&tag=/g, mal.list.status + '&tag=');
				$('#list_surround #tagLinks' + id).html(data);
				$('#list_surround #tagRow' + id).text($(data).text());
			}
			$('#list_surround td[style="border-left-width: 0"] span#tu_info_' + id).text('*');
		}
	});
};	

mal.getAnimeTags = function(html) {
	var M = {	
		TYPE: 1, STATUS: 2,	AIRED: 3, PRODUCERS: 4, GENRES: 5, RATING: 6 
	};
	var tags = html.match(/<h2>Information<\/h2>[\s\S]*?>Type:<\/span>([\s\S]*?)<\/div>[\s\S]*?>Status:<\/span>([\s\S]*?)<\/div>[\s\S]*?>Aired:<\/span>([\s\S]*?)<\/div>[\s\S]*?>Producers:<\/span>([\s\S]*?)<\/div>[\s\S]*?>Genres:<\/span>([\s\S]*?)<\/div>[\s\S]*?>Rating:<\/span>([\s\S]*?)( -|None)/);
	
	var date = mal.getDate(tags[M.AIRED]);

	return tags === null ? '' : $.map([
		tags[M.TYPE]
		,$(tags[M.GENRES]
			.replace(/No genres have been added yet\./, '')
		).text()
		,$(tags[M.PRODUCERS]
			.replace(/None found, <a href="\/dbchanges\.php\?(.*)>add some<\/a>\./, '')
			.replace(/<sup>[\s\S]*?<\/sup>/g, '')
			.replace(/,/g, '')
			.replace(/<\/a>[\s]*?<a/g, '</a>, <a')
		).text()
		,date.year
		,date.season
		,date.period
		//,tags[M.RATING] 
		//,tags[M.STATUS].replace('Finished Airing', 'Finished').replace('Currently Airing', 'Airing')		
	], $.trim).join(',');
};

mal.getMangaTags = function(html) {
	var M = {	
		TYPE: 1, STATUS: 2,	PUBLISHED: 3, GENRES: 4, AUTHORS: 5, SERIALIZATION: 6 
	};
	var tags = html.match(/<h2>Information<\/h2>[\s\S]*?>Type:<\/span>([\s\S]*?)<\/div>[\s\S]*?>Status:<\/span>([\s\S]*?)<\/div>[\s\S]*?>Published:<\/span>([\s\S]*?)<\/div>[\s\S]*?>Genres:<\/span>([\s\S]*?)<\/div>[\s\S]*?>Authors:<\/span>([\s\S]*?)<\/div>[\s\S]*?>Serialization:<\/span>([\s\S]*?)(None<\/div>|<\/div>)/);
	
	var date = mal.getDate(tags[M.PUBLISHED]);
	
	return tags === null ? '' : $.map([
		tags[M.TYPE]
		,$(tags[M.GENRES]
			.replace(/No genres have been added yet\./, '')
		).text()
		,$(tags[M.AUTHORS]
			.replace(/,/g, '')
			.replace(/\((Art|Story|Story & Art)\)/g, '')
			.replace(/<\/a>[\s]*?<a/g, '</a>, <a')
		).text()
		,$(tags[M.SERIALIZATION]).text()
		,date.year
		//,date.season
		,date.period
		//,tags[M.STATUS]		
	], $.trim).join(',');
};

mal.getDate = function(str) {
	var date = str.replace(/to(.*)$/, '').trim(),
		m_year = date.match(/[\d]{4}/),
		months = { 'Jan':1,'Feb':2,'Mar':3,'Apr':4,'May':5,'Jun':6,'Jul':7,'Aug':8,'Sep':9,'Oct':10,'Nov':11,'Dec':12 },
		m_month = date.match(/^[a-zA-Z]{3}/),
		month = m_month !== null ? months[m_month[0]] : -1;
	
	var result = {
		year: '', season: '', period: ''
	};
	
	if (m_year !== null) {
		result.year = m_year[0];
	}
	
	if (result.year.length > 0 && month > 0 && result.year >= 2000) {
		if (month >= 1 && month <= 3) result.season = 'Winter';
		if (month >= 4 && month <= 6) result.season = 'Spring';
		if (month >= 7 && month <= 9) result.season = 'Summer';
		if (month >= 10 && month <= 12) result.season = 'Fall';
		result.season += ' ' + result.year;
	}
	
	if (result.year.length > 0) {
		if (result.year < 1950) result.period = '....-1949';
		if (result.year >= 1950 && result.year < 1970) result.period = '1950-1969';
		if (result.year >= 1970 && result.year < 1990) result.period = '1970-1989';
		if (result.year >= 1990 && result.year < 2000) result.period = '1990-1999';
		if (result.year >= 2000 && result.year < 2005) result.period = '2000-2004';
		if (result.year >= 2005 && result.year < 2010) result.period = '2005-2009';
		if (result.year >= 2010 && result.year < 2015) result.period = '2010-2014';
		if (result.year >= 2015) result.period = '2015-....';
	}
	
	if (result.year < 1970) {
		result.year = '';
	}
	
	return result;
};

$('<style type="text/css" />').html('\
	#content span[class^="tu_status_id_"] { float: right; padding: 0 7px; }\
').appendTo('head');

mal.main();

})(jQuery);