HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Greasemonkey Fantasy Football script

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
scriptfantasyfootballgreasemonkey

Problem

I have the following Greasemonkey script that fetches some data from a Yahoo webpage and injects it into a webpage. It has the feature that it only fetches the data once per day instead of fetching it every single time the page is loaded.

I think my displayresults function is a little messy just because it has to access a lot of variables to work.

You can test this script for yourself by installing it and loading this page.

```
// ==UserScript==
// @name Yahoo Fantasy Football Rank
// @author Bijan
// @version 2.5
// @description Very simple script to conveniently see how many points a team gives up
// @namespace http://albuyeh.com
// @match ://football.fantasysports.yahoo.com/
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==

//Map Team Name to abbreviation
var teamNameList = {
"Arizona Cardinals": "Ari",
"Atlanta Falcons": "Atl",
"Baltimore Ravens": "Bal",
"Buffalo Bills": "Buf",
"Carolina Panthers": "Car",
"Chicago Bears": "Chi",
"Cincinnati Bengals": "Cin",
"Cleveland Browns": "Cle",
"Dallas Cowboys": "Dal",
"Denver Broncos": "Den",
"Detroit Lions": "Det",
"Green Bay Packers": "GB",
"Houston Texans": "Hou",
"Indianapolis Colts": "Ind",
"Jacksonville Jaguars": "Jax",
"Kansas City Chiefs": "KC",
"Miami Dolphins": "Mia",
"Minnesota Vikings": "Min",
"New England Patriots": "NE",
"New Orleans Saints": "NO",
"New York Giants": "NYG",
"New York Jets": "NYJ",
"Oakland Raiders": "Oak",
"Philadelphia Eagles": "Phi",
"Pittsburgh Steelers": "Pit",
"San Diego Chargers": "SD",
"San Francisco 49ers": "SF",
"Seattle Seahawks": "Sea",
"St. Louis Rams": "StL",
"Tampa Bay Buccaneers": "TB",
"Tennessee Titans": "Ten",
"Washington Redskins"

Solution

Consider storing large data in a separate file

teamNameList is quite long without necessarily providing any information for readers of the code. Why not save it in a separate file, say data.js and include it that way? You can read this SO post that explains how to include local files when working with GreaseMonkey.

Avoid loose statements that sort of run when the page loads without being explicit about it

What I mean by this is you usually want to avoid JavaScript statements that aren't tied to an event. Sure you want to run these when the page loads, right? In that case make this explicit like so:

document.onload = function ...


and put everything you want to happen in there. This will save you from the spaghetti code effect of not quite knowing why or when something is happening. Your code will be much more maintainable if you have everything that should happen on page load in one place. Otherwise, when you are debugging, things could get ugly. Read more about this in this SO post.

So I'd recommend, for example, moving this entire unanchored chunk into some kind of onload function:

//Fetch results once per day. 
var now = new Date();
var currdate = Math.floor(now/8.64e7);
if (GM_getValue("date2", "") < currdate || GM_getValue (tableName,  "").length == 0) {
    GM_setValue("date2", currdate);
    console.log("Fetched results @ :" + currdate);
    main(); //Go to main() to fetch data
}
else {
    console.log("Already fetched data. Continuing to display results");
    displayResults(); 
}


Variable Names

I agree with @Malachi that your for-loop variable names (K, J) could be improved by naming them something related to what they are indexing. Also you'd usually want to use a lower-case letter for a for-loop variable, not an uppercase. Use upper case and people wonder if they are missing something...

Formatting

displayResults is kind of a beast. Even without rewriting anything, I prefer it with some spacing to make it more comprehensible:

function displayResults(result) {
    //Get saved results
    var myList      = {};
    var myListObj   = GM_getValue (tableName,  "");
    if (myListObj) {
        myList      = JSON.parse (myListObj);
    }

    //Check if we have already fetched results for the day
    if (typeof result === 'undefined') { result = myList; console.log("Loading pre-loaded data");}

   //Comment here would be nice
    var player = document.querySelectorAll('span[class="Fz-xxs"]'); //Get player element
    var opp = document.querySelectorAll('a[class="F-reset"]'); //Get opposing team element

   //Comment here would be nice
    if(opp[0].innerHTML == "View Details") opp = Array.prototype.slice.call(opp, 1, opp.length);
    if(player.length == opp.length) {
        console.log("Player length = Opposing Team length. We can continue")
        for (i=0; i " + oppTeam + " - " + rank + ""; //Modify element to include rank and color
        }
    }
    else {
        console.log("Player & Team mismatch: " + player.length + " & " + opp.length)   
    }
}


Use more DOM access methods

If you use more DOM or jQuery you can shorten a lot of lines of code here, such as the last line in your for-loop in displayResults. It's not just about safety or what have you (I don't know much about why people hate innerHTML so much)...it's also about readability. Some of those lines get awfully long when you throw in ` tags and all the rest in your JavaScript code.

