diff options
author | Frederic Guillot <fred@kanboard.net> | 2016-11-13 22:51:59 -0500 |
---|---|---|
committer | Frederic Guillot <fred@kanboard.net> | 2016-11-13 22:51:59 -0500 |
commit | ebb6b2827d0496303852f524a551592295dd0040 (patch) | |
tree | a3e6dff7036ba83e436941b0b0e96c89311eb0f8 /assets/js/components | |
parent | 527a1677a0578e479302e1f80b127e177ed915e9 (diff) |
Replace SimpleMDE with custom Markdown editor
Diffstat (limited to 'assets/js/components')
-rw-r--r-- | assets/js/components/text-editor.js | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/assets/js/components/text-editor.js b/assets/js/components/text-editor.js new file mode 100644 index 00000000..625ade0d --- /dev/null +++ b/assets/js/components/text-editor.js @@ -0,0 +1,158 @@ +Vue.component('texteditor', { + props: ['text', 'name', 'labelPreview', 'labelWrite', 'placeholder', 'css', 'tabindex', 'required', 'autofocus'], + template: + '<div class="text-editor">' + + '<div class="text-editor-toolbar">' + + '<button v-if="!preview" v-on:click.prevent="togglePreview"><i class="fa fa-fw fa-eye"></i>{{ labelPreview }}</button>' + + '<button v-if="preview" v-on:click.prevent="toggleEditor"><i class="fa fa-fw fa-pencil-square-o"></i>{{ labelWrite }}</button>' + + '<button :disabled="isPreview" v-on:click.prevent="insertBoldTag"><i class="fa fa-bold fa-fw"></i></button>' + + '<button :disabled="isPreview" v-on:click.prevent="insertItalicTag"><i class="fa fa-italic fa-fw"></i></button>' + + '<button :disabled="isPreview" v-on:click.prevent="insertStrikethroughTag"><i class="fa fa-strikethrough fa-fw"></i></button>' + + '<button :disabled="isPreview" v-on:click.prevent="insertQuoteTag"><i class="fa fa-quote-right fa-fw"></i></button>' + + '<button :disabled="isPreview" v-on:click.prevent="insertBulletListTag"><i class="fa fa-list-ul fa-fw"></i></button>' + + '<button :disabled="isPreview" v-on:click.prevent="insertCodeTag"><i class="fa fa-code fa-fw"></i></button>' + + '</div>' + + '<div v-show="!preview" class="text-editor-write-area">' + + '<textarea ' + + 'v-model="text" ' + + 'name="{{ name }}" ' + + 'id="{{ getId }}" ' + + 'class="{{ css }}" ' + + 'tabindex="{{ tabindex }}" ' + + ':autofocus="hasAutofocus" ' + + 'placeholder="{{ placeholder }}" ' + + '></textarea>' + + '</div>' + + '<div v-show="preview" class="text-editor-preview-area markdown">{{{ renderedText }}}</div>' + + '</div>' + , + data: function() { + return { + id: null, + preview: false, + renderedText: '', + textarea: null, + selectionStart: 0, + selectionEnd: 0 + }; + }, + ready: function() { + this.textarea = document.getElementById(this.id); + }, + computed: { + hasAutofocus: function() { + return this.autofocus === '1'; + }, + isPreview: function() { + return this.preview; + }, + getId: function() { + if (! this.id) { + var i = 0; + var uniqueId; + + while (true) { + i++; + uniqueId = 'text-editor-textarea-' + i; + + if (! document.getElementById(uniqueId)) { + break; + } + } + + this.id = uniqueId; + } + + return this.id; + } + }, + methods: { + toggleEditor: function() { + this.preview = false; + }, + togglePreview: function() { + this.preview = true; + this.renderedText = marked(this.text, {sanitize: true}); + }, + insertBoldTag: function() { + this.insertEnclosedTag('**'); + }, + insertItalicTag: function() { + this.insertEnclosedTag('_'); + }, + insertStrikethroughTag: function() { + this.insertEnclosedTag('~~'); + }, + insertQuoteTag: function() { + this.insertPrependTag('> '); + }, + insertBulletListTag: function() { + this.insertPrependTag('* '); + }, + insertCodeTag: function() { + this.insertBlockTag('```'); + }, + replaceTextRange: function(s, start, end, substitute) { + return s.substring(0, start) + substitute + s.substring(end); + }, + getSelectedText: function() { + return this.text.substring(this.textarea.selectionStart, this.textarea.selectionEnd); + }, + insertEnclosedTag: function(tag) { + var selectedText = this.getSelectedText(); + + this.insertText(tag + selectedText + tag); + this.setCursorBeforeClosingTag(tag); + }, + insertPrependTag: function(tag) { + var selectedText = this.getSelectedText(); + + if (selectedText.indexOf('\n') === -1) { + this.insertText('\n' + tag + selectedText); + } else { + var lines = selectedText.split('\n'); + + for (var i = 0; i < lines.length; i++) { + if (lines[i].indexOf(tag) === -1) { + lines[i] = tag + lines[i]; + } + } + + this.insertText(lines.join('\n')); + } + }, + insertBlockTag: function(tag) { + var selectedText = this.getSelectedText(); + + this.insertText('\n' + tag + '\n' + selectedText + '\n' + tag); + this.setCursorBeforeClosingTag(tag, 2); + }, + insertText: function(replacedText) { + var result = false; + + this.selectionStart = this.textarea.selectionStart; + this.selectionEnd = this.textarea.selectionEnd; + this.textarea.focus(); + + if (document.queryCommandSupported('insertText')) { + result = document.execCommand('insertText', false, replacedText); + } + + if (! result) { + try { + document.execCommand("ms-beginUndoUnit"); + } catch (error) {} + + this.textarea.value = this.replaceTextRange(this.text, this.textarea.selectionStart, this.textarea.selectionEnd, replacedText); + + try { + document.execCommand("ms-endUndoUnit"); + } catch (error) {} + } + }, + setCursorBeforeClosingTag: function(tag, offset) { + var position = this.selectionEnd + tag.length + offset; + this.textarea.setSelectionRange(position, position); + } + } +}); |