diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index 4dc14dbe..5f3c85f9 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -498,36 +498,32 @@ $(function() { //~~ view model binding settingsViewModel.requestData(function() { - ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog")); + var viewModelsToInit = additionalViewModels.concat([ + [settingsViewModel, document.getElementById("settings_dialog")], + [connectionViewModel, document.getElementById("connection_wrapper")], + [printerStateViewModel, document.getElementById("state_wrapper")], + [gcodeFilesViewModel, document.getElementById("files_wrapper")], + [temperatureViewModel, document.getElementById("temp")], + [controlViewModel, document.getElementById("control")], + [gcodeViewModel, document.getElementById("gcode")], + [terminalViewModel, document.getElementById("term")], + [navigationViewModel, document.getElementById("navbar")], + [appearanceViewModel, document.getElementsByTagName("head")[0]], + [printerStateViewModel, document.getElementById("drop_overlay")], + [logViewModel, document.getElementById("logs")], + [timelapseViewModel, document.getElementById("timelapse")], + [slicingViewModel, document.getElementById("slicing_configuration_dialog")] + ]); - ko.applyBindings(connectionViewModel, document.getElementById("connection_wrapper")); - ko.applyBindings(printerStateViewModel, document.getElementById("state_wrapper")); - ko.applyBindings(gcodeFilesViewModel, document.getElementById("files_wrapper")); - ko.applyBindings(temperatureViewModel, document.getElementById("temp")); - ko.applyBindings(controlViewModel, document.getElementById("control")); - ko.applyBindings(terminalViewModel, document.getElementById("term")); - var gcode = document.getElementById("gcode"); - if (gcode) { - gcodeViewModel.initialize(); - ko.applyBindings(gcodeViewModel, gcode); - } - //ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog")); - ko.applyBindings(navigationViewModel, document.getElementById("navbar")); - ko.applyBindings(appearanceViewModel, document.getElementsByTagName("head")[0]); - ko.applyBindings(printerStateViewModel, document.getElementById("drop_overlay")); - ko.applyBindings(logViewModel, document.getElementById("logs")); + // apply bindings + var rerenderControls = false; + _.each(viewModelsToInit, function(viewModelData) { + if (!Array.isArray(viewModelData) || viewModelData.length != 2) { + return; + } - var timelapseElement = document.getElementById("timelapse"); - if (timelapseElement) { - ko.applyBindings(timelapseViewModel, timelapseElement); - } - - ko.applyBindings(slicingViewModel, document.getElementById("slicing_configuration_dialog")); - - // apply bindings and signal startup - _.each(additionalViewModels, function(additionalViewModel) { - var viewModel = additionalViewModel[0]; - var targets = additionalViewModel[1]; + var viewModel = viewModelData[0]; + var targets = viewModelData[1]; if (targets === undefined) { return; @@ -542,17 +538,30 @@ $(function() { } _.each(targets, function(target) { - try { - ko.applyBindings(viewModel, target); - } catch (exc) { - console.log("Could not apply bindings for additional view model " + viewModel + ": " + exc.message); + if (target) { + try { + ko.applyBindings(viewModel, target); + } catch (exc) { + console.log("Could not apply bindings for additional view model " + viewModel + ": " + exc.message); + } + } else { + console.log("Could not apply binding for view model " + viewModel + ", target does not exist"); } }); if (viewModel.hasOwnProperty("onAfterBinding")) { viewModel.onAfterBinding(); } + + if (viewModel.hasOwnProperty("getAdditionalControls")) { + controlViewModel.additionalControls = controlViewModel.additionalControls.concat(viewModel.getAdditionalControls()); + rerenderControls = true + } }); + + if (rerenderControls) { + controlViewModel.rerenderControls(); + } }); //~~ UI stuff diff --git a/src/octoprint/static/js/app/viewmodels/control.js b/src/octoprint/static/js/app/viewmodels/control.js index c12767cb..a66e5d3b 100644 --- a/src/octoprint/static/js/app/viewmodels/control.js +++ b/src/octoprint/static/js/app/viewmodels/control.js @@ -26,6 +26,9 @@ function ControlViewModel(loginStateViewModel, settingsViewModel) { self.feedbackControlLookup = {}; + self.controlsFromServer = []; + self.additionalControls = []; + self.keycontrolActive = ko.observable(false); self.keycontrolHelpActive = ko.observable(false); self.keycontrolPossible = ko.computed(function() { @@ -84,6 +87,11 @@ function ControlViewModel(loginStateViewModel, settingsViewModel) { } }; + self.rerenderControls = function() { + var allControls = self.controlsFromServer.concat(self.additionalControls); + self.controls(self._processControls(allControls)) + }; + self.requestData = function() { $.ajax({ url: API_BASEURL + "printer/command/custom", @@ -96,7 +104,8 @@ function ControlViewModel(loginStateViewModel, settingsViewModel) { }; self._fromResponse = function(response) { - self.controls(self._processControls(response.controls)); + self.controlsFromServer = response.controls; + self.rerenderControls(); }; self._processControls = function(controls) { @@ -117,9 +126,49 @@ function ControlViewModel(loginStateViewModel, settingsViewModel) { } else if (control.type == "section") { control.children = self._processControls(control.children); } + + var js; + if (control.hasOwnProperty("javascript")) { + js = control.javascript; + + // if js is a function everything's fine already, but if it's a string we need to eval that first + if (!_.isFunction(js)) { + control.javascript = function(data) { + eval(js); + }; + } + } + + if (control.hasOwnProperty("enabled")) { + js = control.enabled; + + // if js is a function everything's fine already, but if it's a string we need to eval that first + if (!_.isFunction(js)) { + control.enabled = function(data) { + return eval(js); + } + } + } + return control; }; + self.isCustomEnabled = function(data) { + if (data.hasOwnProperty("enabled")) { + return data.enabled(data); + } else { + return self.isOperational() && self.loginState.isUser(); + } + }; + + self.clickCustom = function(data) { + if (data.hasOwnProperty("javascript")) { + data.javascript(data); + } else { + self.sendCustomCommand(data); + } + }; + self.sendJogCommand = function(axis, multiplier, distance) { if (typeof distance === "undefined") distance = $('#jog_distance button.active').data('distance'); @@ -185,17 +234,17 @@ function ControlViewModel(loginStateViewModel, settingsViewModel) { self.sendSelectToolCommand = function(data) { if (!data || !data.key()) return; - var data = { + var payload = { command: "select", tool: data.key() - } + }; $.ajax({ url: API_BASEURL + "printer/tool", type: "POST", dataType: "json", contentType: "application/json; charset=UTF-8", - data: JSON.stringify(data) + data: JSON.stringify(payload) }); }; @@ -211,7 +260,7 @@ function ControlViewModel(loginStateViewModel, settingsViewModel) { contentType: "application/json; charset=UTF-8", data: JSON.stringify(data) }) - } + }; var data = undefined; if (command.type == "command" || command.type == "parametric_command" || command.type == "feedback_command") { // single command diff --git a/src/octoprint/templates/tabs/control.jinja2 b/src/octoprint/templates/tabs/control.jinja2 index 93c54ef6..1c0d78a1 100644 --- a/src/octoprint/templates/tabs/control.jinja2 +++ b/src/octoprint/templates/tabs/control.jinja2 @@ -105,12 +105,12 @@