diff --git a/src/octoprint/static/js/app/client/printerprofiles.js b/src/octoprint/static/js/app/client/printerprofiles.js index 34552c9e..181eaa5e 100644 --- a/src/octoprint/static/js/app/client/printerprofiles.js +++ b/src/octoprint/static/js/app/client/printerprofiles.js @@ -12,7 +12,7 @@ }; OctoPrint.printerprofiles = { - get: function (opts) { + list: function (opts) { return OctoPrint.get(url, opts); }, @@ -26,6 +26,10 @@ return OctoPrint.postJson(url, data, opts); }, + get: function (id, opts) { + return OctoPrint.get(profileUrl(id), opts); + }, + update: function (id, profile, additional, opts) { profile = profile || {}; additional = additional || {}; diff --git a/src/octoprint/static/js/app/viewmodels/printerprofiles.js b/src/octoprint/static/js/app/viewmodels/printerprofiles.js index 0236fc55..44da1879 100644 --- a/src/octoprint/static/js/app/viewmodels/printerprofiles.js +++ b/src/octoprint/static/js/app/viewmodels/printerprofiles.js @@ -1,37 +1,285 @@ $(function() { - function PrinterProfilesViewModel() { + var cleanProfile = function() { + return { + id: "", + name: "", + model: "", + color: "default", + volume: { + formFactor: "rectangular", + width: 200, + depth: 200, + height: 200, + origin: "lowerleft" + }, + heatedBed: true, + axes: { + x: {speed: 6000, inverted: false}, + y: {speed: 6000, inverted: false}, + z: {speed: 200, inverted: false}, + e: {speed: 300, inverted: false} + }, + extruder: { + count: 1, + offsets: [ + [0,0] + ], + nozzleDiameter: 0.4 + } + } + }; + + function EditedProfileViewModel(profiles) { var self = this; - self._cleanProfile = function() { - return { - id: "", - name: "", - model: "", - color: "default", + self.profiles = profiles; + + self.isNew = ko.observable(false); + + self.name = ko.observable(); + self.color = ko.observable(); + self.identifier = ko.observable(); + self.identifierPlaceholder = ko.observable(); + self.model = ko.observable(); + + self.volumeWidth = ko.observable(); + self.volumeHeight = ko.observable(); + self.volumeDepth = ko.observable(); + self.volumeFormFactor = ko.observable(); + self.volumeOrigin = ko.observable(); + + self.volumeFormFactor.subscribe(function(value) { + if (value == "circular") { + self.volumeOrigin("center"); + } + }); + + self.heatedBed = ko.observable(); + + self.nozzleDiameter = ko.observable(); + self.extruders = ko.observable(); + self.extruderOffsets = ko.observableArray(); + + self.axisXSpeed = ko.observable(); + self.axisYSpeed = ko.observable(); + self.axisZSpeed = ko.observable(); + self.axisESpeed = ko.observable(); + + self.axisXInverted = ko.observable(false); + self.axisYInverted = ko.observable(false); + self.axisZInverted = ko.observable(false); + self.axisEInverted = ko.observable(false); + + self.koExtruderOffsets = ko.pureComputed(function() { + var extruderOffsets = self.extruderOffsets(); + var numExtruders = self.extruders(); + if (!numExtruders) { + numExtruders = 1; + } + + if (numExtruders - 1 > extruderOffsets.length) { + for (var i = extruderOffsets.length; i < numExtruders; i++) { + extruderOffsets[i] = { + idx: i + 1, + x: ko.observable(0), + y: ko.observable(0) + } + } + self.extruderOffsets(extruderOffsets); + } + + return extruderOffsets.slice(0, numExtruders - 1); + }); + + self.nameInvalid = ko.pureComputed(function() { + return !self.name(); + }); + + self.identifierInvalid = ko.pureComputed(function() { + var identifier = self.identifier(); + var placeholder = self.identifierPlaceholder(); + var data = identifier; + if (!identifier) { + data = placeholder; + } + + var validCharacters = (data && (data == self._sanitize(data))); + + var existingProfile = self.profiles.getItem(function(item) {return item.id == data}); + return !data || !validCharacters || (self.isNew() && existingProfile != undefined); + }); + + self.identifierInvalidText = ko.pureComputed(function() { + if (!self.identifierInvalid()) { + return ""; + } + + if (!self.identifier() && !self.identifierPlaceholder()) { + return gettext("Identifier must be set"); + } else if (self.identifier() != self._sanitize(self.identifier())) { + return gettext("Invalid characters, only a-z, A-Z, 0-9, -, ., _, ( and ) are allowed") + } else { + return gettext("A profile with such an identifier already exists"); + } + }); + + self.name.subscribe(function() { + self.identifierPlaceholder(self._sanitize(self.name()).toLowerCase()); + }); + + self.valid = function() { + return !self.nameInvalid() && !self.identifierInvalid(); + }; + + self.availableColors = ko.observable([ + {key: "default", name: gettext("default")}, + {key: "red", name: gettext("red")}, + {key: "orange", name: gettext("orange")}, + {key: "yellow", name: gettext("yellow")}, + {key: "green", name: gettext("green")}, + {key: "blue", name: gettext("blue")}, + {key: "black", name: gettext("black")} + ]); + + self.availableOrigins = ko.pureComputed(function() { + var formFactor = self.volumeFormFactor(); + + var possibleOrigins = { + "lowerleft": gettext("Lower Left"), + "center": gettext("Center") + }; + + var keys = []; + if (formFactor == "rectangular") { + keys = ["lowerleft", "center"]; + } else if (formFactor == "circular") { + keys = ["center"]; + } + + var result = []; + _.each(keys, function(key) { + result.push({key: key, name: possibleOrigins[key]}); + }); + return result; + }); + + self.fromProfileData = function(data) { + self.isNew(data === undefined); + + if (data === undefined) { + data = cleanProfile(); + } + + self.identifier(data.id); + self.name(data.name); + self.color(data.color); + self.model(data.model); + + self.volumeWidth(data.volume.width); + self.volumeHeight(data.volume.depth); + self.volumeDepth(data.volume.height); + self.volumeFormFactor(data.volume.formFactor); + self.volumeOrigin(data.volume.origin); + + self.heatedBed(data.heatedBed); + + self.nozzleDiameter(data.extruder.nozzleDiameter); + self.extruders(data.extruder.count); + var offsets = []; + if (data.extruder.count > 1) { + _.each(_.slice(data.extruder.offsets, 1), function(offset, index) { + offsets.push({ + idx: index + 1, + x: ko.observable(offset[0]), + y: ko.observable(offset[1]) + }); + }); + } + self.extruderOffsets(offsets); + + self.axisXSpeed(data.axes.x.speed); + self.axisXInverted(data.axes.x.inverted); + self.axisYSpeed(data.axes.y.speed); + self.axisYInverted(data.axes.y.inverted); + self.axisZSpeed(data.axes.z.speed); + self.axisZInverted(data.axes.z.inverted); + self.axisESpeed(data.axes.e.speed); + self.axisEInverted(data.axes.e.inverted); + }; + + self.toProfileData = function() { + var identifier = self.identifier(); + if (!identifier) { + identifier = self.identifierPlaceholder(); + } + + var profile = { + id: identifier, + name: self.name(), + color: self.color(), + model: self.model(), volume: { - formFactor: "rectangular", - width: 200, - depth: 200, - height: 200, - origin: "lowerleft" - }, - heatedBed: true, - axes: { - x: {speed: 6000, inverted: false}, - y: {speed: 6000, inverted: false}, - z: {speed: 200, inverted: false}, - e: {speed: 300, inverted: false} + width: parseFloat(self.volumeWidth()), + depth: parseFloat(self.volumeHeight()), + height: parseFloat(self.volumeHeight()), + formFactor: self.volumeFormFactor(), + origin: self.volumeOrigin() }, + heatedBed: self.heatedBed(), extruder: { - count: 1, + count: parseInt(self.extruders()), offsets: [ - [0,0] + [0.0, 0.0] ], - nozzleDiameter: 0.4 + nozzleDiameter: parseFloat(self.nozzleDiameter()) + }, + axes: { + x: { + speed: parseInt(self.axisXSpeed()), + inverted: self.axisXInverted() + }, + y: { + speed: parseInt(self.axisYSpeed()), + inverted: self.axisYInverted() + }, + z: { + speed: parseInt(self.axisZSpeed()), + inverted: self.axisZInverted() + }, + e: { + speed: parseInt(self.axisESpeed()), + inverted: self.axisEInverted() + } + } + }; + + if (self.extruders() > 1) { + for (var i = 0; i < self.extruders() - 1; i++) { + var offset = [0.0, 0.0]; + if (i < self.extruderOffsets().length) { + try { + offset = [parseFloat(self.extruderOffsets()[i]["x"]()), parseFloat(self.extruderOffsets()[i]["y"]())]; + } catch (exc) { + log.error("Invalid offset in profile", identifier, "for extruder", i+1, ":", self.extruderOffsets()[i]["x"], ",", self.extruderOffsets()[i]["y"]); + } + } + profile.extruder.offsets.push(offset); } } + + return profile; }; + self._sanitize = function(name) { + return name.replace(/[^a-zA-Z0-9\-_\.\(\) ]/g, "").replace(/ /g, "_"); + }; + + self.fromProfileData(cleanProfile()); + } + + function PrinterProfilesViewModel() { + var self = this; + self.requestInProgress = ko.observable(false); self.profiles = new ItemListHelper( @@ -53,135 +301,19 @@ $(function() { self.defaultProfile = ko.observable(); self.currentProfile = ko.observable(); - self.currentProfileData = ko.observable(ko.mapping.fromJS(self._cleanProfile())); - - self.editorNew = ko.observable(false); - - self.editorName = ko.observable(); - self.editorColor = ko.observable(); - self.editorIdentifier = ko.observable(); - self.editorIdentifierPlaceholder = ko.observable(); - self.editorModel = ko.observable(); - - self.editorVolumeWidth = ko.observable(); - self.editorVolumeDepth = ko.observable(); - self.editorVolumeHeight = ko.observable(); - self.editorVolumeFormFactor = ko.observable(); - self.editorVolumeOrigin = ko.observable(); - - self.editorVolumeFormFactor.subscribe(function(value) { - if (value == "circular") { - self.editorVolumeOrigin("center"); + self.createProfileEditor = function(data) { + var editor = new EditedProfileViewModel(self.profiles); + if (data !== undefined) { + editor.fromProfileData(data); } - }); + return editor; + }; - self.editorHeatedBed = ko.observable(); - - self.editorNozzleDiameter = ko.observable(); - self.editorExtruders = ko.observable(); - self.editorExtruderOffsets = ko.observableArray(); - - self.editorAxisXSpeed = ko.observable(); - self.editorAxisYSpeed = ko.observable(); - self.editorAxisZSpeed = ko.observable(); - self.editorAxisESpeed = ko.observable(); - - self.editorAxisXInverted = ko.observable(false); - self.editorAxisYInverted = ko.observable(false); - self.editorAxisZInverted = ko.observable(false); - self.editorAxisEInverted = ko.observable(false); - - self.availableColors = ko.observable([ - {key: "default", name: gettext("default")}, - {key: "red", name: gettext("red")}, - {key: "orange", name: gettext("orange")}, - {key: "yellow", name: gettext("yellow")}, - {key: "green", name: gettext("green")}, - {key: "blue", name: gettext("blue")}, - {key: "black", name: gettext("black")} - ]); - - self.availableOrigins = ko.pureComputed(function() { - var formFactor = self.editorVolumeFormFactor(); - - var possibleOrigins = { - "lowerleft": gettext("Lower Left"), - "center": gettext("Center") - }; - - var keys = []; - if (formFactor == "rectangular") { - keys = ["lowerleft", "center"]; - } else if (formFactor == "circular") { - keys = ["center"]; - } - - var result = []; - _.each(keys, function(key) { - result.push({key: key, name: possibleOrigins[key]}); - }); - return result; - }); - - self.koEditorExtruderOffsets = ko.pureComputed(function() { - var extruderOffsets = self.editorExtruderOffsets(); - var numExtruders = self.editorExtruders(); - if (!numExtruders) { - numExtruders = 1; - } - - if (numExtruders - 1 > extruderOffsets.length) { - for (var i = extruderOffsets.length; i < numExtruders; i++) { - extruderOffsets[i] = { - idx: i + 1, - x: ko.observable(0), - y: ko.observable(0) - } - } - self.editorExtruderOffsets(extruderOffsets); - } - - return extruderOffsets.slice(0, numExtruders - 1); - }); - - self.editorNameInvalid = ko.pureComputed(function() { - return !self.editorName(); - }); - - self.editorIdentifierInvalid = ko.pureComputed(function() { - var identifier = self.editorIdentifier(); - var placeholder = self.editorIdentifierPlaceholder(); - var data = identifier; - if (!identifier) { - data = placeholder; - } - - var validCharacters = (data && (data == self._sanitize(data))); - - var existingProfile = self.profiles.getItem(function(item) {return item.id == data}); - return !data || !validCharacters || (self.editorNew() && existingProfile != undefined); - }); - - self.editorIdentifierInvalidText = ko.pureComputed(function() { - if (!self.editorIdentifierInvalid()) { - return ""; - } - - if (!self.editorIdentifier() && !self.editorIdentifierPlaceholder()) { - return gettext("Identifier must be set"); - } else if (self.editorIdentifier() != self._sanitize(self.editorIdentifier())) { - return gettext("Invalid characters, only a-z, A-Z, 0-9, -, ., _, ( and ) are allowed") - } else { - return gettext("A profile with such an identifier already exists"); - } - }); + self.editor = self.createProfileEditor(); + self.currentProfileData = ko.observable(ko.mapping.fromJS(cleanProfile())); self.enableEditorSubmitButton = ko.pureComputed(function() { - return !self.editorNameInvalid() && !self.editorIdentifierInvalid() && !self.requestInProgress(); - }); - - self.editorName.subscribe(function() { - self.editorIdentifierPlaceholder(self._sanitize(self.editorName()).toLowerCase()); + return self.editor.valid() && !self.requestInProgress(); }); self.makeDefault = function(data) { @@ -194,7 +326,7 @@ $(function() { }; self.requestData = function() { - OctoPrint.printerprofiles.get() + OctoPrint.printerprofiles.list() .done(self.fromResponse); }; @@ -222,7 +354,7 @@ $(function() { }; self.addProfile = function(callback) { - var profile = self._editorData(); + var profile = self.editor.toProfileData(); self.requestInProgress(true); OctoPrint.printerprofiles.add(profile) .done(function() { @@ -257,7 +389,7 @@ $(function() { self.updateProfile = function(profile, callback) { if (profile == undefined) { - profile = self._editorData(); + profile = self.editor.toProfileData(); } self.requestInProgress(true); @@ -278,54 +410,13 @@ $(function() { }; self.showEditProfileDialog = function(data) { - var add = false; - if (data == undefined) { - data = self._cleanProfile(); - add = true; - } - - self.editorNew(add); - - self.editorIdentifier(data.id); - self.editorName(data.name); - self.editorColor(data.color); - self.editorModel(data.model); - - self.editorVolumeWidth(data.volume.width); - self.editorVolumeDepth(data.volume.depth); - self.editorVolumeHeight(data.volume.height); - self.editorVolumeFormFactor(data.volume.formFactor); - self.editorVolumeOrigin(data.volume.origin); - - self.editorHeatedBed(data.heatedBed); - - self.editorNozzleDiameter(data.extruder.nozzleDiameter); - self.editorExtruders(data.extruder.count); - var offsets = []; - if (data.extruder.count > 1) { - _.each(_.slice(data.extruder.offsets, 1), function(offset, index) { - offsets.push({ - idx: index + 1, - x: ko.observable(offset[0]), - y: ko.observable(offset[1]) - }); - }); - } - self.editorExtruderOffsets(offsets); - - self.editorAxisXSpeed(data.axes.x.speed); - self.editorAxisXInverted(data.axes.x.inverted); - self.editorAxisYSpeed(data.axes.y.speed); - self.editorAxisYInverted(data.axes.y.inverted); - self.editorAxisZSpeed(data.axes.z.speed); - self.editorAxisZInverted(data.axes.z.inverted); - self.editorAxisESpeed(data.axes.e.speed); - self.editorAxisEInverted(data.axes.e.inverted); + self.editor.fromProfileData(data); var editDialog = $("#settings_printerProfiles_editDialog"); var confirmButton = $("button.btn-confirm", editDialog); var dialogTitle = $("h3.modal-title", editDialog); + var add = data === undefined; dialogTitle.text(add ? gettext("Add Printer Profile") : _.sprintf(gettext("Edit Printer Profile \"%(name)s\""), {name: data.name})); confirmButton.unbind("click"); confirmButton.bind("click", function() { @@ -348,73 +439,6 @@ $(function() { } }; - self._editorData = function() { - var identifier = self.editorIdentifier(); - if (!identifier) { - identifier = self.editorIdentifierPlaceholder(); - } - - var profile = { - id: identifier, - name: self.editorName(), - color: self.editorColor(), - model: self.editorModel(), - volume: { - width: parseFloat(self.editorVolumeWidth()), - depth: parseFloat(self.editorVolumeDepth()), - height: parseFloat(self.editorVolumeHeight()), - formFactor: self.editorVolumeFormFactor(), - origin: self.editorVolumeOrigin() - }, - heatedBed: self.editorHeatedBed(), - extruder: { - count: parseInt(self.editorExtruders()), - offsets: [ - [0.0, 0.0] - ], - nozzleDiameter: parseFloat(self.editorNozzleDiameter()) - }, - axes: { - x: { - speed: parseInt(self.editorAxisXSpeed()), - inverted: self.editorAxisXInverted() - }, - y: { - speed: parseInt(self.editorAxisYSpeed()), - inverted: self.editorAxisYInverted() - }, - z: { - speed: parseInt(self.editorAxisZSpeed()), - inverted: self.editorAxisZInverted() - }, - e: { - speed: parseInt(self.editorAxisESpeed()), - inverted: self.editorAxisEInverted() - } - } - }; - - if (self.editorExtruders() > 1) { - for (var i = 0; i < self.editorExtruders() - 1; i++) { - var offset = [0.0, 0.0]; - if (i < self.editorExtruderOffsets().length) { - try { - offset = [parseFloat(self.editorExtruderOffsets()[i]["x"]()), parseFloat(self.editorExtruderOffsets()[i]["y"]())]; - } catch (exc) { - log.error("Invalid offset in profile", identifier, "for extruder", i+1, ":", self.editorExtruderOffsets()[i]["x"], ",", self.editorExtruderOffsets()[i]["y"]); - } - } - profile.extruder.offsets.push(offset); - } - } - - return profile; - }; - - self._sanitize = function(name) { - return name.replace(/[^a-zA-Z0-9\-_\.\(\) ]/g, "").replace(/ /g, "_"); - }; - self.onSettingsShown = self.requestData; self.onStartup = self.requestData; } diff --git a/src/octoprint/templates/_snippets/settings/printerprofiles/profileEditor.jinja2 b/src/octoprint/templates/_snippets/settings/printerprofiles/profileEditor.jinja2 new file mode 100644 index 00000000..3293b777 --- /dev/null +++ b/src/octoprint/templates/_snippets/settings/printerprofiles/profileEditor.jinja2 @@ -0,0 +1,150 @@ +
+
+ +
+ +
{{ _('Name must be set') }}
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+ + mm +
+
+
+ +
+ + mm +
+
+
+ +
+ + mm +
+
+
+
+ +
+ +
+
+
+ +
+ +
+ + mm/min +
+ +
+
+ +
+ + mm/min +
+ +
+
+ +
+ + mm/min +
+ +
+
+ +
+ + mm/min +
+
+
+
+ +
+
+ + mm +
+
+
+
+ +
+ +
+
+
+ + +
+ + +
+ + mm +
+ +
+ + mm +
+
+ +
+
diff --git a/src/octoprint/templates/_snippets/settings/printerprofiles/profileImporter.jinja2 b/src/octoprint/templates/_snippets/settings/printerprofiles/profileImporter.jinja2 new file mode 100644 index 00000000..d831c6dd --- /dev/null +++ b/src/octoprint/templates/_snippets/settings/printerprofiles/profileImporter.jinja2 @@ -0,0 +1,15 @@ + diff --git a/src/octoprint/templates/_snippets/settings/printerprofiles/profiles.jinja2 b/src/octoprint/templates/_snippets/settings/printerprofiles/profiles.jinja2 new file mode 100644 index 00000000..643458be --- /dev/null +++ b/src/octoprint/templates/_snippets/settings/printerprofiles/profiles.jinja2 @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/octoprint/templates/dialogs/settings/printerprofiles.jinja2 b/src/octoprint/templates/dialogs/settings/printerprofiles.jinja2 index dbd3655a..0bdcdae0 100644 --- a/src/octoprint/templates/dialogs/settings/printerprofiles.jinja2 +++ b/src/octoprint/templates/dialogs/settings/printerprofiles.jinja2 @@ -1,197 +1,7 @@

{{ _('Printer Profiles') }}

- - - - - - - - - - - - - - - -
{{ _('Name') }}{{ _('Model') }}{{ _('Action') }}
-  |  |  -
- - +{% include "_snippets/settings/printerprofiles/profiles.jinja2" %} - +{% include "_snippets/settings/printerprofiles/profileImporter.jinja2" %}