UNPKG

@anywhichway/nerd-editor

Version:

A JavaScript rich text editor based on and with support for custom elements.

332 lines (329 loc) 12 kB
const controls = { file: { type: "select", innerHTML: [ '<option disabled selected default hidden>File</option>', '<option value=open">Open</option>', '<option value="preview">Preview</option>', '<option value="save">Save</option>', ].join(""), title: "File options", onclick({path}) { const select = path[0], f = this.editor[select.value], done = () => { select.style.height = ""; select.removeEventListener("blur", done); select.value = "File"; } select.style.height = "0px"; select.addEventListener("blur", done); if (typeof (f) === "function") { f.call(this.editor); done(); } } }, edit: { type: "select", innerHTML: [ '<option disabled selected default hidden>Edit</option>', '<option value="undo">Undo</option>', '<option value="redo">Redo</option>', '<option value="find">Find</option>', '<option value="replace">Replace</option>', ].join(""), title: "Edit options", onclick({path}) { const select = path[0], f = this.editor[select.value], done = () => { select.style.height = ""; select.removeEventListener("blur", done); select.value = "Edit"; } select.style.height = "0px"; select.addEventListener("blur", done); if (typeof (f) === "function") { f.call(this.editor); done(); } } }, heading: { type: "select", innerHTML: [ "<option disabled selected default hidden>Heading</option>", '<option>H1</option>', '<option>H2</option>', '<option>H3</option>', '<option>H4</option>', '<option>H5</option>', '<option>H6</option>', ].join(""), title: "Format Heading", onclick({path}) { const select = path[0], level = parseInt(select.value[1]), done = () => { select.style.height = ""; select.removeEventListener("blur", done); select.value = "Heading"; }; select.style.height = "0px"; select.addEventListener("blur", done); if (!isNaN(level)) { this.editor.wrapSelection(select.value); done(); } } }, i: { innerHTML: '<i>I</i>', title: "Italic", onclick() { this.editor.wrapSelection('i') } }, b: { innerHTML: '<b>B</b>', title: "Bold", onclick() { this.editor.wrapSelection('b') } }, u: { innerHTML: '<u>U</u>', title: "Underline", onclick() { this.editor.wrapSelection('u') } }, del: { innerHTML: '<del>Del</del>', title: "Deleted", onclick() { this.editor.wrapSelection('del') } }, ins: { innerHTML: '<ins>Ins</ins>', title: "Inserted", css: "ins { text-decoration-line: underline; text-decoration-style: wavy; }", onclick() { this.editor.wrapSelection('ins') } }, kbd: { innerHTML: '<kbd>Key</kbd>', icon: '<i class="fa-light fa-keyboard"></i>', title: "Keyboard Key", onclick() { this.editor.wrapSelection('kbd') } }, aside: { innerHTML: 'Aside', icon: '<i class="fa-light fa-sidebar-flip"></i>', title: "Aside", onclick() { this.editor.wrapSelection('aside'); } }, details: { innerHTML: 'Details', icon: 'Details <i class="fa-solid fa-caret-down"></i>', title: "Details", onclick() { this.editor.wrapSelection('details'); const {anchorNode} = window.getSelection(), summary = document.createElement("summary"), title = anchorNode.firstChild.textContent.substring(0,15) + " ..."; summary.innerHTML = title; anchorNode.insertBefore(summary,anchorNode.firstChild); } }, q: { innerHTML: '<q>Quote</q>', icon: '<i class="fa-solid fa-quotes"></i>', title: "Quote", onclick() { this.editor.wrapSelection('q') } }, abbr: { innerHTML: '<abbr>Abrv</abbr>', title: "Abbreviation", css: "abbr { font-style: italic; font-weight: 100;}", onclick() { this.editor.wrapSelection('abbr') } }, samp: { innerHTML: '<samp>Sample</samp', title: "Sample", icon: '<i class="fa-solid fa-laptop-code"></i>', onclick() { this.editor.wrapSelection('samp') } }, blockquote: { innerHTML: '<blockquote>Quote</blockquote>', icon: '<i class="fa-regular fa-block-quote"></i>', title: "Block Quote", onclick() { this.editor.wrapSelection('blockquote') } }, code: { innerHTML: '&lt;/&gt;', title: "Code", onclick() { this.editor.wrapSelection('code') } }, sup: { innerHTML: '<span style="font-size:80%">x<sup>y</sup></span>', title: "Superscript", onclick() { this.editor.wrapSelection('sup') } }, sub: { innerHTML: '<span style="font-size:80%;vertical-align:top;">x<sub>y</sub></span>', title: "Subscript", onclick() { this.editor.wrapSelection('sub') } }, var: { innerHTML: '<var>Var</var>', title: "Variable", onclick() { this.editor.wrapSelection('var') } }, function: { innerHTML: '<i>f(x)</i>', title: "Inline Function", onclick() { const node = document.createElement('inline-function'), expression = document.createElement("expression"); expression.innerHTML = window.getSelection().toString(); node.innerHTML = ` <script src="https://cdn.jsdelivr.net/npm/mathjs@11.1.0/lib/browser/math.min.js"></${"script"}> <script> </${"script"}>`; node.children[1].innerHTML = `(value) => { let result; try { result = Function("math","with(math) { return " + value + "}")(math); } catch(e) { try { result = math.evaluate(value) } catch(e) { return e + ""; } } if(result && result.toString) { return result.toString(); } return result; }`; node.appendChild(expression); this.editor.replaceSelection(node); } }, formula: { innerHTML: '&Sigma;', title: "Math/Science Formula", onclick() { this.editor.wrapSelection('math-science-formula') } }, datablock: { innerHTML: 'Data', title: "Data Block", icon: '<i class="fa-light fa-database"></i>', onclick() { this.editor.wrapSelection('data-block',{asText:true}); } }, music: { innerHTML: 'Music', title: "Sheet Music (ABC Notation)", icon: '<i class="fa-regular fa-music"></i>', onclick() { this.editor.replaceSelection('sheet-music') } }, chart: { innerHTML: 'Chart', title: "Chart", icon: '<i class="fa-regular fa-chart-pie"></i>', onclick() { this.editor.wrapSelection('plotly-chart',{asText:true}) } }, img: { innerHTML: 'Img', title: "Image", icon: '<i class="fa-light fa-image"></i>', onclick() { const img = this.editor.replaceSelection('img'); img.setAttribute("src", "test"); this.editor.edit(img) } }, video: { innerHTML: 'Video', title: "Video File", icon: '<i class="fa-light fa-video"></i>', onclick() { const video = this.editor.replaceSelection('video'); img.setAttribute("src", "test"); this.editor.edit(video) } }, audio: { innerHTML: 'Audio', title: "Audio File", icon: '<i class="fa-light fa-headphones"></i>', onclick() { const audio = this.editor.replaceSelection('audio'); img.setAttribute("src", "test"); this.editor.edit(audio) } }, link: { innerHTML: '<a>Link</a>', title: "Link", icon: '<i class="fa-light fa-link-horizontal"></i>', onclick() { const a = this.editor.replaceSelection('a'); a.setAttribute("href", "test"); this.editor.edit(a) } }, table: { innerHTML: 'Table', title: "Table", icon: '<i class="fa-light fa-table"></i>', onclick() { const table = this.editor.createElement({tagName: 'table', innerHTML: '<tr><td>Test</td></tr>'}); this.editor.replaceSelection(table); this.editor.edit(table); } }, ul: { innerHTML: 'UL', title: "Unordered List", icon: '<i class="fa-light fa-list-ul"></i>', onclick() { this.editor.replaceSelection(this.editor.createElement({tagName: 'ul', innerHTML: '<li>&nbsp;</li>'})); } }, ol: { innerHTML: 'OL', title: "Ordered List", icon: '<i class="fa-light fa-list-ol"></i>', onclick() { this.editor.replaceSelection(this.editor.createElement({tagName: 'ol', innerHTML: '<li>&nbsp;</li>'})); } }, unformat: { innerHTML: 'Unformat', title: "Unformat", icon: '<i class="fa-regular fa-eraser"></i>', onclick() { this.editor.unformatSelection() } }, "editor-dialog": { innerHTML: 'Edit', title: "Edit element", icon: '<i class="fa-regular fa-pencil"></i>', onclick() { this.editor.editSelection() } }, delete: { innerHTML: 'Delete', title: "Delete selection", icon: '<i class="fa-regular fa-trash"></i>', onclick() { this.editor.deleteSelection() } }, undo: { innerHTML: 'Undo', title: "Undo", icon: '<i class="fa-regular fa-rotate-left"></i>', onclick() { this.editor.undo() } }, redo: { innerHTML: 'Redo', title: "Redo", icon: '<i class="fa-regular fa-rotate-right"></i>', onclick() { this.editor.redo() } }, repl: { innerHTML: "&lt;/<blink>_</blink>&gt;", title: "REPL", css: "@keyframes condemned_blink_effect { 0% { visibility: hidden; } 50% { visibility: hidden; } 100% { visibility: visible; } } blink { animation: 2s linear infinite condemned_blink_effect; }", onclick() { this.editor.replaceSelection(this.editor.createElement({ tagName: 'repl-host', attributes: {head: "", css: "", body: "", javascript: "", contenteditable: false} })); } } } export {controls}