Modularize printer profile manager UI components
This commit is contained in:
parent
dedffe3514
commit
d2f8b957bc
6 changed files with 486 additions and 453 deletions
|
|
@ -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 || {};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
<form class="form-horizontal">
|
||||
<div class="control-group" data-bind="css: {error: nameInvalid()}">
|
||||
<label class="control-label">{{ _('Name') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" data-bind="value: name, valueUpdate: 'afterkeydown'">
|
||||
<span data-bind="visible: nameInvalid()"><br><span class="help-inline">{{ _('Name must be set') }}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="css: {error: identifierInvalid()}">
|
||||
<label class="control-label">{{ _('Identifier') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" data-bind="value: identifier, valueUpdate: 'afterkeydown', enable: isNew, css: {disabled: !isNew()}, attr: {placeholder: identifierPlaceholder}">
|
||||
<span data-bind="visible: identifierInvalid()"><br><span class="help-inline" data-bind="text: identifierInvalidText()"></span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Model') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" data-bind="value: model">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Color') }}</label>
|
||||
<div class="controls">
|
||||
<select data-bind="value: color, options: availableColors, optionsText: 'name', optionsValue: 'key'">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Form Factor') }}</label>
|
||||
<div class="controls">
|
||||
<label class="radio"><input type="radio" name="printerProfileFormFactorGroup" value="rectangular" data-bind="checked: volumeFormFactor"> {{ _('Rectangular') }}</label>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<label class="radio"><input type="radio" name="printerProfileFormFactorGroup" value="circular" data-bind="checked: volumeFormFactor"> {{ _('Circular') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Origin') }}</label>
|
||||
<div class="controls">
|
||||
<select data-bind="value: volumeOrigin, options: availableOrigins, optionsText: 'name', optionsValue: 'key'">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Volume') }}</label>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('X') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: volumeWidth">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('Y') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: volumeDepth">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('Z') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: volumeHeight">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Heated Bed') }}</label>
|
||||
<div class="controls">
|
||||
<input type="checkbox" data-bind="checked: heatedBed">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Axis') }}</label>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('X') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" class="input-mini text-right" data-bind="value: axisXSpeed">
|
||||
<span class="add-on">mm/min</span>
|
||||
</div>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: axisXInverted"> {{ _('Invert control') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('Y') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" class="input-mini text-right" data-bind="value: axisYSpeed">
|
||||
<span class="add-on">mm/min</span>
|
||||
</div>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: axisYInverted"> {{ _('Invert control') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('Z') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" class="input-mini text-right" data-bind="value: axisZSpeed">
|
||||
<span class="add-on">mm/min</span>
|
||||
</div>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: axisZInverted"> {{ _('Invert control') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('E') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" class="input-mini text-right" data-bind="value: axisESpeed">
|
||||
<span class="add-on">mm/min</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Nozzle Diameter') }}</label>
|
||||
<div class="controls">
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: nozzleDiameter">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Number of Extruders') }}</label>
|
||||
<div class="controls">
|
||||
<input type="number" class="input-mini text-right" min="1" max="10" data-bind="value: extruders">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="visible: extruders() > 1">
|
||||
<label class="control-label">{{ _('Nozzle Offsets (relative to first nozzle T0)') }}</label>
|
||||
<!-- ko foreach: koExtruderOffsets -->
|
||||
<div class="controls form-inline">
|
||||
<label>T<span data-bind="text: idx"></span>:</label>
|
||||
<label>X</label>
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: x">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
<label>Y</label>
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: y">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<div id="settings_printerProfiles_editDialog" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<a href="#" class="close" data-dismiss="modal" aria-hidden="true">×</a>
|
||||
<h3 class="modal-title"></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- ko with: printerProfiles.editor -->
|
||||
{% include '_snippets/settings/printerprofiles/profileEditor.jinja2' %}
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Abort') }}</button>
|
||||
<button class="btn btn-primary btn-confirm" data-bind="enabled: printerProfiles.enableEditorSubmitButton, css: {disabled: !printerProfiles.enableEditorSubmitButton()}"><i class="icon-spinner icon-spin" data-bind="visible: printerProfiles.requestInProgress()"></i> {{ _('Confirm') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<table class="table table-striped table-hover table-condensed table-hover" id="settings_printerProfiles_profiles" data-bind="visible: printerProfiles.profiles.items().length > 0" style="display: none">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="settings_printerProfiles_profiles_name">{{ _('Name') }}</th>
|
||||
<th class="settings_printerProfiles_profiles_model">{{ _('Model') }}</th>
|
||||
<th class="settings_printerProfiles_profiles_action">{{ _('Action') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind="foreach: printerProfiles.profiles.paginatedItems">
|
||||
<tr data-bind="attr: {title: name}">
|
||||
<td class="settings_printerProfiles_profiles_name"><span class="icon-star" data-bind="invisible: !isdefault()"></span> <span data-bind="text: name"></span></td>
|
||||
<td class="settings_printerProfiles_profiles_model" data-bind="text: model"></td>
|
||||
<td class="settings_printerProfiles_profiles_action">
|
||||
<a href="#" class="icon-star" title="{{ _('Set as default profile') }}" data-bind="click: function() { $root.printerProfiles.makeDefault($data); }, css: {disabled: $root.printerProfiles.requestInProgress()}, enabled: !$root.printerProfiles.requestInProgress()"></a> | <a href="#" class="icon-pencil" title="{{ _('Edit Profile') }}" data-bind="click: function() { $root.printerProfiles.showEditProfileDialog($data); }, css: {disabled: $root.printerProfiles.requestInProgress()}, enabled: !$root.printerProfiles.requestInProgress()"></a> | <a href="#" class="icon-trash" title="{{ _('Delete Profile') }}" data-bind="click: function() { $root.printerProfiles.removeProfile($data); }, css: {disabled: $root.printerProfiles.requestInProgress()}, enabled: !$root.printerProfiles.requestInProgress()"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="pagination pagination-mini pagination-centered">
|
||||
<ul>
|
||||
<li data-bind="css: {disabled: printerProfiles.profiles.currentPage() === 0}"><a href="#" data-bind="click: printerProfiles.profiles.prevPage">«</a></li>
|
||||
</ul>
|
||||
<ul data-bind="foreach: printerProfiles.profiles.pages">
|
||||
<li data-bind="css: { active: $data.number === $root.printerProfiles.profiles.currentPage(), disabled: $data.number === -1 }"><a href="#" data-bind="text: $data.text, click: function() { $root.printerProfiles.profiles.changePage($data.number); }"></a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li data-bind="css: {disabled: printerProfiles.profiles.currentPage() === printerProfiles.profiles.lastPage()}"><a href="#" data-bind="click: printerProfiles.profiles.nextPage">»</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -1,197 +1,7 @@
|
|||
<h3>{{ _('Printer Profiles') }}</h3>
|
||||
<table class="table table-striped table-hover table-condensed table-hover" id="settings_printerProfiles_profiles">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="settings_printerProfiles_profiles_name">{{ _('Name') }}</th>
|
||||
<th class="settings_printerProfiles_profiles_model">{{ _('Model') }}</th>
|
||||
<th class="settings_printerProfiles_profiles_action">{{ _('Action') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind="foreach: printerProfiles.profiles.paginatedItems">
|
||||
<tr data-bind="attr: {title: name}">
|
||||
<td class="settings_printerProfiles_profiles_name"><span class="icon-star" data-bind="invisible: !isdefault()"></span> <span data-bind="text: name"></span></td>
|
||||
<td class="settings_printerProfiles_profiles_model" data-bind="text: model"></td>
|
||||
<td class="settings_printerProfiles_profiles_action">
|
||||
<a href="#" class="icon-star" title="{{ _('Set as default profile') }}" data-bind="click: function() { $root.printerProfiles.makeDefault($data); }, css: {disabled: $root.printerProfiles.requestInProgress()}, enabled: !$root.printerProfiles.requestInProgress()"></a> | <a href="#" class="icon-pencil" title="{{ _('Edit Profile') }}" data-bind="click: function() { $root.printerProfiles.showEditProfileDialog($data); }, css: {disabled: $root.printerProfiles.requestInProgress()}, enabled: !$root.printerProfiles.requestInProgress()"></a> | <a href="#" class="icon-trash" title="{{ _('Delete Profile') }}" data-bind="click: function() { $root.printerProfiles.removeProfile($data); }, css: {disabled: $root.printerProfiles.requestInProgress()}, enabled: !$root.printerProfiles.requestInProgress()"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="pagination pagination-mini pagination-centered">
|
||||
<ul>
|
||||
<li data-bind="css: {disabled: printerProfiles.profiles.currentPage() === 0}"><a href="#" data-bind="click: printerProfiles.profiles.prevPage">«</a></li>
|
||||
</ul>
|
||||
<ul data-bind="foreach: printerProfiles.profiles.pages">
|
||||
<li data-bind="css: { active: $data.number === $root.printerProfiles.profiles.currentPage(), disabled: $data.number === -1 }"><a href="#" data-bind="text: $data.text, click: function() { $root.printerProfiles.profiles.changePage($data.number); }"></a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li data-bind="css: {disabled: printerProfiles.profiles.currentPage() === printerProfiles.profiles.lastPage()}"><a href="#" data-bind="click: printerProfiles.profiles.nextPage">»</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% include "_snippets/settings/printerprofiles/profiles.jinja2" %}
|
||||
|
||||
<button class="btn pull-right" data-bind="click: function() { $root.printerProfiles.showEditProfileDialog(); }">{{ _('Add Profile...') }}</button>
|
||||
|
||||
<div id="settings_printerProfiles_editDialog" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<a href="#" class="close" data-dismiss="modal" aria-hidden="true">×</a>
|
||||
<h3 class="modal-title"></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group" data-bind="css: {error: printerProfiles.editorNameInvalid()}">
|
||||
<label class="control-label">{{ _('Name') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" data-bind="value: printerProfiles.editorName, valueUpdate: 'afterkeydown'">
|
||||
<span data-bind="visible: printerProfiles.editorNameInvalid()"><br><span class="help-inline">{{ _('Name must be set') }}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="css: {error: printerProfiles.editorIdentifierInvalid()}">
|
||||
<label class="control-label">{{ _('Identifier') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" data-bind="value: printerProfiles.editorIdentifier, valueUpdate: 'afterkeydown', enable: printerProfiles.editorNew, css: {disabled: !printerProfiles.editorNew()}, attr: {placeholder: printerProfiles.editorIdentifierPlaceholder}">
|
||||
<span data-bind="visible: printerProfiles.editorIdentifierInvalid()"><br><span class="help-inline" data-bind="text: printerProfiles.editorIdentifierInvalidText()"></span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Model') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" data-bind="value: printerProfiles.editorModel">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Color') }}</label>
|
||||
<div class="controls">
|
||||
<select data-bind="value: printerProfiles.editorColor, options: printerProfiles.availableColors, optionsText: 'name', optionsValue: 'key'">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Form Factor') }}</label>
|
||||
<div class="controls">
|
||||
<label class="radio"><input type="radio" name="printerProfileFormFactorGroup" value="rectangular" data-bind="checked: printerProfiles.editorVolumeFormFactor"> {{ _('Rectangular') }}</label>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<label class="radio"><input type="radio" name="printerProfileFormFactorGroup" value="circular" data-bind="checked: printerProfiles.editorVolumeFormFactor"> {{ _('Circular') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Origin') }}</label>
|
||||
<div class="controls">
|
||||
<select data-bind="value: printerProfiles.editorVolumeOrigin, options: printerProfiles.availableOrigins, optionsText: 'name', optionsValue: 'key'">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Volume') }}</label>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('X') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: printerProfiles.editorVolumeWidth">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('Y') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: printerProfiles.editorVolumeDepth">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('Z') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: printerProfiles.editorVolumeHeight">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Heated Bed') }}</label>
|
||||
<div class="controls">
|
||||
<input type="checkbox" data-bind="checked: printerProfiles.editorHeatedBed">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Axis') }}</label>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('X') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" class="input-mini text-right" data-bind="value: printerProfiles.editorAxisXSpeed">
|
||||
<span class="add-on">mm/min</span>
|
||||
</div>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: printerProfiles.editorAxisXInverted"> {{ _('Invert control') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('Y') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" class="input-mini text-right" data-bind="value: printerProfiles.editorAxisYSpeed">
|
||||
<span class="add-on">mm/min</span>
|
||||
</div>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: printerProfiles.editorAxisYInverted"> {{ _('Invert control') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('Z') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" class="input-mini text-right" data-bind="value: printerProfiles.editorAxisZSpeed">
|
||||
<span class="add-on">mm/min</span>
|
||||
</div>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" data-bind="checked: printerProfiles.editorAxisZInverted"> {{ _('Invert control') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="controls form-inline">
|
||||
<label>{{ _('E') }}</label>
|
||||
<div class="input-append">
|
||||
<input type="number" class="input-mini text-right" data-bind="value: printerProfiles.editorAxisESpeed">
|
||||
<span class="add-on">mm/min</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Nozzle Diameter') }}</label>
|
||||
<div class="controls">
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: printerProfiles.editorNozzleDiameter">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Number of Extruders') }}</label>
|
||||
<div class="controls">
|
||||
<input type="number" class="input-mini text-right" min="1" max="10" data-bind="value: printerProfiles.editorExtruders">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="visible: printerProfiles.editorExtruders() > 1">
|
||||
<label class="control-label">{{ _('Nozzle Offsets (relative to first nozzle T0)') }}</label>
|
||||
<!-- ko foreach: printerProfiles.koEditorExtruderOffsets -->
|
||||
<div class="controls form-inline">
|
||||
<label>T<span data-bind="text: idx"></span>:</label>
|
||||
<label>X</label>
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: x">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
<label>Y</label>
|
||||
<div class="input-append">
|
||||
<input type="number" step="0.01" class="input-mini text-right" data-bind="value: y">
|
||||
<span class="add-on">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Abort') }}</button>
|
||||
<button class="btn btn-primary btn-confirm" data-bind="enabled: printerProfiles.enableEditorSubmitButton, css: {disabled: !printerProfiles.enableEditorSubmitButton()}"><i class="icon-spinner icon-spin" data-bind="visible: printerProfiles.requestInProgress()"></i> {{ _('Confirm') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% include "_snippets/settings/printerprofiles/profileImporter.jinja2" %}
|
||||
|
|
|
|||
Loading…
Reference in a new issue