Files
onefinity-firmware/src/js/gcode-viewer.js
2024-03-29 03:05:37 +05:30

160 lines
3.4 KiB
JavaScript

"use strict";
const entityMap = {
"&": "&",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#39;",
"/": "&#x2F;",
"`": "&#x60;",
"=": "&#x3D;",
};
function escapeHTML(s) {
return s.replace(/[&<>"'`=\\/]/g, function (c) {
return entityMap[c];
});
}
module.exports = {
template: "#gcode-viewer-template",
data: function () {
return {
empty: true,
file: "",
line: -1,
};
},
events: {
"gcode-load": function (file) {
this.load(file);
},
"gcode-clear": function () {
this.clear();
},
"gcode-reload": function (file) {
this.reload(file);
},
"gcode-line": function (line) {
this.update_line(line);
},
},
ready: function () {
this.clusterize = new Clusterize({
rows: [],
scrollElem: this.$el.querySelector(".clusterize-scroll"),
contentElem: this.$el.querySelector(".clusterize-content"),
no_data_text: "GCode view...",
callbacks: { clusterChanged: this.highlight },
});
},
attached: function () {
if (typeof this.clusterize != "undefined") {
this.clusterize.refresh(true);
}
},
methods: {
load: async function (file) {
if (file == this.file) {
return;
}
this.clear();
this.file = file;
if (!file) {
return;
}
const response = await fetch(`/api/file/${file}`, { cache: "no-cache" });
const text = await response.text();
if (text.length > 20e6) {
this.clusterize.update(["File is large - gcode view disabled"]);
} else {
const lines = escapeHTML(text.trimRight())
.split(/[\r\n]/)
.map((line, i) => `<li class="ln${i + 1}" contenteditable="true"><b>${i + 1}</b>${line}</li>`);
this.clusterize.update(lines);
}
this.empty = false;
Vue.nextTick(this.update_line);
},
clear: function () {
this.empty = true;
this.file = "";
this.line = -1;
this.clusterize.clear();
},
reload: function (file) {
if (file != this.file) {
return;
}
this.clear();
this.load(file);
},
highlight: function () {
const highlights = this.$el.querySelectorAll(".highlight");
for (const highlight of highlights) {
highlight.className = (highlight.className || "")
.split(" ")
.filter(c => c !== "highlight")
.join(" ");
}
const lines = this.$el.querySelectorAll(`.ln${this.line}`);
for (const line of lines) {
line.className = (line.className || "")
.split(" ")
.filter(c => c !== "highlight")
.concat(["highlight"])
.join(" ");
}
},
update_line: function (line) {
if (typeof line != "undefined") {
if (this.line == line) {
return;
}
this.line = line;
} else {
line = this.line;
}
const totalLines = this.clusterize.getRowsAmount();
if (line <= 0) {
line = 1;
}
if (totalLines < line) {
line = totalLines;
}
const scroll = this.$el.querySelector(".clusterize-scroll");
const lineHeight = scroll.scrollHeight / totalLines;
const linesPerPage = Math.floor(scroll.clientHeight / lineHeight);
const target = line - 1 - Math.floor(linesPerPage / 2);
// Update scroll position
scroll.scrollTop = target * lineHeight;
Vue.nextTick(this.highlight);
},
},
};