Compare commits

14 Commits

Author SHA1 Message Date
d25334d35f Initial implementation of overlay functionality 2024-11-03 22:54:23 -08:00
8c8499fb1c Add backwards compatibility for average changes 2024-11-03 16:29:31 -08:00
0180e8a3b5 Swap to v2 of the historical api 2024-11-01 00:09:19 -07:00
fa058cc64e Swap to v2 of current API 2024-10-31 22:35:11 -07:00
ef95388185 Fix live prices being added to the chart with incorrect price 2024-10-24 18:09:21 -07:00
ad069cb7bf Dependency updates 2024-10-17 00:27:35 -07:00
367b767f52 Remove unused aggregate function toggle 2024-10-17 00:05:37 -07:00
c9e14e265f Flip high and low sections
This is more in line with how English reads, left to right, low to high
2024-10-16 23:58:16 -07:00
5abf6fe132 Adds a high and low price data callout 2024-10-16 23:53:53 -07:00
487bb86a29 Fix late updates to chart 2024-10-14 00:18:07 -07:00
a51d3f8d7b Start the process of splitting JS files
I am a JS noob so this is probably poorly thought out. But it is a wip and I dont really care about the quality, just more the learning experience.
2024-10-13 23:23:31 -07:00
27eb2ccb45 Remove advanced region options for now.
It may come back later, but it's kinda confusing just being disabled
2024-10-12 22:31:26 -07:00
94e08c3657 Revert "Add early reply header to first requests"
This reverts commit fa60c3ea53.
2024-05-26 22:47:18 -07:00
fa60c3ea53 Add early reply header to first requests
This lets my CloudFront function know whether to serve a request from the Lambda@edge function with more body, or an early request from the Cloudfront KeyValueStore. This is an A/B test with retail vs classic site at the time of commit
2024-05-26 11:38:51 -07:00
14 changed files with 964 additions and 633 deletions

812
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,9 +15,9 @@
"webpack-cli": "^4.7.2"
},
"dependencies": {
"chart.js": "^4.4.0",
"chartjs-adapter-dayjs-3": "^1.2.3",
"css-minimizer-webpack-plugin": "^5.0.0",
"dayjs": "^1.11.7"
"chart.js": "^4.4.5",
"chartjs-adapter-dayjs-4": "^1.0.4",
"css-minimizer-webpack-plugin": "^7.0.0",
"dayjs": "^1.11.13"
}
}

22
src/datum.js Normal file
View File

@@ -0,0 +1,22 @@
export default class Datum {
constructor(time, price) {
this._time = time;
this._price = price;
}
getTime() {
return this._time;
}
getPrice() {
return this._price;
}
getX() {
return this.getTime();
}
getY() {
return this.getPrice();
}
}

4
src/fetchCurrent.js Normal file
View File

@@ -0,0 +1,4 @@
export default async function fetchCurrent() {
const resp = await fetch("https://data.wowtoken.app/v2/current/retail.json");
return await resp.json();
}

13
src/fetchData.js Normal file
View File

@@ -0,0 +1,13 @@
import Datum from "./datum";
import urlBuilder from "./urlBuilder";
export default async function fetchData(currentRegionSelection, currentTimeSelection, currentAggregateSelection) {
const data = [];
const resp = await fetch(urlBuilder(currentRegionSelection, currentTimeSelection, currentAggregateSelection));
const respData = await resp.json();
for (let i = 0, l = respData.length; i < l; i++) {
const datum = new Datum(new Date(respData[i][0]), respData[i][1]);
data.push(datum);
}
return data;
}

18
src/highTime.js Normal file
View File

@@ -0,0 +1,18 @@
import Datum from "./datum";
function updateHighTime() {
const highTime= document.getElementById("high-time");
const currentTime = document.getElementById("time").selectedOptions[0].innerText;
if (currentTime.toLowerCase() !== highTime.innerText) {
highTime.innerText = currentTime.toLowerCase();
}
}
function updateHighVal(datum) {
const highVal = document.getElementById("high-val");
highVal.innerHTML = datum.getPrice().toLocaleString();
}
export {updateHighTime, updateHighVal};

