FPLStats watchlist

Updates the FPLStats websites to allow selection of players to a watchlists. Watchlist is toggleable. By default also displays every player now

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// ==UserScript==
// @name         FPLStats watchlist
// @namespace    http://tampermonkey.net/
// @version      1.1
// @license MIT
// @description  Updates the FPLStats websites to allow selection of players to a watchlists. Watchlist is toggleable. By default also displays every player now
// @author       DangerSalmon
// @match        http://www.fplstatistics.co.uk/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=fplstatistics.co.uk
// @grant       GM.setValue
// @grant       GM.getValue
// ==/UserScript==

// Currently I'm just editing the view on the page. This means its a little slow to update as it needs all the data to be displayed, then operations to happen.
// A potential improvement would be to make this a Chrome extension which pulls the data from network and skips the inital display. But likely FPLStats will add their
// own watchlist before I get that working :P Especially since they have the link ID now. But this added ability to watch certain players so I still think its useful

// I was thinking of having two selectable lists. One for your team and one for watchlist

// Also should add loading states but can't really be arsed right now

/*
// Example of how the data is stored in greasemonkey storage. Rare occasion where same name same club could duplicate the results, may see if better way to sort later
const myTeam = [
    {club: "Man City", name: "Haaland"},
    {club: "Chelsea",name: "Mendy"},
    {club: "Leicester",name: "Ward"},
    {club: "Newcastle",name: "Trippier"},
    {club: "Liverpool",name: "Salah"},
    {club: "Fulham",name: "Andreas"},
    {club: "Leicester",name: "Barnes"},
    {club: "Arsenal",name: "Jesus"},
]*/

let latestTableRows = [] //#myDataTable > tbody > tr
let isFiltered = false

/**********
The FPLstats site when first getting results shows an error table row. Need to wait for this to go away before filtering
**********/
const waitForElement = async (whereWait) => {
    await new Promise(r => setTimeout(r, 100));
}

const waitForNoTableError = async () => {
    await waitForElement('waitForNoTableError')
    const searchError = document.querySelectorAll('#myDataTable > tbody > tr')[0]
    if (searchError.textContent.includes('Error: Invalid search / IP Ban or initialising table')) return waitForNoTableError()
    else return true
}
/**********************************************/

/**** Function show all players in data *****/
const showAllPlayers = async () => {
    const newDropdownOption = document.querySelectorAll('select[name=myDataTable_length] option')[3]
    newDropdownOption.value = 700
    newDropdownOption.text = '700'

    const select = document.querySelector('select[name=myDataTable_length]')
    select.value = 700;
    select.dispatchEvent(new Event('change'));

    await waitForNoTableError()
    latestTableRows = document.querySelectorAll('#myDataTable > tbody > tr')
    return true
}
/**********************************************/


/**** Function to toggle filtering my team *****/
const toggleFilterWatchlist = async () => {
    const storedTeam = await GM.getValue('fplStats_storedTeam')
    if (isFiltered) {
        isFiltered = false
        document.querySelector('#watchlistFilter').style.backgroundColor = 'transparent'
        latestTableRows.forEach((playerRow, index) => {playerRow.style.display = 'table-row'})
    } else {
        isFiltered = true
        document.querySelector('#watchlistFilter').style.backgroundColor = '#b3ffaa'
        latestTableRows.forEach((playerRow, index) => {
            const playerName = playerRow.querySelectorAll('td')[1].innerText
            const playerClub = playerRow.querySelectorAll('td')[2].innerText
            const isIncluded = storedTeam.some((myTeamPlayer) => {
                return myTeamPlayer.name === playerName && myTeamPlayer.club === playerClub
            })
            if (!isIncluded) playerRow.style.display = 'none'
        })
    }
}
/**********************************************/

/**** Function to add toggle filtering my team button*****/
const addFilterButton = () => {
    const filterDiv = document.querySelector('div #myDataTable_length')
    const newElement = document.createElement('div');
    newElement.id = 'watchlistFilter'
    newElement.innerText = 'Watchlist'
    newElement.style.backgroundColor = isFiltered ? '#b3ffaan' : 'transparent'
    newElement.style.border = isFiltered ? '1px solid green' : '1px solid black'
    newElement.style.cursor = 'pointer'
    newElement.style.display = 'inline-block'
    newElement.style.marginLeft = '5px'
    newElement.style.marginRight = '5px'
    newElement.style.paddingLeft = '5px'
    newElement.style.paddingRight = '5px'
    newElement.onclick = () => {toggleFilterWatchlist()}
    filterDiv.parentNode.insertBefore(newElement, filterDiv.nextSibling);
}
/**********************************************/

