PSNprofiles.net enhancements

On guide pages: adds a button to hide earend trophies, their description and links and uses a new style for earned trophies, On all pages: adds update button, On game pages: persist search string

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        PSNprofiles.net enhancements
// @author      Barokai | www.loigistal.at
// @icon        http://psnprofiles.com/forums/favicon.ico
// @namespace   http://www.loigistal.at/userscripts/
// @license     https://github.com/Barokai/psnprofiles-enhanced/blob/master/LICENSE
// @homepageURL http://barokai.github.io/psnprofiles-enhanced/
// @supportURL  https://github.com/Barokai/psnprofiles-enhanced/issues/new
// @version     0.86
// @description On guide pages: adds a button to hide earend trophies, their description and links and uses a new style for earned trophies, On all pages: adds update button, On game pages: persist search string
// @match       http*://psnprofiles.com/*
// @grant       GM_addStyle
// @require     http://code.jquery.com/jquery-latest.js
// @copyright   2016+, Barokai
// ==/UserScript==

// better visible green for earned trophies, used before: #57FF3B
/*jshint multistr: true */
GM_addStyle("\
.tableofcontents li.earned {	\
background: #bada55 !important;  \
} \
.roadmap-trophies li.earned { \
background: #bada55 !important; \
} \
.roadmap-trophies .trophy.earned { \
background: #bada55 !important; \
} \
.section-holder.earned{ \
border: 3px solid #bada55; \
} \
#toggleEarned { \
background-color: #bada55; \
}\
.invisible.tags a.earned{\
text-shadow: 1px 1px 1px #bada55;\
}\
");


/* Guide enhancements ------------------------------------------------------- */
// TODO barokai: fix scrolling behavior when trophies are hidden and a trophy link is clicked in Guide Contens column (ID="TOCWrapper")
function addToggleEarnedButton() {
	// add class earned to all links which match earned trophies in overview-info box
	$('.earned > a').each(function() {
		var trophyName = $(this).text().trim();
		$('nobr > a:contains(' + trophyName + ')').addClass("earned");
	});

	// adds class "earned" to sections to hide them with the same toggle function
	$("img[class*='earned']").each(function() {
		// .closest('li').parent().closest('li')
		// TODO check if class of img isn't .unearned!
		$($(this).closest("div[class*='section-holder']").parent().closest("div[class*='section-holder']")).addClass("earned");
	});

	// workaround, above scripts adds earned to all secionts as img with class .unearned is matched too.
	$("img[class*='unearned']").each(function() {
		// .closest('li').parent().closest('li')
		$($(this).closest("div[class*='section-holder']").parent().closest("div[class*='section-holder']")).removeClass("earned");
	});

	// workaround to hide trophies in overview completely
	$(".trophy.earned").each(function(){
		$($(this).closest(".col-xs-6")).addClass("earned");
		$($(this).closest(".col-xs-12")).addClass("earned");
	});

	// adds button for toggling to overview info box
	$(".overview-info").append('<span class="tag" id="toggleEarned" title="click to toggle visiblity of earned trophies"><span class="typo-top">toggle</span><br/><span class="typo-bottom">earned</span>');
	$(document).on("click", "#toggleEarned", toggleEarned);
}

// TODO: fix this, doesn't work in new version.
function addToggleTypeButton() {
	// get trophy types (without spaces) - used as classes to toggle them later.
	var trophyTypes = $('table.invisible .tag').map(function(i, el) {
		var type = $(el);
		var typeName = type.text().replace(/\s+/g, '');
		// add toggle visibility to type boxes
		type.click(function(e) {
			toggleClass(e, typeName);
		});
		return typeName;
	}).get();

	// get corresponding trophies - start with the ones in overview box
	$('table.invisible td small').each(function(index) {
		var typeClassName = trophyTypes[index];
		var trophyHref = $("nobr a", $(this));
		// add class to all found hrefs for this section
		trophyHref.addClass(typeClassName);

		trophyHref.each(function() {
			var trophyName = $(this).text();

			// contents of roadmap
			$(".roadmap-trophies li:contains(" + trophyName + ")").addClass(typeClassName);

			// all the sections
			$("div[class*='element section-holder']:contains(" + trophyName + ")").addClass(typeClassName);

			// next the ones in the guide contents on the right
			$('#TOCList li:contains(' + trophyName + ')').addClass(typeClassName);

			// add class to trophies which are found in multible type sections
			$('table.invisible nobr a:contains(' + trophyName + ')').addClass(typeClassName);
		});
	});
}

function toggleEarned(e) {
	toggleClass(e, "earned");
}

