Horriblesubs Release Time Until

Change times on horriblesubs to "until/ago", highlight shows you're watching, and highlights newly added shows, and adds links to various anime databases

目前為 2016-01-20 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Horriblesubs Release Time Until
// @namespace    horriblesubs_release_time_until
// @description  Change times on horriblesubs to "until/ago", highlight shows you're watching, and highlights newly added shows, and adds links to various anime databases
// @homepageURL  https://github.com/namiman/horriblesubs_release_time_until
// @author       namiman
// @version      1.2.5
// @date         2016-01-19
// @include      /^https?:\/\/horriblesubs\.info\/.*/
// @grant        none
// ==/UserScript==

console.log( "Horriblesubs Release Time Until userscript loaded" );

var user_shows_key = 'hrtu_user_shows';
var all_shows_key = 'hrtu_all_shows';
var version_key = 'hrtu_last_version';
var is_new_install = false;
var current_version = '1.2.5';
var user_shows = JSON.parse( localStorage.getItem( user_shows_key ) );
if ( ! user_shows )
	user_shows = {};
var all_shows = JSON.parse( localStorage.getItem( all_shows_key ) );
if ( ! all_shows ) {
	all_shows = {};
	is_new_install = true;
}
var script_version = localStorage.getItem( version_key );
if ( ! script_version ) {
	if ( is_new_install )
		script_version = current_version;
	else
		script_version = '0';
}

function updateVersion() {
	if ( is_new_install ) {
		console.log( "HRTU version: "+ current_version );
	}
	localStorage.setItem( version_key, current_version );
}

var weekdays = [
	"YABOI", // horriblesubs starts the week on monday, not sunday
	"Monday",
	"Tuesday",
	"Wednesday",
	"Thursday",
	"Friday",
	"Saturday",
	"Sunday"
];

function parseTime( str ) {
	var match = str.match( /(\d+):(\d+)/ );
	return {
		hours: match[1],
		minutes: match[2]
	};
}
function timeAgo( hours, minutes, day ) {
	var now = new Date();
	var dst_start = new Date( now.getFullYear(), 3, 8 );
	var dst_end = new Date( now.getFullYear(), 11, 1 );
	var offset = ( now > dst_start && now < dst_end ) ? -7 : -8 ;
	var pacific_time = new Date( now.getTime() + ( offset * 3600 * 1000 ) );

	var time_show = new Date( pacific_time.getFullYear(), pacific_time.getMonth(), pacific_time.getDate(), 0, 0, 0 );
	var pacific_day = pacific_time.getDay();
	// if it is sunday(pacific), then the actual day will be 0, but horriblesubs day will be 7, so set it to 0, but only on sundays
	var day_diff = ( day == 7 && pacific_day == 0 ) ? 0 : ( day - pacific_day );
		time_show.setDate( pacific_time.getDate() + day_diff );
		time_show.setHours( parseInt( hours ) + parseInt( offset ) );
		time_show.setMinutes( minutes );

	var time_units;
	var time_until = Math.round( ( time_show - pacific_time ) / 1000 / 60 );
	var time_direction = (time_until > 0) ? 1 : (time_until === 0) ? 0 : -1;
	time_until = Math.abs( time_until );
	if ( time_until === 0 )
		time_units = '';
	else if ( time_until > 60 ) {
		time_until = ( time_until / 60 ).toFixed( 1 );
		time_units = ( time_until > 1 ) ? 'hours' : 'hour';
	}
	else
		time_units = ( time_until > 1 ) ? 'minutes' : 'minute';

	var ending_phrase = (time_direction > 0) ? 'until' : (time_direction === 0) ? 'now' : 'ago';

	return {
		'text': ( time_direction === 0 ) ? ending_phrase : time_until + ' ' + time_units + ' ' + ending_phrase,
		'time': time_until,
		'direction': time_direction
	};
}

function linkIdentifier( link ) {
	return link.substr( link.lastIndexOf( '/' ) + 1 );
}