View File

@@ -7,12 +7,18 @@
<meta name="description" content="Track current and historical gold price trends for the World of Warcraft (WoW) in game token, including the US, EU, TW, and KR regions. Prices updated every minute. Simple, quick, and easy info, no ads or tracking, ever.">
<link rel="preconnect" href="https://data.wowtoken.app">
<link rel="dns-prefetch" href="https://data.wowtoken.app">
<link rel="preload" href="https://data.wowtoken.app/token/current.json" as="fetch" type="application/json" crossorigin="anonymous">
<link rel="preload" href="https://data.wowtoken.app/token/history/us/72h.json" as="fetch" type="application/json" crossorigin="anonymous">
<link rel="preload" href="https://data.wowtoken.app/v2/current/retail.json" as="fetch" type="application/json" crossorigin="anonymous">
<link rel="preload" href="https://data.wowtoken.app/v2/relative/retail/us/72h.json" as="fetch" type="application/json" crossorigin="anonymous">
</head>
<body>
<div class="flex-container">
<div><h1>1 Token = <u id="token">0</u> Gold</h1></div>
<div class="data-header">
<h1>1 Token = <u id="token">0</u> Gold</h1>
<div class="high-low">
<p>Lowest in last <em id="low-time">3 days</em>: <u id="low-val">0</u></p>
<p>Highest in last <em id="high-time">3 days</em>: <u id="high-val">0</u></p>
</div>
</div>
<div id="chart-frame">
<div class="lds-ripple" id="loader"><div></div><div></div></div>
<canvas id="token-chart"></canvas>
@@ -49,25 +55,19 @@
</fieldset>
<fieldset id="advanced-options">
<legend>Advanced chart options</legend>
<fieldset id="advanced-region-options">
<legend>Multi-Region Selection</legend>
<label for="adv-r-us">US:</label>
<input type="checkbox" id="adv-r-us" name="adv-r-us" value="enable" disabled />
<label for="adv-r-eu">EU:</label>
<input type="checkbox" id="adv-r-eu" name="adv-r-eu" value="enable" disabled />
<label for="adv-r-kr">KR:</label>
<input type="checkbox" id="adv-r-kr" name="adv-r-kr" value="enable" disabled />
<label for="adv-r-tw">TW:</label>
<input type="checkbox" id="adv-r-tw" name="adv-r-tw" value="enable" disabled/>
</fieldset>
<fieldset id="basic-smoothing">
<label for="aggregate">Smoothing Function:</label>
<select name="aggregate" id="aggregate">
<option id='agg_none' value="none">None</option>
<option id='agg_davg' value="daily_mean">Daily Average</option>
<option id='agg_wavg' value="weekly_mean" disabled>Weekly Average</option>
<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"/>

View File