Questions

  • In parseResponse why do you return [[teamName, teamRank]] instead


of just
return [teamName, teamRank]?

  • Did I miss your error handling apart from main()`? What actually happens if your data retrieval fails?

Code Snippets

document.onload = function ...
//Fetch results once per day. 
var now = new Date();
var currdate = Math.floor(now/8.64e7);
if (GM_getValue("date2", "") < currdate || GM_getValue (tableName,  "").length == 0) {
    GM_setValue("date2", currdate);
    console.log("Fetched results @ :" + currdate);
    main(); //Go to main() to fetch data
}
else {
    console.log("Already fetched data. Continuing to display results");
    displayResults(); 
}
function displayResults(result) {
    //Get saved results
    var myList      = {};
    var myListObj   = GM_getValue (tableName,  "");
    if (myListObj) {
        myList      = JSON.parse (myListObj);
    }

    //Check if we have already fetched results for the day
    if (typeof result === 'undefined') { result = myList; console.log("Loading pre-loaded data");}

   //Comment here would be nice
    var player = document.querySelectorAll('span[class="Fz-xxs"]'); //Get player element
    var opp = document.querySelectorAll('a[class="F-reset"]'); //Get opposing team element

   //Comment here would be nice
    if(opp[0].innerHTML == "View Details") opp = Array.prototype.slice.call(opp, 1, opp.length);
    if(player.length == opp.length) {
        console.log("Player length = Opposing Team length. We can continue")
        for (i=0; i < player.length; i++) {
            var playerPos = player[i].innerHTML.split(" - ").splice(-1)[0]; //Returns position (i.e. QB)
            var playerIndex = playerPositions.indexOf(playerPos); 

             //Comment here would be nice
            if (playerIndex == -1) {console.log("Skipped " + playerPos); continue;} //Currently does not fetch Defensive players. 
            var oppTeam = opp[i].innerHTML.split(" ").splice(-1)[0]; //Returns opposing team (i.e. SF)
            var rank = result[oppTeam][playerIndex]; //Get rank # based on opposing team and player position
            if (1 <= rank && rank <= 10) color = "F-rank-good"; //Green
            else if (11 <= rank && rank <= 22) color = "F-rank-neutral"; //Yellow
            else if (23 <= rank && rank <= 32) color = "F-rank-bad"; //Red

            console.log(oppTeam + " gives up the #" + rank + " points to the " + playerPos + " position")
            opp[i].innerHTML = opp[i].innerHTML.split(/\@|vs/)[0] + " @ <span class='" + color + "'>" + oppTeam + " - " + rank + "</span>"; //Modify element to include rank and color
        }
    }
    else {
        console.log("Player & Team mismatch: " + player.length + " & " + opp.length)   
    }
}

Context

StackExchange Code Review Q#103735, answer score: 2

Revisions (0)

No revisions yet.