function sideBar() {
	if ( ! jQuery( ".schedule-today:not( .hrtu_sidebar )" ).length ) {
		console.warn( "Horriblesubs Release Time Until sideBar(): Unable to find '.schedule-today'" );
		return false;
	}
	
	jQuery( ".schedule-today:not( .hrtu_sidebar ) .schedule-table tr" ).each(function(){
		var row = jQuery(this);
		var title_el = row.find( ".schedule-widget-show" );
		var no_link = false;
		if ( ! title_el.length ) {
			title_el = row.find( ".schedule-show" );
			no_link = true;
		}
		if ( ! title_el.hasClass( "hrtu_sidebar_show_name" ) ) {
			title_el.addClass( "hrtu_sidebar_show_name" );
			if ( no_link ) {
				var title = fixTitle( title_el.text() );
				var link = false;
				title_el.text( title );
			}
			else {
				var title = fixTitle( title_el.find( "a" ).text() );
				var link = linkIdentifier( title_el.find( "a" ).attr( "href" ) );
				title_el.find( "a" ).text( title );
			}

			if ( isUserShow( title, link ) )
				row.addClass( "hrtu_sidebar_highlight" );
			else
				row.removeClass( "hrtu_sidebar_highlight" );

			if ( ! isAllShow( title, link ) )
				row.addClass( "hrtu_sidebar_highlight_new" );
			else
				row.removeClass( "hrtu_sidebar_highlight_new" );
		}
		
		var time_el = row.find( '.schedule-time' );
		if ( time_el[0].hasAttribute( 'data-hrtu-time' ) )
			var time_text = time_el.attr( 'title' );
		else
			var time_text = time_el.text();
		var match = parseTime( time_text );
		var today = new Date();
		var show = timeAgo( match.hours, match.minutes, today.getDay() );
		time_el
			.attr( 'title', time_text )
			.attr( 'data-hrtu-time', show.time )
			.text( show.text )
			.addClass( 'hrtu_time' );
		if ( show.direction < 0 )
			time_el.addClass( 'hrtu_release_page_time_passed' );
	});
}

function fixTitle( str ) {
	return str.replace( /\u2013|\u002D/g, "-" );
}

function addShow( title, link ) {
	if ( typeof all_shows[ title ] !== "undefined" ) {
		if ( link ) {
			all_shows[ link ] = 1;
			delete all_shows[ title ];
		}
		else {
			all_shows[ title ] = 1;
		}
	}
	else {
		if ( link )
			all_shows[ link ] = 1;
		else
			all_shows[ title ] = 1;
	}
	localStorage.setItem( all_shows_key, JSON.stringify( all_shows ) );
}

function isAllShow( title, link ) {
	if ( ( typeof all_shows[ title ] !== "undefined" ) ) {
		if ( link ) {
			all_shows[ link ] = JSON.parse( JSON.stringify( all_shows[ title ] ) );
			delete all_shows[ title ];
		}
		else {
			all_shows[ title ] = 1;
		}
		localStorage.setItem( all_shows_key, JSON.stringify( all_shows ) );
		return true
	}
	else {
		return ( typeof all_shows[ link ] !== "undefined" );
	}
}

function addUserShow( title, link ) {
	if ( typeof user_shows[ title ] !== "undefined" ) {
		if ( link ) {
			user_shows[ link ] = 1;
			delete user_shows[ title ];
		}
		else {
			user_shows[ title ] = 1;
		}
	}
	else {
		if ( link )
			user_shows[ link ] = 1;
		else
			user_shows[ title ] = 1;
	}
	localStorage.setItem( user_shows_key, JSON.stringify( user_shows ) );
}

function removeUserShow( title, link ) {
	delete user_shows[ title ];
	delete user_shows[ link ];
	localStorage.setItem( user_shows_key, JSON.stringify( user_shows ) );
}
function isUserShow( title, link ) {
	if ( ( typeof user_shows[ title ] !== "undefined" ) ) {
		if ( link ) {
			user_shows[ link ] = JSON.parse( JSON.stringify( user_shows[ title ] ) );
			delete user_shows[ title ];
		}
		else {
			user_shows[ title ] = 1;
		}
		localStorage.setItem( user_shows_key, JSON.stringify( user_shows ) );
		return true
	}
	else {
		return ( typeof user_shows[ link ] !== "undefined" );
	}
}