@@ -1,230 +1,99 @@
import {
Chart,
Legend,
LinearScale,
LineController,
LineElement,
PointElement,
TimeSeriesScale,
Title,
Tooltip
} from 'chart.js';
import 'chartjs-adapter-dayjs-3';
import 'chartjs-adapter-dayjs-4';
import "./style.css"
// TODO: This file should be seperated into multiple with better ownership
import fetchCurrent from "./fetchCurrent";
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";
Chart.register(
LineElement,
PointElement,
LineController,
LinearScale,
TimeSeriesScale,
Legend,
Title,
Tooltip
)
// TODO: This file should be seperated into multiple with better ownership
let currentRegionSelection = '';
let currentTimeSelection = '';
let currentAggregateSelection = '';
let startYAtZero = false;
let datum;
let chart;
const currentPriceHash = {
us: 0,
eu: 0,
kr: 0,
tw: 0
};
let chartData = {
const chartData = {
us: [],
eu: [],
kr: [],
tw: []
}
let chartOptions = {
us: {
color: 'gold'
},
eu: {
color: 'red'
},
kr: {
color: 'white'
},
tw: {
color: 'pink'
}
}
let chartJsData;
let ctx;
let tokenChart;
function populateChart() {
ctx = document.getElementById("token-chart").getContext('2d');
tokenChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
borderColor: 'gold',
label: currentRegionSelection.toUpperCase() + " WoW Token Price",
data: chartJsData,
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(currentTimeSelection)
}
},
y: {
beginAtZero: startYAtZero,
grid: {
color: '#2f2c2f',
},
ticks: {
color: '#a7a4ab',
font: {
size: 18,
}
}
}
},
}
});
}
function lookupTimeUnit(query){
const lookup = {
'h': 'day',
'd': 'week',
'm': 'month',
'y': 'month',
'l': 'year'
}
return lookup[query.charAt(query.length - 1)]
}
async function callUpdateURL() {
let resp = await fetch("https://data.wowtoken.app/token/current.json");
let data = await resp.json();
updateTokens(data);
await updateTokens(await fetchCurrent());
}
function updateTokens(data) {
updateRegionalToken('us', data);
updateRegionalToken('eu', data);
updateRegionalToken('kr', data);
updateRegionalToken('tw', data);
async function updateTokens(data) {
await Promise.all([
updateRegionalToken('us', data),
updateRegionalToken('eu', data),
updateRegionalToken('kr', data),
updateRegionalToken('tw', data)
]);
}
function updateRegionalToken(region, data) {
if (currentPriceHash[region] !== data['price_data'][region]) {
currentPriceHash[region] = data['price_data'][region];
async function updateRegionalToken(region, data) {
if (currentPriceHash[region] !== data[region][1]) {
currentPriceHash[region] = data[region][1];
if (region === currentRegionSelection) {
formatToken();
if (currentAggregateSelection === 'none') {
addDataToChart(region, data);
datum = new Datum(Date.parse(data[region][0]), data[region][1]);
if (currentAggregateSelection === 'none' && chart.active()) {
await chart.addDataToChart(datum);
}
else if (currentAggregateSelection === 'none' && !chart.active()) {
await chart.lateUpdate(datum);
}
}
}
}
function addDataToChart(region, data) {
if (tokenChart) {
const datum = {x: data['current_time'], y: data['price_data'][region]}
tokenChart.data.datasets.forEach((dataset) => {
dataset.data.push(datum);
})
tokenChart.update();
}
}
async function aggregateFunctionToggle() {
// TODO: We should probably make these global or something
// so if the need to be updated in the future we can do so easily
const smallTimes = ['72h', '168h', '336h'];
const longTimes = ['720h', '30d', '2190h', '90d', '1y', '2y', '6m', 'all'];
const idsToModify = ['agg_wavg']
if (smallTimes.includes(currentTimeSelection)) {
for (const id of idsToModify) {
let ele = document.getElementById(id);
ele.disabled = true;
}
} else if (longTimes.includes(currentTimeSelection)) {
for (const id of idsToModify) {
let ele = document.getElementById(id);
ele.disabled = false;
}
}
}
function addLoader() {
let loader = document.getElementById('loader');
if (!loader) {
const blank_div = document.createElement('div');
let loaderNode = blank_div.cloneNode();
loaderNode.id = 'loader';
loaderNode.className = 'lds-ripple';
loaderNode.appendChild(blank_div.cloneNode());
loaderNode.appendChild(blank_div.cloneNode());
let chartNode = document.getElementById('token-chart');
chartNode.before(loaderNode);
}
}
function removeLoader () {
let loader = document.getElementById('loader');
if (loader) {
loader.remove();
}
}
function updateRegionPreference(newRegion) {
async function updateRegionPreference(newRegion) {
if (newRegion !== currentRegionSelection) {
tokenChart.destroy();
await chart.destroyChart();
addLoader();
currentRegionSelection = newRegion;
}
formatToken();
pullChartData().then(populateChart);
await pullChartData();
}
function updateTimePreference(newTime) {
async function updateTimePreference(newTime) {
if (newTime !== currentTimeSelection) {
tokenChart.destroy();
await chart.destroyChart();
addLoader();
currentTimeSelection = newTime;
aggregateFunctionToggle();
}
pullChartData().then(populateChart);
if (newTime === "all") {
forceOverlayOff();
}
else {
allowOverlay();
}
await pullChartData();
updateHighTime();
updateLowTime();
}
function updateAggregatePreference(newAggregate) {
async function updateAggregatePreference(newAggregate) {
if (newAggregate !== currentAggregateSelection) {
tokenChart.destroy();
await chart.destroyChart();
addLoader();
currentAggregateSelection = newAggregate;
}
pullChartData().then(populateChart);
await pullChartData();
}
function toggleAdvancedSetting() {
@@ -241,33 +110,32 @@ function toggleAdvancedSetting() {
function toggleStartYAtZero(){
startYAtZero = document.getElementById('y-start').checked;
if (tokenChart){
tokenChart.options.scales.y.beginAtZero = startYAtZero;
tokenChart.update();
}
chart.toggleYStart(startYAtZero);
}
function urlBuilder() {
let url = "https://data.wowtoken.app/token/history/";
if (currentAggregateSelection !== 'none') {
url += `${currentAggregateSelection}/`
}
url += `${currentRegionSelection}/${currentTimeSelection}.json`
return url;
async function toggleOverlay() {
await chart.destroyChart();
addLoader();
await pullChartData();
}
async function pullChartData() {
let resp = await fetch(urlBuilder());
let chartData = await resp.json();
let newChartJSData = [];
for (let i = 0; i < chartData.length; i++) {
let datum = {
x: chartData[i]['time'],
y: chartData[i]['value']
};
newChartJSData.push(datum);
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");
}
chartJsData = newChartJSData;
removeLoader();
}
@@ -299,28 +167,36 @@ 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) {
timeDDL.options[i].selected = true;
}
}
updateHighTime();
updateLowTime();
} else {
console.warn("An incorrect or malformed time selection was made in the query string");
}
}
function detectAggregateQuery(urlSearchParams) {
const validOperations = ['none', 'daily_mean', 'weekly_mean'];
const validOperations = ['none', 'daily_mean', 'avg'];
if (validOperations.includes(urlSearchParams.get('aggregate').toLowerCase())) {
currentAggregateSelection = urlSearchParams.get('aggregate').toLowerCase();
// For backwards compatibility
if (currentAggregateSelection === 'daily_mean') {
currentAggregateSelection = 'avg';
}
let aggregateDDL = document.getElementById('aggregate');
for (let i = 0; i < aggregateDDL.options.length; i++) {
if (aggregateDDL.options[i].value === currentAggregateSelection) {
aggregateDDL.options[i].selected = true;
}
}
aggregateFunctionToggle();
} else {
console.warn("An incorrect or malformed aggregate selection was made in the query string");
}
@@ -392,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();
@@ -399,6 +277,9 @@ function registerAdvancedHandlers() {
document.getElementById('y-start').addEventListener('change', () => {
toggleStartYAtZero();
})
document.getElementById('period-overlay').addEventListener('change', () => {
toggleOverlay();
})
}
function registerCopyHandlers() {
@@ -428,7 +309,11 @@ function registerOptionHandlers() {
document.addEventListener('DOMContentLoaded', function () {
registerEventHandles();
detectURLQuery();
Promise.all([callUpdateURL(), pullChartData()]).then(populateChart)
chart = new TokenChart();
Promise.all([
callUpdateURL(),
]).then(pullChartData);
setInterval(callUpdateURL, 60*1000);
}, false);

22
src/loader.js Normal file
View File

@@ -0,0 +1,22 @@
function addLoader() {
let loader = document.getElementById('loader');
if (!loader) {
const blank_div = document.createElement('div');
let loaderNode = blank_div.cloneNode();
loaderNode.id = 'loader';
loaderNode.className = 'lds-ripple';
loaderNode.appendChild(blank_div.cloneNode());
loaderNode.appendChild(blank_div.cloneNode());
let chartNode = document.getElementById('token-chart');
chartNode.before(loaderNode);
}
}
function removeLoader () {
let loader = document.getElementById('loader');
if (loader) {
loader.remove();
}
}
export {addLoader, removeLoader};

17
src/lowTime.js Normal file
View File

@@ -0,0 +1,17 @@
import Datum from "./datum";
function updateLowTime() {
const lowTime= document.getElementById("low-time");
const currentTime = document.getElementById("time").selectedOptions[0].innerText;
if (currentTime.toLowerCase() !== lowTime.innerText) {
lowTime.innerText = currentTime.toLowerCase();
}
}
function updateLowVal(datum) {
const lowVal = document.getElementById("low-val");
lowVal.innerHTML = datum.getPrice().toLocaleString();
}
export {updateLowTime, updateLowVal};

27
src/overlay.js Normal file
View File

@@ -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};

View File

@@ -367,6 +367,7 @@ details[open] summary {
margin-bottom: 0.5em;
}
#option_select {
font-size: 20px;
line-height: 40px;
@@ -419,6 +420,21 @@ details[open] summary {
margin: 10px;
}
.data-header h1 {
margin-top: 24px;
margin-bottom: 24px;
}
.high-low {
display: flex;
justify-content: space-evenly;
}
.high-low p {
line-height: 1em;
font-size: 24px;
}
.lds-ripple {
position: relative;
align-self: center;

305
src/tokenChart.js Normal file
View File

@@ -0,0 +1,305 @@
import {
Chart,
Legend,
LinearScale,
LineController,
LineElement,
PointElement,
TimeSeriesScale,
Title,
Tooltip
} from 'chart.js';
import 'chartjs-adapter-dayjs-4';
Chart.register(
LineElement,
PointElement,
LineController,
LinearScale,
TimeSeriesScale,
Legend,
Title,
Tooltip
)
import {updateHighVal} from "./highTime";
import {updateLowVal} from "./lowTime";
import {isOverlaySelected, getOverlayTime, setOverlayLabelTime} from "./overlay";
function lookupTimeUnit(query){
const lookup = {
'h': 'day',
'd': 'week',
'm': 'month',
'y': 'month',
'l': 'year'
}
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');
this._chartActive = false;
this._lastDatum = null;
this._highDatum = null;
this._lowDatum = null;
this._lateUpdate = false;
}
get highDatum() {
return this._highDatum;
}
get lowDatum() {
return this._lowDatum;
}
async #newChart(chartConfig) {
this._chart = new Chart(this._context, chartConfig);
}
async #createOverlayChart(region, time, yLevel, data){
const chartData = [];
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];
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
}]
},
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 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() &&
this._lastDatum.getTime() < lateUpdateData.getTime()) {
await this.addDataToChart(lateUpdateData);
}
this._lateUpdate = false
}
this._chartActive = true;
}
async destroyChart() {
await this._chart.destroy();
this._chartActive = false;
this._lastDatum = null;
this._highDatum = null;
this._lowDatum = null;
this._lateUpdate = false;
}
async lateUpdate(datum){
this._lastDatum = datum;
this._lateUpdate = true;
}
async addDataToChart(datum) {
this._lastDatum = datum;
if (datum.getPrice() > this._highDatum.getPrice()) {
this._highDatum = datum;
updateHighVal(this.highDatum);
}
else if (datum.getPrice() < this._lowDatum.getPrice()) {
this._lowDatum = datum;
updateLowVal(this.lowDatum);
}
this._chart.data.datasets[0].data.push(datum);
this._chart.update();
}
active() {
return this._chartActive;
}
toggleYStart(startYAtZero) {
this._chart.options.scales.y.beginAtZero = startYAtZero;
this._chart.update();
}
}

12
src/urlBuilder.js Normal file
View File

@@ -0,0 +1,12 @@
export default function urlBuilder(currentRegionSelection, currentTimeSelection, currentAggregateSelection) {
let url = "https://data.wowtoken.app/v2/";
if (currentAggregateSelection !== '' && currentAggregateSelection !== 'none'){
url += `math/${currentAggregateSelection}/retail/`
}
else {
url += `relative/retail/`
}
url += `${currentRegionSelection}/${currentTimeSelection}.json`;
return url;
}