diff --git a/index.css b/index.css
index d129d5a5..6d121555 100644
--- a/index.css
+++ b/index.css
@@ -2273,8 +2273,9 @@ svg.button {
.dontAsk {
- display: inline-block;
margin: 0.9em 0 0 0.6em;
+ display: inline-flex;
+ align-items: center;
}
#errorBox {
diff --git a/index.html b/index.html
index d68f815e..7fcc6759 100644
--- a/index.html
+++ b/index.html
@@ -6297,7 +6297,7 @@
-
+
diff --git a/main.js b/main.js
index d565e4da..a677d7ad 100644
--- a/main.js
+++ b/main.js
@@ -10,13 +10,22 @@ const TIME = DEBUG || !PRODUCTION;
const WARN = true;
const ERROR = true;
-// register service worker responsible for caching
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker.register("./sw.js").catch(err => {
console.error("ServiceWorker registration failed: ", err);
});
});
+
+ window.addEventListener(
+ "beforeinstallprompt",
+ async event => {
+ event.preventDefault();
+ const Installation = await import("/modules/dynamic/installation.js");
+ Installation.init(event);
+ },
+ {once: true}
+ );
}
// append svg layers (in default order)
diff --git a/modules/dynamic/installation.js b/modules/dynamic/installation.js
new file mode 100644
index 00000000..c6c23ee5
--- /dev/null
+++ b/modules/dynamic/installation.js
@@ -0,0 +1,70 @@
+// module to prompt PWA installation
+let installButton = null;
+let deferredPrompt = null;
+
+export function init(event) {
+ const dontAskforInstallation = localStorage.getItem("installationDontAsk");
+ if (dontAskforInstallation) return;
+
+ installButton = createButton();
+ deferredPrompt = event;
+
+ window.addEventListener("appinstalled", () => {
+ tip("Application is installed", false, "success", 8000);
+ cleanup();
+ });
+}
+
+function createButton() {
+ const button = document.createElement("button");
+ button.style = `
+ position: fixed;
+ top: 1em;
+ right: 1em;
+ padding: 0.6em 0.8em;
+ width: auto;
+ `;
+ button.className = "options glow";
+ button.innerHTML = "Install";
+ button.onclick = openDialog;
+ button.onmouseenter = () => tip("Install the Application");
+ document.querySelector("body").appendChild(button);
+ return button;
+}
+
+function openDialog() {
+ alertMessage.innerHTML = /* html */ `You can install the tool so that it will look and feel like desktop application`;
+ $("#alert").dialog({
+ resizable: false,
+ title: "Install the Application",
+ buttons: {
+ Install: function () {
+ $(this).dialog("close");
+ deferredPrompt.prompt();
+ },
+ Cancel: function () {
+ $(this).dialog("close");
+ }
+ },
+ open: function () {
+ const checkbox =
+ '';
+ const pane = this.parentElement.querySelector(".ui-dialog-buttonpane");
+ pane.insertAdjacentHTML("afterbegin", checkbox);
+ },
+ close: function () {
+ const box = this.parentElement.querySelector(".checkbox");
+ if (box?.checked) {
+ localStorage.setItem("installationDontAsk", true);
+ cleanup();
+ }
+ $(this).dialog("destroy");
+ }
+ });
+
+ function cleanup() {
+ installButton.remove();
+ installButton = null;
+ deferredPrompt = null;
+ }
+}
diff --git a/modules/ui/tools.js b/modules/ui/tools.js
index 2189b1d8..66eaa9ea 100644
--- a/modules/ui/tools.js
+++ b/modules/ui/tools.js
@@ -2,10 +2,7 @@
// module to control the Tools options (click to edit, to re-geenerate, tp add)
toolsContent.addEventListener("click", function (event) {
- if (customization) {
- tip("Please exit the customization mode first", false, "warning");
- return;
- }
+ if (customization) return tip("Please exit the customization mode first", false, "warning");
if (!["BUTTON", "I"].includes(event.target.tagName)) return;
const button = event.target.id;