Text Editor
<template> <div> <h1>Text Editor</h1> <div id="editor_div"></div> <hr /> <h3>Log</h3> <textarea id="log" v-model="data" readonly></textarea> </div> </template> <script> const defaultValue = {"time":1660845097559,"blocks":[{"id":"4XsJ0L41Oe","type":"paragraph","data":{"text":"<b>Item #:</b> SCP-XXXX"}},{"id":"oW1V8YHHgh","type":"blank","data":{"text":"\n"}},{"id":"IBps68PdFt","type":"paragraph","data":{"text":"<b>Object Class:</b> Euclid"}},{"id":"I9ybKs9jup","type":"blank","data":{"text":"\n"}},{"id":"Fq8942G970","type":"paragraph","data":{"text":"<b>Special Containment Procedures:</b> S.C.P!"}},{"id":"YDWOosImbs","type":"collapsible","data":{"open":"unfold","close":"fold","content":"collapsible block"}},{"id":"nm_f1NbQsP","type":"blank","data":{"text":"\n"}},{"id":"YYt89dfXiR","type":"paragraph","data":{"text":"<b>Description:</b> text <i>text</i>."}},{"id":"4Ein2tgKle","type":"blank","data":{"text":"\n"}},{"id":"l2y6JVuIh7","type":"paragraph","data":{"text":"aaaa"}},{"id":"5Kxm1zGB6j","type":"blank","data":{"text":"\n"}},{"id":"9mEDd8cVnc","type":"paragraph","data":{"text":"bbbb"}},{"id":"aa4690OjUO","type":"blank","data":{"text":"\n"}},{"id":"Mt_41nsvob","type":"paragraph","data":{"text":"cccc"}}],"version":"2.25.0"}; module.exports = { data: function() { return { is_EditorJS_loading: false, editor: null, data: '' } }, created: function() { this.$root.$on('loading_EditorJS', e => { this.is_EditorJS_loading = true }) }, methods: { load_EditorJS: function() { const self = this; return new Promise((resolve, reject) => { if(self.is_script_loading){ this.$root.$on('EditorJS_loaded', resolve); return; } const script = document.createElement('script'); script.setAttribute('src', 'https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest'); script.async = true; this.$root.$emit('loading_EditorJS'); script.onload = () => { this.$root.$emit('EditorJS_loaded'); resolve(); }; document.head.appendChild(script); }) }, use_EditorJS: async function() { try { await this.load_EditorJS(); if(this.editor) { this.editor.destroy(); } const Collapsible = class { static get toolbox() { return { title: 'Collapsible', icon: '<i class="fa fa-terminal"></i>' }; } constructor({ data }) { this.data = data; } render() { const div = document.createElement('div'); div.classList.add('collapsible'); const _open = document.createElement('span'); const open = document.createElement('input'); _open.classList.add('fa', 'fa-plus'); _open.appendChild(open); open.value = this.data.open || 'unfold'; const _close = document.createElement('span'); const close = document.createElement('input'); _close.classList.add('fa', 'fa-minus'); _close.appendChild(close); close.value = this.data.close || 'fold'; const content = document.createElement('textarea'); content.value = this.data.content || ''; div.appendChild(_open); div.appendChild(_close); div.appendChild(content); return div; } save(data) { const [open, close] = [...data.querySelectorAll('input')].map(e => e.value); const content = data.querySelector('textarea').value; return { open, close, content }; } }; const Blank = class { static get toolbox() { return { title: 'Blank Line', icon: '<i class="fa fa-level-down"></i>' }; } constructor({ data }) { this.data = data; } render() { const div = document.createElement('div'); div.style.textAlign = 'center'; div.innerHTML = '<i class="fa fa-level-down"></i>'; return div; } save(data) { return { text: '\n' }; } }; const log = localStorage.getItem('TextEditorLog'); this.editor = new EditorJS({ holder: 'editor_div', onReady: () => { this.data = log || JSON.stringify(defaultValue); }, onChange: () => this.save(), tools: { blank: Blank, collapsible: Collapsible }, data: log ? JSON.parse(log) : defaultValue }); } catch(e) { console.log(e) } }, save: async function() { if(!this.editor) return; const data = await this.editor.save(); this.data = JSON.stringify(data); localStorage.setItem('TextEditorLog', this.data); } }, mounted: async function() { document.title = '7happy7\'s Warehouse - Text Editor'; await this.use_EditorJS(); } } </script> <style scoped> #editor_div { box-shadow: 1px 1px 3px #aaa; margin: 0 auto; max-width: 800px; padding: 2em 0; position: relative; text-align: left; width: 80%; } #log { border: 1px solid #aaa; border-radius: 5px; font-family: 'Ubuntu Mono', monospace; font-size: .8em; height: 250px; max-width: 800px; resize: vertical; width: 80%; } #log:focus { outline: none; } .collapsible { box-shadow: 1px 1px 3px #aaa; display: flex; flex-wrap: wrap; padding: 0.5em; } .collapsible > .fa { align-items: center; color: #aaa; display: inline-flex; padding: 0.5em; vertical-align: middle; justify-content: center; } .collapsible > .fa::before { padding: 0 0.5em; } .collapsible > .fa > input { border: none; box-shadow: 1px 1px 3px #aaa; padding: 0.35em 0; text-align: center; } .collapsible > .fa > input:focus { outline: none; } .collapsible > textarea { border: none; box-shadow: 1px 1px 3px #aaa; flex-grow: 1; resize: vertical; width: 100%; } .collapsible > textarea:focus { outline: none; } </style>
// note const HTML2Wiki = str => { const dom = new DOMParser().parseFromString(str, 'text/html'); const _fn = (...nodes) => { return nodes.map(node => { const format = { 'B': t => `**${t}**`, 'STRONG': t => `**${t}**`, 'I': t => `//${t}//`, '#text': t => node.nodeValue }[node.nodeName] || (t => t); return format(_fn(...node.childNodes)); }).join(''); }; return _fn(...dom.body.childNodes); }; HTML2Wiki('<strong>bbbb<i>bibi</i></strong>'); // -> **bbbb//bibi//**
page revision: 26, last edited: 18 Aug 2022 17:56






