commit d25334d35f0bf443a0104c3dc6fa68d67bf2be78
parent 8c8499fb1c58941b3cefbb1520fbcfeefdadb66a
Author: Emily Doherty <git@emily.sh>
Date: Sun, 3 Nov 2024 22:54:23 -0800
Initial implementation of overlay functionality
Diffstat:
5 files changed, 201 insertions(+), 19 deletions(-)
diff --git a/src/fetchData.js b/src/fetchData.js
@@ -6,7 +6,7 @@ export default async function fetchData(currentRegionSelection, currentTimeSelec
const resp = await fetch(urlBuilder(currentRegionSelection, currentTimeSelection, currentAggregateSelection));
const respData = await resp.json();
for (let i = 0, l = respData.length; i < l; i++) {
- let datum = new Datum(Date.parse(respData[i][0]), respData[i][1]);
+ const datum = new Datum(new Date(respData[i][0]), respData[i][1]);
data.push(datum);
}
return data;
diff --git a/src/index.html b/src/index.html
@@ -62,6 +62,12 @@
<option id='agg_davg' value="avg">Daily Average</option>
</select>
</fieldset>
+ <fieldset id="period-overlay-options">
+ <label for="period-overlay" id="period-overlay-label">
+ Overlay previous <em id="period-time">0 hours</em> on current period:
+ </label>
+ <input type="checkbox" id="period-overlay" name="period-overlay">
+ </fieldset>
<fieldset id="y-start-options">
<label for="y-start">Start y-axis at 0:</label>
<input type="checkbox" id="y-start" name="y-start"/>
diff --git a/src/index.js b/src/index.js
@@ -6,6 +6,7 @@ import fetchData from "./fetchData";
import {updateHighTime} from "./highTime";
import {updateLowTime} from "./lowTime";
import {addLoader, removeLoader} from "./loader";
+import {allowOverlay, forceOverlayOff, isOverlaySelected} from "./overlay";
import TokenChart from "./tokenChart";
import Datum from "./datum";
@@ -66,7 +67,6 @@ async function updateRegionPreference(newRegion) {
currentRegionSelection = newRegion;
}
formatToken();
- chart = new TokenChart();
await pullChartData();
}
@@ -76,7 +76,12 @@ async function updateTimePreference(newTime) {
addLoader();
currentTimeSelection = newTime;
}
- chart = new TokenChart();
+ if (newTime === "all") {
+ forceOverlayOff();
+ }
+ else {
+ allowOverlay();
+ }
await pullChartData();
updateHighTime();
updateLowTime();
@@ -88,7 +93,6 @@ async function updateAggregatePreference(newAggregate) {
addLoader();
currentAggregateSelection = newAggregate;
}
- chart = new TokenChart();
await pullChartData();
}
@@ -109,16 +113,28 @@ function toggleStartYAtZero(){
chart.toggleYStart(startYAtZero);
}
+async function toggleOverlay() {
+ await chart.destroyChart();
+ addLoader();
+ await pullChartData();
+}
+
async function pullChartData() {
- chartData[currentRegionSelection] = await fetchData(currentRegionSelection, currentTimeSelection, currentAggregateSelection);
+ let timeSelection = currentTimeSelection;
+ if (isOverlaySelected()) {
+ let timeDigits = parseInt(timeSelection.slice(0, timeSelection.length - 1)) * 2;
+ let timeUnit = timeSelection.slice(timeSelection.length - 1);
+ timeSelection = `${timeDigits}${timeUnit}`;
+ }
+ chartData[currentRegionSelection] = await fetchData(currentRegionSelection, timeSelection, currentAggregateSelection);
if (!chart.active()) {
await chart.createChart(currentRegionSelection, currentTimeSelection, startYAtZero, chartData[currentRegionSelection]);
}
else {
for (let i = 0; i < chartData[currentRegionSelection].length; i++) {
await chart.addDataToChart(chartData[currentRegionSelection][i]);
- console.warn("This should never hit, and should be okay to remove");
}
+ console.warn("This should never hit, and should be okay to remove");
}
removeLoader();
}
@@ -151,6 +167,9 @@ function detectTimeQuery(urlSearchParams) {
const validTimes = ['72h', '168h', '336h', '720h', '30d', '2190h', '90d', '1y', '2y', '6m', 'all'];
if (validTimes.includes(urlSearchParams.get('time').toLowerCase())) {
currentTimeSelection = urlSearchParams.get('time').toLowerCase();
+ if (currentTimeSelection === 'all') {
+ forceOverlayOff();
+ }
let timeDDL = document.getElementById('time');
for (let i = 0; i < timeDDL.options.length; i++) {
if (timeDDL.options[i].value === currentTimeSelection) {
@@ -249,6 +268,8 @@ function registerEventHandles() {
registerAdvancedHandlers();
}
+// TODO: These need to be moved out into probably tokenChart if not other files
+
function registerAdvancedHandlers() {
document.getElementById('enable-advanced').addEventListener('change', () => {
toggleAdvancedSetting();
@@ -256,6 +277,9 @@ function registerAdvancedHandlers() {
document.getElementById('y-start').addEventListener('change', () => {
toggleStartYAtZero();
})
+ document.getElementById('period-overlay').addEventListener('change', () => {
+ toggleOverlay();
+ })
}
function registerCopyHandlers() {
diff --git a/src/overlay.js b/src/overlay.js
@@ -0,0 +1,27 @@
+function isOverlaySelected() {
+ return document.getElementById('period-overlay').checked;
+}
+
+function getOverlayTime() {
+ return document.getElementById("time").selectedOptions[0].innerText;
+}
+
+function setOverlayLabelTime() {
+ const currentTime = document.getElementById("time").selectedOptions[0].innerText;
+ let overlayTimeLabel = document.getElementById("period-time");
+ overlayTimeLabel.innerText = currentTime.toLocaleString();
+}
+
+function forceOverlayOff(){
+ const overlaySetting = document.getElementById("period-overlay");
+ const periodOverlayField = document.getElementById("period-overlay-options");
+ overlaySetting.checked = false;
+ periodOverlayField.style.display = 'none';
+}
+
+function allowOverlay(){
+ const periodOverlayField = document.getElementById("period-overlay-options");
+ periodOverlayField.style.display = 'flex';
+}
+
+export {isOverlaySelected, getOverlayTime, setOverlayLabelTime, forceOverlayOff, allowOverlay};
+\ No newline at end of file
diff --git a/src/tokenChart.js b/src/tokenChart.js
@@ -24,6 +24,7 @@ Chart.register(
import {updateHighVal} from "./highTime";
import {updateLowVal} from "./lowTime";
+import {isOverlaySelected, getOverlayTime, setOverlayLabelTime} from "./overlay";
function lookupTimeUnit(query){
const lookup = {
@@ -36,6 +37,24 @@ function lookupTimeUnit(query){
return lookup[query.charAt(query.length - 1)]
}
+function timeDeltaInMilliseconds(time) {
+ let timeDigits = (parseInt(time.slice(0, time.length - 1))).toFixed(0);
+ let timeUnit = time.slice(time.length - 1);
+
+ switch (timeUnit) {
+ case 'h':
+ return timeDigits * (60 * 60) * 1000;
+ case 'd':
+ return timeDigits * (24 * 60 * 60) * 1000;
+ case 'm':
+ return (timeDigits * (30.437 * 24 * 60 * 60)).toFixed(0) * 1000;
+ case 'y':
+ return (timeDigits * (365.25 * 24 * 60 * 60)).toFixed(0) * 1000;
+ case 'l':
+ console.warn("This path should not happen, this warning is an error in logic")
+ }
+}
+
export default class TokenChart {
constructor() {
this._context = document.getElementById("token-chart").getContext('2d');
@@ -54,9 +73,104 @@ export default class TokenChart {
return this._lowDatum;
}
- async createChart(region, time, yLevel, data) {
+ async #newChart(chartConfig) {
+ this._chart = new Chart(this._context, chartConfig);
+ }
+
+ async #createOverlayChart(region, time, yLevel, data){
const chartData = [];
- let lateUpdateData = this._lastDatum;
+ const overlayData = [];
+ const overlayDelta = timeDeltaInMilliseconds(time);
+
+ for (let i = 0; i < data.length; i++) {
+ const originalDate = data[i].getX();
+ if (i < (data.length / 2)) {
+ overlayData.push({
+ x: new Date(originalDate.getTime() + overlayDelta),
+ y: data[i].getY(),
+ });
+ }
+ else {
+
+ this._lastDatum = data[i];
+ if (this._highDatum === null || this._lastDatum.getPrice() > this._highDatum.getPrice()) {
+ this._highDatum = data[i];
+ }
+
+ if (this._lowDatum === null || this._lowDatum.getPrice() > this._lastDatum.getPrice()) {
+ this._lowDatum = data[i];
+ }
+
+ chartData.push({
+ x: data[i].getX(),
+ y: data[i].getY(),
+ })
+ }
+ }
+
+ const chartConfig = {
+ type: 'line',
+ data: {
+ datasets: [
+ {
+ borderColor: 'gold',
+ label: region.toUpperCase() + " WoW Token Price",
+ data: chartData,
+ cubicInterpolationMode: 'monotone',
+ pointRadius: 0
+ },
+ {
+ borderColor: 'red',
+ label: `Previous ${getOverlayTime()} ${region.toUpperCase()} WoW Token Price`,
+ data: overlayData,
+ cubicInterpolationMode: 'monotone',
+ pointRadius: 0
+ }
+ ]
+ },
+ options: {
+ interaction: {
+ intersect: false,
+ mode: "index"
+ },
+ scales: {
+ x: {
+ type: 'time',
+ grid: {
+ color: '#625f62',
+ },
+ ticks: {
+ color: '#a7a4ab',
+ font: {
+ size: 18,
+ }
+ },
+ time: {
+ unit: lookupTimeUnit(time)
+ }
+ },
+ y: {
+ beginAtZero: yLevel,
+ grid: {
+ color: '#2f2c2f',
+ },
+ ticks: {
+ color: '#a7a4ab',
+ font: {
+ size: 18,
+ }
+ }
+ }
+ },
+ }
+ }
+
+ await this.#newChart(chartConfig)
+ }
+
+ async #createNormalChart(region, time, yLevel, data) {
+ const chartData = [];
+
for (let i = 0; i < data.length; i++) {
this._lastDatum = data[i];
@@ -74,10 +188,7 @@ export default class TokenChart {
})
}
- updateHighVal(this.highDatum);
- updateLowVal(this.lowDatum);
-
- this._chart = new Chart(this._context, {
+ const chartConfig = {
type: 'line',
data: {
datasets: [{
@@ -123,7 +234,25 @@ export default class TokenChart {
}
},
}
- });
+ }
+
+ await this.#newChart(chartConfig)
+ }
+
+ async createChart(region, time, yLevel, data) {
+ let lateUpdateData = this._lastDatum;
+
+ if (isOverlaySelected()) {
+ await this.#createOverlayChart(region, time, yLevel, data)
+ }
+ else {
+ await this.#createNormalChart(region, time, yLevel, data)
+ }
+
+ setOverlayLabelTime();
+
+ updateHighVal(this.highDatum);
+ updateLowVal(this.lowDatum);
if (this._lateUpdate) {
if (this._lastDatum.getPrice() !== lateUpdateData.getPrice() &&
@@ -160,12 +289,7 @@ export default class TokenChart {
this._lowDatum = datum;
updateLowVal(this.lowDatum);
}
- this._chart.data.datasets.forEach((dataset) => {
- dataset.data.push({
- x: datum.getX(),
- y: datum.getY(),
- })
- });
+ this._chart.data.datasets[0].data.push(datum);
this._chart.update();
}