Compare commits
24 Commits
30f72f91ac
...
feature/ca
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e0b4a0a1f | |||
| 17ffbc3db1 | |||
| 0c9e7ed183 | |||
| 4a7c03307d | |||
| 0e40f403e4 | |||
| 66ab042995 | |||
| 8cd9a2ddeb | |||
| 702ca8c4d3 | |||
| c1b05851a4 | |||
| 86555f5983 | |||
| aab8a9cd12 | |||
| 7df594f22a | |||
| be4678eab1 | |||
| 5c80316d89 | |||
| 69cd58e487 | |||
| 4ce5481fd0 | |||
| 25e9353f22 | |||
| 543b9ba9d2 | |||
| 71b38778f1 | |||
| 6379803a45 | |||
| 11a3fd7fff | |||
| e0b6ea9f8e | |||
| b92f24739b | |||
| 23ca3cbcef |
@@ -2,6 +2,6 @@
|
||||
|
||||
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.
|
||||
|
||||

|
||||
@@ -3,7 +3,7 @@ version: 0.2
|
||||
phases:
|
||||
install:
|
||||
runtime-versions:
|
||||
nodejs: 14
|
||||
nodejs: 16
|
||||
commands:
|
||||
- echo Installing dependencies...
|
||||
- npm install
|
||||
|
||||
3353
package-lock.json
generated
3353
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -4,16 +4,20 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "webpack && mkdir -p dist && cp src/robots.txt src/index.html src/style.css src/favicon.ico dist/"
|
||||
"build": "webpack --mode production && mkdir -p dist && cp src/robots.txt src/style.css src/favicon.ico dist/",
|
||||
"build-dev": "webpack --mode development && mkdir -p dist && cp src/robots.txt src/style.css src/favicon.ico dist/"
|
||||
},
|
||||
"devDependencies": {
|
||||
"css-loader": "^6.7.4",
|
||||
"html-webpack-plugin": "^5.5.1",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack-cli": "^4.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"cash-dom": "^8.1.2",
|
||||
"chart.js": "^4.0.1",
|
||||
"chart.js": "^4.4.0",
|
||||
"chartjs-adapter-dayjs-3": "^1.2.3",
|
||||
"dayjs": "^1.11.6"
|
||||
"css-minimizer-webpack-plugin": "^5.0.0",
|
||||
"dayjs": "^1.11.7"
|
||||
}
|
||||
}
|
||||
|
||||
108
src/index.html
108
src/index.html
@@ -5,12 +5,10 @@
|
||||
<meta charset="UTF-8">
|
||||
<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.">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<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">
|
||||
<script src="bundle.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="flex-container">
|
||||
@@ -19,32 +17,92 @@
|
||||
<div class="lds-ripple" id="loader"><div></div><div></div></div>
|
||||
<canvas id="token-chart"></canvas>
|
||||
<div id="option_select">
|
||||
<label for="region">Region:</label>
|
||||
<select name="region" id="region">
|
||||
<option value="us">US</option>
|
||||
<option value="eu">EU</option>
|
||||
<option value="kr">KR</option>
|
||||
<option value="tw">TW</option>
|
||||
</select>
|
||||
<br />
|
||||
<label for="time">Time Selection:</label>
|
||||
<select name="time" id="time">
|
||||
<option value="72h">3 Days</option>
|
||||
<option value="168h">7 Days</option>
|
||||
<option value="336h">14 Days</option>
|
||||
<option value="720h">1 Month</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>
|
||||
</select>
|
||||
<fieldset id="basic-options">
|
||||
<legend>Basic chart options</legend>
|
||||
<div>
|
||||
<label for="region">Region:</label>
|
||||
<select name="region" id="region">
|
||||
<option value="us">US</option>
|
||||
<option value="eu">EU</option>
|
||||
<option value="kr">KR</option>
|
||||
<option value="tw">TW</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="time">Time Selection:</label>
|
||||
<select name="time" id="time">
|
||||
<option value="72h">3 Days</option>
|
||||
<option value="168h">7 Days</option>
|
||||
<option value="336h">14 Days</option>
|
||||
<option value="720h">1 Month</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>
|
||||
</select>
|
||||
</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="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>
|
||||
</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">
|
||||
<button id="copyURLButton">
|
||||
<span class="tooltiptext" id="urlTooltip">Copy to clipboard</span>
|
||||
Copy URL to this Chart
|
||||
</button>
|
||||
</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>
|
||||
<details id="about">
|
||||
<summary>About this Site</summary>
|
||||
This is a site developed to track the value of the World of Warcraft Token from various
|
||||
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"
|
||||
technologies. As such, my promise to you is never to use any tracking Javascript, and the
|
||||
only logging is for debugging purposes of the backend - which does not get IPs.
|
||||
</details>
|
||||
<details id="what-is">
|
||||
<summary>What is the WoW Token</summary>
|
||||
The World of Warcraft 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
|
||||
in retail World of Warcraft, or use gold to buy game time or Blizzard Balance. To find out
|
||||
more, visit the support article on Blizzard's website
|
||||
<a href="https://us.battle.net/support/en/article/31218">here</a>.
|
||||
</details>
|
||||
<div id="source">
|
||||
<p>
|
||||
<a href="https://github.com/sneaky-emily/wowtoken.app">Source</a>
|
||||
|
|
||||
<a href="https://blog.emily.sh/2021/04/developing-a-simple-wow-token-tracker/">About</a>
|
||||
<a href="https://blog.emily.sh/wowtoken-app/">Source</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
369
src/index.js
369
src/index.js
@@ -1,17 +1,18 @@
|
||||
import {
|
||||
Chart,
|
||||
Legend,
|
||||
LinearScale,
|
||||
LineController,
|
||||
LineElement,
|
||||
PointElement,
|
||||
LineController,
|
||||
LinearScale,
|
||||
TimeSeriesScale,
|
||||
Legend,
|
||||
Title,
|
||||
Tooltip
|
||||
} from 'chart.js';
|
||||
import $ from 'cash-dom';
|
||||
import 'chartjs-adapter-dayjs-3';
|
||||
import "./style.css"
|
||||
|
||||
// TODO: This file should be seperated into multiple with better ownership
|
||||
|
||||
Chart.register(
|
||||
LineElement,
|
||||
@@ -24,28 +25,50 @@ Chart.register(
|
||||
Tooltip
|
||||
)
|
||||
|
||||
let current_region_selection = ''
|
||||
let current_time_selection = ''
|
||||
const current_price_hash = {
|
||||
let currentRegionSelection = '';
|
||||
let currentTimeSelection = '';
|
||||
let currentAggregateSelection = '';
|
||||
let startYAtZero = false;
|
||||
const currentPriceHash = {
|
||||
us: 0,
|
||||
eu: 0,
|
||||
kr: 0,
|
||||
tw: 0
|
||||
};
|
||||
let chartData = {
|
||||
us: [],
|
||||
eu: [],
|
||||
kr: [],
|
||||
tw: []
|
||||
}
|
||||
let chart_js_data;
|
||||
let chartOptions = {
|
||||
us: {
|
||||
color: 'gold'
|
||||
},
|
||||
eu: {
|
||||
color: 'red'
|
||||
},
|
||||
kr: {
|
||||
color: 'white'
|
||||
},
|
||||
tw: {
|
||||
color: 'pink'
|
||||
}
|
||||
}
|
||||
let chartJsData;
|
||||
let ctx;
|
||||
let token_chart;
|
||||
let tokenChart;
|
||||
|
||||
|
||||
function populateChart() {
|
||||
ctx = document.getElementById("token-chart").getContext('2d');
|
||||
token_chart = new Chart(ctx, {
|
||||
tokenChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [{
|
||||
borderColor: 'gold',
|
||||
label: current_region_selection.toUpperCase() + " WoW Token Price",
|
||||
data: chart_js_data,
|
||||
label: currentRegionSelection.toUpperCase() + " WoW Token Price",
|
||||
data: chartJsData,
|
||||
cubicInterpolationMode: 'monotone',
|
||||
pointRadius: 0
|
||||
}]
|
||||
@@ -57,13 +80,48 @@ function populateChart() {
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time'
|
||||
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");
|
||||
@@ -79,22 +137,43 @@ function updateTokens(data) {
|
||||
}
|
||||
|
||||
function updateRegionalToken(region, data) {
|
||||
if (current_price_hash[region] !== data['price_data'][region]) {
|
||||
current_price_hash[region] = data['price_data'][region];
|
||||
if (region === current_region_selection) {
|
||||
if (currentPriceHash[region] !== data['price_data'][region]) {
|
||||
currentPriceHash[region] = data['price_data'][region];
|
||||
if (region === currentRegionSelection) {
|
||||
formatToken();
|
||||
add_data_to_chart(region, data);
|
||||
if (currentAggregateSelection === 'none') {
|
||||
addDataToChart(region, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function add_data_to_chart(region, data) {
|
||||
if (token_chart) {
|
||||
function addDataToChart(region, data) {
|
||||
if (tokenChart) {
|
||||
const datum = {x: data['current_time'], y: data['price_data'][region]}
|
||||
token_chart.data.datasets.forEach((dataset) => {
|
||||
tokenChart.data.datasets.forEach((dataset) => {
|
||||
dataset.data.push(datum);
|
||||
})
|
||||
token_chart.update();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,97 +198,237 @@ function removeLoader () {
|
||||
}
|
||||
}
|
||||
|
||||
export function updateRegionPreference(newRegion) {
|
||||
if (newRegion !== current_region_selection) {
|
||||
token_chart.destroy();
|
||||
function updateRegionPreference(newRegion) {
|
||||
if (newRegion !== currentRegionSelection) {
|
||||
tokenChart.destroy();
|
||||
addLoader();
|
||||
current_region_selection = newRegion;
|
||||
currentRegionSelection = newRegion;
|
||||
}
|
||||
formatToken();
|
||||
pullChartData().then(populateChart);
|
||||
}
|
||||
|
||||
export function updateTimePreference(newTime) {
|
||||
if (newTime !== current_time_selection) {
|
||||
token_chart.destroy();
|
||||
function updateTimePreference(newTime) {
|
||||
if (newTime !== currentTimeSelection) {
|
||||
tokenChart.destroy();
|
||||
addLoader();
|
||||
current_time_selection = newTime;
|
||||
currentTimeSelection = newTime;
|
||||
aggregateFunctionToggle();
|
||||
}
|
||||
pullChartData().then(populateChart);
|
||||
}
|
||||
|
||||
function updateAggregatePreference(newAggregate) {
|
||||
if (newAggregate !== currentAggregateSelection) {
|
||||
tokenChart.destroy();
|
||||
addLoader();
|
||||
currentAggregateSelection = newAggregate;
|
||||
}
|
||||
pullChartData().then(populateChart);
|
||||
}
|
||||
|
||||
function toggleAdvancedSetting() {
|
||||
let element = document.getElementById('advanced-options')
|
||||
if (document.getElementById('enable-advanced').checked)
|
||||
{
|
||||
element.style.display = 'flex';
|
||||
}
|
||||
else
|
||||
{
|
||||
element.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleStartYAtZero(){
|
||||
startYAtZero = document.getElementById('y-start').checked;
|
||||
if (tokenChart){
|
||||
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() {
|
||||
let resp = await fetch("https://data.wowtoken.app/token/history/" + current_region_selection + "/" + current_time_selection + ".json");
|
||||
let chart_data = await resp.json();
|
||||
let new_chart_js_data = [];
|
||||
for (let i = 0; i < chart_data.length; i++) {
|
||||
let resp = await fetch(urlBuilder());
|
||||
let chartData = await resp.json();
|
||||
let newChartJSData = [];
|
||||
for (let i = 0; i < chartData.length; i++) {
|
||||
let datum = {
|
||||
x: chart_data[i]['time'],
|
||||
y: chart_data[i]['value']
|
||||
x: chartData[i]['time'],
|
||||
y: chartData[i]['value']
|
||||
};
|
||||
new_chart_js_data.push(datum);
|
||||
newChartJSData.push(datum);
|
||||
}
|
||||
chart_js_data = new_chart_js_data;
|
||||
chartJsData = newChartJSData;
|
||||
removeLoader();
|
||||
}
|
||||
|
||||
async function updateChartData() {
|
||||
token_chart.destroy();
|
||||
pullChartData().then(populateChart);
|
||||
}
|
||||
|
||||
function formatToken() {
|
||||
$("#token").html(current_price_hash[current_region_selection].toLocaleString());
|
||||
document.getElementById("token").innerText = currentPriceHash[currentRegionSelection].toLocaleString();
|
||||
}
|
||||
|
||||
function detectURLQuery() {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const allowedRegions = ['us', 'eu', 'tw', 'kr']
|
||||
if (urlSearchParams.has('region')) {
|
||||
if (allowedRegions.includes(urlSearchParams.get('region').toLowerCase())) {
|
||||
current_region_selection = urlSearchParams.get('region').toLowerCase()
|
||||
let region_ddl = document.getElementById('region')
|
||||
for (let i = 0; i < region_ddl.options.length; i++){
|
||||
if (region_ddl.options[i].value === current_region_selection) {
|
||||
region_ddl.options[i].selected = true;
|
||||
}
|
||||
// TODO: These maybe able to be collapsed into a single function with params or a lambda
|
||||
|
||||
function detectRegionQuery(urlSearchParams) {
|
||||
const validRegions = ['us', 'eu', 'tw', 'kr'];
|
||||
if (validRegions.includes(urlSearchParams.get('region').toLowerCase())) {
|
||||
currentRegionSelection = urlSearchParams.get('region').toLowerCase();
|
||||
let regionDDL = document.getElementById('region');
|
||||
for (let i = 0; i < regionDDL.options.length; i++) {
|
||||
if (regionDDL.options[i].value === currentRegionSelection) {
|
||||
regionDDL.options[i].selected = true;
|
||||
}
|
||||
} else {
|
||||
console.log("An incorrect or malformed region selection was made in the query string")
|
||||
}
|
||||
} else {
|
||||
console.warn("An incorrect or malformed region selection was made in the query string");
|
||||
}
|
||||
}
|
||||
|
||||
function detectTimeQuery(urlSearchParams) {
|
||||
// In the future, we will allow all the times to be selected,
|
||||
// once I come up with a good reduction algorithm.
|
||||
// For larger time selections, it's currently hardcoded into the backend
|
||||
const validTimes = ['72h', '168h', '336h', '720h', '30d', '90d', '1y', '2y', '6m', 'all'];
|
||||
if (urlSearchParams.has('time')) {
|
||||
if (validTimes.includes(urlSearchParams.get('time').toLowerCase())) {
|
||||
current_time_selection = urlSearchParams.get('time').toLowerCase();
|
||||
let time_ddl = document.getElementById('time')
|
||||
for (let i = 0; i < time_ddl.options.length; i++){
|
||||
if (time_ddl.options[i].value === current_time_selection) {
|
||||
time_ddl.options[i].selected = true;
|
||||
}
|
||||
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();
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
console.log("An incorrect or malformed time selection was made in the query string");
|
||||
}
|
||||
} 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'];
|
||||
if (validOperations.includes(urlSearchParams.get('aggregate').toLowerCase())) {
|
||||
currentAggregateSelection = urlSearchParams.get('aggregate').toLowerCase();
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
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() {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||
if (urlSearchParams.has('region')) {
|
||||
detectRegionQuery(urlSearchParams);
|
||||
}
|
||||
if (urlSearchParams.has('time')) {
|
||||
detectTimeQuery(urlSearchParams);
|
||||
}
|
||||
if (urlSearchParams.has('aggregate')) {
|
||||
detectAggregateQuery(urlSearchParams);
|
||||
}
|
||||
if (urlSearchParams.has('startAtZero')) {
|
||||
detectZeroQuery(urlSearchParams)
|
||||
}
|
||||
}
|
||||
|
||||
function buildDeepLinksURL() {
|
||||
let url = "https://wowtoken.app/?"
|
||||
if (currentTimeSelection !== '72h'){
|
||||
url += `time=${currentTimeSelection}&`
|
||||
}
|
||||
if (currentRegionSelection !== 'us'){
|
||||
url += `region=${currentRegionSelection}&`
|
||||
}
|
||||
if (currentAggregateSelection !== '' && currentAggregateSelection !== 'none'){
|
||||
url += `aggregate=${currentAggregateSelection}&`
|
||||
}
|
||||
if (startYAtZero !== false){
|
||||
url += `startAtZero=${startYAtZero}&`
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
function copyURL() {
|
||||
let toolTip = document.getElementById('urlTooltip');
|
||||
navigator.clipboard.writeText(buildDeepLinksURL()).then(
|
||||
() => {
|
||||
toolTip.innerHTML= "Copied the URL";
|
||||
},
|
||||
() => {
|
||||
toolTip.innerHTML = "Unable to copy URL to clipboard";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function toolTipMouseOut() {
|
||||
let tooltip = document.getElementById("urlTooltip");
|
||||
tooltip.innerHTML = "Copy to clipboard";
|
||||
}
|
||||
|
||||
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() {
|
||||
updateRegionPreference(this.value);
|
||||
});
|
||||
current_region_selection = document.getElementById('region').value;
|
||||
currentRegionSelection = document.getElementById('region').value;
|
||||
document.getElementById('time').addEventListener('change', function() {
|
||||
updateTimePreference(this.value);
|
||||
});
|
||||
current_time_selection = document.getElementById('time').value;
|
||||
detectURLQuery();
|
||||
callUpdateURL();
|
||||
setInterval(callUpdateURL, 60*1000);
|
||||
pullChartData().then(populateChart);
|
||||
});
|
||||
currentTimeSelection = document.getElementById('time').value;
|
||||
document.getElementById('aggregate').addEventListener('change', function () {
|
||||
updateAggregatePreference(this.value);
|
||||
})
|
||||
currentAggregateSelection = document.getElementById('aggregate').value;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
registerEventHandles();
|
||||
detectURLQuery();
|
||||
Promise.all([callUpdateURL(), pullChartData()]).then(populateChart)
|
||||
setInterval(callUpdateURL, 60*1000);
|
||||
}, false);
|
||||
|
||||
|
||||
123
src/style.css
123
src/style.css
@@ -137,9 +137,6 @@ input[type="radio"] {
|
||||
padding: 0;
|
||||
}
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield;
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
@@ -181,7 +178,7 @@ html {
|
||||
margin: 1em;
|
||||
}
|
||||
/*body {
|
||||
|
||||
|
||||
}*/
|
||||
code {
|
||||
background-color: #073642;
|
||||
@@ -309,7 +306,7 @@ h6 {
|
||||
max-width: 85%;
|
||||
border: 1pt solid #586e75;
|
||||
padding: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.flex-container > div {
|
||||
flex: 100%;
|
||||
@@ -318,15 +315,74 @@ h6 {
|
||||
line-height: 75px;
|
||||
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 {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 3em;
|
||||
}
|
||||
|
||||
details {
|
||||
background-color: #073642;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 4px;
|
||||
padding: 0.5em 0.5em 0;
|
||||
font-size: 17px;
|
||||
line-height: 1.5em;
|
||||
margin: .5em 5vw;
|
||||
}
|
||||
|
||||
details > summary {
|
||||
color: #aaaaaa;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
margin: -0.5em -0.5em 0;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
details[open] {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
details[open] summary {
|
||||
border-bottom: 1px solid #aaa;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#option_select {
|
||||
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 {
|
||||
@@ -352,12 +408,23 @@ p {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#advanced-options {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
#advanced-options > fieldset {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.lds-ripple {
|
||||
position: relative;
|
||||
align-self: center;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-bottom: -80px;
|
||||
}
|
||||
.lds-ripple div {
|
||||
position: absolute;
|
||||
@@ -369,6 +436,44 @@ p {
|
||||
.lds-ripple div:nth-child(2) {
|
||||
animation-delay: -0.5s;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 280px;
|
||||
background-color: #555;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 80%;
|
||||
left: -15%;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: #555 transparent transparent transparent;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@keyframes lds-ripple {
|
||||
0% {
|
||||
top: 36px;
|
||||
@@ -392,8 +497,8 @@ p {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
opacity: 0;
|
||||
|
||||
@@ -1,10 +1,38 @@
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
|
||||
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.js',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /.s?css$/,
|
||||
use: [MiniCssExtractPlugin.loader, "css-loader"],
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
`...`,
|
||||
new CssMinimizerPlugin(),
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].[contenthash].css'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: "src/index.html"
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
filename: '[name].[contenthash].js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
libraryTarget: 'window',
|
||||
clean: true,
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user