mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-02 23:41:27 +00:00
remove plotly charts legacy web modules (#5034)
This commit is contained in:
BIN
openbb_terminal/core/plots/assets/icon.png
vendored
BIN
openbb_terminal/core/plots/assets/icon.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 727 B |
41
openbb_terminal/core/plots/old_table.html
vendored
41
openbb_terminal/core/plots/old_table.html
vendored
@@ -1,41 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="color-scheme" content="dark" />
|
||||
<link rel="stylesheet" href="/{{MAIN_PATH}}/web/css/table.css" />
|
||||
|
||||
<script src="/{{MAIN_PATH}}/web/helpers.js" type="text/javascript"></script>
|
||||
<script
|
||||
src="/{{MAIN_PATH}}/web/bar_menus.js"
|
||||
type="text/javascript"
|
||||
></script>
|
||||
<script src="/{{MAIN_PATH}}/web/popups.js" type="text/javascript"></script>
|
||||
<script
|
||||
src="/{{MAIN_PATH}}/web/main_table.js"
|
||||
type="text/javascript"
|
||||
></script>
|
||||
|
||||
<link
|
||||
href="https://unpkg.com/tabulator-tables@5.4.3/dist/css/tabulator_midnight.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://unpkg.com/tabulator-tables@5.4.3/dist/js/tabulator.min.js"
|
||||
></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="example-table"></div>
|
||||
<script type="text/javascript">
|
||||
var interval = setInterval(function () {
|
||||
// Waits for OpenBBMain and plotly_figure to be defined before
|
||||
// calling OpenBBMain to avoid errors
|
||||
if (typeof TableProcessor != "undefined" && window.plotly_figure) {
|
||||
clearInterval(interval);
|
||||
TableProcessor(window.plotly_figure);
|
||||
}
|
||||
}, 20);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
460
openbb_terminal/core/plots/web/bar_menus.js
vendored
460
openbb_terminal/core/plots/web/bar_menus.js
vendored
@@ -1,460 +0,0 @@
|
||||
// Custom Menu functions for Plotly charts
|
||||
|
||||
async function autoScaling(eventdata, graphs) {
|
||||
try {
|
||||
if (eventdata["xaxis.range[0]"] != undefined) {
|
||||
const x_min = eventdata["xaxis.range[0]"];
|
||||
const x_max = eventdata["xaxis.range[1]"];
|
||||
let to_update = {};
|
||||
let y_min, y_max;
|
||||
|
||||
const YaxisData = graphs.data.filter((trace) => trace.yaxis != undefined);
|
||||
const yaxis_unique = [
|
||||
...new Set(
|
||||
YaxisData.map(
|
||||
(trace) =>
|
||||
trace.yaxis || trace.y != undefined || trace.type == "candlestick"
|
||||
)
|
||||
),
|
||||
];
|
||||
|
||||
const get_all_yaxis_traces = (yaxis) => {
|
||||
return graphs.data.filter(
|
||||
(trace) =>
|
||||
trace.yaxis == yaxis && (trace.y || trace.type == "candlestick")
|
||||
);
|
||||
};
|
||||
|
||||
yaxis_unique.forEach((unique) => {
|
||||
if (typeof unique != "string") {
|
||||
return;
|
||||
}
|
||||
let yaxis = "yaxis" + unique.replace("y", "");
|
||||
let y_candle = [];
|
||||
let y_values = [];
|
||||
let log_scale = graphs.layout[yaxis].type == "log";
|
||||
|
||||
get_all_yaxis_traces(unique).forEach((trace2) => {
|
||||
let x = trace2.x;
|
||||
log_scale = graphs.layout[yaxis].type == "log";
|
||||
|
||||
let y = trace2.y != undefined ? trace2.y : [];
|
||||
let y_low = trace2.type == "candlestick" ? trace2.low : [];
|
||||
let y_high = trace2.type == "candlestick" ? trace2.high : [];
|
||||
|
||||
if (log_scale) {
|
||||
y = y.map(Math.log10);
|
||||
if (trace2.type == "candlestick") {
|
||||
y_low = trace2.low.map(Math.log10);
|
||||
y_high = trace2.high.map(Math.log10);
|
||||
}
|
||||
}
|
||||
|
||||
let yx_values = x.map((x, i) => {
|
||||
let out = null;
|
||||
if (x >= x_min && x <= x_max) {
|
||||
if (trace2.y != undefined) {
|
||||
console.log(trace2.name, trace2.type, trace2.yaxis);
|
||||
out = y[i];
|
||||
}
|
||||
if (trace2.type == "candlestick") {
|
||||
y_candle.push(y_low[i]);
|
||||
y_candle.push(y_high[i]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
});
|
||||
|
||||
y_values = y_values.concat(yx_values);
|
||||
});
|
||||
|
||||
y_values = y_values.filter((y2) => y2 != undefined && y2 != null);
|
||||
y_min = Math.min(...y_values);
|
||||
y_max = Math.max(...y_values);
|
||||
|
||||
if (y_candle.length > 0) {
|
||||
y_candle = y_candle.filter((y2) => y2 != undefined && y2 != null);
|
||||
y_min = Math.min(...y_candle);
|
||||
y_max = Math.max(...y_candle);
|
||||
}
|
||||
|
||||
let org_y_max = y_max;
|
||||
let is_volume =
|
||||
graphs.layout[yaxis].fixedrange != undefined &&
|
||||
graphs.layout[yaxis].fixedrange == true;
|
||||
|
||||
if (y_min != undefined && y_max != undefined) {
|
||||
let y_range = y_max - y_min;
|
||||
let y_mult = 0.15;
|
||||
if (y_candle.length > 0) {
|
||||
y_mult = 0.3;
|
||||
}
|
||||
|
||||
y_min -= y_range * y_mult;
|
||||
y_max += y_range * y_mult;
|
||||
|
||||
if (is_volume) {
|
||||
if (graphs.layout[yaxis].tickvals != undefined) {
|
||||
const range_x = 7;
|
||||
let volume_ticks = org_y_max;
|
||||
let round_digits = -3;
|
||||
let first_val = Math.round(volume_ticks * 0.2, round_digits);
|
||||
let x_zipped = [2, 5, 6, 7, 8, 9, 10];
|
||||
let y_zipped = [1, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
for (let i = 0; i < x_zipped.length; i++) {
|
||||
if (String(volume_ticks).length > x_zipped[i]) {
|
||||
round_digits = -y_zipped[i];
|
||||
first_val = Math.round(volume_ticks * 0.2, round_digits);
|
||||
}
|
||||
}
|
||||
let tickvals = [
|
||||
Math.floor(first_val),
|
||||
Math.floor(first_val * 2),
|
||||
Math.floor(first_val * 3),
|
||||
Math.floor(first_val * 4),
|
||||
];
|
||||
let volume_range = [0, Math.floor(volume_ticks * range_x)];
|
||||
|
||||
to_update[yaxis + ".tickvals"] = tickvals;
|
||||
to_update[yaxis + ".range"] = volume_range;
|
||||
to_update[yaxis + ".tickformat"] = ".2s";
|
||||
return;
|
||||
}
|
||||
y_min = 0;
|
||||
y_max = graphs.layout[yaxis].range[1];
|
||||
}
|
||||
to_update[yaxis + ".range"] = [y_min, y_max];
|
||||
}
|
||||
});
|
||||
Plotly.update(graphs, {}, to_update);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`Error in AutoScaling: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
function changeColor() {
|
||||
if (!globals.color_picker) {
|
||||
let color_picker = document.getElementById("changecolor");
|
||||
globals.color_picker = color_picker;
|
||||
|
||||
color_picker.style.display = "block";
|
||||
color_picker.style.width = null;
|
||||
dragElement(color_picker);
|
||||
|
||||
function dragElement(elmnt) {
|
||||
let pos1 = 0,
|
||||
pos2 = 0,
|
||||
pos3 = 0,
|
||||
pos4 = 0;
|
||||
if (document.getElementById(elmnt.id + "_header")) {
|
||||
// if present, the header is where you move the DIV from:
|
||||
document.getElementById(elmnt.id + "_header").onmousedown =
|
||||
dragMouseDown;
|
||||
} else {
|
||||
// otherwise, move the DIV from anywhere inside the DIV:
|
||||
elmnt.onmousedown = dragMouseDown;
|
||||
}
|
||||
|
||||
function dragMouseDown(e) {
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
// get the mouse cursor position at startup:
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
document.onmouseup = closeDragElement;
|
||||
// call a function whenever the cursor moves:
|
||||
document.onmousemove = elementDrag;
|
||||
}
|
||||
|
||||
function elementDrag(e) {
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
// calculate the new cursor position:
|
||||
pos1 = pos3 - e.clientX;
|
||||
pos2 = pos4 - e.clientY;
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
// set the element's new position:
|
||||
elmnt.style.top = elmnt.offsetTop - pos2 + "px";
|
||||
elmnt.style.left = elmnt.offsetLeft - pos1 + "px";
|
||||
}
|
||||
|
||||
function closeDragElement() {
|
||||
// stop moving when mouse button is released:
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
globals.color_picker.style.display = "none";
|
||||
globals.color_picker = null;
|
||||
}
|
||||
}
|
||||
|
||||
function uploadImage() {
|
||||
const loader = document.getElementById("loader");
|
||||
loader.classList.add("show");
|
||||
Plotly.toImage(globals.CHART_DIV, {
|
||||
format: "png",
|
||||
height: 627,
|
||||
width: 1200,
|
||||
})
|
||||
.then(function (url) {
|
||||
let data = {
|
||||
image: url,
|
||||
title: globals.title,
|
||||
description: "Check out this chart from OpenBB",
|
||||
};
|
||||
|
||||
fetch("https://uppy-self.vercel.app/api/upload-image", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(function (response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
const { url, id } = data;
|
||||
loader.classList.remove("show");
|
||||
openPopup("popup_upload", { url });
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
loader.classList.remove("show");
|
||||
});
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
loader.classList.remove("show");
|
||||
});
|
||||
}
|
||||
|
||||
const openbb_watermark = {
|
||||
yref: "paper",
|
||||
xref: "paper",
|
||||
x: 1,
|
||||
y: 0,
|
||||
text: "OpenBB Terminal",
|
||||
font_size: 17,
|
||||
font_color: "gray",
|
||||
opacity: 0.5,
|
||||
xanchor: "right",
|
||||
yanchor: "bottom",
|
||||
yshift: -80,
|
||||
xshift: 40,
|
||||
};
|
||||
|
||||
async function setWatermarks(margin, old_index, init = false) {
|
||||
if (init) {
|
||||
globals.CHART_DIV.layout.annotations.push(openbb_watermark);
|
||||
if (globals.cmd_idx && globals.cmd_src) {
|
||||
globals.CHART_DIV.layout.annotations[globals.cmd_idx].text =
|
||||
globals.cmd_src;
|
||||
}
|
||||
|
||||
Plotly.relayout(globals.CHART_DIV, {
|
||||
"title.text": globals.title,
|
||||
margin: globals.old_margin,
|
||||
});
|
||||
}
|
||||
|
||||
if (!init) {
|
||||
if (globals.cmd_idx && globals.cmd_src) {
|
||||
globals.CHART_DIV.layout.annotations[globals.cmd_idx].text = "";
|
||||
}
|
||||
globals.CHART_DIV.layout.annotations.splice(old_index, 1);
|
||||
|
||||
Plotly.relayout(globals.CHART_DIV, {
|
||||
"title.text": "",
|
||||
margin: margin,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadImage(filename, extension, writable = undefined) {
|
||||
const loader = document.getElementById("loader");
|
||||
loader.classList.add("show");
|
||||
|
||||
let imageDownload = undefined;
|
||||
|
||||
if (extension == "png") {
|
||||
imageDownload = domtoimage.toPng;
|
||||
} else if (extension == "jpeg") {
|
||||
imageDownload = domtoimage.toJpeg;
|
||||
// } else if (extension == "svg") {
|
||||
// imageDownload = domtoimage.toSvg;
|
||||
} else if (["svg", "pdf"].includes(extension)) {
|
||||
const margin = globals.CHART_DIV.layout.margin;
|
||||
const old_index = globals.CHART_DIV.layout.annotations.length;
|
||||
|
||||
await setWatermarks(margin, old_index, true);
|
||||
|
||||
if (window.showSaveFilePicker && writable) {
|
||||
await Plotly.toImage(globals.CHART_DIV, {
|
||||
format: "svg",
|
||||
height: globals.CHART_DIV.clientHeight,
|
||||
width: globals.CHART_DIV.clientWidth,
|
||||
}).then(async function (dataUrl) {
|
||||
const blob = await fetch(dataUrl).then((r) => r.blob());
|
||||
await writable.write(blob);
|
||||
await writable.close();
|
||||
});
|
||||
} else {
|
||||
Plotly.downloadImage(globals.CHART_DIV, {
|
||||
format: "svg",
|
||||
height: globals.CHART_DIV.clientHeight,
|
||||
width: globals.CHART_DIV.clientWidth,
|
||||
filename: filename,
|
||||
});
|
||||
}
|
||||
|
||||
await setWatermarks(margin, old_index, false);
|
||||
|
||||
return;
|
||||
} else {
|
||||
console.log("Invalid extension");
|
||||
return;
|
||||
}
|
||||
|
||||
imageDownload(document.getElementById("openbb_container"))
|
||||
.then(async function (dataUrl) {
|
||||
if (window.showSaveFilePicker && writable) {
|
||||
const blob = await fetch(dataUrl).then((r) => r.blob());
|
||||
await writable.write(blob);
|
||||
await writable.close();
|
||||
} else {
|
||||
downloadURI(dataUrl, filename + "." + extension);
|
||||
}
|
||||
loader.classList.remove("show");
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error("oops, something went wrong!", error);
|
||||
loader.classList.remove("show");
|
||||
hideModebar();
|
||||
});
|
||||
}
|
||||
|
||||
function downloadURI(uri, name) {
|
||||
let link = document.createElement("a");
|
||||
link.download = name;
|
||||
link.href = uri;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
async function downloadData(gd, filename, writable = undefined) {
|
||||
let data = gd.data;
|
||||
let candlestick = false;
|
||||
let csv = undefined;
|
||||
|
||||
data.forEach(function (trace) {
|
||||
// check if candlestick
|
||||
if (trace.type == "candlestick") {
|
||||
candlestick = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
let xaxis =
|
||||
"title" in gd.layout["xaxis"] &&
|
||||
gd.layout["xaxis"]["title"]["text"] != undefined
|
||||
? gd.layout["xaxis"]["title"]["text"]
|
||||
: "x";
|
||||
|
||||
let yaxis =
|
||||
"title" in gd.layout["yaxis"] &&
|
||||
gd.layout["yaxis"]["title"]["text"] != undefined
|
||||
? gd.layout["yaxis"]["title"]["text"]
|
||||
: "y";
|
||||
|
||||
if (candlestick) {
|
||||
csv = "Date,Open,High,Low,Close\n";
|
||||
data.forEach(function (trace) {
|
||||
if (trace.type == "candlestick") {
|
||||
let x = trace.x;
|
||||
let open = trace.open;
|
||||
let high = trace.high;
|
||||
let low = trace.low;
|
||||
let close = trace.close;
|
||||
|
||||
for (let i = 0; i < x.length; i++) {
|
||||
csv += `${x[i]},${open[i]},${high[i]},${low[i]},${close[i]}\n`;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let traces = 0;
|
||||
data.forEach(function (trace) {
|
||||
if (trace.type == "scatter") {
|
||||
traces++;
|
||||
}
|
||||
});
|
||||
|
||||
if (traces == 1) {
|
||||
csv = `${data[0].name},${xaxis},${yaxis}\n`;
|
||||
data.forEach(function (trace) {
|
||||
if (trace.type == "scatter") {
|
||||
let x = trace.x;
|
||||
let y = trace.y;
|
||||
|
||||
for (let i = 0; i < x.length; i++) {
|
||||
csv += `${x[i]},${y[i]}\n`;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (traces > 1) {
|
||||
csv = `${xaxis}`;
|
||||
data.forEach(function (trace) {
|
||||
if (trace.type == "scatter") {
|
||||
csv += `,${trace.name}`;
|
||||
}
|
||||
});
|
||||
csv += "\n";
|
||||
|
||||
let x = data[0].x;
|
||||
for (let i = 0; i < x.length; i++) {
|
||||
csv += `${x[i]}`;
|
||||
data.forEach(function (trace) {
|
||||
if (trace.type == "scatter") {
|
||||
csv += `,${trace.y[i]}`;
|
||||
}
|
||||
});
|
||||
csv += "\n";
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
||||
if (navigator.msSaveBlob) {
|
||||
// IE 10+
|
||||
navigator.msSaveBlob(blob, filename);
|
||||
} else {
|
||||
// feature detection
|
||||
// Browsers that support showSaveFilePicker or HTML5 download attribute
|
||||
if (window.showSaveFilePicker && writable) {
|
||||
await writable.write(blob);
|
||||
await writable.close();
|
||||
} else {
|
||||
let link = window.document.createElement("a");
|
||||
if (link.download !== undefined) {
|
||||
// feature detection
|
||||
// Browsers that support HTML5 download attribute
|
||||
let url = URL.createObjectURL(blob);
|
||||
link.setAttribute("href", url);
|
||||
link.setAttribute("download", filename);
|
||||
link.style.visibility = "hidden";
|
||||
window.document.body.appendChild(link);
|
||||
link.click();
|
||||
window.document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
1351
openbb_terminal/core/plots/web/css/style.css
vendored
1351
openbb_terminal/core/plots/web/css/style.css
vendored
File diff suppressed because it is too large
Load Diff
7
openbb_terminal/core/plots/web/css/table.css
vendored
7
openbb_terminal/core/plots/web/css/table.css
vendored
@@ -1,7 +0,0 @@
|
||||
body {
|
||||
font-family: "Fira Code", monospace, "Arial Black";
|
||||
font-size: 1.2rem;
|
||||
color: #ffffff;
|
||||
background-color: #000000;
|
||||
overflow: hidden;
|
||||
}
|
||||
1080
openbb_terminal/core/plots/web/helpers.js
vendored
1080
openbb_terminal/core/plots/web/helpers.js
vendored
File diff suppressed because it is too large
Load Diff
646
openbb_terminal/core/plots/web/main.js
vendored
646
openbb_terminal/core/plots/web/main.js
vendored
@@ -1,646 +0,0 @@
|
||||
let globals = {
|
||||
dark_mode: false,
|
||||
modebarHidden: false,
|
||||
volume: {},
|
||||
added_traces: [],
|
||||
old_nticks: {},
|
||||
csv_yaxis_id: null,
|
||||
cmd_src_idx: null,
|
||||
};
|
||||
|
||||
function loadingOverlay(message) {
|
||||
const loading = document.getElementById("loading");
|
||||
const loading_text = document.getElementById("loading_text");
|
||||
|
||||
loading_text.innerHTML = message;
|
||||
loading.classList.add("show");
|
||||
|
||||
let is_loaded = setInterval(function () {
|
||||
if (loading.classList.contains("show")) {
|
||||
clearInterval(is_loaded);
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
|
||||
const non_blocking = (func, delay) => {
|
||||
let timeout;
|
||||
return function () {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(context, args), delay);
|
||||
};
|
||||
};
|
||||
|
||||
function OpenBBMain(
|
||||
plotly_figure,
|
||||
chartdiv,
|
||||
csvdiv,
|
||||
textdiv,
|
||||
titlediv,
|
||||
downloaddiv
|
||||
) {
|
||||
// Main function that plots the graphs and initializes the bar menus
|
||||
globals.CHART_DIV = chartdiv;
|
||||
globals.TITLE_DIV = titlediv;
|
||||
globals.TEXT_DIV = textdiv;
|
||||
globals.CSV_DIV = csvdiv;
|
||||
globals.dark_mode = plotly_figure.theme === "dark";
|
||||
globals.DOWNLOAD_DIV = downloaddiv;
|
||||
console.log("main.js loaded");
|
||||
console.log("plotly_figure", plotly_figure);
|
||||
let graphs = plotly_figure;
|
||||
|
||||
const loading = document.getElementById("loading");
|
||||
|
||||
// We add the event listeners for csv file/type changes
|
||||
globals.CSV_DIV.querySelector("#csv_file").addEventListener(
|
||||
"change",
|
||||
function () {
|
||||
console.log("file changed");
|
||||
loadingOverlay("Loading CSV");
|
||||
setTimeout(function () {
|
||||
non_blocking(function () {
|
||||
checkFile(globals.CSV_DIV);
|
||||
}, 2)();
|
||||
setTimeout(function () {
|
||||
loading.classList.remove("show");
|
||||
}, 200);
|
||||
}, 1000);
|
||||
}
|
||||
);
|
||||
globals.CSV_DIV.querySelector("#csv_trace_type").addEventListener(
|
||||
"change",
|
||||
function () {
|
||||
console.log("type changed");
|
||||
checkFile(globals.CSV_DIV, true);
|
||||
}
|
||||
);
|
||||
|
||||
globals.filename = openbbFilename(graphs);
|
||||
|
||||
// Sets the config with the custom buttons
|
||||
CONFIG = {
|
||||
plotGlPixelRatio: 1,
|
||||
scrollZoom: true,
|
||||
responsive: true,
|
||||
displaylogo: false,
|
||||
displayModeBar: true,
|
||||
modeBarButtonsToRemove: ["lasso2d", "select2d", "downloadImage"],
|
||||
modeBarButtons: [
|
||||
[
|
||||
{
|
||||
name: "Download CSV (Ctrl+Shift+S)",
|
||||
icon: ICONS.downloadCsv,
|
||||
click: async function (gd) {
|
||||
await downloadCsvButton(gd);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Download Chart as Image (Ctrl+S)",
|
||||
icon: ICONS.downloadImage,
|
||||
click: async function () {
|
||||
await downloadImageButton();
|
||||
},
|
||||
},
|
||||
// {
|
||||
// name: "Upload Image (Ctrl+U)",
|
||||
// icon: Plotly.Icons.uploadImage,
|
||||
// click: function (gd) {
|
||||
// downloadImage();
|
||||
// },
|
||||
// },
|
||||
],
|
||||
[
|
||||
{
|
||||
name: "Edit Color (Ctrl+E)",
|
||||
icon: ICONS.changeColor,
|
||||
click: function () {
|
||||
// We need to check if the button is active or not
|
||||
let title = "Edit Color (Ctrl+E)";
|
||||
let button = globals.barButtons[title];
|
||||
let active = true;
|
||||
if (button.style.border == "transparent") {
|
||||
active = false;
|
||||
}
|
||||
// We call the function that changes the border color
|
||||
button_pressed(title, active);
|
||||
changeColor();
|
||||
},
|
||||
},
|
||||
"drawline",
|
||||
"drawopenpath",
|
||||
"drawcircle",
|
||||
"drawrect",
|
||||
"eraseshape",
|
||||
],
|
||||
[
|
||||
{
|
||||
name: "Overlay chart from CSV (Ctrl+O)",
|
||||
icon: ICONS.plotCsv,
|
||||
click: function () {
|
||||
// We make sure to close any other popup that might be open
|
||||
// before opening the CSV popup
|
||||
closePopup();
|
||||
openPopup("popup_csv");
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Add Text (Ctrl+T)",
|
||||
icon: ICONS.addText,
|
||||
click: function () {
|
||||
openPopup("popup_text");
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Change Titles (Ctrl+Shift+T)",
|
||||
icon: ICONS.changeTitle,
|
||||
click: function () {
|
||||
openPopup("popup_title");
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Change Theme",
|
||||
icon: ICONS.sunIcon,
|
||||
click: function () {
|
||||
changeTheme();
|
||||
},
|
||||
},
|
||||
],
|
||||
["hoverClosestCartesian", "hoverCompareCartesian", "toggleSpikelines"],
|
||||
[
|
||||
{
|
||||
name: "Auto Scale (Ctrl+Shift+A)",
|
||||
icon: Plotly.Icons.autoscale,
|
||||
click: function () {
|
||||
autoscaleButton();
|
||||
},
|
||||
},
|
||||
"zoomIn2d",
|
||||
"zoomOut2d",
|
||||
"autoScale2d",
|
||||
"zoom2d",
|
||||
"pan2d",
|
||||
],
|
||||
],
|
||||
};
|
||||
graphs.layout.title = "";
|
||||
|
||||
// We make sure to fill in any missing layout properties with default values
|
||||
if (!("font" in graphs.layout)) {
|
||||
graphs.layout["font"] = {
|
||||
family: "Fira Code, monospace, Arial Black",
|
||||
size: 18,
|
||||
};
|
||||
}
|
||||
graphs.layout.annotations = !graphs.layout.annotations
|
||||
? []
|
||||
: graphs.layout.annotations;
|
||||
|
||||
if (!("margin" in graphs.layout)) {
|
||||
graphs.layout["margin"] = {
|
||||
l: 0,
|
||||
r: 0,
|
||||
b: 0,
|
||||
t: 0,
|
||||
pad: 2,
|
||||
};
|
||||
}
|
||||
|
||||
// RIP: Juan hated it so had to remove it
|
||||
// graphs.layout.xaxis.tickangle = -20;
|
||||
|
||||
graphs.layout.autosize = true;
|
||||
// We set the height and width to undefined, so that plotly.js can
|
||||
// automatically resize the chart to fit the PyWry window
|
||||
delete graphs.layout.height;
|
||||
delete graphs.layout.width;
|
||||
|
||||
if (graphs.layout.annotations != undefined) {
|
||||
graphs.layout.annotations.forEach(function (annotation) {
|
||||
// We make sure to fill in any missing annotation properties with default values
|
||||
// We also make sure to set the font size to a reasonable value based on the
|
||||
// width of the client window
|
||||
if (!("font" in annotation) || !("size" in annotation.font)) {
|
||||
annotation["font"] = {
|
||||
family: "Fira Code, monospace, Arial Black",
|
||||
size: 16,
|
||||
};
|
||||
}
|
||||
|
||||
if (annotation.text != undefined) {
|
||||
if (annotation.text[0] == "/") {
|
||||
globals.cmd_src_idx = graphs.layout.annotations.indexOf(annotation);
|
||||
}
|
||||
}
|
||||
annotation.font.size = Math.min(
|
||||
globals.CHART_DIV.clientWidth / 50,
|
||||
annotation.font.size
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// We add spaces to all trace names, due to Fira Code font width issues
|
||||
// to make sure that the legend is not cut off
|
||||
graphs.data.forEach(function (trace) {
|
||||
if (trace.name != undefined) {
|
||||
let name_length = trace.name.length;
|
||||
trace.name = trace.name + " ";
|
||||
trace.hoverlabel = {
|
||||
namelength: name_length,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Set the default dragmode to pan and set the font size to a reasonable value
|
||||
graphs.layout.dragmode = "pan";
|
||||
graphs.layout.font.size = Math.min(
|
||||
globals.CHART_DIV.clientWidth / 50,
|
||||
graphs.layout.font.size
|
||||
);
|
||||
|
||||
// We check for the dark mode
|
||||
if (graphs.layout.template.layout.mapbox.style == "dark") {
|
||||
document.body.style.backgroundColor = "#000000";
|
||||
document.getElementById("openbb_footer").style.color = "#000000";
|
||||
graphs.layout.template.layout.paper_bgcolor = "#000000";
|
||||
graphs.layout.template.layout.plot_bgcolor = "#000000";
|
||||
} else {
|
||||
document.body.style.backgroundColor = "#FFFFFF";
|
||||
document.getElementById("openbb_footer").style.color = "#FFFFFF";
|
||||
}
|
||||
|
||||
// We set the plot config and plot the chart
|
||||
Plotly.setPlotConfig(CONFIG);
|
||||
Plotly.react(globals.CHART_DIV, graphs, { responsive: true });
|
||||
|
||||
let AXES = Object.keys(graphs.layout).filter(
|
||||
(x) => x.startsWith("xaxis") || x.startsWith("yaxis")
|
||||
);
|
||||
|
||||
// We set all the axes automargin to false
|
||||
if (AXES.length > 0) {
|
||||
AXES.forEach((axis) => {
|
||||
let margin = axis.startsWith("y") ? "l+r" : "t+b";
|
||||
graphs.layout[axis].automargin = margin;
|
||||
});
|
||||
Plotly.react(globals.CHART_DIV, graphs, { responsive: true });
|
||||
}
|
||||
|
||||
// Create global variables to for use later
|
||||
const modebar = document.getElementsByClassName("modebar-container")[0];
|
||||
const modebar_buttons = modebar.getElementsByClassName("modebar-btn");
|
||||
globals.barButtons = {};
|
||||
|
||||
const captureButtons = [
|
||||
"Download CSV",
|
||||
"Download Chart as Image",
|
||||
"Overlay chart from CSV",
|
||||
"Add Text",
|
||||
"Change Titles",
|
||||
"Auto Scale (Ctrl+Shift+A)",
|
||||
"Reset Axes",
|
||||
];
|
||||
|
||||
for (let i = 0; i < modebar_buttons.length; i++) {
|
||||
// We add the buttons to the global variable for later use
|
||||
// and set the border to transparent so we can change the
|
||||
// color of the buttons when they are pressed
|
||||
let button = modebar_buttons[i];
|
||||
if (captureButtons.includes(button.getAttribute("data-title"))) {
|
||||
button.classList.add("ph-capture");
|
||||
}
|
||||
button.style.border = "transparent";
|
||||
globals.barButtons[button.getAttribute("data-title")] = button;
|
||||
}
|
||||
|
||||
// We change the Plotly default Autoscale button icon and title to Reset Axes
|
||||
globals.barButtons["Reset Axes"] = globals.barButtons["Autoscale"];
|
||||
globals.barButtons["Autoscale"]
|
||||
.getElementsByTagName("path")[0]
|
||||
.setAttribute("d", Plotly.Icons.home.path);
|
||||
globals.barButtons["Autoscale"].setAttribute("data-title", "Reset Axes");
|
||||
|
||||
if (globals.CHART_DIV.layout.yaxis.type != undefined) {
|
||||
if (globals.CHART_DIV.layout.yaxis.type == "log" && !globals.logYaxis) {
|
||||
console.log("yaxis.type changed to log");
|
||||
globals.logYaxis = true;
|
||||
|
||||
// We update the yaxis exponent format to SI,
|
||||
// set the tickformat to '.0s' and the exponentbase to 100
|
||||
let layout_update = {
|
||||
"yaxis.exponentformat": "SI",
|
||||
"yaxis.tickformat": ".0s",
|
||||
"yaxis.exponentbase": 100,
|
||||
};
|
||||
Plotly.update(globals.CHART_DIV, layout_update);
|
||||
}
|
||||
if (globals.CHART_DIV.layout.yaxis.type == "linear" && globals.logYaxis) {
|
||||
console.log("yaxis.type changed to linear");
|
||||
globals.logYaxis = false;
|
||||
|
||||
// We update the yaxis exponent format to none,
|
||||
// set the tickformat to null and the exponentbase to 10
|
||||
let layout_update = {
|
||||
"yaxis.exponentformat": "none",
|
||||
"yaxis.tickformat": null,
|
||||
"yaxis.exponentbase": 10,
|
||||
};
|
||||
Plotly.update(globals.CHART_DIV, layout_update);
|
||||
}
|
||||
}
|
||||
|
||||
if (window.plotly_figure.layout.template.layout.mapbox.style === "light") {
|
||||
for (const el of document.styleSheets[0].cssRules) {
|
||||
if (el.selectorText === ".modebar-group") {
|
||||
el.style.backgroundColor = "#FFFFFF";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hideModebar() {
|
||||
if (globals.modebarHidden) {
|
||||
modebar.style.display = "flex";
|
||||
globals.modebarHidden = false;
|
||||
} else {
|
||||
modebar.style.display = "none";
|
||||
globals.modebarHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function autoscaleButton() {
|
||||
// We need to check if the button is active or not
|
||||
let title = "Auto Scale (Ctrl+Shift+A)";
|
||||
let button = globals.barButtons[title];
|
||||
let active = true;
|
||||
if (button.style.border == "transparent") {
|
||||
active = false;
|
||||
globals.CHART_DIV.on(
|
||||
"plotly_relayout",
|
||||
non_blocking(async function (eventdata) {
|
||||
if (eventdata["xaxis.range[0]"] == undefined) {
|
||||
return;
|
||||
}
|
||||
await autoScaling(eventdata, globals.CHART_DIV);
|
||||
}, 100)
|
||||
);
|
||||
} else {
|
||||
// If the button isn't active, we remove the listener so
|
||||
// the graphs don't autoscale anymore
|
||||
globals.CHART_DIV.removeAllListeners("plotly_relayout");
|
||||
}
|
||||
button_pressed(title, active);
|
||||
}
|
||||
|
||||
async function downloadImageButton() {
|
||||
let filename = globals.filename;
|
||||
let ext = "png";
|
||||
let writable;
|
||||
if (window.showSaveFilePicker) {
|
||||
const handle = await showSaveFilePicker({
|
||||
suggestedName: `${filename}.${ext}`,
|
||||
types: [
|
||||
{
|
||||
description: "PNG Image",
|
||||
accept: {
|
||||
"image/png": [".png"],
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "JPEG Image",
|
||||
accept: {
|
||||
"image/jpeg": [".jpeg"],
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "SVG Image",
|
||||
accept: {
|
||||
"image/svg+xml": [".svg"],
|
||||
},
|
||||
},
|
||||
],
|
||||
excludeAcceptAllOption: true,
|
||||
});
|
||||
writable = await handle.createWritable();
|
||||
filename = handle.name;
|
||||
ext = handle.name.split(".").pop();
|
||||
}
|
||||
|
||||
loadingOverlay(`Saving ${ext.toUpperCase()}`);
|
||||
hideModebar();
|
||||
non_blocking(async function () {
|
||||
await downloadImage(filename, ext, writable);
|
||||
setTimeout(function () {
|
||||
setTimeout(function () {
|
||||
loading.classList.remove("show");
|
||||
hideModebar();
|
||||
}, 50);
|
||||
}, 25);
|
||||
}, 2)();
|
||||
}
|
||||
|
||||
async function downloadCsvButton(gd) {
|
||||
let filename = globals.filename;
|
||||
let writable;
|
||||
|
||||
if (window.showSaveFilePicker) {
|
||||
const handle = await showSaveFilePicker({
|
||||
suggestedName: `${filename}.csv`,
|
||||
types: [
|
||||
{
|
||||
description: "CSV File",
|
||||
accept: {
|
||||
"image/csv": [".csv"],
|
||||
},
|
||||
},
|
||||
],
|
||||
excludeAcceptAllOption: true,
|
||||
});
|
||||
writable = await handle.createWritable();
|
||||
filename = handle.name;
|
||||
}
|
||||
|
||||
loadingOverlay("Saving CSV");
|
||||
setTimeout(async function () {
|
||||
await downloadData(gd, filename, writable);
|
||||
}, 500);
|
||||
setTimeout(function () {
|
||||
loading.classList.remove("show");
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// We setup keyboard shortcuts custom to OpenBB
|
||||
window.document.addEventListener("keydown", function (e) {
|
||||
if (e.key.toLowerCase() == "h" && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
hideModebar();
|
||||
}
|
||||
|
||||
if (e.ctrlKey) {
|
||||
if (e.shiftKey && e.key.toLowerCase() == "t") {
|
||||
openPopup("popup_title");
|
||||
} else if (e.key.toLowerCase() == "t") {
|
||||
openPopup("popup_text");
|
||||
} else if (e.key.toLowerCase() == "e") {
|
||||
changeColor();
|
||||
} else if (e.shiftKey && e.key.toLowerCase() == "s") {
|
||||
e.preventDefault();
|
||||
downloadCsvButton(globals.CHART_DIV);
|
||||
} else if (e.key.toLowerCase() == "s") {
|
||||
e.preventDefault();
|
||||
downloadImageButton();
|
||||
} else if (e.key.toLowerCase() == "o") {
|
||||
e.preventDefault();
|
||||
openPopup("popup_csv");
|
||||
} else if (e.shiftKey && e.key.toLowerCase() == "a") {
|
||||
e.preventDefault();
|
||||
autoscaleButton();
|
||||
}
|
||||
} else if (e.key == "Escape") {
|
||||
closePopup();
|
||||
}
|
||||
});
|
||||
|
||||
// send a relayout event to trigger the initial zoom/bars-resize
|
||||
// check if the xaxis.range is defined
|
||||
if (
|
||||
graphs.layout.xaxis != undefined &&
|
||||
graphs.layout.xaxis.range != undefined
|
||||
) {
|
||||
Plotly.relayout(globals.CHART_DIV, {
|
||||
"xaxis.range[0]": graphs.layout.xaxis.range[0],
|
||||
"xaxis.range[1]": graphs.layout.xaxis.range[1],
|
||||
});
|
||||
}
|
||||
|
||||
// We find all xaxis that have showticklabels set to true
|
||||
let XAXIS = Object.keys(globals.CHART_DIV.layout)
|
||||
.filter((x) => x.startsWith("xaxis"))
|
||||
.filter((x) => globals.CHART_DIV.layout[x].showticklabels);
|
||||
|
||||
let TRACES = globals.CHART_DIV.data.filter((trace) =>
|
||||
trace?.name?.startsWith("Volume")
|
||||
);
|
||||
|
||||
window.addEventListener("resize", function () {
|
||||
// We check to see if the window.innerWidth is smaller than 900px
|
||||
let layout_update = {};
|
||||
let width = window.innerWidth;
|
||||
let height = window.innerHeight;
|
||||
let tick_size =
|
||||
height > 420 && width < 920 ? 9 : height > 420 && width < 500 ? 10 : 8;
|
||||
|
||||
if (width < 920) {
|
||||
// We hide the modebar and set the number of ticks to 5
|
||||
TRACES.forEach((trace) => {
|
||||
if (trace.type == "bar") {
|
||||
trace.opacity = 1;
|
||||
trace.marker.line.width = 0.09;
|
||||
if (globals.volume.yaxis == undefined) {
|
||||
globals.volume.yaxis = "yaxis" + trace.yaxis.replace("y", "");
|
||||
layout_update[globals.volume.yaxis + ".tickfont.size"] = tick_size;
|
||||
|
||||
globals.volume.tickfont =
|
||||
globals.CHART_DIV.layout[globals.volume.yaxis].tickfont;
|
||||
|
||||
globals.CHART_DIV.layout.margin.l -= 40;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
XAXIS.forEach((x) => {
|
||||
if (globals.old_nticks?.[x] == undefined) {
|
||||
layout_update[x + ".nticks"] = 6;
|
||||
globals.old_nticks[x] = globals.CHART_DIV.layout[x].nticks || 10;
|
||||
}
|
||||
});
|
||||
|
||||
modebar.style.display = "none";
|
||||
globals.modebarHidden = true;
|
||||
} else if (globals.modebarHidden) {
|
||||
// We show the modebar
|
||||
modebar.style.display = "flex";
|
||||
globals.modebarHidden = false;
|
||||
|
||||
if (globals.old_nticks != undefined) {
|
||||
XAXIS.forEach((x) => {
|
||||
if (globals.old_nticks[x] != undefined) {
|
||||
layout_update[x + ".nticks"] = globals.old_nticks[x];
|
||||
globals.old_nticks[x] = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (globals.volume.yaxis != undefined) {
|
||||
TRACES.forEach((trace) => {
|
||||
if (trace.type == "bar") {
|
||||
trace.opacity = 0.5;
|
||||
trace.marker.line.width = 0.2;
|
||||
layout_update[globals.volume.yaxis + ".tickfont.size"] =
|
||||
globals.volume.tickfont.size + 3;
|
||||
globals.CHART_DIV.layout.margin.l += 40;
|
||||
globals.volume.yaxis = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(layout_update).length > 0) {
|
||||
Plotly.relayout(globals.CHART_DIV, layout_update);
|
||||
}
|
||||
});
|
||||
|
||||
// We check to see if window.save_png is defined and true
|
||||
if (window.save_image != undefined && window.export_image) {
|
||||
// We get the extension of the file and check if it is valid
|
||||
let filename = window.export_image.split("/").pop();
|
||||
const extension = filename.split(".").pop().replace("jpg", "jpeg");
|
||||
|
||||
if (["jpeg", "png", "svg", "pdf"].includes(extension)) {
|
||||
hideModebar();
|
||||
non_blocking(function () {
|
||||
downloadImage(filename.split(".")[0], extension);
|
||||
}, 2)();
|
||||
}
|
||||
}
|
||||
function changeTheme() {
|
||||
globals.dark_mode = !globals.dark_mode;
|
||||
document.body.style.backgroundColor = globals.dark_mode ? "#000" : "#fff";
|
||||
globals.CHART_DIV.layout.font.color = globals.dark_mode ? "#fff" : "#000";
|
||||
|
||||
const changeIcon = globals.dark_mode ? ICONS.sunIcon : ICONS.moonIcon;
|
||||
|
||||
globals.barButtons["Change Theme"]
|
||||
.getElementsByTagName("path")[0]
|
||||
.setAttribute("d", changeIcon.path);
|
||||
|
||||
globals.barButtons["Change Theme"]
|
||||
.getElementsByTagName("svg")[0]
|
||||
.setAttribute("viewBox", changeIcon.viewBox);
|
||||
|
||||
const volumeColors = {
|
||||
"#e4003a": "#c80000",
|
||||
"#00ACFF": "#009600",
|
||||
"#009600": "#00ACFF",
|
||||
"#c80000": "#e4003a",
|
||||
};
|
||||
|
||||
TRACES.forEach((trace) => {
|
||||
if (trace.type == "bar") {
|
||||
trace.marker.color.forEach((color, i) => {
|
||||
trace.marker.color[i] = volumeColors[color] || color;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Plotly.update(
|
||||
globals.CHART_DIV,
|
||||
{},
|
||||
{
|
||||
template: globals.dark_mode
|
||||
? DARK_CHARTS_TEMPLATE
|
||||
: LIGHT_CHARTS_TEMPLATE,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
119
openbb_terminal/core/plots/web/main_table.js
vendored
119
openbb_terminal/core/plots/web/main_table.js
vendored
@@ -1,119 +0,0 @@
|
||||
function TableProcessor(data) {
|
||||
const parsedData = JSON.parse(data);
|
||||
console.log(parsedData);
|
||||
const processedData = processData(parsedData);
|
||||
console.log(processedData);
|
||||
const body = document.getElementsByTagName("body")[0];
|
||||
const table = new Tabulator("#example-table", {
|
||||
height: body.clientHeight - 54,
|
||||
layout: "fitColumns",
|
||||
data: processedData.data,
|
||||
columns: processedData.columns,
|
||||
});
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.innerHTML = `
|
||||
<div style="margin-top:10px;">
|
||||
<select id="filter-field">
|
||||
<option></option>
|
||||
${processedData.columns.map((column) => {
|
||||
return `<option value="${column.field}">${column.title}</option>`;
|
||||
})}
|
||||
</select>
|
||||
|
||||
<select id="filter-type">
|
||||
<option value="=">=</option>
|
||||
<option value="<"><</option>
|
||||
<option value="<="><=</option>
|
||||
<option value=">">></option>
|
||||
<option value=">=">>=</option>
|
||||
<option value="!=">!=</option>
|
||||
<option value="like">like</option>
|
||||
</select>
|
||||
|
||||
<input id="filter-value" type="text" placeholder="value to filter">
|
||||
|
||||
<button id="filter-clear">Clear Filter</button>
|
||||
</div>
|
||||
|
||||
`;
|
||||
body.appendChild(div);
|
||||
const filterField = document.getElementById("filter-field");
|
||||
const filterType = document.getElementById("filter-type");
|
||||
const filterValue = document.getElementById("filter-value");
|
||||
const filterClear = document.getElementById("filter-clear");
|
||||
|
||||
filterField.addEventListener("change", function (e) {
|
||||
table.setFilter(filterField.value, filterType.value, filterValue.value);
|
||||
});
|
||||
|
||||
filterType.addEventListener("change", function (e) {
|
||||
table.setFilter(filterField.value, filterType.value, filterValue.value);
|
||||
});
|
||||
|
||||
filterValue.addEventListener("keyup", function (e) {
|
||||
table.setFilter(filterField.value, filterType.value, filterValue.value);
|
||||
});
|
||||
|
||||
filterClear.addEventListener("click", function (e) {
|
||||
table.clearFilter();
|
||||
});
|
||||
}
|
||||
|
||||
function processData(data) {
|
||||
/*
|
||||
data is coming as {
|
||||
columns: ["Name", "Age"],
|
||||
data: ["John", 30]
|
||||
}
|
||||
*/
|
||||
/*
|
||||
final data should be {
|
||||
columns: [
|
||||
{
|
||||
title: "Name",
|
||||
field: "name",
|
||||
sorter: "string",
|
||||
},
|
||||
{
|
||||
title: "Age",
|
||||
field: "age",
|
||||
sorter: "number",
|
||||
}
|
||||
],
|
||||
data: {
|
||||
name: "John",
|
||||
age: "30",
|
||||
}
|
||||
}
|
||||
*/
|
||||
const columns = data.columns.map((column) => {
|
||||
return {
|
||||
title: column,
|
||||
field: column,
|
||||
sorter: column.includes("Change"),
|
||||
/*formatter: (cell) => {
|
||||
if (column.startsWith("Change")) {
|
||||
if (cell.getValue() > 0) {
|
||||
return `<span style="color: green">${cell.getValue()}</span>`;
|
||||
} else {
|
||||
return `<span style="color: red">${cell.getValue()}</span>`;
|
||||
}
|
||||
} else {
|
||||
return cell.getValue();
|
||||
}
|
||||
},*/
|
||||
};
|
||||
});
|
||||
const finalData = data.data.map((row) => {
|
||||
const obj = {};
|
||||
row.forEach((item, index) => {
|
||||
obj[data.columns[index]] = item;
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
return {
|
||||
columns,
|
||||
data: finalData,
|
||||
};
|
||||
}
|
||||
738
openbb_terminal/core/plots/web/popups.js
vendored
738
openbb_terminal/core/plots/web/popups.js
vendored
@@ -1,738 +0,0 @@
|
||||
function get_popup(data = null, popup_id = null) {
|
||||
let popup = null;
|
||||
popup_id = popup_id.replace("popup_", "");
|
||||
const style =
|
||||
"padding: 5px 2px 2px 5px !important; margin: 2px 0 !important;";
|
||||
|
||||
if (popup_id == "title") {
|
||||
let title = globals.title;
|
||||
data = globals.CHART_DIV.layout;
|
||||
let yaxes = Object.keys(data).filter((k) => k.startsWith("yaxis"));
|
||||
let xaxes = Object.keys(data).filter((k) => k.startsWith("xaxis"));
|
||||
|
||||
globals.TITLE_DIV.innerHTML = `
|
||||
<div style="display:flex; flex-direction: column; gap: 6px;">
|
||||
<div>
|
||||
<label for="title_text">Title:</label>
|
||||
<textarea
|
||||
id="title_text" style="${style} width: 100%; max-width: 100%;
|
||||
max-height: 200px; margin-top: 8px;" rows="2" cols="20"
|
||||
value="${title}">${title}</textarea>
|
||||
</div>
|
||||
<div id="xaxis_div"></div>
|
||||
<div id="yaxis_div"></div>
|
||||
</div>
|
||||
|
||||
<div style="float: right; margin-top: 20px;">
|
||||
<button class="_btn-tertiary ph-capture" id="title_cancel" onclick="closePopup()">Cancel</button>
|
||||
<button class="_btn ph-capture" id="title_submit" onclick="on_submit('title')">Submit</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// if there are multiple titles we create an input for each
|
||||
let yaxis_div = "";
|
||||
for (let i = 0; i < yaxes.length; i++) {
|
||||
if (data[yaxes[i]]?.title?.text != undefined) {
|
||||
let title = data[yaxes[i]].title.text;
|
||||
yaxis_div += `
|
||||
<div style="margin-top: 10px;">
|
||||
<label for="title_${yaxes[i]}">Y axis ${i + 1}:</label>
|
||||
<input id="title_${
|
||||
yaxes[i]
|
||||
}" style="${style}" type="text" value="${title}"></input>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
if (yaxis_div != "") {
|
||||
globals.TITLE_DIV.querySelector("#yaxis_div").innerHTML = yaxis_div;
|
||||
}
|
||||
|
||||
let xaxis_div = "";
|
||||
for (let i = 0; i < xaxes.length; i++) {
|
||||
if (data[xaxes[i]]?.title?.text != undefined) {
|
||||
let title = data[xaxes[i]].title.text;
|
||||
xaxis_div += `
|
||||
<div style="margin-top: 10px;">
|
||||
<label for="title_${xaxes[i]}">X axis ${i + 1}:</label>
|
||||
<input id="title_${
|
||||
xaxes[i]
|
||||
}" style="${style}" type="text" value="${title}"></input>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
if (xaxis_div != "") {
|
||||
globals.TITLE_DIV.querySelector("#xaxis_div").innerHTML = xaxis_div;
|
||||
}
|
||||
|
||||
// when opening the popup, we make sure to focus on the title input
|
||||
globals.TITLE_DIV.style.display = "inline-block";
|
||||
globals.TITLE_DIV.querySelector("#title_text").focus();
|
||||
popup = globals.TITLE_DIV;
|
||||
} else if (popup_id == "text") {
|
||||
let has_annotation = data == undefined ? false : true;
|
||||
if (data == undefined) {
|
||||
data = {
|
||||
text: "",
|
||||
font: {
|
||||
color: "#0088CC",
|
||||
size: 18,
|
||||
},
|
||||
bordercolor: "#822661",
|
||||
};
|
||||
}
|
||||
|
||||
// we replace <br> with \n so that the textarea can display the text properly
|
||||
data.text = data.text.replace(/<br>/g, "\n");
|
||||
|
||||
let yanchor =
|
||||
globals.TEXT_DIV.querySelector("#addtext_above") == null
|
||||
? "above"
|
||||
: globals.TEXT_DIV.querySelector("#addtext_above").checked
|
||||
? "above"
|
||||
: "below";
|
||||
|
||||
globals.TEXT_DIV.innerHTML = `
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label for="popup_textarea"><b>Text:</b>
|
||||
<div id="popup_textarea_warning" class="popup_warning">Text is required</div></label><br>
|
||||
<textarea id="addtext_textarea" style="${style}
|
||||
width: 100%; max-width: 100%; max-height: 200px;
|
||||
margin-top: 8px;" rows="4" cols="50" value="${data.text}"
|
||||
placeholder="Enter text here">${data.text}</textarea><br>
|
||||
</div>
|
||||
<div style="display:flex;justify-content: space-between;">
|
||||
<div>
|
||||
<label for="addtext_color"><b>Font color:</b></label>
|
||||
<input type="color" id="addtext_color" value="${
|
||||
data.font.color
|
||||
}"></input>
|
||||
</div>
|
||||
<div>
|
||||
<label for="addtext_border"><b>Border color:</b></label>
|
||||
<input type="color" id="addtext_border" value="${
|
||||
data.bordercolor
|
||||
}"></input>
|
||||
</div>
|
||||
</div><br>
|
||||
|
||||
<div style="display:flex;justify-content: space-between;">
|
||||
<div>
|
||||
<label for="addtext_size"><b>Font size:</b></label>
|
||||
<input style="${style} width: 45px;" type="number" id="addtext_size" value="${
|
||||
data.font.size
|
||||
}"></input>
|
||||
</div>
|
||||
<div>
|
||||
<label for="addtext_above"><b>Position (Above):</b></label>
|
||||
<input type="checkbox" id="addtext_above" name="check"
|
||||
value="above" ${yanchor == "above" ? "checked" : ""}></input>
|
||||
</div>
|
||||
</div><br>
|
||||
`;
|
||||
|
||||
if (has_annotation) {
|
||||
globals.TEXT_DIV.innerHTML += `
|
||||
<div style="float: right; margin-top: 20px;">
|
||||
<button class="_btn-tertiary ph-capture" id="addtext_cancel" onclick="closePopup()">Cancel</button>
|
||||
<button class="_btn ph-capture" id="addtext_delete" onclick="on_delete('text')">Delete</button>
|
||||
<button class="_btn ph-capture" id="addtext_submit" onclick="on_submit('text', true)">Submit</button>
|
||||
<input id="addtext_annotation" type="hidden" value='${JSON.stringify(
|
||||
data
|
||||
)}'></input>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
globals.TEXT_DIV.innerHTML += `
|
||||
<div style="float: right; margin-top: 20px;">
|
||||
<button class="_btn-tertiary ph-capture" id="addtext_cancel" onclick="closePopup()">Cancel</button>
|
||||
<button class="_btn ph-capture" id="addtext_submit" onclick="on_submit('text')">Submit</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (globals.TEXT_DIV.querySelector("#addtext_annotation") != null) {
|
||||
globals.TEXT_DIV.querySelector("#addtext_annotation").remove();
|
||||
}
|
||||
}
|
||||
|
||||
// when opening the popup, we make sure to focus on the textarea
|
||||
globals.TEXT_DIV.style.display = "inline-block";
|
||||
globals.TEXT_DIV.querySelector("#addtext_textarea").focus();
|
||||
popup = globals.TEXT_DIV;
|
||||
} else if (popup_id == "csv") {
|
||||
globals.CSV_DIV.style.display = "inline-block";
|
||||
popup = globals.CSV_DIV;
|
||||
console.log("csv");
|
||||
} else if (popup_id == "upload") {
|
||||
globals.TEXT_DIV.style.display = "inline-block";
|
||||
globals.TEXT_DIV.innerHTML = `
|
||||
<div>
|
||||
<p>Media preview (you can share it on twitter):</p>
|
||||
<a style="margin-top: 10px;" href="${data.url}" target="_blank" rel="noreferrer noopener">${data.url}</a>
|
||||
<div style="margin-top: 10px; float: right;">
|
||||
<button onclick="closePopup()" style="margin-top: 10px;" class="_btn-tertiary">Close</button>
|
||||
<a
|
||||
class="_btn"
|
||||
href="https://twitter.com/intent/tweet?text=Check this chart from @openbb_finance - ${data.url}"
|
||||
>Share</a>
|
||||
<button onclick="navigator.clipboard.writeText('${data.url}')" style="margin-top: 10px;"
|
||||
class="_btn">Copy to clipboard</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
popup = globals.TEXT_DIV;
|
||||
console.log("upload");
|
||||
} else if (popup_id == "download") {
|
||||
popup = globals.DOWNLOAD_DIV;
|
||||
|
||||
popup.querySelector("#download_path").innerHTML = `
|
||||
<textarea style="${style} width: 100%; max-width: 100%; max-height: 200px;
|
||||
margin-top: 8px;" rows="4" cols="50" value="${data}"
|
||||
placeholder="Enter text here">${data}</textarea><br>
|
||||
`;
|
||||
|
||||
openfile_button = popup.querySelector("#openfile_button");
|
||||
openfile_button.onclick = function () {
|
||||
window.ipc.postMessage(`#OPEN:${data}`);
|
||||
};
|
||||
|
||||
openfolder_button = popup.querySelector("#openfolder_button");
|
||||
openfolder_button.onclick = function () {
|
||||
folder_path = data.split("\\").slice(0, -1).join("\\");
|
||||
window.ipc.postMessage(`#OPEN:${folder_path}`);
|
||||
};
|
||||
|
||||
globals.DOWNLOAD_DIV.style.display = "inline-block";
|
||||
}
|
||||
|
||||
let popup_divs = [
|
||||
globals.TITLE_DIV,
|
||||
globals.TEXT_DIV,
|
||||
globals.CSV_DIV,
|
||||
globals.DOWNLOAD_DIV,
|
||||
];
|
||||
popup_divs.forEach(function (div) {
|
||||
if (div.id != popup.id) {
|
||||
div.style.display = "none";
|
||||
}
|
||||
});
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
function get_popup_data(popup_id = null) {
|
||||
// popup_id is either 'title', 'text', or 'csv' (for now)
|
||||
// and is used to determine which popup to get data from
|
||||
let data = null;
|
||||
|
||||
if (popup_id == "title") {
|
||||
data = { title: globals.TITLE_DIV.querySelector("#title_text").value };
|
||||
|
||||
let xaxis_div = globals.TITLE_DIV.querySelector("#xaxis_div");
|
||||
let yaxis_div = globals.TITLE_DIV.querySelector("#yaxis_div");
|
||||
console.log("xaxis_div: ", xaxis_div, "yaxis_div: ", yaxis_div);
|
||||
|
||||
if (xaxis_div != null) {
|
||||
// We query all inputs that start with 'title_xaxis'
|
||||
let xaxis_inputs = xaxis_div.querySelectorAll(
|
||||
"input[id^=title_xaxis], select[id^=title_xaxis]"
|
||||
);
|
||||
xaxis_inputs.forEach(function (input) {
|
||||
let xaxis_id = input.id.replace("title_", "");
|
||||
data[xaxis_id] = input.value;
|
||||
});
|
||||
}
|
||||
|
||||
if (yaxis_div != null) {
|
||||
// We query all inputs that start with 'title_yaxis'
|
||||
let yaxis_inputs = yaxis_div.querySelectorAll(
|
||||
"input[id^=title_yaxis], select[id^=title_yaxis]"
|
||||
);
|
||||
yaxis_inputs.forEach(function (input) {
|
||||
let yaxis_id = input.id.replace("title_", "");
|
||||
data[yaxis_id] = input.value;
|
||||
});
|
||||
}
|
||||
console.log("Title data:", data);
|
||||
} else if (popup_id == "text") {
|
||||
data = {
|
||||
text: globals.TEXT_DIV.querySelector("#addtext_textarea").value,
|
||||
color: globals.TEXT_DIV.querySelector("#addtext_color").value,
|
||||
size: globals.TEXT_DIV.querySelector("#addtext_size").value,
|
||||
yanchor: globals.TEXT_DIV.querySelector("#addtext_above").checked
|
||||
? "above"
|
||||
: "below",
|
||||
bordercolor: globals.TEXT_DIV.querySelector("#addtext_border").value,
|
||||
};
|
||||
if (globals.TEXT_DIV.querySelector("#addtext_annotation") != null) {
|
||||
data.annotation = JSON.parse(
|
||||
globals.TEXT_DIV.querySelector("#addtext_annotation").value
|
||||
);
|
||||
}
|
||||
|
||||
// we replace \n with <br> so that line breaks are displayed properly on the graph
|
||||
data.text = data.text.replace(/\n/g, "<br>");
|
||||
console.log("Text data:", data);
|
||||
} else if (popup_id == "csv") {
|
||||
let trace_type = globals.CSV_DIV.querySelector("#csv_trace_type").value;
|
||||
if (trace_type == "candlestick") {
|
||||
data = {
|
||||
x: globals.CSV_DIV.querySelector("#csv_x").value,
|
||||
open: globals.CSV_DIV.querySelector("#csv_open").value,
|
||||
high: globals.CSV_DIV.querySelector("#csv_high").value,
|
||||
low: globals.CSV_DIV.querySelector("#csv_low").value,
|
||||
close: globals.CSV_DIV.querySelector("#csv_close").value,
|
||||
increasing: globals.CSV_DIV.querySelector("#csv_increasing").value,
|
||||
decreasing: globals.CSV_DIV.querySelector("#csv_decreasing").value,
|
||||
same_yaxis: globals.CSV_DIV.querySelector("#csv_same_yaxis").checked
|
||||
? true
|
||||
: false,
|
||||
};
|
||||
} else if (trace_type == "scatter") {
|
||||
data = {
|
||||
x: globals.CSV_DIV.querySelector("#csv_x").value,
|
||||
y: globals.CSV_DIV.querySelector("#csv_y").value,
|
||||
color: globals.CSV_DIV.querySelector("#csv_color").value,
|
||||
percent_change: globals.CSV_DIV.querySelector("#csv_percent_change")
|
||||
.checked
|
||||
? true
|
||||
: false,
|
||||
same_yaxis: globals.CSV_DIV.querySelector("#csv_same_yaxis").checked
|
||||
? true
|
||||
: false,
|
||||
};
|
||||
} else if (trace_type == "bar") {
|
||||
let orientation = globals.CSV_DIV.querySelector("#csv_bar_horizontal")
|
||||
.checked
|
||||
? "h"
|
||||
: "v";
|
||||
data = {
|
||||
x: globals.CSV_DIV.querySelector("#csv_x").value,
|
||||
y: globals.CSV_DIV.querySelector("#csv_y").value,
|
||||
color: globals.CSV_DIV.querySelector("#csv_color").value,
|
||||
same_yaxis: globals.CSV_DIV.querySelector("#csv_same_yaxis").checked
|
||||
? true
|
||||
: false,
|
||||
orientation: orientation,
|
||||
};
|
||||
}
|
||||
data.trace_type = trace_type;
|
||||
data.name = globals.CSV_DIV.querySelector("#csv_name").value;
|
||||
console.log("CSV data:", data);
|
||||
data.file = globals.CSV_DIV.querySelector("#csv_file");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
const layout_defaults = {
|
||||
overlaying: "y",
|
||||
side: "left",
|
||||
tickfont: { size: 12 },
|
||||
tickpadding: 5,
|
||||
showgrid: false,
|
||||
showline: false,
|
||||
showticklabels: true,
|
||||
showlegend: true,
|
||||
zeroline: false,
|
||||
anchor: "x",
|
||||
type: "linear",
|
||||
autorange: true,
|
||||
};
|
||||
|
||||
function on_submit(popup_id, on_annotation = null) {
|
||||
// popup_id is either 'title', 'text', or 'csv' (for now)
|
||||
// and is used to determine which popup to get data from
|
||||
let popup_data = get_popup_data(popup_id);
|
||||
let gd = globals.CHART_DIV;
|
||||
Plotly.update(gd, {}, { hovermode: "closest" });
|
||||
|
||||
if (popup_id == "text") {
|
||||
if (!popup_data.text == "") {
|
||||
if ("annotation" in popup_data) {
|
||||
let current_text = popup_data.annotation.text;
|
||||
let data = {
|
||||
x: popup_data.annotation.x,
|
||||
y: popup_data.annotation.y,
|
||||
yref: popup_data.annotation.yref,
|
||||
};
|
||||
plot_text(data, popup_data, current_text);
|
||||
return;
|
||||
}
|
||||
|
||||
gd.on("plotly_clickannotation", function (eventData) {
|
||||
console.log("plotly_clickannotation", eventData);
|
||||
let annotation = eventData.annotation;
|
||||
openPopup("popup_text");
|
||||
get_popup(annotation, (popup_id = "text"));
|
||||
|
||||
if (on_annotation != null) {
|
||||
let data = {
|
||||
x: popup_data.annotation.x,
|
||||
y: popup_data.annotation.y,
|
||||
yref: popup_data.annotation.yref,
|
||||
};
|
||||
plot_text(data, popup_data, popup_data.annotation.text);
|
||||
}
|
||||
Plotly.update(gd, {}, { hovermode: "x" });
|
||||
});
|
||||
|
||||
let clickHandler = function (eventData) {
|
||||
console.log("plotly_click", eventData);
|
||||
let x = eventData.points[0].x;
|
||||
let yaxis = eventData.points[0].fullData.yaxis;
|
||||
let y = 0;
|
||||
|
||||
// We need to check if the trace is a candlestick or not
|
||||
// this is because the y value is stored in the high or low
|
||||
if (eventData.points[0].y != undefined) {
|
||||
y = eventData.points[0].y;
|
||||
} else if (eventData.points[0].low != undefined) {
|
||||
if (popup_data.yanchor == "below") {
|
||||
y = eventData.points[0].low;
|
||||
} else {
|
||||
y = eventData.points[0].high;
|
||||
}
|
||||
}
|
||||
let data = {
|
||||
x: x,
|
||||
y: y,
|
||||
yref: yaxis,
|
||||
};
|
||||
plot_text(data, popup_data);
|
||||
Plotly.update(gd, {}, { hovermode: "x" });
|
||||
};
|
||||
|
||||
Plotly.update(gd, {}, { dragmode: "select" });
|
||||
gd.on("plotly_click", clickHandler);
|
||||
} else {
|
||||
let textarea = globals.TEXT_DIV.querySelector("#addtext_textarea");
|
||||
document.getElementById("popup_textarea_warning").style.display = "block";
|
||||
textarea.style.border = "1px solid red";
|
||||
textarea.focus();
|
||||
|
||||
globals.TEXT_DIV.style.display = "inline-block";
|
||||
return;
|
||||
}
|
||||
} else if (popup_id == "title") {
|
||||
document.getElementById("title").innerHTML = popup_data.title;
|
||||
globals.title = popup_data.title;
|
||||
|
||||
let to_update = {};
|
||||
|
||||
Object.keys(popup_data).forEach(function (key) {
|
||||
if (key != "title") {
|
||||
to_update[key + ".title"] = popup_data[key];
|
||||
}
|
||||
});
|
||||
|
||||
console.log(to_update);
|
||||
|
||||
Plotly.update(gd, {}, to_update);
|
||||
} else if (popup_id == "csv") {
|
||||
console.log("got popup file");
|
||||
let popup_file = popup_data.file;
|
||||
|
||||
if (popup_file.files.length > 0) {
|
||||
console.log("file selected");
|
||||
|
||||
let file = popup_file.files[0];
|
||||
let popup_file_reader = new FileReader();
|
||||
popup_file_reader.onload = function (e) {
|
||||
let lines = e.target.result
|
||||
.split("\n")
|
||||
.map((x) => x.replace(/\r/g, ""));
|
||||
let data = [];
|
||||
let headers = lines[0].split(",");
|
||||
let trace = {};
|
||||
let new_yaxis = false;
|
||||
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
let obj = {};
|
||||
let currentline = lines[i].split(",");
|
||||
for (let j = 0; j < headers.length; j++) {
|
||||
obj[headers[j]] = currentline[j];
|
||||
}
|
||||
data.push(obj);
|
||||
}
|
||||
|
||||
// We get main plotly trace to get the x/y axis
|
||||
let main_trace = gd.data[0];
|
||||
|
||||
// We check how many yaxis have ticklabels on the left
|
||||
let left_yaxis_ticks = Object.keys(gd.layout)
|
||||
.filter((k) => k.startsWith("yaxis"))
|
||||
.map((k) => gd.layout[k])
|
||||
.filter(
|
||||
(yaxis) =>
|
||||
yaxis.side == "left" &&
|
||||
(yaxis.overlaying == "y" ||
|
||||
(yaxis.fixedrange != undefined && yaxis.fixedrange == true))
|
||||
).length;
|
||||
|
||||
// Multiply by 5 to get the xshift for cmd source text
|
||||
let add_xshift = Math.min(left_yaxis_ticks * 5, 30);
|
||||
|
||||
// We set showlegend's to true
|
||||
main_trace.showlegend = true;
|
||||
gd.layout.showlegend = true;
|
||||
|
||||
// Just in case xaxis/yaxis is not defined
|
||||
if (main_trace.xaxis == undefined) {
|
||||
main_trace.xaxis = "x";
|
||||
}
|
||||
if (main_trace.yaxis == undefined) {
|
||||
main_trace.yaxis = "y";
|
||||
}
|
||||
|
||||
// Set the yaxis id
|
||||
let yaxis_id = main_trace.yaxis;
|
||||
let yaxis;
|
||||
|
||||
// If we want to plot on a secondary yaxis
|
||||
// we get the number of yaxis and add 1 to it
|
||||
if (popup_data.same_yaxis == false) {
|
||||
let yaxes = Object.keys(gd.layout)
|
||||
.filter((k) => k.startsWith("yaxis"))
|
||||
.map((k) => gd.layout[k]);
|
||||
|
||||
yaxis = `y${yaxes.length + 1}`;
|
||||
yaxis_id = `yaxis${yaxes.length + 1}`;
|
||||
console.log(`yaxis: ${yaxis} ${yaxis_id}`);
|
||||
|
||||
// If percent change is true we set the yaxis id
|
||||
// in the globals so we can use it later
|
||||
if (
|
||||
globals.csv_yaxis_id == null &&
|
||||
popup_data.percent_change == true
|
||||
) {
|
||||
globals.csv_yaxis_id = yaxis_id;
|
||||
globals.csv_yaxis = yaxis;
|
||||
}
|
||||
} else {
|
||||
// Plot on the same yaxis
|
||||
yaxis = main_trace.yaxis.replace("yaxis", "y");
|
||||
}
|
||||
|
||||
// We get the yaxis ticksuffix
|
||||
let ticksuffix = left_yaxis_ticks > 0 ? " " : "";
|
||||
if (globals.percent_yaxis_added || globals.added_traces.length > 0) {
|
||||
ticksuffix = " ".repeat(left_yaxis_ticks);
|
||||
}
|
||||
|
||||
// Bar trace
|
||||
if (popup_data.trace_type == "bar") {
|
||||
trace = {
|
||||
x: data.map(function (row) {
|
||||
return row[popup_data.x];
|
||||
}),
|
||||
y: data.map(function (row) {
|
||||
return row[popup_data.y];
|
||||
}),
|
||||
type: popup_data.trace_type,
|
||||
name: popup_data.name,
|
||||
marker: { color: popup_data.color, opacity: 0.7 },
|
||||
orientation: popup_data.orientation,
|
||||
showlegend: true,
|
||||
yaxis: yaxis,
|
||||
};
|
||||
}
|
||||
|
||||
// Candlestick trace
|
||||
if (popup_data.trace_type == "candlestick") {
|
||||
trace = {
|
||||
x: data.map(function (row) {
|
||||
return row[popup_data.x];
|
||||
}),
|
||||
open: data.map(function (row) {
|
||||
return row[popup_data.open];
|
||||
}),
|
||||
high: data.map(function (row) {
|
||||
return row[popup_data.high];
|
||||
}),
|
||||
low: data.map(function (row) {
|
||||
return row[popup_data.low];
|
||||
}),
|
||||
close: data.map(function (row) {
|
||||
return row[popup_data.close];
|
||||
}),
|
||||
type: popup_data.trace_type,
|
||||
name: popup_data.name,
|
||||
increasing: {
|
||||
line: { color: popup_data.increasing },
|
||||
fillcolor: popup_data.increasing,
|
||||
},
|
||||
decreasing: {
|
||||
line: { color: popup_data.decreasing },
|
||||
fillcolor: popup_data.decreasing,
|
||||
},
|
||||
showlegend: true,
|
||||
yaxis: yaxis,
|
||||
};
|
||||
}
|
||||
|
||||
// Scatter trace
|
||||
if (popup_data.trace_type == "scatter") {
|
||||
// We get the first non null value
|
||||
orginal_data = data;
|
||||
let non_null = data.findIndex(
|
||||
(x) => x[popup_data.y] != null && x[popup_data.y] != 0
|
||||
);
|
||||
console.log(`non_null: ${non_null} ${data[non_null][popup_data.y]}`);
|
||||
|
||||
trace = {
|
||||
x: data.map(function (row) {
|
||||
return row[popup_data.x];
|
||||
}),
|
||||
y: data.map(function (row) {
|
||||
if (popup_data.percent_change) {
|
||||
return (
|
||||
(row[popup_data.y] - data[non_null][popup_data.y]) /
|
||||
data[non_null][popup_data.y]
|
||||
);
|
||||
} else {
|
||||
return row[popup_data.y];
|
||||
}
|
||||
}),
|
||||
type: popup_data.trace_type,
|
||||
mode: "lines",
|
||||
name: popup_data.name,
|
||||
line: { color: popup_data.color },
|
||||
customdata: data.map(function (row) {
|
||||
return row[popup_data.y];
|
||||
}),
|
||||
hovertemplate: "%{customdata}<extra></extra>",
|
||||
showlegend: true,
|
||||
connectgaps: true,
|
||||
xaxis: main_trace.xaxis,
|
||||
yaxis: popup_data.percent_change ? globals.csv_yaxis : yaxis,
|
||||
};
|
||||
|
||||
// For the percent change we add a new yaxis
|
||||
// if it doesn't exist
|
||||
if (
|
||||
!globals.percent_yaxis_added &&
|
||||
popup_data.percent_change == true &&
|
||||
popup_data.same_yaxis == false
|
||||
) {
|
||||
gd.layout[yaxis_id] = {
|
||||
...layout_defaults,
|
||||
title: {
|
||||
text: "% Change",
|
||||
font: {
|
||||
size: 14,
|
||||
},
|
||||
standoff: 0,
|
||||
},
|
||||
ticksuffix: ticksuffix,
|
||||
tickformat: ".0%",
|
||||
};
|
||||
new_yaxis = true;
|
||||
globals.percent_yaxis_added = true;
|
||||
if (globals.cmd_src_idx != null) {
|
||||
let xshift = gd.layout.annotations[globals.cmd_src_idx].xshift;
|
||||
xshift -= left_yaxis_ticks > 0 ? 50 + add_xshift : 35;
|
||||
|
||||
// just in case we have a lot of yaxis
|
||||
xshift += xshift < -320 ? 10 + 2 * left_yaxis_ticks : 0;
|
||||
|
||||
gd.layout.annotations[globals.cmd_src_idx].xshift = xshift;
|
||||
}
|
||||
gd.layout.margin.l += left_yaxis_ticks > 0 ? 50 + add_xshift : 45;
|
||||
}
|
||||
}
|
||||
|
||||
// New yaxis and not percent change
|
||||
if (!popup_data.percent_change && popup_data.same_yaxis == false) {
|
||||
gd.layout[yaxis_id] = {
|
||||
...layout_defaults,
|
||||
title: {
|
||||
text: popup_data.name,
|
||||
font: {
|
||||
size: 14,
|
||||
},
|
||||
standoff: 0,
|
||||
},
|
||||
ticksuffix: ticksuffix,
|
||||
layer: "below traces",
|
||||
};
|
||||
new_yaxis = true;
|
||||
if (globals.cmd_src_idx != null) {
|
||||
let xshift = gd.layout.annotations[globals.cmd_src_idx].xshift;
|
||||
xshift -= left_yaxis_ticks > 0 ? 40 + add_xshift : 40;
|
||||
|
||||
// just in case we have a lot of yaxis
|
||||
xshift += xshift < -320 ? 10 + 2 * left_yaxis_ticks : 0;
|
||||
|
||||
gd.layout.annotations[globals.cmd_src_idx].xshift = xshift;
|
||||
}
|
||||
gd.layout.margin.l += left_yaxis_ticks > 0 ? 50 : 45;
|
||||
}
|
||||
console.log("trace: ", trace);
|
||||
|
||||
globals.added_traces.push(trace.name);
|
||||
|
||||
Plotly.addTraces(gd, trace);
|
||||
Plotly.react(gd, gd.data, gd.layout);
|
||||
|
||||
if (new_yaxis) {
|
||||
gd.layout[yaxis_id].automargin = false;
|
||||
Plotly.react(gd, gd.data, gd.layout);
|
||||
}
|
||||
|
||||
// We empty the fields and innerHTML after the plot is made
|
||||
globals.CSV_DIV.querySelector("#csv_colors").innerHTML = "";
|
||||
globals.CSV_DIV.querySelector("#csv_columns").innerHTML = "";
|
||||
globals.CSV_DIV.querySelector("#csv_plot_yaxis_options").style.display =
|
||||
"none";
|
||||
|
||||
globals.CSV_DIV.querySelectorAll("input").forEach(function (input) {
|
||||
input.value = "";
|
||||
});
|
||||
globals.CSV_DIV.querySelectorAll("textarea").forEach(function (input) {
|
||||
input.value = "";
|
||||
});
|
||||
};
|
||||
|
||||
popup_file_reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
closePopup();
|
||||
}
|
||||
|
||||
function on_cancel() {
|
||||
// We close any popup that is open
|
||||
closePopup();
|
||||
}
|
||||
|
||||
function on_delete(popup_id) {
|
||||
let gd = globals.CHART_DIV;
|
||||
closePopup();
|
||||
|
||||
if (popup_id == "text") {
|
||||
let annotation = JSON.parse(
|
||||
globals.TEXT_DIV.querySelector("#addtext_annotation").value
|
||||
);
|
||||
gd.layout.annotations.splice(gd.layout.annotations.indexOf(annotation), 1);
|
||||
gd.layout.dragmode = "pan";
|
||||
|
||||
Plotly.react(gd, gd.data, gd.layout);
|
||||
|
||||
globals.TEXT_DIV.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function closePopup() {
|
||||
// We set the display to none to hide the popup overlay
|
||||
var popup = document.getElementById("popup_overlay");
|
||||
popup.style.display = "none";
|
||||
}
|
||||
|
||||
function openPopup(popup_id, popup_data = null) {
|
||||
// We make sure to close the incase there is a popup already open
|
||||
closePopup();
|
||||
var overlay = document.getElementById("popup_overlay");
|
||||
// We get the popup div and set the display to block to show the popup
|
||||
overlay.style.display = "block";
|
||||
get_popup(popup_data, popup_id);
|
||||
}
|
||||
Reference in New Issue
Block a user