├── .DS_Store ├── js ├── .DS_Store ├── script.js ├── screenshot.js ├── powerRankingUtil.js ├── options.js ├── util.js ├── recordBook.js └── popup.js ├── html ├── .DS_Store ├── screenshot.html ├── popup.html └── options.html ├── images ├── .DS_Store ├── LeagueManagerNoteLogo128.png ├── LeagueManagerNoteLogo16.png ├── LeagueManagerNoteLogo32.png ├── LeagueManagerNoteLogo48.png ├── LeagueManagerNoteLogo64.png ├── LeagueManagerNoteLogo16-rounded.png ├── LeagueManagerNoteLogo128-rounded-monotone.png ├── LeagueManagerNoteLogo16-rounded-monotone.png ├── LeagueManagerNoteLogo32-rounded-monotone.png └── LeagueManagerNoteLogo48-rounded-monotone.png ├── vendor ├── .DS_Store ├── FileSaver.min.js ├── canvas-toBlob.js └── Blob.js ├── css ├── screenshot.css ├── record-book.css ├── all-time-leader-board.css ├── popup.css ├── options.css └── power-ranking.css ├── LICENSE ├── manifest.json └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/.DS_Store -------------------------------------------------------------------------------- /js/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/js/.DS_Store -------------------------------------------------------------------------------- /html/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/html/.DS_Store -------------------------------------------------------------------------------- /images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/.DS_Store -------------------------------------------------------------------------------- /vendor/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/vendor/.DS_Store -------------------------------------------------------------------------------- /images/LeagueManagerNoteLogo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/LeagueManagerNoteLogo128.png -------------------------------------------------------------------------------- /images/LeagueManagerNoteLogo16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/LeagueManagerNoteLogo16.png -------------------------------------------------------------------------------- /images/LeagueManagerNoteLogo32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/LeagueManagerNoteLogo32.png -------------------------------------------------------------------------------- /images/LeagueManagerNoteLogo48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/LeagueManagerNoteLogo48.png -------------------------------------------------------------------------------- /images/LeagueManagerNoteLogo64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/LeagueManagerNoteLogo64.png -------------------------------------------------------------------------------- /images/LeagueManagerNoteLogo16-rounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/LeagueManagerNoteLogo16-rounded.png -------------------------------------------------------------------------------- /images/LeagueManagerNoteLogo128-rounded-monotone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/LeagueManagerNoteLogo128-rounded-monotone.png -------------------------------------------------------------------------------- /images/LeagueManagerNoteLogo16-rounded-monotone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/LeagueManagerNoteLogo16-rounded-monotone.png -------------------------------------------------------------------------------- /images/LeagueManagerNoteLogo32-rounded-monotone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/LeagueManagerNoteLogo32-rounded-monotone.png -------------------------------------------------------------------------------- /images/LeagueManagerNoteLogo48-rounded-monotone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmayer/fantasy-chrome-extension/HEAD/images/LeagueManagerNoteLogo48-rounded-monotone.png -------------------------------------------------------------------------------- /html/screenshot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 |
9 | 10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /js/script.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | 'use strict'; 6 | 7 | chrome.runtime.onInstalled.addListener(function() { 8 | chrome.declarativeContent.onPageChanged.removeRules(undefined, function() { 9 | chrome.declarativeContent.onPageChanged.addRules([{ 10 | conditions: [new chrome.declarativeContent.PageStateMatcher({ 11 | pageUrl: {hostEquals: 'fantasy.espn.com', pathEquals: '/football/league'}, 12 | }) 13 | ], 14 | actions: [new chrome.declarativeContent.ShowPageAction()] 15 | }]); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /css/screenshot.css: -------------------------------------------------------------------------------- 1 | @import 'power-ranking.css'; 2 | @import 'all-time-leader-board.css'; 3 | @import 'record-book.css'; 4 | 5 | .header { 6 | height: 20px; 7 | width: fit-content; 8 | background-color: #1D7225; 9 | color: white; 10 | font-size: 16px; 11 | padding: 5px; 12 | border-radius: 4px 4px 0px 0px; 13 | margin-bottom: 1px; 14 | } 15 | .container { 16 | background-color: #F2F2E8; 17 | color: black; 18 | width: fit-content; 19 | } 20 | .container-row { 21 | padding: 5px; 22 | } 23 | #create-screenshot { 24 | float: left; 25 | width: 600px; 26 | } 27 | #screenshot-img { 28 | float: right; 29 | } 30 | .sub-header { 31 | height: 16px; 32 | background-color: #6DBB75; 33 | color: white; 34 | font-size: 12px; 35 | padding: 5px; 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Joshua Mayer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /js/screenshot.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const saveScreenshot = (callback) => { 4 | chrome.runtime.onMessage.removeListener(listenerFunction); 5 | chrome.runtime.onMessage.addListener(listenerFunction); 6 | chrome.runtime.sendMessage({ 7 | msg: "screen_ready", 8 | data: {} 9 | }); 10 | } 11 | 12 | const listenerFunction = (request, sender, sendResponse) => { 13 | if (request.msg === "something_completed") { 14 | document.getElementById("create-screenshot").innerHTML = request.data.html; 15 | html2canvas(document.getElementById("create-screenshot"), 16 | {width: "fit-content", allowTaint: true}).then(function(canvas) { 17 | document.getElementById("screenshot-img").src = canvas.toDataURL(); 18 | canvas.toBlob((blob) => { 19 | saveAs(blob, request.data.name + ".png"); 20 | callback(); 21 | }); 22 | //Comment the below line to retain the original table 23 | document.getElementById("create-screenshot").style.display = "none"; 24 | }); 25 | } 26 | }; 27 | 28 | const closeWindow = () => { 29 | //setTimeout(() => { 30 | //window.close(); 31 | //}, 1500); 32 | } 33 | 34 | saveScreenshot(closeWindow); 35 | -------------------------------------------------------------------------------- /css/record-book.css: -------------------------------------------------------------------------------- 1 | 60 | -------------------------------------------------------------------------------- /css/all-time-leader-board.css: -------------------------------------------------------------------------------- 1 | 64 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LM Note Generator For ESPN Fantasy Football", 3 | "version": "2.0.0", 4 | "description": "Build All Time Leader Board, Record Book, and Weekly Power Rankings for your ESPN Fantasy Football League!", 5 | "permissions": ["activeTab", "tabs", "declarativeContent", "storage", "http://games.espn.com/"], 6 | "background": { 7 | "scripts": ["js/script.js"], 8 | "persistent": false 9 | }, 10 | "content_scripts": [ 11 | { 12 | "matches": ["http://*.espn.com/*", "https://*.espn.com/*"], 13 | "js": ["vendor/html2canvas.min.js", "js/screenshot.js"] 14 | } 15 | ], 16 | "web_accessible_resources": [ 17 | "js/screenshot.js" 18 | ], 19 | "options_page": "html/options.html", 20 | "page_action": { 21 | "default_popup": "html/popup.html", 22 | "default_icon": { 23 | "16": "images/LeagueManagerNoteLogo16-rounded.png", 24 | "32": "images/LeagueManagerNoteLogo32-rounded-monotone.png", 25 | "48": "images/LeagueManagerNoteLogo48-rounded-monotone.png", 26 | "128": "images/LeagueManagerNoteLogo128-rounded-monotone.png" 27 | } 28 | }, 29 | "icons": { 30 | "16": "images/LeagueManagerNoteLogo16-rounded.png", 31 | "32": "images/LeagueManagerNoteLogo32-rounded-monotone.png", 32 | "48": "images/LeagueManagerNoteLogo48-rounded-monotone.png", 33 | "128": "images/LeagueManagerNoteLogo128-rounded-monotone.png" 34 | }, 35 | "manifest_version": 2 36 | } 37 | -------------------------------------------------------------------------------- /css/popup.css: -------------------------------------------------------------------------------- 1 | .popup-container { 2 | width: 400px; 3 | } 4 | .header { 5 | height: 20px; 6 | background-color: #1D7225; 7 | color: white; 8 | font-size: 16px; 9 | padding: 5px; 10 | border-radius: 4px 4px 0px 0px; 11 | margin-bottom: 1px; 12 | } 13 | .container { 14 | background-color: #F2F2E8; 15 | color: black; 16 | } 17 | .container-row { 18 | padding: 10px 5px 10px 5px; 19 | } 20 | .sub-header { 21 | height: 16px; 22 | background-color: #6DBB75; 23 | color: white; 24 | font-size: 12px; 25 | padding: 5px; 26 | } 27 | .col-8 { 28 | width: 32%; 29 | display: inline-block; 30 | } 31 | .col-12 { 32 | width: 49%; 33 | display: inline-block; 34 | } 35 | .align-left { 36 | text-align: left; 37 | } 38 | .align-center { 39 | text-align: center; 40 | } 41 | .align-right { 42 | text-align: right; 43 | } 44 | button#update-database { 45 | float: right; 46 | margin-right: 5px; 47 | } 48 | button#delete-database { 49 | float: right; 50 | } 51 | button#all-time-wins, button#record-book, button#power-rankings { 52 | width: 100% 53 | } 54 | span#database-last-update { 55 | margin-left: 5px; 56 | } 57 | #periodId { 58 | float: right; 59 | width: 75px; 60 | text-align: center; 61 | } 62 | #power-rankings-table { 63 | width: 100%; 64 | text-align: center; 65 | padding-bottom: 10px; 66 | } 67 | #power-rankings-table thead { 68 | font-weight: bold; 69 | } 70 | #power-rankings-table input[type="text"] { 71 | width: 90%; 72 | padding-left: 8px; 73 | border-radius: 4px; 74 | border: 1px solid #aaa; 75 | } 76 | #power-rankings-table td:first-child { 77 | text-align: right; 78 | } 79 | #power-rankings-table select { 80 | width: 95%; 81 | background: #fafafa; 82 | margin-left: 5px; 83 | } 84 | input#power-ranking-title { 85 | background: inherit; 86 | border: none !important; 87 | border-bottom: 1px solid #aaa !important; 88 | margin-bottom: 5px; 89 | text-align: center; 90 | font-size: 12px; 91 | width: 68% !important; 92 | margin-left: auto; 93 | margin-right: auto; 94 | position: relative; 95 | display: block; 96 | border-radius: 0px !important; 97 | } 98 | -------------------------------------------------------------------------------- /html/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 18 |
19 |
20 | Database Management - 21 |
22 |
23 | 26 |
27 | Last Updated: 28 |
29 | 30 | 31 |
32 |
33 |
34 |
35 | Generate League History Content 36 |
37 |
38 |
39 | 40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 |
48 | Weekly Power Rankings - 49 |
50 |
51 |
52 |
53 | 54 |
55 |
56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /css/options.css: -------------------------------------------------------------------------------- 1 | select#league-selector { 2 | background: #f8f8f2; 3 | border: none; 4 | } 5 | .options-container { 6 | width: 500px; 7 | } 8 | .header { 9 | height: 22px; 10 | background-color: #1D7225; 11 | color: white; 12 | font-size: 16px; 13 | padding: 5px; 14 | border-radius: 4px 4px 0px 0px; 15 | margin-bottom: 1px; 16 | line-height: 22px; 17 | text-align: center; 18 | } 19 | .container { 20 | background-color: #F2F2E8; 21 | color: black; 22 | } 23 | .container-row { 24 | padding: 5px; 25 | overflow: hidden; 26 | transition: max-height .25s linear; 27 | } 28 | table { 29 | margin-left: auto; 30 | margin-right: auto; 31 | width: 100%; 32 | padding: 5px; 33 | } 34 | th.label-header { 35 | text-align: right; 36 | width: 140px; 37 | } 38 | tr td:first-child { 39 | text-align: right; 40 | } 41 | td input[type="number"] { 42 | width: 50px; 43 | } 44 | td input { 45 | text-align: center; 46 | width: 94%; 47 | margin-left: 5%; 48 | } 49 | .sub-header { 50 | height: 16px; 51 | background-color: #6DBB75; 52 | color: white; 53 | font-size: 12px; 54 | padding: 5px; 55 | user-select: none; 56 | margin-bottom: 1px; 57 | } 58 | .category.inactive > .container-row { 59 | max-height: 0px !important; 60 | padding: 0px; 61 | } 62 | .category.active > .sub-header { 63 | margin-bottom: 0px; 64 | background: #1D7225; 65 | } 66 | .category > .sub-header { 67 | cursor: pointer; 68 | } 69 | .button-container { 70 | text-align: right; 71 | margin-top: 0px; 72 | height: 22px; 73 | background-color: #1D7225; 74 | color: white; 75 | padding: 5px; 76 | border-radius: 0px 0px 4px 4px; 77 | line-height: 22px; 78 | } 79 | #league-selector { 80 | display: inline-block; 81 | } 82 | .category .accordian-icon::before { 83 | content: "-"; 84 | } 85 | .category.inactive .accordian-icon::before { 86 | content: "+"; 87 | } 88 | table#sacko-override tr td select{ 89 | width: 100%; 90 | background: #f8f8f2; 91 | } 92 | table#sacko-override tr td { 93 | text-align: center; 94 | } 95 | .leader-board-options td input { 96 | text-align: center; 97 | width: 90%; 98 | margin-left: 5%; 99 | } 100 | td input[type="checkbox"] { 101 | text-align: left; 102 | text-indent: 20px; 103 | line-height: 12px; 104 | } 105 | td input[type="checkbox"]::before { 106 | content: 'DISABLED'; 107 | } 108 | td input[type="checkbox"]:checked::before { 109 | content: 'ENABLED'; 110 | } 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LM Note Generator For ESPN Fantasy Football 2 | Creates LM Note content for a public ESPN Fantasy Football League. 3 | 4 | Chrome store: [link](https://chrome.google.com/webstore/detail/lm-note-generator-for-esp/ahcblhpcealjpkmndgmkdnebbjakicno) 5 | 6 | ## Documentation 7 | 8 | [Wiki](https://github.com/jpmayer/fantasy-chrome-extension/wiki) 9 | 10 | ## Installation 11 | 12 | Pull code 13 | 14 | ```sh 15 | $ git clone https://github.com/jpmayer/fantasy-chrome-extension.git 16 | ``` 17 | 18 | Open Chrome Extensions in browser 19 | 20 | > [chrome://extensions/](chrome://extensions/) 21 | 22 | Choose to 'Load Unpacked' and choose the directory of the pulled code. (You may have to switch into Developer Mode first) 23 | 24 | ## Usage 25 | 26 | Navigate to your ESPN Fantasy Football league homepage. It should have a url like so: 27 | 28 | > http://games.espn.com/ffl/leagueoffice?leagueId={leagueId}&seasonId=2018 29 | 30 | Once you are on this page you should see the extension icon in a clickable state. Click to launch to launch the extension as a popup 31 | 32 | To get your league setup, simply click the "Update" button to pull your league's information into a local HTML database. 33 | 34 | Once this completes, simply hit one of the generate actions to generate a leader board, record book or power ranking image for your league. 35 | 36 | The power ranking action has an input form to fill out before rendering, you can change the position of the managers and give them a weekly comment blurb. (The power rankings are fully manual, the default listing oreder is the original order in which the managers joined the league as returned by ESPN API) 37 | 38 | ![Extension Popup](https://i.imgur.com/MwYjdeo.png) ![Filled In](https://i.imgur.com/o23FTDe.png) 39 | 40 | 41 | ## Options 42 | 43 | Right click the extension icon in the browser toolbar and click 'Options' 44 | 45 | Or 46 | 47 | Navigate to 48 | 49 | > [chrome://extensions/](chrome://extensions/) 50 | 51 | Find the ESPN LM Note Builder extension in the list. 52 | 53 | Click "Details". Then find and select "Extension options" 54 | 55 | Here you can add in old league records, update manager display names, and set some other personal league options. These are all completely optional and are here to provide a more personal experience. The options are per league and can only be set on leagues who have a database stored in local storage (see the Usage section) 56 | 57 | ![Options](https://i.imgur.com/u47UAUU.png) 58 | 59 | 60 | ## Example Output 61 | 62 | You can find more examples in the [wiki](https://github.com/jpmayer/fantasy-chrome-extension/wiki/Example-Gallery) 63 | 64 | #### ****Power Ranking**** 65 | 66 | ![Power Rankings](https://i.imgur.com/k1CSiCb.png) 67 | 68 | #### ****Leader Board**** 69 | 70 | ![Leader Board](https://i.imgur.com/lL5wIC6.png) 71 | 72 | #### ****Record Book**** 73 | 74 | ![Record Book](https://i.imgur.com/XDUjJMB.png) 75 | -------------------------------------------------------------------------------- /vendor/FileSaver.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 2 | var saveAs=saveAs||function(e){"use strict";if("undefined"==typeof navigator||!/MSIE [1-9]\./.test(navigator.userAgent)){var t=e.document,n=function(){return e.URL||e.webkitURL||e},o=t.createElementNS("http://www.w3.org/1999/xhtml","a"),r="download"in o,i=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},a=/Version\/[\d\.]+.*Safari/.test(navigator.userAgent),c=e.webkitRequestFileSystem,d=e.requestFileSystem||c||e.mozRequestFileSystem,u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},s="application/octet-stream",f=0,l=4e4,v=function(e){var t=function(){"string"==typeof e?n().revokeObjectURL(e):e.remove()};setTimeout(t,l)},p=function(e,t,n){t=[].concat(t);for(var o=t.length;o--;){var r=e["on"+t[o]];if("function"==typeof r)try{r.call(e,n||e)}catch(i){u(i)}}},w=function(e){return/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)?new Blob(["\uFEFF",e],{type:e.type}):e},y=function(t,u,l){l||(t=w(t));var y,m,S,h=this,R=t.type,O=!1,g=function(){p(h,"writestart progress write writeend".split(" "))},b=function(){if(m&&a&&"undefined"!=typeof FileReader){var o=new FileReader;return o.onloadend=function(){var e=o.result;m.location.href="data:attachment/file"+e.slice(e.search(/[,;]/)),h.readyState=h.DONE,g()},o.readAsDataURL(t),void(h.readyState=h.INIT)}if((O||!y)&&(y=n().createObjectURL(t)),m)m.location.href=y;else{var r=e.open(y,"_blank");void 0===r&&a&&(e.location.href=y)}h.readyState=h.DONE,g(),v(y)},E=function(e){return function(){return h.readyState!==h.DONE?e.apply(this,arguments):void 0}},N={create:!0,exclusive:!1};return h.readyState=h.INIT,u||(u="download"),r?(y=n().createObjectURL(t),void setTimeout(function(){o.href=y,o.download=u,i(o),g(),v(y),h.readyState=h.DONE})):(e.chrome&&R&&R!==s&&(S=t.slice||t.webkitSlice,t=S.call(t,0,t.size,s),O=!0),c&&"download"!==u&&(u+=".download"),(R===s||c)&&(m=e),d?(f+=t.size,void d(e.TEMPORARY,f,E(function(e){e.root.getDirectory("saved",N,E(function(e){var n=function(){e.getFile(u,N,E(function(e){e.createWriter(E(function(n){n.onwriteend=function(t){m.location.href=e.toURL(),h.readyState=h.DONE,p(h,"writeend",t),v(e)},n.onerror=function(){var e=n.error;e.code!==e.ABORT_ERR&&b()},"writestart progress write abort".split(" ").forEach(function(e){n["on"+e]=h["on"+e]}),n.write(t),h.abort=function(){n.abort(),h.readyState=h.DONE},h.readyState=h.WRITING}),b)}),b)};e.getFile(u,{create:!1},E(function(e){e.remove(),n()}),E(function(e){e.code===e.NOT_FOUND_ERR?n():b()}))}),b)}),b)):void b())},m=y.prototype,S=function(e,t,n){return new y(e,t,n)};return"undefined"!=typeof navigator&&navigator.msSaveOrOpenBlob?function(e,t,n){return n||(e=w(e)),navigator.msSaveOrOpenBlob(e,t||"download")}:(m.abort=function(){var e=this;e.readyState=e.DONE,p(e,"abort")},m.readyState=m.INIT=0,m.WRITING=1,m.DONE=2,m.error=m.onwritestart=m.onprogress=m.onwrite=m.onabort=m.onerror=m.onwriteend=null,S)}}("undefined"!=typeof self&&self||"undefined"!=typeof window&&window||this.content);"undefined"!=typeof module&&module.exports?module.exports.saveAs=saveAs:"undefined"!=typeof define&&null!==define&&null!==define.amd&&define([],function(){return saveAs}); -------------------------------------------------------------------------------- /css/power-ranking.css: -------------------------------------------------------------------------------- 1 | #powerRanking { 2 | width:600px; 3 | position:relative; 4 | margin-left:auto; 5 | margin-right:auto; 6 | border: 1px solid white; 7 | border-radius: 3px; 8 | font:normal 10px verdana; 9 | } 10 | #powerRanking table { 11 | border:0px solid black; 12 | font-size:12px; 13 | width:100%; 14 | border-collapse: collapse; 15 | } 16 | #powerRanking table tr { 17 | background-color:#F8F8F2; 18 | } 19 | #powerRanking table tr:nth-child(odd){ 20 | background-color:#F2F2E8; 21 | } 22 | #powerRanking table tr:nth-child(2) { 23 | background-color:#6DBB75; 24 | } 25 | #powerRanking table tr:first-child { 26 | width:500px; 27 | background-color:#1D7225; 28 | color:white; 29 | text-align:center; 30 | } 31 | th { 32 | border-radius: 3px 3px 0px 0px; 33 | } 34 | #powerRanking table th h3{ 35 | margin:0px; 36 | } 37 | tr.rank { 38 | height: 60px; 39 | vertical-align: middle; 40 | } 41 | tr.rank td:first-child { 42 | font-size: 20px; 43 | text-align: center; 44 | vertical-align: middle; 45 | width: 10%; 46 | } 47 | .ranking { 48 | border-radius: 50px; 49 | color: white; 50 | padding: 0px; 51 | margin: auto; 52 | } 53 | tr.rank td:nth-child(2) { 54 | width: 100px; 55 | vertical-align:middle; 56 | text-align: center; 57 | display: inline-block; 58 | } 59 | tr.rank td:nth-child(3) { 60 | width: 10%; 61 | vertical-align: middle; 62 | } 63 | tr.rank td:nth-child(4) { 64 | width: 15%; 65 | vertical-align: middle; 66 | } 67 | tr.rank td:nth-child(5) { 68 | width: 55%; 69 | vertical-align: middle; 70 | font-size: 10px; 71 | padding-left:5px; 72 | padding-right:5px; 73 | padding-top: 5px; 74 | padding-bottom: 5px; 75 | } 76 | .teamPicture { 77 | height: 100%; 78 | display: table-cell; 79 | padding: 5px 10px; 80 | } 81 | .teamPicture img{ 82 | position: relative; 83 | max-width: 65px; 84 | height: 55px; 85 | vertical-align: middle; 86 | object-fit: cover; 87 | border-radius: 10px; 88 | } 89 | .manager-name { 90 | font-size: 13px; 91 | white-space: nowrap; 92 | top: -5px; 93 | position: relative; 94 | } 95 | .manager-name a { 96 | text-decoration: none !important; 97 | color: #225DB7 !important; 98 | } 99 | .team-record { 100 | color: #888; 101 | } 102 | .up { 103 | border-bottom: 8px solid green; 104 | border-left: 4px solid transparent; 105 | border-right: 4px solid transparent; 106 | width: 0px; 107 | position: relative; 108 | left: 17px; 109 | top: 6px; 110 | } 111 | .down { 112 | border-top: 8px solid red; 113 | border-left: 4px solid transparent; 114 | border-right: 4px solid transparent; 115 | width: 0px; 116 | position: relative; 117 | top: 7px; 118 | left: 17px; 119 | } 120 | .no-change { 121 | border-top: 8px solid transparent; 122 | border-left: 4px solid transparent; 123 | border-right: 4px solid transparent; 124 | } 125 | .delta-div { 126 | position: relative; right: -5px; 127 | } 128 | .delta { 129 | width: 25px; 130 | position: relative; 131 | font-size: 17px; 132 | line-height: 20px; 133 | top: -9px; 134 | left: 26px; 135 | } 136 | .delta-up { 137 | color: green; 138 | } 139 | .delta-down { 140 | color: red; 141 | } 142 | .last-weeks-position { 143 | color: #888; 144 | font-size: 10px; 145 | top: -5px; 146 | position: relative; 147 | } 148 | .center{ 149 | text-align:center; 150 | } 151 | .manager-name-under-team { 152 | font-size: 10px; 153 | padding-top: 2px; 154 | padding-bottom: 2px; 155 | white-space:nowrap; 156 | } 157 | .team-name { 158 | white-space:nowrap; 159 | font-weight: bold; 160 | font-size: 11px; 161 | } 162 | -------------------------------------------------------------------------------- /vendor/canvas-toBlob.js: -------------------------------------------------------------------------------- 1 | /* canvas-toBlob.js 2 | * A canvas.toBlob() implementation. 3 | * 2016-05-26 4 | * 5 | * By Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr 6 | * License: MIT 7 | * See https://github.com/eligrey/canvas-toBlob.js/blob/master/LICENSE.md 8 | */ 9 | 10 | /*global self */ 11 | /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, 12 | plusplus: true */ 13 | 14 | /*! @source http://purl.eligrey.com/github/canvas-toBlob.js/blob/master/canvas-toBlob.js */ 15 | 16 | (function(view) { 17 | "use strict"; 18 | var 19 | Uint8Array = view.Uint8Array 20 | , HTMLCanvasElement = view.HTMLCanvasElement 21 | , canvas_proto = HTMLCanvasElement && HTMLCanvasElement.prototype 22 | , is_base64_regex = /\s*;\s*base64\s*(?:;|$)/i 23 | , to_data_url = "toDataURL" 24 | , base64_ranks 25 | , decode_base64 = function(base64) { 26 | var 27 | len = base64.length 28 | , buffer = new Uint8Array(len / 4 * 3 | 0) 29 | , i = 0 30 | , outptr = 0 31 | , last = [0, 0] 32 | , state = 0 33 | , save = 0 34 | , rank 35 | , code 36 | , undef 37 | ; 38 | while (len--) { 39 | code = base64.charCodeAt(i++); 40 | rank = base64_ranks[code-43]; 41 | if (rank !== 255 && rank !== undef) { 42 | last[1] = last[0]; 43 | last[0] = code; 44 | save = (save << 6) | rank; 45 | state++; 46 | if (state === 4) { 47 | buffer[outptr++] = save >>> 16; 48 | if (last[1] !== 61 /* padding character */) { 49 | buffer[outptr++] = save >>> 8; 50 | } 51 | if (last[0] !== 61 /* padding character */) { 52 | buffer[outptr++] = save; 53 | } 54 | state = 0; 55 | } 56 | } 57 | } 58 | // 2/3 chance there's going to be some null bytes at the end, but that 59 | // doesn't really matter with most image formats. 60 | // If it somehow matters for you, truncate the buffer up outptr. 61 | return buffer; 62 | } 63 | ; 64 | if (Uint8Array) { 65 | base64_ranks = new Uint8Array([ 66 | 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1 67 | , -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 68 | , 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 69 | , -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 70 | , 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 71 | ]); 72 | } 73 | if (HTMLCanvasElement && (!canvas_proto.toBlob || !canvas_proto.toBlobHD)) { 74 | if (!canvas_proto.toBlob) 75 | canvas_proto.toBlob = function(callback, type /*, ...args*/) { 76 | if (!type) { 77 | type = "image/png"; 78 | } if (this.mozGetAsFile) { 79 | callback(this.mozGetAsFile("canvas", type)); 80 | return; 81 | } if (this.msToBlob && /^\s*image\/png\s*(?:$|;)/i.test(type)) { 82 | callback(this.msToBlob()); 83 | return; 84 | } 85 | 86 | var 87 | args = Array.prototype.slice.call(arguments, 1) 88 | , dataURI = this[to_data_url].apply(this, args) 89 | , header_end = dataURI.indexOf(",") 90 | , data = dataURI.substring(header_end + 1) 91 | , is_base64 = is_base64_regex.test(dataURI.substring(0, header_end)) 92 | , blob 93 | ; 94 | if (Blob.fake) { 95 | // no reason to decode a data: URI that's just going to become a data URI again 96 | blob = new Blob 97 | if (is_base64) { 98 | blob.encoding = "base64"; 99 | } else { 100 | blob.encoding = "URI"; 101 | } 102 | blob.data = data; 103 | blob.size = data.length; 104 | } else if (Uint8Array) { 105 | if (is_base64) { 106 | blob = new Blob([decode_base64(data)], {type: type}); 107 | } else { 108 | blob = new Blob([decodeURIComponent(data)], {type: type}); 109 | } 110 | } 111 | callback(blob); 112 | }; 113 | 114 | if (!canvas_proto.toBlobHD && canvas_proto.toDataURLHD) { 115 | canvas_proto.toBlobHD = function() { 116 | to_data_url = "toDataURLHD"; 117 | var blob = this.toBlob(); 118 | to_data_url = "toDataURL"; 119 | return blob; 120 | } 121 | } else { 122 | canvas_proto.toBlobHD = canvas_proto.toBlob; 123 | } 124 | } 125 | }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this)); -------------------------------------------------------------------------------- /js/powerRankingUtil.js: -------------------------------------------------------------------------------- 1 | const generatePowerRanking = (rankingList, title, downloadImageFunction) => { 2 | isOverridePowerRanking = true; 3 | // Get trending stat 4 | leagueDatabase.webdb.db.transaction((tx) => { 5 | tx.executeSql("SELECT manager, week, year, ranking FROM rankings " + 6 | "WHERE week = ? AND year = ? ", [currentWeek - 1, selectedYear], 7 | (tx, tr) => { 8 | // get sacko overrides from local storage 9 | chrome.storage.sync.get(['league-' + leagueId], (response) => { 10 | const leagueDict = response['league-' + leagueId]; 11 | 12 | let lastWeeksRanking = (tr.rows.length > 0) ? {} : null; 13 | 14 | for(var r = 0; r < tr.rows.length; r++) { 15 | lastWeeksRanking[tr.rows[r].manager] = { 16 | manager: tr.rows[r].manager, 17 | week: tr.rows[r].week, 18 | year: tr.rows[r].year, 19 | place: tr.rows[r].ranking 20 | }; 21 | } 22 | 23 | let resultString = "
"; 24 | resultString = resultString + ""; 25 | resultString = resultString + ""; 26 | resultString = resultString + ""; 27 | rankingList.forEach((ranking) => { 28 | let manager = ranking.manager; 29 | let record = getTeamRecord(selectedYearLeagueSettings.teams, manager); 30 | let lwr = (lastWeeksRanking && lastWeeksRanking[manager]) ? lastWeeksRanking[manager] : null; 31 | resultString = resultString + generateHTMLRowForPowerRanking(ranking, record, lwr, leagueDict); 32 | }) 33 | resultString = resultString + "

" + title + "

RankTeam / RecordTrendingComments
"; 57 | 58 | downloadImageFunction(resultString); 59 | }); 60 | }, errorHandler); 61 | }); 62 | 63 | } 64 | 65 | const generateHTMLRowForPowerRanking = (ranking, record, lastWeeksRanking, leagueDict) => { 66 | let delta = (lastWeeksRanking) ? parseInt(ranking.place) - parseInt(lastWeeksRanking.place) : 0; 67 | let lastWeekPositionString = (lastWeeksRanking) ? 'Last Week: ' + lastWeeksRanking.place : ''; 68 | let managerName = (leagueDict && leagueDict.managerMap && leagueDict.managerMap[ranking.manager]) ? leagueDict.managerMap[ranking.manager] : ranking.manager; 69 | let resultString = ""; 70 | let teamName = "
" + managerName + "
"; 71 | console.log(record); 72 | if(leagueDict && !leagueDict.hideTeamNames) { 73 | teamName = "
" + record.teamName + "
" + managerName + "
"; 74 | } 75 | if(leagueDict && leagueDict.showTeamPictures) { 76 | let teamPicture = (leagueDict.managerImageMap[ranking.manager]) ? leagueDict.managerImageMap[ranking.manager]: record.image; 77 | resultString = "
" + ranking.place + "
" + teamName + "
" + record.wins + '-' + record.losses + '-' + record.ties + "
" + getDeltaString(delta) + "
" + lastWeekPositionString + "
" + ranking.description + ""; 78 | } else { 79 | resultString = "
" + ranking.place + "
" + teamName + "
" + record.wins + '-' + record.losses + '-' + record.ties + "
" + getDeltaString(delta) + "
" + lastWeekPositionString + "
" + ranking.description + ""; 80 | } 81 | return resultString; 82 | } 83 | 84 | const getDeltaSymbolClass = (delta) => { 85 | if(delta < 0) { 86 | return "up"; 87 | } else if(delta > 0) { 88 | return "down"; 89 | } else { 90 | return "no-change"; 91 | } 92 | } 93 | 94 | const getDeltaClass = (delta) => { 95 | if(delta < 0) { 96 | return "delta-up"; 97 | } else if(delta > 0) { 98 | return "delta-down"; 99 | } else { 100 | return ""; 101 | } 102 | } 103 | 104 | const getDeltaString = (delta) => { 105 | if(delta === 0) { 106 | return "---"; 107 | } else if (delta < 0){ 108 | return -delta; 109 | } else { 110 | return delta; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /html/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |
10 | ESPN Fantasy Football League Note Generator Options 11 |
12 |
13 |
14 | League: 15 |
16 |
17 |
18 | Manager Options 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | Leader Board Options 27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
Track Losers' Bracket Games:
Track 3rd Place Game:
Collapse Icons:
Hide Average Line:
Average Line Title:
Last Place Column Header:
Manually Override Last Place:
YearManager
38 |
39 |
40 |
41 |
42 | Record Book Options 43 |
44 |
45 | 46 | 47 | 48 |
Hide Player Records:
Manually Override Player Records:
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
Position RecordPlayer NameScoreDate
Quarter Back - Game:
Quarter Back - Season:
Running Back - Game:
Running Back - Season:
Wide Reciever - Game:
Wide Reciever - Season:
Tight End - Game:
Tight End - Season:
D/ST - Game:
D/ST - Season:
Kicker - Game:
Kicker - Season:
69 |
70 |
71 |
72 |
73 | Power Ranking Options 74 |
75 |
76 | 77 | 78 | 79 | 80 |
Hide Team Names:
Show Team Pictures:
Manually Override Pictures:
81 |
ManagerImage URL
82 |
83 |
84 |
85 |
86 | Advanced Options 87 |
88 |
89 | 90 | 91 |
Data Manipulation:
92 | 93 | 94 |
95 |
96 |
97 |
98 | 99 | 100 |
101 |
102 |
103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /vendor/Blob.js: -------------------------------------------------------------------------------- 1 | /* Blob.js 2 | * A Blob, File, FileReader & URL implementation. 3 | * 2018-08-09 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * By Jimmy Wärting, https://github.com/jimmywarting 7 | * License: MIT 8 | * See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md 9 | */ 10 | 11 | ;(function(){ 12 | 13 | var global = typeof window === 'object' 14 | ? window : typeof self === 'object' 15 | ? self : this 16 | 17 | var BlobBuilder = global.BlobBuilder 18 | || global.WebKitBlobBuilder 19 | || global.MSBlobBuilder 20 | || global.MozBlobBuilder; 21 | 22 | global.URL = global.URL || global.webkitURL || function(href, a) { 23 | a = document.createElement('a') 24 | a.href = href 25 | return a 26 | } 27 | 28 | var origBlob = global.Blob 29 | var createObjectURL = URL.createObjectURL 30 | var revokeObjectURL = URL.revokeObjectURL 31 | var strTag = global.Symbol && global.Symbol.toStringTag 32 | var blobSupported = false 33 | var blobSupportsArrayBufferView = false 34 | var arrayBufferSupported = !!global.ArrayBuffer 35 | var blobBuilderSupported = BlobBuilder 36 | && BlobBuilder.prototype.append 37 | && BlobBuilder.prototype.getBlob; 38 | 39 | try { 40 | // Check if Blob constructor is supported 41 | blobSupported = new Blob(['ä']).size === 2 42 | 43 | // Check if Blob constructor supports ArrayBufferViews 44 | // Fails in Safari 6, so we need to map to ArrayBuffers there. 45 | blobSupportsArrayBufferView = new Blob([new Uint8Array([1,2])]).size === 2 46 | } catch(e) {} 47 | 48 | /** 49 | * Helper function that maps ArrayBufferViews to ArrayBuffers 50 | * Used by BlobBuilder constructor and old browsers that didn't 51 | * support it in the Blob constructor. 52 | */ 53 | function mapArrayBufferViews(ary) { 54 | return ary.map(function(chunk) { 55 | if (chunk.buffer instanceof ArrayBuffer) { 56 | var buf = chunk.buffer; 57 | 58 | // if this is a subarray, make a copy so we only 59 | // include the subarray region from the underlying buffer 60 | if (chunk.byteLength !== buf.byteLength) { 61 | var copy = new Uint8Array(chunk.byteLength); 62 | copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength)); 63 | buf = copy.buffer; 64 | } 65 | 66 | return buf; 67 | } 68 | 69 | return chunk; 70 | }); 71 | } 72 | 73 | function BlobBuilderConstructor(ary, options) { 74 | options = options || {}; 75 | 76 | var bb = new BlobBuilder(); 77 | mapArrayBufferViews(ary).forEach(function(part) { 78 | bb.append(part); 79 | }); 80 | 81 | return options.type ? bb.getBlob(options.type) : bb.getBlob(); 82 | }; 83 | 84 | function BlobConstructor(ary, options) { 85 | return new origBlob(mapArrayBufferViews(ary), options || {}); 86 | }; 87 | 88 | if (global.Blob) { 89 | BlobBuilderConstructor.prototype = Blob.prototype; 90 | BlobConstructor.prototype = Blob.prototype; 91 | } 92 | 93 | function FakeBlobBuilder() { 94 | function toUTF8Array(str) { 95 | var utf8 = []; 96 | for (var i=0; i < str.length; i++) { 97 | var charcode = str.charCodeAt(i); 98 | if (charcode < 0x80) utf8.push(charcode); 99 | else if (charcode < 0x800) { 100 | utf8.push(0xc0 | (charcode >> 6), 101 | 0x80 | (charcode & 0x3f)); 102 | } 103 | else if (charcode < 0xd800 || charcode >= 0xe000) { 104 | utf8.push(0xe0 | (charcode >> 12), 105 | 0x80 | ((charcode>>6) & 0x3f), 106 | 0x80 | (charcode & 0x3f)); 107 | } 108 | // surrogate pair 109 | else { 110 | i++; 111 | // UTF-16 encodes 0x10000-0x10FFFF by 112 | // subtracting 0x10000 and splitting the 113 | // 20 bits of 0x0-0xFFFFF into two halves 114 | charcode = 0x10000 + (((charcode & 0x3ff)<<10) 115 | | (str.charCodeAt(i) & 0x3ff)); 116 | utf8.push(0xf0 | (charcode >>18), 117 | 0x80 | ((charcode>>12) & 0x3f), 118 | 0x80 | ((charcode>>6) & 0x3f), 119 | 0x80 | (charcode & 0x3f)); 120 | } 121 | } 122 | return utf8; 123 | } 124 | function fromUtf8Array(array) { 125 | var out, i, len, c; 126 | var char2, char3; 127 | 128 | out = ""; 129 | len = array.length; 130 | i = 0; 131 | while (i < len) { 132 | c = array[i++]; 133 | switch (c >> 4) 134 | { 135 | case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: 136 | // 0xxxxxxx 137 | out += String.fromCharCode(c); 138 | break; 139 | case 12: case 13: 140 | // 110x xxxx 10xx xxxx 141 | char2 = array[i++]; 142 | out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); 143 | break; 144 | case 14: 145 | // 1110 xxxx 10xx xxxx 10xx xxxx 146 | char2 = array[i++]; 147 | char3 = array[i++]; 148 | out += String.fromCharCode(((c & 0x0F) << 12) | 149 | ((char2 & 0x3F) << 6) | 150 | ((char3 & 0x3F) << 0)); 151 | break; 152 | } 153 | } 154 | return out; 155 | } 156 | function isDataView(obj) { 157 | return obj && DataView.prototype.isPrototypeOf(obj) 158 | } 159 | function bufferClone(buf) { 160 | var view = new Array(buf.byteLength) 161 | var array = new Uint8Array(buf) 162 | var i = view.length 163 | while(i--) { 164 | view[i] = array[i] 165 | } 166 | return view 167 | } 168 | function encodeByteArray(input) { 169 | var byteToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' 170 | 171 | var output = []; 172 | 173 | for (var i = 0; i < input.length; i += 3) { 174 | var byte1 = input[i]; 175 | var haveByte2 = i + 1 < input.length; 176 | var byte2 = haveByte2 ? input[i + 1] : 0; 177 | var haveByte3 = i + 2 < input.length; 178 | var byte3 = haveByte3 ? input[i + 2] : 0; 179 | 180 | var outByte1 = byte1 >> 2; 181 | var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4); 182 | var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6); 183 | var outByte4 = byte3 & 0x3F; 184 | 185 | if (!haveByte3) { 186 | outByte4 = 64; 187 | 188 | if (!haveByte2) { 189 | outByte3 = 64; 190 | } 191 | } 192 | 193 | output.push( 194 | byteToCharMap[outByte1], byteToCharMap[outByte2], 195 | byteToCharMap[outByte3], byteToCharMap[outByte4]) 196 | } 197 | 198 | return output.join('') 199 | } 200 | 201 | var create = Object.create || function (a) { 202 | function c() {} 203 | c.prototype = a; 204 | return new c 205 | } 206 | 207 | if (arrayBufferSupported) { 208 | var viewClasses = [ 209 | '[object Int8Array]', 210 | '[object Uint8Array]', 211 | '[object Uint8ClampedArray]', 212 | '[object Int16Array]', 213 | '[object Uint16Array]', 214 | '[object Int32Array]', 215 | '[object Uint32Array]', 216 | '[object Float32Array]', 217 | '[object Float64Array]' 218 | ] 219 | 220 | var isArrayBufferView = ArrayBuffer.isView || function(obj) { 221 | return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 222 | } 223 | } 224 | 225 | 226 | 227 | /********************************************************/ 228 | /* Blob constructor */ 229 | /********************************************************/ 230 | function Blob(chunks, opts) { 231 | chunks = chunks || [] 232 | for (var i = 0, len = chunks.length; i < len; i++) { 233 | var chunk = chunks[i] 234 | if (chunk instanceof Blob) { 235 | chunks[i] = chunk._buffer 236 | } else if (typeof chunk === 'string') { 237 | chunks[i] = toUTF8Array(chunk) 238 | } else if (arrayBufferSupported && (ArrayBuffer.prototype.isPrototypeOf(chunk) || isArrayBufferView(chunk))) { 239 | chunks[i] = bufferClone(chunk) 240 | } else if (arrayBufferSupported && isDataView(chunk)) { 241 | chunks[i] = bufferClone(chunk.buffer) 242 | } else { 243 | chunks[i] = toUTF8Array(String(chunk)) 244 | } 245 | } 246 | 247 | this._buffer = [].concat.apply([], chunks) 248 | this.size = this._buffer.length 249 | this.type = opts ? opts.type || '' : '' 250 | } 251 | 252 | Blob.prototype.slice = function(start, end, type) { 253 | var slice = this._buffer.slice(start || 0, end || this._buffer.length) 254 | return new Blob([slice], {type: type}) 255 | } 256 | 257 | Blob.prototype.toString = function() { 258 | return '[object Blob]' 259 | } 260 | 261 | 262 | 263 | /********************************************************/ 264 | /* File constructor */ 265 | /********************************************************/ 266 | function File(chunks, name, opts) { 267 | opts = opts || {} 268 | var a = Blob.call(this, chunks, opts) || this 269 | a.name = name 270 | a.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date 271 | a.lastModified = +a.lastModifiedDate 272 | 273 | return a 274 | } 275 | 276 | File.prototype = create(Blob.prototype); 277 | File.prototype.constructor = File; 278 | 279 | if (Object.setPrototypeOf) 280 | Object.setPrototypeOf(File, Blob); 281 | else { 282 | try {File.__proto__ = Blob} catch (e) {} 283 | } 284 | 285 | File.prototype.toString = function() { 286 | return '[object File]' 287 | } 288 | 289 | 290 | /********************************************************/ 291 | /* FileReader constructor */ 292 | /********************************************************/ 293 | function FileReader() { 294 | if (!(this instanceof FileReader)) 295 | throw new TypeError("Failed to construct 'FileReader': Please use the 'new' operator, this DOM object constructor cannot be called as a function.") 296 | 297 | var delegate = document.createDocumentFragment() 298 | this.addEventListener = delegate.addEventListener 299 | this.dispatchEvent = function(evt) { 300 | var local = this['on' + evt.type] 301 | if (typeof local === 'function') local(evt) 302 | delegate.dispatchEvent(evt) 303 | } 304 | this.removeEventListener = delegate.removeEventListener 305 | } 306 | 307 | function _read(fr, blob, kind) { 308 | if (!(blob instanceof Blob)) 309 | throw new TypeError("Failed to execute '" + kind + "' on 'FileReader': parameter 1 is not of type 'Blob'.") 310 | 311 | fr.result = '' 312 | 313 | setTimeout(function(){ 314 | this.readyState = FileReader.LOADING 315 | fr.dispatchEvent(new Event('load')) 316 | fr.dispatchEvent(new Event('loadend')) 317 | }) 318 | } 319 | 320 | FileReader.EMPTY = 0 321 | FileReader.LOADING = 1 322 | FileReader.DONE = 2 323 | FileReader.prototype.error = null 324 | FileReader.prototype.onabort = null 325 | FileReader.prototype.onerror = null 326 | FileReader.prototype.onload = null 327 | FileReader.prototype.onloadend = null 328 | FileReader.prototype.onloadstart = null 329 | FileReader.prototype.onprogress = null 330 | 331 | FileReader.prototype.readAsDataURL = function(blob) { 332 | _read(this, blob, 'readAsDataURL') 333 | this.result = 'data:' + blob.type + ';base64,' + encodeByteArray(blob._buffer) 334 | } 335 | 336 | FileReader.prototype.readAsText = function(blob) { 337 | _read(this, blob, 'readAsText') 338 | this.result = fromUtf8Array(blob._buffer) 339 | } 340 | 341 | FileReader.prototype.readAsArrayBuffer = function(blob) { 342 | _read(this, blob, 'readAsText') 343 | this.result = blob._buffer.slice() 344 | } 345 | 346 | FileReader.prototype.abort = function() {} 347 | 348 | 349 | /********************************************************/ 350 | /* URL */ 351 | /********************************************************/ 352 | URL.createObjectURL = function(blob) { 353 | return blob instanceof Blob 354 | ? 'data:' + blob.type + ';base64,' + encodeByteArray(blob._buffer) 355 | : createObjectURL.call(URL, blob) 356 | } 357 | 358 | URL.revokeObjectURL = function(url) { 359 | revokeObjectURL && revokeObjectURL.call(URL, url) 360 | } 361 | 362 | /********************************************************/ 363 | /* XHR */ 364 | /********************************************************/ 365 | var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send 366 | if (_send) { 367 | XMLHttpRequest.prototype.send = function(data) { 368 | if (data instanceof Blob) { 369 | this.setRequestHeader('Content-Type', data.type) 370 | _send.call(this, fromUtf8Array(data._buffer)) 371 | } else { 372 | _send.call(this, data) 373 | } 374 | } 375 | } 376 | 377 | global.FileReader = FileReader 378 | global.File = File 379 | global.Blob = Blob 380 | } 381 | 382 | if (strTag) { 383 | File.prototype[strTag] = 'File' 384 | Blob.prototype[strTag] = 'Blob' 385 | FileReader.prototype[strTag] = 'FileReader' 386 | } 387 | 388 | function fixFileAndXHR() { 389 | var isIE = !!global.ActiveXObject || ( 390 | '-ms-scroll-limit' in document.documentElement.style && 391 | '-ms-ime-align' in document.documentElement.style 392 | ) 393 | 394 | // Monkey patched 395 | // IE don't set Content-Type header on XHR whose body is a typed Blob 396 | // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6047383 397 | var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send 398 | if (isIE && _send) { 399 | XMLHttpRequest.prototype.send = function(data) { 400 | if (data instanceof Blob) { 401 | this.setRequestHeader('Content-Type', data.type) 402 | _send.call(this, data) 403 | } else { 404 | _send.call(this, data) 405 | } 406 | } 407 | } 408 | 409 | try { 410 | new File([], '') 411 | } catch(e) { 412 | try { 413 | var klass = new Function('class File extends Blob {' + 414 | 'constructor(chunks, name, opts) {' + 415 | 'opts = opts || {};' + 416 | 'super(chunks, opts || {});' + 417 | 'this.name = name;' + 418 | 'this.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date;' + 419 | 'this.lastModified = +this.lastModifiedDate;' + 420 | '}};' + 421 | 'return new File([], ""), File' 422 | )() 423 | global.File = klass 424 | } catch(e) { 425 | var klass = function(b, d, c) { 426 | var blob = new Blob(b, c) 427 | var t = c && void 0 !== c.lastModified ? new Date(c.lastModified) : new Date 428 | 429 | blob.name = d 430 | blob.lastModifiedDate = t 431 | blob.lastModified = +t 432 | blob.toString = function() { 433 | return '[object File]' 434 | } 435 | 436 | if (strTag) 437 | blob[strTag] = 'File' 438 | 439 | return blob 440 | } 441 | global.File = klass 442 | } 443 | } 444 | } 445 | 446 | if (blobSupported) { 447 | fixFileAndXHR() 448 | global.Blob = blobSupportsArrayBufferView ? global.Blob : BlobConstructor 449 | } else if (blobBuilderSupported) { 450 | fixFileAndXHR() 451 | global.Blob = BlobBuilderConstructor; 452 | } else { 453 | FakeBlobBuilder() 454 | } 455 | 456 | })(); -------------------------------------------------------------------------------- /js/options.js: -------------------------------------------------------------------------------- 1 | let currentLeague = null; 2 | let leagueDBNames = null; 3 | let leagueNameMap = null; 4 | const container = document.getElementById('container-body'); 5 | const leagueSelector = document.getElementById('league-selector'); 6 | const categories = document.getElementsByClassName('category'); 7 | const leagueDatabase = {}; 8 | leagueDatabase.webdb = {}; 9 | leagueDatabase.webdb.db = null; 10 | const saveButton = document.getElementById('save'); 11 | const resetButton = document.getElementById('reset'); 12 | const scriptButton = document.getElementById('script'); 13 | const customScript = document.getElementById('custom-script'); 14 | const QBG = document.getElementById('QBG'); 15 | const QBGName = document.getElementById('QBG-Name'); 16 | const QBGDate = document.getElementById('QBG-Date'); 17 | const QBS= document.getElementById('QBS'); 18 | const QBSName= document.getElementById('QBS-Name'); 19 | const QBSDate= document.getElementById('QBS-Date'); 20 | const RBG = document.getElementById('RBG'); 21 | const RBGName = document.getElementById('RBG-Name'); 22 | const RBGDate = document.getElementById('RBG-Date'); 23 | const RBS = document.getElementById('RBS'); 24 | const RBSName = document.getElementById('RBS-Name'); 25 | const RBSDate = document.getElementById('RBS-Date'); 26 | const WRG = document.getElementById('WRG'); 27 | const WRGName = document.getElementById('WRG-Name'); 28 | const WRGDate = document.getElementById('WRG-Date'); 29 | const WRS = document.getElementById('WRS'); 30 | const WRSName = document.getElementById('WRS-Name'); 31 | const WRSDate = document.getElementById('WRS-Date'); 32 | const TEG = document.getElementById('TEG'); 33 | const TEGName = document.getElementById('TEG-Name'); 34 | const TEGDate = document.getElementById('TEG-Date'); 35 | const TES = document.getElementById('TES'); 36 | const TESName = document.getElementById('TES-Name'); 37 | const TESDate = document.getElementById('TES-Date'); 38 | const DSTG = document.getElementById('DSTG'); 39 | const DSTGName = document.getElementById('DSTG-Name'); 40 | const DSTGDate = document.getElementById('DSTG-Date'); 41 | const DSTS = document.getElementById('DSTS'); 42 | const DSTSName = document.getElementById('DSTS-Name'); 43 | const DSTSDate = document.getElementById('DSTS-Date'); 44 | const KG = document.getElementById('KG'); 45 | const KGName = document.getElementById('KG-Name'); 46 | const KGDate = document.getElementById('KG-Date'); 47 | const KS = document.getElementById('KS'); 48 | const KSName = document.getElementById('KS-Name'); 49 | const KSDate = document.getElementById('KS-Date'); 50 | 51 | const showLoserCheckbox = document.getElementById('losers-show'); 52 | const show3rdPlaceCheckbox = document.getElementById('3rd-show'); 53 | const hideAverageLineCheckbox = document.getElementById('acuna-show'); 54 | const collapseIconsCheckbox = document.getElementById('collapse-icons'); 55 | const averageLineNameInput = document.getElementById('acuna-name'); 56 | const lastPlaceNameInput = document.getElementById('sacko-name'); 57 | const hideTeamNamesCheckbox = document.getElementById('team-name-show'); 58 | const showPicturesCheckbox = document.getElementById('pictures-show'); 59 | const playerRecordHideCheckbox = document.getElementById('player-record-hide'); 60 | 61 | const leaderBoardOptionsDiv = document.getElementById('leader-board-options'); 62 | const managerOptionsDiv = document.getElementById('manager-options'); 63 | const recordBoardOptionsDiv = document.getElementById('record-board-options'); 64 | const powerRankingOptionsDiv = document.getElementById('power-ranking-options'); 65 | 66 | const managerTable = document.getElementById('manager-override'); 67 | const managerImageTable = document.getElementById('pictures-override'); 68 | const sackoTable = document.getElementById('sacko-override'); 69 | 70 | let os = 'mac'; 71 | let numColumns = 60; 72 | 73 | const errorHandler = (transaction, error) => { 74 | alert("Error processing SQL: "+ error.message); 75 | return true; 76 | } 77 | 78 | let yearArray = []; 79 | let managerArray = []; 80 | let managerYearMap = {}; 81 | let managerImageMap = {}; 82 | let sackoMap = null; 83 | let managerMap = null; 84 | let lastSync = null; 85 | 86 | for(var h = 0; h < categories.length; h++) { 87 | ((index) => { 88 | categories[index].addEventListener("click", () => { 89 | if(categories[index].className.indexOf('inactive') > -1) { 90 | for(var j = 0; j < categories.length; j++) { 91 | categories[j].classList.remove('inactive'); 92 | categories[j].classList.add('inactive'); 93 | } 94 | categories[index].classList.remove('inactive'); 95 | } 96 | }); 97 | })(h); 98 | } 99 | 100 | show3rdPlaceCheckbox.addEventListener('change', (event) => { 101 | alert("Changing this setting will require the database be refreshed for the change to affect already recorded records."); 102 | }); 103 | 104 | leagueSelector.addEventListener('change', (event) => { 105 | currentLeague = event.target.value; 106 | updateOptionsForLeague(); 107 | }); 108 | 109 | showLoserCheckbox.addEventListener('change', (event) => { 110 | alert("Changing this setting will require the database be refreshed for the change to affect already recorded records."); 111 | }); 112 | 113 | const updateOptionsForLeague = () => { 114 | yearArray = []; managerArray = []; 115 | managerYearMap = {}; 116 | managerImageMap = {}; 117 | sackoMap = null; managerMap = null; lastSync = null; 118 | leagueDatabase.webdb.open = () => { 119 | var dbSize = 5 * 1024 * 1024; // 5MB 120 | leagueDatabase.webdb.db = openDatabase((currentLeague), "1", "League Database", dbSize); 121 | } 122 | 123 | leagueDatabase.webdb.open(); 124 | chrome.storage.sync.get([currentLeague], (response) => { 125 | const data = response[currentLeague]; 126 | lastSync = data.lastSync; 127 | QBG.value = (data.QBG) ? data.QBG.score : null; 128 | QBS.value = (data.QBS) ? data.QBS.score : null; 129 | RBG.value = (data.RBG) ? data.RBG.score : null; 130 | RBS.value = (data.RBS) ? data.RBS.score : null; 131 | WRG.value = (data.WRG) ? data.WRG.score : null; 132 | WRS.value = (data.WRS) ? data.WRS.score : null; 133 | TEG.value = (data.TEG) ? data.TEG.score : null; 134 | TES.value = (data.TES) ? data.TES.score : null; 135 | DSTG.value = (data.DSTG) ? data.DSTG.score : null; 136 | DSTS.value = (data.DSTS) ? data.DSTS.score : null; 137 | KG.value = (data.KG) ? data.KG.score : null; 138 | KS.value = (data.KS) ? data.KS.score : null; 139 | 140 | QBGName.value = (data.QBG) ? data.QBG.name : null; 141 | QBSName.value = (data.QBS) ? data.QBS.name : null; 142 | RBGName.value = (data.RBG) ? data.RBG.name : null; 143 | RBSName.value = (data.RBS) ? data.RBS.name : null; 144 | WRGName.value = (data.WRG) ? data.WRG.name : null; 145 | WRSName.value = (data.WRS) ? data.WRS.name : null; 146 | TEGName.value = (data.TEG) ? data.TEG.name : null; 147 | TESName.value = (data.TES) ? data.TES.name : null; 148 | DSTGName.value = (data.DSTG) ? data.DSTG.name : null; 149 | DSTSName.value = (data.DSTS) ? data.DSTS.name : null; 150 | KGName.value = (data.KG) ? data.KG.name : null; 151 | KSName.value = (data.KS) ? data.KS.name : null; 152 | 153 | QBGDate.value = (data.QBG) ? data.QBG.date : null; 154 | QBSDate.value = (data.QBS) ? data.QBS.date : null; 155 | RBGDate.value = (data.RBG) ? data.RBG.date : null; 156 | RBSDate.value = (data.RBS) ? data.RBS.date : null; 157 | WRGDate.value = (data.WRG) ? data.WRG.date : null; 158 | WRSDate.value = (data.WRS) ? data.WRS.date : null; 159 | TEGDate.value = (data.TEG) ? data.TEG.date : null; 160 | TESDate.value = (data.TES) ? data.TES.date : null; 161 | DSTGDate.value = (data.DSTG) ? data.DSTG.date : null; 162 | DSTSDate.value = (data.DSTS) ? data.DSTS.date : null; 163 | KGDate.value = (data.KG) ? data.KG.date : null; 164 | KSDate.value = (data.KS) ? data.KS.date : null; 165 | 166 | showLoserCheckbox.checked = (data.trackLosers) ? data.trackLosers : false; 167 | show3rdPlaceCheckbox.checked = (data.track3rdPlaceGame) ? data.track3rdPlaceGame : false; 168 | hideAverageLineCheckbox.checked = (data.hideAverageLine) ? data.hideAverageLine : false; 169 | averageLineNameInput.value = (data.averageLineName) ? data.averageLineName : null; 170 | lastPlaceNameInput.value = (data.lastPlaceName) ? data.lastPlaceName : null; 171 | hideTeamNamesCheckbox.checked = (data.hideTeamNames) ? data.hideTeamNames : false; 172 | showPicturesCheckbox.checked = (data.showTeamPictures) ? data.showTeamPictures : false; 173 | collapseIconsCheckbox.checked = (data.areIconsCollapsed) ? data.areIconsCollapsed : false; 174 | playerRecordHideCheckbox.checked = (data.hidePlayerRecords) ? data.hidePlayerRecords : false; 175 | 176 | //get options from database 177 | const query = 'SELECT DISTINCT year FROM matchups'; 178 | const query2 = 'SELECT DISTINCT manager, year FROM matchups'; 179 | leagueDatabase.webdb.db.transaction((tx) => { 180 | tx.executeSql(query, [], 181 | (tx, rs) => { 182 | for(var i = 0; i < rs.rows.length; i++) { 183 | yearArray.push(rs.rows[i].year); 184 | } 185 | tx.executeSql(query2, [], 186 | (tx, rs2) => { 187 | for(var m = 0; m < rs2.rows.length; m++) { 188 | if(managerYearMap[rs2.rows[m].year]) { 189 | managerYearMap[rs2.rows[m].year].push(rs2.rows[m].manager); 190 | } else { 191 | managerYearMap[rs2.rows[m].year] = [rs2.rows[m].manager]; 192 | } 193 | if(managerArray.indexOf(rs2.rows[m].manager) === -1){ 194 | managerArray.push(rs2.rows[m].manager); 195 | } 196 | } 197 | sackoMap = (data.sackoMap) ? data.sackoMap : {}; 198 | managerMap = (data.managerMap) ? data.managerMap : {}; 199 | managerImageMap = (data.managerImageMap) ? data.managerImageMap : {}; 200 | populateManagerAlias(managerArray); 201 | populateLastPlaceSelection(yearArray, sackoMap, managerYearMap); 202 | populateManagerImageOverride(managerArray); 203 | }, errorHandler); 204 | }, errorHandler); 205 | }); 206 | }); 207 | } 208 | 209 | chrome.runtime.getPlatformInfo(function(info) { 210 | os = info.os; 211 | chrome.storage.sync.get(['leagueDBNames','leagueNameMap'], (result) => { 212 | leagueDBNames = result.leagueDBNames; 213 | leagueNameMap = result.leagueNameMap; 214 | currentLeague = (leagueDBNames && leagueDBNames.length > 0) ? leagueDBNames[0] : null; 215 | if(currentLeague) { 216 | const options = []; 217 | for(var i = 0; i < leagueDBNames.length; i++) { 218 | let selected = (i === 0) ? 'selected' : ''; 219 | options.push(``) 220 | } 221 | leagueSelector.innerHTML = options; 222 | //leagueTitle.innerHTML = result.leagueNameMap[currentLeague]; 223 | updateOptionsForLeague(); 224 | } else { 225 | resetButton.setAttribute('disabled','disabled'); 226 | saveButton.setAttribute('disabled','disabled'); 227 | container.innerHTML = "
Upload league database before adding league options
"; 228 | } 229 | }); 230 | }); 231 | 232 | const populateManagerImageOverride = (managers) => { 233 | managerImageTable.innerHTML = ""; 234 | if(managers && managers.length > 0) { 235 | const thead = document.createElement('thead'); 236 | const tr = document.createElement('tr'); 237 | const managerHeader = document.createElement('th'); 238 | managerHeader.innerHTML = 'Manager'; 239 | const imageHeader = document.createElement('th'); 240 | imageHeader.innerHTML = 'Image URL'; 241 | const tbody = document.createElement('tbody'); 242 | managerHeader.setAttribute('style','text-align:right;'); 243 | tr.appendChild(managerHeader); 244 | tr.appendChild(imageHeader); 245 | thead.appendChild(tr); 246 | managerImageTable.appendChild(thead); 247 | managerImageTable.appendChild(tbody); 248 | managers.forEach((manager) => { 249 | const row = document.createElement('tr'); 250 | const nameCell = document.createElement('td'); 251 | nameCell.setAttribute('style','width: 33%;'); 252 | nameCell.innerHTML = manager + ":"; 253 | const pictureCell = document.createElement('td'); 254 | const pictureInput = document.createElement('input'); 255 | pictureInput.setAttribute('type','text'); 256 | pictureInput.setAttribute('style','margin-left: 5%; width: 90%;'); 257 | let managerValue = (managerImageMap[manager]) ? managerImageMap[manager] : ''; 258 | pictureInput.setAttribute('value', managerValue); 259 | pictureInput.addEventListener('change', (event) => { 260 | managerImageMap[manager] = event.target.value; 261 | }); 262 | pictureCell.appendChild(pictureInput); 263 | row.appendChild(nameCell); 264 | row.appendChild(pictureCell); 265 | managerImageTable.getElementsByTagName( 'tbody' )[0].appendChild(row); 266 | }); 267 | } else { 268 | managerImageTable.innerHTML = "
No matchup data has been uploaded to the database, please come back after having uploaded at least one weekly matchup.
"; 269 | } 270 | let tableHeight = window.getComputedStyle(managerImageTable).getPropertyValue('height'); 271 | const newHeight = parseInt(tableHeight.split('px')[0],10) + 105; 272 | powerRankingOptionsDiv.style['max-height'] = `${newHeight}px`; 273 | } 274 | 275 | const populateManagerAlias = (managers) => { 276 | managerTable.innerHTML = ""; 277 | if(managers && managers.length > 0) { 278 | const thead = document.createElement('thead'); 279 | const tr = document.createElement('tr'); 280 | const managerHeader = document.createElement('th'); 281 | managerHeader.innerHTML = 'Manager'; 282 | const aliasHeader = document.createElement('th'); 283 | aliasHeader.innerHTML = 'Alias'; 284 | const tbody = document.createElement('tbody'); 285 | managerHeader.setAttribute('style','text-align:right;'); 286 | tr.appendChild(managerHeader); 287 | tr.appendChild(aliasHeader); 288 | thead.appendChild(tr); 289 | managerTable.appendChild(thead); 290 | managerTable.appendChild(tbody); 291 | managers.forEach((manager) => { 292 | const row = document.createElement('tr'); 293 | const nameCell = document.createElement('td'); 294 | nameCell.setAttribute('style','width: 33%;'); 295 | nameCell.innerHTML = manager + ":"; 296 | const aliasCell = document.createElement('td'); 297 | const aliasInput = document.createElement('input'); 298 | aliasInput.setAttribute('type','text'); 299 | aliasInput.setAttribute('style','margin-left: 5%; width: 90%;'); 300 | let managerValue = (managerMap[manager]) ? managerMap[manager] : ''; 301 | aliasInput.setAttribute('value', managerValue); 302 | aliasInput.addEventListener('change', (event) => { 303 | managerMap[manager] = event.target.value; 304 | }); 305 | aliasCell.appendChild(aliasInput); 306 | row.appendChild(nameCell); 307 | row.appendChild(aliasCell); 308 | managerTable.getElementsByTagName( 'tbody' )[0].appendChild(row); 309 | }); 310 | } else { 311 | managerTable.innerHTML = "
No matchup data has been uploaded to the database, please come back after having uploaded at least one weekly matchup.
"; 312 | } 313 | 314 | let tableHeight = window.getComputedStyle(managerTable).getPropertyValue('height'); 315 | const newHeight = parseInt(tableHeight.split('px')[0],10) + 11; 316 | managerOptionsDiv.style['max-height'] = `${newHeight}px`; 317 | } 318 | 319 | const populateLastPlaceSelection = (years, sackoMap, owners) => { 320 | sackoTable.innerHTML = ""; 321 | if(years.length > 0) { 322 | const thead = document.createElement('thead'); 323 | const tr = document.createElement('tr'); 324 | const managerHeader = document.createElement('th'); 325 | managerHeader.innerHTML = 'Manager'; 326 | const yearHeader = document.createElement('th'); 327 | yearHeader.innerHTML = 'Year'; 328 | const tbody = document.createElement('tbody'); 329 | tr.appendChild(yearHeader); 330 | tr.appendChild(managerHeader); 331 | thead.appendChild(tr); 332 | sackoTable.appendChild(thead); 333 | sackoTable.appendChild(tbody); 334 | years.forEach((year) => { 335 | const row = document.createElement('tr'); 336 | const yearCell = document.createElement('td'); 337 | yearCell.innerHTML = year; 338 | const managerCell = document.createElement('td'); 339 | managerCell.appendChild(generateManagerDropdown(owners, sackoMap[year], year)); 340 | row.appendChild(yearCell); 341 | row.appendChild(managerCell); 342 | sackoTable.getElementsByTagName('tbody')[0].appendChild(row); 343 | }) 344 | } else { 345 | sackoTable.innerHTML = "
No matchup data has been uploaded to the database, please come back after having uploaded at least one weekly matchup.
"; 346 | } 347 | 348 | let tableHeight = window.getComputedStyle(sackoTable).getPropertyValue('height'); 349 | const newHeight = parseInt(tableHeight.split('px')[0],10) + 149; 350 | leaderBoardOptionsDiv.style['max-height'] = `${newHeight}px`; 351 | } 352 | 353 | const generateManagerDropdown = (managers, selectedManager, year) => { 354 | let selectManager = document.createElement('select'); 355 | selectManager.setAttribute('data-year', year); 356 | selectManager.addEventListener('change', (event) => { 357 | sackoMap[event.target.getAttribute('data-year')] = event.target.value; 358 | }); 359 | let noneOption = document.createElement('option'); 360 | noneOption.setAttribute('value', 'none'); 361 | noneOption.innerHTML = ''; 362 | selectManager.appendChild(noneOption); 363 | 364 | managers[year].forEach((manager) => { 365 | let managerOption = document.createElement('option'); 366 | managerOption.setAttribute('value', manager); 367 | managerOption.innerHTML = manager; 368 | if(manager === selectedManager) { 369 | managerOption.setAttribute('selected', 'selected'); 370 | } 371 | selectManager.appendChild(managerOption); 372 | }) 373 | return selectManager; 374 | } 375 | 376 | 377 | saveButton.onclick = (element) => { 378 | const savedObject = {}; 379 | savedObject[currentLeague] = {QBG: { score: QBG.value, name: QBGName.value, date: QBGDate.value }, QBS: { score: QBS.value, name: QBSName.value, date: QBSDate.value }, RBG: { score: RBG.value, name: RBGName.value, date: RBGDate.value }, RBS: { score: RBS.value, name: RBSName.value, date: RBSDate.value }, 380 | WRG: { score: WRG.value, name: WRGName.value, date: WRGDate.value }, WRS: { score: WRS.value, name: WRSName.value, date: WRSDate.value }, TEG: { score: TEG.value, name: TEGName.value, date: TEGDate.value }, TES: { score: TES.value, name: TESName.value, date: TESDate.value }, 381 | DSTG: { score: DSTG.value, name: DSTGName.value, date: DSTGDate.value }, DSTS: { score: DSTS.value, name: DSTSName.value, date: DSTSDate.value }, KG: { score: KG.value, name: KGName.value, date: KGDate.value }, KS: { score: KS.value, name: KSName.value, date: KSDate.value }, 382 | lastSync: lastSync, sackoMap: sackoMap, managerMap: managerMap, managerImageMap: managerImageMap, lastPlaceName: lastPlaceNameInput.value, averageLineName: averageLineNameInput.value, 383 | hideAverageLine: hideAverageLineCheckbox.checked, track3rdPlaceGame: show3rdPlaceCheckbox.checked, trackLosers: showLoserCheckbox.checked, hideTeamNames: hideTeamNamesCheckbox.checked, showTeamPictures: showPicturesCheckbox.checked, areIconsCollapsed: collapseIconsCheckbox.checked, hidePlayerRecords: playerRecordHideCheckbox.checked }; 384 | chrome.storage.sync.set(savedObject, () => { 385 | alert("saved"); 386 | }); 387 | }; 388 | 389 | resetButton.onclick = (element) => { 390 | updateOptionsForLeague(); 391 | } 392 | 393 | scriptButton.onclick = (element) => { 394 | if(confirm('Are you sure you want to run this script?')) { 395 | leagueDatabase.webdb.db.transaction((tx) => { 396 | tx.executeSql(customScript.value, [], 397 | (tx, rs) => { 398 | customScript.value = ""; 399 | alert('Success'); 400 | }); 401 | }, errorHandler); 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /js/util.js: -------------------------------------------------------------------------------- 1 | const errorHandler = (transaction, error) => { 2 | alert("Error processing SQL: "+ error.message); 3 | return true; 4 | } 5 | 6 | const getLeagueSettings = (yearList, leagueId) => { 7 | const leagueSettingsMap = {}; 8 | yearList.forEach((year) => { 9 | var xhr = new XMLHttpRequest(); 10 | if(year === currentYear) { 11 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/seasons/${year}/segments/0/leagues/${leagueId}?view=mLiveScoring&view=mMatchupScore&view=mRoster&view=mSettings&view=mStandings&view=mStatus&view=mTeam&view=modular&view=mNav`, false); 12 | } else if(year < 2018) { 13 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/leagueHistory/${leagueId}?view=mLiveScoring&view=mMatchupScore&view=mRoster&view=mSettings&view=mStandings&view=mStatus&view=mTeam&view=modular&view=mNav&seasonId=${year}`, false); 14 | } else { 15 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/seasons/${year}/segments/0/leagues/${leagueId}?view=mSettings&view=mTeam&view=modular&view=mNav`, false); 16 | } 17 | 18 | 19 | xhr.send(); 20 | leagueSettingsMap[year] = (year < 2018) ? JSON.parse(xhr.responseText)[0].settings : JSON.parse(xhr.responseText).settings; 21 | }); 22 | return leagueSettingsMap; 23 | } 24 | 25 | const getPowerRankingWeekNameDisplay = (currentWeek, finalRegSeasonGame, champGame) => { 26 | if(currentWeek == 1) { 27 | return 'Preseason'; 28 | } else if(currentWeek <= (finalRegSeasonGame + 1)) { 29 | return 'Week ' + (currentWeek - 1); 30 | } else { 31 | var title = ''; 32 | switch(champGame - currentWeek) { 33 | case 3: title = 'Round of 32'; break; 34 | case 2: title = 'Round of 16'; break; 35 | case 1: title = 'Quarterfinals'; break; 36 | case 0: title = 'Semifinal'; break; 37 | default: title = 'Championship'; 38 | } 39 | return title; 40 | } 41 | } 42 | 43 | /* 44 | * Retrieves the sacko holder for any given year 45 | */ 46 | const getSacko = (db, year, leagueSettings, callback) => { 47 | let query = "SELECT manager, year, week, winLoss, score FROM matchups WHERE year = ? AND week <= ? ORDER BY manager ASC, year ASC, week ASC"; 48 | var db = leagueDatabase.webdb.db; 49 | // get sacko overrides from local storage 50 | chrome.storage.sync.get(['league-' + leagueId], (response) => { 51 | console.log(response) 52 | const leagueDict = response['league-' + leagueId]; 53 | const sackoMap = (leagueDict.sackoMap) ? leagueDict.sackoMap : {}; 54 | if(sackoMap[year]) { 55 | callback({ 56 | 'manager': sackoMap[year], 57 | 'count': 0, 58 | 'score': 0, 59 | 'gamesPlayed': 17 60 | }); 61 | } else { 62 | db.transaction((tx) => { 63 | tx.executeSql(query, [year, leagueSettings.scheduleSettings.matchupPeriodCount], 64 | (tx, rs) => { 65 | const ownerMap = {}; 66 | for(var i = 0; i < rs.rows.length; i++) { 67 | let row = rs.rows[i]; 68 | let count = 0; // loss 69 | if(row.winLoss === 1) { 70 | // win 71 | count += 1; 72 | } else if(row.winLoss === 3) { 73 | // draw 74 | count += .5; 75 | } 76 | if(row.manager in ownerMap) { 77 | ownerMap[row.manager] = { 78 | 'manager': row.manager, 79 | 'count': ownerMap[row.manager].count + count, 80 | 'score': ownerMap[row.manager].score + row.score, 81 | 'gamesPlayed': ownerMap[row.manager].gamesPlayed + 1 82 | } 83 | } else { 84 | ownerMap[row.manager] = { 85 | 'manager': row.manager, 86 | 'count': count, 87 | 'score': row.score, 88 | 'gamesPlayed': 1 89 | } 90 | } 91 | } 92 | let sacko = { 93 | 'count': 9999, 94 | 'score': 9999, 95 | 'gamesPlayed': 13, 96 | 'manager': 'Sacko' 97 | } 98 | for (var owner in ownerMap) { 99 | if (ownerMap.hasOwnProperty(owner)) { 100 | if(ownerMap[owner].gamesPlayed === leagueSettings.scheduleSettings.matchupPeriodCount) { 101 | if(ownerMap[owner].count < sacko.count) { 102 | sacko = ownerMap[owner]; 103 | } else if (ownerMap[owner].count === sacko.count) { 104 | if(ownerMap[owner].score < sacko.score) { 105 | sacko = ownerMap[owner]; 106 | } 107 | } 108 | } 109 | } 110 | } 111 | callback(sacko); 112 | }, 113 | errorHandler); 114 | }); 115 | } 116 | }); 117 | } 118 | 119 | /* 120 | * Return League Champion for Given Year 121 | */ 122 | const getChampion = (db, year, leagueSettings, callback) => { 123 | let query = 'SELECT manager, year, week, winLoss, score, isThirdPlaceGame, isChampionship FROM matchups WHERE year = ? AND week = ? AND winLoss = 1 AND isThirdPlaceGame = "false" AND isChampionship = "true" ORDER BY manager ASC, year ASC, week ASC'; 124 | db.transaction((tx) => { 125 | console.log(leagueSettings); 126 | let matchupPeriods = Object.keys(leagueSettings.scheduleSettings.matchupPeriods); 127 | console.log(matchupPeriods, leagueSettings) 128 | tx.executeSql(query, [year, matchupPeriods[matchupPeriods.length - 1]], 129 | (tx, rs) => { 130 | if(rs.rows.length === 0) { 131 | callback(null); 132 | } else { 133 | callback(rs.rows[0]); 134 | } 135 | }, errorHandler) 136 | }) 137 | } 138 | 139 | /* 140 | * 141 | */ 142 | const didAppearInPlayoffs = (db, manager, year, leagueSettings, callback) => { 143 | let query = 'SELECT DISTINCT manager, year, isLosersBacketGame FROM matchups WHERE manager = ? AND year = ? AND week > ? AND isLosersBacketGame = "false" ORDER BY year ASC, week ASC'; 144 | db.transaction((tx) => { 145 | tx.executeSql(query, [manager, year, leagueSettings.scheduleSettings.matchupPeriodCount], 146 | (tx, rs) => { 147 | callback((rs.rows.length > 0)); 148 | }, errorHandler) 149 | }) 150 | } 151 | 152 | /* 153 | * Return the Points, ppg, and point differential for a given manager 154 | */ 155 | const getPoints = (db, manager, callback) => { 156 | let query = 'SELECT sum(score) AS scoreSum, sum(pointDiff) As pointDiffSum, count(score) as numGame FROM matchups WHERE manager = ?'; 157 | db.transaction((tx) => { 158 | tx.executeSql(query, [manager], 159 | (tx, rs) => { 160 | let ppg = 0; 161 | if(rs.rows[0].numGame > 0) { 162 | ppg = (rs.rows[0].scoreSum / rs.rows[0].numGame); 163 | } 164 | callback({ 165 | total: rs.rows[0].scoreSum, 166 | pointDiff: rs.rows[0].pointDiffSum, 167 | pointsPer: ppg 168 | }); 169 | }, errorHandler) 170 | }) 171 | } 172 | 173 | const getManagers = (db, callback) => { 174 | let query = 'SELECT DISTINCT manager FROM matchups'; 175 | db.transaction((tx) => { 176 | tx.executeSql(query, [], 177 | (tx, rs) => { 178 | const managerList = []; 179 | for(var i=0; i { 188 | let query = 'SELECT DISTINCT manager, year FROM matchups WHERE year = ?'; 189 | db.transaction((tx) => { 190 | tx.executeSql(query, [year], 191 | (tx, rs) => { 192 | const managerList = []; 193 | for(var i=0; i { 205 | let query = 'SELECT manager, year, week, winLoss FROM matchups WHERE manager = ? ORDER BY year ASC, week ASC'; 206 | let wins = 0, losses = 0, ties = 0; 207 | db.transaction((tx) => { 208 | tx.executeSql(query, [manager], 209 | (tx, rs) => { 210 | for(var i=0; i { 242 | const records = []; 243 | managerList.forEach((manager, index) => { 244 | getRecord(database, manager, (record) => { 245 | records.push(record); 246 | if(index === managerList.length - 1) { 247 | records.sort((a,b) => { 248 | if(a.winPercentage == b.winPercentage) 249 | return 0; 250 | if(a.winPercentage > b.winPercentage) 251 | return -1; 252 | if(a.winPercentage < b.winPercentage) 253 | return 1; 254 | }) 255 | callback(records); 256 | } 257 | }); 258 | }); 259 | } 260 | 261 | const getSackos = (database, yearsActive, leagueSettingsMap, callback) => { 262 | const sackos = []; 263 | yearsActive.forEach((year, index) => { 264 | getSacko(database, year, leagueSettingsMap[year], (sacko) => { 265 | sackos.push(sacko); 266 | if(index === yearsActive.length - 1) { 267 | callback(sackos); 268 | } 269 | }); 270 | }); 271 | } 272 | 273 | const getChampionships = (database, yearsActive, leagueSettingsMap, callback) => { 274 | const champions = []; 275 | yearsActive.forEach((year, index) => { 276 | getChampion(database, year, leagueSettingsMap[year], (champ) => { 277 | champions.push(champ); 278 | if(index === yearsActive.length - 1) { 279 | callback(champions); 280 | } 281 | }); 282 | }); 283 | } 284 | 285 | const getAllManagersPlayoffAppearences = (database, managerList, yearsActive, leagueSettingsMap, callback) => { 286 | const playoffApps = {}; 287 | managerList.forEach((manager, index) => { 288 | playoffApps[manager] = 0; 289 | yearsActive.forEach((year, yearIndex) => { 290 | didAppearInPlayoffs(database, manager, year, leagueSettingsMap[year], (yearPlayoffCount) => { // 1 or 0 291 | playoffApps[manager] = playoffApps[manager] + yearPlayoffCount; 292 | if(index === managerList.length - 1 && yearIndex === yearsActive.length - 1) { 293 | callback(playoffApps); 294 | } 295 | }); 296 | }); 297 | }); 298 | } 299 | 300 | const getAllManagersPoints = (database, managerList, callback) => { 301 | const pointsScored = {}; 302 | managerList.forEach((manager, index) => { 303 | getPoints(database, manager, (pointsObject) => { 304 | pointsScored[manager] = pointsObject; 305 | if(index === managerList.length - 1) { 306 | callback(pointsScored); 307 | } 308 | }); 309 | }); 310 | } 311 | 312 | const yearRangeGenerator = (start, end) => { 313 | return [...Array(1+end-start).keys()].map(v => start+v) 314 | } 315 | 316 | const getSackoNumber = (manager, sackos) => { 317 | let numSackos = 0; 318 | sackos.forEach((sacko) => { 319 | if(sacko.manager === manager) { numSackos++; } 320 | }); 321 | return numSackos; 322 | } 323 | 324 | const getChampionshipNumber = (manager, championships) => { 325 | let numChamps = 0; 326 | championships.forEach((champ) => { 327 | if(champ && champ.manager === manager) { numChamps++; } 328 | }); 329 | return numChamps; 330 | } 331 | 332 | const mergeDataIntoRecords = (records, sackos, champions, playoffApps, points) => { 333 | records.forEach((record) => { 334 | let manager = record.manager; 335 | record['sackos'] = getSackoNumber(manager, sackos); 336 | record['championships'] = getChampionshipNumber(manager, champions); 337 | record['playoffApps'] = playoffApps[manager]; 338 | record['pointsScored'] = points[manager].total; 339 | record['pointsPer'] = points[manager].pointsPer; 340 | record['pointDiff'] = points[manager].pointDiff; 341 | }); 342 | return records; 343 | } 344 | 345 | const getAllTimeLeaderBoard = (recordArray, leagueSettings, callback) => { 346 | // get sacko overrides from local storage 347 | chrome.storage.sync.get(['league-' + leagueId], (response) => { 348 | const leagueDict = response['league-' + leagueId]; 349 | let thresholdReached = leagueDict.hideAverageLine; 350 | const lastPlaceName = (leagueDict.lastPlaceName) ? leagueDict.lastPlaceName : 'Sackos'; 351 | let resultString = "
"; 352 | resultString = resultString + ""; 353 | resultString = resultString + ""; 354 | resultString = resultString + ``; 355 | for(var i = 0; i < recordArray.length; i++){ 356 | let trophyString = '', sackoString = ''; 357 | let managerName = (leagueDict.managerMap && leagueDict.managerMap[recordArray[i].manager]) ? leagueDict.managerMap[recordArray[i].manager] : recordArray[i].manager; 358 | let pointDiff = (recordArray[i].pointDiff > 0) ? "+" + recordArray[i].pointDiff.toFixed(1) : recordArray[i].pointDiff.toFixed(1); 359 | if(recordArray[i].sackos > 0) { 360 | if(leagueDict.areIconsCollapsed) { 361 | if (recordArray[i].sackos === 1) { 362 | sackoString = sackoString + '' 363 | } else { 364 | sackoString = sackoString + ''+'x'+(recordArray[i].sackos)+""; 365 | } 366 | } else { 367 | for(var s = 0; s < recordArray[i].sackos; s++) { 368 | sackoString = sackoString + ''; 369 | } 370 | } 371 | 372 | } 373 | if(recordArray[i].championships > 0) { 374 | if(leagueDict.areIconsCollapsed) { 375 | if (recordArray[i].championships === 1) { 376 | trophyString = trophyString + ''; 377 | } else { 378 | trophyString = trophyString + ''+'x'+recordArray[i].championships+""; 379 | } 380 | } else { 381 | for(var c = 0; c < recordArray[i].championships; c++) { 382 | trophyString = trophyString + ''; 383 | } 384 | } 385 | 386 | } 387 | 388 | if(recordArray[i].winPercentage < 50 && !thresholdReached) { 389 | const lineName = (leagueDict.averageLineName) ? leagueDict.averageLineName : 'The Threshold of Mediocrity'; 390 | resultString = resultString + ``; 391 | thresholdReached = true; 392 | } 393 | resultString = resultString + "" 394 | } 395 | resultString = resultString + "

All Time Leader Board

HolderWinsLossesTiesWin %PointsPPGDiffTitles${lastPlaceName}Pl App
${lineName}
" + (i + 1) + ". " + managerName + "" + recordArray[i].wins + "" + recordArray[i].losses + "" + recordArray[i].ties + "" + recordArray[i].winPercentage.toFixed(1) + "%" + recordArray[i].pointsScored.toFixed(1) + "" + recordArray[i].pointsPer.toFixed(1) + "" + pointDiff + "" + trophyString + "" + sackoString + "" + recordArray[i].playoffApps + "
"; 396 | callback(resultString); 397 | }); 398 | } 399 | -------------------------------------------------------------------------------- /js/recordBook.js: -------------------------------------------------------------------------------- 1 | const getMostPointsGame = (db, callback) => { 2 | let query = "SELECT manager, score, year, week, vs FROM matchups ORDER BY score DESC LIMIT 1" 3 | db.transaction((tx) => { 4 | tx.executeSql(query, [], 5 | (tx, rs) => { 6 | if(rs.rows.length === 0) { 7 | callback(null); 8 | } else { 9 | callback(rs.rows[0]); 10 | } 11 | }); 12 | }); 13 | } 14 | 15 | const getMostPointsSeason = (db, leagueSettings, callback) => { 16 | let query = "SELECT manager, score, year, week FROM matchups" 17 | console.log(leagueSettings) 18 | db.transaction((tx) => { 19 | tx.executeSql(query, [], 20 | (tx, rs) => { 21 | if(rs.rows.length === 0) { 22 | callback(null); 23 | } else { 24 | var scoreMap = new Map(); 25 | for(var i=0; i { 52 | let query = "SELECT manager, matchupTotal, score, year, week, vs, winLoss, isHomeGame FROM matchups ORDER BY matchupTotal DESC LIMIT 1" 53 | db.transaction((tx) => { 54 | tx.executeSql(query, [], 55 | (tx, rs) => { 56 | if(rs.rows.length === 0) { 57 | callback(null); 58 | } else { 59 | callback(rs.rows[0]); 60 | } 61 | }); 62 | }); 63 | } 64 | 65 | const getFewestPointsGame = (db, callback) => { 66 | let query = "SELECT manager, score, year, week, vs FROM matchups ORDER BY score ASC LIMIT 1" 67 | db.transaction((tx) => { 68 | tx.executeSql(query, [], 69 | (tx, rs) => { 70 | if(rs.rows.length === 0) { 71 | callback(null); 72 | } else { 73 | callback(rs.rows[0]); 74 | } 75 | }); 76 | }); 77 | } 78 | 79 | const getFewestPointsSeason = (db, leagueSettings, callback) => { 80 | let query = "SELECT manager, score, year, week FROM matchups" 81 | db.transaction((tx) => { 82 | tx.executeSql(query, [], 83 | (tx, rs) => { 84 | if(rs.rows.length === 0) { 85 | callback(null); 86 | } else { 87 | var scoreMap = new Map(); 88 | for(var i=0; i parseFloat(v.score)){ 107 | minEntry.score = parseFloat(v.score); 108 | minEntry.manager = k.split("++-++")[0]; 109 | minEntry.year = tempYear; 110 | } 111 | } 112 | } 113 | if(hasCompletedSeason) callback(minEntry); 114 | else callback({ 115 | score: 'N/A', 116 | numGames: 'N/A', 117 | manager: 'N/A', 118 | year: 'N/A' 119 | }); 120 | } 121 | }); 122 | }); 123 | } 124 | 125 | const getFewestPointsMatchup = (db, callback) => { 126 | let query = "SELECT manager, matchupTotal, score, year, week, vs, winLoss, isHomeGame FROM matchups ORDER BY matchupTotal ASC LIMIT 1" 127 | db.transaction((tx) => { 128 | tx.executeSql(query, [], 129 | (tx, rs) => { 130 | if(rs.rows.length === 0) { 131 | callback(null); 132 | } else { 133 | callback(rs.rows[0]); 134 | } 135 | }); 136 | }); 137 | } 138 | 139 | const getMostPointsAllowedSeason = (db, leagueSettings, callback) => { 140 | let query = "SELECT vs, score, year, week FROM matchups" 141 | db.transaction((tx) => { 142 | tx.executeSql(query, [], 143 | (tx, rs) => { 144 | if(rs.rows.length === 0) { 145 | callback(null); 146 | } else { 147 | var scoreMap = new Map(); 148 | for(var i=0; i { 177 | let query = "SELECT vs, score, year, week FROM matchups" 178 | db.transaction((tx) => { 179 | tx.executeSql(query, [], 180 | (tx, rs) => { 181 | if(rs.rows.length === 0) { 182 | callback(null); 183 | } else { 184 | var scoreMap = new Map(); 185 | for(var i=0; i { 226 | let query = "SELECT manager, matchupTotal, score, year, week, vs, winLoss, pointDiff FROM matchups WHERE winLoss = 1 AND pointDiff <> 0 ORDER BY pointDiff ASC LIMIT 1"; 227 | db.transaction((tx) => { 228 | tx.executeSql(query, [], 229 | (tx, rs) => { 230 | if(rs.rows.length === 0) { 231 | callback(null); 232 | } else { 233 | callback(rs.rows[0]); 234 | } 235 | }); 236 | }); 237 | } 238 | 239 | const getBiggestBlowoutMatchup = (db, callback) => { 240 | let query = "SELECT manager, matchupTotal, score, year, week, vs, winLoss, pointDiff FROM matchups WHERE winLoss = 1 ORDER BY pointDiff DESC LIMIT 1"; 241 | db.transaction((tx) => { 242 | tx.executeSql(query, [], 243 | (tx, rs) => { 244 | if(rs.rows.length === 0) { 245 | callback(null); 246 | } else { 247 | callback(rs.rows[0]); 248 | } 249 | }); 250 | }); 251 | } 252 | 253 | 254 | const getLongestWinStreak = (db, callback) => { 255 | let query = "SELECT manager, year, week, winLoss FROM matchups ORDER BY manager ASC, year ASC, week ASC"; 256 | db.transaction((tx) => { 257 | tx.executeSql(query, [], 258 | (tx, rs) => { 259 | if(rs.rows.length === 0) { 260 | callback(null); 261 | } else { 262 | var topWinStreak = []; 263 | var currWinStreak = []; 264 | var allWinStreaks = { manager: "", years: "", games: [] }; 265 | var isWinning = -1; 266 | var currManager = rs.rows[0].manager; 267 | for(var i=0; i topWinStreak.length){ 292 | topWinStreak = currWinStreak; 293 | allWinStreaks.games = topWinStreak; 294 | } 295 | } 296 | allWinStreaks.years = allWinStreaks.games[0].year + "-" + allWinStreaks.games[allWinStreaks.games.length-1].year; 297 | allWinStreaks.manager = allWinStreaks.games[0].manager; 298 | callback(allWinStreaks); 299 | } 300 | }); 301 | }); 302 | } 303 | 304 | const getLongestLosingStreak = (db, callback) => { 305 | let query = "SELECT manager, year, week, winLoss FROM matchups ORDER BY manager ASC, year ASC, week ASC"; 306 | db.transaction((tx) => { 307 | tx.executeSql(query, [], 308 | (tx, rs) => { 309 | if(rs.rows.length === 0) { 310 | callback(null); 311 | } else { 312 | var topLoseStreak = []; 313 | var currLoseStreak = []; 314 | var allLoseStreaks = { manager: "", years: "", games: [] }; 315 | var isLosing = -1; 316 | var currManager = rs.rows[0].manager; 317 | for(var i=0; i topLoseStreak.length){ 342 | topLoseStreak = currLoseStreak; 343 | allLoseStreaks.games = topLoseStreak; 344 | } 345 | } 346 | allLoseStreaks.years = allLoseStreaks.games[0].year + "-" + allLoseStreaks.games[allLoseStreaks.games.length-1].year; 347 | allLoseStreaks.manager = allLoseStreaks.games[0].manager; 348 | callback(allLoseStreaks); 349 | } 350 | }); 351 | }); 352 | } 353 | 354 | const mapPositionAndTypeToKey = (position, type) => { 355 | let returnString = positionsByESPNValue[position]; 356 | if (type === "game") { 357 | returnString += "G"; 358 | } else { 359 | returnString += "S"; 360 | } 361 | return returnString; 362 | } 363 | 364 | const getMostPointsPlayerGame = (db, playerPosition, leagueDict, callback) => { 365 | let query = "SELECT player, playerPosition, score, year, week, manager FROM history WHERE playerPosition='"+positionsByESPNValue[playerPosition]+"' AND score<>'undefined' ORDER BY score DESC LIMIT 1"; 366 | db.transaction((tx) => { 367 | tx.executeSql(query, [], 368 | (tx, rs) => { 369 | if(rs.rows.length === 0) { 370 | callback(null); 371 | } else { 372 | let localStorageRecord = leagueDict[mapPositionAndTypeToKey(playerPosition, 'game').replace('\/','')]; 373 | if(localStorageRecord && parseFloat(localStorageRecord.score) > rs.rows[0].score) { 374 | callback({ 375 | score: localStorageRecord.score, 376 | player: localStorageRecord.name, 377 | year: localStorageRecord.date, 378 | }); 379 | } else { 380 | callback(rs.rows[0]); 381 | } 382 | } 383 | }); 384 | }); 385 | } 386 | 387 | const getMostPointsPlayerSeason = (db, playerPosition, leagueSettings, leagueDict, callback) => { 388 | let query = "SELECT player, playerPosition, score, year, week, manager FROM history WHERE playerPosition='"+positionsByESPNValue[playerPosition]+"' AND score<>'undefined' ORDER BY score DESC"; 389 | db.transaction((tx) => { 390 | tx.executeSql(query, [], 391 | (tx, rs) => { 392 | if(rs.rows.length === 0) { 393 | callback(null); 394 | } else { 395 | var scoreMap = new Map(); 396 | for(var i=0; i maxEntry.score) { 418 | callback({ 419 | score: localStorageRecord.score, 420 | player: localStorageRecord.name, 421 | year: localStorageRecord.date, 422 | }); 423 | } else { 424 | callback(maxEntry); 425 | } 426 | } 427 | }); 428 | }); 429 | } 430 | 431 | const mergeDataIntoRecordBook = (db, playerPositions, leagueSettings, leagueLocalStorage, callback) => { 432 | var records = {}; 433 | chrome.storage.sync.get(['league-' + leagueId], (response) => { 434 | const leagueDict = (response['league-' + leagueId]) ? response['league-' + leagueId] : {}; 435 | getMostPointsGame(db, function(resultMPG){ 436 | records["mostPointsGame"] = resultMPG; 437 | getMostPointsSeason(db, leagueSettings, function(resultMPS){ 438 | records["mostPointsSeason"] = resultMPS; 439 | getMostPointsMatchup(db, function(resultMPM){ 440 | records["mostPointsMatchup"] = resultMPM; 441 | getFewestPointsGame(db, function(resultFPG){ 442 | records["fewestPointsGame"] = resultFPG; 443 | getFewestPointsSeason(db, leagueSettings, function(resultFPS){ 444 | records["fewestPointsSeason"] = resultFPS; 445 | getFewestPointsMatchup(db, function(resultFPM){ 446 | records["fewestPointsMatchup"] = resultFPM; 447 | getSmallestMarginMatchup(db, function(resultSMM){ 448 | records["smallestMarginMatchup"] = resultSMM; 449 | getBiggestBlowoutMatchup(db, function(resultBBM){ 450 | records["biggestBlowoutMatchup"] = resultBBM; 451 | getMostPointsAllowedSeason(db, leagueSettings, function(resultMPAS){ 452 | records["mostPointsAllowedSeason"] = resultMPAS; 453 | getFewestPointsAllowedSeason(db, leagueSettings, function(resultFPAS){ 454 | records["fewestPointsAllowedSeason"] = resultFPAS; 455 | getLongestWinStreak(db, function(resultLWS){ 456 | records["winStreak"] = resultLWS; 457 | getLongestLosingStreak(db, function(resultLLS){ 458 | records["loseStreak"] = resultLLS; 459 | Object.keys(playerPositions).forEach(pos => { 460 | getMostPointsPlayerGame(db, playerPositions[pos], leagueDict, function(resultMPPG){ 461 | records["mostPointsPlayerGame-"+pos] = resultMPPG; 462 | }); 463 | getMostPointsPlayerSeason(db, playerPositions[pos], leagueSettings, leagueDict, function(resultMPPS){ 464 | records["mostPointsPlayerSeason-"+pos] = resultMPPS; 465 | if (pos === "K"){ 466 | callback(generateRecordBookHTML(records, leagueDict)); 467 | } 468 | }); 469 | }); 470 | }); 471 | }); 472 | }); 473 | }); 474 | }); 475 | }); 476 | }); 477 | }); 478 | }); 479 | }); 480 | }); 481 | }); 482 | }); 483 | } 484 | 485 | const getManagerName = (manager, managerMap) => { 486 | return (managerMap && managerMap[manager]) ? managerMap[manager] : manager; 487 | } 488 | 489 | const roundScore = (score) => { 490 | console.log(score) 491 | if(score === 'N/A') return score; 492 | else if(score % 1 === 0) { 493 | return score; 494 | } else { 495 | return parseFloat(score).toFixed(2); 496 | } 497 | } 498 | 499 | const generateRecordBookHTML = (records, leagueDict) => { 500 | 501 | mostMatchupString = getManagerName(records["mostPointsMatchup"].manager, leagueDict.managerMap) + " ("+roundScore(records["mostPointsMatchup"].score)+") vs " + getManagerName(records["mostPointsMatchup"].vs, leagueDict.managerMap) +" ("+roundScore(parseFloat(records["mostPointsMatchup"].matchupTotal-records["mostPointsMatchup"].score))+")"; 502 | fewestMatchupString = getManagerName(records["fewestPointsMatchup"].manager, leagueDict.managerMap) + " ("+roundScore(records["fewestPointsMatchup"].score)+") vs " + getManagerName(records["fewestPointsMatchup"].vs, leagueDict.managerMap) +" ("+roundScore(parseFloat(records["fewestPointsMatchup"].matchupTotal-records["fewestPointsMatchup"].score))+")"; 503 | smallestMarginString = getManagerName(records["smallestMarginMatchup"].manager, leagueDict.managerMap) + " ("+roundScore(records["smallestMarginMatchup"].score)+") vs " + getManagerName(records["smallestMarginMatchup"].vs, leagueDict.managerMap) +" ("+roundScore(parseFloat(records["smallestMarginMatchup"].matchupTotal-records["smallestMarginMatchup"].score))+")"; 504 | biggestBlowoutString = getManagerName(records["biggestBlowoutMatchup"].manager, leagueDict.managerMap) + " ("+roundScore(records["biggestBlowoutMatchup"].score)+") vs " + getManagerName(records["biggestBlowoutMatchup"].vs, leagueDict.managerMap) +" ("+roundScore(parseFloat(records["biggestBlowoutMatchup"].matchupTotal-records["biggestBlowoutMatchup"].score))+")"; 505 | 506 | resultString = "
" 507 | resultString = resultString + "" 508 | resultString = resultString + "" 509 | resultString = resultString + "" 510 | resultString = resultString + "" 511 | resultString = resultString + "" 512 | resultString = resultString + "" 513 | resultString = resultString + "" 514 | resultString = resultString + "" 515 | resultString = resultString + "" 516 | resultString = resultString + "" 517 | resultString = resultString + "" 518 | resultString = resultString + "" 519 | resultString = resultString + "" 520 | resultString = resultString + "" 521 | resultString = resultString + "" 522 | resultString = resultString + "" 523 | resultString = resultString + ""; 524 | console.log(leagueDict, !leagueDict.hidePlayerRecords); 525 | if(leagueDict && !leagueDict.hidePlayerRecords) { 526 | console.log("here", records); 527 | if(records["mostPointsPlayerGame-QB"]) resultString = resultString + "" 528 | if(records["mostPointsPlayerSeason-QB"]) resultString = resultString + "" 529 | if(records["mostPointsPlayerGame-RB"]) resultString = resultString + "" 530 | if(records["mostPointsPlayerSeason-RB"]) resultString = resultString + "" 531 | if(records["mostPointsPlayerGame-WR"]) resultString = resultString + "" 532 | if(records["mostPointsPlayerSeason-WR"]) resultString = resultString + "" 533 | if(records["mostPointsPlayerGame-TE"]) resultString = resultString + "" 534 | if(records["mostPointsPlayerSeason-TE"]) resultString = resultString + "" 535 | if(records["mostPointsPlayerGame-DST"]) resultString = resultString + "" 536 | if(records["mostPointsPlayerSeason-DST"]) resultString = resultString + "" 537 | if(records["mostPointsPlayerGame-K"]) resultString = resultString + "" 538 | if(records["mostPointsPlayerSeason-K"]) resultString = resultString + "" 539 | } 540 | resultString = resultString + "

League Record Book

CategoryRecordHolder
Most Points (Game) " + roundScore(records["mostPointsGame"].score) + "" + getManagerName(records["mostPointsGame"].manager, leagueDict.managerMap) + "
Most Points (Season) " + roundScore(records["mostPointsSeason"].score) + "" + getManagerName(records["mostPointsSeason"].manager, leagueDict.managerMap) + " - " + records["mostPointsSeason"].year + "
Most Points (Matchup) " + roundScore(records["mostPointsMatchup"].matchupTotal) + "" + mostMatchupString + "
Fewest Points (G) " + roundScore(records["fewestPointsGame"].score) + "" + getManagerName(records["fewestPointsGame"].manager, leagueDict.managerMap) + "
Fewest Points (S) " + roundScore(records["fewestPointsSeason"].score) + "" + getManagerName(records["fewestPointsSeason"].manager, leagueDict.managerMap) + " - " + records["fewestPointsSeason"].year + "
Fewest Points (M) " + roundScore(records["fewestPointsMatchup"].matchupTotal) + "" + fewestMatchupString + "
Smallest Margin of Victory (M) " + roundScore(records["smallestMarginMatchup"].pointDiff) + "" + smallestMarginString + "
Biggest Blowout (M) " + roundScore(records["biggestBlowoutMatchup"].pointDiff) + "" + biggestBlowoutString + "
Most Points Allowed (S) " + roundScore(records["mostPointsAllowedSeason"].totalPoints) + "" + getManagerName(records["mostPointsAllowedSeason"].vs, leagueDict.managerMap) + " - " + records["mostPointsAllowedSeason"].year + "
Fewest Points Allowed (S) " + roundScore(records["fewestPointsAllowedSeason"].totalPoints) + "" + getManagerName(records["fewestPointsAllowedSeason"].vs, leagueDict.managerMap) + " - " + records["fewestPointsAllowedSeason"].year + "
Longest Win Streak " + records["winStreak"].games.length + "" + getManagerName(records["winStreak"].manager, leagueDict.managerMap) + "
Longest Losing Streak " + records["loseStreak"].games.length + "" + getManagerName(records["loseStreak"].manager, leagueDict.managerMap) + "
Most Points-QB (G) " + roundScore(records["mostPointsPlayerGame-QB"].score) + "" + records["mostPointsPlayerGame-QB"].player + "
Most Points-QB (S) " + roundScore(records["mostPointsPlayerSeason-QB"].score) + "" + records["mostPointsPlayerSeason-QB"].player + " - " + records["mostPointsPlayerSeason-QB"].year + "
Most Points-RB (G) " + roundScore(records["mostPointsPlayerGame-RB"].score) + "" + records["mostPointsPlayerGame-RB"].player + "
Most Points-RB (S) " + roundScore(records["mostPointsPlayerSeason-RB"].score) + "" + records["mostPointsPlayerSeason-RB"].player + " - " + records["mostPointsPlayerSeason-RB"].year + "
Most Points-WR (G) " + roundScore(records["mostPointsPlayerGame-WR"].score) + "" + records["mostPointsPlayerGame-WR"].player + "
Most Points-WR (S) " + roundScore(records["mostPointsPlayerSeason-WR"].score) + "" + records["mostPointsPlayerSeason-WR"].player + " - " + records["mostPointsPlayerSeason-WR"].year + "
Most Points-TE (G) " + roundScore(records["mostPointsPlayerGame-TE"].score) + "" + records["mostPointsPlayerGame-TE"].player + "
Most Points-TE (S) " + roundScore(records["mostPointsPlayerSeason-TE"].score) + "" + records["mostPointsPlayerSeason-TE"].player + " - " + records["mostPointsPlayerSeason-TE"].year + "
Most Points-D/ST (G) " + roundScore(records["mostPointsPlayerGame-DST"].score) + "" + records["mostPointsPlayerGame-DST"].player + "
Most Points-D/ST (S) " + roundScore(records["mostPointsPlayerSeason-DST"].score) + "" + records["mostPointsPlayerSeason-DST"].player + " - " + records["mostPointsPlayerSeason-DST"].year + "
Most Points-K (G) " + roundScore(records["mostPointsPlayerGame-K"].score) + "" + records["mostPointsPlayerGame-K"].player + "
Most Points-K (S) " + roundScore(records["mostPointsPlayerSeason-K"].score) + "" + records["mostPointsPlayerSeason-K"].player + " - " + records["mostPointsPlayerSeason-K"].year + "
" 541 | console.log(resultString) 542 | return resultString; 543 | } 544 | -------------------------------------------------------------------------------- /js/popup.js: -------------------------------------------------------------------------------- 1 | const leagueDatabase = {}; 2 | leagueDatabase.webdb = {}; 3 | leagueDatabase.webdb.db = null; 4 | let dbName = ''; 5 | let leagueLocalStorage = {}; 6 | let leagueDBNames = null; 7 | let leagueName = ''; 8 | let leagueNameMap = null; 9 | let leagueInfo = {}; 10 | let leagueSettings = {}; 11 | let selectedYearLeagueSettings = {}; 12 | let leagueId = null; 13 | 14 | const recordBook = document.getElementById('record-book'); 15 | const allTimeWins = document.getElementById('all-time-wins'); 16 | const powerRankings = document.getElementById('power-rankings'); 17 | const currentPeriod = document.getElementById('periodId'); 18 | const updateDatabase = document.getElementById('update-database'); 19 | const deleteDatabase = document.getElementById('delete-database'); 20 | const databaseInfo = document.getElementById('database-info'); 21 | const loadingDiv = document.getElementById('loading-div'); 22 | const syncText = document.getElementById('database-last-update'); 23 | const screenshot = document.getElementById('create-screenshot'); 24 | const lmNote = document.getElementById('lm-note'); 25 | const nameDisplay = document.getElementById('league-name'); 26 | const powerRankingTable = document.getElementById('power-rankings-table').getElementsByTagName('tbody')[0]; 27 | const powerRankingWeek = document.getElementById('power-ranking-week'); 28 | 29 | let lastSync = null; 30 | let firstYear = null; 31 | let currentYear = (new Date()).getUTCFullYear(); 32 | let selectedYear = null; 33 | let currentWeek = null; 34 | let lastDate = ''; 35 | let previousManager = ''; // for power rankings manager dropdown 36 | let isOverridePowerRanking = false; 37 | let lastCreatedTabId = null; 38 | let htmlBlock = null; 39 | let noOwnerCount = 0; 40 | let totalHistoryUpdates = 0; 41 | let historyUpdatesCompleted = 0; 42 | let members = []; 43 | let memberMap = {}; 44 | let settingsMap = {}; 45 | const defaultPositions = { 1: 'QB', 2: 'RB', 3: 'WR', 4: 'TE', 5: 'K', 16: 'D/ST' } 46 | const positions = { QB: "0.0", RB: "2.0", 'RB/WR': '2.0', WR: "4.0", "WR/TE": "4.0", TE: "6.0", DST: "16.0", 'D/ST': "16.0", K: "17.0" } 47 | const positionsByESPNValue = { "0.0": "QB", "2.0": "RB", "4.0": "WR", "6.0": "TE", "16.0": "D/ST", "17.0": "K" } 48 | 49 | let periodIndex = {}; 50 | 51 | //get initial options - league id and name, lastSync and former saved options 52 | chrome.storage.sync.get(['leagueDBNames','leagueNameMap'], (data) => { 53 | leagueDBNames = (data.leagueDBNames) ? data.leagueDBNames : []; 54 | leagueNameMap = (data.leagueNameMap) ? data.leagueNameMap : {}; 55 | 56 | chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, (tabs) => { 57 | chrome.tabs.executeScript( 58 | tabs[0].id, 59 | {code: ''}, 60 | () => { 61 | leagueId = tabs[0].url.split('leagueId=')[1].split('&')[0]; 62 | 63 | const dbName = 'league-' + leagueId; 64 | chrome.storage.sync.get([dbName], (data) => { 65 | if(data[dbName]) { 66 | currentPeriod.value = data[dbName].currentPeriod || 1; 67 | } 68 | 69 | currentPeriod.onchange = (e) => { 70 | onPeriodChange(tabs); 71 | leagueLocalStorage.currentPeriod = currentPeriod.value || 1; 72 | let saveState = {}; 73 | saveState[dbName] = leagueLocalStorage; 74 | chrome.storage.sync.set(saveState, () => { 75 | console.log("updated periodId") 76 | }); 77 | } 78 | onPeriodChange(tabs); 79 | }); 80 | }); 81 | }); 82 | }); 83 | 84 | const onPeriodChange = (tabs) => { 85 | currentWeek = currentPeriod.value || 1; 86 | chrome.tabs.executeScript( 87 | tabs[0].id, 88 | {code: 'document.getElementsByClassName("dropdown__select")[0].lastChild.value;'}, 89 | (firstYearResults) => { 90 | firstYear = (firstYearResults[0]) ? firstYearResults[0] : 2021; 91 | if(parseInt(firstYear) < 2007) { 92 | alert("League history limited to after 2006 due to API constraints"); 93 | firstYear = 2007; 94 | } 95 | selectedYear = 2021; 96 | if(!firstYearResults[0]) { 97 | // first year league 98 | retrieveLeagueDetails(tabs) 99 | } else { 100 | chrome.tabs.executeScript( 101 | tabs[0].id, 102 | {code: 'document.getElementsByClassName("dropdown__select")[0].value'}, 103 | (latestResults) => { 104 | if(parseInt(selectedYear) !== parseInt(latestResults[0])) { 105 | updateDatabase.setAttribute('disabled','disabled'); 106 | } 107 | retrieveLeagueDetails(tabs); 108 | }); 109 | } 110 | }); 111 | } 112 | 113 | const retrieveLeagueDetails = (tabs) => { 114 | var xhr = new XMLHttpRequest(); 115 | if(selectedYear < 2018) { 116 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/leagueHistory/${leagueId}?view=mLiveScoring&view=mMatchupScore&view=mRoster&view=mSettings&view=mStandings&view=mStatus&view=mTeam&view=modular&view=mNav&seasonId=${selectedYear}`, false); 117 | } else { 118 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/seasons/${selectedYear}/segments/0/leagues/${leagueId}?view=mSettings&view=mTeam&view=modular&view=mNav`, false); 119 | } 120 | xhr.send(); 121 | selectedYearLeagueSettings = JSON.parse(xhr.responseText); 122 | members = selectedYearLeagueSettings.members; 123 | powerRankingWeek.innerHTML = 'Matchup Week ' + currentWeek; 124 | 125 | chrome.tabs.executeScript( 126 | tabs[0].id, 127 | {code: ''}, 128 | (results) => { 129 | leagueName = selectedYearLeagueSettings.settings.name; 130 | owners = selectedYearLeagueSettings.members; 131 | nameDisplay.innerHTML = leagueName; 132 | dbName = 'league-' + leagueId; 133 | 134 | leagueDatabase.webdb.open = () => { 135 | var dbSize = 5 * 1024 * 1024; // 5MB 136 | leagueDatabase.webdb.db = openDatabase(dbName, "1", "League Database", dbSize); 137 | } 138 | leagueDatabase.webdb.open(); 139 | 140 | leagueDatabase.webdb.db.transaction((tx) => { 141 | tx.executeSql("CREATE TABLE IF NOT EXISTS " + 142 | "rankings(manager TEXT, week INTEGER, year INTEGER, ranking INTEGER, description TEXT, title TEXT)", [], 143 | () => {}, errorHandler); 144 | }); 145 | 146 | if(leagueDBNames.indexOf(dbName) === -1) { 147 | if(leagueDBNames.length === 0) { 148 | leagueDBNames = [dbName]; 149 | } else { 150 | leagueDBNames.push(dbName); 151 | } 152 | leagueNameMap[dbName] = leagueName; 153 | } 154 | 155 | chrome.storage.sync.get([dbName], (data) => { 156 | if(data[dbName]) { 157 | lastSync = data[dbName].lastSync; 158 | leagueLocalStorage = data[dbName]; 159 | } 160 | // generate power ranking table 161 | populatePowerRankings(); 162 | //set last sync display time 163 | syncText.innerHTML = (lastSync) ? ('Week ' + lastSync.split('-')[1] + ", Year " + lastSync.split('-')[0]) : 'Never'; 164 | }); 165 | }); 166 | } 167 | 168 | const generatePRManagerDropdown = (place, selectedTeam) => { 169 | let selectManager = document.createElement('select'); 170 | selectManager.setAttribute('place', place); 171 | selectManager.addEventListener('focus', (event) => { 172 | previousManager = event.target.value; 173 | }); 174 | selectManager.addEventListener('change', (event) => { 175 | let selectElements = document.getElementsByTagName('select'); 176 | for(var s = 0; s < selectElements.length; s++) { 177 | let select = selectElements[s]; 178 | if(select.getAttribute('place') !== event.target.getAttribute('place')) { 179 | if(select.value === event.target.value) { 180 | let descripElem = select.parentElement.nextSibling.getElementsByTagName('input')[0]; 181 | let newDescripElem = event.target.parentElement.nextSibling.getElementsByTagName('input')[0]; 182 | let oldText = descripElem.value; 183 | descripElem.value = newDescripElem.value; 184 | newDescripElem.value = oldText; 185 | select.value = previousManager; 186 | previousManager = event.target.value; 187 | } 188 | } 189 | } 190 | }); 191 | for(var team of selectedYearLeagueSettings.teams) { 192 | let managerOption = document.createElement('option'); 193 | let oName = getOwnerName(team.primaryOwner); 194 | memberMap[team.id] = team.primaryOwner; 195 | managerOption.setAttribute('value', oName); 196 | managerOption.innerHTML = (leagueLocalStorage.managerMap && leagueLocalStorage.managerMap[oName]) ? leagueLocalStorage.managerMap[oName] : oName; 197 | if(oName === selectedTeam) { 198 | managerOption.setAttribute('selected', 'selected'); 199 | } 200 | selectManager.appendChild(managerOption); 201 | } 202 | return selectManager; 203 | } 204 | 205 | const getOwnerName = (id) => { 206 | for(var i = 0; i < members.length; i++) { 207 | if(members[i].id === id) { 208 | return members[i].firstName + ' ' + members[i].lastName; 209 | } 210 | } 211 | return id; 212 | } 213 | 214 | const rankingToPlaceString = (ranking) => { 215 | let place = ranking.toString(); 216 | if(place === '11' || place === '12' || place === '13') { 217 | return place + 'th'; 218 | } 219 | switch(place.split('').pop()) { 220 | case '1': place = place + 'st'; break; 221 | case '2': place = place + 'nd'; break; 222 | case '3': place = place + 'rd'; break; 223 | default: place = place + 'th'; break; 224 | } return place; 225 | } 226 | 227 | const populatePowerRankings = () => { 228 | const query = "SELECT manager, ranking, week, year, description, title FROM rankings WHERE week = ? AND year = ?"; 229 | const powerRankingTitleRow = document.createElement('tr'); 230 | const powerRankingTitleInputCell = document.createElement('td'); 231 | const powerRankingTitleInput = document.createElement('input'); 232 | powerRankingTitleInput.setAttribute('type', 'text'); 233 | powerRankingTitleInput.setAttribute('id', 'power-ranking-title'); 234 | powerRankingTitleInputCell.setAttribute('colspan', '3'); 235 | let matchupPeriods = Object.keys(selectedYearLeagueSettings.settings.scheduleSettings.matchupPeriods); 236 | powerRankingTitleInput.setAttribute('placeholder', getPowerRankingWeekNameDisplay(currentWeek, selectedYearLeagueSettings.settings.scheduleSettings.matchupPeriodCount, matchupPeriods[matchupPeriods.length - 1]) + ' ' + selectedYear); 237 | powerRankingTitleInputCell.appendChild(powerRankingTitleInput); 238 | powerRankingTitleRow.appendChild(powerRankingTitleInputCell); 239 | powerRankingTable.innerHTML = powerRankingTitleRow.outerHTML; 240 | leagueDatabase.webdb.db.transaction((tx) => { 241 | tx.executeSql(query, [currentWeek, selectedYear], 242 | (tx, tr) => { 243 | var place = 1; 244 | if(tr.rows.length === 0) { 245 | //show managers by points scored 246 | let sortedArray = []; 247 | for(var teamKey in selectedYearLeagueSettings.teams) { 248 | sortedArray.push(selectedYearLeagueSettings.teams[teamKey]); 249 | } 250 | sortedArray.sort((a,b) => { 251 | if(a.record.pointsFor < b.record.pointsFor) return 1; 252 | else if(a.record.pointsFor > b.record.pointsFor) return -1; 253 | else return 0; 254 | }) 255 | for(var i = 0; i < sortedArray.length; i++) { 256 | let oName = getOwnerName(sortedArray[i].primaryOwner); //sortedArray[i].owners[0].firstName.trim() + ' ' + sortedArray[i].owners[0].lastName.trim(); 257 | const row = document.createElement('tr'); 258 | const rankingCell = document.createElement('td'); 259 | const managerCell = document.createElement('td'); 260 | const descriptionCell = document.createElement('td'); 261 | const descriptionInput = document.createElement('input'); 262 | rankingCell.innerHTML = rankingToPlaceString(i+1) + ": "; 263 | managerCell.appendChild(generatePRManagerDropdown(place, oName)); 264 | place += 1; 265 | descriptionInput.setAttribute('type','text'); 266 | descriptionInput.setAttribute('data-name',oName) 267 | descriptionCell.appendChild(descriptionInput); 268 | row.appendChild(rankingCell); 269 | row.appendChild(managerCell); 270 | row.appendChild(descriptionCell); 271 | powerRankingTable.appendChild(row); 272 | } 273 | } else { 274 | isOverridePowerRanking = true; 275 | document.getElementById('power-ranking-title').value = tr.rows[0].title; 276 | for(var i = 0; i < tr.rows.length; i++) { 277 | let oName = tr.rows[i].manager; 278 | const row = document.createElement('tr'); 279 | const rankingCell = document.createElement('td'); 280 | const managerCell = document.createElement('td'); 281 | const descriptionCell = document.createElement('td'); 282 | const descriptionInput = document.createElement('input'); 283 | rankingCell.innerHTML = rankingToPlaceString(tr.rows[i].ranking) + ": "; 284 | managerCell.appendChild(generatePRManagerDropdown(tr.rows[i].ranking, oName)); 285 | descriptionInput.setAttribute('type','text'); 286 | descriptionInput.setAttribute('data-name',oName) 287 | descriptionInput.value = tr.rows[i].description; 288 | descriptionCell.appendChild(descriptionInput); 289 | row.appendChild(rankingCell); 290 | row.appendChild(managerCell); 291 | row.appendChild(descriptionCell); 292 | powerRankingTable.appendChild(row); 293 | } 294 | } 295 | }, errorHandler 296 | ); 297 | }); 298 | } 299 | 300 | let parseTeams = (teamArray) => { 301 | const tempOwnerMap = {}; 302 | teamArray.forEach((team) => { 303 | let ownerName = '' 304 | let owner = getOwnerName(team.primaryOwner); 305 | 306 | tempOwnerMap[team.primaryOwner] = owner; 307 | }); 308 | return tempOwnerMap; 309 | } 310 | 311 | isValidPostSeasonMatchup = (periodId, champWeek) => { 312 | let tempIndex = periodIndex[periodId]; // weeks times the number of games a week (fails because of BYE weeks in playoffs) 313 | const matchupsTillFinal = champWeek - periodId; 314 | if(leagueLocalStorage.trackLosers) { 315 | return true; 316 | } else if(leagueLocalStorage.track3rdPlaceGame && matchupsTillFinal === 0) { 317 | return tempIndex < 2; 318 | } return tempIndex < Math.pow(2, matchupsTillFinal); 319 | } 320 | 321 | const addMatchupBoxscoreToDB = (matchup, index, periodId, pointerYear, seasonId, ownerLookup, hasNextMatchup, scheduleItems, currentLeagueSettings) => { 322 | if(!hasNextMatchup) { 323 | return; 324 | } 325 | let matchupPeriods = Object.keys(currentLeagueSettings.scheduleSettings.matchupPeriods); 326 | periodIndex[periodId] = (typeof periodIndex[periodId] === 'undefined') ? 0 : periodIndex[periodId] + 1; 327 | if(matchup.home && matchup.away) { 328 | if(periodId <= currentLeagueSettings.scheduleSettings.matchupPeriodCount || isValidPostSeasonMatchup(periodId, matchupPeriods[matchupPeriods.length - 1])) { 329 | 330 | // only if 2019 or later do we have boxscore details - consider nixing it - we have the override in extension options for previous record holders 331 | if(pointerYear > 2018) { 332 | var xhr = new XMLHttpRequest(); 333 | 334 | //need to add x-fantasy-filter: {"schedule":{"filterMatchupPeriodIds":{"value":[1]}}} 335 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/seasons/${pointerYear}/segments/0/leagues/${leagueId}?view=mMatchup&view=mMatchupScore&scoringPeriodId=${periodId}`, true); 336 | xhr.setRequestHeader("x-fantasy-filter", JSON.stringify({"schedule":{"filterMatchupPeriodIds":{"value":[periodId]}}})); 337 | xhr.onload = (e) => { 338 | //no longer an html returned of one match - this is a data API for returning all matchup data for the week given 339 | let weekDetails = JSON.parse(xhr.responseText); 340 | let thisMatchup; 341 | //find game in schedule Array 342 | for(var i = 0; i < weekDetails.schedule.length; i++) { 343 | if(matchup.id === weekDetails.schedule[i].id) { 344 | thisMatchup = weekDetails.schedule[i]; 345 | } 346 | } 347 | totalHistoryUpdates += thisMatchup.home.rosterForMatchupPeriod.entries.length; 348 | totalHistoryUpdates += thisMatchup.away.rosterForMatchupPeriod.entries.length; 349 | 350 | for(var i = 0; i < thisMatchup.away.rosterForMatchupPeriod.entries.length; i++) { 351 | let player = thisMatchup.away.rosterForMatchupPeriod.entries[i].playerPoolEntry.player; 352 | let playerName = player.firstName + ' ' + player.lastName; 353 | let playerPos = defaultPositions[player.defaultPositionId]; 354 | let playerScore = thisMatchup.away.rosterForMatchupPeriod.entries[i].playerPoolEntry.appliedStatTotal; 355 | if(playerScore !== '--') { 356 | leagueDatabase.webdb.db.transaction((tx) => { 357 | tx.executeSql("INSERT INTO history(manager, week, year, player, playerPosition, score) VALUES (?,?,?,?,?,?)", 358 | [ownerLookup[memberMap[thisMatchup.away.teamId]] || memberMap[thisMatchup.away.teamId], periodId, seasonId, playerName, playerPos, playerScore], ()=>{ 359 | loadingDiv.innerHTML = 'Uploading History Record ' + historyUpdatesCompleted + ' of ' + totalHistoryUpdates; 360 | historyUpdatesCompleted += 1; 361 | if(totalHistoryUpdates === historyUpdatesCompleted) { 362 | setTimeout(() => { 363 | lastSync = lastDate; 364 | leagueLocalStorage.lastSync = lastSync; 365 | let saveState = {}; 366 | saveState[dbName] = leagueLocalStorage; 367 | chrome.storage.sync.set(saveState, () => { 368 | let dateParts = lastDate.split('-'); 369 | syncText.innerHTML = 'Week ' + dateParts[1] + ", Year " + dateParts[0]; 370 | }); 371 | alert("Database Update Complete") 372 | enableButtons(); 373 | }) 374 | } 375 | }, errorHandler); 376 | }); 377 | } 378 | 379 | } 380 | for(var i = 0; i < thisMatchup.home.rosterForMatchupPeriod.entries.length; i++) { 381 | let player = thisMatchup.home.rosterForMatchupPeriod.entries[i].playerPoolEntry.player; 382 | let playerName = player.firstName + ' ' + player.lastName; 383 | let playerPos = defaultPositions[player.defaultPositionId]; 384 | let playerScore = thisMatchup.home.rosterForMatchupPeriod.entries[i].playerPoolEntry.appliedStatTotal; 385 | if(playerScore !== '--') { 386 | leagueDatabase.webdb.db.transaction((tx) => { 387 | tx.executeSql("INSERT INTO history(manager, week, year, player, playerPosition, score) VALUES (?,?,?,?,?,?)", 388 | [ownerLookup[memberMap[thisMatchup.home.teamId]] || memberMap[thisMatchup.home.teamId], periodId, seasonId, playerName, playerPos, playerScore], ()=>{ 389 | loadingDiv.innerHTML = 'Uploading History Record ' + historyUpdatesCompleted + ' of ' + totalHistoryUpdates; 390 | historyUpdatesCompleted += 1; 391 | if(totalHistoryUpdates === historyUpdatesCompleted) { 392 | setTimeout(() => { 393 | lastSync = lastDate; 394 | leagueLocalStorage.lastSync = lastSync; 395 | let saveState = {}; 396 | saveState[dbName] = leagueLocalStorage; 397 | chrome.storage.sync.set(saveState, () => { 398 | let dateParts = lastDate.split('-'); 399 | syncText.innerHTML = 'Week ' + dateParts[1] + ", Year " + dateParts[0]; 400 | }); 401 | alert("Database Update Complete") 402 | enableButtons(); 403 | }) 404 | } 405 | }, errorHandler); 406 | }); 407 | 408 | } 409 | } 410 | } 411 | xhr.send(null); 412 | } 413 | console.log(matchup); 414 | let homeScore = matchup.home.pointsByScoringPeriod[periodId]; 415 | let awayScore = matchup.away.pointsByScoringPeriod[periodId]; 416 | let awayOutcome = 3; 417 | let homeOutcome = 3; 418 | 419 | //figure out what is up with index - i dont think it means the same anymore'[-] 420 | let isThirdPlaceGame = false; // no longer show this as its in losers bracket 421 | let isChampionship = (periodId == matchupPeriods[matchupPeriods.length - 1]) ? true : false; 422 | let isLosersBacketGame = false; 423 | let homeTeamBonus = 2; 424 | if(matchup.winner === 'AWAY') { 425 | awayOutcome = 1; 426 | homeOutcome = 2; 427 | } else if(matchup.winner === 'HOME') { 428 | awayOutcome = 2; 429 | homeOutcome = 1; 430 | } 431 | // home team 432 | leagueDatabase.webdb.db.transaction((tx) => { 433 | tx.executeSql("INSERT INTO matchups(manager, week, year, vs, isHomeGame, winLoss, score, matchupTotal, pointDiff, isThirdPlaceGame, isChampionship, isLosersBacketGame) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", 434 | [ownerLookup[memberMap[matchup.home.teamId]] || memberMap[matchup.home.teamId], periodId, seasonId, ownerLookup[memberMap[matchup.away.teamId]] || memberMap[matchup.away.teamId], true, homeOutcome, (homeScore + homeTeamBonus), (awayScore + homeScore + homeTeamBonus), ((homeScore + homeTeamBonus) - awayScore), isThirdPlaceGame, isChampionship, isLosersBacketGame], ()=>{}, errorHandler); 435 | }); 436 | // away team 437 | leagueDatabase.webdb.db.transaction((tx) => { 438 | tx.executeSql("INSERT INTO matchups(manager, week, year, vs, isHomeGame, winLoss, score, matchupTotal, pointDiff, isThirdPlaceGame, isChampionship, isLosersBacketGame) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", 439 | [ownerLookup[memberMap[matchup.away.teamId]] || memberMap[matchup.away.teamId], periodId, seasonId, ownerLookup[memberMap[matchup.home.teamId]] || memberMap[matchup.home.teamId], false, awayOutcome, awayScore, (awayScore + homeScore + homeTeamBonus), (awayScore - (homeScore + homeTeamBonus)), isThirdPlaceGame, isChampionship, isLosersBacketGame], 440 | (tx, tr) => { 441 | loadingDiv.innerHTML = 'Uploading Matchup ' + periodId + ', Year ' + pointerYear; 442 | }, errorHandler); 443 | }); 444 | if(hasNextMatchup) { 445 | let matchup = scheduleItems.shift(); 446 | addMatchupBoxscoreToDB(matchup, ++index, matchup.matchupPeriodId, pointerYear, seasonId, ownerLookup, scheduleItems.length > 0, scheduleItems, currentLeagueSettings); 447 | } 448 | 449 | } else if(hasNextMatchup) { 450 | let matchup = scheduleItems.shift(); 451 | addMatchupBoxscoreToDB(matchup, ++index, matchup.matchupPeriodId, pointerYear, seasonId, ownerLookup, scheduleItems.length > 0, scheduleItems, currentLeagueSettings); 452 | } 453 | } else { 454 | // doesnt have two teams in the game so it isnt valid 455 | let matchup = scheduleItems.shift(); 456 | //indexModifier[periodId] = (indexModifier[periodId]) ? indexModifier[periodId] + 1 : 1; 457 | addMatchupBoxscoreToDB(matchup, ++index, matchup.matchupPeriodId, pointerYear, seasonId, ownerLookup, scheduleItems.length > 0, scheduleItems, currentLeagueSettings); 458 | } 459 | 460 | } 461 | 462 | const isEndOfDatabaseUpdates = (lastDate, pointerYear, periodId, isChampionship, isThirdPlaceGame, hasNextMatchup) => { 463 | if (lastDate === (pointerYear + '-' + periodId)) { 464 | // check if the last date in the queue is the current pointer date 465 | if (isChampionship && (!leagueLocalStorage.trackLosers && !leagueLocalStorage.track3rdPlaceGame)) { 466 | // if championship week, in a league not counting losers bracket or 3rd place games 467 | // return immediately because we only process one matchup record for the date 468 | return true; 469 | } else if (leagueLocalStorage.trackLosers && !hasNextMatchup) { 470 | // if tracking losers, return when we are on the last matchup of the week 471 | return true; 472 | } else if (leagueLocalStorage.track3rdPlaceGame && isThirdPlaceGame) { 473 | // if tracking 3rd place but not other loser and it is third place game, return true 474 | return true; 475 | } else if (!hasNextMatchup) { 476 | // if not the championship game and not tracking any losers, then return when there 477 | // are no matchups left to process for the week 478 | return true; 479 | } 480 | } return false; 481 | } 482 | 483 | createTables = () => { 484 | // update from last sync to present - if no last sync, then from firstYear 485 | let yearPointer = (lastSync) ? parseInt(lastSync.split("-")[0], 10) : firstYear; 486 | let weekPointer = (lastSync) ? parseInt(lastSync.split("-")[1], 10) : 1; 487 | let initialRun = true; 488 | for(yearPointer; yearPointer <= currentYear; yearPointer++) { 489 | var xhr = new XMLHttpRequest(); 490 | periodIndex = {}; 491 | 492 | if(yearPointer === currentYear) { 493 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/seasons/${yearPointer}/segments/0/leagues/${leagueId}?view=mLiveScoring&view=mMatchupScore&view=mRoster&view=mSettings&view=mStandings&view=mStatus&view=mTeam&view=modular&view=mNav`, false); 494 | } else if(yearPointer < 2018) { 495 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/leagueHistory/${leagueId}?view=mLiveScoring&view=mMatchupScore&view=mRoster&view=mSettings&view=mStandings&view=mStatus&view=mTeam&view=modular&view=mNav&seasonId=${yearPointer}`, false); 496 | } else { 497 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/seasons/${yearPointer}/segments/0/leagues/${leagueId}?view=mSettings&view=mTeam&view=modular&view=mNav`, false); 498 | } 499 | 500 | 501 | xhr.send(); 502 | leagueSettings = (yearPointer < 2018) ? JSON.parse(xhr.responseText)[0].settings : JSON.parse(xhr.responseText).settings; 503 | settingsMap[yearPointer] = leagueSettings; 504 | if(!leagueSettings) { 505 | alert("Private League. API request denied."); 506 | } 507 | 508 | //get team info for the year to match with ids later 509 | var teamInfo = (yearPointer < 2018) ? JSON.parse(xhr.responseText)[0].teams : JSON.parse(xhr.responseText).teams; 510 | const ownerMap = parseTeams(teamInfo); 511 | 512 | var leagueSchedule = (yearPointer < 2018) ? JSON.parse(xhr.responseText)[0].settings.scheduleSettings : JSON.parse(xhr.responseText).settings.scheduleSettings; 513 | 514 | let seasonId = (yearPointer < 2018) ? JSON.parse(xhr.responseText)[0].seasonId : JSON.parse(xhr.responseText).seasonId; 515 | let scheduleItems; 516 | if(yearPointer === currentYear) { 517 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/seasons/${yearPointer}/segments/0/leagues/${leagueId}?view=mLiveScoring&view=mMatchupScore&view=mRoster&view=mSettings&view=mStandings&view=mStatus&view=mTeam&view=modular&view=mNav`, false); 518 | xhr.send(); 519 | 520 | scheduleItems = JSON.parse(xhr.responseText).schedule; 521 | } else { 522 | xhr.open("GET", `https://fantasy.espn.com/apis/v3/games/ffl/leagueHistory/${leagueId}?seasonId=${yearPointer}&view=mMatchup`, false); 523 | xhr.send(); 524 | 525 | scheduleItems = JSON.parse(xhr.responseText)[0].schedule; 526 | } 527 | 528 | let week = scheduleItems.shift(); 529 | if(initialRun) { 530 | // check for current week 531 | while(weekPointer !== week.matchupPeriodId) { 532 | week = scheduleItems.shift(); 533 | } 534 | //weekPointer = week.matchupPeriodId; 535 | if(currentYear == yearPointer && currentWeek == weekPointer) { 536 | alert("Database Update Complete") 537 | enableButtons(); 538 | return true; 539 | } 540 | } else if(currentYear == yearPointer && currentWeek == weekPointer) { 541 | let matchupPeriods = Object.keys(settingsMap[yearPointer - 1].scheduleSettings.matchupPeriods); 542 | lastDate = (weekPointer === 1) ? "" + (yearPointer - 1) + "-" + matchupPeriods[matchupPeriods.length - 1] : "" + (yearPointer) + "-" + (weekPointer - 1); 543 | return true; 544 | } 545 | initialRun = false; 546 | addMatchupBoxscoreToDB(week, 0, weekPointer, yearPointer, seasonId, ownerMap, scheduleItems.length > 0, scheduleItems, leagueSettings); 547 | } 548 | } 549 | 550 | disableButtons = () => { 551 | let buttons = document.getElementsByTagName('button'); 552 | for(var b = 0; b < buttons.length; b++) { 553 | buttons[b].setAttribute('disabled','disabled'); 554 | } 555 | databaseInfo.style.display = 'none'; 556 | loadingDiv.innerHTML = 'Preparing SQL Statements...'; 557 | loadingDiv.style.display = 'inline-block'; 558 | } 559 | 560 | enableButtons = () => { 561 | let buttons = document.getElementsByTagName('button'); 562 | for(var b = 0; b < buttons.length; b++) { 563 | buttons[b].removeAttribute('disabled'); 564 | } 565 | databaseInfo.style.display = 'inline-block'; 566 | loadingDiv.style.display = 'none'; 567 | } 568 | 569 | updateDatabase.onclick = (element) => { 570 | alert("Warning: Do not close the extension popup while database is updating.") 571 | noOwnerCount = 0; 572 | disableButtons(); 573 | setTimeout(() => { 574 | if(lastSync === null) { 575 | if(leagueDBNames.indexOf(dbName) === -1) { 576 | if(leagueDBNames.length === 0) { 577 | leagueDBNames = [dbName]; 578 | } else { 579 | leagueDBNames.push(dbName); 580 | } 581 | leagueNameMap[dbName] = leagueName; 582 | } 583 | // if last sync is null, clear database to be sure an old instance isnt still there 584 | chrome.storage.sync.set({'leagueDBNames': leagueDBNames, 'leagueNameMap': leagueNameMap}, () => { 585 | leagueDatabase.webdb.db.transaction((tx) => { 586 | tx.executeSql("CREATE TABLE IF NOT EXISTS " + 587 | "history(manager TEXT, week INTEGER, year INTEGER, player TEXT, playerPosition TEXT, score FLOAT)", [], ()=>{}, errorHandler); 588 | tx.executeSql("CREATE TABLE IF NOT EXISTS " + 589 | "matchups(manager TEXT, week INTEGER, year INTEGER, vs TEXT, isHomeGame BOOLEAN, winLoss INTEGER, score FLOAT, matchupTotal Float, pointDiff FLOAT, isThirdPlaceGame BOOLEAN, isChampionship BOOLEAN, isLosersBacketGame BOOLEAN)", [], 590 | () => { createTables(); }, errorHandler); 591 | }); 592 | }); 593 | } else { 594 | createTables(); 595 | } 596 | }, 1) 597 | }; 598 | 599 | deleteDatabase.onclick = (element) => { 600 | let shouldDelete = confirm('Are you sure you want to delete the data from your database?'); 601 | 602 | if(shouldDelete) { 603 | let shouldDeleteRankings = confirm('Would you like to clear the Power Rankings database as well? \n(Cancel will delete only the rankings and history tables)'); 604 | if(shouldDeleteRankings) { 605 | try { 606 | leagueDatabase.webdb.db.transaction((tx) => { 607 | tx.executeSql("DROP TABLE IF EXISTS rankings",[],() => { 608 | tx.executeSql("DROP TABLE IF EXISTS matchups",[],() => { 609 | tx.executeSql("DROP TABLE IF EXISTS history",[],() => { 610 | leagueDBNames.splice(leagueDBNames.indexOf(dbName), 1); 611 | delete leagueNameMap[leagueName]; 612 | let saveState = {'leagueDBNames': leagueDBNames, 'leagueNameMap': leagueNameMap}; 613 | leagueLocalStorage.lastSync = null; 614 | saveState[dbName] = leagueLocalStorage; 615 | chrome.storage.sync.set(saveState, () => { 616 | alert('Database deletion complete'); 617 | lastSync = null; 618 | syncText.innerHTML = 'Never'; 619 | }); 620 | }, errorHandler); 621 | }, errorHandler); 622 | }, errorHandler); 623 | }); 624 | } catch(e) { 625 | alert('Database deletion failed. Please try again'); 626 | } 627 | } else { 628 | try { 629 | leagueDatabase.webdb.db.transaction((tx) => { 630 | tx.executeSql("DROP TABLE IF EXISTS matchups",[],() => { 631 | tx.executeSql("DROP TABLE IF EXISTS history",[],() => { 632 | leagueDBNames.splice(leagueDBNames.indexOf(dbName), 1); 633 | delete leagueNameMap[leagueName]; 634 | let saveState = {'leagueDBNames': leagueDBNames, 'leagueNameMap': leagueNameMap}; 635 | leagueLocalStorage.lastSync = null; 636 | saveState[dbName] = leagueLocalStorage; 637 | chrome.storage.sync.set(saveState, () => { 638 | alert('Database deletion complete'); 639 | lastSync = null; 640 | syncText.innerHTML = 'Never'; 641 | }); 642 | }, errorHandler); 643 | }, errorHandler); 644 | }); 645 | } catch(e) { 646 | alert('Database deletion failed. Please try again'); 647 | } 648 | } 649 | } 650 | }; 651 | 652 | const popupRecordBookListenerFunction = (request, sender, sendResponse) => { 653 | if (request.msg === "screen_ready") { 654 | chrome.runtime.sendMessage({ 655 | msg: "something_completed", 656 | data: { 657 | name: "league_record_book", 658 | html: htmlBlock 659 | } 660 | }); 661 | } 662 | } 663 | 664 | const createRecordBookTab = () => { 665 | chrome.runtime.onMessage.removeListener(popupPRListenerFunction); 666 | chrome.runtime.onMessage.removeListener(popupRecordBookListenerFunction); 667 | chrome.runtime.onMessage.removeListener(popupListenerFunction); 668 | chrome.runtime.onMessage.addListener(popupRecordBookListenerFunction); 669 | chrome.tabs.query({ 670 | active: true, currentWindow: true 671 | }, tabs => { 672 | let index = tabs[0].index; 673 | chrome.tabs.create({ 674 | url: chrome.extension.getURL('../html/screenshot.html'), 675 | index: index + 1, 676 | active: false, 677 | }, (tab) => { 678 | lastCreatedTabId = tab.id; 679 | }); 680 | }); 681 | } 682 | 683 | recordBook.onclick = (element) => { 684 | const onReady = (result) => { 685 | htmlBlock = result; 686 | if(lastCreatedTabId) { 687 | chrome.tabs.remove(lastCreatedTabId, ()=> { 688 | createRecordBookTab() 689 | }) 690 | } else { 691 | createRecordBookTab(); 692 | } 693 | } 694 | 695 | var yearList = yearRangeGenerator(parseInt(firstYear,10),parseInt(currentYear,10)); 696 | const leagueSettingsMap = getLeagueSettings(yearList, leagueId); 697 | mergeDataIntoRecordBook(leagueDatabase.webdb.db, positions, leagueSettingsMap, leagueLocalStorage, onReady); 698 | }; 699 | 700 | const popupListenerFunction = (request, sender, sendResponse) => { 701 | if (request.msg === "screen_ready") { 702 | chrome.runtime.sendMessage({ 703 | msg: "something_completed", 704 | data: { 705 | name: "league_all_time_wins", 706 | html: htmlBlock 707 | } 708 | }); 709 | } 710 | } 711 | 712 | const createLeaderBoardTab = () => { 713 | chrome.runtime.onMessage.removeListener(popupPRListenerFunction); 714 | chrome.runtime.onMessage.removeListener(popupRecordBookListenerFunction); 715 | chrome.runtime.onMessage.removeListener(popupListenerFunction); 716 | chrome.runtime.onMessage.addListener(popupListenerFunction); 717 | chrome.tabs.query({ 718 | active: true, currentWindow: true 719 | }, tabs => { 720 | let index = tabs[0].index; 721 | chrome.tabs.create({ 722 | url: chrome.extension.getURL('../html/screenshot.html'), 723 | index: index + 1, 724 | active: false, 725 | }, (tab) => { 726 | lastCreatedTabId = tab.id; 727 | }); 728 | }); 729 | } 730 | 731 | allTimeWins.onclick = (element) => { 732 | const onReady = (result) => { 733 | htmlBlock = result; 734 | if(lastCreatedTabId) { 735 | chrome.tabs.remove(lastCreatedTabId, ()=> { 736 | createLeaderBoardTab() 737 | }) 738 | } else { 739 | createLeaderBoardTab(); 740 | } 741 | 742 | } 743 | getManagers(leagueDatabase.webdb.db, (managerList) => { 744 | if(managerList.length === 0) { 745 | // no data in database 746 | alert('No matchups saved in the database, cannot generate leader board without matchup data. Please upload the database with at least one matchup before attempting again.'); 747 | } else { 748 | getRecords(leagueDatabase.webdb.db, managerList, (records) => { 749 | getAllManagersPoints(leagueDatabase.webdb.db, managerList, (points) => { 750 | var yearList = yearRangeGenerator(parseInt(firstYear,10),parseInt(currentYear,10)); 751 | const leagueSettingsMap = getLeagueSettings(yearList, leagueId); 752 | getChampionships(leagueDatabase.webdb.db, yearList, leagueSettingsMap, (champions) => { 753 | getSackos(leagueDatabase.webdb.db, yearList, leagueSettingsMap, (sackos) => { 754 | getAllManagersPlayoffAppearences(leagueDatabase.webdb.db, managerList, yearList, leagueSettingsMap, (playoffApps) => { 755 | const mergedRecords = mergeDataIntoRecords(records, sackos, champions, playoffApps, points); 756 | getAllTimeLeaderBoard(mergedRecords, leagueSettingsMap[yearList[0]], onReady); 757 | }); 758 | }) 759 | }) 760 | }) 761 | }) 762 | } 763 | }); 764 | } 765 | 766 | const saveWeeklyPowerRanking = (clonedRanking, title, ranking, callback, downloadImageFunction) => { 767 | let row = clonedRanking.shift(); 768 | leagueDatabase.webdb.db.transaction((tx) => { 769 | tx.executeSql("INSERT INTO rankings(manager, week, year, ranking, description, title) VALUES (?,?,?,?,?,?)", 770 | [row.manager, currentWeek, selectedYear, row.place, row.description, title], 771 | () => { 772 | if(clonedRanking.length > 0) { saveWeeklyPowerRanking(clonedRanking, title, ranking, callback, downloadImageFunction); } 773 | else { callback(ranking, title, downloadImageFunction); } 774 | }, errorHandler); 775 | }); 776 | } 777 | 778 | const getTeamRecord = (teamMap, ownerName) => { 779 | for(var team of teamMap) { 780 | let oName = getOwnerName(team.primaryOwner); 781 | if(oName === ownerName) { 782 | return { 783 | wins: team.record.overall.wins, 784 | losses: team.record.overall.losses, 785 | ties: team.record.overall.ties, 786 | image: team.logo, 787 | teamName: team.location + ' ' + team.nickname 788 | }; 789 | } 790 | 791 | } 792 | } 793 | 794 | powerRankings.onclick = (element) => { 795 | if(isOverridePowerRanking) { 796 | if(confirm('Power ranking for the week has already been saved, are you sure you want to overwrite it?')) { 797 | leagueDatabase.webdb.db.transaction((tx) => { 798 | tx.executeSql("DELETE FROM rankings WHERE year = ? AND week = ?", [selectedYear, currentWeek], 799 | () => { 800 | powerRankingClickFunction(element); 801 | }, errorHandler); 802 | }); 803 | } 804 | } else { 805 | powerRankingClickFunction(element); 806 | } 807 | } 808 | 809 | const popupPRListenerFunction = (request, sender, sendResponse) => { 810 | if (request.msg === "screen_ready") { 811 | chrome.runtime.sendMessage({ 812 | msg: "something_completed", 813 | data: { 814 | name: "power_ranking", 815 | html: htmlBlock 816 | } 817 | }); 818 | } 819 | } 820 | 821 | const createPowerRankingTab = () => { 822 | chrome.runtime.onMessage.removeListener(popupPRListenerFunction); 823 | chrome.runtime.onMessage.removeListener(popupRecordBookListenerFunction); 824 | chrome.runtime.onMessage.removeListener(popupListenerFunction); 825 | chrome.runtime.onMessage.addListener(popupPRListenerFunction); 826 | chrome.tabs.query({ 827 | active: true, currentWindow: true 828 | }, tabs => { 829 | let index = tabs[0].index; 830 | chrome.tabs.create({ 831 | url: chrome.extension.getURL('../html/screenshot.html'), 832 | index: index + 1, 833 | active: false 834 | }, (tab) => { 835 | lastCreatedTabId = tab.id; 836 | }); 837 | }); 838 | } 839 | 840 | const powerRankingClickFunction = (element) => { 841 | const onReady = (result) => { 842 | htmlBlock = result; 843 | if(lastCreatedTabId) { 844 | chrome.tabs.remove(lastCreatedTabId, () => { 845 | createPowerRankingTab() 846 | }) 847 | } else { 848 | createPowerRankingTab(); 849 | } 850 | } 851 | 852 | leagueDatabase.webdb.db.transaction((tx) => { 853 | tx.executeSql("CREATE TABLE IF NOT EXISTS " + 854 | "rankings(manager TEXT, week INTEGER, year INTEGER, ranking INTEGER, description TEXT, title TEXT)", [], 855 | () => { 856 | // first retrieve all the form data 857 | const selectElements = document.getElementById('power-rankings-table').getElementsByTagName('select'); 858 | const powerRankingTitle = (document.getElementById('power-ranking-title').value !== '') ? document.getElementById('power-ranking-title').value : document.getElementById('power-ranking-title').placeholder; 859 | const weeklyPowerRanking = []; 860 | for(var i = 0; i < selectElements.length; i++) { 861 | const selectElem = selectElements[i]; 862 | const descElem = selectElements[i].parentElement.nextSibling.getElementsByTagName('input')[0]; 863 | const place = i + 1; 864 | weeklyPowerRanking.push({ 865 | manager: selectElem.value, 866 | id: selectElem.getAttribute('data-id'), 867 | description: descElem.value, 868 | place: place 869 | }); 870 | } 871 | //save to DB 872 | let weeklyPowerRankingClone = JSON.parse(JSON.stringify(weeklyPowerRanking)); 873 | saveWeeklyPowerRanking(weeklyPowerRankingClone, powerRankingTitle, weeklyPowerRanking, generatePowerRanking, onReady); 874 | }, errorHandler); 875 | }); 876 | } 877 | 878 | const stripLinks = (s) => { 879 | var div = document.createElement('div'); 880 | div.innerHTML = s; 881 | var links = div.getElementsByTagName('a'); 882 | var i = links.length; 883 | while (i--) { 884 | let innerText = links[i].innerHTML; 885 | links[i].parentNode.prepend(innerText); 886 | links[i].parentNode.removeChild(links[i]); 887 | } 888 | return div.innerHTML; 889 | } 890 | 891 | const stripImgs = (s) => { 892 | var div = document.createElement('div'); 893 | div.innerHTML = s; 894 | var imgs = div.getElementsByTagName('img'); 895 | var i = imgs.length; 896 | while (i--) { 897 | imgs[i].parentNode.removeChild(imgs[i]); 898 | } 899 | return div.innerHTML; 900 | } 901 | 902 | const getPlayerTable = (s) => { 903 | var div = document.createElement('div'); 904 | div.innerHTML = s; 905 | var tables = div.getElementsByTagName('table'); 906 | var i = tables.length; 907 | let tableList = []; 908 | while (i--) { 909 | if(tables[i].classList.contains('Table') && !tables[i].classList.contains('hideableGroup')) { 910 | tableList.push(tables[i]); 911 | } 912 | } 913 | return tableList; 914 | } 915 | --------------------------------------------------------------------------------