From 9aa45041f6c88bd9f15c040545b1ca3f6c2a1459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 17 Feb 2015 13:51:05 +0100 Subject: [PATCH] View models can now implement getAdditionalControls to add controls to the control tab Controls may now contain a new property "javascript" which may be either a javascript function to be called when the control is executed, or a string to be eval'd when the control is executed. If it is a function, it will be called with the config of the custom control as single parameter "data". If it is a string it will be eval'd within a function context providing the custom control configuration as variable data. Additionally controls may contain a new property "enabled", which may also be either a javascript function or a string to be eval'd. The custom control config will be available to the enabled code as described above. The code is expected to return true if the component should be enabled or false if not. --- src/octoprint/static/js/app/main.js | 73 +++++++++++-------- .../static/js/app/viewmodels/control.js | 59 +++++++++++++-- src/octoprint/templates/tabs/control.jinja2 | 6 +- 3 files changed, 98 insertions(+), 40 deletions(-) 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 @@