/**** Functions to add/remove players from watchlist*****/
const AddPlayerToWatchlist = async (newPlayer) => {
    let storedTeam = await GM.getValue('fplStats_storedTeam')
    if (!storedTeam) {
        GM.setValue('fplStats_storedTeam', [{club: newPlayer.club, name: newPlayer.name}])
    }
    else if(storedTeam.some((myTeamPlayer) => {
        return myTeamPlayer.name === newPlayer.name && myTeamPlayer.club === newPlayer.club
    })) {
         return
    }
    else {
        storedTeam.push(newPlayer)
    }
    console.log(storedTeam)
    await GM.setValue('fplStats_storedTeam', storedTeam)
}

const removePlayerFromWatchlist = async (newPlayer) => {
    let storedTeam = await GM.getValue('fplStats_storedTeam')
    if (!storedTeam) {
        return
    }
    else {
        storedTeam = storedTeam.filter(player => {
            return player.name !== newPlayer.name && player.club !== newPlayer.club;
        });
    }
    console.log(storedTeam)
    await GM.setValue('fplStats_storedTeam', storedTeam)
}
/**********************************************/

/*******Function which adds an Add/Remove button beside each player ***********/
const addTogglePlayerToTeamButton = async () => {
    if (latestTableRows.length) {

        /** Add 'Watchlist' text to header **/
        const tableHeaderRow = document.querySelector('#myDataTable > thead > tr')
        const headerWatchlistElement = document.createElement('th');
        headerWatchlistElement.className = 'all sorting ui-state-default'
        headerWatchlistElement.alignItems = 'center'
        headerWatchlistElement.display = 'flex'
        headerWatchlistElement.height = '21px'
        headerWatchlistElement.innerText = 'Watchlist'
        headerWatchlistElement.style.margin = '0px 10xp'
        tableHeaderRow.insertAdjacentElement('beforeend', headerWatchlistElement);
        /**********************************/

        const storedTeam = await GM.getValue('fplStats_storedTeam')
        latestTableRows.forEach((playerRow) => {
            const playerName = playerRow.querySelectorAll('td')[1].innerText
            const playerClub = playerRow.querySelectorAll('td')[2].innerText
            const isIncluded = storedTeam.some((myTeamPlayer) => {
                return myTeamPlayer.name === playerName && myTeamPlayer.club === playerClub
            })
            const newElement = document.createElement('td');
            newElement.innerText = isIncluded ? 'Added' : 'Add'
            newElement.style.alignItems = 'center'
            newElement.style.backgroundColor = isIncluded ? '#b3ffaa' : 'transparent';
            newElement.style.border = isIncluded ? '1px solid green' : '1px solid black';
            newElement.style.cursor = 'pointer'
            newElement.style.display = 'flex'
            newElement.style.height = '21px'
            newElement.style.margin = '0px 10xp'
            newElement.style.position = 'relative'
            newElement.style.width = '50px'
            newElement.onclick = async (event, data = {element: newElement, newPlayer: {club: playerClub, name: playerName}}) => {
                event.stopPropagation();
                const {element, newPlayer} = {...data}
                const latestStoredTeam = await GM.getValue('fplStats_storedTeam')
                const isIncluded = latestStoredTeam.some((myTeamPlayer) => {
                    return myTeamPlayer.name === newPlayer.name && myTeamPlayer.club === newPlayer.club
                })
                if (isIncluded) {
                    await removePlayerFromWatchlist(newPlayer);
                    newElement.innerText = 'Add';
                    newElement.style.backgroundColor = 'transparent';
                    newElement.style.border = '1px solid black';
                } else {
                    await AddPlayerToWatchlist(newPlayer);
                    newElement.innerText = 'Added';
                    newElement.style.backgroundColor = '#b3ffaa'
                    newElement.style.border = '1px solid green';
                }
            }
              if(playerRow.querySelector('td')) {
                  playerRow.insertAdjacentElement('beforeend',newElement);
              }
        })
    }
}

/***********
Below is whats actually ran when the FPLStats site is launched.
***********/
window.onload = async function () {
    await showAllPlayers()
    await addFilterButton()
    await addTogglePlayerToTeamButton()
}
/**********************************************/