function releasePage() {
	if ( ! jQuery( '.entry-content' ).length || ! jQuery( '.entry-content' ).children().length ) {
		console.warn( "Horriblesubs Release Time Until releasePage(): Unable to find release entries" );
		return false;
	}

	if ( ! jQuery( '.hrtu_instructions' ).length )
		jQuery( jQuery( ".entry-content ul" ).get(0) ).append(
			'<li class="hrtu_instructions">Click [+] or [-] on shows you\'re watching to highlight them</li>' +
			'<li class="hrtu_instructions">Shows with [NEW] are newly listed, click on [NEW] to unmark individual shows or <span id="hrtu_unmark_all_new">click&nbsp;here</span> to unmark all of them at once.</li>'
		);

	jQuery( '#hrtu_unmark_all_new' ).click(function(){
		jQuery( '.schedule-page-show' ).each(function(){
			var anchor_el = jQuery(this).find( "a" ).first();
			var title = fixTitle( anchor_el.text() );
			var link = linkIdentifier( anchor_el.attr( "href" ) );
			addShow( title, link );
			releasePage();
			sideBar();
		});
	});

	var entry_day;
	jQuery( '.entry-content' ).children().each(function(){
		var time_text = '';
		var el = jQuery(this);
		if ( el.hasClass( 'weekday' ) ) {
			if ( el.text() == "To be scheduled" )
				entry_day = 'tbd';
			else
				entry_day = weekdays.indexOf( el.text() );
		}
		else if ( el.hasClass( 'schedule-today-table' ) ) {
			el.find( '.schedule-page-show' ).each(function(){
				var title_el = jQuery(this);
				var anchor_el = title_el.find( "a" ).first();
				var title = fixTitle( anchor_el.text() );
				var link = linkIdentifier( anchor_el.attr( "href" ) );
				anchor_el.text( title );

				/* set up user shows */
				if ( isUserShow( title, link ) )
					title_el.parent().addClass( "hrtu_release_page_highlight" );
				else
					title_el.parent().removeClass( "hrtu_release_page_highlight" );
				if ( ! title_el.find( '.hrtu_release_page_toggle' ).length ) {
					title_el.append( '<div class="hrtu_release_page_toggle"></div>' );
					title_el.on( "click", ".hrtu_release_page_toggle", function(e){
						var title = jQuery(this).parent().find( "a" ).text();
						var is_saved = jQuery(this).parent().parent().hasClass( "hrtu_release_page_highlight" );
						if ( is_saved ) {
							removeUserShow( title, link );
							hrtuSidebarRemoveShow( title );
						}
						else
							addUserShow( title, link );
						releasePage();
						sideBar();
						e.stopPropagation();
					});
				}

				/* set up new show */
				if ( ! isAllShow( title, link ) ) {
					title_el.parent().addClass( "hrtu_release_page_highlight_new" );
					if ( ! title_el.find( '.hrtu_release_page_toggle_new' ).length ) {
						title_el.append( '<div class="hrtu_release_page_toggle_new"></div>' );
						title_el.on( "click", ".hrtu_release_page_toggle_new", function(e){
							var title = jQuery(this).parent().find( "a" ).text();
							addShow( title, link );
							releasePage();
							sideBar();
							e.stopPropagation();
						});
					}
				}
				else
					title_el.parent().removeClass( "hrtu_release_page_highlight_new" );
			});
			el.find( '.schedule-show' ).each(function(){
				var title_el = jQuery(this);
 				var title = fixTitle( title_el.text() );
				title_el.text( title );

				/* set up user shows */
				if ( isUserShow( title ) )
					title_el.parent().addClass( "hrtu_release_page_highlight" );
				else
					title_el.parent().removeClass( "hrtu_release_page_highlight" );
				if ( ! title_el.find( '.hrtu_release_page_toggle' ).length ) {
					title_el.append( '<div class="hrtu_release_page_toggle"></div>' );
					title_el.on( "click", ".hrtu_release_page_toggle", function(e){
						var title = jQuery(this).parent().text();
						var is_saved = jQuery(this).parent().parent().hasClass( "hrtu_release_page_highlight" );
						if ( is_saved ) {
							removeUserShow( title );
							hrtuSidebarRemoveShow( title );
						}
						else
							addUserShow( title );
						releasePage();
						sideBar();
						e.stopPropagation();
					});
				}

				/* set up new show */
				if ( ! isAllShow( title ) ) {
					title_el.parent().addClass( "hrtu_release_page_highlight_new" );
					if ( ! title_el.find( '.hrtu_release_page_toggle_new' ).length ) {
						title_el.append( '<div class="hrtu_release_page_toggle_new"></div>' );
						title_el.on( "click", ".hrtu_release_page_toggle_new", function(e){
							var title = jQuery(this).parent().text();
							addShow( title );
							releasePage();
							sideBar();
							e.stopPropagation();
						});
					}
				}
				else
					title_el.parent().removeClass( "hrtu_release_page_highlight_new" );
			});
			el.find( '.schedule-time' ).each(function(){
				var show;
				var time_el = jQuery(this);
				if ( ! time_el.length ) {
					console.warn( "Horriblesubs Release Time Until releasePage(): No .schedule-time found" );
					return false;
				}
				if ( entry_day == 'tbd' || time_el.attr( 'title' ) === "" ) {
					show = {
						time: "00:00",
						text: "",
						direction: 1
					};
				}
				else {
					if ( time_el[0].hasAttribute( 'data-hrtu-time' ) )
						var time_text = time_el.attr( 'title' );
					else
						var time_text = time_el.text();
					
					time_el.title = time_text;
					var match = parseTime( time_text );
					if ( ! match )
						console.warn( "Horriblesubs Release Time Until releasePage(): Unable to parse release time ["+ time_text +"]" );
					show = timeAgo( match.hours, match.minutes, entry_day );
				}
				time_el
					.attr( 'title', time_text )
					.attr( 'data-hrtu-time', show.time )
					.text( show.text )
					.addClass( 'hrtu_time' )
					.parent()
						.addClass( 'hrtu_series_name' );
				if ( show.direction < 0 )
					time_el.addClass( 'hrtu_release_page_time_passed' );

				if ( time_el.parent().hasClass( "hrtu_release_page_highlight" ) ) {
					var title_a = time_el.parent().find( ".schedule-page-show a" );
					hrtuSidebarAddShow( title_a.text(), show.time, show.text, title_a.attr( 'href' ) );
				}
			});
		}
	});
}

