./static/js/editors/textarea.js annotated source

Back to index

        

This is a reference implementation of the EditorCore interface that allows any third-party editor to be plugged into Codeframe as the code editor of choice. TextAreaEditor is backed by a simple <textarea> element.

4class TextAreaEditor {
5
6    constructor(callback) {
7        this.mode = 'html';
8

When the file contents are not being edited, they are cached in this.frames.

11        this.frames = {
12            html: '',
13            javascript: '',
14        }
15
16        this.container = document.createElement('textarea');
17        this.container.classList.add('editorContainer');
18

Attach some basic event handlers to allow correct indentation with tabs (4 spaces, for consistency).

21        this.container.addEventListener('keydown', evt => {
22            if (evt.key === 'Tab') {

By default, the tab key will lead the user to go the next element in the tab-index order.

25                evt.preventDefault();
26
27                const tgt = evt.target;
28                if (evt.shiftKey) {
29                    const idx = tgt.selectionStart;
30                    if (idx !== null) {
31                        let front = tgt.value.substr(0, idx);
32                        const back = tgt.value.substr(idx);
33                        let diff = 0;

If dedenting, remove any whitespace up to 4 spaces in front of the cursor.

36                        while (front.endsWith(' ') && diff < 4) {
37                            front = front.substr(0, front.length - 1);
38                            tgt.value = front + back;
39                            diff ++;
40                        }

Rendering the new input value will make us lose focus on the textarea, so we put the focus back by selecting the area the user was just editing.

44                        tgt.setSelectionRange(idx - diff, idx - diff);
45                    }
46                } else {
47                    const idx = tgt.selectionStart;
48                    if (idx !== null) {
49                        const front = tgt.value.substr(0, idx);
50                        const back = tgt.value.substr(idx);

If indenting, just add 4 spaces at the position of the cursor.

52                        tgt.value = front + '    ' + back;

Rendering the new input value will make us lose focus on the textarea, so we put the focus back by selecting the area the user was just editing.

56                        tgt.setSelectionRange(idx + 4, idx + 4);
57                    }
58                }
59            }
60        });
61

Set the first file editing mode ('html')

63        this.setMode(this.mode);
64

This is a trick to get around the asynchrony requirement for the callback. (Promise callbacks are executed in the microtask queue, not immediately.) This is required because Codeframe's editor component assumes that this callback is called after the editor is instantiated, so a synchronous callback in the constructor doesn't work.

70        Promise.resolve().then(() => {
71            callback(this);
72        });
73    }
74
75    getValue(mode = this.mode) {
76        if (mode === this.mode) {
77            return this.container.value;
78        } else {
79            return this.frames[mode];
80        }
81    }
82
83    setValue(value, mode = this.mode) {
84        if (mode === this.mode) {
85            this.container.value = value;
86        } else {
87            this.frames[mode] = value;
88        }
89    }
90
91    getMode() {
92        return this.mode;
93    }
94
95    setMode(mode) {

Persist the current editor value to memory first

97        this.frames[this.mode] = this.container.value;
98
99        this.mode = mode;
100        this.container.value = this.frames[this.mode];
101    }
102
103    addChangeHandler(handler) {
104        this.container.addEventListener('input', handler);
105    }
106
107    getContainer() {
108        return this.container;
109    }
110
111    resize() {
112        // no-op
113    }
114
115    ready() {

Since the text area editor is synchronously initialized, it is always "ready" to be used.

118        return true;
119    }
120
121}
122