strikeout.co scores

inclues game info on strikeout.co main page

  1. // ==UserScript==
  2. // @name strikeout.co scores
  3. // @namespace http://your.homepage/
  4. // @version 1.0
  5. // @description inclues game info on strikeout.co main page
  6. // @author muffleyd
  7. // @match http://www.strikeout.co/
  8. // @grant $
  9. // ==/UserScript==
  10.  
  11. /** Copywrite David Muffley 2015-2016.
  12. * Use however you like, with acknowledgement.
  13. */
  14.  
  15. (function () {
  16. // TODO more styling for info text?
  17.  
  18. //remove crap at the top of the content
  19. if ("#content #accordian") {
  20. $("#content > *:not(:has(#accordion))").remove();
  21. }
  22. //just, stop
  23. $('#toTop').remove();
  24.  
  25. //_main() won't run through if tab is hidden, this stores when that occurs so on tab visibility it can run again
  26. var SKIPPED = false;
  27. //_main() will always run the first time through even if the tab is hidden
  28. var firstGet = true;
  29.  
  30. //completed inning value to display no hitter info (middle of previous inning for away hitters)
  31. var NOHITATBATS = 6;
  32.  
  33. var addedClasses = ['_tmMLBFree', '_tmESPN'];
  34. var selectorParent = '#content h1[league="Major League Baseball"]';
  35.  
  36. function parseTime(time) {
  37. //time can be in format of "10:35" or timezone like "-03:30" or "+9:30"
  38. var mod = 1;
  39. if (time[0] == '-' || time[0] == '+') {
  40. if (time[0] == '-') {
  41. mod = -1;
  42. }
  43. time = time.substring(1);
  44. }
  45. time = time.split(':');
  46. return mod * ((parseInt(time[0]) * 60) + parseInt(time[1]));
  47. }
  48. //set "mlbgday" attr to match mlb.com's scoreboard since "gday" attribute may not match mlb's date, based on timezone
  49. (function () {
  50. var timezoneMod = parseTime(timezone) + 240; // + 240 makes this effectively eastern time, close enough
  51. $(selectorParent + ' .matchtime').each(function (index, el) {
  52. var val = el.getAttribute('gday');
  53. //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
  54. if (parseTime(el.innerText) - timezoneMod < 0) {
  55. var dateObj = new Date(val);
  56. dateObj.setTime(dateObj.getTime() - (86400 * 1000));
  57. val = dateObj.toJSON().slice(0, 10);
  58. }
  59. el.setAttribute('mlbgday', val);
  60. })
  61. })();
  62.  
  63. //pass an optional delay in
  64. // when waking up from sleep the network connection is not immediately active, this allows you to compensate for that
  65. function main(delay) {
  66. if (!delay) {
  67. delay = 0;
  68. }
  69. setTimeout(_main, delay * 1000);
  70. }
  71.  
  72. // 5 hours in the past, for when it's after midnight, not sure if needed
  73. function _main() {
  74. //skip when tab is hidden or window is minimized, and set so event is later told to run main()
  75. if (!firstGet && document.hidden) {
  76. SKIPPED = true;
  77. return;
  78. }
  79. firstGet = false;
  80. var dateObj = new Date();
  81.  
  82. //offset the computer's timezone to get UTC, then go back 10 hours to guarantee you use the correct date for mlb.com
  83. var date = new Date(dateObj.getTime() + (dateObj.getTimezoneOffset() * 60 * 1000) - (1000 * 60 * 60 * 10));
  84. var year = date.getFullYear().toString();
  85.  
  86. //January is 0, etc, so add 1
  87. var month = date.getMonth() + 1;
  88. if (month < 10) {
  89. month = "0" + month.toString();
  90. } else {
  91. month = month.toString();
  92. }
  93. var day = date.getDate();
  94. if (day < 10) {
  95. day = "0" + day.toString();
  96. } else {
  97. day = day.toString();
  98. }
  99. $.getJSON('http://gd2.mlb.com/components/game/mlb/year_' + year + '/month_' + month + '/day_' + day + '/miniscoreboard.json', function (resp, type) {
  100. if (type == 'success') {
  101. $(document).ready(function () {
  102. //clear previously added classes to elements
  103. addedClasses.forEach(function (item) {
  104. $(selectorParent).removeClass(item);
  105. });
  106. var info = resp.data.games.game;
  107. if (info.length === undefined) { //if only one game, it's not passed back as an array
  108. info = [info];
  109. }
  110. var gameObj = {};
  111. //on later runs, remove added text immediately
  112. $(selectorParent + ' a ._tmInfo').remove();
  113. //get all team names from today
  114. $(selectorParent + ' a:has(span[mlbgday="' + year + '-' + month + '-' + day + '"])').each(function (index, el) {
  115. var gameText = el.lastChild.textContent.trim();
  116. //later regex now checks for hyphens, so deal with it in D-backs
  117. gameText = gameText.replace('D-backs', 'Diamondbacks');
  118. //Find games missing the colon before "Game #" and add it in
  119. // But don't for ones using a hyphen
  120. gameText = gameText.replace(/([^:\-])(\s+[Gg]ame\s+[0-9]+)$/, '$1:$2');
  121. //Turn "Team vsTeam" into "Team vs Team". Not truely needed since only the team name, not city is used, but just in case
  122. gameText = gameText.replace(/(\s+[Vv][Ss])([A-Z])/, '$1 $2');
  123. //this match regex handles things like:
  124. // Team vs Team
  125. // Team vs Team: Game 2
  126. // MLB Wildcard: Team vs Team
  127. // ALDS: Team vs Team: Game 1
  128. // Team vs Team - Game 2
  129. //regex split is for occasional non-space blank character (tab has been there)
  130. var txt = gameText.match(/([^:]+:|^)(.*?vs[^:\-$]+)(.*)/i);
  131. if (txt === null) {
  132. console.log('not a game', gameText);
  133. //something like "MLB on Fox Sports 1 Pregame"
  134. return;
  135. }
  136. var texts = txt[2].split(/\s+vs\s+/i);
  137. texts.forEach(function (item) {
  138. var splitItems = item.trim().split(/\s+/);
  139. //strip out this special thing that has appeared during spring training
  140. if (splitItems[splitItems.length-1] == '(ss)') {
  141. splitItems.pop();
  142. }
  143. splitItems.forEach(function (item, index, arr) {
  144. //combine all words after the first, getting smaller and smaller,
  145. //because of things like "Chicago White Sox" while avoiding "Los Angeles Dodgers"
  146. if (index == 0) {
  147. return;
  148. }
  149. var key = arr.slice(index).join(' ');
  150. if (!gameObj.hasOwnProperty(key)) {
  151. gameObj[key] = [];
  152. }
  153.  
  154. //sometimes a game appears as "A vs B - Game 2", so keep the spaces around the "-"
  155. if (txt[3][0] == '-') {
  156. var txt2end = txt[2].substr(-1);
  157. if (txt2end.match(/\s/)) {
  158. txt[3] = txt2end + txt[3];
  159. txt[2] = txt[2].slice(0, -1);
  160. }
  161. }
  162. //place at the head since the (reversed) game array goes from the beginning of gameObj[key]
  163. //include the original text so extra stuff can stay when rewriting it later
  164. gameObj[key].unshift([el, txt]);
  165. });
  166. });
  167. });
  168. //fix differences
  169. if (gameObj.hasOwnProperty('Diamondbacks')) {
  170. gameObj['D-backs'] = gameObj['Diamondbacks'];
  171. }
  172. console.log(gameObj);
  173. //sort handles out of order games in the info list which happens sometimes with resumed games or double headers
  174. info.sort(function (a, b) {
  175. //test for NaN because sometimes "time" is like "Game 2"
  176. var t1 = a.time.split(':');
  177. t1 = (t1[0] * 60) + (1*t1[1]);
  178. if (isNaN(t1)) {
  179. return 0;
  180. }
  181. var t2 = b.time.split(':');
  182. t2 = (t2[0] * 60) + (1*t2[1]);
  183. if (isNaN(t2)) {
  184. return 0;
  185. }
  186. //note that mlb.com returns all times as ET
  187. //some games start at 12:35pm, and rarely in the 11am hour, while never being 11pm or later
  188. //since there's no 24 hour time these need to be set as being before 1pm
  189. if (t1 >= 11*60) {
  190. t1 -= (12*60);
  191. }
  192. if (t2 >= 11*60) {
  193. t2 -= (12*60);
  194. }
  195. //
  196. if (t1 < t2) {
  197. return -1;
  198. }
  199. if (t1 > t2) {
  200. return 1;
  201. }
  202. return 0;
  203. });
  204. info.reverse();
  205. info.forEach(function (item, index, arr) {
  206. var unknownStatus = false;
  207. var delayed = false;
  208. var postponed = false;
  209. var stop = false;
  210. var media = true;
  211. var final = false;
  212. var checkAwayNoHit = false;
  213. var checkHomeNoHit = false;
  214. var includeReason = false;
  215. if (item.status == 'Pre-Game' || item.status == 'Preview') {
  216. //after removing the first item and managing media info this will return
  217. stop = true;
  218. } else if (item.status == 'In Progress' || item.status == 'Review' || item.status == 'Manager Challenge') {
  219. //good
  220. } else if (item.status == 'Game Over' || item.status == 'Final' || item.status == 'Completed Early') {
  221. //good
  222. if (item.status == 'Completed Early') {
  223. includeReason = true;
  224. }
  225. media = false;
  226. final = true;
  227. } else if (item.status == 'Delayed Start' || item.status == 'Delayed') {
  228. //delay, but good
  229. includeReason = true;
  230. delayed = true;
  231. } else if (item.status == 'Warmup') {
  232. //good
  233. } else if (item.status == 'Postponed' || item.status == 'Suspended' || item.status == 'Cancelled') {
  234. includeReason = true;
  235. postponed = true;
  236. } else {
  237. unknownStatus = true;
  238. console.log('other status:', item.status, item);
  239. }
  240. console.log(item.away_team_name, item.home_team_name);
  241. if (!gameObj.hasOwnProperty(item.away_team_name) || !gameObj.hasOwnProperty(item.home_team_name)) {
  242. return;
  243. }
  244. var gameEl = gameObj[item.away_team_name][0];
  245. if (gameEl === undefined) {
  246. //likely a double header's first game that's fallen off the top of the list
  247. console.log('game gone', item);
  248. return;
  249. } else {
  250. gameEl = gameEl[0]
  251. }
  252. var originalMainText = gameObj[item.away_team_name][0][1];
  253. if (gameEl !== gameObj[item.home_team_name][0][0]) {
  254. //cry
  255. console.log('mismatch', item);
  256. } else if (gameEl) {
  257. //pop off the game values, in case of double header
  258. gameObj[item.away_team_name].shift();
  259. gameObj[item.home_team_name].shift();
  260. console.log(item, gameEl);
  261.  
  262. if (media) {
  263. handle_game_media(item, gameEl);
  264. }
  265.  
  266. //for double headers, now that the game is popped off we can return
  267. if (stop) {
  268. return;
  269. }
  270.  
  271. var text = '';
  272. var cls = '';
  273. if (item.status == 'Warmup') {
  274. text = 'Warmup';
  275. cls = 'warmup';
  276. } else {
  277. var status = item.status;
  278. if (status != 'Game Over' && status != 'Final') {
  279. if (postponed) {
  280. cls = 'postponed';
  281. }
  282. //Top 3, Bottom 5, Middle 7, etc.
  283. //also determine if no hitter worth mentioning
  284. //if home team is ahead in the middle or later of inning 9 or greater, it's actually over
  285. //same if score is different at end of inning 9 or greater
  286. if (item.hasOwnProperty('inning')) {
  287. cls += ' inprogress';
  288. //check for no-hitter through NOHITATBATS number of at-bats for a team
  289. if (item.inning > NOHITATBATS) {
  290. checkAwayNoHit = true;
  291. checkHomeNoHit = true;
  292. }
  293. if (item.top_inning == "Y") {
  294. if (item.outs == 3) {
  295. var topbottom = 'Middle';
  296. if (item.inning >= item.scheduled_innings && item.away_team_runs < item.home_team_runs) {
  297. final = true;
  298. }
  299. if (item.inning >= NOHITATBATS) {
  300. checkAwayNoHit = true;
  301. }
  302. } else {
  303. topbottom = 'Top';
  304. }
  305. } else {
  306. if (item.outs == 3) {
  307. topbottom = 'End';
  308. if (item.inning >= item.scheduled_innings && item.away_team_runs != item.home_team_runs) {
  309. final = true;
  310. }
  311. if (item.inning >= NOHITATBATS) {
  312. checkAwayNoHit = true;
  313. checkHomeNoHit = true;
  314. }
  315. } else {
  316. topbottom = 'Bottom';
  317. if (item.inning >= item.scheduled_innings && item.away_team_runs < item.home_team_runs) {
  318. final = true;
  319. }
  320. if (item.inning >= NOHITATBATS) {
  321. checkAwayNoHit = true;
  322. }
  323. }
  324. }
  325. if (!final) {
  326. if (topbottom) {
  327. text = topbottom + ' '
  328. }
  329. text += item.inning;
  330. }
  331. }
  332. }
  333. if (final) {
  334. text = 'Final';
  335. //usually extra innings, perhaps rain-shortened?
  336. if (item.inning != 9) {
  337. text += ' (' + item.inning + ')';
  338. }
  339. cls = 'final';
  340. }
  341. if ((checkHomeNoHit && item.home_team_hits == 0) ||
  342. (checkAwayNoHit && item.away_team_hits == 0)) {
  343. cls += ' noHitter';
  344. }
  345. if (item.hasOwnProperty('away_team_runs') && item.hasOwnProperty('home_team_runs')) {
  346. text += ', ' + item.away_team_runs + '-' + item.home_team_runs;
  347. var parens = true;
  348. } else {
  349. parens = false;
  350. }
  351. if (includeReason) {
  352. text += ' ';
  353. if (parens) {
  354. text += '(';
  355. }
  356. text += item.status;
  357. if (item.reason) {
  358. text += ': ' + item.reason;
  359. }
  360. if (parens) {
  361. text += ')';
  362. }
  363. }
  364. }
  365. if (unknownStatus) {
  366. text += ' DEBUG:' + item.status;
  367. }
  368. //get original text surrounding the 'Team vs Team' to include along with the reordering of the Teams to Away vs Home
  369. gameEl.lastChild.textContent = originalMainText[1] + ' ' + getTeamName(item, 'away') + ' vs ' + getTeamName(item, 'home') + originalMainText[3];
  370. if (text) {
  371. $('<span class="_tmInfo">, <span class="_tmText ' + cls + '">' + text + '</span></span>').appendTo(gameEl);
  372. }
  373. }
  374. });
  375. });
  376. }
  377. });
  378. }
  379.  
  380. function handle_game_media(item, gameEl) {
  381. if (item.game_media && item.game_media.media) {
  382. var media = item.game_media.media;
  383. if ($.isArray(item.game_media.media)) {
  384. media = item.game_media.media[0];
  385. }
  386. if (media.free != "NO") {
  387. if (media.free != "ALL") {
  388. console.log('different media free info', media);
  389. }
  390. gameEl.parentElement.classList.add('_tmMLBFree');
  391. }
  392. }
  393. if (item.tv_station) {
  394. item.tv_station.split(', ').forEach(function (station) {
  395. //TODO find ESPN more generally
  396. if (station == 'ESPN' || station == 'ESPN2' || station == 'ESPN (out-of-market only)') {
  397. gameEl.parentElement.classList.add('_tmESPN');
  398. }
  399. });
  400. }
  401. }
  402.  
  403. //example why this is needed:
  404. //team_city: 'NY Yankees'
  405. //team_name: 'Yankees'
  406. //Should return 'NY Yankees' rather than 'NY Yankees Yankees'
  407. function getTeamName(item, homeaway) {
  408. var city = item[homeaway + '_team_city'];
  409. var name = item[homeaway + '_team_name'];
  410. if (city.slice(-name.length) == name) {
  411. return city;
  412. } else {
  413. return city + ' ' + name;
  414. }
  415. }
  416.  
  417. var initial_delay = 6;
  418. var constant_delay = 120;
  419.  
  420. main();
  421. //this blob compensates for 'initial_delay'
  422. // e.g. main loop runs every 2 minutes, with a 6 second initial delay,
  423. // this will wait 114 seconds to run main(6) and register the Interval of 120 seconds at the same time to run main(6)
  424. setTimeout(function () {
  425. main(initial_delay);
  426. setInterval(main, 1000 * constant_delay, initial_delay);
  427. }, 1000 * (constant_delay - initial_delay));
  428.  
  429. //since we don't do anything when the tab is invisible, force an update when made visible if one was skipped
  430. document.addEventListener('visibilitychange', function () {
  431. if (!document.hidden) {
  432. if (SKIPPED) {
  433. SKIPPED = false;
  434. main();
  435. }
  436. }
  437. });
  438.  
  439.  
  440. function addGlobalStyle(css) {
  441. var head, style;
  442. head = document.getElementsByTagName('head')[0];
  443. if (!head) { return; }
  444. style = document.createElement('style');
  445. style.type = 'text/css';
  446. style.innerHTML = css;
  447. head.appendChild(style);
  448. }
  449.  
  450. $(document).ready(function () {
  451. //do an awful job of figuring out which theme is running
  452. if ($('body').css('background-color').replace(/\s/g, '') == 'rgb(0,0,0)') {
  453. //golden midnight
  454. $('body').addClass('_tmDark');
  455. addGlobalStyle('body._tmDark ._tmInfo ._tmText.inprogress { color: lightblue; }');
  456. addGlobalStyle('body._tmDark ' + selectorParent + '._tmMLBFree { background: green; }');
  457. addGlobalStyle('body._tmDark ' + selectorParent + '._tmESPN { background: rgba(255,7,7,.35); }');
  458. addGlobalStyle('body._tmDark ._tmInfo ._tmText.noHitter { background: crimson; }');
  459. } else {
  460. //white magic or failure
  461. }
  462. addGlobalStyle('._tmInfo ._tmText.inprogress { color: red; }');
  463. addGlobalStyle('._tmInfo ._tmText.warmup { color: goldenrod; }');
  464. addGlobalStyle('._tmInfo ._tmText.final { color: grey; }');
  465. addGlobalStyle('._tmInfo ._tmText.postponed { color: blue; }');
  466. addGlobalStyle('._tmInfo ._tmText.noHitter { background: chartreuse; }');
  467.  
  468. addGlobalStyle(selectorParent + '._tmMLBFree { background: gold; }');
  469. addGlobalStyle(selectorParent + '._tmESPN { background: rgba(255,7,7,.25); }');
  470. });
  471. })()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址