Compare commits

23 Commits

Author SHA1 Message Date
13ed6dfdce Dependency updates
(cherry picked from commit ad069cb7bf)
2024-10-21 19:59:39 -07:00
b8d11b3815 Remove unused aggregate function toggle
(cherry picked from commit 367b767f52)
2024-10-21 19:59:34 -07:00
2e020633b5 Flip high and low sections
This is more in line with how English reads, left to right, low to high

(cherry picked from commit c9e14e265f)
2024-10-21 19:59:29 -07:00
d07eec1cf0 Adds a high and low price data callout
(cherry picked from commit 5abf6fe132)
2024-10-21 19:59:14 -07:00
de6621e81c Fix late updates to chart
(cherry picked from commit 487bb86a29)
2024-10-21 19:56:51 -07:00
7a17bb8821 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.

(cherry picked from commit a51d3f8d7b)
2024-10-21 19:53:52 -07:00
f207e36ccd Remove advanced region options for now.
It may come back later, but it's kinda confusing just being disabled

(cherry picked from commit 27eb2ccb45)
2024-10-21 19:48:19 -07:00
9ac97425c7 Make time and cost delineation more apparent
(cherry picked from commit 1e0b4a0a1f)
2024-04-25 00:18:47 -07:00
f7a27f9350 Enabled the option to start Y at zero
(cherry picked from commit 17ffbc3db1)
2024-04-24 23:54:53 -07:00
4c77828d5a Adjust all legend for classic since there is less data 2024-04-12 05:29:54 -07:00
50787c1d83 Change the timescale legend to be more readable instead of dense
(cherry picked from commit 0c9e7ed183)
2024-04-12 05:28:30 -07:00
09ec5f0d4d Change logs to warnings, since that's what they really are
(cherry picked from commit 4a7c03307d)
2024-04-12 05:08:39 -07:00
36ba83d9be Increase font size and brightness for better contrast
(cherry picked from commit 0e40f403e4)
2024-04-12 05:08:34 -07:00
8f6cbbc75d Fix bad merge from cherry-pick of main 2023-10-15 01:35:26 -07:00
354381936d Changes to advanced options and styling in prep for future work 2023-10-15 01:27:40 -07:00
214910aa25 Remove Cash dependency, refactor event registration
(cherry picked from commit 8cd9a2ddeb)
2023-10-15 01:19:47 -07:00
ac12a9c55d Update dependencies
(cherry picked from commit 702ca8c4d3)
2023-10-15 01:18:30 -07:00
fa21b50b28 Remove weekly aggregation options for now 2023-06-05 19:46:47 -07:00
ae789181f4 Enable 1 month 2023-06-05 19:17:10 -07:00
a1b1dc63ff Remove extraneous time selections, enable 14d 2023-05-30 03:49:54 -07:00
249f9e281f Textual updates 2023-05-23 19:14:30 -07:00
bda9edf3df Bump node version 2023-05-23 18:27:53 -07:00
e090a93605 Initial branch and changes for Classic token 2023-05-23 18:19:06 -07:00
14 changed files with 791 additions and 653 deletions

View File

