Ghost/ghost/admin/app/components/gh-editor.js
Kevin Ansfield 62553cdf92 Replace jQuery-based uploader.js with ember components
no issue
- adds `gh-image-uploader` that handles image uploads in a fully ember fashion and with no dependency on `uploader.js`
- adds `gh-image-uploader-with-preview` that can fully replace the old `gh-uploader`
- replace uses of `gh-uploader` in PSM & TSM with `gh-image-uploader-with-preview`
- updates the editor preview image handling to use the new `gh-image-uploader-with-preview` component
- updates the image upload modal to use `gh-image-uploader` (utilises the `saveButton=false` flag which means the preview has to be handled externally to avoid auto-replacement when typing a URL)
- removes all old `uploader.js` related code
- adds custom `RequestEntityTooLargeError` and `UnsupportedMediaTypeError` errors to our `ajax` service
2016-04-05 12:03:20 +01:00

124 lines
4.0 KiB
JavaScript

import Ember from 'ember';
import ShortcutsMixin from 'ghost/mixins/shortcuts';
import imageManager from 'ghost/utils/ed-image-manager';
import editorShortcuts from 'ghost/utils/editor-shortcuts';
const {Component, computed, run} = Ember;
const {equal} = computed;
export default Component.extend(ShortcutsMixin, {
tagName: 'section',
classNames: ['view-container', 'view-editor'],
activeTab: 'markdown',
editor: null,
editorDisabled: undefined,
editorScrollInfo: null, // updated when gh-ed-editor component scrolls
height: null, // updated when markdown is rendered
shouldFocusEditor: false,
showCopyHTMLModal: false,
copyHTMLModalContent: null,
shortcuts: editorShortcuts,
markdownActive: equal('activeTab', 'markdown'),
previewActive: equal('activeTab', 'preview'),
// HTML Preview listens to scrollPosition and updates its scrollTop value
// This property receives scrollInfo from the textEditor, and height from the preview pane, and will update the
// scrollPosition value such that when either scrolling or typing-at-the-end of the text editor the preview pane
// stays in sync
scrollPosition: computed('editorScrollInfo', 'height', function () {
let scrollInfo = this.get('editorScrollInfo');
let {$previewContent, $previewViewPort} = this;
if (!scrollInfo || !$previewContent || !$previewViewPort) {
return 0;
}
let previewHeight = $previewContent.height() - $previewViewPort.height();
let previewPosition, ratio;
ratio = previewHeight / scrollInfo.diff;
previewPosition = scrollInfo.top * ratio;
return previewPosition;
}),
didInsertElement() {
this._super(...arguments);
this.registerShortcuts();
run.scheduleOnce('afterRender', this, this._cacheElements);
},
willDestroyElement() {
if (this.attrs.onTeardown) {
this.attrs.onTeardown();
}
this.removeShortcuts();
},
_cacheElements() {
// cache these elements for use in other methods
this.$previewViewPort = this.$('.js-entry-preview-content');
this.$previewContent = this.$('.js-rendered-markdown');
},
actions: {
selectTab(tab) {
this.set('activeTab', tab);
},
updateScrollInfo(scrollInfo) {
this.set('editorScrollInfo', scrollInfo);
},
updateHeight(height) {
this.set('height', height);
},
// set from a `sendAction` on the gh-ed-editor component,
// so that we get a reference for handling uploads.
setEditor(editor) {
this.set('editor', editor);
},
disableEditor() {
this.set('editorDisabled', true);
},
enableEditor() {
this.set('editorDisabled', undefined);
},
// The actual functionality is implemented in utils/ed-editor-shortcuts
editorShortcut(options) {
if (this.editor.$().is(':focus')) {
this.editor.shortcut(options.type);
}
},
// Match the uploaded file to a line in the editor, and update that line with a path reference
// ensuring that everything ends up in the correct place and format.
handleImgUpload(imageIndex, newSrc) {
let editor = this.get('editor');
let editorValue = editor.getValue();
let replacement = imageManager.getSrcRange(editorValue, imageIndex);
let cursorPosition;
if (replacement) {
cursorPosition = replacement.start + newSrc.length + 1;
if (replacement.needsParens) {
newSrc = `(${newSrc})`;
}
editor.replaceSelection(newSrc, replacement.start, replacement.end, cursorPosition);
}
},
toggleCopyHTMLModal(generatedHTML) {
this.set('copyHTMLModalContent', generatedHTML);
this.toggleProperty('showCopyHTMLModal');
}
}
});