From 720cdadfbb62ad64d13b0ff6745ba68795101e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 18 Nov 2015 17:13:41 +0100 Subject: [PATCH] Track browser tab visibility, only activate webcam/gcode viewer when visible This might help with #1065 if indeed is related to background tab suspending behaviours in browsers, but is a completely blind guess at this point since I still have not been able to reproduce that issue myself. --- .../static/gcodeviewer/js/renderer.js | 2 +- src/octoprint/static/js/app/main.js | 82 ++++++++++++++++++- .../static/js/app/viewmodels/control.js | 64 ++++++++++----- .../static/js/app/viewmodels/gcode.js | 7 +- src/octoprint/templates/initscript.jinja2 | 8 ++ 5 files changed, 135 insertions(+), 28 deletions(-) diff --git a/src/octoprint/static/gcodeviewer/js/renderer.js b/src/octoprint/static/gcodeviewer/js/renderer.js index 2f046d90..32f2195a 100644 --- a/src/octoprint/static/gcodeviewer/js/renderer.js +++ b/src/octoprint/static/gcodeviewer/js/renderer.js @@ -379,7 +379,7 @@ GCODE.renderer = (function(){ }; var drawLayer = function(layerNum, fromProgress, toProgress, isNotCurrentLayer){ - console.log("Drawing layer " + layerNum + " from " + fromProgress + " to " + toProgress + " (current: " + !isNotCurrentLayer + ")"); + log.trace("Drawing layer " + layerNum + " from " + fromProgress + " to " + toProgress + " (current: " + !isNotCurrentLayer + ")"); var i; diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index c103409c..e00016fe 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -26,6 +26,71 @@ $(function() { }); }); + //~~ some CoreUI specific stuff we put into OctoPrint.coreui + + OctoPrint.coreui = (function() { + var exports = { + browserTabVisibility: undefined, + selectedTab: undefined + }; + + var browserVisibilityCallbacks = []; + + var getHiddenProp = function() { + var prefixes = ["webkit", "moz", "ms", "o"]; + + // if "hidden" is natively supported just return it + if ("hidden" in document) { + return "hidden" + } + + // otherwise loop over all the known prefixes until we find one + var vendorPrefix = _.find(prefixes, function(prefix) { + return (prefix + "Hidden" in document); + }); + if (vendorPrefix !== undefined) { + return vendorPrefix + "Hidden"; + } + + // nothing found + return undefined; + }; + + var isHidden = function() { + var prop = getHiddenProp(); + if (!prop) return false; + + return document[prop]; + }; + + var updateBrowserVisibility = function() { + var visible = !isHidden(); + exports.browserTabVisible = visible; + _.each(browserVisibilityCallbacks, function(callback) { + callback(visible); + }) + }; + + // register for browser visibility tracking + + var prop = getHiddenProp(); + if (!prop) return undefined; + + var eventName = prop.replace(/[H|h]idden/, "") + "visibilitychange"; + document.addEventListener(eventName, updateBrowserVisibility); + + updateBrowserVisibility(); + + // exports + + exports.isVisible = function() { return !isHidden() }; + exports.onBrowserVisibilityChange = function(callback) { + browserVisibilityCallbacks.push(callback); + }; + + return exports; + })(); + //~~ AJAX setup // work around a stupid iOS6 bug where ajax requests get cached and only work once, as described at @@ -410,11 +475,17 @@ $(function() { $('.nav-pills, .nav-tabs').tabdrop(); // Allow components to react to tab change + var onTabChange = function(current, previous) { + log.debug("Selected OctoPrint tab changed: previous = " + previous + ", current = " + current); + OctoPrint.coreui.selectedTab = current; + callViewModels(allViewModels, "onTabChange", [current, previous]); + }; + var tabs = $('#tabs a[data-toggle="tab"]'); tabs.on('show', function (e) { var current = e.target.hash; var previous = e.relatedTarget.hash; - callViewModels(allViewModels, "onTabChange", [current, previous]); + onTabChange(current, previous); }); tabs.on('shown', function (e) { @@ -423,6 +494,8 @@ $(function() { callViewModels(allViewModels, "onAfterTabChange", [current, previous]); }); + onTabChange(OCTOPRINT_INITIAL_TAB); + // Fix input element click problems on dropdowns $(".dropdown input, .dropdown label").click(function(e) { e.stopPropagation(); @@ -516,7 +589,14 @@ $(function() { callViewModels(allViewModels, "onAllBound", [allViewModels]); log.info("... binding done"); + // startup complete callViewModels(allViewModels, "onStartupComplete"); + + // make sure we can track the browser tab visibility + OctoPrint.coreui.onBrowserVisibilityChange(function(status) { + log.debug("Browser tab is now " + (status ? "visible" : "hidden")); + callViewModels(allViewModels, "onBrowserTabVisibilityChange", [status]); + }); }; if (!_.has(viewModelMap, "settingsViewModel")) { diff --git a/src/octoprint/static/js/app/viewmodels/control.js b/src/octoprint/static/js/app/viewmodels/control.js index d6c944c9..7a89718e 100644 --- a/src/octoprint/static/js/app/viewmodels/control.js +++ b/src/octoprint/static/js/app/viewmodels/control.js @@ -357,31 +357,51 @@ $(function() { self.onSettingsBeforeSave = self.updateRotatorWidth; + self._disableWebcam = function() { + // only disable webcam stream if tab is out of focus for more than 5s, otherwise we might cause + // more load by the constant connection creation than by the actual webcam stream + self.webcamDisableTimeout = setTimeout(function () { + $("#webcam_image").attr("src", ""); + }, 5000); + }; + + self._enableWebcam = function() { + if (OctoPrint.coreui.selectedTab != "#control" || !OctoPrint.coreui.browserTabVisible) { + return; + } + + if (self.webcamDisableTimeout != undefined) { + clearTimeout(self.webcamDisableTimeout); + } + var webcamImage = $("#webcam_image"); + var currentSrc = webcamImage.attr("src"); + if (currentSrc === undefined || currentSrc.trim() == "") { + var newSrc = CONFIG_WEBCAM_STREAM; + if (CONFIG_WEBCAM_STREAM.lastIndexOf("?") > -1) { + newSrc += "&"; + } else { + newSrc += "?"; + } + newSrc += new Date().getTime(); + + self.updateRotatorWidth(); + webcamImage.attr("src", newSrc); + } + }; + self.onTabChange = function (current, previous) { if (current == "#control") { - if (self.webcamDisableTimeout != undefined) { - clearTimeout(self.webcamDisableTimeout); - } - var webcamImage = $("#webcam_image"); - var currentSrc = webcamImage.attr("src"); - if (currentSrc === undefined || currentSrc.trim() == "") { - var newSrc = CONFIG_WEBCAM_STREAM; - if (CONFIG_WEBCAM_STREAM.lastIndexOf("?") > -1) { - newSrc += "&"; - } else { - newSrc += "?"; - } - newSrc += new Date().getTime(); - - self.updateRotatorWidth(); - webcamImage.attr("src", newSrc); - } + self._enableWebcam(); } else if (previous == "#control") { - // only disable webcam stream if tab is out of focus for more than 5s, otherwise we might cause - // more load by the constant connection creation than by the actual webcam stream - self.webcamDisableTimeout = setTimeout(function () { - $("#webcam_image").attr("src", ""); - }, 5000); + self._disableWebcam(); + } + }; + + self.onBrowserTabVisibilityChange = function(status) { + if (status) { + self._enableWebcam(); + } else { + self._disableWebcam(); } }; diff --git a/src/octoprint/static/js/app/viewmodels/gcode.js b/src/octoprint/static/js/app/viewmodels/gcode.js index b16fd9d1..bb3f99c8 100644 --- a/src/octoprint/static/js/app/viewmodels/gcode.js +++ b/src/octoprint/static/js/app/viewmodels/gcode.js @@ -356,7 +356,7 @@ $(function() { if(self.loadedFilename && self.loadedFilename == data.job.file.name && self.loadedFileDate == data.job.file.date) { - if (self.tabActive && self.currentlyPrinting && self.renderer_syncProgress() && !self.waitForApproval()) { + if (OctoPrint.coreui.browserTabVisible && self.tabActive && self.currentlyPrinting && self.renderer_syncProgress() && !self.waitForApproval()) { var cmdIndex = GCODE.gCodeReader.getCmdIndexForPercentage(data.progress.completion); if(cmdIndex){ GCODE.renderer.render(cmdIndex.layer, 0, cmdIndex.cmd); @@ -502,12 +502,11 @@ $(function() { self.onBeforeBinding = function() { self.initialize(); - } + }; self.onTabChange = function(current, previous) { self.tabActive = current == "#gcode"; - - } + }; } OCTOPRINT_VIEWMODELS.push([ diff --git a/src/octoprint/templates/initscript.jinja2 b/src/octoprint/templates/initscript.jinja2 index 5866008c..6d02f35f 100644 --- a/src/octoprint/templates/initscript.jinja2 +++ b/src/octoprint/templates/initscript.jinja2 @@ -34,4 +34,12 @@ var OCTOPRINT_VIEWMODELS = []; var ADDITIONAL_VIEWMODELS = []; var OCTOPRINT_ADDITIONAL_BINDINGS = []; + + {% if templates.tab.order %} + {% set first_tab = templates.tab.order[0] %} + {% set entry, data = templates.tab.entries[first_tab] %} + var OCTOPRINT_INITIAL_TAB = "#{{ data._div }}"; + {% else %} + var OCTOPRINT_INITIAL_TAB = undefined; + {% endif %}