2017-05-08 12:44:02 +03:00
|
|
|
import {
|
|
|
|
replaceWithHeaderSection,
|
|
|
|
replaceWithListSection
|
|
|
|
} from 'mobiledoc-kit/editor/text-input-handlers';
|
|
|
|
|
|
|
|
// Text expansions watch text entry events and will look for matches, replacing
|
|
|
|
// the matches with additional markup, atoms, or cards
|
|
|
|
// https://github.com/bustlelabs/mobiledoc-kit#responding-to-text-input
|
2017-02-27 07:44:15 +03:00
|
|
|
|
2018-01-30 13:01:07 +03:00
|
|
|
// TODO: this was copied from our old Koenig editor, it could do with some
|
|
|
|
// comments, cleanup, and refactoring
|
|
|
|
|
2017-02-27 07:44:15 +03:00
|
|
|
export default function (editor) {
|
2017-05-08 12:44:02 +03:00
|
|
|
// We don't want to run all our content rules on every text entry event,
|
|
|
|
// instead we check to see if this text entry event could match a content
|
|
|
|
// rule, and only then run the rules. Right now we only want to match
|
|
|
|
// content ending with *, _, ), ~, and `. This could increase as we support
|
|
|
|
// more markdown.
|
2017-02-27 07:44:15 +03:00
|
|
|
|
2017-03-02 19:51:57 +03:00
|
|
|
editor.onTextInput({
|
|
|
|
name: 'inline_markdown',
|
|
|
|
match: /[*_)~`]$/,
|
|
|
|
run(postEditor, matches) {
|
|
|
|
let text = postEditor.range.head.section.textUntil(postEditor.range.head);
|
|
|
|
|
|
|
|
switch (matches[0]) {
|
|
|
|
case '*':
|
|
|
|
matchStrongStar(postEditor, text);
|
|
|
|
matchEmStar(postEditor, text);
|
|
|
|
break;
|
|
|
|
case '_':
|
2018-01-30 13:01:07 +03:00
|
|
|
matchStrongUnderscore(postEditor, text);
|
|
|
|
matchEmUnderscore(postEditor, text);
|
2017-03-02 19:51:57 +03:00
|
|
|
break;
|
|
|
|
case ')':
|
|
|
|
matchLink(postEditor, text);
|
2018-01-30 23:46:03 +03:00
|
|
|
matchImage(postEditor, text);
|
2017-03-02 19:51:57 +03:00
|
|
|
break;
|
|
|
|
case '~':
|
|
|
|
matchStrikethrough(postEditor, text);
|
|
|
|
break;
|
|
|
|
case '`':
|
2018-01-30 13:01:07 +03:00
|
|
|
matchCode(postEditor, text);
|
2017-03-02 19:51:57 +03:00
|
|
|
break;
|
2017-02-27 07:44:15 +03:00
|
|
|
}
|
|
|
|
}
|
2017-03-02 19:51:57 +03:00
|
|
|
});
|
2017-02-27 07:44:15 +03:00
|
|
|
|
2018-01-30 13:01:07 +03:00
|
|
|
/* block level markdown ------------------------------------------------- */
|
|
|
|
|
|
|
|
// mobiledoc-kit has `* ` already built-in so we only need to add `- `
|
2017-03-02 19:51:57 +03:00
|
|
|
editor.onTextInput({
|
2018-01-30 13:01:07 +03:00
|
|
|
name: 'md_ul',
|
2017-03-02 19:51:57 +03:00
|
|
|
match: /^- $/,
|
|
|
|
run(editor) {
|
|
|
|
replaceWithListSection(editor, 'ul');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
editor.onTextInput({
|
2018-01-30 13:01:07 +03:00
|
|
|
name: 'md_blockquote',
|
2017-03-02 19:51:57 +03:00
|
|
|
match: /^> $/,
|
|
|
|
run(editor) {
|
|
|
|
replaceWithHeaderSection(editor, 'blockquote');
|
|
|
|
}
|
|
|
|
});
|
2017-02-27 07:44:15 +03:00
|
|
|
|
2018-01-30 18:19:30 +03:00
|
|
|
editor.onTextInput({
|
|
|
|
name: 'md_hr',
|
|
|
|
match: /^---$/,
|
|
|
|
run(editor) {
|
|
|
|
let {range: {head, head: {section}}} = editor;
|
|
|
|
|
|
|
|
// Skip if cursor is not at end of section
|
|
|
|
if (!head.isTail()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip if section is a list item
|
|
|
|
if (section.isListItem) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
editor.run((postEditor) => {
|
2018-02-01 14:26:24 +03:00
|
|
|
let card = postEditor.builder.createCardSection('hr');
|
2018-01-30 18:19:30 +03:00
|
|
|
let needsTrailingParagraph = !section.next;
|
|
|
|
|
|
|
|
postEditor.replaceSection(section, card);
|
|
|
|
|
|
|
|
// add an empty paragraph after if necessary so writing can continue
|
|
|
|
if (needsTrailingParagraph) {
|
|
|
|
let newSection = postEditor.builder.createMarkupSection('p');
|
|
|
|
postEditor.insertSectionAtEnd(newSection);
|
|
|
|
postEditor.setRange(newSection.tailPosition());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-01-30 13:01:07 +03:00
|
|
|
/* inline markdown ------------------------------------------------------ */
|
|
|
|
|
2017-02-27 07:44:15 +03:00
|
|
|
function matchStrongStar(editor, text) {
|
2017-03-02 19:51:57 +03:00
|
|
|
let {range} = editor;
|
2017-02-27 07:44:15 +03:00
|
|
|
let matches = text.match(/\*\*(.+?)\*\*$/);
|
2017-03-02 19:51:57 +03:00
|
|
|
if (matches) {
|
2017-02-27 07:44:15 +03:00
|
|
|
range = range.extend(-(matches[0].length));
|
2017-03-02 19:51:57 +03:00
|
|
|
editor.run((postEditor) => {
|
2017-02-27 07:44:15 +03:00
|
|
|
let position = postEditor.deleteRange(range);
|
|
|
|
let bold = postEditor.builder.createMarkup('strong');
|
|
|
|
let nextPosition = postEditor.insertTextWithMarkup(position, matches[1], [bold]);
|
2018-01-30 13:01:07 +03:00
|
|
|
postEditor.insertTextWithMarkup(nextPosition, ' ', []);
|
2017-02-27 07:44:15 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2017-03-02 19:51:57 +03:00
|
|
|
|
2018-01-30 13:01:07 +03:00
|
|
|
function matchStrongUnderscore(editor, text) {
|
2017-03-02 19:51:57 +03:00
|
|
|
let {range} = editor;
|
2017-02-27 07:44:15 +03:00
|
|
|
let matches = text.match(/__(.+?)__$/);
|
2017-03-02 19:51:57 +03:00
|
|
|
if (matches) {
|
2017-02-27 07:44:15 +03:00
|
|
|
range = range.extend(-(matches[0].length));
|
2017-03-02 19:51:57 +03:00
|
|
|
editor.run((postEditor) => {
|
2017-02-27 07:44:15 +03:00
|
|
|
let position = postEditor.deleteRange(range);
|
|
|
|
let bold = postEditor.builder.createMarkup('strong');
|
|
|
|
let nextPosition = postEditor.insertTextWithMarkup(position, matches[1], [bold]);
|
2018-01-30 13:01:07 +03:00
|
|
|
postEditor.insertTextWithMarkup(nextPosition, ' ', []);
|
2017-02-27 07:44:15 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2017-03-02 19:51:57 +03:00
|
|
|
|
2017-02-27 07:44:15 +03:00
|
|
|
function matchEmStar(editor, text) {
|
2017-03-02 19:51:57 +03:00
|
|
|
let {range} = editor;
|
2018-01-05 18:38:23 +03:00
|
|
|
let matches = text.match(/(^|[^*])\*([^*].*?)\*$/);
|
2017-03-02 19:51:57 +03:00
|
|
|
if (matches) {
|
2017-02-27 07:44:15 +03:00
|
|
|
let match = matches[0][0] === '*' ? matches[0] : matches[0].substr(1);
|
|
|
|
range = range.extend(-(match.length));
|
2017-03-02 19:51:57 +03:00
|
|
|
editor.run((postEditor) => {
|
2017-02-27 07:44:15 +03:00
|
|
|
let position = postEditor.deleteRange(range);
|
|
|
|
let em = postEditor.builder.createMarkup('em');
|
|
|
|
let nextPosition = postEditor.insertTextWithMarkup(position, matches[2], [em]);
|
2018-01-30 13:01:07 +03:00
|
|
|
postEditor.insertTextWithMarkup(nextPosition, ' ', []);
|
2017-02-27 07:44:15 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2017-03-02 19:51:57 +03:00
|
|
|
|
2018-01-30 13:01:07 +03:00
|
|
|
function matchEmUnderscore(editor, text) {
|
2017-03-02 19:51:57 +03:00
|
|
|
let {range} = editor;
|
2017-02-27 07:44:15 +03:00
|
|
|
let matches = text.match(/(^|[^_])_([^_].+?)_$/);
|
2017-03-02 19:51:57 +03:00
|
|
|
if (matches) {
|
2017-02-27 07:44:15 +03:00
|
|
|
let match = matches[0][0] === '_' ? matches[0] : matches[0].substr(1);
|
|
|
|
range = range.extend(-(match.length));
|
2017-03-02 19:51:57 +03:00
|
|
|
editor.run((postEditor) => {
|
2017-02-27 07:44:15 +03:00
|
|
|
let position = postEditor.deleteRange(range);
|
|
|
|
let em = postEditor.builder.createMarkup('em');
|
|
|
|
let nextPosition = postEditor.insertTextWithMarkup(position, matches[2], [em]);
|
2018-01-30 13:01:07 +03:00
|
|
|
postEditor.insertTextWithMarkup(nextPosition, ' ', []);
|
2017-02-27 07:44:15 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2017-03-02 19:51:57 +03:00
|
|
|
|
2017-02-27 07:44:15 +03:00
|
|
|
function matchLink(editor, text) {
|
2017-03-02 19:51:57 +03:00
|
|
|
let {range} = editor;
|
2017-02-27 07:44:15 +03:00
|
|
|
let matches = text.match(/(^|[^!])\[(.*?)\]\((.*?)\)$/);
|
2017-03-02 19:51:57 +03:00
|
|
|
if (matches) {
|
2017-02-27 07:44:15 +03:00
|
|
|
let url = matches[3];
|
|
|
|
let text = matches[2];
|
|
|
|
let match = matches[0][0] === '[' ? matches[0] : matches[0].substr(1);
|
|
|
|
range = range.extend(-match.length);
|
2017-03-02 19:51:57 +03:00
|
|
|
editor.run((postEditor) => {
|
2017-02-27 07:44:15 +03:00
|
|
|
let position = postEditor.deleteRange(range);
|
|
|
|
let a = postEditor.builder.createMarkup('a', {href: url});
|
|
|
|
let nextPosition = postEditor.insertTextWithMarkup(position, text, [a]);
|
2018-01-30 13:01:07 +03:00
|
|
|
postEditor.insertTextWithMarkup(nextPosition, ' ', []); // insert the un-marked-up space
|
2017-02-27 07:44:15 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2017-03-02 19:51:57 +03:00
|
|
|
|
2018-01-30 23:46:03 +03:00
|
|
|
function matchImage(editor, text) {
|
|
|
|
let matches = text.match(/^!\[(.*?)\]\((.*?)\)$/);
|
|
|
|
if (matches) {
|
|
|
|
let {range: {head, head: {section}}} = editor;
|
|
|
|
let src = matches[2];
|
|
|
|
let alt = matches[1];
|
|
|
|
|
|
|
|
// skip if cursor is not at end of section
|
|
|
|
if (!head.isTail()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// mobiledoc lists don't support cards
|
|
|
|
if (section.isListItem) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
editor.run((postEditor) => {
|
2018-02-01 14:26:24 +03:00
|
|
|
let card = postEditor.builder.createCardSection('image', {src, alt});
|
2018-01-30 23:46:03 +03:00
|
|
|
// need to check the section before replacing else it will always
|
|
|
|
// add a trailing paragraph
|
|
|
|
let needsTrailingParagraph = !section.next;
|
|
|
|
|
|
|
|
editor.range.extend(-(matches[0].length));
|
|
|
|
postEditor.replaceSection(editor.range.headSection, card);
|
|
|
|
|
|
|
|
if (needsTrailingParagraph) {
|
|
|
|
let newSection = editor.builder.createMarkupSection('p');
|
|
|
|
postEditor.insertSectionAtEnd(newSection);
|
|
|
|
postEditor.setRange(newSection.tailPosition());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-27 07:44:15 +03:00
|
|
|
function matchStrikethrough(editor, text) {
|
2017-03-02 19:51:57 +03:00
|
|
|
let {range} = editor;
|
2018-01-30 13:01:07 +03:00
|
|
|
let matches = text.match(/~(.+?)~$/);
|
2017-03-02 19:51:57 +03:00
|
|
|
if (matches) {
|
2017-02-27 07:44:15 +03:00
|
|
|
range = range.extend(-(matches[0].length));
|
2017-03-02 19:51:57 +03:00
|
|
|
editor.run((postEditor) => {
|
2017-02-27 07:44:15 +03:00
|
|
|
let position = postEditor.deleteRange(range);
|
|
|
|
let s = postEditor.builder.createMarkup('s');
|
|
|
|
let nextPosition = postEditor.insertTextWithMarkup(position, matches[1], [s]);
|
2018-01-30 13:01:07 +03:00
|
|
|
postEditor.insertTextWithMarkup(nextPosition, ' ', []); // insert the un-marked-up space
|
2017-02-27 07:44:15 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2017-03-02 19:51:57 +03:00
|
|
|
|
2018-01-30 13:01:07 +03:00
|
|
|
function matchCode(editor, text) {
|
|
|
|
let {range} = editor;
|
|
|
|
let matches = text.match(/`(.+?)`/);
|
2017-03-02 19:51:57 +03:00
|
|
|
if (matches) {
|
2018-01-30 13:01:07 +03:00
|
|
|
range = range.extend(-(matches[0].length));
|
2017-03-02 19:51:57 +03:00
|
|
|
editor.run((postEditor) => {
|
2018-01-30 13:01:07 +03:00
|
|
|
let position = postEditor.deleteRange(range);
|
|
|
|
let code = postEditor.builder.createMarkup('code');
|
|
|
|
let nextPosition = postEditor.insertTextWithMarkup(position, matches[1], [code]);
|
|
|
|
postEditor.insertTextWithMarkup(nextPosition, ' ', []); // insert the un-marked-up space
|
2017-02-27 07:44:15 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|