MrDraw/src/octoprint/static/js/app/viewmodels/control.js
Gina Häußge ea662849bc Construct message/confirmation dialogs programmatically
No need to have templates here, we do it directly from the helper methods.
That also leaves us more options regarding callbacks, classes etc.

Helper methods now take an options dictionary (but still can fallback to the
old signature) containing messages, titles, callbacks etc instead of using
positional arguments for that. Switched code over to utilize that new
calling approach.
2015-08-17 16:41:12 +02:00

574 lines
20 KiB
JavaScript

$(function() {
function ControlViewModel(parameters) {
var self = this;
self.loginState = parameters[0];
self.settings = parameters[1];
self._createToolEntry = function () {
return {
name: ko.observable(),
key: ko.observable()
}
};
self.isErrorOrClosed = ko.observable(undefined);
self.isOperational = ko.observable(undefined);
self.isPrinting = ko.observable(undefined);
self.isPaused = ko.observable(undefined);
self.isError = ko.observable(undefined);
self.isReady = ko.observable(undefined);
self.isLoading = ko.observable(undefined);
self.extrusionAmount = ko.observable(undefined);
self.controls = ko.observableArray([]);
self.tools = ko.observableArray([]);
self.feedRate = ko.observable(100);
self.flowRate = ko.observable(100);
self.feedbackControlLookup = {};
self.controlsFromServer = [];
self.additionalControls = [];
self.webcamDisableTimeout = undefined;
self.keycontrolActive = ko.observable(false);
self.keycontrolHelpActive = ko.observable(false);
self.keycontrolPossible = ko.computed(function () {
return self.isOperational() && !self.isPrinting() && self.loginState.isUser() && !$.browser.mobile;
});
self.showKeycontrols = ko.computed(function () {
return self.keycontrolActive() && self.keycontrolPossible();
});
self.settings.printerProfiles.currentProfileData.subscribe(function () {
self._updateExtruderCount();
self.settings.printerProfiles.currentProfileData().extruder.count.subscribe(self._updateExtruderCount);
});
self._updateExtruderCount = function () {
var tools = [];
var numExtruders = self.settings.printerProfiles.currentProfileData().extruder.count();
if (numExtruders > 1) {
// multiple extruders
for (var extruder = 0; extruder < numExtruders; extruder++) {
tools[extruder] = self._createToolEntry();
tools[extruder]["name"](gettext("Tool") + " " + extruder);
tools[extruder]["key"]("tool" + extruder);
}
} else {
// only one extruder, no need to add numbers
tools[0] = self._createToolEntry();
tools[0]["name"](gettext("Hotend"));
tools[0]["key"]("tool0");
}
self.tools(tools);
};
self.fromCurrentData = function (data) {
self._processStateData(data.state);
};
self.fromHistoryData = function (data) {
self._processStateData(data.state);
};
self._processStateData = function (data) {
self.isErrorOrClosed(data.flags.closedOrError);
self.isOperational(data.flags.operational);
self.isPaused(data.flags.paused);
self.isPrinting(data.flags.printing);
self.isError(data.flags.error);
self.isReady(data.flags.ready);
self.isLoading(data.flags.loading);
};
self.onEventSettingsUpdated = function (payload) {
self.requestData();
};
self.onEventRegisteredMessageReceived = function(payload) {
if (payload.key in self.feedbackControlLookup) {
var outputs = self.feedbackControlLookup[payload.key];
_.each(payload.outputs, function(value, key) {
if (outputs.hasOwnProperty(key)) {
outputs[key](value);
}
});
}
};
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",
method: "GET",
dataType: "json",
success: function (response) {
self._fromResponse(response);
}
});
};
self._fromResponse = function (response) {
self.controlsFromServer = response.controls;
self.rerenderControls();
};
self._processControls = function (controls) {
for (var i = 0; i < controls.length; i++) {
controls[i] = self._processControl(controls[i]);
}
return controls;
};
self._processControl = function (control) {
if (control.hasOwnProperty("processed") && control.processed) {
return control;
}
if (control.hasOwnProperty("template") && control.hasOwnProperty("key") && control.hasOwnProperty("template_key") && !control.hasOwnProperty("output")) {
control.output = ko.observable(control.default || "");
if (!self.feedbackControlLookup.hasOwnProperty(control.key)) {
self.feedbackControlLookup[control.key] = {};
}
self.feedbackControlLookup[control.key][control.template_key] = control.output;
}
if (control.hasOwnProperty("children")) {
control.children = ko.observableArray(self._processControls(control.children));
if (!control.hasOwnProperty("layout") || !(control.layout == "vertical" || control.layout == "horizontal" || control.layout == "horizontal_grid")) {
control.layout = "vertical";
}
if (!control.hasOwnProperty("collapsed")) {
control.collapsed = false;
}
}
if (control.hasOwnProperty("input")) {
var attributeToInt = function(obj, key, def) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
if (_.isNumber(val)) {
return val;
}
var parsedVal = parseInt(val);
if (!isNaN(parsedVal)) {
return parsedVal;
}
}
return def;
};
_.each(control.input, function (element) {
if (element.hasOwnProperty("slider") && _.isObject(element.slider)) {
element.slider["min"] = attributeToInt(element.slider, "min", 0);
element.slider["max"] = attributeToInt(element.slider, "max", 255);
// try defaultValue, default to min
var defaultValue = attributeToInt(element, "default", element.slider.min);
// if default value is not within range of min and max, correct that
if (!_.inRange(defaultValue, element.slider.min, element.slider.max)) {
// use bound closer to configured default value
defaultValue = defaultValue < element.slider.min ? element.slider.min : element.slider.max;
}
element.value = ko.observable(defaultValue);
} else {
element.slider = false;
element.value = ko.observable((element.hasOwnProperty("default")) ? element["default"] : undefined);
}
});
}
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);
}
}
}
control.processed = true;
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) {
var callback;
if (data.hasOwnProperty("javascript")) {
callback = data.javascript;
} else {
callback = self.sendCustomCommand;
}
if (data.confirm) {
showConfirmationDialog({
message: data.confirm,
onproceed: function (e) {
callback(data);
}
});
} else {
callback(data);
}
};
self.sendJogCommand = function (axis, multiplier, distance) {
if (typeof distance === "undefined")
distance = $('#jog_distance button.active').data('distance');
if (self.settings.printerProfiles.currentProfileData() && self.settings.printerProfiles.currentProfileData()["axes"] && self.settings.printerProfiles.currentProfileData()["axes"][axis] && self.settings.printerProfiles.currentProfileData()["axes"][axis]["inverted"]()) {
multiplier *= -1;
}
var data = {
"command": "jog"
};
data[axis] = distance * multiplier;
self.sendPrintHeadCommand(data);
};
self.sendHomeCommand = function (axis) {
self.sendPrintHeadCommand({
"command": "home",
"axes": axis
});
};
self.sendFeedRateCommand = function () {
self.sendPrintHeadCommand({
"command": "feedrate",
"factor": self.feedRate()
});
};
self.sendExtrudeCommand = function () {
self._sendECommand(1);
};
self.sendRetractCommand = function () {
self._sendECommand(-1);
};
self.sendFlowRateCommand = function () {
self.sendToolCommand({
"command": "flowrate",
"factor": self.flowRate()
});
};
self._sendECommand = function (dir) {
var length = self.extrusionAmount();
if (!length) length = self.settings.printer_defaultExtrusionLength();
self.sendToolCommand({
command: "extrude",
amount: length * dir
});
};
self.sendSelectToolCommand = function (data) {
if (!data || !data.key()) return;
self.sendToolCommand({
command: "select",
tool: data.key()
});
};
self.sendPrintHeadCommand = function (data) {
$.ajax({
url: API_BASEURL + "printer/printhead",
type: "POST",
dataType: "json",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify(data)
});
};
self.sendToolCommand = function (data) {
$.ajax({
url: API_BASEURL + "printer/tool",
type: "POST",
dataType: "json",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify(data)
});
};
self.sendCustomCommand = function (command) {
if (!command)
return;
var data = undefined;
if (command.hasOwnProperty("command")) {
// single command
data = {"command": command.command};
} else if (command.hasOwnProperty("commands")) {
// multi command
data = {"commands": command.commands};
} else if (command.hasOwnProperty("script")) {
data = {"script": command.script};
if (command.hasOwnProperty("context")) {
data["context"] = command.context;
}
} else {
return;
}
if (command.hasOwnProperty("input")) {
// parametric command(s)
data["parameters"] = {};
_.each(command.input, function(input) {
if (!input.hasOwnProperty("parameter") || !input.hasOwnProperty("value")) {
return;
}
data["parameters"][input.parameter] = input.value();
});
}
$.ajax({
url: API_BASEURL + "printer/command",
type: "POST",
dataType: "json",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify(data)
})
};
self.displayMode = function (customControl) {
if (customControl.hasOwnProperty("children")) {
if (customControl.name) {
return "customControls_containerTemplate_collapsable";
} else {
return "customControls_containerTemplate_nameless";
}
} else {
return "customControls_controlTemplate";
}
};
self.rowCss = function (customControl) {
var span = "span2";
var offset = "";
if (customControl.hasOwnProperty("width")) {
span = "span" + customControl.width;
}
if (customControl.hasOwnProperty("offset")) {
offset = "offset" + customControl.offset;
}
return span + " " + offset;
};
self.onStartup = function () {
self.requestData();
};
self.updateRotatorWidth = function() {
var webcamImage = $("#webcam_image");
if (self.settings.webcam_rotate90()) {
if (webcamImage.width() > 0) {
$("#webcam_rotator").css("height", webcamImage.width());
} else {
webcamImage.off("load.rotator");
webcamImage.on("load.rotator", function() {
$("#webcam_rotator").css("height", webcamImage.width());
webcamImage.off("load.rotator");
});
}
} else {
$("#webcam_rotator").css("height", "");
}
}
self.onSettingsBeforeSave = self.updateRotatorWidth;
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);
}
} 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.onAllBound = function (allViewModels) {
var additionalControls = [];
_.each(allViewModels, function (viewModel) {
if (viewModel.hasOwnProperty("getAdditionalControls")) {
additionalControls = additionalControls.concat(viewModel.getAdditionalControls());
}
});
if (additionalControls.length > 0) {
self.additionalControls = additionalControls;
self.rerenderControls();
}
};
self.onFocus = function (data, event) {
if (!self.settings.feature_keyboardControl()) return;
self.keycontrolActive(true);
};
self.onMouseOver = function (data, event) {
if (!self.settings.feature_keyboardControl()) return;
$("#webcam_container").focus();
self.keycontrolActive(true);
};
self.onMouseOut = function (data, event) {
if (!self.settings.feature_keyboardControl()) return;
$("#webcam_container").blur();
self.keycontrolActive(false);
};
self.toggleKeycontrolHelp = function () {
self.keycontrolHelpActive(!self.keycontrolHelpActive());
};
self.onKeyDown = function (data, event) {
if (!self.settings.feature_keyboardControl()) return;
var button = undefined;
var visualizeClick = true;
switch (event.which) {
case 37: // left arrow key
// X-
button = $("#control-xdec");
break;
case 38: // up arrow key
// Y+
button = $("#control-yinc");
break;
case 39: // right arrow key
// X+
button = $("#control-xinc");
break;
case 40: // down arrow key
// Y-
button = $("#control-ydec");
break;
case 49: // number 1
case 97: // numpad 1
// Distance 0.1
button = $("#control-distance01");
visualizeClick = false;
break;
case 50: // number 2
case 98: // numpad 2
// Distance 1
button = $("#control-distance1");
visualizeClick = false;
break;
case 51: // number 3
case 99: // numpad 3
// Distance 10
button = $("#control-distance10");
visualizeClick = false;
break;
case 52: // number 4
case 100: // numpad 4
// Distance 100
button = $("#control-distance100");
visualizeClick = false;
break;
case 33: // page up key
case 87: // w key
// z lift up
button = $("#control-zinc");
break;
case 34: // page down key
case 83: // s key
// z lift down
button = $("#control-zdec");
break;
case 36: // home key
// xy home
button = $("#control-xyhome");
break;
case 35: // end key
// z home
button = $("#control-zhome");
break;
default:
event.preventDefault();
return false;
}
if (button === undefined) {
return false;
} else {
event.preventDefault();
if (visualizeClick) {
button.addClass("active");
setTimeout(function () {
button.removeClass("active");
}, 150);
}
button.click();
}
};
}
OCTOPRINT_VIEWMODELS.push([
ControlViewModel,
["loginStateViewModel", "settingsViewModel"],
"#control"
]);
});