Compare commits
19 Commits
classic
...
ef95388185
| Author | SHA1 | Date | |
|---|---|---|---|
| ef95388185 | |||
| ad069cb7bf | |||
| 367b767f52 | |||
| c9e14e265f | |||
| 5abf6fe132 | |||
| 487bb86a29 | |||
| a51d3f8d7b | |||
| 27eb2ccb45 | |||
| 94e08c3657 | |||
| fa60c3ea53 | |||
| 1e0b4a0a1f | |||
| 17ffbc3db1 | |||
| 0c9e7ed183 | |||
| 4a7c03307d | |||
| 0e40f403e4 | |||
| 66ab042995 | |||
| 8cd9a2ddeb | |||
| 702ca8c4d3 | |||
| c1b05851a4 |
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
These are the public assets for the [wowtoken.app](https://wowtoken.app) website, served off Amazon S3 behind CloudFront.
|
These are the public assets for the [wowtoken.app](https://wowtoken.app) website, served off Amazon S3 behind CloudFront.
|
||||||
|
|
||||||
This project gets picked up by CodePipline, built via CodeBuild, and deployed.
|
This project gets picked up by CodePipeline, built via CodeBuild, and deployed.
|
||||||
|
|
||||||

|

|
||||||
1334
package-lock.json
generated
1334
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -15,10 +15,9 @@
|
|||||||
"webpack-cli": "^4.7.2"
|
"webpack-cli": "^4.7.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cash-dom": "^8.1.5",
|
"chart.js": "^4.4.5",
|
||||||
"chart.js": "^4.3.0",
|
"chartjs-adapter-dayjs-4": "^1.0.4",
|
||||||
"chartjs-adapter-dayjs-3": "^1.2.3",
|
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||||
"css-minimizer-webpack-plugin": "^5.0.0",
|
"dayjs": "^1.11.13"
|
||||||
"dayjs": "^1.11.7"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/datum.js
Normal file
22
src/datum.js
Normal 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
4
src/fetchCurrent.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default async function fetchCurrent() {
|
||||||
|
const resp = await fetch("https://data.wowtoken.app/token/current.json");
|
||||||
|
return await resp.json();
|
||||||
|
}
|
||||||
13
src/fetchData.js
Normal file
13
src/fetchData.js
Normal 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
18
src/highTime.js
Normal 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};
|
||||||
@@ -12,12 +12,20 @@
|
|||||||
</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 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>
|
||||||
<div id="option_select">
|
<div id="option_select">
|
||||||
<p>
|
<fieldset id="basic-options">
|
||||||
|
<legend>Basic chart options</legend>
|
||||||
|
<div>
|
||||||
<label for="region">Region:</label>
|
<label for="region">Region:</label>
|
||||||
<select name="region" id="region">
|
<select name="region" id="region">
|
||||||
<option value="us">US</option>
|
<option value="us">US</option>
|
||||||
@@ -25,8 +33,8 @@
|
|||||||
<option value="kr">KR</option>
|
<option value="kr">KR</option>
|
||||||
<option value="tw">TW</option>
|
<option value="tw">TW</option>
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</div>
|
||||||
<p>
|
<div>
|
||||||
<label for="time">Time Selection:</label>
|
<label for="time">Time Selection:</label>
|
||||||
<select name="time" id="time">
|
<select name="time" id="time">
|
||||||
<option value="72h">3 Days</option>
|
<option value="72h">3 Days</option>
|
||||||
@@ -39,30 +47,37 @@
|
|||||||
<option value="2y">2 Years</option>
|
<option value="2y">2 Years</option>
|
||||||
<option value="all">All Available</option>
|
<option value="all">All Available</option>
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="enable-advanced">Enable advanced charting options:</label>
|
||||||
|
<input type="checkbox" id="enable-advanced" name="enable-advanced" />
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset id="advanced-options">
|
||||||
|
<legend>Advanced chart options</legend>
|
||||||
|
<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>
|
||||||
|
</select>
|
||||||
|
</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"/>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
<div class="tooltip">
|
<div class="tooltip">
|
||||||
<button id="copyURLButton">
|
<button id="copyURLButton">
|
||||||
<span class="tooltiptext" id="urlTooltip">Copy to clipboard</span>
|
<span class="tooltiptext" id="urlTooltip">Copy to clipboard</span>
|
||||||
Copy URL to this Chart
|
Copy URL to this Chart
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<p><em>Looking for the classic WoW Token price? Find it <a href="https://classic.wowtoken.app">here!</a></em></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<details id="advanced">
|
|
||||||
<summary>Advanced Options</summary>
|
|
||||||
<p>
|
|
||||||
<label for="aggregate">Aggregate Function:</label>
|
|
||||||
<select name="aggregate" id="aggregate">
|
|
||||||
<option id='agg_none' value="none">None</option>
|
|
||||||
<option id='agg_dmax' value="daily_max">Daily Maximum</option>
|
|
||||||
<option id='agg_dmin' value="daily_min">Daily Minimum</option>
|
|
||||||
<option id='agg_davg' value="daily_mean">Daily Average</option>
|
|
||||||
<option id='agg_wmax' value="weekly_max" disabled>Weekly Maximum</option>
|
|
||||||
<option id='agg_wmin' value="weekly_min" disabled>Weekly Minimum</option>
|
|
||||||
<option id='agg_wavg' value="weekly_mean" disabled>Weekly Average</option>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<em>More coming soon™</em>
|
|
||||||
</details>
|
|
||||||
<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 Token from various
|
||||||
|
|||||||
290
src/index.js
290
src/index.js
@@ -1,201 +1,130 @@
|
|||||||
import {
|
import 'chartjs-adapter-dayjs-4';
|
||||||
Chart,
|
|
||||||
Legend,
|
|
||||||
LinearScale,
|
|
||||||
LineController,
|
|
||||||
LineElement,
|
|
||||||
PointElement,
|
|
||||||
TimeSeriesScale,
|
|
||||||
Title,
|
|
||||||
Tooltip
|
|
||||||
} from 'chart.js';
|
|
||||||
import $ from 'cash-dom';
|
|
||||||
import 'chartjs-adapter-dayjs-3';
|
|
||||||
import "./style.css"
|
import "./style.css"
|
||||||
|
|
||||||
Chart.register(
|
import fetchCurrent from "./fetchCurrent";
|
||||||
LineElement,
|
import fetchData from "./fetchData";
|
||||||
PointElement,
|
import {updateHighTime} from "./highTime";
|
||||||
LineController,
|
import {updateLowTime} from "./lowTime";
|
||||||
LinearScale,
|
import {addLoader, removeLoader} from "./loader";
|
||||||
TimeSeriesScale,
|
import TokenChart from "./tokenChart";
|
||||||
Legend,
|
import Datum from "./datum";
|
||||||
Title,
|
|
||||||
Tooltip
|
// TODO: This file should be seperated into multiple with better ownership
|
||||||
)
|
|
||||||
|
|
||||||
let currentRegionSelection = '';
|
let currentRegionSelection = '';
|
||||||
let currentTimeSelection = '';
|
let currentTimeSelection = '';
|
||||||
let currentAggregateSelection = '';
|
let currentAggregateSelection = '';
|
||||||
|
let startYAtZero = false;
|
||||||
|
let datum;
|
||||||
|
let chart;
|
||||||
const currentPriceHash = {
|
const currentPriceHash = {
|
||||||
us: 0,
|
us: 0,
|
||||||
eu: 0,
|
eu: 0,
|
||||||
kr: 0,
|
kr: 0,
|
||||||
tw: 0
|
tw: 0
|
||||||
};
|
};
|
||||||
let chartJsData;
|
const chartData = {
|
||||||
let ctx;
|
us: [],
|
||||||
let tokenChart;
|
eu: [],
|
||||||
|
kr: [],
|
||||||
|
tw: []
|
||||||
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'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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') {
|
datum = new Datum(Date.parse(data['update_times'][region]), 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_wmax', 'agg_wmin', '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 urlBuilder() {
|
function toggleAdvancedSetting() {
|
||||||
let url = "https://data.wowtoken.app/token/history/";
|
let element = document.getElementById('advanced-options')
|
||||||
if (currentAggregateSelection !== 'none') {
|
if (document.getElementById('enable-advanced').checked)
|
||||||
url += `${currentAggregateSelection}/`
|
{
|
||||||
|
element.style.display = 'flex';
|
||||||
}
|
}
|
||||||
url += `${currentRegionSelection}/${currentTimeSelection}.json`
|
else
|
||||||
return url;
|
{
|
||||||
|
element.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleStartYAtZero(){
|
||||||
|
startYAtZero = document.getElementById('y-start').checked;
|
||||||
|
chart.toggleYStart(startYAtZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatToken() {
|
function formatToken() {
|
||||||
$("#token").html(currentPriceHash[currentRegionSelection].toLocaleString());
|
document.getElementById("token").innerText = currentPriceHash[currentRegionSelection].toLocaleString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: These maybe able to be collapsed into a single function with params or a lambda
|
// TODO: These maybe able to be collapsed into a single function with params or a lambda
|
||||||
@@ -211,7 +140,7 @@ function detectRegionQuery(urlSearchParams) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("An incorrect or malformed region selection was made in the query string");
|
console.warn("An incorrect or malformed region selection was made in the query string");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,13 +157,15 @@ function detectTimeQuery(urlSearchParams) {
|
|||||||
timeDDL.options[i].selected = true;
|
timeDDL.options[i].selected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updateHighTime();
|
||||||
|
updateLowTime();
|
||||||
} else {
|
} else {
|
||||||
console.log("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_max', 'daily_min', 'daily_mean', 'weekly_max', 'weekly_min', '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');
|
||||||
@@ -243,12 +174,21 @@ function detectAggregateQuery(urlSearchParams) {
|
|||||||
aggregateDDL.options[i].selected = true;
|
aggregateDDL.options[i].selected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
aggregateFunctionToggle();
|
|
||||||
} else {
|
} else {
|
||||||
console.log("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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function detectZeroQuery(urlSearchParams) {
|
||||||
|
startYAtZero = urlSearchParams.get('startAtZero') === 'true';
|
||||||
|
let advOptions = document.getElementById('enable-advanced');
|
||||||
|
let startAtZeroOption = document.getElementById('y-start');
|
||||||
|
advOptions.checked = startYAtZero;
|
||||||
|
startAtZeroOption.checked = startYAtZero;
|
||||||
|
toggleAdvancedSetting();
|
||||||
|
toggleStartYAtZero();
|
||||||
|
}
|
||||||
|
|
||||||
function detectURLQuery() {
|
function detectURLQuery() {
|
||||||
const urlSearchParams = new URLSearchParams(window.location.search);
|
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||||
if (urlSearchParams.has('region')) {
|
if (urlSearchParams.has('region')) {
|
||||||
@@ -260,6 +200,9 @@ function detectURLQuery() {
|
|||||||
if (urlSearchParams.has('aggregate')) {
|
if (urlSearchParams.has('aggregate')) {
|
||||||
detectAggregateQuery(urlSearchParams);
|
detectAggregateQuery(urlSearchParams);
|
||||||
}
|
}
|
||||||
|
if (urlSearchParams.has('startAtZero')) {
|
||||||
|
detectZeroQuery(urlSearchParams)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildDeepLinksURL() {
|
function buildDeepLinksURL() {
|
||||||
@@ -271,7 +214,10 @@ function buildDeepLinksURL() {
|
|||||||
url += `region=${currentRegionSelection}&`
|
url += `region=${currentRegionSelection}&`
|
||||||
}
|
}
|
||||||
if (currentAggregateSelection !== '' && currentAggregateSelection !== 'none'){
|
if (currentAggregateSelection !== '' && currentAggregateSelection !== 'none'){
|
||||||
url += `aggregate=${currentAggregateSelection}`
|
url += `aggregate=${currentAggregateSelection}&`
|
||||||
|
}
|
||||||
|
if (startYAtZero !== false){
|
||||||
|
url += `startAtZero=${startYAtZero}&`
|
||||||
}
|
}
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
@@ -293,7 +239,31 @@ function toolTipMouseOut() {
|
|||||||
tooltip.innerHTML = "Copy to clipboard";
|
tooltip.innerHTML = "Copy to clipboard";
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
function registerEventHandles() {
|
||||||
|
registerCopyHandlers();
|
||||||
|
registerOptionHandlers();
|
||||||
|
registerAdvancedHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerAdvancedHandlers() {
|
||||||
|
document.getElementById('enable-advanced').addEventListener('change', () => {
|
||||||
|
toggleAdvancedSetting();
|
||||||
|
})
|
||||||
|
document.getElementById('y-start').addEventListener('change', () => {
|
||||||
|
toggleStartYAtZero();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerCopyHandlers() {
|
||||||
|
document.getElementById('copyURLButton').addEventListener('click', function () {
|
||||||
|
copyURL();
|
||||||
|
})
|
||||||
|
document.getElementById('copyURLButton').addEventListener('mouseout', function () {
|
||||||
|
toolTipMouseOut();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerOptionHandlers() {
|
||||||
document.getElementById('region').addEventListener('change', function() {
|
document.getElementById('region').addEventListener('change', function() {
|
||||||
updateRegionPreference(this.value);
|
updateRegionPreference(this.value);
|
||||||
});
|
});
|
||||||
@@ -305,15 +275,17 @@ $(document).ready(function() {
|
|||||||
document.getElementById('aggregate').addEventListener('change', function () {
|
document.getElementById('aggregate').addEventListener('change', function () {
|
||||||
updateAggregatePreference(this.value);
|
updateAggregatePreference(this.value);
|
||||||
})
|
})
|
||||||
document.getElementById('copyURLButton').addEventListener('click', function (event) {
|
|
||||||
copyURL();
|
|
||||||
})
|
|
||||||
document.getElementById('copyURLButton').addEventListener('mouseout', function (event) {
|
|
||||||
toolTipMouseOut();
|
|
||||||
})
|
|
||||||
currentAggregateSelection = document.getElementById('aggregate').value;
|
currentAggregateSelection = document.getElementById('aggregate').value;
|
||||||
detectURLQuery();
|
}
|
||||||
Promise.all([callUpdateURL(), pullChartData()]).then(populateChart)
|
|
||||||
setInterval(callUpdateURL, 60*1000);
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
});
|
registerEventHandles();
|
||||||
|
detectURLQuery();
|
||||||
|
chart = new TokenChart();
|
||||||
|
Promise.all([
|
||||||
|
callUpdateURL(),
|
||||||
|
]).then(pullChartData);
|
||||||
|
|
||||||
|
setInterval(callUpdateURL, 60*1000);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
|||||||
22
src/loader.js
Normal file
22
src/loader.js
Normal 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
17
src/lowTime.js
Normal 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};
|
||||||
@@ -137,9 +137,6 @@ input[type="radio"] {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
input[type="search"] {
|
input[type="search"] {
|
||||||
-webkit-appearance: textfield;
|
|
||||||
-moz-box-sizing: content-box;
|
|
||||||
-webkit-box-sizing: content-box;
|
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
}
|
}
|
||||||
input[type="search"]::-webkit-search-cancel-button,
|
input[type="search"]::-webkit-search-cancel-button,
|
||||||
@@ -318,9 +315,28 @@ h6 {
|
|||||||
line-height: 75px;
|
line-height: 75px;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
}
|
}
|
||||||
|
.adv-options-container {
|
||||||
|
display: flex;
|
||||||
|
padding: 20px;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
.box > div {
|
||||||
|
border: solid #bfbdbf;
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 20px;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
p {
|
p {
|
||||||
margin: 0px;
|
margin: 0;
|
||||||
padding: 0px;
|
padding: 0;
|
||||||
line-height: 3em;
|
line-height: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,8 +367,23 @@ details[open] summary {
|
|||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#option_select {
|
#option_select {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
line-height: 40px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#option_select > div {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#option_select > fieldset {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
max-width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
#token {
|
#token {
|
||||||
@@ -378,6 +409,32 @@ details[open] summary {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#advanced-options {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#advanced-options > fieldset {
|
||||||
|
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;
|
||||||
@@ -456,8 +513,8 @@ details[open] summary {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
top: 0px;
|
top: 0;
|
||||||
left: 0px;
|
left: 0;
|
||||||
width: 72px;
|
width: 72px;
|
||||||
height: 72px;
|
height: 72px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
181
src/tokenChart.js
Normal file
181
src/tokenChart.js
Normal 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
8
src/urlBuilder.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export default function urlBuilder(currentRegionSelection, currentTimeSelection, currentAggregateSelection) {
|
||||||
|
let url = "https://data.wowtoken.app/token/history/";
|
||||||
|
if (currentAggregateSelection !== 'none') {
|
||||||
|
url += `${currentAggregateSelection}/`
|
||||||
|
}
|
||||||
|
url += `${currentRegionSelection}/${currentTimeSelection}.json`
|
||||||
|
return url;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user