For making SP a little bit better
// ==UserScript==
// @name Better Soccer Project
// @namespace sp
// @description For making SP a little bit better
// @include *soccerproject.com/*
// @version 2.1
// @grant none
// ==/UserScript==
// add jquery
//add jquery
function includeJs(jsFilePath) {
var js = document.createElement("script");
js.type = "text/javascript";
js.src = jsFilePath;
document.body.appendChild(js);
}
includeJs("//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js");
function bootSP(){
if(typeof window.jQuery !== 'function'){
window.setTimeout(bootSP, 150);
return;
}
var $ = window.jQuery;
//Rebind console to c
var c = false;
if(typeof console === "object" && typeof console.error === "function"){
c = console,
c = function (msg){"use strict"; console.info(msg);};
}
var SP = {};
function parsePercentValue(text) {
var match = String(text || '').match(/(\d+)/);
return match ? parseInt(match[1], 10) : 0;
}
function getHiddenSkillEstimateFromDetails(statsTable) {
var excludedLabels = {
'Club': true,
'Fitness': true,
'Global rating': true,
'Morale': true,
'Aggression': true,
'Experience': true
};
var rating = 0;
var visibleSkills = [];
$(statsTable).find('tr').each(function () {
var cells = $(this).find('td');
if (cells.length < 2) return;
var label = $(cells[0]).text().replace(/\s+/g, ' ').trim();
var value = parsePercentValue($(cells[1]).text());
if (!label) return;
if (label === 'Global rating') {
rating = value * 10;
return;
}
if (!excludedLabels[label]) {
visibleSkills.push(value * 10);
}
});
if (!rating || visibleSkills.length < 9) return 0;
var regularSkillTotal = visibleSkills.slice(0, 7).reduce(function (sum, value) {
return sum + value;
}, 0);
var mainSkill1 = visibleSkills[7];
var mainSkill2 = visibleSkills[8];
var mainSkillSum = mainSkill1 + mainSkill2;
var morale = parsePercentValue($(statsTable).find('tr td:first-child').filter(function(){ return $(this).text().replace(/\s+/g, ' ').trim() === 'Morale'; }).next('td').first().text()) * 10;
var experience = parsePercentValue($(statsTable).find('tr td:first-child').filter(function(){ return $(this).text().replace(/\s+/g, ' ').trim() === 'Experience'; }).next('td').first().text()) * 10;
var estimate = 4 + (7.5 * rating) - (0.5 * regularSkillTotal) - mainSkillSum - (0.5 * morale) - (0.5 * experience);
return Math.min(Math.max(Math.round(estimate), 0), 1000);
}
function addHiddenSkillToPlayerDetails() {
const tables = document.querySelectorAll(".player_detail_window");
const statsTable = tables[2];
if (!statsTable || statsTable.querySelector(".sp-hidden-skill-row")) return;
var hiddenSkill = getHiddenSkillEstimateFromDetails(statsTable);
if (!hiddenSkill) return;
var hiddenPercent = (hiddenSkill / 10).toFixed(1).replace(/\.0$/, '');
var powerBand = Math.ceil((hiddenSkill / 10) / 10) * 10;
var powerClass = powerBand >= 100 ? 'power100 powermax' : 'power' + (powerBand - 10) + 'a' + powerBand;
$(statsTable).find("tr").eq(2).after(
`<tr class="sp-hidden-skill-row"><td>Hidden skill</td><td class="kleurright" title="Estimated from visible stats">${hiddenPercent} %</td><td><div class="powerbar" title="${hiddenPercent} % (estimated)"><img src="https://media.soccerproject.com/trans2.gif" class="powerbarfill ${powerClass}" style="display:block;width:${hiddenPercent}%;" alt="${hiddenPercent}% estimated" /></div></td></tr>`
);
}
//urls
const setNavigation = () => {
$('head').append('<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">')
$('body').append(
'<div aria-expanded="false" class="menuBtn" id="hamburgerToggle" role="button"><div class="menuBtnContainer"><div></div><div></div><div></div></div></div>'
)
$('#navigation li:last').remove()
$('#navigation li:first').after(
$(
'<li><a href="./spnewl_team_fixtures.php" class="more">Fixtures</a></li><li><a href="./spnewl_team_results.php" class="more">Results</a></li><li><a' +
' href="./spnewl_speler_overview.php" class="more">Players</a></li><li><a href="./spnewl_friendly_pool.php" class="more">Friendlies</a></li><li><a' +
' href="./spnewl_transfer_overview.php" class="more">Transfer Overview</a><li><a' +
' href="./spnewl_friendlycup_invitations.php?step=6" class="more">FC</a></li>'
)
)
}
/*************************************
* FUNCTIONS *
**************************************/
var GOOD_PLAYERS_STORAGE_KEY = 'sp_public_good_players';
var GOOD_PLAYERS_STORAGE_TS_KEY = 'sp_public_good_players_timestamp';
var GOOD_PLAYERS_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1000;
var GOOD_PLAYERS_MIN_HIDDEN = 950;
function parsePriceValue(text) {
var normalized = String(text || '')
.replace(/\u00a0/g, ' ')
.replace(/\s+/g, ' ')
.trim();
if (!normalized) return '';
var match = normalized.match(/€\s*([\d.,]+)\s*([MK])?/i);
if (!match) return normalized;
var amount = match[1].replace(/\./g, '').replace(',', '.');
var suffix = (match[2] || '').toUpperCase();
return '€ ' + amount + (suffix ? ' ' + suffix : '');
}
function extractPlayerIdFromOnclick(onclickValue) {
var match = String(onclickValue || '').match(/LoadPlayerInDiv\((\d+),/);
return match ? parseInt(match[1], 10) : 0;
}
function parsePowerbarPercent(cell) {
if (!cell) return 0;
var img = cell.querySelector('.powerbarfill');
if (img) {
var alt = img.getAttribute('alt') || '';
var altMatch = alt.match(/(\d+)/);
if (altMatch) return parseInt(altMatch[1], 10);
}
var powerbar = cell.querySelector('.powerbar');
if (powerbar) {
var title = powerbar.getAttribute('title') || '';
var titleMatch = title.match(/(\d+)/);
if (titleMatch) return parseInt(titleMatch[1], 10);
}
return parsePercentValue(cell.textContent);
}
function getTransferTableFromRoot(root) {
var tables = root.querySelectorAll('table.sortable, table[summary=""]');
for (var i = 0; i < tables.length; i++) {
var headers = Array.prototype.slice.call(tables[i].querySelectorAll('th')).map(function (th) {
return $(th).text().replace(/\s+/g, ' ').trim();
});
if (headers.indexOf('Name') !== -1 && headers.indexOf('Age') !== -1 && headers.indexOf('Rating') !== -1 && headers.indexOf('Price') !== -1) {
return tables[i];
}
}
return null;
}
function parseTransferPlayersFromDocument(root) {
var table = getTransferTableFromRoot(root);
if (!table) return [];
var players = [];
$(table).find('tr').each(function () {
var row = this;
if (row.id && row.id.indexOf('id_') === 0) return;
var cells = row.querySelectorAll('td');
if (cells.length < 6) return;
var link = cells[1].querySelector('a.dummylink');
if (!link) return;
var playerId = extractPlayerIdFromOnclick(link.getAttribute('onclick'));
if (!playerId) return;
var name = $(link).attr('title') || $(link).text().replace(/\u00a0/g, ' ').trim();
var rating = parsePowerbarPercent(cells[3]) * 10;
players.push({
id: playerId,
name: name,
age: parsePercentValue(cells[2].textContent),
rating: rating,
timeframe: $(cells[4]).text().replace(/\u00a0/g, ' ').replace(/\s+/g, ' ').trim(),
price: parsePriceValue(cells[5].textContent),
buyNow: parsePriceValue(cells[6] ? cells[6].textContent : ''),
url: 'spnewl_transfer_buy.php?step=2&spid=' + playerId
});
});
return players;
}
function getPaginatorPageUrls(root) {
var urls = [window.location.href];
$(root).find('#paginator a').each(function () {
var href = this.getAttribute('href');
if (!href) return;
var absolute = new URL(href, window.location.origin).href;
if (urls.indexOf(absolute) === -1) urls.push(absolute);
});
return urls;
}
function fetchHtmlDocument(url) {
return $.get(url).then(function (html) {
return new DOMParser().parseFromString(html, 'text/html');
});
}
function getStatsTableFromDetailDocument(doc) {
var tables = doc.querySelectorAll('.player_detail_window');
return tables && tables[2] ? tables[2] : null;
}
function fetchPlayerDetailSummary(playerId) {
var url = '/spnewl_speler_detail.php?spid=' + playerId + '&newWindow=1';
return new Promise(function (resolve) {
$.get(url)
.done(function (html) {
var doc = new DOMParser().parseFromString(html, 'text/html');
var statsTable = getStatsTableFromDetailDocument(doc);
resolve({ hidden: statsTable ? getHiddenSkillEstimateFromDetails(statsTable) : 0 });
})
.fail(function () {
resolve({ hidden: 0 });
});
});
}
function cleanupOldGoodPlayers() {
var timestamp = parseInt(localStorage.getItem(GOOD_PLAYERS_STORAGE_TS_KEY), 10);
if (!timestamp) return [];
if ((Date.now() - timestamp) > GOOD_PLAYERS_MAX_AGE_MS) {
localStorage.removeItem(GOOD_PLAYERS_STORAGE_KEY);
localStorage.removeItem(GOOD_PLAYERS_STORAGE_TS_KEY);
return [];
}
try {
return JSON.parse(localStorage.getItem(GOOD_PLAYERS_STORAGE_KEY)) || [];
} catch (e) {
localStorage.removeItem(GOOD_PLAYERS_STORAGE_KEY);
localStorage.removeItem(GOOD_PLAYERS_STORAGE_TS_KEY);
return [];
}
}
function saveGoodPlayers(players) {
var filteredPlayers = (players || []).filter(function (player) {
return player && Number(player.hidden) >= GOOD_PLAYERS_MIN_HIDDEN;
});
localStorage.setItem(GOOD_PLAYERS_STORAGE_KEY, JSON.stringify(filteredPlayers));
localStorage.setItem(GOOD_PLAYERS_STORAGE_TS_KEY, String(Date.now()));
}
function mergeGoodPlayers(existingPlayers, newPlayers) {
var byId = {};
(existingPlayers || []).forEach(function (player) {
if (!player || !player.id || Number(player.hidden) < GOOD_PLAYERS_MIN_HIDDEN) return;
byId[player.id] = player;
});
(newPlayers || []).forEach(function (player) {
if (!player || !player.id || Number(player.hidden) < GOOD_PLAYERS_MIN_HIDDEN) return;
byId[player.id] = player;
});
return Object.keys(byId).map(function (key) {
return byId[key];
});
}
function ensureGoodPlayersContainer() {
if (!document.getElementById('divShowAll')) {
var paginator = document.getElementById('paginator');
var container = document.createElement('div');
container.id = 'divShowAll';
container.style.marginTop = '12px';
if (paginator && paginator.parentNode) {
paginator.parentNode.insertBefore(container, paginator.nextSibling);
} else {
document.querySelector('#content').appendChild(container);
}
}
return $('#divShowAll');
}
function getSelectedTransferPositionLabel() {
var select = document.getElementById('selpos');
if (!select) return '';
var option = select.options[select.selectedIndex];
return option ? String(option.text || '').replace(/\s+/g, ' ').trim() : '';
}
function ensureFindGoodPlayersButton() {
if ($('.findGoodPlayers').length) return;
var buttonHtml = '<a class="button findGoodPlayers" href="#" style="margin-right:8px;">Find Good Players</a>';
if ($('#paginator').length) {
$('#paginator').before(buttonHtml);
return;
}
var table = getTransferTableFromRoot(document);
if (table) {
$(table).before(buttonHtml);
return;
}
$('#content').append(buttonHtml);
}
function renderGoodPlayers(players) {
var container = ensureGoodPlayersContainer();
if (!players.length) {
container.html('<div class="box"><b>Good Players</b><div>No players found with hidden skill 950 or more.</div></div>');
return;
}
var rows = players.map(function (player) {
return '<tr>' +
'<td style="text-align:left"><a href="#" title=" ' + player.name + ' " onclick="window.open(\'spnewl_speler_detail.php?spid=' + player.id + '\', \'_blank\',\'height=640, width=500\'); return false;">' + player.name + '</a></td>' +
'<td style="text-align:center">' + (player.position || '-') + '</td>' +
'<td style="text-align:center">' + player.age + '</td>' +
'<td style="text-align:center">' + (player.rating / 10).toFixed(0) + '%</td>' +
'<td style="text-align:center">' + (player.hidden / 10).toFixed(0) + '%</td>' +
'<td style="text-align:center">' + player.timeframe + '</td>' +
'<td style="text-align:center">' + player.price + '</td>' +
'<td style="text-align:center">' + (player.buyNow || '-') + '</td>' +
'<td style="text-align:center"><a href="' + player.url + '">Open</a></td>' +
'</tr>';
}).join('');
container.html(
'<div class="box">' +
'<div style="margin-bottom:6px;"><b>Good Players</b> - hidden skill 950 or more (' + players.length + ')</div>' +
'<table class="sortable" summary=""><tbody>' +
'<tr><th style="text-align:left">Name</th><th>Pos</th><th>Age</th><th>Rating</th><th>Hidden</th><th>Timeframe</th><th>Price</th><th>Buy Now</th><th>Link</th></tr>' +
rows +
'</tbody></table>' +
'</div>'
);
}
function setGoodPlayersStatus(text) {
ensureGoodPlayersContainer().html('<div class="box"><b>Good Players</b><div>' + text + '</div></div>');
}
function findGoodPlayers() {
var existingPlayers = cleanupOldGoodPlayers();
var selectedPosition = getSelectedTransferPositionLabel();
setGoodPlayersStatus('Scanning transfer pages...');
var pageUrls = getPaginatorPageUrls(document);
var allPlayers = [];
var seenIds = {};
var chain = Promise.resolve();
pageUrls.forEach(function (pageUrl, pageIndex) {
chain = chain.then(function () {
setGoodPlayersStatus('Scanning page ' + (pageIndex + 1) + ' of ' + pageUrls.length + '...');
return fetchHtmlDocument(pageUrl).then(function (doc) {
var pagePlayers = parseTransferPlayersFromDocument(doc);
var innerChain = Promise.resolve();
pagePlayers.forEach(function (player, playerIndex) {
if (seenIds[player.id]) return;
seenIds[player.id] = true;
innerChain = innerChain.then(function () {
setGoodPlayersStatus('Checking ' + player.name + ' (' + (allPlayers.length + playerIndex + 1) + ')...');
return fetchPlayerDetailSummary(player.id).then(function (summary) {
if (summary.hidden >= GOOD_PLAYERS_MIN_HIDDEN) {
player.hidden = summary.hidden;
player.position = selectedPosition || player.position || '';
allPlayers.push(player);
}
});
});
});
return innerChain;
});
});
});
chain.then(function () {
var mergedPlayers = mergeGoodPlayers(existingPlayers, allPlayers);
mergedPlayers.sort(function (a, b) {
if (b.hidden !== a.hidden) return b.hidden - a.hidden;
if (b.rating !== a.rating) return b.rating - a.rating;
return a.age - b.age;
});
saveGoodPlayers(mergedPlayers);
renderGoodPlayers(mergedPlayers);
}).catch(function (error) {
console.error(error);
setGoodPlayersStatus('Failed to scan transfer pages.');
});
}
function removeAllPlayers () {
$('#formation_field select').each(function(){ //loop through selectable players.
var me = $(this),
options = me.find('option');
me.find('option:selected').removeAttr('selected'); //remove all selected items
options.first().attr('selected','selected');
updatePlayer(options.first().parent().get(0));
});
}
function updatefitestplayers () {
removeAllPlayers ();
var allPlayers = [], i=0;
$('[id^=trPlayer]').each(function(){ //loop through all of the players
var player = $(this).find('td');
var fitness = player.eq(3).text().replace( /^\D+/g, '');
var fitness = fitness.substring(0, fitness.length - 1); //get out their fitness and then remove the trailing %
var playerID = player.eq(0).find('a').attr("onclick").replace( /^\D+/g, '');
var playerID = playerID.slice(0,8);
var playerID = parseInt(playerID, 10); // get out the players ID
allPlayers.push([playerID,fitness]); //update array of players
});
allPlayers.sort(function(a,b){ //sort players by highest fitness
return a[1] - b[1];
}).reverse();
allPlayers = allPlayers.slice(0,16); //remove the most unfit players
console.info(allPlayers);
$('#formation_field select').each(function(){ //loop through selectable players.
var me = $(this),
options = me.find('option');
me.find('option:selected').removeAttr('selected'); //remove all selected items
options.each(function(){ //loop through and add selected if it matches
if($(this).val()==allPlayers[i][0]){
var meText = $(this).text(),
number = meText.substring(0,2),
name = meText.substring(4);
$(this).attr('selected','selected');
updatePlayer(this.parentNode.parentElement);
};
});
i++;
});
}
function updateLowestMoralPlayers () {
//removeAllPlayers ();
var allPlayers = [], i=0;
$('[id^=trPlayer]').each(function(){ //loop through all of the players
var player = $(this).find('td');
var moral = player.eq(2).text().replace( /^\D+/g, ''); //get moral from players
var moral = moral.substring(0, moral.length - 1); //get out their fitness and then remove the trailing %
var playerID = player.eq(0).find('a').attr("onclick").replace( /^\D+/g, '');
var playerID = playerID.slice(0,8);
var playerID = parseInt(playerID, 10); // get out the players ID
allPlayers.push([playerID,moral]); //update array of players
});
allPlayers.sort(function(a,b){ //sort players by highest fitness
return a[1] - b[1];
});
allPlayers = allPlayers.slice(0,16);
$('#formation_field select').each(function(){ //loop through selectable players.
var me = $(this),
options = me.find('option');
me.find('option:selected').removeAttr('selected'); //remove all selected items
options.each(function(){ //loop through and add selected if it matches
if(typeof allPlayers[i] !== "undefined" && $(this).val()==allPlayers[i][0]){
var meText = $(this).text(),
number = meText.substring(0,2),
name = meText.substring(4);
$(this).attr('selected','selected');
updatePlayer(this.parentNode.parentElement);
};
});
i++;
});
}
/*************************************
* Players *
**************************************/
function workOutStam(type){
if(type==='startNonFullyStam'){
selectArray.forEach(function(key) {
var select = selects[key],
stam = selects[key][1],
currentlySelected = selects[key][selects[key].selectedIndex];
if(typeof stam !== "undefined" && stam.className!=="maxed"){
currentlySelected.removeAttribute('selected');
stam.setAttribute('selected', 'selected');
}
});
}
if(type==='startStamFullMaxedOtherSkills'){
var i=1;
selectArray.forEach(function(key) {
i++;
var select = selects[key],
stam = selects[key][1],
skill1 = selects[key][1],
skill2 = selects[key][2],
skill3 = selects[key][3],
skill4 = selects[key][4],
skill5 = selects[key][5],
skill6 = selects[key][6],
skill7 = selects[key][7],
skill8 = selects[key][8],
skill9 = selects[key][9],
nothing = selects[key][0],
currentlySelected = selects[key][selects[key].selectedIndex],
maxedNumber = $(select).find('.maxed').length;
if(typeof stam !== "undefined" && maxedNumber===8){
currentlySelected.removeAttribute('selected');
stam.setAttribute('selected', 'selected');
}else if(typeof stam !== "undefined" && maxedNumber===9){
currentlySelected.removeAttribute('selected');
stam.removeAttribute('selected');
nothing.setAttribute("selected", "selected");
select.removeAttribute('style');
$('<span>fully trained</span>').insertAfter(select);
}
});
}
}
/*************************************
* Pages *
**************************************/
if(document.location.pathname==="/spnewl_speler_training.php"){
var selects = document.getElementsByTagName('select'),
selectArray = Object.keys(selects).slice(0,selects.length);
selectArray.forEach(function(key) {
var myself = selects[key][selects[key].selectedIndex];
if(typeof myself !== "undefined" && myself.className==="maxed"){
selects[key].setAttribute("style","background: red; color: #FFF");
}
});
$('#trainform .pbutton').append('<a class="button startStam" href="#">Start ALL Non Fully Trained Stamina</a><a class="button startStamFullMaxedOtherSkills" href="#">Start Stam on players with maxed everything else</a>');
$('.startStam').click(function(){
workOutStam('startNonFullyStam');
return false;
});
$('.startStamFullMaxedOtherSkills').click(function(){
workOutStam('startStamFullMaxedOtherSkills');
return false;
});
}
if(document.location.pathname==="/spnewl_game_selectie.php"){
if($('.info').text() === 'The selection was saved.'){
//window.location.href = '/spnewl_team_fixtures.php'
window.close();
}
$('.pbutton a:first').after('<a class="button fittness" href="#">Pick Fittest Team</a><a class="button lowmoral" href="#">Pick lowest moral</a>');
$('.pbutton a:last').after('<a class="button removeplayers" href="#">Remove All Players</a>');
$('.fittness').click(function(){
updatefitestplayers('remove');
return false;
});
$('.removeplayers').click(function(){
removeAllPlayers();
return false;
});
$('.lowmoral').click(function(){
updateLowestMoralPlayers();
document.forms['selform'].submit();
return false;
});
}
if(document.location.pathname==="/spnewl_friendly_pool.php" && location.search.includes('step=2')){
document.forms['inviteform'].submit();
}
if(document.location.pathname==="/spnewl_speler_detail.php"){
addHiddenSkillToPlayerDetails();
}
if(document.location.pathname==="/spnewl_transfer_buy.php"){
cleanupOldGoodPlayers();
ensureFindGoodPlayersButton();
if (localStorage.getItem(GOOD_PLAYERS_STORAGE_KEY)) {
try {
renderGoodPlayers(JSON.parse(localStorage.getItem(GOOD_PLAYERS_STORAGE_KEY)) || []);
} catch (e) {
localStorage.removeItem(GOOD_PLAYERS_STORAGE_KEY);
localStorage.removeItem(GOOD_PLAYERS_STORAGE_TS_KEY);
}
}
$('.findGoodPlayers').off('click').on('click', function(){
findGoodPlayers();
return false;
});
}
var actionButtons = document.querySelectorAll('.button');
if(actionButtons.length > 1 && actionButtons[1].innerHTML === "Selection"){
window.open(actionButtons[1]);
if (actionButtons[0]) {
actionButtons[0].click();
}
}
const startSP = () => {
setNavigation()
}
startSP()
console.info('loaded');
}
bootSP();