90 lines
2.9 KiB
JavaScript
Raw Normal View History

2017-11-16 11:25:21 -05:00
import { Block, Text } from 'slate';
/**
* Validation functions are used to validate the editor state each time it
* changes, to ensure it is never rendered in an undesirable state.
*/
export function validateNode(node) {
/**
* Validation of the document itself.
*/
if (node.object === 'document') {
2017-11-16 16:37:02 -05:00
const doc = node;
2017-11-16 11:25:21 -05:00
/**
* If the editor is ever in an empty state, insert an empty
* paragraph block.
*/
const hasBlocks = !doc.getBlocks().isEmpty();
if (!hasBlocks) {
return change => {
const block = Block.create({
type: 'paragraph',
nodes: [Text.create('')],
});
2017-11-16 12:36:48 -05:00
const { key } = change.value.document;
2017-11-16 11:25:21 -05:00
return change.insertNodeByKey(key, 0, block).focus();
};
}
/**
* Ensure that shortcodes are children of the root node.
*/
2017-11-16 16:37:02 -05:00
const nestedShortcode = doc.findDescendant(descendant => {
2017-11-16 11:25:21 -05:00
const { type, key } = descendant;
2017-11-16 16:37:02 -05:00
return type === 'shortcode' && doc.getParent(key).key !== doc.key;
2017-11-16 11:25:21 -05:00
});
if (nestedShortcode) {
2017-11-16 16:37:02 -05:00
const unwrapShortcode = change => {
const key = nestedShortcode.key;
const newDoc = change.value.document;
const newParent = newDoc.getParent(key);
const docIsParent = newParent.key === newDoc.key;
const newParentParent = newDoc.getParent(newParent.key);
const docIsParentParent = newParentParent && newParentParent.key === newDoc.key;
if (docIsParent) {
return change;
}
/**
* Normalization happens by default, and causes all validation to
* restart with the result of a change upon execution. This unwrap loop
* could temporarily place a shortcode node in conflict with an outside
* plugin's schema, resulting in an infinite loop. To ensure against
* this, we turn off normalization until the last change.
*/
change.unwrapNodeByKey(nestedShortcode.key, { normalize: docIsParentParent });
};
return unwrapShortcode;
2017-11-16 11:25:21 -05:00
}
/**
* Ensure that trailing shortcodes are followed by an empty paragraph.
*/
2017-11-16 16:37:02 -05:00
const trailingShortcode = doc.findDescendant(descendant => {
2017-11-16 11:25:21 -05:00
const { type, key } = descendant;
2017-11-16 16:37:02 -05:00
return type === 'shortcode' && doc.getBlocks().last().key === key;
2017-11-16 11:25:21 -05:00
});
if (trailingShortcode) {
return change => {
const text = Text.create('');
const block = Block.create({ type: 'paragraph', nodes: [text] });
2017-11-16 16:37:02 -05:00
return change.insertNodeByKey(doc.key, doc.get('nodes').size, block);
2017-11-16 11:25:21 -05:00
};
}
}
/**
* Ensure that code blocks contain no marks.
*/
if (node.type === 'code') {
const invalidChild = node.getTexts().find(text => !text.getMarks().isEmpty());
if (invalidChild) {
return change =>
invalidChild
.getMarks()
.forEach(mark =>
change.removeMarkByKey(invalidChild.key, 0, invalidChild.get('characters').size, mark),
);
2017-11-16 11:25:21 -05:00
}
}
}