// ==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);
};
})();