Ghost/ghost/admin/app/mixins/ed-editor-shortcuts.js
Hannah Wolfe 2f8de51d67 No more CodeMirror
closes #4368, fixes #1240 (spellcheck), fixes #4974 & fixes #4983 (caret positioning bugs)

- Drop CodeMirror in favour of a plain text area
- Use rangyinputs to handle selections cross-browser
- Create an API for interacting with the textarea
- Replace marker manager with a much simpler image manager
- Reimplement shortcuts, including some bug fixes
2015-03-17 14:32:55 +00:00

176 lines
5.1 KiB
JavaScript

/* global moment, Showdown */
import Ember from 'ember';
import titleize from 'ghost/utils/titleize';
var simpleShortcutSyntax,
shortcuts,
EditorShortcuts;
// Used for simple, noncomputational replace-and-go! shortcuts.
// See default case in shortcut function below.
simpleShortcutSyntax = {
bold: {
regex: '**|**',
cursor: '|'
},
italic: {
regex: '*|*',
cursor: '|'
},
strike: {
regex: '~~|~~',
cursor: '|'
},
code: {
regex: '`|`',
cursor: '|'
},
blockquote: {
regex: '> |',
cursor: '|',
newline: true
},
list: {
regex: '* |',
cursor: '|',
newline: true
},
link: {
regex: '[|](http://)',
cursor: 'http://'
},
image: {
regex: '![|](http://)',
cursor: 'http://',
newline: true
}
};
shortcuts = {
simple: function (type, replacement, selection, line) {
var shortcut,
startIndex = 0;
if (simpleShortcutSyntax.hasOwnProperty(type)) {
shortcut = simpleShortcutSyntax[type];
// insert the markdown
replacement.text = shortcut.regex.replace('|', selection.text);
// add a newline if needed
if (shortcut.newline && line.text !== '') {
startIndex = 1;
replacement.text = '\n' + replacement.text;
}
// handle cursor position
if (selection.text === '' && shortcut.cursor === '|') {
// the cursor should go where | was
replacement.position = startIndex + replacement.start + shortcut.regex.indexOf(shortcut.cursor);
} else if (shortcut.cursor !== '|') {
// the cursor should select the string which matches shortcut.cursor
replacement.position = {
start: replacement.start + replacement.text.indexOf(shortcut.cursor)
};
replacement.position.end = replacement.position.start + shortcut.cursor.length;
}
}
return replacement;
},
cycleHeaderLevel: function (replacement, line) {
// jscs:disable
var match = line.text.match(/^#+/),
// jscs:enable
currentHeaderLevel,
hashPrefix;
if (!match) {
currentHeaderLevel = 1;
} else {
currentHeaderLevel = match[0].length;
}
if (currentHeaderLevel > 2) {
currentHeaderLevel = 1;
}
hashPrefix = new Array(currentHeaderLevel + 2).join('#');
// jscs:disable
replacement.text = hashPrefix + ' ' + line.text.replace(/^#* /, '');
// jscs:enable
replacement.start = line.start;
replacement.end = line.end;
return replacement;
},
copyHTML: function (editor, selection) {
var converter = new Showdown.converter(),
generatedHTML;
if (selection.text) {
generatedHTML = converter.makeHtml(selection.text);
} else {
generatedHTML = converter.makeHtml(editor.getValue());
}
// Talk to the editor
editor.sendAction('openModal', 'copy-html', {generatedHTML: generatedHTML});
},
currentDate: function (replacement) {
replacement.text = moment(new Date()).format('D MMMM YYYY');
return replacement;
},
uppercase: function (replacement, selection) {
replacement.text = selection.text.toLocaleUpperCase();
return replacement;
},
lowercase: function (replacement, selection) {
replacement.text = selection.text.toLocaleLowerCase();
return replacement;
},
titlecase: function (replacement, selection) {
replacement.text = titleize(selection.text);
return replacement;
}
};
EditorShortcuts = Ember.Mixin.create({
shortcut: function (type) {
var selection = this.getSelection(),
replacement = {
start: selection.start,
end: selection.end,
position: 'collapseToEnd'
};
switch (type) {
// This shortcut is special as it needs to send an action
case 'copyHTML':
shortcuts.copyHTML(this, selection);
break;
case 'cycleHeaderLevel':
replacement = shortcuts.cycleHeaderLevel(replacement, this.getLine());
break;
// These shortcuts all process the basic information
case 'currentDate':
case 'uppercase':
case 'lowercase':
case 'titlecase':
replacement = shortcuts[type](replacement, selection, this.getLineToCursor());
break;
// All the of basic formatting shortcuts work with a regex
default:
replacement = shortcuts.simple(type, replacement, selection, this.getLineToCursor());
}
if (replacement.text) {
this.replaceSelection(replacement.text, replacement.start, replacement.end, replacement.position);
}
}
});
export default EditorShortcuts;