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.
This commit is contained in:
parent
6cdd8de248
commit
9aa45041f6
3 changed files with 98 additions and 40 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -105,12 +105,12 @@
|
|||
</script>
|
||||
<script type="text/html" id="customControls_commandTemplate">
|
||||
<form class="form-inline">
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button>
|
||||
<button class="btn" data-bind="text: name, enable: $root.isCustomEnabled($data), click: function() { $root.clickCustom($data) }"></button>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_feedbackCommandTemplate">
|
||||
<form class="form-inline">
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button> <span data-bind="text: output"></span>
|
||||
<button class="btn" data-bind="text: name, enable: $root.isCustomEnabled($data), click: function() { $root.clickCustom($data) }"></button> <span data-bind="text: output"></span>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_feedbackTemplate">
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
<label data-bind="text: name"></label>
|
||||
<input type="text" class="input-small" data-bind="attr: {placeholder: name}, value: value">
|
||||
<!-- /ko -->
|
||||
<button class="btn" data-bind="text: name, enable: $root.isOperational() && $root.loginState.isUser(), click: function() { $root.sendCustomCommand($data) }"></button>
|
||||
<button class="btn" data-bind="text: name, enable: $root.isCustomEnabled($data), click: function() { $root.clickCustom($data) }"></button>
|
||||
</form>
|
||||
</script>
|
||||
<script type="text/html" id="customControls_emptyTemplate"><div></div></script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue