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//**
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License