Ghost-Admin/app/components/gh-codemirror.js

165 lines
4.8 KiB
JavaScript

import Ember from 'ember';
/*global CodeMirror */
import MarkerManager from 'ghost/mixins/marker-manager';
import mobileCodeMirror from 'ghost/utils/codemirror-mobile';
import setScrollClassName from 'ghost/utils/set-scroll-classname';
import codeMirrorShortcuts from 'ghost/utils/codemirror-shortcuts';
var onChangeHandler,
onScrollHandler,
Codemirror;
codeMirrorShortcuts.init();
onChangeHandler = function (cm, changeObj) {
var line,
component = cm.component;
// fill array with a range of numbers
for (line = changeObj.from.line; line < changeObj.from.line + changeObj.text.length; line += 1) {
component.checkLine.call(component, line, changeObj.origin);
}
// Is this a line which may have had a marker on it?
component.checkMarkers.call(component);
cm.component.set('value', cm.getValue());
component.sendAction('typingPause');
};
onScrollHandler = function (cm) {
var scrollInfo = cm.getScrollInfo(),
component = cm.component;
scrollInfo.codemirror = cm;
// throttle scroll updates
component.throttle = Ember.run.throttle(component, function () {
this.set('scrollInfo', scrollInfo);
}, 10);
};
Codemirror = Ember.TextArea.extend(MarkerManager, {
focus: true,
focusCursorAtEnd: false,
setFocus: function () {
if (this.get('focus')) {
this.$().val(this.$().val()).focus();
}
}.on('didInsertElement'),
didInsertElement: function () {
Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
},
afterRenderEvent: function () {
var self = this,
codemirror;
// replaces CodeMirror with TouchEditor only if we're on mobile
mobileCodeMirror.createIfMobile();
codemirror = this.initCodemirror();
this.set('codemirror', codemirror);
this.sendAction('setCodeMirror', this);
if (this.get('focus') && this.get('focusCursorAtEnd')) {
codemirror.execCommand('goDocEnd');
}
codemirror.eachLine(function initMarkers() {
self.initMarkers.apply(self, arguments);
});
},
// this needs to be placed on the 'afterRender' queue otherwise CodeMirror gets wonky
initCodemirror: function () {
// create codemirror
var codemirror,
self = this;
codemirror = CodeMirror.fromTextArea(this.get('element'), {
mode: 'gfm',
tabMode: 'indent',
tabindex: '2',
cursorScrollMargin: 10,
lineWrapping: true,
dragDrop: false,
extraKeys: {
Home: 'goLineLeft',
End: 'goLineRight',
'Ctrl-U': false,
'Cmd-U': false,
'Shift-Ctrl-U': false,
'Shift-Cmd-U': false,
'Ctrl-S': false,
'Cmd-S': false,
'Ctrl-D': false,
'Cmd-D': false
}
});
// Codemirror needs a reference to the component
// so that codemirror originating events can propogate
// up the ember action pipeline
codemirror.component = this;
// propagate changes to value property
codemirror.on('change', onChangeHandler);
// on scroll update scrollPosition property
codemirror.on('scroll', onScrollHandler);
codemirror.on('scroll', Ember.run.bind(Ember.$('.CodeMirror-scroll'), setScrollClassName, {
target: Ember.$('.js-entry-markdown'),
offset: 10
}));
codemirror.on('focus', function () {
self.sendAction('onFocusIn');
});
return codemirror;
},
disableCodeMirror: function () {
var codemirror = this.get('codemirror');
codemirror.setOption('readOnly', 'nocursor');
codemirror.off('change', onChangeHandler);
},
enableCodeMirror: function () {
var codemirror = this.get('codemirror');
codemirror.setOption('readOnly', false);
// clicking the trash button on an image dropzone causes this function to fire.
// this line is a hack to prevent multiple event handlers from being attached.
codemirror.off('change', onChangeHandler);
codemirror.on('change', onChangeHandler);
},
removeThrottle: function () {
Ember.run.cancel(this.throttle);
}.on('willDestroyElement'),
removeCodemirrorHandlers: function () {
// not sure if this is needed.
var codemirror = this.get('codemirror');
codemirror.off('change', onChangeHandler);
codemirror.off('scroll');
}.on('willDestroyElement'),
clearMarkerManagerMarkers: function () {
this.clearMarkers();
}.on('willDestroyElement')
});
export default Codemirror;