summaryrefslogtreecommitdiff
path: root/assets/js/components/text-editor.js
blob: 625ade0d31a8d3ee79a7ab5fe66a8f6041575716 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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);
        }
    }
});