Yahoo Fantasy Basketball

By Dave 3 Last update Oct 21, 2009 — Installed 15,273 times. Daily Installs: 2, 2, 1, 0, 5, 2, 2, 0, 0, 0, 5, 1, 2, 4, 1, 2, 4, 7, 13, 67, 24, 9, 13, 10, 5, 1, 7, 4, 1, 29, 6, 4

There are 2 previous versions of this script.

// ==UserScript==
// @name            Yahoo Fantasy Basketball
// @namespace       http://robobruin.com/greasemonkey/fantasysports
// @description     Live basketball fantasy scoring
// @include         http://basketball.fantasysports.yahoo.com/*
//
// ==/UserScript==

/*
This script is released under Mozilla Public License 1.1, http://www.mozilla.org/MPL/
The purpose is to provide live scoring updates for your default yahoo fantasy baseball team.

For code enhancements or feature requests, visit:
http://code.google.com/p/freebiestats/issues/list

credits:-
http://gabrito.com/files/subModal/
http://www.sitening.com/blog/2006/03/29/create-a-modal-dialog-using-css-and-javascript/

revision
2006-Nov-05: don't rely on hard coded offset to find boxscore link
2007-Dec-15: new boxscore format
2008-Nov-18: location regexp
*/

(function () {
    if (!location.href.match(/^http\:\/\/basketball\.fantasysports\.yahoo\.com\/nba\/\d+\/(\d+|team.+)/i)) {
        return;
    }

    const HIDDEN_DIV_ID = 'robobruinDiv';
    const MODAL_DIV_ID = 'robobruinModal';
    const STAT_BODY_ID = 'robobruinTableBody';
    const BUTTON_LABEL   = 'Show Freebie Stats!';
    const STAT_BUTTON_ID = 'robobruinStatBtn';

    var RE_MADE_ATTEMPT = new RegExp(".+\\D+-\\D+.+", 'g');
    
    function Baller() {
        this._playerId = '';
        this._name     = '';
        this._position = '';
        this._order = 0;
    }
    Baller.prototype.isOnBench   = function ()    {return (this._position == 'BN');}
    Baller.prototype.playerId   = function (arg) {if (arguments.length) this._playerId  = parseInt(arg); else return this._playerId;}
    Baller.prototype.name   = function (arg) {if (arguments.length) this._name  = arg; else return this._name;}
    Baller.prototype.position = function (arg) {if (arguments.length) this._position  = arg; else return this._position;}
    Baller.prototype.order       = function (arg) {if (arguments.length) this._order = parseInt(arg); else return this._order;}

    function xpath(doc, xpath) {
        return doc.evaluate(xpath, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    }

    function getDocument(url, player) {
        GM_xmlhttpRequest({
            method:"GET",
            url:url,
            headers:{
                "User-Agent":"monkeyagent",
                "Accept":"text/monkey,text/xml"
            },
            onload:function(details) {
                var s = new String(details.responseText);
                s = s.replace(/\n/g, ' ');
                s = s.replace(/^.*<body[^>]*>(.*)<\/body>.*/gi, "$1");
                var row = processBoxscore(s, player);
                printStats(row, player);
            }
        });
    }

    function processBoxscore(html, player) {
        var div = document.getElementById(HIDDEN_DIV_ID);
        if (!div) {
            div = document.createElement("div");
            document.body.appendChild(div);
            div.id = HIDDEN_DIV_ID;
            div.style.display = 'none';
        }

        div.innerHTML = html;
        var nodes = xpath(document, "//tr[contains(@class,'ysprow')]/td//a[contains(@href,'/" + player.playerId() + "')]");
        var i = nodes.snapshotLength;

        if (!i) {
            return null;
        } else {
            var node = nodes.snapshotItem(i - 1);
            var href = node.getAttribute("href");
            node.setAttribute("href", "http://sports.yahoo.com" + href);
            node.setAttribute("target", "sports")
            var count = 0;
            var tmpNode = null;
            while (count < 4) {
                if (node.nodeName == 'TR') {
                    tmpNode = node.cloneNode(true);
                    break;
                } else {
                    node = node.parentNode;
                }
                count++;
            }
            return tmpNode;
        }
    }

    function printStats(row, player) {
        var statBody = findOrCreateStatBody();

        if (row) {
            var rows = statBody.getElementsByTagName("TR");
            var tr = document.createElement("tr").appendChild(row);
            var td = document.createElement("td");
            td.innerHTML = player.position();
            tr.insertBefore(td, tr.firstChild);            
            statBody.replaceChild(tr, rows[player.order()]);
            if (player.isOnBench()) {
                tr.className = 'bench';
            } else {
                tr.className = (player.order() % 2 == 0) ? 'even' : 'odd';
                var totalRow = rows[rows.length-1];
                updateTotals(totalRow, tr);
            }
        }
    }

    function updateTotals(totalRow, currentRow) {
        var cells = currentRow.getElementsByTagName("TD");
        var totalCells = totalRow.getElementsByTagName("TD");

        if (cells.length < 10) {return;}

        if (totalCells.length==0) {
            for (var i=0; i < cells.length; i++) {
                totalRow.appendChild(document.createElement("td"));
            }
        }

        //index 0 is the position
        //index 1 is the name
        for (var i=2; i < cells.length; i++) {
            totalCells[i].innerHTML = sumValues(totalCells[i].innerHTML, cells[i].innerHTML);
        }
    }

    function sumValues(oldValue, newValue) {
        newValue = newValue.replace(/^\s+/,"");
        if (newValue.indexOf('-') > 0) {
            var ma = parseMadeAttempt(newValue);
            var tma = parseMadeAttempt(oldValue);
            var totalMade = parseInt(tma.made) + parseInt(ma.made);
            var totalAttempted = parseInt(tma.attempted) + parseInt(ma.attempted);

            var pct = (totalAttempted==0) ? "" : "<br/>" + parseFloat(totalMade*100/totalAttempted).toFixed(1) + "%";
            return totalMade + "-" + totalAttempted + pct;
        } else {
            var oldNumValue = parseInt(oldValue == '' ? 0 : oldValue);
            var newNumValue = parseInt(newValue);

            if (isNaN(newNumValue) || isNaN(oldNumValue)) {
                return '';
            } else {
                return oldNumValue + newNumValue;
            }
        }
    }

    function parseMadeAttempt(s) {
        var made = 0;
        var attempted = 0;

        var i = s.indexOf("-");
        if (i>-1) {
            var re = new RegExp("\\D+(\\d+-\\d+).+", 'gi');
            s = s.replace(re, "$1");
            i = s.indexOf("-");
            made = s.substring(0,i);
            attempted = s.substring(i+1,s.length);
        }
        return {made:made,attempted:attempted};
    }

    function findOrCreateStatBody() {
        var statBody = document.getElementById(STAT_BODY_ID);
        if (!statBody) {
            statBody = addModalOverlay();
            addShowStatsButton();

            var totalRow = document.createElement("tr");
            totalRow.className = 'total';
            statBody.appendChild(totalRow);

        }
        return statBody;
    }

    function addModalOverlay() {
        if (document.getElementById(MODAL_DIV_ID)) {
            document.body.removeChild(document.getElementById(MODAL_DIV_ID));
        }

        var div = document.createElement("div");
        document.body.appendChild(div);
        div.id = MODAL_DIV_ID;

        div.innerHTML =
        '<div><table id="roboTable" width="100%">' +
            '<thead><tr><td width="6%">Pos</td><td width="17%" height="18" align="left"> Name</td><td width="4%"/><td width="7%">Min</td><td width="7%">FG</td><td width="6%">3Pt</td><td width="6%">FT</td><td width="6%">+/-</td><td width="5%">Off</td><td width="5%">Reb</td><td width="5%">Ast</td><td width="5%">TO</td><td width="5%">Stl</td><td width="5%">BS</td><td width="5%">BA</td><td width="5%">PF</td><td width="7%">Pts </td></tr></thead>' +
            '<tbody id="'+ STAT_BODY_ID+'">' +
            '</tbody>' +
        '</table></div>';
        
        GM_addStyle('#' + MODAL_DIV_ID + " {position: absolute;left: 0px;top: 0px;width:100%;height:100%;text-align:center;z-index: 200;background: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%002%00%00%002%01%03%00%00%00%24%F1%1A%F2%00%00%00%06PLTE%9D%BF%C4%FF%FF%FFo%99%7C%D4%00%00%00%02tRNS%FF%00%E5%B70J%00%00%00%01bKGD%01%FF%02-%DE%00%00%00%09pHYs%00%00%00H%00%00%00H%00F%C9k%3E%00%00%00yIDATx%01%05%C1%01%01%00%00%08%02%20%1C%D9I%07u%A2%13A%06%5C%0A6%03.%05%9B%01%97%82%CD%80K%C1f%C0%A5%603%E0R%B0%19p)%D8%0C%B8%14l%06%5C%0A6%03.%05%9B%01%97%82%CD%80K%C1f%C0%A5%603%E0R%B0%19p)%D8%0C%B8%14l%06%5C%0A6%03.%05%9B%01%97%82%CD%80K%C1f%C0%A5%603%E0R%B0%19p)%D8%0C%B8%14l%06%5C%0A%F6%01%90%ADD%F3%BDe%02%17%00%00%00%00IEND%AEB%60%82\");}");
        GM_addStyle('#' + MODAL_DIV_ID + ' div {width:700px;margin: 100px auto;background-color:#fff;border:1px solid #000;padding:15px;text-align:center;z-index:201;}');
        GM_addStyle(GM_addStyle('.roboTable {width:100%;margin-bottom:20px;padding:3px;border-collapse:collapse;border: 1px solid #000;} tr.odd {background-color:white;font-weight:bold;} tr.even {background-color:beige;font-weight:bold;} thead tr {background-color:#ABAB9E;border-bottom:1px solid #000;} td {text-align:center;} tr.bench {background-color:#f1f2ed;font-weight:normal;} tr.total {background-color:yellow;font-weight:bold}'));
        addCloseAndRefresh();
        return document.getElementById(STAT_BODY_ID);
    }

        /**
     Add a close link and refresh to the modal
     */
    function addCloseAndRefresh() {
        var div = document.getElementById(MODAL_DIV_ID).childNodes[0];

        var close = document.createElement("a");
        close.id ="roboClose";
        close.href = "#";
        close.innerHTML = "[close]";
        div.appendChild(close);
        close.addEventListener('click', function(e) { removeOverlay(); }, false);

        var refresh = document.createElement("a");
        refresh.id = "roboRefresh";
        refresh.href = "#";
        refresh.innerHTML = "[refresh]";
        refresh.addEventListener('click', function(e) { removeOverlay(); getStats();},false);
        div.appendChild(refresh);
        GM_addStyle('#roboRefresh {padding-left:10px;}');
    }

    function processFantasyHome(nodes) {
        var numActivePlayers = 0;
        for (var i = 0; i < nodes.snapshotLength; i++) {
            var userIdNode = nodes.snapshotItem(i);
            var playerId = userIdNode.getAttribute('href');
            playerId = playerId.replace(/[^0-9]/g, '');

            var row = userIdNode.parentNode.parentNode.parentNode;
            var position = row.childNodes[0].innerHTML;

            //iterate columns to find the boxscore column
            //start at 1 since we know 0 is BN or position
            for(var j=1; j < row.childNodes.length; j++) {
                var column = row.childNodes[j];
                if (column.className && column.className == 'gametime') {
                    if (column.childNodes.length && column.childNodes[0].nodeName == 'A') {
                        var boxscoreLink = column.childNodes[0].getAttribute("href");
                        boxscoreLink = new String(boxscoreLink).replace('recap', 'boxscore');
                        if (boxscoreLink.indexOf("preview")> -1) {break;}
                        //update score link on management page to go directly to boxscore instead of recap
                        column.childNodes[0].setAttribute("href", boxscoreLink);

                        var statBody = findOrCreateStatBody();
                        var rows = statBody.getElementsByTagName("TR");
                        var lastRow = rows[rows.length-1];
                        var statRow = document.createElement("tr");
                        statBody.insertBefore(statRow, lastRow);

                        var player = new Baller();
                        player.playerId(playerId);
                        player.position(position);
                        player.order(numActivePlayers);

                        getDocument(boxscoreLink, player);
                        numActivePlayers++;
                    }
                    break;
                }
            }
        }
    }

    /**
     Remove modal window overlay
     */
    function removeOverlay() {
        if (document.getElementById(MODAL_DIV_ID)) {
            document.body.removeChild(document.getElementById(MODAL_DIV_ID));
        }
    }

    function addShowStatsButton() {
        if (document.getElementById(STAT_BUTTON_ID)) {
            return;
        }
        var nodes = xpath(document, "//a[contains(@href,'stattracker')]");

        if (nodes.snapshotItem(0)) {
            var a = nodes.snapshotItem(0);
            a.href = '#';
            a.target = null;
            a.innerHTML = BUTTON_LABEL;
            a.id = STAT_BUTTON_ID;
            a.addEventListener('click', function(e) {getStats(); return false;},false);
        }
        else {
            //left this here just in case stattracker button disappears
            var button = document.createElement("button");
            document.body.appendChild(button);
            button.id = STAT_BUTTON_ID;
            button.innerHTML = BUTTON_LABEL;
            button.addEventListener('click', function(e) {getStats();}, false);
            GM_addStyle('#' + STAT_BUTTON_ID + '{position:fixed;top:80px;right:80px;z-index:200;background-color:#0781C8;color:#fff;}');
        }
    }

    function getStats() {
        var nodes = xpath(document, "//table[@id='statTable0']/tbody/tr/td[@class='player']/div/a[@class='name' and @href]");
        processFantasyHome(nodes);
    }
    addShowStatsButton();
})();