MyAnimeList (MAL) Track Missing Relations

Allows to find missing relations and entries with wrong chapter/episode count.

当前为 2015-04-17 提交的版本,查看 最新版本

// ==UserScript==
// @name         MyAnimeList (MAL) Track Missing Relations
// @namespace    https://greasyfork.org/users/7517
// @description  Allows to find missing relations and entries with wrong chapter/episode count.
// @version      1.0.2
// @author       akarin
// @include      /^http\:\/\/myanimelist\.net\/editlist\.php\?type=anime$/
// @include      /^http\:\/\/myanimelist\.net\/panel\.php\?go=editmanga$/
// @grant        none
// @noframes
// ==/UserScript==

;(function() {	
	if ($('#malLogin').length > 0) {
		return
	}

	const GET_DELAY = 300
	const ANIME_T = 'anime'
	const MANGA_T = 'manga'
	const LIST_TYPE = document.URL.match(/\?type=anime$/) ? ANIME_T : MANGA_T
	const NICKNAME = $('ul#nav li:first > ul > li > a:contains(Profile)').prop('href').match(/(?!.*\/).*$/)[0]
	
	cacheVersion('Cache_1.0')
	
	var total
	var calc = -1
	var left = []
	var right = []
	
	var $relBody = $('<div id="mr_body"></div>')
		.css({
			'text-align': 'center',
			'width': '650px',
			'height': 'auto'
		})
		.append('<div style="font-size: 1.1em; font-weight: bold; color: #000;">Missing Titles</div>')
		.append($('&nbsp;<small></small>').append(
			$('<a href="javascript:void(0);">refresh</a>').click(function() {
				$.fancybox.close()
				if (true === confirm('Are you sure you want to recalculate missing relations?')) {
					recalculate($('#content'))
				}
			})		
		))
		.append('<br/>')
		.append('<hr size="1" color="#ccc" width="98%" noshade>')
		
	var $relList = $('<div id="mr_list"></div>').appendTo($relBody)	
	
	var content = loadValue('relList', null)
	if (content !== null) {
		$relList.html(content)
	}
	else {
		$relList.html('<br><small>No missing relations found.</small>')
	}
	restyle($relList)
	
	$('<div style="display: none;"></div>').append($relBody).insertAfter('#content')
	
	$('<span id="mr_link"></span>')
		.append('<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>')
		.append($('<a href="#mr_body">Missing Relations</a>').fancybox({
			'hideOnContentClick': false,
			'hideOnOverlayClick': true,
			'titleShow': false,
			'transitionIn': 'none', 
			'transitionOut': 'none',
			'scrolling': 'no'
		}))
		.append('&nbsp;(')
		.append($('<small></small>').append(
			$('<a href="javascript:void(0);">refresh</a>').click(function() {
				if (true === confirm('Are you sure you want to recalculate missing relations?')) {
					recalculate($('#content'))
				}
			})
		))
		.append(')&nbsp;')
		.append('<span id="mr_status" style="color: green;"></span>')
		.appendTo('#content > div:first')
		
	function cacheVersion(ver) {
		var v = loadValue('cache', null)
		if (v === null || v !== ver) {
			localStorage.clear()
			saveValue('cache', ver)
		}
	}
		
	function recalculate(context) {		
		if (calc > -1) {
			return
		}
		
		var $links = $('strong + a', context)
		total = $links.length
		
		if (total === 0) {
			$relList.html('<br><small>No missing relations found.</small>')
			return
		}
		
		calc = total
		$('span#mr_status').text(total - calc + '/' + total)
		$('span[id^="mr_id_"]').empty()
		left = []
		right = []
		
		$relList.html('<br><small>Calculating missing relations...</small>')
		
		$links.each(function(index) {
			var id = $(this).prop('href').match(/\d+$/)[0]	
			var $span = $(this).parent().find('span[id="mr_' + id + '"]')
			if ($span.length === 0) {
				$('<span id="mr_id_' + id + '" style="float: right; margin: 0 7px;"></span>').appendTo($(this).parent())
			}
			left.push(parseInt(id))
			setTimeout(function() { 
				$.get('/' + LIST_TYPE + '/' + id, function(data) {
					checkRelations(id, data)
					checkCorrectInfo(id, data)
					$('span[id="mr_id_' + id + '"]').html('<small style="color: green;">done</small>')
					$('span#mr_status').text(total - (--calc) + '/' + total)
					finalize()
				})
			}, GET_DELAY * index)
		})
	}
	
	function checkRelations(id, data) {		
		var title = data.match(/<div id="contentWrapper">[\s\S]*?<h1><div [\s\S]*?<\/div>(.+?)</)[1].trim()		
		var cnEx = LIST_TYPE === ANIME_T ? /<h2>Related Anime<\/h2>[\s\S]*?<h2>/ : /<h2>Related Manga<\/h2>[\s\S]*?<h2>/
		var idEx = LIST_TYPE === ANIME_T ? /\/anime\/(\d+)\// : /\/manga\/(\d+)\//
		
		$('a', '<context>' + data.match(cnEx) + '</context>').each(function() {
			var idData = $(this).prop('href').match(idEx)
			if (idData !== null && idData.length > 1) {
				right.push({
					lId: parseInt(id),
					lTitle: title,
					rId: parseInt(idData[1]),
					rTitle: $(this).text().trim()
				})
			}
		})
	}
	
	function checkCorrectInfo(id, data) {
		var correct = true
		do {
			var title = data.match(/<div id="contentWrapper">[\s\S]*?<h1><div [\s\S]*?<\/div>(.+?)</)[1].trim()
			var status = data.match(/>Status:<\/span>([\s\S]*?)<\/div>/)[1].trim()
			var myStatus = data.match(/selected>(.*?)<\/option/)[1].trim()
			
			if (status === (LIST_TYPE === ANIME_T ? 'Not yet aired' : 'Not yet published')) {
				if (myStatus !== (LIST_TYPE === ANIME_T ? 'Plan to Watch' : 'Plan to Read')) {
					correct = false
					break
				}
			}
			else if (status === (LIST_TYPE === ANIME_T ? 'Currently Airing' : 'Publishing')) {
				if (myStatus === 'Completed') {
					correct = false
					break
				}
			}
			
			if (LIST_TYPE === ANIME_T) {
				var eps = data.match(/>Episodes:<\/span>([\s\S]*?)<\/div>/)[1].trim()
				var myEps = data.match(/name="myinfo_watchedeps"[\s\S]*?value="(\d*)"/)[1].trim()
				if (eps !== 'Unknown') {
					if (parseInt(myEps) > parseInt(eps) || (myStatus === 'Completed' && parseInt(myEps) !== parseInt(eps))) {
						correct = false
						break
					}
				}
			}
			else {
				var vols = data.match(/>Volumes:<\/span>([\s\S]*?)<\/div>/)[1].trim()
				var myVols = data.match(/id="myinfo_volumes"[\s\S]*?value="(\d*)"/)[1].trim()
				if (vols !== 'Unknown') {
					if (parseInt(myVols) > parseInt(vols) || (myStatus === 'Completed' && parseInt(myVols) !== parseInt(vols))) {
						correct = false
						break
					}
				}
				var chap = data.match(/>Chapters:<\/span>([\s\S]*?)<\/div>/)[1].trim()
				var myChap = data.match(/id="myinfo_chapters"[\s\S]*?value="(\d*)"/)[1].trim()
				if (chap !== 'Unknown') {
					if (parseInt(myChap) > parseInt(chap) || (myStatus === 'Completed' && parseInt(myChap) !== parseInt(chap))) {
						correct = false
						break
					}
				}
			}
		} while (false);
		
		if (correct === false) {
			right.push({ 
				lId: parseInt(id),	
				lTitle: title, 
				rId: -1,	
				rTitle: '' 
			})
		}
	}
	
	function finalize() {
		if (calc > 0) {
			return
		}
		
		if (right.length === 0) {
			$relList.html('<br><small>No missing relations found.</small>')
			return
		}
		
		var $table = $('<table id="relTable" border="0" cellpadding="0" cellspacing="0" width="100%"></table>')
			.append($('<tr></tr>').html(
				'<th>You\'ve watched this…</th>' +
				'<th>…so you might want to check this:</th>'
			))
			
		right.sort(function(a, b) {
			return a.rId > b.rId
		})	
			
		var prev = -1
		for (var i = 0; i < right.length; ++i) {
			if (right[i].rId < 0) {
				continue
			}
			if (right[i].rId === prev || left.indexOf(right[i].rId) > -1) {
				right.splice(i--, 1)
			}
			else {
				prev = right[i].rId
			}
		}
			
		right.sort(function(a, b) {
			if (a.lTitle === b.lTitle) {
				return b.rId < 0 || a.rTitle > b.rTitle
			}
			return a.lTitle > b.lTitle
		})	
			
		prev = -1
		var count = 0
		$.each(right, function(index, rel) {
			var leftLink = ''
			var rightLink = ''
			var className = ''
			
			if (rel.lId !== prev) {
				leftLink = '<a href="/' + LIST_TYPE + '/' + rel.lId + '" target="_blank">' + rel.lTitle + '</a>'
				className = ' class="first_row"'
				if (rel.rId < 0) {
					rightLink = '<div class="mr_warning">Wrong status or ' + (LIST_TYPE === ANIME_T ? 'episode' : 'chapter') + ' count</div>'
				}
			}
			
			if (rel.rId > -1) {
				rightLink = '<div class="mr_count"><small>' + (++count) + '</small></div>' + 
					'<a href="/' + LIST_TYPE + '/' + rel.rId + '" target="_blank">' + rel.rTitle + '</a>'
			}
			
			$table.append($('<tr></tr>').html(
				'<td' + className + '>' + leftLink + '</td>' +
				'<td' + className + '>' + rightLink + '</td>'
			))
			prev = rel.lId
		})
			
		$relList.empty().append($table)
		saveValue('relList', $relList.html())
		restyle($relList)
		
		calc = -1
	}
	
	function restyle(context) {		
		$(context).css({
			'width': '98%',
			'height': '700px',
			'overflow-y': 'auto',
			'margin': '0 auto 15px'
		})
	
		$('#relTable th', context).css({
			'width': '50%',
			'background-color': '#f5f5f5', 
			'padding': '0.5em 1em',
			'font-weight': 'bold', 
			'text-align': 'left'
		})
		
		$('#relTable td', context).css({
			'width': '50%',
			'padding': '0.2em 1em', 
			'font-weight': 'normal', 
			'text-align': 'left',
			'border-top': '1px solid #eee'
		})
		
		$('#relTable td.first_row', context).css({
			'border-top': '2px solid #bebebe'
		})
		
		$('#relTable tr:last-of-type td', context).css({
			'border-bottom': '1px solid #eee'
		})
		
		$('#relTable td:last-of-type > div.mr_warning', context).css({
			'color': '#E43',
			'font-weight': 'bold'
		})
		
		$('#relTable td:last-of-type > div.mr_count', context).css({
			'width': '25px',
			'float': 'right',
			'color': '#666',
			'text-align': 'center'
		})
	}
	
	function loadValue(name, defaultValue) {
		var value = localStorage.getItem(NICKNAME + '#' + LIST_TYPE + '#' + name)
		return value ? value : defaultValue
	}

	function saveValue(name, value) {
		localStorage.setItem(NICKNAME + '#' + LIST_TYPE + '#' + name, value)
	}  
})()