diff --git a/assets/js/favorites.js b/assets/js/favorites.js
index 873860a..b84b408 100644
--- a/assets/js/favorites.js
+++ b/assets/js/favorites.js
@@ -20,7 +20,7 @@ export class FavoritesManager {
serverRegistration.isFavorite = true
// Update icon since by default it is unfavorited
- document.getElementById('favorite-toggle_' + serverRegistration.serverId).setAttribute('class', this.getIconClass(serverRegistration.isFavorite))
+ document.getElementById(`favorite-toggle_${serverRegistration.serverId}`).setAttribute('class', this.getIconClass(serverRegistration.isFavorite))
}
}
}
@@ -47,7 +47,7 @@ export class FavoritesManager {
serverRegistration.isFavorite = !serverRegistration.isFavorite
// Update the displayed favorite icon
- document.getElementById('favorite-toggle_' + serverRegistration.serverId).setAttribute('class', this.getIconClass(serverRegistration.isFavorite))
+ document.getElementById(`favorite-toggle_${serverRegistration.serverId}`).setAttribute('class', this.getIconClass(serverRegistration.isFavorite))
// Request the app controller instantly re-sort the server listing
// This handles the favorite sorting logic internally
diff --git a/assets/js/graph.js b/assets/js/graph.js
index 199a49e..1f2305b 100644
--- a/assets/js/graph.js
+++ b/assets/js/graph.js
@@ -50,10 +50,7 @@ export class GraphDisplayManager {
}
// Paint updated data structure
- this._plotInstance.setData([
- this._graphTimestamps,
- ...this._graphData
- ])
+ this._plotInstance.setData(this.getGraphData())
}
loadLocalStorage () {
@@ -127,6 +124,13 @@ export class GraphDisplayManager {
}
}
+ getGraphData () {
+ return [
+ this._graphTimestamps,
+ ...this._graphData
+ ]
+ }
+
getGraphDataPoint (serverId, index) {
const graphData = this._graphData[serverId]
if (graphData && index < graphData.length && typeof graphData[index] === 'number') {
@@ -134,6 +138,37 @@ export class GraphDisplayManager {
}
}
+ getClosestPlotSeriesIndex (idx) {
+ let closestSeriesIndex = -1
+ let closestSeriesDist = Number.MAX_VALUE
+
+ for (let i = 1; i < this._plotInstance.series.length; i++) {
+ const series = this._plotInstance.series[i]
+
+ if (!series.show) {
+ continue
+ }
+
+ const point = this._plotInstance.data[i][idx]
+
+ if (typeof point === 'number') {
+ const scale = this._plotInstance.scales[series.scale]
+ const posY = (1 - ((point - scale.min) / (scale.max - scale.min))) * this._plotInstance.height
+
+ // +20 to offset some sort of strange calculation bug
+ // cursor.top does not seem to correctly align with the generated posY values
+ const dist = Math.abs(posY - (this._plotInstance.cursor.top + 20))
+
+ if (dist < closestSeriesDist) {
+ closestSeriesIndex = i
+ closestSeriesDist = dist
+ }
+ }
+ }
+
+ return closestSeriesIndex
+ }
+
buildPlotInstance (timestamps, data) {
// Lazy load settings from localStorage, if any and if enabled
if (!this._hasLoadedSettings) {
@@ -150,7 +185,7 @@ export class GraphDisplayManager {
scale: 'Players',
stroke: serverRegistration.data.color,
width: 2,
- value: (_, raw) => formatNumber(raw) + ' Players',
+ value: (_, raw) => `${formatNumber(raw)} Players`,
show: serverRegistration.isVisible,
spanGaps: true,
points: {
@@ -165,44 +200,32 @@ export class GraphDisplayManager {
// eslint-disable-next-line new-cap
this._plotInstance = new uPlot({
plugins: [
- uPlotTooltipPlugin((pos, id) => {
+ uPlotTooltipPlugin((pos, idx) => {
if (pos) {
- let text = this._app.serverRegistry.getServerRegistrations()
+ const closestSeriesIndex = this.getClosestPlotSeriesIndex(idx)
+
+ const text = this._app.serverRegistry.getServerRegistrations()
.filter(serverRegistration => serverRegistration.isVisible)
.sort((a, b) => {
if (a.isFavorite !== b.isFavorite) {
return a.isFavorite ? -1 : 1
- }
-
- const aPoint = this.getGraphDataPoint(a.serverId, id)
- const bPoint = this.getGraphDataPoint(b.serverId, id)
-
- if (typeof aPoint === typeof bPoint) {
- if (typeof aPoint === 'undefined') {
- return 0
- }
} else {
- return typeof aPoint === 'number' ? -1 : 1
+ return a.data.name.localeCompare(b.data.name)
}
-
- return bPoint - aPoint
})
.map(serverRegistration => {
- const point = this.getGraphDataPoint(serverRegistration.serverId, id)
+ const point = this.getGraphDataPoint(serverRegistration.serverId, idx)
let serverName = serverRegistration.data.name
+ if (closestSeriesIndex === serverRegistration.getGraphDataIndex()) {
+ serverName = `${serverName}`
+ }
if (serverRegistration.isFavorite) {
- serverName = ' ' + serverName
+ serverName = ` ${serverName}`
}
- if (typeof point === 'number') {
- return serverName + ': ' + formatNumber(point)
- } else {
- return serverName + ': -'
- }
- }).join('
')
-
- text += '
' + formatTimestampSeconds(this._graphTimestamps[id]) + ''
+ return `${serverName}: ${formatNumber(point)}`
+ }).join('
') + `
${formatTimestampSeconds(this._graphTimestamps[idx])}`
this._app.tooltip.set(pos.left, pos.top, 10, 10, text)
} else {
@@ -238,8 +261,8 @@ export class GraphDisplayManager {
},
split: () => {
const visibleGraphData = this.getVisibleGraphData()
- const [, max, scale] = RelativeScale.scaleMatrix(visibleGraphData, tickCount, maxFactor)
- const ticks = RelativeScale.generateTicks(0, max, scale)
+ const { scaledMax, scale } = RelativeScale.scaleMatrix(visibleGraphData, tickCount, maxFactor)
+ const ticks = RelativeScale.generateTicks(0, scaledMax, scale)
return ticks
}
}
@@ -249,18 +272,15 @@ export class GraphDisplayManager {
auto: false,
range: () => {
const visibleGraphData = this.getVisibleGraphData()
- const [, scaledMax] = RelativeScale.scaleMatrix(visibleGraphData, tickCount, maxFactor)
- return [0, scaledMax]
+ const { scaledMin, scaledMax } = RelativeScale.scaleMatrix(visibleGraphData, tickCount, maxFactor)
+ return [scaledMin, scaledMax]
}
}
},
legend: {
show: false
}
- }, [
- this._graphTimestamps,
- ...this._graphData
- ], document.getElementById('big-graph'))
+ }, this.getGraphData(), document.getElementById('big-graph'))
// Show the settings-toggle element
document.getElementById('settings-toggle').style.display = 'inline-block'
@@ -273,7 +293,7 @@ export class GraphDisplayManager {
// Copy application state into the series data used by uPlot
for (const serverRegistration of this._app.serverRegistry.getServerRegistrations()) {
- this._plotInstance.series[serverRegistration.serverId + 1].show = serverRegistration.isVisible
+ this._plotInstance.series[serverRegistration.getGraphDataIndex()].show = serverRegistration.isVisible
}
this._plotInstance.redraw()
diff --git a/assets/js/mojang.js b/assets/js/mojang.js
index ac4b6a0..498dce9 100644
--- a/assets/js/mojang.js
+++ b/assets/js/mojang.js
@@ -13,8 +13,8 @@ export class MojangUpdater {
updateServiceStatus (name, title) {
// HACK: ensure mojang-status is added for alignment, replace existing class to swap status color
- document.getElementById('mojang-status_' + name).setAttribute('class', MOJANG_STATUS_BASE_CLASS + ' mojang-status-' + title.toLowerCase())
- document.getElementById('mojang-status-text_' + name).innerText = title
+ document.getElementById(`mojang-status_${name}`).setAttribute('class', `${MOJANG_STATUS_BASE_CLASS} mojang-status-${title.toLowerCase()}`)
+ document.getElementById(`mojang-status-text_${name}`).innerText = title
}
reset () {
diff --git a/assets/js/percbar.js b/assets/js/percbar.js
index 596bdf2..f23339a 100644
--- a/assets/js/percbar.js
+++ b/assets/js/percbar.js
@@ -20,12 +20,15 @@ export class PercentageBar {
// Update position/width
// leftPadding is a sum of previous iterations width value
- const div = document.getElementById('perc-bar-part_' + serverRegistration.serverId) || this.createPart(serverRegistration)
+ const div = document.getElementById(`perc-bar-part_${serverRegistration.serverId}`) || this.createPart(serverRegistration)
+
+ const widthPixels = `${width}px`
+ const leftPaddingPixels = `${leftPadding}px`
// Only redraw if needed
- if (div.style.width !== width + 'px' || div.style.left !== leftPadding + 'px') {
- div.style.width = width + 'px'
- div.style.left = leftPadding + 'px'
+ if (div.style.width !== widthPixels || div.style.left !== leftPaddingPixels) {
+ div.style.width = widthPixels
+ div.style.left = leftPaddingPixels
}
leftPadding += width
@@ -35,7 +38,7 @@ export class PercentageBar {
createPart (serverRegistration) {
const div = document.createElement('div')
- div.id = 'perc-bar-part_' + serverRegistration.serverId
+ div.id = `perc-bar-part_${serverRegistration.serverId}`
div.style.background = serverRegistration.data.color
div.setAttribute('class', 'perc-bar-part')
@@ -55,10 +58,10 @@ export class PercentageBar {
const serverRegistration = this._app.serverRegistry.getServerRegistration(serverId)
this._app.tooltip.set(event.target.offsetLeft, event.target.offsetTop, 10, this._parent.offsetTop + this._parent.offsetHeight + 10,
- (typeof serverRegistration.rankIndex !== 'undefined' ? '#' + (serverRegistration.rankIndex + 1) + ' ' : '') +
- serverRegistration.data.name +
- '
' + formatNumber(serverRegistration.playerCount) + ' Players
' +
- '' + formatPercent(serverRegistration.playerCount, this._app.getTotalPlayerCount()) + '')
+ `${typeof serverRegistration.rankIndex !== 'undefined' ? `#${serverRegistration.rankIndex + 1} ` : ''}
+ ${serverRegistration.data.name}
+ ${formatNumber(serverRegistration.playerCount)} Players
+ ${formatPercent(serverRegistration.playerCount, this._app.getTotalPlayerCount())}`)
}
handleMouseOut = () => {
diff --git a/assets/js/scale.js b/assets/js/scale.js
index a94ca17..01ecd7e 100644
--- a/assets/js/scale.js
+++ b/assets/js/scale.js
@@ -1,6 +1,6 @@
export class RelativeScale {
static scale (data, tickCount, maxFactor) {
- const [min, max] = RelativeScale.calculateBounds(data)
+ const { min, max } = RelativeScale.calculateBounds(data)
let factor = 1
@@ -12,8 +12,12 @@ export class RelativeScale {
const ticks = (scaledMax - scaledMin) / scale
- if (ticks < tickCount + 1 || (typeof maxFactor === 'number' && factor === maxFactor)) {
- return [scaledMin, scaledMax, scale]
+ if (ticks <= tickCount || (typeof maxFactor === 'number' && factor === maxFactor)) {
+ return {
+ scaledMin,
+ scaledMax,
+ scale
+ }
} else {
// Too many steps between min/max, increase factor and try again
factor++
@@ -22,27 +26,9 @@ export class RelativeScale {
}
static scaleMatrix (data, tickCount, maxFactor) {
- let max = Number.MIN_VALUE
+ const max = Math.max(...data.flat())
- for (const row of data) {
- let testMax = Number.MIN_VALUE
-
- for (const point of row) {
- if (point > testMax) {
- testMax = point
- }
- }
-
- if (testMax > max) {
- max = testMax
- }
- }
-
- if (max === Number.MIN_VALUE) {
- max = 0
- }
-
- return RelativeScale.scale([0, max], tickCount, maxFactor)
+ return RelativeScale.scale([0, RelativeScale.isFiniteOrZero(max)], tickCount, maxFactor)
}
static generateTicks (min, max, step) {
@@ -55,30 +41,22 @@ export class RelativeScale {
static calculateBounds (data) {
if (data.length === 0) {
- return [0, 0]
+ return {
+ min: 0,
+ max: 0
+ }
} else {
- let min = Number.MAX_VALUE
- let max = Number.MIN_VALUE
+ const min = Math.min(...data)
+ const max = Math.max(...data)
- for (const point of data) {
- if (typeof point === 'number') {
- if (point > max) {
- max = point
- }
- if (point < min) {
- min = point
- }
- }
+ return {
+ min: RelativeScale.isFiniteOrZero(min),
+ max: RelativeScale.isFiniteOrZero(max)
}
-
- if (min === Number.MAX_VALUE) {
- min = 0
- }
- if (max === Number.MIN_VALUE) {
- max = 0
- }
-
- return [min, max]
}
}
+
+ static isFiniteOrZero (val) {
+ return Number.isFinite(val) ? val : 0
+ }
}
diff --git a/assets/js/servers.js b/assets/js/servers.js
index b79945e..f103f67 100644
--- a/assets/js/servers.js
+++ b/assets/js/servers.js
@@ -67,6 +67,10 @@ export class ServerRegistration {
this._failedSequentialPings = 0
}
+ getGraphDataIndex () {
+ return this.serverId + 1
+ }
+
addGraphPoints (points, timestampPoints) {
this._graphData = [
timestampPoints.slice(),
@@ -87,9 +91,7 @@ export class ServerRegistration {
if (typeof playerCount !== 'number') {
this._app.tooltip.hide()
} else {
- const text = formatNumber(playerCount) + ' Players
' + formatTimestampSeconds(this._graphData[0][id])
-
- this._app.tooltip.set(pos.left, pos.top, 10, 10, text)
+ this._app.tooltip.set(pos.left, pos.top, 10, 10, `${formatNumber(playerCount)} Players
${formatTimestampSeconds(this._graphData[0][id])}`)
}
} else {
this._app.tooltip.hide()
@@ -116,7 +118,7 @@ export class ServerRegistration {
scale: 'Players',
stroke: '#E9E581',
width: 2,
- value: (_, raw) => formatNumber(raw) + ' Players',
+ value: (_, raw) => `${formatNumber(raw)} Players`,
spanGaps: true,
points: {
show: false
@@ -139,8 +141,8 @@ export class ServerRegistration {
width: 1
},
split: () => {
- const [min, max, scale] = RelativeScale.scale(this._graphData[1], tickCount)
- const ticks = RelativeScale.generateTicks(min, max, scale)
+ const { scaledMin, scaledMax, scale } = RelativeScale.scale(this._graphData[1], tickCount)
+ const ticks = RelativeScale.generateTicks(scaledMin, scaledMax, scale)
return ticks
}
}
@@ -149,7 +151,7 @@ export class ServerRegistration {
Players: {
auto: false,
range: () => {
- const [scaledMin, scaledMax] = RelativeScale.scale(this._graphData[1], tickCount)
+ const { scaledMin, scaledMax } = RelativeScale.scale(this._graphData[1], tickCount)
return [scaledMin, scaledMax]
}
}
@@ -157,7 +159,7 @@ export class ServerRegistration {
legend: {
show: false
}
- }, this._graphData, document.getElementById('chart_' + this.serverId))
+ }, this._graphData, document.getElementById(`chart_${this.serverId}`))
}
handlePing (payload, timestamp) {
@@ -193,15 +195,15 @@ export class ServerRegistration {
updateServerRankIndex (rankIndex) {
this.rankIndex = rankIndex
- document.getElementById('ranking_' + this.serverId).innerText = '#' + (rankIndex + 1)
+ document.getElementById(`ranking_${this.serverId}`).innerText = `#${rankIndex + 1}`
}
_renderValue (prefix, handler) {
- const labelElement = document.getElementById(prefix + '_' + this.serverId)
+ const labelElement = document.getElementById(`${prefix}_${this.serverId}`)
labelElement.style.display = 'block'
- const valueElement = document.getElementById(prefix + '-value_' + this.serverId)
+ const valueElement = document.getElementById(`${prefix}-value_${this.serverId}`)
const targetElement = valueElement || labelElement
if (targetElement) {
@@ -214,7 +216,7 @@ export class ServerRegistration {
}
_hideValue (prefix) {
- const element = document.getElementById(prefix + '_' + this.serverId)
+ const element = document.getElementById(`${prefix}_${this.serverId}`)
element.style.display = 'none'
}
@@ -227,8 +229,8 @@ export class ServerRegistration {
if (ping.recordData) {
this._renderValue('record', (element) => {
if (ping.recordData.timestamp > 0) {
- element.innerText = formatNumber(ping.recordData.playerCount) + ' (' + formatDate(ping.recordData.timestamp) + ')'
- element.title = 'At ' + formatDate(ping.recordData.timestamp) + ' ' + formatTimestampSeconds(ping.recordData.timestamp)
+ element.innerText = `${formatNumber(ping.recordData.playerCount)} (${formatDate(ping.recordData.timestamp)})`
+ element.title = `At ${formatDate(ping.recordData.timestamp)} ${formatTimestampSeconds(ping.recordData.timestamp)}`
} else {
element.innerText = formatNumber(ping.recordData.playerCount)
}
@@ -240,7 +242,7 @@ export class ServerRegistration {
if (ping.graphPeakData) {
this._renderValue('peak', (element) => {
element.innerText = formatNumber(ping.graphPeakData.playerCount)
- element.title = 'At ' + formatTimestampSeconds(ping.graphPeakData.timestamp)
+ element.title = `At ${formatTimestampSeconds(ping.graphPeakData.timestamp)}`
})
this.lastPeakData = ping.graphPeakData
@@ -262,7 +264,7 @@ export class ServerRegistration {
// An updated favicon has been sent, update the src
if (ping.favicon) {
- const faviconElement = document.getElementById('favicon_' + this.serverId)
+ const faviconElement = document.getElementById(`favicon_${this.serverId}`)
// Since favicons may be URLs, only update the attribute when it has changed
// Otherwise the browser may send multiple requests to the same URL
@@ -275,20 +277,20 @@ export class ServerRegistration {
initServerStatus (latestPing) {
const serverElement = document.createElement('div')
- serverElement.id = 'container_' + this.serverId
- serverElement.innerHTML = '