From 930fc0644bf3bc4b32562e586aff7768da060523 Mon Sep 17 00:00:00 2001 From: teh_coderer <me@tehcoderer.com> Date: Thu, 13 Apr 2023 01:26:18 -0400 Subject: [PATCH 1/5] init --- openbb_terminal/core/plots/plotly.html | 21 +++++++++++++++++- openbb_terminal/core/plots/web/main.js | 10 ++++++++- openbb_terminal/core/plots/web/popups.js | 28 +++++++++++++++++++++++- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/openbb_terminal/core/plots/plotly.html b/openbb_terminal/core/plots/plotly.html index 070eb5cea66c..6d9475dbbbc6 100644 --- a/openbb_terminal/core/plots/plotly.html +++ b/openbb_terminal/core/plots/plotly.html @@ -160,6 +160,23 @@ <!-- Title Popup --> <div class="popup_content" id="popup_title"></div> + + <!-- Download Popup --> + <div class="popup_content" id="popup_download"> + <p>File saved to:</p> + <p id="download_path"></p> + <button class="_btn" id="openfile_button" style="margin-top: 20px"> + Open File + </button> + <button class="_btn" id="openfolder_button">Open Folder</button> + <button + class="_btn-tertiary" + id="close_button" + onclick="closePopup()" + > + Close + </button> + </div> </div> </div> @@ -248,12 +265,14 @@ CSV_DIV = document.getElementById("popup_csv"); TEXT_DIV = document.getElementById("popup_text"); TITLE_DIV = document.getElementById("popup_title"); + DOWNLOAD_DIV = document.getElementById("popup_download"); OpenBBMain( window.plotly_figure, CHART_DIV, CSV_DIV, TEXT_DIV, - TITLE_DIV + TITLE_DIV, + DOWNLOAD_DIV ); } }, 20); diff --git a/openbb_terminal/core/plots/web/main.js b/openbb_terminal/core/plots/web/main.js index 548636e64c51..86d1086a03da 100644 --- a/openbb_terminal/core/plots/web/main.js +++ b/openbb_terminal/core/plots/web/main.js @@ -32,12 +32,20 @@ const non_blocking = (func, delay) => { }; }; -function OpenBBMain(plotly_figure, chartdiv, csvdiv, textdiv, titlediv) { +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.DOWNLOAD_DIV = downloaddiv; console.log("main.js loaded"); console.log("plotly_figure", plotly_figure); let graphs = plotly_figure; diff --git a/openbb_terminal/core/plots/web/popups.js b/openbb_terminal/core/plots/web/popups.js index bfb90633983f..5e1ab8ac0b56 100644 --- a/openbb_terminal/core/plots/web/popups.js +++ b/openbb_terminal/core/plots/web/popups.js @@ -183,9 +183,35 @@ function get_popup(data = null, popup_id = null) { `; 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]; + 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"; From b1fc9d30fe6e7ea130282c45530445a969caa0cd Mon Sep 17 00:00:00 2001 From: teh_coderer <me@tehcoderer.com> Date: Thu, 13 Apr 2023 02:09:20 -0400 Subject: [PATCH 2/5] Update plotly.html --- openbb_terminal/core/plots/plotly.html | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/openbb_terminal/core/plots/plotly.html b/openbb_terminal/core/plots/plotly.html index 6d9475dbbbc6..0c0a887f1cc6 100644 --- a/openbb_terminal/core/plots/plotly.html +++ b/openbb_terminal/core/plots/plotly.html @@ -165,17 +165,17 @@ <div class="popup_content" id="popup_download"> <p>File saved to:</p> <p id="download_path"></p> - <button class="_btn" id="openfile_button" style="margin-top: 20px"> - Open File - </button> - <button class="_btn" id="openfolder_button">Open Folder</button> - <button - class="_btn-tertiary" - id="close_button" - onclick="closePopup()" - > - Close - </button> + <div style="float: right; margin-top: 20px"> + <button class="_btn" id="openfile_button">Open File</button> + <button class="_btn" id="openfolder_button">Open Folder</button> + <button + class="_btn-tertiary" + id="close_button" + onclick="closePopup()" + > + Close + </button> + </div> </div> </div> </div> From 053342d6af87e4db6c74277183e6d4bf11f7f2f0 Mon Sep 17 00:00:00 2001 From: teh_coderer <me@tehcoderer.com> Date: Thu, 13 Apr 2023 12:26:44 -0400 Subject: [PATCH 3/5] save as --- openbb_terminal/core/plots/web/bar_menus.js | 88 +++++++++++++++------ openbb_terminal/core/plots/web/main.js | 55 ++++++++++--- 2 files changed, 110 insertions(+), 33 deletions(-) diff --git a/openbb_terminal/core/plots/web/bar_menus.js b/openbb_terminal/core/plots/web/bar_menus.js index 93f4146afb00..81c911aeb6e2 100644 --- a/openbb_terminal/core/plots/web/bar_menus.js +++ b/openbb_terminal/core/plots/web/bar_menus.js @@ -234,7 +234,7 @@ function uploadImage() { }); } -function downloadImage(filename, extension) { +async function downloadImage(filename, extension, writable = undefined) { const loader = document.getElementById("loader"); loader.classList.add("show"); @@ -247,20 +247,38 @@ function downloadImage(filename, extension) { // } else if (extension == "svg") { // imageDownload = domtoimage.toSvg; } else if (["svg", "pdf"].includes(extension)) { - Plotly.downloadImage(globals.CHART_DIV, { - format: "svg", - height: globals.CHART_DIV.clientHeight, - width: globals.CHART_DIV.clientWidth, - filename: filename, - }); + if (window.showSaveFilePicker && writable) { + blob = await Plotly.toImage(globals.CHART_DIV, { + format: "svg", + height: globals.CHART_DIV.clientHeight, + width: globals.CHART_DIV.clientWidth, + }).then(async function (dataUrl) { + 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, + }); + } return; } else { console.log("Invalid extension"); return; } imageDownload(document.getElementById("openbb_container")) - .then(function (dataUrl) { - downloadURI(dataUrl, filename + "." + extension); + .then(async function (dataUrl) { + if (window.showSaveFilePicker && writable) { + 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) { @@ -279,11 +297,31 @@ function downloadURI(uri, name) { document.body.removeChild(link); } -function downloadData(gd) { +async function downloadData(gd) { let data = gd.data; let candlestick = false; let csv = undefined; + 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; + } + data.forEach(function (trace) { // check if candlestick if (trace.type == "candlestick") { @@ -363,23 +401,29 @@ function downloadData(gd) { } } - let filename = globals.filename; let blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }); if (navigator.msSaveBlob) { // IE 10+ navigator.msSaveBlob(blob, filename); } 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); + // 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); + } } } } diff --git a/openbb_terminal/core/plots/web/main.js b/openbb_terminal/core/plots/web/main.js index 86d1086a03da..58095dde1b00 100644 --- a/openbb_terminal/core/plots/web/main.js +++ b/openbb_terminal/core/plots/web/main.js @@ -90,15 +90,15 @@ function OpenBBMain( { name: "Download CSV (Ctrl+Shift+S)", icon: ICONS.downloadCsv, - click: function (gd) { - downloadCsvButton(gd); + click: async function (gd) { + await downloadCsvButton(gd); }, }, { name: "Download PNG (Ctrl+S)", icon: ICONS.downloadImage, - click: function () { - downloadImageButton(); + click: async function () { + await downloadImageButton(); }, }, // { @@ -357,11 +357,44 @@ function OpenBBMain( button_pressed(title, active); } - function downloadImageButton() { - loadingOverlay("Saving PNG"); + 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(function () { - downloadImage(globals.filename, "png"); + non_blocking(async function () { + await downloadImage(filename, ext, writable); setTimeout(function () { setTimeout(function () { loading.classList.remove("show"); @@ -371,10 +404,10 @@ function OpenBBMain( }, 2)(); } - function downloadCsvButton(gd) { + async function downloadCsvButton(gd) { loadingOverlay("Saving CSV"); - setTimeout(function () { - downloadData(gd); + setTimeout(async function () { + await downloadData(gd); }, 500); setTimeout(function () { loading.classList.remove("show"); From 7370d2661899ed55c8af4474697e0ac00cc266d9 Mon Sep 17 00:00:00 2001 From: teh_coderer <me@tehcoderer.com> Date: Thu, 13 Apr 2023 12:55:18 -0400 Subject: [PATCH 4/5] move savefilepicker to before csv overlay --- openbb_terminal/core/plots/web/bar_menus.js | 30 ++++----------------- openbb_terminal/core/plots/web/main.js | 22 ++++++++++++++- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/openbb_terminal/core/plots/web/bar_menus.js b/openbb_terminal/core/plots/web/bar_menus.js index 81c911aeb6e2..a96db68d24cc 100644 --- a/openbb_terminal/core/plots/web/bar_menus.js +++ b/openbb_terminal/core/plots/web/bar_menus.js @@ -248,12 +248,12 @@ async function downloadImage(filename, extension, writable = undefined) { // imageDownload = domtoimage.toSvg; } else if (["svg", "pdf"].includes(extension)) { if (window.showSaveFilePicker && writable) { - blob = await Plotly.toImage(globals.CHART_DIV, { + await Plotly.toImage(globals.CHART_DIV, { format: "svg", height: globals.CHART_DIV.clientHeight, width: globals.CHART_DIV.clientWidth, }).then(async function (dataUrl) { - blob = await fetch(dataUrl).then((r) => r.blob()); + const blob = await fetch(dataUrl).then((r) => r.blob()); await writable.write(blob); await writable.close(); }); @@ -273,7 +273,7 @@ async function downloadImage(filename, extension, writable = undefined) { imageDownload(document.getElementById("openbb_container")) .then(async function (dataUrl) { if (window.showSaveFilePicker && writable) { - blob = await fetch(dataUrl).then((r) => r.blob()); + const blob = await fetch(dataUrl).then((r) => r.blob()); await writable.write(blob); await writable.close(); } else { @@ -297,31 +297,11 @@ function downloadURI(uri, name) { document.body.removeChild(link); } -async function downloadData(gd) { +async function downloadData(gd, filename, writable = undefined) { let data = gd.data; let candlestick = false; let csv = undefined; - 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; - } - data.forEach(function (trace) { // check if candlestick if (trace.type == "candlestick") { @@ -401,7 +381,7 @@ async function downloadData(gd) { } } - let blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }); + const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }); if (navigator.msSaveBlob) { // IE 10+ navigator.msSaveBlob(blob, filename); diff --git a/openbb_terminal/core/plots/web/main.js b/openbb_terminal/core/plots/web/main.js index 58095dde1b00..ff8480c0aef3 100644 --- a/openbb_terminal/core/plots/web/main.js +++ b/openbb_terminal/core/plots/web/main.js @@ -405,9 +405,29 @@ function OpenBBMain( } 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); + await downloadData(gd, filename, writable); }, 500); setTimeout(function () { loading.classList.remove("show"); From 69759cc760989826ebd5bf52be88749d0f5cee06 Mon Sep 17 00:00:00 2001 From: teh_coderer <me@tehcoderer.com> Date: Thu, 13 Apr 2023 14:03:14 -0400 Subject: [PATCH 5/5] adds watermarks to svg saves --- openbb_terminal/core/plots/plotly.html | 9 +++- openbb_terminal/core/plots/web/bar_menus.js | 51 +++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/openbb_terminal/core/plots/plotly.html b/openbb_terminal/core/plots/plotly.html index 0c0a887f1cc6..5138d8058a8a 100644 --- a/openbb_terminal/core/plots/plotly.html +++ b/openbb_terminal/core/plots/plotly.html @@ -226,9 +226,13 @@ !["svg", "pdf"].includes(extension) ) { if (annotation.text[0] == "/") { - cmd_src = annotation.text; + globals.cmd_src = annotation.text; + globals.cmd_idx = + window.plotly_figure.layout.annotations.indexOf(annotation); annotation.text = ""; + let margin = window.plotly_figure.layout.margin; + globals.old_margin = { ...margin }; if (margin.t != undefined && margin.t > 40) { margin.t = 40; } @@ -236,7 +240,6 @@ if (cmd_src == "/stocks/candle") { margin.r -= 40; } - window.plotly_figure.layout.margin = margin; } } }); @@ -252,6 +255,8 @@ let title = window.plotly_figure?.layout?.title?.text ?? "Interactive Chart"; + globals.title = title; + if (title.length > 50) { document.getElementById("title").style.fontSize = "12px"; } diff --git a/openbb_terminal/core/plots/web/bar_menus.js b/openbb_terminal/core/plots/web/bar_menus.js index a96db68d24cc..c3f8fd32848a 100644 --- a/openbb_terminal/core/plots/web/bar_menus.js +++ b/openbb_terminal/core/plots/web/bar_menus.js @@ -234,6 +234,48 @@ function uploadImage() { }); } +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"); @@ -247,6 +289,11 @@ async function downloadImage(filename, extension, writable = undefined) { // } 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", @@ -265,11 +312,15 @@ async function downloadImage(filename, extension, writable = undefined) { 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) {