@@ -3,7 +3,7 @@ version: 0.2
phases: phases:
install: install:
runtime-versions: runtime-versions:
nodejs: 16 nodejs: 18
commands: commands:
- echo Installing dependencies... - echo Installing dependencies...
- npm install - npm install

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" "webpack-cli": "^4.7.2"
}, },
"dependencies": { "dependencies": {
"chart.js": "^4.4.0", "chart.js": "^4.4.5",
"chartjs-adapter-dayjs-3": "^1.2.3", "chartjs-adapter-dayjs-4": "^1.0.4",
"css-minimizer-webpack-plugin": "^5.0.0", "css-minimizer-webpack-plugin": "^7.0.0",
"dayjs": "^1.11.7" "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/classic/token/current.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++) {
let datum = new Datum(Date.parse(respData[i]['time']), respData[i]['value']);
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

@@ -1,18 +1,24 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<title>WoW Historical Token Prices Tracker</title> <title>WoW Classic Historical Token Prices Tracker</title>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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."> <meta name="description" content="Track current and historical gold price trends for the World of Warcraft (WoW) Classic 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="preconnect" href="https://data.wowtoken.app">
<link rel="dns-prefetch" 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/classic/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/classic/token/history/us/72h.json" as="fetch" type="application/json" crossorigin="anonymous">
</head> </head>
<body> <body>
<div class="flex-container"> <div class="flex-container">
<div><h1>1 Token = <u id="token">0</u> Gold</h1></div> <div class="data-header">
<h1>1 Classic 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 id="chart-frame">
<div class="lds-ripple" id="loader"><div></div><div></div></div> <div class="lds-ripple" id="loader"><div></div><div></div></div>
<canvas id="token-chart"></canvas> <canvas id="token-chart"></canvas>
@@ -36,9 +42,6 @@
<option value="336h">14 Days</option> <option value="336h">14 Days</option>
<option value="720h">1 Month</option> <option value="720h">1 Month</option>
<option value="90d">3 Months</option> <option value="90d">3 Months</option>
<option value="6m">6 Months</option>
<option value="1y">1 Year</option>
<option value="2y">2 Years</option>
<option value="all">All Available</option> <option value="all">All Available</option>
</select> </select>
</div> </div>
@@ -49,23 +52,11 @@
</fieldset> </fieldset>
<fieldset id="advanced-options"> <fieldset id="advanced-options">
<legend>Advanced chart options</legend> <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"> <fieldset id="basic-smoothing">
<label for="aggregate">Smoothing Function:</label> <label for="aggregate">Smoothing Function:</label>
<select name="aggregate" id="aggregate"> <select name="aggregate" id="aggregate">
<option id='agg_none' value="none">None</option> <option id='agg_none' value="none">None</option>
<option id='agg_davg' value="daily_mean">Daily Average</option> <option id='agg_davg' value="daily_mean">Daily Average</option>
<option id='agg_wavg' value="weekly_mean" disabled>Weekly Average</option>
</select> </select>
</fieldset> </fieldset>
<fieldset id="y-start-options"> <fieldset id="y-start-options">
@@ -81,12 +72,12 @@
</button> </button>
</div> </div>
<div> <div>
<p><em>Looking for the classic WoW Token price? Find it <a href="https://classic.wowtoken.app">here!</a></em></p> <p><em>Looking for the retail WoW Token price? Find it <a href="https://wowtoken.app">here!</a></em></p>
</div> </div>
</div> </div>
<details id="about"> <details id="about">
<summary>About this Site</summary> <summary>About this Site</summary>
This is a site developed to track the value of the World of Warcraft Token from various This is a site developed to track the value of the World of Warcraft Classic Token from various
regions over time. I developed it because I wanted a quick and simple way to track the regions over time. I developed it because I wanted a quick and simple way to track the
cost without being advertised to or tracked, and to play around with various "serverless" cost without being advertised to or tracked, and to play around with various "serverless"
technologies. As such, my promise to you is never to use any tracking Javascript, and the technologies. As such, my promise to you is never to use any tracking Javascript, and the
@@ -94,10 +85,10 @@
</details> </details>
<details id="what-is"> <details id="what-is">
<summary>What is the WoW Token</summary> <summary>What is the WoW Token</summary>
The World of Warcraft Token is a first-party system developed by Blizzard to allow you The World of Warcraft Classic Token is a first-party system developed by Blizzard to allow you
to either spend currency (local denomination or Blizzard Balance) and convert it to gold to either spend currency (local denomination or Blizzard Balance) and convert it to gold
in retail World of Warcraft, or use gold to buy game time or Blizzard Balance. To find out in classic World of Warcraft, or use gold to buy game time.
more, visit the support article on Blizzard's website To find out more, visit the support article on Blizzard's website
<a href="https://us.battle.net/support/en/article/31218">here</a>. <a href="https://us.battle.net/support/en/article/31218">here</a>.
</details> </details>
<div id="source"> <div id="source">

View File

@@ -1,230 +1,94 @@
import { import 'chartjs-adapter-dayjs-4';
Chart,
Legend,
LinearScale,
LineController,
LineElement,
PointElement,
TimeSeriesScale,
Title,
Tooltip
} from 'chart.js';
import 'chartjs-adapter-dayjs-3';
import "./style.css" 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 TokenChart from "./tokenChart";
import Datum from "./datum";
Chart.register( // TODO: This file should be seperated into multiple with better ownership
LineElement,
PointElement,
LineController,
LinearScale,
TimeSeriesScale,
Legend,
Title,
Tooltip
)
let currentRegionSelection = ''; let currentRegionSelection = '';
let currentTimeSelection = ''; let currentTimeSelection = '';
let currentAggregateSelection = ''; let currentAggregateSelection = '';
let startYAtZero = false; let startYAtZero = false;
let chart;
const currentPriceHash = { const currentPriceHash = {
us: 0, us: 0,
eu: 0, eu: 0,
kr: 0, kr: 0,
tw: 0 tw: 0
}; };
let chartData = { const chartData = {
us: [], us: [],
eu: [], eu: [],
kr: [], kr: [],
tw: [] 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() { async function callUpdateURL() {
let resp = await fetch("https://data.wowtoken.app/token/current.json"); await updateTokens(await fetchCurrent());
let data = await resp.json();
updateTokens(data);
} }
function updateTokens(data) { async function updateTokens(data) {
updateRegionalToken('us', data); await Promise.all([
updateRegionalToken('eu', data); updateRegionalToken('us', data),
updateRegionalToken('kr', data); updateRegionalToken('eu', data),
updateRegionalToken('tw', data); updateRegionalToken('kr', data),
updateRegionalToken('tw', data)
]);
} }
function updateRegionalToken(region, data) { async function updateRegionalToken(region, data) {
if (currentPriceHash[region] !== data['price_data'][region]) { if (currentPriceHash[region] !== data['price_data'][region]) {
currentPriceHash[region] = data['price_data'][region]; currentPriceHash[region] = data['price_data'][region];
if (region === currentRegionSelection) { if (region === currentRegionSelection) {
formatToken(); formatToken();
if (currentAggregateSelection === 'none') { const datum = new Datum(Date.parse(data['current_time']), data['price_data'][region]);
addDataToChart(region, data); if (currentAggregateSelection === 'none' && chart.active()) {
await chart.addDataToChart(datum);
}
else if (currentAggregateSelection === 'none' && !chart.active()) {
await chart.lateUpdate(datum);
} }
} }
} }
} }
function addDataToChart(region, data) { async function updateRegionPreference(newRegion) {
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) {
if (newRegion !== currentRegionSelection) { if (newRegion !== currentRegionSelection) {
tokenChart.destroy(); await chart.destroyChart();
addLoader(); addLoader();
currentRegionSelection = newRegion; currentRegionSelection = newRegion;
} }
formatToken(); formatToken();
pullChartData().then(populateChart); chart = new TokenChart();
await pullChartData();
} }
function updateTimePreference(newTime) { async function updateTimePreference(newTime) {
if (newTime !== currentTimeSelection) { if (newTime !== currentTimeSelection) {
tokenChart.destroy(); await chart.destroyChart();
addLoader(); addLoader();
currentTimeSelection = newTime; currentTimeSelection = newTime;
aggregateFunctionToggle();
} }
pullChartData().then(populateChart); chart = new TokenChart();
await pullChartData();
updateHighTime();
updateLowTime();
} }
function updateAggregatePreference(newAggregate) { async function updateAggregatePreference(newAggregate) {
if (newAggregate !== currentAggregateSelection) { if (newAggregate !== currentAggregateSelection) {
tokenChart.destroy(); await chart.destroyChart();
addLoader(); addLoader();
currentAggregateSelection = newAggregate; currentAggregateSelection = newAggregate;
} }
pullChartData().then(populateChart); chart = new TokenChart();
await pullChartData();
} }
function toggleAdvancedSetting() { function toggleAdvancedSetting() {
@@ -241,33 +105,20 @@ function toggleAdvancedSetting() {
function toggleStartYAtZero(){ function toggleStartYAtZero(){
startYAtZero = document.getElementById('y-start').checked; startYAtZero = document.getElementById('y-start').checked;
if (tokenChart){ chart.toggleYStart(startYAtZero);
tokenChart.options.scales.y.beginAtZero = startYAtZero;
tokenChart.update();
}
}
function urlBuilder() {
let url = "https://data.wowtoken.app/token/history/";
if (currentAggregateSelection !== 'none') {
url += `${currentAggregateSelection}/`
}
url += `${currentRegionSelection}/${currentTimeSelection}.json`
return url;
} }
async function pullChartData() { async function pullChartData() {
let resp = await fetch(urlBuilder()); chartData[currentRegionSelection] = await fetchData(currentRegionSelection, currentTimeSelection, currentAggregateSelection);
let chartData = await resp.json(); if (!chart.active()) {
let newChartJSData = []; await chart.createChart(currentRegionSelection, currentTimeSelection, startYAtZero, chartData[currentRegionSelection]);
for (let i = 0; i < chartData.length; i++) { }
let datum = { else {
x: chartData[i]['time'], for (let i = 0; i < chartData[currentRegionSelection].length; i++) {
y: chartData[i]['value'] await chart.addDataToChart(chartData[currentRegionSelection][i]);
}; console.warn("This should never hit, and should be okay to remove");
newChartJSData.push(datum); }
} }
chartJsData = newChartJSData;
removeLoader(); removeLoader();
} }
@@ -305,13 +156,15 @@ function detectTimeQuery(urlSearchParams) {
timeDDL.options[i].selected = true; timeDDL.options[i].selected = true;
} }
} }
updateHighTime();
updateLowTime();
} else { } else {
console.warn("An incorrect or malformed time selection was made in the query string"); console.warn("An incorrect or malformed time selection was made in the query string");
} }
} }
function detectAggregateQuery(urlSearchParams) { function detectAggregateQuery(urlSearchParams) {
const validOperations = ['none', 'daily_mean', 'weekly_mean']; const validOperations = ['none', 'daily_mean'];
if (validOperations.includes(urlSearchParams.get('aggregate').toLowerCase())) { if (validOperations.includes(urlSearchParams.get('aggregate').toLowerCase())) {
currentAggregateSelection = urlSearchParams.get('aggregate').toLowerCase(); currentAggregateSelection = urlSearchParams.get('aggregate').toLowerCase();
let aggregateDDL = document.getElementById('aggregate'); let aggregateDDL = document.getElementById('aggregate');
@@ -320,7 +173,6 @@ function detectAggregateQuery(urlSearchParams) {
aggregateDDL.options[i].selected = true; aggregateDDL.options[i].selected = true;
} }
} }
aggregateFunctionToggle();
} else { } else {
console.warn("An incorrect or malformed aggregate selection was made in the query string"); console.warn("An incorrect or malformed aggregate selection was made in the query string");
} }
@@ -353,7 +205,7 @@ function detectURLQuery() {
} }
function buildDeepLinksURL() { function buildDeepLinksURL() {
let url = "https://wowtoken.app/?" let url = "https://classic.wowtoken.app/?"
if (currentTimeSelection !== '72h'){ if (currentTimeSelection !== '72h'){
url += `time=${currentTimeSelection}&` url += `time=${currentTimeSelection}&`
} }
@@ -428,7 +280,11 @@ function registerOptionHandlers() {
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
registerEventHandles(); registerEventHandles();
detectURLQuery(); detectURLQuery();
Promise.all([callUpdateURL(), pullChartData()]).then(populateChart) chart = new TokenChart();
Promise.all([
callUpdateURL(),
]).then(pullChartData);
setInterval(callUpdateURL, 60*1000); setInterval(callUpdateURL, 60*1000);
}, false); }, 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};

