import {select, inputs} from "@jashkenas/inputs";
viewof dateSelect1 = Inputs.date({min: "2014-01-02", max: "2022-12-30", value: "2018-01-01"})
dateTemp1 = new Date(2014, 0, 1)
date1 = dateTemp1.toLocaleDateString()
startDateSelect = {
if (dateSelect1 == null) {
return d3.timeParse("%m/%d/%Y")(date1)
} else {
return d3.timeParse("%m/%d/%Y")(dateSelect1.toLocaleDateString())
}
}
minDate = dateSelect1.setDate(dateSelect1.getDate() + 1);
viewof dateSelect2 = Inputs.date({min: minDate, max: "2023-01-01", value: "2023-01-01"})
dateTemp2 = new Date(2023, 0, 0)
date2 = dateTemp2.toLocaleDateString()
endDateSelect = {
if (dateSelect2 == null) {
return d3.timeParse("%m/%d/%Y")(date2)
} else {
return d3.timeParse("%m/%d/%Y")(dateSelect2.toLocaleDateString())
}
}
lineIndicator = endDateSelect - startDateSelect;
html`
<div class="row">
<div class="column-end-date" style="margin: 0px 2px 0 2px; width: 100px">
<span style="font-weight: 700;">Currency/Asset:</span>
${viewof tradAssetSelect}
</div>
<div class="column-end-date" style="margin: 0px 2px 0 2px; width: 100px;">
<span style="font-weight: 700;">Crypto:</span>
${viewof cryptoAssetSelect}
</div>
<div class="column-start-date" style="margin: 0px 2px 0 2px; width: 100px">
<span style="font-weight: 700;">Start Date:</span>
${viewof dateSelect1}
<span style="font-weight: 700;">End Date:</span>
${viewof dateSelect2}
</div>
</div>
<div class="row">
<div class="column-2"> ${cryptoTraditionalChart}</div>
</div>
`
viewof tradAssetSelect = Inputs.select(["Euro", "Pound", "Real", "Ruble", "Rupee", "Yen", "Russell 2000", "S&P 500", "Gold", "Silver"], {multiple: 4, value: ["Euro", "Pound", "Yen"]});
viewof cryptoAssetSelect = Inputs.select(["Bitcoin", "Ethereum", "Binance", "Ripple", "Cardano", "Polygon", "DogeCoin", "FTX Token"], {multiple: 4, value: ["Bitcoin"]});
assetSelect = tradAssetSelect.concat(cryptoAssetSelect);
html`
<style>
.column-1 {
float: left;
font-size: 12px;
width: 100px;
}
.column-2 {
float: left;
width: ${width}px;
}
.column-title {
float: left;
font-size: 18px;
display: inline-block;
}
.column-start-date {
font-size: 12px;
display: inline-block;
float: left;
}
.column-end-date {
font-size: 12px;
display: inline-block;
float: left;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
</style>
`
tempData = FileAttachment("./data/crypto_data_030723.csv").csv().then(
function(data) {
data.forEach(function(d) {
d.datez = d3.timeParse("%Y-%m-%d")(d.date);
});
return data;
}
)
tempData2 = aq.from(tempData)
.params({startDateSelect: startDateSelect})
.params({endDateSelect: endDateSelect})
.filter((d, $) => d.datez >= $.startDateSelect)
.filter((d, $) => d.datez <= $.endDateSelect)
.objects()
tempData3 = aq.from(tempData2)
.derive({"date": d => d.datez})
.derive({"S&P 500": d => +d.GSPC})
.derive({"VIX": d => +d.VIX})
.derive({"Russell 2000": d => +d.RUT})
.derive({"Crude Oil": d => +d.CL})
.derive({"Gold": d => +d.GC})
.derive({"Silver": d => +d.SI})
.derive({"Euro": d => +d.EUR})
.derive({"Pound": d => +d.GBP})
.derive({"Ruble": d => +d.RUB})
.derive({"Yen": d => +d.JPY})
.derive({"Real": d => +d.BRL})
.derive({"Rupee": d => +d.INR})
.derive({"Bitcoin": d => +d.BTC})
.derive({"Ethereum": d => +d.ETH})
.derive({"Binance": d => +d.BNB})
.derive({"Ripple": d => +d.XRP})
.derive({"Cardano": d => +d.ADA})
.derive({"Polygon": d => +d.MATIC})
.derive({"DogeCoin": d => +d.DOGE})
.derive({"Binance USD": d => +d.BUSD})
.derive({"Tether": d => +d.USDT})
.derive({"USD Coin": d => +d.USDC})
.derive({"FTX Token": d => +d.FTT})
.select("date", "S&P 500", "Russell 2000", "VIX", "Crude Oil", "Gold", "Silver", "Euro", "Pound", "Ruble", "Yen", "Real", "Rupee", "Bitcoin", "Ethereum", "Binance", "Ripple", "Cardano", "Polygon", "DogeCoin", "Binance USD", "Tether", "USD Coin", "FTX Token")
.fold(aq.not('date'), { as: ['id', 'value'] })
.orderby('id')
.objects();
tempData4 = aq.from(tempData2)
.derive({"date": d => d.datez})
.derive({"S&P 500": d => +d["GSPC.Adjusted"]})
.derive({"VIX": d => +d["VIX.Adjusted"]})
.derive({"Russell 2000": d => +d["RUT.Adjusted"]})
.derive({"Crude Oil": d => +d["CL=F.Adjusted"]})
.derive({"Gold": d => +d["GC=F.Adjusted"]})
.derive({"Silver": d => +d["SI=F.Adjusted"]})
.derive({"Euro": d => +d["EURUSD=X.Adjusted"]})
.derive({"Pound": d => +d["GBPUSD=X.Adjusted"]})
.derive({"Ruble": d => +d["RUBUSD=X.Adjusted"]})
.derive({"Yen": d => +d["JPYUSD=X.Adjusted"]})
.derive({"Real": d => +d["BRLUSD=X.Adjusted"]})
.derive({"Rupee": d => +d["INRUSD=X.Adjusted"]})
.derive({"Bitcoin": d => +d["BTC-USD.Adjusted"]})
.derive({"Ethereum": d => +d["ETH-USD.Adjusted"]})
.derive({"Binance": d => +d["BNB-USD.Adjusted"]})
.derive({"Ripple": d => +d["XRP-USD.Adjusted"]})
.derive({"Cardano": d => +d["ADA-USD.Adjusted"]})
.derive({"Polygon": d => +d["MATIC-USD.Adjusted"]})
.derive({"DogeCoin": d => +d["DOGE-USD.Adjusted"]})
.derive({"Binance USD": d => +d["BUSD-USD.Adjusted"]})
.derive({"Tether": d => +d["USDT-USD.Adjusted"]})
.derive({"USD Coin": d => +d["USDC-USD.Adjusted"]})
.derive({"FTX Token": d => +d["FTT-USD.Adjusted"]})
.select("date", "S&P 500", "Russell 2000", "VIX", "Crude Oil", "Gold", "Silver", "Euro", "Pound", "Ruble", "Yen", "Real", "Rupee", "Bitcoin", "Ethereum", "Binance", "Ripple", "Cardano", "Polygon", "DogeCoin", "Binance USD", "Tether", "USD Coin", "FTX Token")
.fold(aq.not('date'), { as: ['id', 'price'] })
.orderby('id')
.objects();
data = aq.from(tempData3)
.join_left(aq.from(tempData4), [["date", "id"], ["date", "id"]])
.objects();
dataSum = aq.from(data)
.groupby("id")
.rollup({"absMDC": d => op.mean(op.abs(d.value))})
.objects();
d3 = require("d3@6") // Load D3
format = ({
date1: d3.timeFormat("%B %e, %Y"), // Timeline Tooltip Date Format
date2: d3.timeFormat("%b %e %Y"), // Timeline Tooltip Date Format
percent1d: d3.format(".1%"),
percent2d: d3.format(".2%"),
dollar: d3.format("$,.2f"),
cents: d3.format("$,.4f"),
avg: d3.format("(,.1f"),
chg: d3.format(".2%")
})
function isNum(n) {
return !isNaN(parseFloat(n)) && isFinite(n) && n !== "";
}
chartTip = d3.select("body").append("div").attr("class", "toolTip chartTip");
dataDict = ({
"S&P 500": {
label: "S&P 500",
color: "rgb(196, 41, 51)",
absMDC: format.chg(dataSum[17].absMDC),
},
"VIX": {
label: "VIX",
color: "rgb(8, 30, 88)",
absMDC: format.chg(dataSum[21].absMDC)
},
"Russell 2000": {
label: "Russell",
color: "#000",
absMDC: format.chg(dataSum[16].absMDC)
},
"Crude Oil": {
label: "Crude",
color: "#3B3131",
absMDC: format.chg(dataSum[4].absMDC)
},
"Gold": {
label: "Gold",
color: "#d4af37",
absMDC: format.chg(dataSum[9].absMDC)
},
"Silver": {
label: "Silver",
color: "#C0C0C0",
absMDC: format.chg(dataSum[18].absMDC)
},
"Euro": {
label: "Euro",
color: "#003399",
absMDC: format.chg(dataSum[7].absMDC)
},
"Pound": {
label: "Pound",
color: "#012169",
absMDC: format.chg(dataSum[11].absMDC)
},
"Ruble": {
label: "Ruble",
color: "#0033A0",
absMDC: format.chg(dataSum[14].absMDC)
},
"Yen": {
label: "Yen",
color: "#BC002D",
absMDC: format.chg(dataSum[22].absMDC)
},
"Real": {
label: "Real",
color: "#009c3b",
absMDC: format.chg(dataSum[12].absMDC)
},
"Rupee": {
label: "Rupee",
color: "#FF9933",
absMDC: format.chg(dataSum[15].absMDC)
},
"Bitcoin": {
label: "Bitcoin",
color: "rgb(233, 153, 61)",
absMDC: format.chg(dataSum[2].absMDC)
},
"Ethereum": {
label: "Ethereum",
color: "rgb(79, 79, 80)",
absMDC: format.chg(dataSum[6].absMDC)
},
"Binance": {
label: "Binance",
color: "rgb(231, 188, 66)",
absMDC: format.chg(dataSum[0].absMDC)
},
"Ripple": {
label: "Ripple",
color: "rgb(36, 41, 46)",
absMDC: format.chg(dataSum[13].absMDC)
},
"Cardano": {
label: "Cardano",
color: "rgb(25, 56, 168)",
absMDC: format.chg(dataSum[3].absMDC)
},
"Polygon": {
label: "Polygon",
color: "rgb(122, 74, 221)",
absMDC: format.chg(dataSum[10].absMDC)
},
"DogeCoin": {
label: "DogeCoin",
color: "rgb(183, 162, 74)",
absMDC: format.chg(dataSum[5].absMDC)
},
"Binance USD": {
label: "Binance $",
color: "rgb(231, 188, 66)",
absMDC: format.chg(dataSum[1].absMDC)
},
"Tether": {
label: "Tether",
color: "rgb(66, 147, 148)",
absMDC: format.chg(dataSum[19].absMDC)
},
"USD Coin": {
label: "USD Coin",
color: "rgb(61, 114, 195)",
absMDC: format.chg(dataSum[20].absMDC)
},
"FTX Token": {
label: "FTX Token",
color: "rgb(127, 206, 226)",
absMDC: format.chg(dataSum[8].absMDC)
},
})
cryptoTraditionalChart = {
let height = 375
let margin = {top: 40, right: 155, bottom: 60, left: 50}
let innerHeight = height - margin.top - margin.bottom;
let innerWidth = width - margin.left - margin.right;
let chartid = "daily-movement";
const cVar = 'color';
const selectType = "value";
const selectGroup = assetSelect; // array of the checkbox selections for filtering
let filtered = data.filter(d => selectGroup.includes(d.id)); // filter data.set for extext
let extData = d3.extent(filtered, d => d[selectType]); // filter data.set for extext
let extDates = d3.extent(filtered, d => d.date)
let selectSeries = d3.groups(filtered, d => d.id);
let selectLookup = d3.group(filtered, d => d.date.toISOString())
let selectDates = Array.from(selectLookup).map(d => d[1][0].date);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("id", chartid);
const group = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Source
group.append("text")
.attr("class", "graph-source")
.text('Data: Market data from Yahoo Finance')
.attr("text-anchor", "left")
.attr("x", 0)
.attr("y", innerHeight + margin.bottom - 18)
.style('fill', '#999')
.style('font-size', '.60em')
.style('font-family', "'Open Sans', sans-serif");
if (selectGroup.length === 0){
group.append("text")
.text('Select assets above')
.attr("text-anchor", "middle")
.attr("class", "notification")
.attr("x", innerWidth/2)
.attr("y", innerHeight/2)
.style('fill', '#999')
.style('font-size', '.85em');
}
let x = d3.scaleTime()
.range([0, width - margin.left - margin.right])
.domain(extDates);
let y = d3.scaleLinear()
.range([innerHeight, 0])
.domain(extData);
// .nice();
let xAxis = d3.axisBottom(x);
let yAxis = d3.axisLeft(y);
// X-AXIS
if (width > 700) {
group.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+ 0 + "," + innerHeight + ")")
.call(xAxis)
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%b '%y"))
.tickSize(5)
.ticks(10)
)
.call(g => g.selectAll(".domain").remove())
.style('color', 'rgb(129, 129, 129)')
.style("font-size", "12px")
.style("font-weight", 500)
.style('font-family', "'Open Sans', sans-serif")
.attr("stroke-opacity", 0.5);
} else {
group.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+ 0 + "," + innerHeight + ")")
.call(xAxis)
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("'%y"))
.tickSize(10)
)
.call(g => g.selectAll(".domain").remove())
.style('color', 'rgb(129, 129, 129)')
.style("font-size", "14px")
.style("font-weight", 500)
.style('font-family', "'Open Sans', sans-serif")
.attr("stroke-opacity", 0.5);
}
// Tooltip Line
group.append("line")
.attr("class", "y-highlight")
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', 0)
.attr('y2', innerHeight)
.style('stroke', '#d3d3d3')
.style('stroke-width', '3px')
.style('opacity', 0.85);
// Y-Axis
group.append("g")
.attr("class", "y axis")
.call(yAxis)
.call(d3.axisLeft(y)
.ticks(6)
.tickFormat((d) => d3.format(`.0%`)(d)))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", innerWidth + 50))
.call(g => g.selectAll(".tick text")
.style("font-size", "14px")
.style("font-weight", 500)
.attr("fill", "rgb(129, 129, 129)")
.attr("transform", `translate(0, -7)`))
.call(g => g.selectAll(".tick line")
.attr("transform", `translate(-40, 0)`)
.attr("stroke-opacity", 0.2))
.call(g => g.append("text")
.attr("text-anchor", "middle")
.style("font-size", "16px")
.attr("fill", "currentColor"))
.attr('font-family', "'Open Sans', sans-serif");
// Append Lines
let lineGroup = group.append("g")
.attr('class', 'line-group')
let line = d3.line()
.defined(d => !isNaN(d[selectType]))
.x(d => x(d.date))
.y(d => y(d[selectType]))
lineGroup.append('g')
.selectAll('path')
.data(selectSeries)
.join('path')
.attr('d', (d) => line(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", "none")
.style("stroke", function(d) {
return dataDict[d[0]].color
})
.attr('stroke-width', lineIndicator < 1e11 ? '1.75px' : '1.25px')
// Label Object
let labelset = Array.from(selectSeries).map(d => {
let obj = {}
obj.id = d[0];
obj.label = dataDict[obj.id].label;
obj.color = dataDict[obj.id].color;
obj.absMDC = dataDict[obj.id].absMDC;
obj.val = d[1][d[1].length - 1][selectType];
return obj
}).sort((a, b) => d3.descending(a.val, b.val))
// Force Line Labels - Basic Version
let forceLabelOffset = 8
let forceLabelR = 20
let forceLabels = group.append("g")
.attr('class', 'line-force-labels')
const forceLabelLeader = forceLabels.selectAll('line')
.data(labelset)
.join('line')
.attr("class", 'lfl-leader')
.attr('x1', innerWidth + 2)
.attr('x2', innerWidth + forceLabelOffset)
.attr('y1', d => y(d.val))
.attr('y2', d => y(d.val))
.attr('stroke', d => d.color)
const forceLabel = forceLabels.selectAll('g')
.data(labelset)
.join('g')
.attr('transform', function(d, i) {
// contrains node x,y to bounds
d.x = innerWidth + forceLabelOffset;
//d.y = y(d.val);
d.y = innerHeight/3 + (i * 15); // initializes the labels in a stepped
return `translate(${d.x},${d.y})`
})
.on("mouseover", (event, d) => {
d3.select(event.target).style("cursor", "pointer");
d3.selectAll("#" + chartid + " .ts-line")
.transition().style('opacity', g => { return g[0] === d.id ? 1 : 0.2; });
d3.selectAll("#" + chartid + " .lfl-text")
.transition().style('opacity', g => { return g.id === d.id ? 1 : 0.2; });
d3.selectAll("#" + chartid + " .lfl-leader")
.transition().style('opacity', g => { return g.id === d.id ? 1 : 0.2; });
})
.on("mouseout", (event, d) => {
d3.select(event.target).style("cursor", "default");
d3.selectAll("#" + chartid + " .ts-line")
.transition().style('opacity', 1 );
d3.selectAll("#" + chartid + " .lfl-text")
.transition().style('opacity', 1 );
d3.selectAll("#" + chartid + " .lfl-leader")
.transition().style('opacity', 1 );
});
forceLabel.append('text')
.attr("class", 'lfl-text')
.html(d => d.label + ` ` + d.absMDC + ` avg`)
.attr('fill', d => d.color)
.style('font-size', '14px')
.style("font-weight", 700);
const simulation = d3.forceSimulation(labelset)
.force("charge", d3.forceManyBody().strength(-10))
.force('y', d3.forceY().y(d => y(d.val)))
.force('x', d3.forceX().x(innerWidth + forceLabelOffset))
//.force("rectcollide", rectCollide())
.force('collision', d3.forceCollide().radius(forceLabelR));
simulation.on('tick', () => {
forceLabel.attr('transform', function(d) {
d.x = innerWidth + forceLabelOffset; // constrain x for all labels
d.y = Math.max(forceLabelR, Math.min((innerHeight+margin.bottom/2) - forceLabelR, d.y))
return `translate(${d.x},${d.y})`
});
forceLabelLeader
.attr("x2", d => d.x-2)
.attr("y2", d => d.y)
})
// Chart toolTip Area (defines where hover with be active)
let tipArea = group.append("g")
.attr("class", "tip-area");
tipArea.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', innerWidth)
.attr('height', innerHeight)
.attr('opacity', 0)
.attr('pointer-events', 'all')
.on('mouseover', (event) => {
console.log('mouseover');
})
.on('mousemove', (event) => {
console.log('mousemove');
// Get the date on the y axis for the mouse position
let invert = x.invert(d3.pointer(event)[0])
let bisect = d3.bisector(function(d) { return d; }).right;
let hoverDate = (selectDates[bisect(selectDates, invert)]);
// 1) Lookup/Get values by date 2) Filter by selection group 3) Sort by top values
let lookup = selectLookup.get(hoverDate.toISOString()).filter(d => selectGroup.includes(d.id));
// Display and position vertical line
d3.select("#" + chartid + " .y-highlight")
.attr('x1',x(hoverDate))
.attr('x2',x(hoverDate))
.style('opacity', 1);
// Custom toolTip Content
let tipContent = `<hr style='margin:2px; padding:0; border-top: 3px solid #333;'>`;
// Interate over selected categories
tipContent += `<table class="tip-table"><thead><tr><th></th><th>Daily % Δ</th><th>Price</th></tr></thead><tbody>`
// Interate over selected categories
lookup.map( d => {
tipContent += `<tr>
<td class="row-header"><div class="chip-circle" style="background-color: ${dataDict[d.id][cVar]};"></div> ${dataDict[d.id].label}</td>`
tipContent += `<td>${format.percent2d(d.value)}</td>`
tipContent += `<td>${d.price < 1 ? format.cents(d.price) : format.dollar(d.price)}</td></tr>`
})
// Close Table
tipContent += `</tbody></table>`
chartTip.style("left", () => {
if ((innerWidth - d3.pointer(event)[0]) < 120) {
return event.pageX - (24+240) + "px";
} else {
return event.pageX + 24 + "px";
}
})
.style("top", event.pageY + 10 + "px")
.style("display", "inline-block")
.html(`<strong style="font-size: 14px;">${format.date1(hoverDate)}</strong><br>${tipContent}`); // toolTip Content
})
.on('mouseout', (event) => {
console.log('mouseout');
chartTip.style("display", "none"); // Hide toolTip
d3.select("#" + chartid + " .y-highlight").style('opacity', 0); // Hide y-highlight line
d3.selectAll("#" + chartid + " .y-highlight-point").remove(); // Remove y-highlight points
});
return svg.node();
}
import {input} from "@jashkenas/inputs";
aq = {
const aq = await require(`arquero@${aq_version}`);
// load and install any additional packages
(await Promise.all(aq_packages.map(pkg => require(pkg))))
.forEach(pkg => aq.addPackage(pkg));
// Add HTML table view method to tables
aq.addTableMethod('view', toView, { override: true });
return aq;
};
aq_version = "5.1.0";
aq_packages = [];
op = aq.op;
toView = {
const DEFAULT_LIMIT = 100;
const DEFAULT_NULL = value => `<span style="color: #999;">${value}</span>`;
const tableStyle = 'margin: 0; border-collapse: collapse; width: initial;';
const cellStyle = 'padding: 1px 5px; white-space: nowrap; overflow-x: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums;';
// extend table prototype to provide an HTML table view
return function(dt, opt = {}) {
// permit shorthand for limit
if (typeof opt === 'number') opt = { limit: opt };
// marshal cell color options
const color = { ...opt.color };
if (typeof opt.color === 'function') {
// if function, apply to all columns
dt.columnNames().forEach(name => color[name] = opt.color);
} else {
// otherwise, gather per-column color options
for (const key in color) {
const value = color[key];
color[key] = typeof value === 'function' ? value : () => value;
}
}
// marshal CSS styles as toHTML() options
const table = `${tableStyle}`;
const td = (name, index, row) => {
return `${cellStyle} max-width: ${+opt.maxCellWidth || 300}px;`
+ (color[name] ? ` background-color: ${color[name](index, row)};` : '');
};
opt = {
limit: DEFAULT_LIMIT,
null: DEFAULT_NULL,
...opt,
style: { table, td, th: td }
};
// return container div, bind table value to support viewof operator
const size = `max-height: ${+opt.height || 270}px`;
const style = `${size}; overflow-x: auto; overflow-y: auto;`;
const view = html`<div style="${style}">${dt.toHTML(opt)}</div>`;
view.value = dt;
return view;
};
};
mobileTest = { if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
|| /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4))) { return true; } else { return false; }
};
style_sheet = html`<style>
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700;800&display=swap');
body {
font-family: 'Open Sans', sans-serif;
}
select {
font-family: 'Open Sans', sans-serif;
// border-radius: 20px;
background-color: #fff;
}
.chip-circle {
border-radius: 50%;
display: inline-block;
position: relative;
width: 10px;
height: 10px;
}
label {
margin-top: 20px;
padding: 10px;
padding-right:15px;
text-align: center;
cursor: pointer;
background-color: #fff;
color: #2e3745;
font-weight: 600;
}
input[type="range"] {
-webkit-appearance: none;
border: none;
margin: 18px 0;
width: 100%;
box-shadow: -2px -2px 8px white, 2px 2px 8px rgba(black, 0.5);
}
input[type="range"]:focus {
outline: none;
}
input[type="range"]::-webkit-slider-runnable-track {
border: none;
width: 100%;
height: 5px;
cursor: pointer;
background: #fff;
border-radius: 10px;
box-shadow: rgb(204, 219, 232) 3px 3px 6px 0px inset, rgba(255, 255, 255, 0.5) -3px -3px 6px 1px inset;
}
input[type="range"]::-webkit-slider-thumb {
height: 15px;
width: 15px;
border-radius: 30px;
box-shadow: 0px 0px 4px 1px rgba(0,0,0,0.37);
/* border: 1px solid #333; */
background: #fff;
cursor: pointer;
-webkit-appearance: none;
margin-top: -5px;
}
input[type="range"]:focus::-webkit-slider-runnable-track {
background: #fff;
border: none;
}
input[type="range"]::-moz-range-track {
border: none;
width: 100%;
height: 5px;
cursor: pointer;
background: #fff;
border-radius: 10px;
box-shadow: rgb(204, 219, 232) 3px 3px 6px 0px inset, rgba(255, 255, 255, 0.5) -3px -3px 6px 1px inset;
}
input[type="range"]::-moz-range-thumb {
height: 15px;
width: 15px;
border-radius: 30px;
box-shadow: 0px 0px 4px 1px rgba(0,0,0,0.37);
/* border: 1px solid #333; */
background: #fff;
cursor: pointer;
-webkit-appearance: none;
margin-top: -5px;
}
input[type="range"]::-ms-track {
border: none;
width: 100%;
height: 5px;
cursor: pointer;
background: transparent;
border-color: transparent;
border-width: 16px 0;
color: transparent;
}
input[type="range"]::-ms-fill-lower {
background: #e0e0e0;
border-radius: 2.6px;
}
input[type="range"]::-ms-fill-upper {
background: #e0e0e0;
border-radius: 2.6px;
}
input[type="range"]::-ms-thumb {
border: none;
height: 20px;
width: 16px;
border-radius: 3px;
background: #ffffff;
cursor: pointer;
}
input[type="range"]:focus::-ms-fill-lower {
background: #fff;
}
input[type="range"]:focus::-ms-fill-upper {
background: #fff;
}
/* clears the ‘X’ from Chrome */
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration { display: none; }
input[type="time"]::-webkit-clear-button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
#grid-3 {
display: grid;
grid-template-columns: auto auto auto;
background-color: #fff;
}
#grid-4 {
display: grid;
grid-template-columns: auto auto auto auto;
background-color: #fff;
}
#grid-4-full {
display: grid;
grid-template-columns: auto auto auto auto;
background-color: #fff;
border-top: 1px solid #d3d3d3;
border-right: 1px solid #d3d3d3;
border-bottom: 1px solid #d3d3d3;
border-left: 1px solid #d3d3d3;
}
#grid-4-top {
display: grid;
grid-template-columns: auto auto auto auto;
background-color: #fff;
border-top: 1px solid #d3d3d3;
border-right: 1px solid #d3d3d3;
border-left: 1px solid #d3d3d3;
}
#grid-4-bottom {
display: grid;
grid-template-columns: auto auto auto auto;
background-color: #fff;
border-bottom: 1px solid #d3d3d3;
border-right: 1px solid #d3d3d3;
border-left: 1px solid #d3d3d3;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid #d3d3d3;
padding: 20px;
font-size: 16px;
text-align: left;
}
</style>`
tooltipStyle = html`
<style>
.toolTip {
position: absolute;
display: none;
min-width: 30px;
border-radius: 0;
height: auto;
background: #fff;
border: 1px solid #d3d3d3;
padding: 4px 4px;
font-size: .85rem;
text-align: left;
z-index: 1000;
}
.eventTip { max-width: 140px;}
.chartTip { max-width: 240px;}
.tip-values {
font-weight: bold;
color: #787878;
}
.tip-values .secondary{
font-weight: normal;
}
.tip-table {
font-size: 13px;
text-align: right;
color: #787878;
}
.tip-table td, .tip-table th {
padding-right: 4px;
padding-left: 4px;
}
.tip-table th {
font-size: 13px;
text-align: right;
color: #AAA;
vertical-align: bottom;
}
.tip-table .row-header {
text-align: left;
}
.tip-table .primary {
font-weight: bold;
}
</style>
`
html`<style>
.info-title {
margin-bottom: 32px;
text-align: center;
}
.info-title h1 {
max-width: 10000px;
}
.info-box {
padding: 24px;
}
.info-wrapper {
display: flex;
justify-content: center;
}
.wrapper-2 {
display: grid;
grid-template-columns: 1fr 1fr;
}
.wrapper-3 {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.box-title {
padding: 20px;
text-align: center;
font-weight: 500;
}
.wrapper-4 {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
}
</style>
`