Skip to content

Commit

Permalink
release: 1.2.0
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit 810b280
Author: aajfranklin <[email protected]>
Date:   Sat Mar 28 16:04:30 2020 +0000

    docs: update readme

commit 45595c2
Author: aajfranklin <[email protected]>
Date:   Sat Mar 28 14:10:09 2020 +0000

    chore: update version number in manifest

commit 2db3457
Author: aajfranklin <[email protected]>
Date:   Sat Mar 28 14:07:06 2020 +0000

    feat: check cached player data matches current version number and update if not

commit 3a431c0
Author: aajfranklin <[email protected]>
Date:   Sat Mar 28 13:40:07 2020 +0000

    fix: state overlay taller than needed on high res displays, too much white space for short careers

commit 47a59c9
Author: aajfranklin <[email protected]>
Date:   Sat Mar 28 13:22:52 2020 +0000

    refactor: move magic numbers into config

commit 97feee3
Author: aajfranklin <[email protected]>
Date:   Sat Mar 28 12:47:52 2020 +0000

    fix: breaking issue viewing players with no seasons (e.g. just drafted)

commit cf95266
Author: aajfranklin <[email protected]>
Date:   Fri Mar 27 23:23:23 2020 +0000

    feat: include metric heights and weights

commit 5f7f86f
Author: aajfranklin <[email protected]>
Date:   Fri Mar 27 22:28:02 2020 +0000

    feat: option to reverse career rows so career and latest season display first

commit e0e4281
Author: aajfranklin <[email protected]>
Date:   Fri Mar 27 19:42:12 2020 +0000

    wip: add logic for reversing career rows, actual toggle pending

commit 8c1a9cf
Author: aajfranklin <[email protected]>
Date:   Fri Mar 27 18:57:07 2020 +0000

    feat: display stats only after hovering for half second or more

commit 6fe8fef
Author: aajfranklin <[email protected]>
Date:   Fri Mar 27 18:27:46 2020 +0000

    feat: reorder stats so most important ones are in view without scrolling

commit 1d9f19b
Author: aajfranklin <[email protected]>
Date:   Fri Mar 27 18:00:40 2020 +0000

    fix: nested names highlighting

commit ce97b73
Author: aajfranklin <[email protected]>
Date:   Fri Mar 27 15:29:34 2020 +0000

    fix: remove leading 0 from percentages
  • Loading branch information
aajfranklin committed Mar 28, 2020
1 parent f6452cc commit eb0d251
Show file tree
Hide file tree
Showing 17 changed files with 464 additions and 227 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.idea
config.js
.DS_Store
background/endpointAnalysis.js
sandbox/
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ Use the Click and Roll options menu to toggle it on or off, globally or for indi

![Click and Roll toggle demo](assets/animated/demo3.gif)

## Latest Release Notes

1.2.0 Release Notes:

- Moved most important stats (PTS, REB, AST, TOV, BLK, STL) into view without scrolling
- Added ability to reverse career rows, so that career totals and more recent seasons are at the top
- Added metric system heights and weights
- Set stats to only show after hovering on a name for half a second, to prevent accidental openings
- Removed leading zero from percentage stats, for consistency with most stats sites
- Fixed issue where nested names would highlight e.g. Mike Bloom was highlighting within the name Mike Bloomberg
- Fixed issue where un-capped stat window height created huge amounts of white space when viewing players with short careers on very high resolution monitors
- Fixed error when viewing stats for players with no career games (e.g. recently drafted players)
- Other code enhancements

## Future Features

The order and number of features added will depend on user demand. Possible features include:
Expand Down
4 changes: 3 additions & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Click and Roll",
"version": "1.1.0",
"version": "1.2.0",
"icons": {
"128": "./assets/static/clickAndRoll.png"
},
Expand All @@ -12,6 +12,7 @@
],
"js": [
"./scripts/utils/jquery-3.4.1.js",
"./scripts/utils/config.js",
"./scripts/utils/utils.js",
"./scripts/content/nicknameMap.js",
"./scripts/content/ahoCorasick.js",
Expand All @@ -27,6 +28,7 @@
"background": {
"scripts": [
"./scripts/utils/jquery-3.4.1.js",
"./scripts/utils/config.js",
"./scripts/utils/utils.js",
"./scripts/background/playerImageRefMap.js",
"./scripts/background/messageHandler.js",
Expand Down
47 changes: 36 additions & 11 deletions scripts/background/messageHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function MessageHandler() {
method: 'GET',
headers: {'x-click-and-roll': 'true'},
cache: false,
timeout: 10000
timeout: config.networkTimeout
})
};

Expand Down Expand Up @@ -93,7 +93,7 @@ function MessageHandler() {
this.utils.saveToLocalStorage('cache-records', []);
return [];
}
return cacheRecords.length >= 100 ? this.cleanCache(cacheRecords) : cacheRecords;
return cacheRecords.length >= config.maxCachedPlayers ? this.cleanCache(cacheRecords) : cacheRecords;
});
};