function hrtuSidebarAddShow( title, otime, time_text, href ) {
	if ( ! jQuery( '#hrtu_sidebar' ).length ) {
		jQuery( '#sidebar .xoxo' ).first().append(
			'<li id="hrtu_sidebar" class="widget-container widget_text">' +
			'	<h3 class="widget-title">My Shows</h3>' +
			'	<div class="textwidget">' +
			'		<div class="schedule-today hrtu_sidebar">' +
			'			<table class="schedule-table" border="0" cellpadding="0" cellspacing="0">' +
			'				<tbody></tbody>' +
			'			</table>' +
			'		</div>' +
			'	</div>' +
			'</li>'
		);
	}

	var exists = false;
	jQuery( '#hrtu_sidebar .textwidget .schedule-table tbody td.hrtu_sidebar_show_name' ).each(function(){
		if ( jQuery(this).find( 'a' ).text() == title ) {
			exists = true;
			return false;
		}
	});
	if ( exists === false ) {
		var color_class = ( /ago/.test( time_text ) ) ? "hrtu_release_page_time_passed" : "" ;
		jQuery( '#hrtu_sidebar .textwidget .schedule-table tbody' ).append(
			'<tr>' +
			'	<td class="schedule-widget-show hrtu_sidebar_show_name">' +
			'		<a title="See all releases for this show" href="'+ href +'">'+ title +'</a>' +
			'	</td>' +
			'	<td title="'+ otime +'" class="schedule-time hrtu_time '+ color_class +'">'+ time_text +'</td>' +
			'</tr>'
		);
	}
}

function hrtuSidebarRemoveShow( title ) {
	jQuery( '#hrtu_sidebar .textwidget .schedule-table tbody td.hrtu_sidebar_show_name' ).each(function(){
		if ( jQuery(this).find( 'a' ).text() == title ) {
			jQuery( this ).parent().remove();
			return false;
		}
	});
}

function hrtuSidebarClear() {
	jQuery( '#hrtu_sidebar .textwidget .schedule-table tbody tr' ).remove();
}

function hrtuSidebarRefresh() {
	if ( ! jQuery( '.entry-content' ).length || ! jQuery( '.entry-content' ).children().length ) {
		console.warn( "Horriblesubs Release Time Until hrtuSidebarRefresh(): Unable to find release entries" );
		return false;
	}

	hrtuSidebarClear();

	jQuery( '.entry-content' ).children().each( function(){
		var el = jQuery(this);
		if ( el.hasClass( 'schedule-today-table' ) ) {
			el.find( '.schedule-page-show' ).each(function(){
				var title_el = jQuery(this);
				var title = title_el.find( "a" ).text();
				if ( user_shows[ title ] )
					hrtuSidebarAddShow( title );
				else
					hrtuSidebarRemoveShow( title );
			});
		}
	});
}