function toggleClass(e, className) {
	var element;

	// get correct element (togglebutton's layout would be destroyed otherwise)
	if (e.target.parentNode.id != "toggleEarned") {
		element = e.target;
	} else {
		element = e.target.parentNode;
	}

	if (element.innerHTML.indexOf(" *") >= 0) {
		element.innerHTML = element.innerHTML.slice(0, -2);
	} else {
		element.innerHTML += " *";
	}

	$("." + className).toggle("slow", function(e) { /* Animation complete */ });
}
/* Guide enhancements end --------------------------------------------------- */

/* Profile enhancements ----------------------------------------------------- */

// thanks to serverTimeout for sort by rank (his profile: http://psnprofiles.com/forums/user/80890-servertimeout/)
// see his post here: http://psnprofiles.com/forums/topic/24324-sort-by-rank/?view=findpost&p=647509
function addSortByRank() {
	var dropdown = $('.dropdown-toggle.order');
	var buttonNameAsc = "Rank E-S";
	dropdown.next().append(
		$('<li><a href="">' + buttonNameAsc + '</a></li>').on('click', function(ev) {
			sort(ev, '+', buttonNameAsc); // + for ascending sort
		})
	);
	var buttonNameDesc = "Rank S-E";
	dropdown.next().append(
		$('<li><a href="">' + buttonNameDesc + '</a></li>').on('click', function(ev) {
			sort(ev, '-', buttonNameDesc); // + for descending sort
		})
	);
}

function sort(e, order, buttonName) {
	e.preventDefault();
	var trophyOrder = ['F', 'E', 'D', 'C', 'B', 'A', 'S'];
	var r;
	if (order === '+') {
		for (r = 0; r <= trophyOrder.length; r++) {
			// TODO: sort ranks by percent before
			$('.' + trophyOrder[r]).each(function() {
				$('.'+trophyOrder[r]).each(function() {
					$('#content table.zebra').eq(0).append(jQuery(this).closest('tr'));
				});
			});
		}
	}
	if (order === '-') {
		for (r = trophyOrder.length; r >= 0; r--) {
			// TODO: sort ranks by percent before
			$('.' + trophyOrder[r]).each(function() {
				$('.'+trophyOrder[r]).each(function() {
					$('#content table.zebra').eq(0).append(jQuery(this).closest('tr'));
				});
			});
		}
	}

	// set dropdown button name when new order was set
	$('.dropdown-toggle.order').text("Order (" + buttonName + ")");
}
/* Profile enhancements end ------------------------------------------------- */

/* Global enhancements ------------------------------------------------------ */
function addUpdateButton() {
	$('.navigation > ul').append("<li><a href='/?update'>Update Profile</a></li>");
	// TODO barokai: check if successfully updated and redirect to the last visited page (where update was clicked)
}
/* Global enhancements end ---------------------------------------------------*/

/* Games page enhancements -------------------------------------------------- */

// thanks to dernop for this searchFix (his profile: http://psnprofiles.com/forums/user/45256-dernop/)
// see his post here: http://psnprofiles.com/forums/topic/32107-bugsoddities-in-the-games-search-feature/#entry777278
function gameSearchFix() {
	$('#searchGames').off('keyup');
	$('#searchGames').keyup(function() {
		window.clearTimeout('searchInt');
		var input = $(this);
		if (input.val().length > 1) {
			$('#loading').show();
			$('#closeButton').hide();

			var searchInt = window.setTimeout(
				function() {
					$('#pagination').hide();
					var q = encodeURIComponent(input.val());
					$.ajax({
						url: "/php/liveSearch.php?t=g&q=" + q,
						success: function(html) {
							window.location.hash = '#!' + q;
							$('#game_list').html(html);
							$('#loading').hide();
							$('#closeButton').show();
						}
					});
				}, 500);
		} else {
			$('#loading').hide();
		}
	});
	var query = decodeURIComponent(window.location.hash.replace('#!', ''));

	if (query.length > 0) {
		$('#searchGames').val(query).keyup();
	}
}

