strikeout.co scores

inclues game info on strikeout.co main page

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         strikeout.co scores
// @namespace    http://your.homepage/
// @version      1.0
// @description  inclues game info on strikeout.co main page
// @author       muffleyd
// @match        http://www.strikeout.co/
// @grant        $
// ==/UserScript==

/** Copywrite David Muffley 2015-2016.
 * Use however you like, with acknowledgement.
 */

(function () {
// TODO more styling for info text?

//remove crap at the top of the content
if ("#content #accordian") {
    $("#content > *:not(:has(#accordion))").remove();
}
//just, stop
$('#toTop').remove();

//_main() won't run through if tab is hidden, this stores when that occurs so on tab visibility it can run again
var SKIPPED = false;
//_main() will always run the first time through even if the tab is hidden
var firstGet = true;

//completed inning value to display no hitter info (middle of previous inning for away hitters)
var NOHITATBATS = 6;

var addedClasses = ['_tmMLBFree', '_tmESPN'];
var selectorParent = '#content h1[league="Major League Baseball"]';

function parseTime(time) {
    //time can be in format of "10:35" or timezone like "-03:30" or "+9:30"
    var mod = 1;
    if (time[0] == '-' || time[0] == '+') {
        if (time[0] == '-') {
            mod = -1;
        }
        time = time.substring(1);
    }
    time = time.split(':');
    return mod * ((parseInt(time[0]) * 60) + parseInt(time[1]));
}
//set "mlbgday" attr to match mlb.com's scoreboard since "gday" attribute may not match mlb's date, based on timezone
(function () {
    var timezoneMod = parseTime(timezone) + 240; // + 240 makes this effectively eastern time, close enough
    $(selectorParent + ' .matchtime').each(function (index, el) {
        var val = el.getAttribute('gday');
        //even at -12:00 it can't be behind by a day, it can only ever be ahead by a day all the way to +13:00
        if (parseTime(el.innerText) - timezoneMod < 0) {
            var dateObj = new Date(val);
            dateObj.setTime(dateObj.getTime() - (86400 * 1000));
            val = dateObj.toJSON().slice(0, 10);
        }
        el.setAttribute('mlbgday', val);
    })
})();

//pass an optional delay in
// when waking up from sleep the network connection is not immediately active, this allows you to compensate for that
function main(delay) {
    if (!delay) {
        delay = 0;
    }
    setTimeout(_main, delay * 1000);
}

// 5 hours in the past, for when it's after midnight, not sure if needed
function _main() {
    //skip when tab is hidden or window is minimized, and set so event is later told to run main()
    if (!firstGet && document.hidden) {
        SKIPPED = true;
        return;
    }
    firstGet = false;
    var dateObj = new Date();

    //offset the computer's timezone to get UTC, then go back 10 hours to guarantee you use the correct date for mlb.com
    var date = new Date(dateObj.getTime() + (dateObj.getTimezoneOffset() * 60 * 1000) - (1000 * 60 * 60 * 10));
    var year = date.getFullYear().toString();

    //January is 0, etc, so add 1
    var month = date.getMonth() + 1;
    if (month < 10) {
        month = "0" + month.toString();
    } else {
        month = month.toString();
    }
    var day = date.getDate();
    if (day < 10) {
        day = "0" + day.toString();
    } else {
        day = day.toString();
    }
    $.getJSON('http://gd2.mlb.com/components/game/mlb/year_' + year + '/month_' + month + '/day_' + day + '/miniscoreboard.json', function (resp, type) {
        if (type == 'success') {
            $(document).ready(function () {
                //clear previously added classes to elements
                addedClasses.forEach(function (item) {
                    $(selectorParent).removeClass(item);
                });
                var info = resp.data.games.game;
                if (info.length === undefined) { //if only one game, it's not passed back as an array
                    info = [info];
                }
                var gameObj = {};
                //on later runs, remove added text immediately
                $(selectorParent + ' a ._tmInfo').remove();
                //get all team names from today
                $(selectorParent + ' a:has(span[mlbgday="' + year + '-' + month + '-' + day + '"])').each(function (index, el) {
                    var gameText = el.lastChild.textContent.trim();
                    //later regex now checks for hyphens, so deal with it in D-backs
                    gameText = gameText.replace('D-backs', 'Diamondbacks');
                    //Find games missing the colon before "Game #" and add it in
                    // But don't for ones using a hyphen
                    gameText = gameText.replace(/([^:\-])(\s+[Gg]ame\s+[0-9]+)$/, '$1:$2');
                    //Turn "Team vsTeam" into "Team vs Team". Not truely needed since only the team name, not city is used, but just in case
                    gameText = gameText.replace(/(\s+[Vv][Ss])([A-Z])/, '$1 $2');
                    //this match regex handles things like:
                    //   Team vs Team
                    //   Team vs Team: Game 2
                    //   MLB Wildcard: Team vs Team
                    //   ALDS: Team vs Team: Game 1
                    //   Team vs Team - Game 2
                    //regex split is for occasional non-space blank character (tab has been there)
                    var txt = gameText.match(/([^:]+:|^)(.*?vs[^:\-$]+)(.*)/i);
                    if (txt === null) {
                        console.log('not a game', gameText);
                        //something like "MLB on Fox Sports 1 Pregame"
                        return;
                    }
                    var texts = txt[2].split(/\s+vs\s+/i);
                    texts.forEach(function (item) {
                        var splitItems = item.trim().split(/\s+/);
                        //strip out this special thing that has appeared during spring training
                        if (splitItems[splitItems.length-1] == '(ss)') {
                            splitItems.pop();
                        }
                        splitItems.forEach(function (item, index, arr) {
                            //combine all words after the first, getting smaller and smaller,
                            //because of things like "Chicago White Sox" while avoiding "Los Angeles Dodgers"
                            if (index == 0) {
                                return;
                            }
                            var key = arr.slice(index).join(' ');
                            if (!gameObj.hasOwnProperty(key)) {
                                gameObj[key] = [];
                            }

                            //sometimes a game appears as "A vs B - Game 2", so keep the spaces around the "-"
                            if (txt[3][0] == '-') {
                                var txt2end = txt[2].substr(-1);
                                if (txt2end.match(/\s/)) {
                                    txt[3] = txt2end + txt[3];
                                    txt[2] = txt[2].slice(0, -1);
                                }
                            }
                            //place at the head since the (reversed) game array goes from the beginning of gameObj[key]
                            //include the original text so extra stuff can stay when rewriting it later
                            gameObj[key].unshift([el, txt]);
                        });
                    });
                });
                //fix differences
                if (gameObj.hasOwnProperty('Diamondbacks')) {
                    gameObj['D-backs'] = gameObj['Diamondbacks'];
                }
                console.log(gameObj);
                //sort handles out of order games in the info list which happens sometimes with resumed games or double headers
                info.sort(function (a, b) {
                    //test for NaN because sometimes "time" is like "Game 2"
                    var t1 = a.time.split(':');
                    t1 = (t1[0] * 60) + (1*t1[1]);
                    if (isNaN(t1)) {
                        return 0;
                    }
                    var t2 = b.time.split(':');
                    t2 = (t2[0] * 60) + (1*t2[1]);
                    if (isNaN(t2)) {
                        return 0;
                    }
                    //note that mlb.com returns all times as ET
                    //some games start at 12:35pm, and rarely in the 11am hour, while never being 11pm or later
                    //since there's no 24 hour time these need to be set as being before 1pm
                    if (t1 >= 11*60) {
                        t1 -= (12*60);
                    }
                    if (t2 >= 11*60) {
                        t2 -= (12*60);
                    }
                    //
                    if (t1 < t2) {
                        return -1;
                    }
                    if (t1 > t2) {
                        return 1;
                    }
                    return 0;
                });
                info.reverse();
                info.forEach(function (item, index, arr) {
                    var unknownStatus = false;
                    var delayed = false;
                    var postponed = false;
                    var stop = false;
                    var media = true;
                    var final = false;
                    var checkAwayNoHit = false;
                    var checkHomeNoHit = false;
                    var includeReason = false;
                    if (item.status == 'Pre-Game' || item.status == 'Preview') {
                        //after removing the first item and managing media info this will return
                        stop = true;
                    } else if (item.status == 'In Progress' || item.status == 'Review' || item.status == 'Manager Challenge') {
                        //good
                    } else if (item.status == 'Game Over' || item.status == 'Final' || item.status == 'Completed Early') {
                        //good
                        if (item.status == 'Completed Early') {
                            includeReason = true;
                        }
                        media = false;
                        final = true;
                    } else if (item.status == 'Delayed Start' || item.status == 'Delayed') {
                        //delay, but good
                        includeReason = true;
                        delayed = true;
                    } else if (item.status == 'Warmup') {
                        //good
                    } else if (item.status == 'Postponed' || item.status == 'Suspended' || item.status == 'Cancelled') {
                        includeReason = true;
                        postponed = true;
                    } else {
                        unknownStatus = true;
                        console.log('other status:', item.status, item);
                    }
                    console.log(item.away_team_name, item.home_team_name);
                    if (!gameObj.hasOwnProperty(item.away_team_name) || !gameObj.hasOwnProperty(item.home_team_name)) {
                        return;
                    }
                    var gameEl = gameObj[item.away_team_name][0];
                    if (gameEl === undefined) {
                        //likely a double header's first game that's fallen off the top of the list
                        console.log('game gone', item);
                        return;
                    } else {
                        gameEl = gameEl[0]
                    }
                    var originalMainText = gameObj[item.away_team_name][0][1];
                    if (gameEl !== gameObj[item.home_team_name][0][0]) {
                        //cry
                        console.log('mismatch', item);
                    } else if (gameEl) {
                        //pop off the game values, in case of double header
                        gameObj[item.away_team_name].shift();
                        gameObj[item.home_team_name].shift();
                        console.log(item, gameEl);

                        if (media) {
                            handle_game_media(item, gameEl);
                        }

                        //for double headers, now that the game is popped off we can return
                        if (stop) {
                            return;
                        }

                        var text = '';
                        var cls = '';
                        if (item.status == 'Warmup') {
                            text = 'Warmup';
                            cls = 'warmup';
                        } else {
                            var status = item.status;
                            if (status != 'Game Over' && status != 'Final') {
                                if (postponed) {
                                    cls = 'postponed';
                                }
                                //Top 3, Bottom 5, Middle 7, etc.
                                //also determine if no hitter worth mentioning
                                //if home team is ahead in the middle or later of inning 9 or greater, it's actually over
                                //same if score is different at end of inning 9 or greater
                                if (item.hasOwnProperty('inning')) {
                                    cls += ' inprogress';
                                    //check for no-hitter through NOHITATBATS number of at-bats for a team
                                    if (item.inning > NOHITATBATS) {
                                        checkAwayNoHit = true;
                                        checkHomeNoHit = true;
                                    }
                                    if (item.top_inning == "Y") {
                                        if (item.outs == 3) {
                                            var topbottom = 'Middle';
                                            if (item.inning >= item.scheduled_innings && item.away_team_runs < item.home_team_runs) {
                                                final = true;
                                            }
                                            if (item.inning >= NOHITATBATS) {
                                                checkAwayNoHit = true;
                                            }
                                        } else {
                                            topbottom = 'Top';
                                        }
                                    } else {
                                        if (item.outs == 3) {
                                            topbottom = 'End';
                                            if (item.inning >= item.scheduled_innings && item.away_team_runs != item.home_team_runs) {
                                                final = true;
                                            }
                                            if (item.inning >= NOHITATBATS) {
                                                checkAwayNoHit = true;
                                                checkHomeNoHit = true;
                                            }
                                        } else {
                                            topbottom = 'Bottom';
                                            if (item.inning >= item.scheduled_innings && item.away_team_runs < item.home_team_runs) {
                                                final = true;
                                            }
                                            if (item.inning >= NOHITATBATS) {
                                                checkAwayNoHit = true;
                                            }
                                        }
                                    }
                                    if (!final) {
                                        if (topbottom) {
                                            text = topbottom + ' '
                                        }
                                        text += item.inning;
                                    }
                                }
                            }
                            if (final) {
                                text = 'Final';
                                //usually extra innings, perhaps rain-shortened?
                                if (item.inning != 9) {
                                    text += ' (' + item.inning + ')';
                                }
                                cls = 'final';
                            }
                            if ((checkHomeNoHit && item.home_team_hits == 0) ||
                                (checkAwayNoHit && item.away_team_hits == 0)) {
                                cls += ' noHitter';
                            }
                            if (item.hasOwnProperty('away_team_runs') && item.hasOwnProperty('home_team_runs')) {
                                text += ', ' + item.away_team_runs + '-' + item.home_team_runs;
                                var parens = true;
                            } else {
                                parens = false;
                            }
                            if (includeReason) {
                                text += ' ';
                                if (parens) {
                                    text += '(';
                                }
                                text += item.status;
                                if (item.reason) {
                                    text += ': ' + item.reason;
                                }
                                if (parens) {
                                    text += ')';
                                }
                            }
                        }
                        if (unknownStatus) {
                            text += ' DEBUG:' + item.status;
                        }
                        //get original text surrounding the 'Team vs Team' to include along with the reordering of the Teams to Away vs Home
                        gameEl.lastChild.textContent = originalMainText[1] + ' ' + getTeamName(item, 'away') + ' vs ' + getTeamName(item, 'home') + originalMainText[3];
                        if (text) {
                            $('<span class="_tmInfo">, <span class="_tmText ' + cls + '">' + text + '</span></span>').appendTo(gameEl);
                        }
                    }
                });
            });
        }
    });
}

function handle_game_media(item, gameEl) {
    if (item.game_media && item.game_media.media) {
        var media = item.game_media.media;
        if ($.isArray(item.game_media.media)) {
            media = item.game_media.media[0];
        }
        if (media.free != "NO") {
            if (media.free != "ALL") {
                console.log('different media free info', media);
            }
            gameEl.parentElement.classList.add('_tmMLBFree');
        }
    }
    if (item.tv_station) {
        item.tv_station.split(', ').forEach(function (station) {
            //TODO find ESPN more generally
            if (station == 'ESPN' || station == 'ESPN2' || station == 'ESPN (out-of-market only)') {
                gameEl.parentElement.classList.add('_tmESPN');
            }
        });
    }
}

//example why this is needed:
//team_city: 'NY Yankees'
//team_name: 'Yankees'
//Should return 'NY Yankees' rather than 'NY Yankees Yankees'
function getTeamName(item, homeaway) {
    var city = item[homeaway + '_team_city'];
    var name = item[homeaway + '_team_name'];
    if (city.slice(-name.length) == name) {
        return city;
    } else {
        return city + ' ' + name;
    }
}

var initial_delay = 6;
var constant_delay = 120;

main();
//this blob compensates for 'initial_delay'
// e.g. main loop runs every 2 minutes, with a 6 second initial delay,
// this will wait 114 seconds to run main(6) and register the Interval of 120 seconds at the same time to run main(6)
setTimeout(function () {
    main(initial_delay);
    setInterval(main, 1000 * constant_delay, initial_delay);
}, 1000 * (constant_delay - initial_delay));

//since we don't do anything when the tab is invisible, force an update when made visible if one was skipped
document.addEventListener('visibilitychange', function () {
    if (!document.hidden) {
        if (SKIPPED) {
            SKIPPED = false;
            main();
        }
    }
});


function addGlobalStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

$(document).ready(function () {
    //do an awful job of figuring out which theme is running
    if ($('body').css('background-color').replace(/\s/g, '') == 'rgb(0,0,0)') {
        //golden midnight
        $('body').addClass('_tmDark');
        addGlobalStyle('body._tmDark ._tmInfo ._tmText.inprogress { color: lightblue; }');
        addGlobalStyle('body._tmDark ' + selectorParent + '._tmMLBFree { background: green; }');
        addGlobalStyle('body._tmDark ' + selectorParent + '._tmESPN { background: rgba(255,7,7,.35); }');
        addGlobalStyle('body._tmDark ._tmInfo ._tmText.noHitter { background: crimson; }');
    } else {
        //white magic or failure
    }
    addGlobalStyle('._tmInfo ._tmText.inprogress { color: red; }');
    addGlobalStyle('._tmInfo ._tmText.warmup { color: goldenrod; }');
    addGlobalStyle('._tmInfo ._tmText.final { color: grey; }');
    addGlobalStyle('._tmInfo ._tmText.postponed { color: blue; }');
    addGlobalStyle('._tmInfo ._tmText.noHitter { background: chartreuse; }');

    addGlobalStyle(selectorParent + '._tmMLBFree { background: gold; }');
    addGlobalStyle(selectorParent + '._tmESPN { background: rgba(255,7,7,.25); }');
});
})()