diff --git a/src/web/App.mjs b/src/web/App.mjs
index 1dab16e6..453fba22 100755
--- a/src/web/App.mjs
+++ b/src/web/App.mjs
@@ -51,10 +51,12 @@ class App {
*/
setup() {
document.dispatchEvent(this.manager.appstart);
+
this.initialiseSplitter();
this.loadLocalStorage();
this.populateOperationsList();
this.manager.setup();
+ this.manager.output.saveBombe();
this.resetLayout();
this.setCompileMessage();
diff --git a/src/web/OutputWaiter.mjs b/src/web/OutputWaiter.mjs
index 7203a16f..39d6e51b 100755
--- a/src/web/OutputWaiter.mjs
+++ b/src/web/OutputWaiter.mjs
@@ -334,24 +334,55 @@ class OutputWaiter {
/**
- * Shows or hides the loading icon.
+ * Save bombe object before it is removed so that it can be used later
+ */
+ saveBombe() {
+ this.bombeEl = document.getElementById("bombe").cloneNode();
+ this.bombeEl.setAttribute("width", "100%");
+ this.bombeEl.setAttribute("height", "100%");
+ }
+
+
+ /**
+ * Shows or hides the output loading screen.
+ * The animated Bombe SVG, whilst quite aesthetically pleasing, is reasonably CPU
+ * intensive, so we remove it from the DOM when not in use. We only show it if the
+ * recipe is taking longer than 200ms. We add it to the DOM just before that so that
+ * it is ready to fade in without stuttering.
*
- * @param {boolean} value
+ * @param {boolean} value - true == show loader
*/
toggleLoader(value) {
+ clearTimeout(this.appendBombeTimeout);
+ clearTimeout(this.outputLoaderTimeout);
+
const outputLoader = document.getElementById("output-loader"),
- outputElement = document.getElementById("output-text");
+ outputElement = document.getElementById("output-text"),
+ loader = outputLoader.querySelector(".loader");
if (value) {
this.manager.controls.hideStaleIndicator();
- this.bakingStatusTimeout = setTimeout(function() {
+
+ // Start a timer to add the Bombe to the DOM just before we make it
+ // visible so that there is no stuttering
+ this.appendBombeTimeout = setTimeout(function() {
+ loader.appendChild(this.bombeEl);
+ }.bind(this), 150);
+
+ // Show the loading screen
+ this.outputLoaderTimeout = setTimeout(function() {
outputElement.disabled = true;
outputLoader.style.visibility = "visible";
outputLoader.style.opacity = 1;
this.manager.controls.toggleBakeButtonFunction(true);
}.bind(this), 200);
} else {
- clearTimeout(this.bakingStatusTimeout);
+ // Remove the Bombe from the DOM to save resources
+ this.outputLoaderTimeout = setTimeout(function () {
+ try {
+ loader.removeChild(this.bombeEl);
+ } catch (err) {}
+ }.bind(this), 500);
outputElement.disabled = false;
outputLoader.style.opacity = 0;
outputLoader.style.visibility = "hidden";
diff --git a/src/web/html/index.html b/src/web/html/index.html
index f03590ab..a1f915b6 100755
--- a/src/web/html/index.html
+++ b/src/web/html/index.html
@@ -81,7 +81,11 @@
if (!el.classList.contains("loading"))
el.classList.add("loading"); // Causes CSS transition on first message
el.innerHTML = msg;
- } catch (err) {} // Ignore errors if DOM not yet ready
+ } catch (err) {
+ // This error was likely caused by the DOM not being ready yet,
+ // so we wait another second and then try again.
+ setTimeout(changeLoadingMsg, 1000);
+ }
}
changeLoadingMsg();
@@ -138,7 +142,9 @@