Expand All @@ -106,7 +106,10 @@ function MessageHandler() {

this.statsInCacheAndCurrent = (cacheRecords, id) => {
const player = cacheRecords.filter(player => player.id === id)[0] || null;
return player !== null && (!player.active || Date.now() - player.timestamp < (3 * 60 * 60 * 1000));
return player !== null
&& player.version !== undefined
&& player.version === config.currentCacheRecordVersion
&& (!player.active || Date.now() - player.timestamp < (config.playerUpdateInterval));
};

this.fetchNonCachedStats = (id) => {
Expand All @@ -129,7 +132,7 @@ function MessageHandler() {
};

this.cacheStats = (stats, id, records) => {
const newRecord = [{id, timestamp: Date.now(), active: stats.active}];
const newRecord = [{id, timestamp: Date.now(), active: stats.active, version: config.currentCacheRecordVersion}];
this.utils.saveToLocalStorage(`player-${id}`, stats);
this.utils.saveToLocalStorage('cache-records', records.filter(player => player.id !== id).concat(newRecord));
};
Expand All @@ -138,13 +141,14 @@ function MessageHandler() {
return this.utils.getFromLocalStorage('timestamp')
.then(timestamp => {
return new Promise(resolve => {
const timeout = Math.max(0, 1000 - (Date.now() - timestamp));
const timeout = Math.max(0, config.requestRateLimit - (Date.now() - timestamp));
setTimeout(resolve, timeout);
});
});
};

this.getActive = (rows) => {
if (rows.length === 0) return true;
const lastActiveSeason = rows[rows.length - 2];
const lastActiveYear = parseInt(lastActiveSeason['SEASON_ID'].slice(0,4));
const currentYear = new Date().getFullYear();
Expand All @@ -171,22 +175,35 @@ function MessageHandler() {

this.createRow = (season, isCareerRow) => {
const allStarSeasonSpan = '<span style="color:gold; padding-left: 8px">&#9733;</span>';
const countingStats = ['GP','MIN','FGM','FGA','FG_PCT','FG3M','FG3A','FG3_PCT','FTM','FTA','FT_PCT','OREB','DREB','REB','AST','STL','BLK','TOV','PF','PTS'];
const countingStats = ['GP','MIN','PTS','OREB','DREB','REB','AST','TOV','STL','BLK','FGM','FGA','FG_PCT','FG3M','FG3A','FG3_PCT','FTM','FTA','FT_PCT','PF'];

let tableDataCells = `<td class="season stick-left">${season['SEASON_ID']}`
+ `${season['ALL_STAR'] ? allStarSeasonSpan : ''}</td>`
+ `<td>${isCareerRow ? '-' : season['TEAM_ABBREVIATION'] || 'n/a'}</td>`
+ `<td>${isCareerRow ? '-' : season['PLAYER_AGE'] || 'n/a'}</td>`;

for (let stat of countingStats) {
tableDataCells += `<td>${this.parseStatToDisplayValue(season[stat])}</td>`
tableDataCells += `<td>${this.parseRawStatToDisplayString(season[stat], stat.indexOf('PCT') !== -1)}</td>`
}

return '<tr' + (isCareerRow ? ' class="career">' : '>') + tableDataCells + '</tr>';
};

this.parseStatToDisplayValue = (stat) => {
return stat === 0 ? 0 : (stat || 'n/a');
this.parseRawStatToDisplayString = (rawStat, isPct) => {
let displayString = `${rawStat === 0 ? 0 : (rawStat || 'n/a')}`;
if (isPct && displayString !== 'n/a') {
switch (displayString) {
case '1':
displayString = '1.000';
break;
case '0':
displayString = '.000';
break;
default:
displayString = displayString.padEnd(5, '0').substring(1);
}
}
return displayString;
};

this.getProfileHTML = (profile) => {
Expand All @@ -209,7 +226,7 @@ function MessageHandler() {
},
{
label: 'Height',
value: profile['HEIGHT'] || 'n/a'
value: this.formatHeight(profile['HEIGHT']) || 'n/a'
},
{
label: 'College',
Expand Down Expand Up @@ -271,8 +288,16 @@ function MessageHandler() {
return birthday ? birthday.split('T')[0] : 'n/a'
};

this.formatHeight = (height) => {
if (height) {
const metricHeight = Math.round(height[0] * config.cmPerFeet + height.substring(2) * config.cmPerInch);
return `${height} (${Math.floor(metricHeight / 100)}.${(metricHeight % 100).toString().padStart(2, '0')} m)`;
}
return 'n/a';
};

this.formatWeight = (weight) => {
return weight ? weight + ' lb' : 'n/a';
return weight ? `${weight} lb (${Math.round(weight * config.kgPerLb)} kg)` : 'n/a';
};

this.getPlayerImageUrl = (fullName) => {
Expand Down
84 changes: 49 additions & 35 deletions scripts/content/clickAndRoll.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ function ClickAndRoll() {
this.players = [];
this.resultSearch = new ResultSearch();
this.scrollParent = null;
this.hoverTimer = null;
this.reverse = false;

this.utils = new Utils();

Expand Down Expand Up @@ -75,7 +77,7 @@ function ClickAndRoll() {
return this.utils.getFromLocalStorage('players');
})
.then(players => {
const playersUpdatedWithin24Hours = Date.now() - lastUpdated < (24 * 60 * 60 * 1000);
const playersUpdatedWithin24Hours = Date.now() - lastUpdated < (config.playersUpdateInterval);
if (players && playersUpdatedWithin24Hours) return Promise.resolve(players);
return this.utils.sendRuntimeMessage({message: 'fetchPlayers'})
})
Expand All @@ -89,7 +91,8 @@ function ClickAndRoll() {
nodes.forEach(node => {
node.style.color = 'teal';
node.style.display = 'inline';
node.onmouseenter = this.handleHover;
node.onmouseenter = this.handleMouseEnter;
node.onmouseleave = this.handleMouseLeave;
});
};

Expand All @@ -110,41 +113,49 @@ function ClickAndRoll() {
this.observer.observe(document.body, { childList: true, subtree: true });
};

this.handleHover = (mouseEnterEvent) => {
this.updateActiveName(mouseEnterEvent.target);
this.handleMouseEnter = (mouseEnterEvent) => {
this.hoverTimer = setTimeout(() => this.handleHover(mouseEnterEvent), config.hoverTimeout);
};

this.handleMouseLeave = () => {
clearTimeout(this.hoverTimer);
};

this.handleHover = (event) => {
this.updateActiveName(event.target);
this.resetFrame();

const newPlayerId = this.players.filter(player => player['NAME'] === this.activeName.element.textContent)[0]['PLAYER_ID'];
const isNetworkErrorShowing = this.getFrameDocument().getElementById('network-error')
&& !this.getFrameDocument().getElementById('network-error').hidden;

if (newPlayerId !== this.currentPlayerId || isNetworkErrorShowing) {
this.setFrameLoading(newPlayerId);
this.addCloseOverlayListeners();
return this.utils.sendRuntimeMessage({message: 'fetchStats', playerId: this.currentPlayerId})
.then(stats => {
// current player id may have been reassigned by a later hover, making these stats out of date
if (newPlayerId === this.currentPlayerId) {
this.dataReceived = true;
this.displayStats(stats, this.activeName.element.textContent)
}
})
.catch(() => {
this.displayNetworkError();
});
} else {
this.addCloseOverlayListeners();
this.displayStats();
}

this.setFrameLoading(newPlayerId);
this.addCloseOverlayListeners();

return this.utils.isSettingOn('reverse')
.then(reverse => {
this.reverse = reverse;
return this.utils.sendRuntimeMessage({message: 'fetchStats', playerId: this.currentPlayerId});
})
.then(stats => {
// current player id may have been reassigned by a later hover, making these stats out of date
if (newPlayerId === this.currentPlayerId) {
this.dataReceived = true;
this.displayStats(stats, this.activeName.element.textContent)
}
})
.catch(() => {
this.displayNetworkError();
});
};

this.updateActiveName = (target) => {
// reapply handle hover to previous element
if (this.activeName.element) {
this.activeName.element.onmouseenter = this.handleHover
this.activeName.element.onmouseenter = this.handleMouseEnter;
this.activeName.element.onmouseleave = this.handleMouseLeave;
}
this.activeName.element = target;
this.activeName.element.onmouseenter = null;
this.activeName.element.onmouseleave = null;
};

this.resetFrame = () => {
Expand Down Expand Up @@ -217,11 +228,11 @@ function ClickAndRoll() {
// 2 pixel left offset to accommodate box shadow of frame's inner elements
const overlayLeft = this.activeName.isInLeftHalf
? rect.left + scrollX - 2
: rect.left + scrollX - 2 - this.getHalfViewWidth() + rect.width + Math.max(this.getHalfViewWidth() - 800, 0);
: rect.left + scrollX - 2 - this.getHalfViewWidth() + rect.width + Math.max(this.getHalfViewWidth() - config.maxFrameContainerWidth, 0);

const overlayTop = this.activeName.isInTopHalf
? rect.top + scrollY + rect.height
: rect.top + scrollY - this.getHalfViewHeight();
: rect.top + scrollY - this.getHalfViewHeight() + Math.max(this.getHalfViewHeight() - config.maxFrameContainerHeight, 0);

return {
left: overlayLeft,
Expand Down Expand Up @@ -253,7 +264,8 @@ function ClickAndRoll() {
};

this.closeOverlay = () => {
this.activeName.element.onmouseenter = this.handleHover;
this.activeName.element.onmouseenter = this.handleMouseEnter;
this.activeName.element.onmouseleave = this.handleMouseLeave;
this.frameContainer.hidden = true;
this.scrollParent.removeEventListener('scroll', this.positionFrameContainer);
document.removeEventListener('click', this.closeOverlay);
Expand All @@ -272,23 +284,25 @@ function ClickAndRoll() {
this.getFrameDocument().getElementById('player-profile-content').innerHTML += stats.profileHTML;

if (stats.careerHTML.length) {
this.getFrameDocument().getElementById('season-averages-body').innerHTML += stats.careerHTML;
this.getFrameDocument().getElementById('season-averages-body').innerHTML += this.reverse ? this.reverseCareer(stats.careerHTML) : stats.careerHTML;
} else {
this.getFrameDocument().getElementById('content').removeChild(this.getFrameDocument().getElementById('career-heading'));
this.getFrameDocument().getElementById('content').removeChild(this.getFrameDocument().getElementById('career-stats'));
this.getFrameDocument().getElementById('career-stats').removeChild(this.getFrameDocument().getElementById('regular-season-averages-table'));
}
}

if (!this.frameContainer.hidden) {
if (!this.frameContainer.hidden && stats.careerHTML.length !== 0) {
this.checkContentHeight();
}
};

this.reverseCareer = (careerHTML) => {
return careerHTML.replace(/<tr/g, ',<tr').split(',').reverse().join('');
};

this.checkContentHeight = () => {
const frameContent = this.getFrameDocument().getElementById('content');
const playerHeaderHeight = 37;

if (frameContent.scrollHeight + playerHeaderHeight < (this.getHalfViewHeight()) - 2) {
if (frameContent.scrollHeight + config.playerHeaderHeight < (this.getHalfViewHeight()) - 2) {
frameContent.classList.add('short-career');
}
};
Expand Down
22 changes: 14 additions & 8 deletions scripts/content/resultSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ function ResultSearch() {
const nodeIncludesNextHit = currentTextIndex + nodeTextLength >= nextHit.end;

if (nodeIncludesNextHit) {
if (this.parentNodeIsValid(currentNode) && !this.isEditable(currentNode)) {
const resultNode = this.createResultNode(nextHit, currentNode, currentTextIndex);
if (resultNode !== null) resultNodes.push(resultNode);
if (this.isValidHitAndNode(currentNode, nextHit, currentTextIndex)) {
resultNodes.push(this.createResultNode(nextHit, currentNode, currentTextIndex));
}
nextHit = hits.shift();
} else {
Expand All @@ -62,6 +61,13 @@ function ResultSearch() {
return resultNodes;
};

this.isValidHitAndNode = (node, hit, currentTextIndex) => {
return this.parentNodeIsValid(node)
&& !this.isEditable(node)
&& !this.isNestedString(hit, node.textContent, currentTextIndex)
&& !(hit.start < currentTextIndex);
};

this.parentNodeIsValid = (currentNode) => {
if (currentNode.parentNode) {
const parentNode = currentNode.parentNode;
Expand All @@ -72,16 +78,16 @@ function ResultSearch() {
return true;
};

this.isNestedString = (hit, nodeText, currentTextIndex) => {
let nextChar = nodeText[hit.end - currentTextIndex + 1];
return nextChar ? !!nextChar.match(/(?!(?:×|÷))[a-zA-Z\u00c0-\u017f]/) : false;
};

this.isEditable = (node) => {
return node instanceof HTMLInputElement || node.isContentEditable;
};

this.createResultNode = (hit, hitNode, currentTextIndex) => {
// ignore hits split across multiple nodes
if (hit.start < currentTextIndex) {
return null;
}

const range = document.createRange();
range.setStart(hitNode, hit.start - currentTextIndex);
range.setEnd(hitNode, hit.end - currentTextIndex + 1);
Expand Down
Loading

0 comments on commit eb0d251

Please sign in to comment.