2015-04-04 00:50:08 +03:00
|
|
|
/* global CodeMirror */
|
2017-08-22 10:53:26 +03:00
|
|
|
import Component from '@ember/component';
|
2016-06-18 14:44:23 +03:00
|
|
|
import boundOneWay from 'ghost-admin/utils/bound-one-way';
|
2017-08-22 10:53:26 +03:00
|
|
|
import {assign} from '@ember/polyfills';
|
|
|
|
import {bind, once, scheduleOnce} from '@ember/runloop';
|
2017-10-30 12:38:01 +03:00
|
|
|
import {inject as service} from '@ember/service';
|
2018-01-02 21:29:03 +03:00
|
|
|
import {task} from 'ember-concurrency';
|
2015-04-04 00:50:08 +03:00
|
|
|
|
2018-03-20 17:57:59 +03:00
|
|
|
const CmEditorComponent = Component.extend({
|
2018-01-11 20:43:23 +03:00
|
|
|
lazyLoader: service(),
|
|
|
|
|
2017-06-02 00:01:43 +03:00
|
|
|
classNameBindings: ['isFocused:focus'],
|
2015-04-04 00:50:08 +03:00
|
|
|
|
2019-04-30 17:46:29 +03:00
|
|
|
textareaClass: '',
|
2015-10-28 14:36:45 +03:00
|
|
|
isFocused: false,
|
2015-04-04 00:50:08 +03:00
|
|
|
|
|
|
|
// options for the editor
|
2018-04-25 14:06:01 +03:00
|
|
|
autofocus: false,
|
2015-04-04 00:50:08 +03:00
|
|
|
indentUnit: 4,
|
2018-04-25 14:06:01 +03:00
|
|
|
lineNumbers: true,
|
|
|
|
lineWrapping: false,
|
2015-04-04 00:50:08 +03:00
|
|
|
mode: 'htmlmixed',
|
|
|
|
theme: 'xq-light',
|
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
_editor: null, // reference to CodeMirror editor
|
2018-03-20 17:57:59 +03:00
|
|
|
|
|
|
|
// Allowed actions
|
|
|
|
'focus-in': () => {},
|
|
|
|
update: () => {},
|
|
|
|
|
2018-01-11 20:43:23 +03:00
|
|
|
_value: boundOneWay('value'), // make sure a value exists
|
2016-07-05 19:30:14 +03:00
|
|
|
|
2017-08-02 12:32:51 +03:00
|
|
|
didReceiveAttrs() {
|
2021-07-15 17:27:29 +03:00
|
|
|
this._super(...arguments);
|
|
|
|
|
2019-03-06 16:53:54 +03:00
|
|
|
if (this._value === null || undefined) {
|
2019-01-02 12:58:55 +03:00
|
|
|
this.set('_value', '');
|
2017-08-02 12:32:51 +03:00
|
|
|
}
|
2019-04-30 17:46:29 +03:00
|
|
|
|
|
|
|
if (this.mode !== this._lastMode && this._editor) {
|
|
|
|
this._editor.setOption('mode', this.mode);
|
|
|
|
}
|
|
|
|
this._lastMode = this.mode;
|
2017-08-02 12:32:51 +03:00
|
|
|
},
|
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
didInsertElement() {
|
2015-11-15 14:06:49 +03:00
|
|
|
this._super(...arguments);
|
2019-03-06 16:53:54 +03:00
|
|
|
this.initCodeMirror.perform();
|
2018-01-02 21:29:03 +03:00
|
|
|
},
|
|
|
|
|
2018-01-11 20:43:23 +03:00
|
|
|
willDestroyElement() {
|
|
|
|
this._super(...arguments);
|
|
|
|
|
|
|
|
// Ensure the editor exists before trying to destroy it. This fixes
|
|
|
|
// an error that occurs if codemirror hasn't finished loading before
|
|
|
|
// the component is destroyed.
|
|
|
|
if (this._editor) {
|
|
|
|
let editor = this._editor.getWrapperElement();
|
|
|
|
editor.parentNode.removeChild(editor);
|
|
|
|
this._editor = null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2018-01-02 21:29:03 +03:00
|
|
|
actions: {
|
|
|
|
updateFromTextarea(value) {
|
2018-03-20 17:57:59 +03:00
|
|
|
this.update(value);
|
2018-01-02 21:29:03 +03:00
|
|
|
}
|
|
|
|
},
|
2015-11-15 14:06:49 +03:00
|
|
|
|
2018-01-02 21:29:03 +03:00
|
|
|
initCodeMirror: task(function* () {
|
2019-03-06 16:53:54 +03:00
|
|
|
let loader = this.lazyLoader;
|
2019-02-12 00:07:51 +03:00
|
|
|
yield loader.loadScript('codemirror', 'assets/codemirror/codemirror.js');
|
2018-01-02 21:29:03 +03:00
|
|
|
|
2019-01-02 12:58:55 +03:00
|
|
|
scheduleOnce('afterRender', this, this._initCodeMirror);
|
2018-01-02 21:29:03 +03:00
|
|
|
}),
|
2016-07-05 19:30:14 +03:00
|
|
|
|
|
|
|
_initCodeMirror() {
|
2018-04-25 14:06:01 +03:00
|
|
|
let options = this.getProperties('lineNumbers', 'lineWrapping', 'indentUnit', 'mode', 'theme', 'autofocus');
|
2019-03-06 16:53:54 +03:00
|
|
|
assign(options, {value: this._value});
|
2017-04-18 10:20:38 +03:00
|
|
|
|
2018-01-02 21:29:03 +03:00
|
|
|
let textarea = this.element.querySelector('textarea');
|
|
|
|
if (textarea && textarea === document.activeElement) {
|
|
|
|
options.autofocus = true;
|
|
|
|
}
|
|
|
|
|
2019-02-22 06:52:32 +03:00
|
|
|
this._editor = new CodeMirror.fromTextArea(textarea, options);
|
2015-10-15 15:03:26 +03:00
|
|
|
|
2017-04-18 10:20:38 +03:00
|
|
|
// by default CodeMirror will place the cursor at the beginning of the
|
|
|
|
// content, it makes more sense for the cursor to be at the end
|
|
|
|
if (options.autofocus) {
|
|
|
|
this._editor.setCursor(this._editor.lineCount(), 0);
|
|
|
|
}
|
2015-04-04 00:50:08 +03:00
|
|
|
|
|
|
|
// events
|
2017-04-18 10:20:38 +03:00
|
|
|
this._setupCodeMirrorEventHandler('focus', this, this._focus);
|
|
|
|
this._setupCodeMirrorEventHandler('blur', this, this._blur);
|
|
|
|
this._setupCodeMirrorEventHandler('change', this, this._update);
|
|
|
|
},
|
2017-03-30 17:29:08 +03:00
|
|
|
|
2017-04-18 10:20:38 +03:00
|
|
|
_setupCodeMirrorEventHandler(event, target, method) {
|
|
|
|
let callback = bind(target, method);
|
|
|
|
|
|
|
|
this._editor.on(event, callback);
|
|
|
|
|
|
|
|
this.one('willDestroyElement', this, function () {
|
|
|
|
this._editor.off(event, callback);
|
2015-04-04 00:50:08 +03:00
|
|
|
});
|
2017-04-18 10:20:38 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
_update(codeMirror, changeObj) {
|
2018-03-20 17:57:59 +03:00
|
|
|
once(this, this.update, codeMirror.getValue(), codeMirror, changeObj);
|
2017-04-18 10:20:38 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
_focus(codeMirror, event) {
|
|
|
|
this.set('isFocused', true);
|
2019-03-06 16:53:54 +03:00
|
|
|
once(this, this['focus-in'], codeMirror.getValue(), codeMirror, event);
|
2017-04-18 10:20:38 +03:00
|
|
|
},
|
2015-04-04 00:50:08 +03:00
|
|
|
|
2017-04-18 10:20:38 +03:00
|
|
|
_blur(/* codeMirror, event */) {
|
|
|
|
this.set('isFocused', false);
|
2015-04-04 00:50:08 +03:00
|
|
|
}
|
|
|
|
});
|
2016-06-18 14:44:23 +03:00
|
|
|
|
|
|
|
export default CmEditorComponent;
|