View File

@@ -173,8 +173,8 @@ h6 {
font-weight: 700; font-weight: 700;
} }
html { html {
background-color: #073642; background-color: #6b4233;
color: #839496; color: #b7b7b7;
margin: 1em; margin: 1em;
} }
/*body { /*body {
@@ -194,7 +194,7 @@ a:hover {
color: #cb4b16; color: #cb4b16;
} }
h1 { h1 {
color: #d33682; color: #ffd5e9;
} }
h2, h2,
h3, h3,
@@ -206,7 +206,7 @@ h6 {
pre { pre {
background-color: #002b36; background-color: #002b36;
color: #839496; color: #839496;
border: 1pt solid #586e75; border: 1pt solid #000000;
padding: 1em; padding: 1em;
box-shadow: 5pt 5pt 8pt #073642; box-shadow: 5pt 5pt 8pt #073642;
} }
@@ -301,10 +301,10 @@ h6 {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
background-color: #002b36; background-color: #2f201e;
margin: 0 auto; margin: 0 auto;
max-width: 85%; max-width: 85%;
border: 1pt solid #586e75; border: 1pt solid #000000;
padding: 1em; padding: 1em;
} }
@@ -341,8 +341,8 @@ p {
} }
details { details {
background-color: #073642; background-color: #6b4233;
border: 1px solid #aaa; border: 1px solid #000000;
border-radius: 4px; border-radius: 4px;
padding: 0.5em 0.5em 0; padding: 0.5em 0.5em 0;
font-size: 17px; font-size: 17px;
@@ -363,10 +363,11 @@ details[open] {
} }
details[open] summary { details[open] summary {
border-bottom: 1px solid #aaa; border-bottom: 1px solid #000000;
margin-bottom: 0.5em; margin-bottom: 0.5em;
} }
#option_select { #option_select {
font-size: 20px; font-size: 20px;
line-height: 40px; line-height: 40px;
@@ -419,6 +420,21 @@ details[open] summary {
margin: 10px; 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 { .lds-ripple {
position: relative; position: relative;
align-self: center; align-self: center;

181
src/tokenChart.js Normal file
View File

@@ -0,0 +1,181 @@
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";
function lookupTimeUnit(query){
const lookup = {
'h': 'day',
'd': 'week',
'm': 'month',
'y': 'month',
'l': 'year'
}
return lookup[query.charAt(query.length - 1)]
}
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 createChart(region, time, yLevel, data) {
const chartData = [];
let lateUpdateData = this._lastDatum;
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(),
})
}
updateHighVal(this.highDatum);
updateLowVal(this.lowDatum);
this._chart = new Chart(this._context, {
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,
}
}
}
},
}
});
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.forEach((dataset) => {
dataset.data.push({
x: datum.getX(),
y: datum.getY(),
})
});
this._chart.update();
}
active() {
return this._chartActive;
}
toggleYStart(startYAtZero) {
this._chart.options.scales.y.beginAtZero = startYAtZero;
this._chart.update();
}
}

8
src/urlBuilder.js Normal file
View File

@@ -0,0 +1,8 @@
export default function urlBuilder(currentRegionSelection, currentTimeSelection, currentAggregateSelection) {
let url = "https://data.wowtoken.app/classic/token/history/";
if (currentAggregateSelection !== 'none') {
url += `${currentAggregateSelection}/`
}
url += `${currentRegionSelection}/${currentTimeSelection}.json`
return url;
}