// TODO barokai: integrate other func in psnp
var psnp = {
	id: $('div.user-menu a.dropdown-toggle span').text(),
	_gamesTable: $('table#gamesTable'), // game table on user profile
	_gameList: $('table#game_list'),
	_profileBar: $('div.profile-bar')
};
// add percentage on mouseover or integrate into game row
// add mouse over information like percentage (last row), last played if available etc.
// thanks to dernop (again) - http://psnprofiles.com/forums/topic/35583-add-possibility-to-hide-earned-trophies-in-guides-with-userscript/#entry932561
$(function() {
	// initialize psnp page/DOM information

	// psnp properties
	$.extend(psnp, {
		isProfile: psnp._gamesTable[0] !== undefined && psnp._profileBar[0] !== undefined,
		isOwnProfile: $(psnp._profileBar).find('div.info').text().indexOf(psnp.id) > -1,
		isGameList: psnp._gameList.length == 1,
		myGames: JSON.parse(localStorage.getItem('_mygames')) || {}
	});

	psnp.updateMyGames = function(games) {
		var count = 0;
		$.each(games, function(i, e) {
			psnp.myGames[e.id] = e;
			count++;
		});
		localStorage.setItem('_mygames', JSON.stringify(psnp.myGames));
		console.log(count + " games added/updated to localstorage.");
	};

	// PROFILE / GAME LIST
	psnp.gameList = (function() {
		if (!psnp.isProfile)
			return undefined;
		var _games = [];
		// register mutationobserver for gameList to handle 'load-on-scroll'
		var obs = new MutationObserver(function(mutations) {
			parseGames(); // just re-parse all games if list has changed.
		});
		obs.observe(psnp._gamesTable[0], {
			childList: true,
			subtree: true
		});

		// parse PSNP games table (id, name, completion, # of trophies)
		function parseGames() {
			_games = [];
			psnp._gamesTable.find('tr:has(a.bold)').each(function(i, row) {
				var title = $(row).find('a.bold')[0];
				var game = {
					id: title.href.match(/\/trophies\/([^\/]+)/)[1],
					name: title.innerText,
					progress: $(row).find('div.progress_outer span').text(),
					completed: $(row).hasClass('completed'),
					platinum: $(row).hasClass('platinum'),
					gold: $(row).find('li.gold').text(),
					silver: $(row).find('li.silver').text(),
					bronze: $(row).find('li.bronze').text(),
				};

				if (!psnp.isOwnProfile && psnp.myGames[game.id]) {
					var img = $(row).find('img.trophy_image');
					img.removeClass('no-border').addClass('earned'); // mark owned games
				}
				_games.push(game);
			});

			// if this is our own profile, save the games list.
			if (psnp.isOwnProfile) {
				psnp.updateMyGames(_games);
			}
		}

		// parse game list initially
		parseGames();

		return {
			games: _games
		};
	})();

	// #GAMELIST#  global list of games (psnprofiles.com/games)

	// Mark owned game in "Games" list
	if (psnp.isGameList) {
		psnp._gameList.find('tr:has(a.bold)').each(function(i, row) {
			var title = $(row).find('a.bold')[0];
			var id = title.href.match(/\/trophies\/([^\/"]+)/)[1];
			var img = $(row).find('img.trophy_image');

			// mark owned games
			if (psnp.myGames[id]) {
				if (psnp.myGames[id].platinum)
					$(row).addClass('platinum'); // add platinum row style
				if (psnp.myGames[id].completed)
					$(row).addClass('completed'); // add completed row style

				img.removeClass('no-border').addClass('earned');
				if (psnp.myGames[id].progress != '100%')
					img.css('border-color', 'yellow'); //

				// add completion percentage
				var avgProgress = $(row).children('td:eq(4)').find('span.typo-top');
				console.log(avgProgress);
				avgProgress.text(psnp.myGames[id].progress + " / " + avgProgress.text());

			}
		});
	}
});

/* Games page enhancements end -----------------------------------------------*/

// helperfunction to determine if the url matches a certain segment
function matchesUrl(urlSegment) {
	return document.location.pathname.indexOf(urlSegment) === 0;
}

/* -------------------------------------------------------------------------- */
/* Apply enhancements to correct pages -------------------------------------- */
/* -------------------------------------------------------------------------- */

// add toggle button functionality to all guides (if any earned trophies were found
//matchesUrl("/guide/") && addToggleTypeButton(); //TODO: re-enable after fix
matchesUrl("/guide/") && $('.earned > a').length > 0 && addToggleEarnedButton();
// add searchFix to games page
matchesUrl("/games") && gameSearchFix();
// add update button to navigation on all psnprofile pages (if logged in)
psnp.id && addUpdateButton();
// add only on profile pages and others where a sort dropdown is
matchesUrl("/" + psnp.id) && addSortByRank();

// http://stackoverflow.com/a/27363569
(function() {
	var origOpen = XMLHttpRequest.prototype.open;

	XMLHttpRequest.prototype.open = function() {
		// console.log('request started!');
		this.addEventListener('load', function() {
			if (~this.responseURL.indexOf("status?user=")){
				// console.log('request completed!');
				//console.log(this.readyState); //will always be 4 (ajax is completed successfully)
				//console.log(this.responseText); //whatever the response was
				var obj = jQuery.parseJSON(this.responseText);
				var requestName = this.responseURL.substr(this.responseURL.lastIndexOf('/') + 1).replace(psnp.id, "");
				var status = obj.status.replace(psnp.id, "");
				if(requestName == "status?user=" && status === " has been updated"){
					window.location.href = "https://psnprofiles.com/" + psnp.id;
				}
			}
		});

		origOpen.apply(this, arguments);
	};
})();