function addStyles() {
	// added body class to give us some extra specificity to hopefully override page styles
	jQuery( 'body' ).addClass( "hrtu" );
	jQuery( 'head' ).append(
		'<style type="text/css">' +
		'	.hrtu .hrtu_series_name {' +
		'		padding: 0px 6px;' +
		'	}' +
		'	.hrtu .hrtu_series_name:hover {' +
		'		background-color: rgb( 230,230,230 );' +
		'	}' +
		'	.hrtu .hrtu_sidebar_show_name {' +
		'		width: 60%;' +
		'	}' +
		'	.hrtu .hrtu_sidebar_highlight {' +
		'		background-color: rgb( 191,209,236 );' +
		'		color: rgb( 0,0,0 );' +
		'	}' +
		'	.hrtu .hrtu_sidebar_highlight_new {' +
		'		background-color: rgb( 255,255,0 );' +
		'		color: rgb( 0,0,0 );' +
		'	}' +
		'	.hrtu .hrtu_release_page_time_passed {' +
		'		color: rgb( 179,179,179 );' +
		'	}' +
		'	.hrtu .hrtu_sidebar_highlight .hrtu_release_page_time_passed {' +
		'		color: rgb( 129,129,129 );' +
		'	}' +
		'	.hrtu .hrtu_release_page_highlight {' +
		'		background-color: rgb( 214,226,243 );' +
		'	}' +
		'	.hrtu .hrtu_release_page_highlight .hrtu_release_page_time_passed {' +
		'		color: rgb( 144,144,144 );' +
		'	}' +
		'	.hrtu .hrtu_time {' +
		'		white-space: nowrap;' +
		'	}' +
		'	.hrtu .hrtu_release_page_toggle {' +
		'		width: 24px;' +
		'		height: 24px;' +
		'		text-align: center;' +
		'		line-height: 24px;' +
		'		cursor: pointer;' +
		'		display: inline-block;' +
		'		margin-left: 7px;' +
		'	}' +
		'	.hrtu .hrtu_release_page_toggle_new {' +
		'		text-align: center;' +
		'		line-height: 24px;' +
		'		cursor: pointer;' +
		'		display: inline-block;' +
		'		margin-left: 7px;' +
		'	}' +
		'	.hrtu .hrtu_release_page_toggle:before {' +
		'		content: "[+]";' +
		'	}' +
		'	.hrtu .hrtu_release_page_highlight .hrtu_release_page_toggle:before {' +
		'		content: "[-]";' +
		'	}' +
		'	.hrtu .hrtu_release_page_highlight_new {' +
		'		background-color: rgb( 255,255,0 )' +
		'	}' +
		'	.hrtu .hrtu_release_page_highlight_new .hrtu_release_page_toggle_new:before {' +
		'		content: "[NEW]";' +
		'		font-weight: bold;' +
		'	}' +
		'	#hrtu_unmark_all_new {' +
		'		height: 22px;' +
		'		border: 1px solid rgb( 200,200,200 );' +
		'		cursor: pointer;' +
		'		color: rgb( 0,102,204 );' +
		'		border-radius: 25px;' +
		'		padding: 0px 8px 2px;' +
		'		box-shadow: 0px 0px 5px rgba( 0,0,0, 0.1 );' +
		'	}' +
		'	#hrtu_unmark_all_new:hover {' +
		'		border-color: rgb( 120,120,120 );' +
		'	}' +
		'	.hrtu_show_outbound_links {' +
		'		padding: 0px 8px 2px;' +
		'		display: inline-block;' +
		'		margin-top: 14px;' +
		'		floaT: right;' +
		'	}' +
		'	.hrtu_show_outbound_link {' +
		'		display: inline-block;' +
		'		color: rgb( 170,175,191 );' +
		'		border-right: 1px solid rgb( 220,220,220 );' +
		'		font-style: italic;' +
		'		padding: 0px 7px 0px 3px;' +
		'	}' +
		'	.hrtu_show_outbound_link:last-child {' +
		'		border: 0px;' +
		'	}' +
		'</style>'
	);
}

function showPage() {

	jQuery( "article" ).each(function(){
		var el = jQuery(this);
		var title = encodeURIComponent( el.find( "> header .entry-title" ).text() );
		var info_el = el.find( ".series-info" );
		if ( ! info_el.find( ".hrtu_show_outbound_links" ).length ) {
			info_el.append(
				'<div class="hrtu_show_outbound_links">' +
				'	<a class="hrtu_show_outbound_link"href="http://anidb.net/perl-bin/animedb.pl?adb.search='+ title +'&show=animelist&do.search=search">aniDB</a>' +
				'	<a class="hrtu_show_outbound_link" href="http://www.anime-planet.com/anime/all?name='+ title +'">Anime-Planet</a>' +
				'	<a class="hrtu_show_outbound_link" href="http://myanimelist.net/anime.php?q='+ title +'">MAL</a>' +
				'	<a class="hrtu_show_outbound_link" href="https://hummingbird.me/search?query='+ title +'">Hummingbird</a>' +
				'</div>'
			);
		}
	});
	
}



/* Userscript Logic */

updateVersion();

addStyles();
sideBar();
if ( window.location.pathname == '/release-schedule/' )
	releasePage();
else if ( /\/shows\/./.test( window.location.pathname ) )
	showPage();

setInterval( function(){
	sideBar();
	if ( window.location.pathname == '/release-schedule/' )
		releasePage();
}, 5000 );