From d1e96e09438b4a5c7580b86c469e817a61be991f Mon Sep 17 00:00:00 2001 From: Shulhan Date: Sun, 15 Sep 2024 14:32:48 +0700 Subject: all: commit all generate JavaScript files This is to simplify development on third party where they can clone and include the file directly without installing or running anything to build the files. --- editor/editor.js | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 editor/editor.js (limited to 'editor/editor.js') diff --git a/editor/editor.js b/editor/editor.js new file mode 100644 index 0000000..e80e147 --- /dev/null +++ b/editor/editor.js @@ -0,0 +1,205 @@ +// SPDX-FileCopyrightText: 2021 M. Shulhan +// SPDX-License-Identifier: GPL-3.0-or-later +const WUI_EDITOR_CLASS = "wui_editor"; +const WUI_EDITOR_CLASS_LINE_NUMBER = "wui_editor_line_number"; +const WUI_EDITOR_CLASS_CONTENT = "wui_editor_content"; +export class WuiEditor { + constructor(opts) { + this.opts = opts; + this.content = ""; + this.totalLine = 0; + this.elLineNumber = document.createElement("div"); + this.elContent = document.createElement("div"); + this.isKeyControl = false; + this.id = opts.id; + this.isEditable = opts.isEditable; + const el = document.getElementById(opts.id); + if (!el) { + console.error("WuiEditor: element ID not found:", opts.id); + return; + } + this.el = el; + this.initStyle(); + this.initLineNumber(); + this.initContent(); + this.el.classList.add(WUI_EDITOR_CLASS); + } + // getContent return content of file. + getContent() { + let content = ""; + let el; + let line; + this.elContent.childNodes.forEach((node) => { + switch (node.nodeType) { + case Node.ELEMENT_NODE: + el = node; + line = el.innerText; + break; + case Node.TEXT_NODE: + line = node.nodeValue || ""; + break; + } + line = line.trimEnd(); + content += line + "\n"; + }); + content = content.trim(); + return content; + } + // open the node for editing. + // The content MUST be encoded in base64. + open(node) { + this.content = atob(node.content); + this.content = this.content.replace("\r\n", "\n"); + this.render(this.content); + } + setEditable(yes) { + this.isEditable = yes; + if (yes) { + this.elContent.setAttribute("contenteditable", "true"); + } + else { + this.elContent.setAttribute("contenteditable", "false"); + } + } + addNewLine() { + this.totalLine++; + const elLine = document.createElement("div"); + elLine.innerText = `${this.totalLine}`; + this.elLineNumber.appendChild(elLine); + } + initLineNumber() { + this.elLineNumber.classList.add(WUI_EDITOR_CLASS_LINE_NUMBER); + this.el.appendChild(this.elLineNumber); + } + initContent() { + if (this.opts.isEditable) { + this.elContent.setAttribute("contenteditable", "true"); + this.elContent.setAttribute("spellcheck", "false"); + this.elContent.addEventListener("paste", (ev) => { + var _a; + ev.preventDefault(); + let text = ((_a = ev.clipboardData) === null || _a === void 0 ? void 0 : _a.getData("text/plain")) || ""; + if (!text) { + console.error(`on paste: text is ${text}`); + return; + } + const selection = window.getSelection(); + if (!selection || !selection.rangeCount) { + console.error(`on paste: failed to get selection`); + return; + } + text = text.trimEnd(); + selection.deleteFromDocument(); + selection.getRangeAt(0).insertNode(document.createTextNode(text)); + selection.collapseToEnd(); + this.renderLineNumber(this.getContent()); + }); + this.elContent.onkeydown = (ev) => { + this.onKeydownDocument(this, ev); + }; + this.elContent.onkeyup = (ev) => { + this.onKeyupDocument(this, ev); + }; + this.elContent.addEventListener("blur", () => { + this.isKeyControl = false; + }); + } + this.elContent.classList.add(WUI_EDITOR_CLASS_CONTENT); + this.el.appendChild(this.elContent); + } + initStyle() { + const style = document.createElement("style"); + style.type = "text/css"; + style.innerText = ` + [contenteditable] { + outline: 0px solid transparent; + } + .${WUI_EDITOR_CLASS} { + background-color: cornsilk; + border: 1px solid brown; + font-family: monospace; + line-height: 1.6em; + overflow-y: scroll; + width: 100%; + } + .${WUI_EDITOR_CLASS_LINE_NUMBER} { + background-color: bisque; + border-right: 1px dashed brown; + color: dimgrey; + display: inline-block; + font-family: monospace; + margin-right: 4px; + padding: 0px 8px; + position: sticky; + text-align: right; + width: 3em; + } + .${WUI_EDITOR_CLASS_CONTENT} { + caret-color: red; + display: inline-block; + padding: 0px 8px 0 0; + vertical-align: top; + white-space: pre; + width: calc(100% - 10em); + word-wrap: normal; + } + `; + document.head.appendChild(style); + } + onKeydownDocument(ed, ev) { + switch (ev.key) { + case "Control": + ed.isKeyControl = true; + break; + case "Enter": + if (ed.isKeyControl) { + ev.preventDefault(); + ev.stopPropagation(); + if (ed.opts.onSave) { + const content = ed.getContent(); + ed.opts.onSave(content); + ed.render(content); + } + return false; + } + this.addNewLine(); + return false; + } + return true; + } + onKeyupDocument(ed, ev) { + switch (ev.key) { + case "Control": + ed.isKeyControl = false; + return true; + } + return true; + } + render(content) { + const lines = content.split("\n"); + this.elContent.innerText = ""; + this.elLineNumber.innerText = ""; + lines.forEach((line, x) => { + const el = document.createElement("div"); + el.innerText = `${x + 1}`; + this.elLineNumber.appendChild(el); + const div = document.createElement("div"); + div.innerText = line; + if (line == "") { + div.appendChild(document.createElement("br")); + } + this.elContent.appendChild(div); + }); + this.totalLine = lines.length; + } + renderLineNumber(content) { + const lines = content.split("\n"); + this.elLineNumber.innerText = ""; + lines.forEach((_, x) => { + const el = document.createElement("div"); + el.innerText = `${x + 1}`; + this.elLineNumber.appendChild(el); + }); + this.totalLine = lines.length; + } +} -- cgit v1.3