aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editor/editor.d.ts27
-rw-r--r--editor/editor.js152
-rw-r--r--editor/editor.ts180
-rw-r--r--editor/example.html104
4 files changed, 463 insertions, 0 deletions
diff --git a/editor/editor.d.ts b/editor/editor.d.ts
new file mode 100644
index 0000000..ff1c307
--- /dev/null
+++ b/editor/editor.d.ts
@@ -0,0 +1,27 @@
+import { IVfsNode, Response } from "../vfs/vfs";
+export interface IEditor {
+ id: string;
+ is_editable: boolean;
+ OpenFile(path: string): Response;
+ SaveFile(node: IVfsNode): Response;
+}
+export declare class Editor implements IEditor {
+ opts: IEditor;
+ id: string;
+ is_editable: boolean;
+ private el;
+ private activeFile;
+ private activeText;
+ private rangeBegin;
+ private rangeEnd;
+ private lines;
+ constructor(opts: IEditor);
+ OpenFile(path: string): Response;
+ SaveFile(node: IVfsNode): Response;
+ insertNewline(x: number): void;
+ onClickText(text: HTMLElement): void;
+ onKeydownText(x: number, text: HTMLElement, ev: KeyboardEvent): boolean;
+ onMouseDownAtLine(x: number): void;
+ onMouseUpAtLine(x: number): void;
+ render(): void;
+}
diff --git a/editor/editor.js b/editor/editor.js
new file mode 100644
index 0000000..540b42c
--- /dev/null
+++ b/editor/editor.js
@@ -0,0 +1,152 @@
+"use strict";
+exports.__esModule = true;
+exports.Editor = void 0;
+var Editor = /** @class */ (function () {
+ function Editor(opts) {
+ this.opts = opts;
+ this.activeFile = null;
+ this.activeText = null;
+ this.rangeBegin = 0;
+ this.rangeEnd = 0;
+ this.lines = [];
+ this.id = opts.id;
+ this.is_editable = opts.is_editable;
+ this.el = document.getElementById(opts.id);
+ if (!this.el) {
+ console.error("Editor: element ID not found:", opts.id);
+ return;
+ }
+ this.el.classList.add("wui-editor");
+ }
+ Editor.prototype.OpenFile = function (path) {
+ var res = {
+ code: 500
+ };
+ if (!this.el) {
+ return res;
+ }
+ res = this.opts.OpenFile(path);
+ if (res.code != 200) {
+ return res;
+ }
+ if (!res.data) {
+ return res;
+ }
+ this.activeFile = res.data;
+ var content = this.activeFile.content;
+ content = content.replace("\r\n", "\n");
+ var lines = content.split("\n");
+ for (var x = 0; x < lines.length; x++) {
+ var line = new EditorLine(x, lines[x], this);
+ this.lines.push(line);
+ }
+ this.render();
+ return res;
+ };
+ Editor.prototype.SaveFile = function (node) {
+ var res = this.opts.SaveFile(node);
+ return res;
+ };
+ Editor.prototype.insertNewline = function (x) {
+ console.log("enter new line:", x);
+ var newline = new EditorLine(x, "", this);
+ for (var y = x; y < this.lines.length; y++) {
+ this.lines[y].setNumber(y + 2);
+ }
+ this.lines.splice(x, 0, newline);
+ this.render();
+ };
+ Editor.prototype.onClickText = function (text) {
+ if (this.activeText) {
+ this.activeText.contentEditable = "false";
+ this.activeText.style.borderWidth = "0";
+ }
+ text.contentEditable = "true";
+ text.style.borderWidth = "2px";
+ this.activeText = text;
+ text.focus();
+ };
+ Editor.prototype.onKeydownText = function (x, text, ev) {
+ if (ev.key === "Escape") {
+ text.contentEditable = "false";
+ text.style.borderWidth = "0";
+ this.activeText = null;
+ return false;
+ }
+ if (ev.key === "Enter") {
+ this.insertNewline(x + 1);
+ ev.preventDefault();
+ return false;
+ }
+ return true;
+ };
+ Editor.prototype.onMouseDownAtLine = function (x) {
+ this.rangeBegin = x;
+ };
+ Editor.prototype.onMouseUpAtLine = function (x) {
+ this.rangeEnd = x;
+ console.log("range: ", this.rangeBegin, " - ", this.rangeEnd);
+ if (this.rangeEnd < this.rangeBegin) {
+ return;
+ }
+ if (!this.el) {
+ return;
+ }
+ var y = 0;
+ for (; y < this.rangeBegin; y++) {
+ this.el.children[y].setAttribute("style", "");
+ }
+ for (; y <= this.rangeEnd; y++) {
+ this.el.children[y].setAttribute("style", "background-color:lightsalmon");
+ }
+ for (; y < this.el.children.length; y++) {
+ this.el.children[y].setAttribute("style", "");
+ }
+ };
+ Editor.prototype.render = function () {
+ if (!this.el) {
+ return;
+ }
+ this.el.innerHTML = "";
+ for (var _i = 0, _a = this.lines; _i < _a.length; _i++) {
+ var line = _a[_i];
+ this.el.appendChild(line.el);
+ }
+ };
+ return Editor;
+}());
+exports.Editor = Editor;
+var EditorLine = /** @class */ (function () {
+ function EditorLine(x, text, ed) {
+ var _this = this;
+ this.x = x;
+ this.text = text;
+ this.el = document.createElement("div");
+ this.el.classList.add("wui-editor-line");
+ this.elNumber = document.createElement("span");
+ this.elNumber.classList.add("wui-line-number");
+ this.elNumber.innerText = x + 1 + "";
+ this.elNumber.onmousedown = function (ev) {
+ ed.onMouseDownAtLine(x);
+ };
+ this.elNumber.onmouseup = function (ev) {
+ ed.onMouseUpAtLine(x);
+ };
+ this.elText = document.createElement("span");
+ this.elText.classList.add("wui-line-text");
+ this.elText.innerText = text;
+ this.elText.onclick = function (ev) {
+ ed.onClickText(_this.elText);
+ };
+ this.elText.onkeydown = function (ev) {
+ return ed.onKeydownText(x, _this.elText, ev);
+ };
+ this.el.appendChild(this.elNumber);
+ this.el.appendChild(this.elText);
+ }
+ EditorLine.prototype.setNumber = function (x) {
+ this.elNumber.innerText = x + "";
+ };
+ return EditorLine;
+}());
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWRpdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZWRpdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQVVBO0lBVUMsZ0JBQW1CLElBQWE7UUFBYixTQUFJLEdBQUosSUFBSSxDQUFTO1FBTnhCLGVBQVUsR0FBb0IsSUFBSSxDQUFBO1FBQ2xDLGVBQVUsR0FBdUIsSUFBSSxDQUFBO1FBQ3JDLGVBQVUsR0FBVyxDQUFDLENBQUE7UUFDdEIsYUFBUSxHQUFXLENBQUMsQ0FBQTtRQUNwQixVQUFLLEdBQWlCLEVBQUUsQ0FBQTtRQUcvQixJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLENBQUE7UUFDakIsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFBO1FBRW5DLElBQUksQ0FBQyxFQUFFLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDMUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUU7WUFDYixPQUFPLENBQUMsS0FBSyxDQUFDLCtCQUErQixFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQTtZQUN2RCxPQUFNO1NBQ047UUFDRCxJQUFJLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUE7SUFDcEMsQ0FBQztJQUVELHlCQUFRLEdBQVIsVUFBUyxJQUFZO1FBQ3BCLElBQUksR0FBRyxHQUFhO1lBQ25CLElBQUksRUFBRSxHQUFHO1NBQ1QsQ0FBQTtRQUNELElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFO1lBQ2IsT0FBTyxHQUFHLENBQUE7U0FDVjtRQUVELEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQTtRQUM5QixJQUFJLEdBQUcsQ0FBQyxJQUFJLElBQUksR0FBRyxFQUFFO1lBQ3BCLE9BQU8sR0FBRyxDQUFBO1NBQ1Y7UUFDRCxJQUFJ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 \ No newline at end of file
diff --git a/editor/editor.ts b/editor/editor.ts
new file mode 100644
index 0000000..f021db0
--- /dev/null
+++ b/editor/editor.ts
@@ -0,0 +1,180 @@
+import { IVfsNode, Response } from "../vfs/vfs"
+
+export interface IEditor {
+ id: string
+ is_editable: boolean
+
+ OpenFile(path: string): Response
+ SaveFile(node: IVfsNode): Response
+}
+
+export class Editor implements IEditor {
+ id: string
+ is_editable: boolean
+ private el: HTMLElement | null
+ private activeFile: IVfsNode | null = null
+ private activeText: HTMLElement | null = null
+ private rangeBegin: number = 0
+ private rangeEnd: number = 0
+ private lines: EditorLine[] = []
+
+ constructor(public opts: IEditor) {
+ this.id = opts.id
+ this.is_editable = opts.is_editable
+
+ this.el = document.getElementById(opts.id)
+ if (!this.el) {
+ console.error("Editor: element ID not found:", opts.id)
+ return
+ }
+ this.el.classList.add("wui-editor")
+ }
+
+ OpenFile(path: string): Response {
+ let res: Response = {
+ code: 500,
+ }
+ if (!this.el) {
+ return res
+ }
+
+ res = this.opts.OpenFile(path)
+ if (res.code != 200) {
+ return res
+ }
+ if (!res.data) {
+ return res
+ }
+
+ this.activeFile = res.data as IVfsNode
+
+ let content = this.activeFile.content as string
+ content = content.replace("\r\n", "\n")
+ let lines = content.split("\n")
+
+ for (let x = 0; x < lines.length; x++) {
+ let line = new EditorLine(x, lines[x], this)
+ this.lines.push(line)
+ }
+
+ this.render()
+
+ return res
+ }
+
+ SaveFile(node: IVfsNode): Response {
+ let res = this.opts.SaveFile(node)
+ return res
+ }
+
+ insertNewline(x: number) {
+ console.log("enter new line:", x)
+ let newline = new EditorLine(x, "", this)
+ for (let y = x; y < this.lines.length; y++) {
+ this.lines[y].setNumber(y + 2)
+ }
+ this.lines.splice(x, 0, newline)
+ this.render()
+ }
+
+ onClickText(text: HTMLElement) {
+ if (this.activeText) {
+ this.activeText.contentEditable = "false"
+ this.activeText.style.borderWidth = "0"
+ }
+ text.contentEditable = "true"
+ text.style.borderWidth = "2px"
+ this.activeText = text
+ text.focus()
+ }
+
+ onKeydownText(x: number, text: HTMLElement, ev: KeyboardEvent) {
+ if (ev.key === "Escape") {
+ text.contentEditable = "false"
+ text.style.borderWidth = "0"
+ this.activeText = null
+ return false
+ }
+ if (ev.key === "Enter") {
+ this.insertNewline(x + 1)
+ ev.preventDefault()
+ return false
+ }
+ return true
+ }
+
+ onMouseDownAtLine(x: number) {
+ this.rangeBegin = x
+ }
+
+ onMouseUpAtLine(x: number) {
+ this.rangeEnd = x
+ console.log("range: ", this.rangeBegin, " - ", this.rangeEnd)
+ if (this.rangeEnd < this.rangeBegin) {
+ return
+ }
+ if (!this.el) {
+ return
+ }
+ let y = 0
+ for (; y < this.rangeBegin; y++) {
+ this.el.children[y].setAttribute("style", "")
+ }
+ for (; y <= this.rangeEnd; y++) {
+ this.el.children[y].setAttribute("style", "background-color:lightsalmon")
+ }
+ for (; y < this.el.children.length; y++) {
+ this.el.children[y].setAttribute("style", "")
+ }
+ }
+
+ render() {
+ if (!this.el) {
+ return
+ }
+ this.el.innerHTML = ""
+ for (const line of this.lines) {
+ this.el.appendChild(line.el)
+ }
+ }
+}
+
+class EditorLine {
+ el!: HTMLElement
+ elNumber!: HTMLElement
+ elText!: HTMLElement
+
+ constructor(public x: number, public text: string, ed: Editor) {
+ this.el = document.createElement("div")
+ this.el.classList.add("wui-editor-line")
+
+ this.elNumber = document.createElement("span")
+ this.elNumber.classList.add("wui-line-number")
+ this.elNumber.innerText = x + 1 + ""
+
+ this.elNumber.onmousedown = (ev: MouseEvent) => {
+ ed.onMouseDownAtLine(x)
+ }
+ this.elNumber.onmouseup = (ev: MouseEvent) => {
+ ed.onMouseUpAtLine(x)
+ }
+
+ this.elText = document.createElement("span")
+ this.elText.classList.add("wui-line-text")
+ this.elText.innerText = text
+
+ this.elText.onclick = (ev: MouseEvent) => {
+ ed.onClickText(this.elText)
+ }
+ this.elText.onkeydown = (ev: KeyboardEvent) => {
+ return ed.onKeydownText(x, this.elText, ev)
+ }
+
+ this.el.appendChild(this.elNumber)
+ this.el.appendChild(this.elText)
+ }
+
+ setNumber(x: number) {
+ this.elNumber.innerText = x + ""
+ }
+}
diff --git a/editor/example.html b/editor/example.html
new file mode 100644
index 0000000..ac45e60
--- /dev/null
+++ b/editor/example.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8" />
+ <title>WUI - editor</title>
+ <style>
+ [contenteditable] {
+ outline: 0px solid transparent;
+ }
+ .wui-editor {
+ background-color: cornsilk;
+ font-family: monospace;
+ width: 100%;
+ }
+ .wui-editor-line {
+ display: table;
+ }
+ .wui-line-number:hover {
+ background-color: lightsalmon;
+ }
+ .wui-line-number {
+ display: table-cell;
+ padding: 4px 1em 4px 4px;
+ text-align: right;
+ width: 3em;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+ .wui-line-text {
+ display: table-cell;
+ padding: 4px;
+ border-color: lightblue;
+ border-width: 0px;
+ border-style: solid;
+ }
+ </style>
+ </head>
+ <body onload="main()">
+ <div id="editor"></div>
+
+ <script>
+ var exports = {}
+ </script>
+ <script src="editor.js"></script>
+ <script>
+ function main() {
+ let opts = {
+ id: "editor",
+ is_editable: true,
+ OpenFile: doOpenFile,
+ }
+
+ let editor = new Editor(opts)
+
+ editor.OpenFile("/test.aww")
+ }
+
+ function doOpenFile(path) {
+ let res = {
+ code: 200,
+ data: {
+ name: "Test",
+ path: path,
+ content: `mkdir -p \${HOME}/aur/stackdriver-collectd
+
+git -C \${HOME}/aur/stackdriver-collectd clone \
+ ssh://aur@aur.archlinux.org/stackdriver-collectd.git .
+
+sh -c "cd \${HOME}/aur/stackdriver-collectd; \
+ makepkg --force --install --noconfirm"
+pacman -Ql stackdriver-collectd
+
+sudo systemctl enable stackdriver-collectd
+
+#put! {{.BaseDir}}/_template/etc/collectd-influxdb.conf /opt/collectd/etc/collectd.conf
+
+sudo systemctl restart stackdriver-collectd
+sudo systemctl status stackdriver-collectd
+
+##---- Connect telegraf with collectd
+
+{{.Val "influxdb::dir"}}/bin/influx bucket create \
+ --name stackdriver_collectd \
+ --description "stackdriver collectd" \
+ --org {{.Val "influxdb::org"}} \
+ --token {{.Val "influxdb:telegraf:token"}} \
+ --retention "3d"
+
+#put: {{.BaseDir}}/_template/etc/telegraf/telegraf.d/stackdriver-collectd.conf \
+ {{.Val "influxdb::dir"}}/etc/telegraf/telegraf.d/stackdriver-collectd.conf
+
+sudo systemctl restart telegraf
+sudo systemctl status telegraf
+`,
+ },
+ }
+ return res
+ }
+ </script>
+ </body>
+</html>