function CharacterMetadata() { _classCallCheck(this, CharacterMetadata); return _possibleConstructorReturn(this, _CharacterMetadataRec.apply(this, arguments)); }
n/a
function CompositeDraftDecorator(decorators) { _classCallCheck(this, CompositeDraftDecorator); // Copy the decorator array, since we use this array order to determine // precedence of decoration matching. If the array is mutated externally, // we don't want to be affected here. this._decorators = decorators.slice(); }
n/a
function ContentBlock() { _classCallCheck(this, ContentBlock); return _possibleConstructorReturn(this, _ContentBlockRecord.apply(this, arguments)); }
n/a
function ContentState() { _classCallCheck(this, ContentState); return _possibleConstructorReturn(this, _ContentStateRecord.apply(this, arguments)); }
n/a
function DraftEditorContents() { _classCallCheck(this, DraftEditorContents); return _possibleConstructorReturn(this, _React$Component.apply(this, arguments)); }
n/a
function DraftEditorLeaf() { _classCallCheck(this, DraftEditorLeaf); return _possibleConstructorReturn(this, _React$Component.apply(this, arguments)); }
n/a
function DraftEditorPlaceholder() { _classCallCheck(this, DraftEditorPlaceholder); return _possibleConstructorReturn(this, _React$Component.apply(this, arguments)); }
n/a
function DraftEditorTextNode(props) { _classCallCheck(this, DraftEditorTextNode); var _this = _possibleConstructorReturn(this, _React$Component.call(this, props)); _this._forceFlag = false; return _this; }
n/a
function DraftEditor(props) { _classCallCheck(this, DraftEditor); var _this = _possibleConstructorReturn(this, _React$Component.call(this, props)); _this._blockSelectEvents = false; _this._clipboard = null; _this._handler = null; _this._dragCount = 0; _this._editorKey = generateRandomKey(); _this._placeholderAccessibilityID = 'placeholder-' + _this._editorKey; _this._latestEditorState = props.editorState; _this._onBeforeInput = _this._buildHandler('onBeforeInput'); _this._onBlur = _this._buildHandler('onBlur'); _this._onCharacterData = _this._buildHandler('onCharacterData'); _this._onCompositionEnd = _this._buildHandler('onCompositionEnd'); _this._onCompositionStart = _this._buildHandler('onCompositionStart'); _this._onCopy = _this._buildHandler('onCopy'); _this._onCut = _this._buildHandler('onCut'); _this._onDragEnd = _this._buildHandler('onDragEnd'); _this._onDragOver = _this._buildHandler('onDragOver'); _this._onDragStart = _this._buildHandler('onDragStart'); _this._onDrop = _this._buildHandler('onDrop'); _this._onInput = _this._buildHandler('onInput'); _this._onFocus = _this._buildHandler('onFocus'); _this._onKeyDown = _this._buildHandler('onKeyDown'); _this._onKeyPress = _this._buildHandler('onKeyPress'); _this._onKeyUp = _this._buildHandler('onKeyUp'); _this._onMouseDown = _this._buildHandler('onMouseDown'); _this._onMouseUp = _this._buildHandler('onMouseUp'); _this._onPaste = _this._buildHandler('onPaste'); _this._onSelect = _this._buildHandler('onSelect'); // Manual binding for public and internal methods. _this.focus = _this._focus.bind(_this); _this.blur = _this._blur.bind(_this); _this.setMode = _this._setMode.bind(_this); _this.exitCurrentMode = _this._exitCurrentMode.bind(_this); _this.restoreEditorDOM = _this._restoreEditorDOM.bind(_this); _this.setClipboard = _this._setClipboard.bind(_this); _this.getClipboard = _this._getClipboard.bind(_this); _this.getEditorKey = function () { return _this._editorKey; }; _this.update = _this._update.bind(_this); _this.onDragEnter = _this._onDragEnter.bind(_this); _this.onDragLeave = _this._onDragLeave.bind(_this); // See `_restoreEditorDOM()`. _this.state = { containerKey: 0 }; return _this; }
n/a
function DraftEditorBlock() { _classCallCheck(this, DraftEditorBlock); return _possibleConstructorReturn(this, _React$Component.apply(this, arguments)); }
n/a
function EditorState(immutable) { _classCallCheck(this, EditorState); this._immutable = immutable; }
n/a
function DraftEntityInstance() { _classCallCheck(this, DraftEntityInstance); return _possibleConstructorReturn(this, _DraftEntityInstanceR.apply(this, arguments)); }
n/a
function SelectionState() { _classCallCheck(this, SelectionState); return _possibleConstructorReturn(this, _SelectionStateRecord.apply(this, arguments)); }
n/a
function convertFromHTMLtoContentBlocks(html) { var DOMBuilder = arguments.length <= 1 || arguments[1] === undefined ? getSafeBodyFromHTML : arguments[1]; var blockRenderMap = arguments.length <= 2 || arguments[2] === undefined ? DefaultDraftBlockRenderMap : arguments[2]; // Be ABSOLUTELY SURE that the dom builder you pass here won't execute // arbitrary code in whatever environment you're running this in. For an // example of how we try to do this in-browser, see getSafeBodyFromHTML. // TODO: replace DraftEntity with an OrderedMap here var chunkData = getChunkForHTML(html, DOMBuilder, blockRenderMap, DraftEntity); if (chunkData == null) { return null; } var chunk = chunkData.chunk; var newEntityMap = chunkData.entityMap; var start = 0; return { contentBlocks: chunk.text.split('\r').map(function (textBlock, ii) { // Make absolutely certain that our text is acceptable. textBlock = sanitizeDraftText(textBlock); var end = start + textBlock.length; var inlines = nullthrows(chunk).inlines.slice(start, end); var entities = nullthrows(chunk).entities.slice(start, end); var characterList = List(inlines.map(function (style, ii) { var data = { style: style, entity: null }; if (entities[ii]) { data.entity = entities[ii]; } return CharacterMetadata.create(data); })); start = end + 1; return new ContentBlock({ key: generateRandomKey(), type: nullthrows(chunk).blocks[ii].type, depth: nullthrows(chunk).blocks[ii].depth, text: textBlock, characterList: characterList }); }), entityMap: newEntityMap }; }
n/a
function convertFromRawToDraftState(rawState) { var blocks = rawState.blocks; var entityMap = rawState.entityMap; var fromStorageToLocal = {}; // TODO: Update this once we completely remove DraftEntity Object.keys(entityMap).forEach(function (storageKey) { var encodedEntity = entityMap[storageKey]; var type = encodedEntity.type; var mutability = encodedEntity.mutability; var data = encodedEntity.data; var newKey = DraftEntity.__create(type, mutability, data || {}); fromStorageToLocal[storageKey] = newKey; }); var contentBlocks = blocks.map(function (block) { var key = block.key; var type = block.type; var text = block.text; var depth = block.depth; var inlineStyleRanges = block.inlineStyleRanges; var entityRanges = block.entityRanges; var data = block.data; key = key || generateRandomKey(); depth = depth || 0; inlineStyleRanges = inlineStyleRanges || []; entityRanges = entityRanges || []; data = Map(data); var inlineStyles = decodeInlineStyleRanges(text, inlineStyleRanges); // Translate entity range keys to the DraftEntity map. var filteredEntityRanges = entityRanges.filter(function (range) { return fromStorageToLocal.hasOwnProperty(range.key); }).map(function (range) { return _extends({}, range, { key: fromStorageToLocal[range.key] }); }); var entities = decodeEntityRanges(text, filteredEntityRanges); var characterList = createCharacterList(inlineStyles, entities); return new ContentBlock({ key: key, type: type, text: text, depth: depth, characterList: characterList, data: data }); }); return ContentState.createFromBlockArray(contentBlocks); }
n/a
function convertFromDraftStateToRaw(contentState) { var entityStorageKey = 0; var entityStorageMap = {}; var rawBlocks = []; contentState.getBlockMap().forEach(function (block, blockKey) { block.findEntityRanges(function (character) { return character.getEntity() !== null; }, function (start) { // Stringify to maintain order of otherwise numeric keys. var stringifiedEntityKey = DraftStringKey.stringify(block.getEntityAt(start)); if (!entityStorageMap.hasOwnProperty(stringifiedEntityKey)) { entityStorageMap[stringifiedEntityKey] = '' + entityStorageKey++; } }); rawBlocks.push({ key: blockKey, text: block.getText(), type: block.getType(), depth: block.getDepth(), inlineStyleRanges: encodeInlineStyleRanges(block), entityRanges: encodeEntityRanges(block, entityStorageMap), data: block.getData().toObject() }); }); // Flip storage map so that our storage keys map to global // DraftEntity keys. var entityKeys = Object.keys(entityStorageMap); var flippedStorageMap = {}; entityKeys.forEach(function (key, jj) { var entity = contentState.getEntity(DraftStringKey.unstringify(key)); flippedStorageMap[jj] = { type: entity.getType(), mutability: entity.getMutability(), data: entity.getData() }; }); return { entityMap: flippedStorageMap, blocks: rawBlocks }; }
n/a
function generateRandomKey() { var key = void 0; while (key === undefined || seenKeys.hasOwnProperty(key) || !isNaN(+key)) { key = Math.floor(Math.random() * MULTIPLIER).toString(32); } seenKeys[key] = true; return key; }
n/a
function getDefaultKeyBinding(e) { switch (e.keyCode) { case 66: // B return hasCommandModifier(e) ? 'bold' : null; case 68: // D return isCtrlKeyCommand(e) ? 'delete' : null; case 72: // H return isCtrlKeyCommand(e) ? 'backspace' : null; case 73: // I return hasCommandModifier(e) ? 'italic' : null; case 74: // J return hasCommandModifier(e) ? 'code' : null; case 75: // K return !isWindows && isCtrlKeyCommand(e) ? 'secondary-cut' : null; case 77: // M return isCtrlKeyCommand(e) ? 'split-block' : null; case 79: // O return isCtrlKeyCommand(e) ? 'split-block' : null; case 84: // T return isOSX && isCtrlKeyCommand(e) ? 'transpose-characters' : null; case 85: // U return hasCommandModifier(e) ? 'underline' : null; case 87: // W return isOSX && isCtrlKeyCommand(e) ? 'backspace-word' : null; case 89: // Y if (isCtrlKeyCommand(e)) { return isWindows ? 'redo' : 'secondary-paste'; } return null; case 90: // Z return getZCommand(e) || null; case Keys.RETURN: return 'split-block'; case Keys.DELETE: return getDeleteCommand(e); case Keys.BACKSPACE: return getBackspaceCommand(e); // LEFT/RIGHT handlers serve as a workaround for a Firefox bug. case Keys.LEFT: return shouldFixFirefoxMovement && hasCommandModifier(e) ? 'move-selection-to-start-of-block' : null; case Keys.RIGHT: return shouldFixFirefoxMovement && hasCommandModifier(e) ? 'move-selection-to-end-of-block' : null; default: return null; } }
n/a
function getVisibleSelectionRect(global) { var selection = global.getSelection(); if (!selection.rangeCount) { return null; } var range = selection.getRangeAt(0); var boundingRect = getRangeBoundingClientRect(range); var top = boundingRect.top; var right = boundingRect.right; var bottom = boundingRect.bottom; var left = boundingRect.left; // When a re-render leads to a node being removed, the DOM selection will // temporarily be placed on an ancestor node, which leads to an invalid // bounding rect. Discard this state. if (top === 0 && right === 0 && bottom === 0 && left === 0) { return null; } return boundingRect; }
n/a
function insertAtomicBlock(editorState, entityKey, character) { var contentState = editorState.getCurrentContent(); var selectionState = editorState.getSelection(); var afterRemoval = DraftModifier.removeRange(contentState, selectionState, 'backward'); var targetSelection = afterRemoval.getSelectionAfter(); var afterSplit = DraftModifier.splitBlock(afterRemoval, targetSelection); var insertionTarget = afterSplit.getSelectionAfter(); var asAtomicBlock = DraftModifier.setBlockType(afterSplit, insertionTarget, 'atomic'); var charData = CharacterMetadata.create({ entity: entityKey }); var fragmentArray = [new ContentBlock({ key: generateRandomKey(), type: 'atomic', text: character, characterList: List(Repeat(charData, character.length)) }), new ContentBlock({ key: generateRandomKey(), type: 'unstyled', text: '', characterList: List() })]; var fragment = BlockMapBuilder.createFromArray(fragmentArray); var withAtomicBlock = DraftModifier.replaceWithFragment(asAtomicBlock, insertionTarget, fragment); var newContent = withAtomicBlock.merge({ selectionBefore: selectionState, selectionAfter: withAtomicBlock.getSelectionAfter().set('hasFocus', true) }); return EditorState.push(editorState, newContent, 'insert-fragment'); }
n/a
function createFromArray(blocks) { return OrderedMap(blocks.map(function (block) { return [block.getKey(), block]; })); }
...
};
ContentState.createFromBlockArray = function createFromBlockArray(
// TODO: update flow type when we completely deprecate the old entity API
blocks, entityMap) {
// TODO: remove this when we completely deprecate the old entity API
var theBlocks = Array.isArray(blocks) ? blocks : blocks.contentBlocks;
var blockMap = BlockMapBuilder.createFromArray(theBlocks);
var selectionState = blockMap.isEmpty() ? new SelectionState() : SelectionState.createEmpty(blockMap.first().getKey());
return new ContentState({
blockMap: blockMap,
entityMap: entityMap || DraftEntity,
selectionBefore: selectionState,
selectionAfter: selectionState
});
...
function generate(contentState, block, decorator) { var textLength = block.getLength(); if (!textLength) { return List.of(new DecoratorRange({ start: 0, end: 0, decoratorKey: null, leaves: List.of(new LeafRange({ start: 0, end: 0 })) })); } var leafSets = []; var decorations = decorator ? decorator.getDecorations(block, contentState) : List(Repeat(null, textLength)); var chars = block.getCharacterList(); findRangesImmutable(decorations, areEqual, returnTrue, function (start, end) { leafSets.push(new DecoratorRange({ start: start, end: end, decoratorKey: decorations.get(start), leaves: generateLeaves(chars.slice(start, end).toList(), start) })); }); return List(leafSets); }
...
/**
* Regenerate the entire tree map for a given ContentState and decorator.
* Returns an OrderedMap that maps all available ContentBlock objects.
*/
function generateNewTreeMap(contentState, decorator) {
return contentState.getBlockMap().map(function (block) {
return BlockTree.generate(contentState, block, decorator);
}).toOrderedMap();
}
/**
* Regenerate tree map objects for all ContentBlocks that have changed
* between the current editorState and newContent. Returns an OrderedMap
* with only changed regenerated tree map objects.
...
function getFingerprint(tree) { return tree.map(function (leafSet) { var decoratorKey = leafSet.get('decoratorKey'); var fingerprintString = decoratorKey !== null ? decoratorKey + '.' + (leafSet.get('end') - leafSet.get('start')) : ''; return '' + fingerprintString + '.' + leafSet.get('leaves').size; }).join(FINGERPRINT_DELIMITER); }
...
var anchorKey = selection.getAnchorKey();
var anchorTree = editorState.getBlockTree(anchorKey);
// Check the old and new "fingerprints" of the current block to determine
// whether this insertion requires any addition or removal of text nodes,
// in which case we would prevent the native character insertion.
var originalFingerprint = BlockTree.getFingerprint(anchorTree);
var newFingerprint = BlockTree.getFingerprint(newEditorState.getBlockTree(anchorKey));
if (mustPreventDefaultForCharacter(chars) || originalFingerprint !== newFingerprint || nullthrows(newEditorState.getDirectionMap
()).get(anchorKey) !== nullthrows(editorState.getDirectionMap()).get(anchorKey)) {
e.preventDefault();
editor.update(newEditorState);
} else {
newEditorState = EditorState.set(newEditorState, {
...
function CharacterMetadata() { _classCallCheck(this, CharacterMetadata); return _possibleConstructorReturn(this, _CharacterMetadataRec.apply(this, arguments)); }
n/a
function applyEntity(record, entityKey) { var withEntity = record.getEntity() === entityKey ? record : record.set('entity', entityKey); return CharacterMetadata.create(withEntity); }
...
var start = _getRemovalRange.start;
var end = _getRemovalRange.end;
var current;
while (start < end) {
current = chars.get(start);
chars = chars.set(start, CharacterMetadata.applyEntity(current, null));
start++;
}
return block.set('characterList', chars);
}
}
return block;
...
function applyStyle(record, style) { var withStyle = record.set('style', record.getStyle().add(style)); return CharacterMetadata.create(withStyle); }
...
sliceEnd = blockKey === endKey ? endOffset : block.getLength();
}
var chars = block.getCharacterList();
var current;
while (sliceStart < sliceEnd) {
current = chars.get(sliceStart);
chars = chars.set(sliceStart, addOrRemove ? CharacterMetadata.applyStyle(current
, inlineStyle) : CharacterMetadata.removeStyle(current, inlineStyle));
sliceStart++;
}
return block.set('characterList', chars);
});
return contentState.merge({
...
function create(config) { if (!config) { return EMPTY; } var defaultConfig = { style: EMPTY_SET, entity: null }; // Fill in unspecified properties, if necessary. var configMap = Map(defaultConfig).merge(config); var existing = pool.get(configMap); if (existing) { return existing; } var newCharacter = new CharacterMetadata(configMap); pool = pool.set(configMap, newCharacter); return newCharacter; }
...
var EditorState = function () {
EditorState.createEmpty = function createEmpty(decorator) {
return EditorState.createWithContent(ContentState.createFromText(''), decorator);
};
EditorState.createWithContent = function createWithContent(contentState, decorator) {
var firstKey = contentState.getBlockMap().first().getKey();
return EditorState.create({
currentContent: contentState,
undoStack: Stack(),
redoStack: Stack(),
decorator: decorator || null,
selection: SelectionState.createEmpty(firstKey)
});
};
...
function removeStyle(record, style) { var withoutStyle = record.set('style', record.getStyle().remove(style)); return CharacterMetadata.create(withoutStyle); }
...
sliceEnd = blockKey === endKey ? endOffset : block.getLength();
}
var chars = block.getCharacterList();
var current;
while (sliceStart < sliceEnd) {
current = chars.get(sliceStart);
chars = chars.set(sliceStart, addOrRemove ? CharacterMetadata.applyStyle(current, inlineStyle) : CharacterMetadata.removeStyle(current, inlineStyle));
sliceStart++;
}
return block.set('characterList', chars);
});
return contentState.merge({
...
function getEntity() { return this.get('entity'); }
...
CharacterMetadata.removeStyle = function removeStyle(record, style) {
var withoutStyle = record.set('style', record.getStyle().remove(style));
return CharacterMetadata.create(withoutStyle);
};
CharacterMetadata.applyEntity = function applyEntity(record, entityKey) {
var withEntity = record.getEntity() === entityKey ? record : record.set('entity
', entityKey);
return CharacterMetadata.create(withEntity);
};
/**
* Use this function instead of the `CharacterMetadata` constructor.
* Since most content generally uses only a very small number of
* style/entity permutations, we can reuse these objects as often as
...
function getStyle() { return this.get('style'); }
...
};
CharacterMetadata.prototype.getEntity = function getEntity() {
return this.get('entity');
};
CharacterMetadata.prototype.hasStyle = function hasStyle(style) {
return this.getStyle().has(style);
};
CharacterMetadata.applyStyle = function applyStyle(record, style) {
var withStyle = record.set('style', record.getStyle().add(style));
return CharacterMetadata.create(withStyle);
};
...
function hasStyle(style) { return this.getStyle().has(style); }
n/a
function CompositeDraftDecorator(decorators) { _classCallCheck(this, CompositeDraftDecorator); // Copy the decorator array, since we use this array order to determine // precedence of decoration matching. If the array is mutated externally, // we don't want to be affected here. this._decorators = decorators.slice(); }
n/a
function getComponentForKey(key) { var componentKey = parseInt(key.split(DELIMITER)[0], 10); return this._decorators[componentKey].component; }
...
if (!_this2.props.decorator) {
return leaves;
}
var decorator = nullthrows(_this2.props.decorator);
var DecoratorComponent = decorator.getComponentForKey(decoratorKey);
if (!DecoratorComponent) {
return leaves;
}
var decoratorProps = decorator.getPropsForKey(decoratorKey);
var decoratorOffsetKey = DraftOffsetKey.encode(blockKey, ii, 0);
var decoratedText = text.slice(leavesForLeafSet.first().get('start'), leavesForLeafSet.last().get('end'
;));
...
function getDecorations(block, contentState) { var decorations = Array(block.getText().length).fill(null); this._decorators.forEach(function ( /*object*/decorator, /*number*/ii) { var counter = 0; var strategy = decorator.strategy; var callback = function callback( /*number*/start, /*number*/end) { // Find out if any of our matching range is already occupied // by another decorator. If so, discard the match. Otherwise, store // the component key for rendering. if (canOccupySlice(decorations, start, end)) { occupySlice(decorations, start, end, ii + DELIMITER + counter); counter++; } }; strategy(block, callback, contentState); }); return List(decorations); }
...
*
* Note that in order for this to perform optimally, decoration Lists for
* decorators should be preserved when possible to allow for direct immutable
* List comparison.
*/
function regenerateTreeForNewDecorator(content, blockMap, previousTreeMap, decorator, existingDecorator) {
return previousTreeMap.merge(blockMap.toSeq().filter(function (block) {
return decorator.getDecorations(block, content) !== existingDecorator.getDecorations
(block, content);
}).map(function (block) {
return BlockTree.generate(content, block, decorator);
}));
}
/**
* Return whether a change should be considered a boundary state, given
...
function getPropsForKey(key) { var componentKey = parseInt(key.split(DELIMITER)[0], 10); return this._decorators[componentKey].props; }
...
var decorator = nullthrows(_this2.props.decorator);
var DecoratorComponent = decorator.getComponentForKey(decoratorKey);
if (!DecoratorComponent) {
return leaves;
}
var decoratorProps = decorator.getPropsForKey(decoratorKey);
var decoratorOffsetKey = DraftOffsetKey.encode(blockKey, ii, 0);
var decoratedText = text.slice(leavesForLeafSet.first().get('start'), leavesForLeafSet.last().get('end'
;));
// Resetting dir to the same value on a child node makes Chrome/Firefox
// confused on cursor movement. See http://jsfiddle.net/d157kLck/3/
var dir = UnicodeBidiDirection.getHTMLDirIfDifferent(UnicodeBidi.getDirection(decoratedText), _this2.props.direction);
...
function ContentBlock() { _classCallCheck(this, ContentBlock); return _possibleConstructorReturn(this, _ContentBlockRecord.apply(this, arguments)); }
n/a
function findEntityRanges(filterFn, callback) { findRangesImmutable(this.getCharacterList(), haveEqualEntity, filterFn, callback); }
...
function convertFromDraftStateToRaw(contentState) {
var entityStorageKey = 0;
var entityStorageMap = {};
var rawBlocks = [];
contentState.getBlockMap().forEach(function (block, blockKey) {
block.findEntityRanges(function (character) {
return character.getEntity() !== null;
}, function (start) {
// Stringify to maintain order of otherwise numeric keys.
var stringifiedEntityKey = DraftStringKey.stringify(block.getEntityAt(start));
if (!entityStorageMap.hasOwnProperty(stringifiedEntityKey)) {
entityStorageMap[stringifiedEntityKey] = '' + entityStorageKey++;
}
...
function findStyleRanges(filterFn, callback) { findRangesImmutable(this.getCharacterList(), haveEqualStyle, filterFn, callback); }
n/a
function getCharacterList() { return this.get('characterList'); }
...
};
ContentBlock.prototype.getData = function getData() {
return this.get('data');
};
ContentBlock.prototype.getInlineStyleAt = function getInlineStyleAt(offset) {
var character = this.getCharacterList().get(offset);
return character ? character.getStyle() : EMPTY_SET;
};
ContentBlock.prototype.getEntityAt = function getEntityAt(offset) {
var character = this.getCharacterList().get(offset);
return character ? character.getEntity() : null;
};
...
function getData() { return this.get('data'); }
...
return modifyBlockForContentState(contentState, selectionState, function (block) {
return block.merge({ data: blockData });
});
},
mergeBlockData: function mergeBlockData(contentState, selectionState, blockData) {
return modifyBlockForContentState(contentState, selectionState, function (block) {
return block.merge({ data: block.getData().merge(blockData) });
});
},
applyEntity: function applyEntity(contentState, selectionState, entityKey) {
var withoutEntities = removeEntitiesAtEdges(contentState, selectionState);
return applyEntityToContentState(withoutEntities, selectionState, entityKey);
}
...
function getDepth() { return this.get('depth'); }
...
};
var configForType = blockRenderMap.get(blockType);
var wrapperTemplate = configForType.wrapper;
var _Element = configForType.element || blockRenderMap.get('unstyled').element;
var depth = _block.getDepth();
var className = this.props.blockStyleFn(_block);
// List items are special snowflakes, since we handle nesting and
// counters manually.
if (_Element === 'li') {
var shouldResetCount = lastWrapperTemplate !== wrapperTemplate || currentDepth === null || depth > currentDepth;
className = joinClasses(className, getListItemClasses(blockType, depth, shouldResetCount, direction));
...
function getEntityAt(offset) { var character = this.getCharacterList().get(offset); return character ? character.getEntity() : null; }
...
// If so, try to remove the appropriate substring of the entity text.
if (rangeToRemove.getAnchorKey() === rangeToRemove.getFocusKey()) {
var key = rangeToRemove.getAnchorKey();
var startOffset = rangeToRemove.getStartOffset();
var endOffset = rangeToRemove.getEndOffset();
var block = contentState.getBlockForKey(key);
var startEntity = block.getEntityAt(startOffset);
var endEntity = block.getEntityAt(endOffset - 1);
if (startEntity && startEntity === endEntity) {
var adjustedRemovalRange = getCharacterRemovalRange(contentState.getEntityMap(), block, rangeToRemove, removalDirection
);
return removeRangeFromContentState(contentState, adjustedRemovalRange);
}
}
...
function getInlineStyleAt(offset) { var character = this.getCharacterList().get(offset); return character ? character.getStyle() : EMPTY_SET; }
...
var startKey = selection.getStartKey();
var startOffset = selection.getStartOffset();
var startBlock = content.getBlockForKey(startKey);
// If the cursor is not at the start of the block, look backward to
// preserve the style of the preceding character.
if (startOffset > 0) {
return startBlock.getInlineStyleAt(startOffset - 1);
}
// The caret is at position zero in this block. If the block has any
// text at all, use the style of the first character.
if (startBlock.getLength()) {
return startBlock.getInlineStyleAt(0);
}
...
function getKey() { return this.get('key'); }
...
var EditorState = function () {
EditorState.createEmpty = function createEmpty(decorator) {
return EditorState.createWithContent(ContentState.createFromText(''), decorator);
};
EditorState.createWithContent = function createWithContent(contentState, decorator) {
var firstKey = contentState.getBlockMap().first().getKey();
return EditorState.create({
currentContent: contentState,
undoStack: Stack(),
redoStack: Stack(),
decorator: decorator || null,
selection: SelectionState.createEmpty(firstKey)
});
...
function getLength() { return this.getText().length; }
...
return this.getSelection().hasEdgeWithin(firstKey, 0, 0);
};
EditorState.prototype.isSelectionAtEndOfContent = function isSelectionAtEndOfContent() {
var content = this.getCurrentContent();
var blockMap = content.getBlockMap();
var last = blockMap.last();
var end = last.getLength();
return this.getSelection().hasEdgeWithin(last.getKey(), end, end);
};
EditorState.prototype.getDirectionMap = function getDirectionMap() {
return this.getImmutable().get('directionMap');
};
...
function getText() { return this.get('text'); }
...
};
ContentBlock.prototype.getCharacterList = function getCharacterList() {
return this.get('characterList');
};
ContentBlock.prototype.getLength = function getLength() {
return this.getText().length;
};
ContentBlock.prototype.getDepth = function getDepth() {
return this.get('depth');
};
ContentBlock.prototype.getData = function getData() {
...
function getType() { return this.get('type'); }
...
var processedBlocks = [];
var currentDepth = null;
var lastWrapperTemplate = null;
for (var ii = 0; ii < blocksAsArray.length; ii++) {
var _block = blocksAsArray[ii];
var key = _block.getKey();
var blockType = _block.getType();
var customRenderer = blockRendererFn(_block);
var CustomComponent = void 0,
customProps = void 0,
customEditable = void 0;
if (customRenderer) {
CustomComponent = customRenderer.component;
...
function ContentState() { _classCallCheck(this, ContentState); return _possibleConstructorReturn(this, _ContentStateRecord.apply(this, arguments)); }
n/a
function createFromBlockArray( // TODO: update flow type when we completely deprecate the old entity API blocks, entityMap) { // TODO: remove this when we completely deprecate the old entity API var theBlocks = Array.isArray(blocks) ? blocks : blocks.contentBlocks; var blockMap = BlockMapBuilder.createFromArray(theBlocks); var selectionState = blockMap.isEmpty() ? new SelectionState() : SelectionState.createEmpty(blockMap.first().getKey()); return new ContentState({ blockMap: blockMap, entityMap: entityMap || DraftEntity, selectionBefore: selectionState, selectionAfter: selectionState }); }
...
return new ContentBlock({
key: generateRandomKey(),
text: block,
type: 'unstyled',
characterList: List(Repeat(CharacterMetadata.EMPTY, block.length))
});
});
return ContentState.createFromBlockArray(blocks);
};
return ContentState;
}(ContentStateRecord);
module.exports = ContentState;
...
function createFromText(text) { var delimiter = arguments.length <= 1 || arguments[1] === undefined ? /\r\n?|\n/g : arguments[1]; var strings = text.split(delimiter); var blocks = strings.map(function (block) { block = sanitizeDraftText(block); return new ContentBlock({ key: generateRandomKey(), text: block, type: 'unstyled', characterList: List(Repeat(CharacterMetadata.EMPTY, block.length)) }); }); return ContentState.createFromBlockArray(blocks); }
...
undoStack: Stack()
};
var EditorStateRecord = Record(defaultRecord);
var EditorState = function () {
EditorState.createEmpty = function createEmpty(decorator) {
return EditorState.createWithContent(ContentState.createFromText(''),
decorator);
};
EditorState.createWithContent = function createWithContent(contentState, decorator) {
var firstKey = contentState.getBlockMap().first().getKey();
return EditorState.create({
currentContent: contentState,
undoStack: Stack(),
...
function addEntity(instance) { // TODO: update this when we fully remove DraftEntity DraftEntity.__add(instance); return this; }
n/a
function createEntity(type, mutability, data) { // TODO: update this when we fully remove DraftEntity DraftEntity.__create(type, mutability, data); return this; }
n/a
function getBlockAfter(key) { return this.getBlockMap().skipUntil(function (_, k) { return k === key; }).skip(1).first(); }
...
var length = block.getLength();
// The cursor is somewhere within the text. Behave normally.
if (selection.getStartOffset() < length) {
return null;
}
var blockAfter = content.getBlockAfter(startKey);
if (!blockAfter || blockAfter.getType() !== 'atomic') {
return null;
}
var atomicBlockTarget = selection.merge({
focusKey: blockAfter.getKey(),
...
function getBlockBefore(key) { return this.getBlockMap().reverse().skipUntil(function (_, k) { return k === key; }).skip(1).first(); }
...
}
// Otherwise, look upward in the document to find the closest character.
return lookUpwardForInlineStyle(content, startKey);
}
function lookUpwardForInlineStyle(content, fromKey) {
var previousBlock = content.getBlockBefore(fromKey);
var previousLength;
while (previousBlock) {
previousLength = previousBlock.getLength();
if (previousLength) {
return previousBlock.getInlineStyleAt(previousLength - 1);
}
...
function getBlockForKey(key) { var block = this.getBlockMap().get(key); return block; }
...
var lastChangeType = editorState.getLastChangeType();
return changeType !== lastChangeType || changeType !== 'insert-characters' && changeType !== 'backspace
-character' && changeType !== 'delete-character';
}
function getInlineStyleForCollapsedSelection(content, selection) {
var startKey = selection.getStartKey();
var startOffset = selection.getStartOffset();
var startBlock = content.getBlockForKey(startKey);
// If the cursor is not at the start of the block, look backward to
// preserve the style of the preceding character.
if (startOffset > 0) {
return startBlock.getInlineStyleAt(startOffset - 1);
}
...
function getBlockMap() { return this.get('blockMap'); }
...
var EditorState = function () {
EditorState.createEmpty = function createEmpty(decorator) {
return EditorState.createWithContent(ContentState.createFromText(''), decorator);
};
EditorState.createWithContent = function createWithContent(contentState, decorator) {
var firstKey = contentState.getBlockMap().first().getKey();
return EditorState.create({
currentContent: contentState,
undoStack: Stack(),
redoStack: Stack(),
decorator: decorator || null,
selection: SelectionState.createEmpty(firstKey)
});
...
function getBlocksAsArray() { return this.getBlockMap().toArray(); }
...
var content = editorState.getCurrentContent();
var selection = editorState.getSelection();
var forceSelection = editorState.mustForceSelection();
var decorator = editorState.getDecorator();
var directionMap = nullthrows(editorState.getDirectionMap());
var blocksAsArray = content.getBlocksAsArray();
var processedBlocks = [];
var currentDepth = null;
var lastWrapperTemplate = null;
for (var ii = 0; ii < blocksAsArray.length; ii++) {
var _block = blocksAsArray[ii];
var key = _block.getKey();
...
function getEntity(key) { // TODO: update this when we fully remove DraftEntity return DraftEntity.__get(key); }
...
CharacterMetadata.removeStyle = function removeStyle(record, style) {
var withoutStyle = record.set('style', record.getStyle().remove(style));
return CharacterMetadata.create(withoutStyle);
};
CharacterMetadata.applyEntity = function applyEntity(record, entityKey) {
var withEntity = record.getEntity() === entityKey ? record : record.set('entity
', entityKey);
return CharacterMetadata.create(withEntity);
};
/**
* Use this function instead of the `CharacterMetadata` constructor.
* Since most content generally uses only a very small number of
* style/entity permutations, we can reuse these objects as often as
...
function getEntityMap() { // TODO: update this when we fully remove DraftEntity return DraftEntity; }
...
nativelyRenderedContent: null
});
return;
}
var existingContent = editorState.getCurrentContent();
if (newContent !== existingContent) {
state.set('treeMap', regenerateTreeForNewBlocks(editorState, newContent.getBlockMap(), newContent.getEntityMap(), decorator));
}
state.merge(put);
});
return new EditorState(map);
};
...
function getFirstBlock() { return this.getBlockMap().first(); }
...
* Match Chrome `<textarea>` behavior during <kbd>cmd</kbd>+<kbd>backspace</kbd>
;
command at visual line-start
## 0.6.0 (April 27, 2016)
### Added
* `ContentState.getFirstBlock()` convenience method
### Changed
* <kbd>return</kbd> key handling now goes through command flow to enable easier
custom `'split-block'` handling.
* `convertFromRaw` now returns a `ContentState` object instead of an
`Array<ContentBlock>`
...
function getKeyAfter(key) { return this.getBlockMap().keySeq().skipUntil(function (v) { return v === key; }).skip(1).first(); }
...
var focusKey = key;
var focusOffset;
var block = content.getBlockForKey(key);
if (maxDistance > block.getText().length - offset) {
focusKey = content.getKeyAfter(key);
focusOffset = 0;
} else {
focusOffset = offset + maxDistance;
}
return selection.merge({ focusKey: focusKey, focusOffset: focusOffset });
}
...
function getKeyBefore(key) { return this.getBlockMap().reverse().keySeq().skipUntil(function (v) { return v === key; }).skip(1).first(); }
...
var key = selection.getStartKey();
var offset = selection.getStartOffset();
var focusKey = key;
var focusOffset = 0;
if (maxDistance > offset) {
var keyBefore = content.getKeyBefore(key);
if (keyBefore == null) {
focusKey = key;
} else {
focusKey = keyBefore;
var blockBefore = content.getBlockForKey(keyBefore);
focusOffset = blockBefore.getText().length;
}
...
function getLastBlock() { return this.getBlockMap().last(); }
...
/**
* Move selection to the end of the editor without forcing focus.
*/
EditorState.moveSelectionToEnd = function moveSelectionToEnd(editorState) {
var content = editorState.getCurrentContent();
var lastBlock = content.getLastBlock();
var lastKey = lastBlock.getKey();
var length = lastBlock.getLength();
return EditorState.acceptSelection(editorState, new SelectionState({
anchorKey: lastKey,
anchorOffset: length,
focusKey: lastKey,
...
function getLastCreatedEntityKey() { // TODO: update this when we fully remove DraftEntity return DraftEntity.__getLastCreatedEntityKey(); }
n/a
function getPlainText(delimiter) { return this.getBlockMap().map(function (block) { return block ? block.getText() : ''; }).join(delimiter || '\n'); }
n/a
function getSelectionAfter() { return this.get('selectionAfter'); }
...
var directionMap = EditorBidiService.getDirectionMap(contentState, editorState.getDirectionMap());
if (!editorState.getAllowUndo()) {
return EditorState.set(editorState, {
currentContent: contentState,
directionMap: directionMap,
lastChangeType: changeType,
selection: contentState.getSelectionAfter(),
forceSelection: forceSelection,
inlineStyleOverride: null
});
}
var selection = editorState.getSelection();
var currentContent = editorState.getCurrentContent();
...
function getSelectionBefore() { return this.get('selectionBefore'); }
...
var newContent = contentState;
if (selection !== currentContent.getSelectionAfter() || mustBecomeBoundary(editorState, changeType)) {
undoStack = undoStack.push(currentContent);
newContent = newContent.set('selectionBefore', selection);
} else if (changeType === 'insert-characters' || changeType === 'backspace-character' || changeType ===
x27;delete-character') {
// Preserve the previous selection.
newContent = newContent.set('selectionBefore', currentContent.getSelectionBefore
());
}
var inlineStyleOverride = editorState.getInlineStyleOverride();
// Don't discard inline style overrides on block type or depth changes.
if (changeType !== 'adjust-depth' && changeType !== 'change-block-type') {
inlineStyleOverride = null;
...
function hasText() { var blockMap = this.getBlockMap(); return blockMap.size > 1 || blockMap.first().getLength() > 0; }
...
var method = _this2._handler && _this2._handler[eventName];
method && method(_this2, e);
}
};
};
DraftEditor.prototype._showPlaceholder = function _showPlaceholder() {
return !!this.props.placeholder && !this.props.editorState.isInCompositionMode() && !this.props.editorState
.getCurrentContent().hasText();
};
DraftEditor.prototype._renderPlaceholder = function _renderPlaceholder() {
if (this._showPlaceholder()) {
return React.createElement(DraftEditorPlaceholder, {
text: nullthrows(this.props.placeholder),
editorState: this.props.editorState,
...
function mergeEntityData(key, toMerge) { // TODO: update this when we fully remove DraftEntity DraftEntity.__mergeData(key, toMerge); return this; }
n/a
function replaceEntityData(key, newData) { // TODO: update this when we fully remove DraftEntity DraftEntity.__replaceData(key, newData); return this; }
n/a
function add(contentState, selectionState, inlineStyle) { return modifyInlineStyle(contentState, selectionState, inlineStyle, true); }
...
var withoutEntities = removeEntitiesAtEdges(contentState, selectionState);
var withoutText = removeRangeFromContentState(withoutEntities, selectionState);
return splitBlockInContentState(withoutText, withoutText.getSelectionAfter());
},
applyInlineStyle: function applyInlineStyle(contentState, selectionState, inlineStyle) {
return ContentStateInlineStyle.add(contentState, selectionState, inlineStyle);
},
removeInlineStyle: function removeInlineStyle(contentState, selectionState, inlineStyle) {
return ContentStateInlineStyle.remove(contentState, selectionState, inlineStyle);
},
setBlockType: function setBlockType(contentState, selectionState, blockType) {
...
function remove(contentState, selectionState, inlineStyle) { return modifyInlineStyle(contentState, selectionState, inlineStyle, false); }
...
},
applyInlineStyle: function applyInlineStyle(contentState, selectionState, inlineStyle) {
return ContentStateInlineStyle.add(contentState, selectionState, inlineStyle);
},
removeInlineStyle: function removeInlineStyle(contentState, selectionState, inlineStyle) {
return ContentStateInlineStyle.remove(contentState, selectionState, inlineStyle);
},
setBlockType: function setBlockType(contentState, selectionState, blockType) {
return modifyBlockForContentState(contentState, selectionState, function (block) {
return block.merge({ type: blockType, depth: 0 });
});
},
...
function onBeforeInput(editor, e) { textInputData = (textInputData || '') + e.data; }
n/a
function onCompositionEnd(editor) { resolved = false; stillComposing = false; setTimeout(function () { if (!resolved) { DraftEditorCompositionHandler.resolveComposition(editor); } }, RESOLVE_DELAY); }
n/a
function onCompositionStart(editor) { stillComposing = true; }
n/a
function onKeyDown(editor, e) { if (!stillComposing) { // If a keydown event is received after compositionend but before the // 20ms timer expires (ex: type option-E then backspace, or type A then // backspace in 2-Set Korean), we should immediately resolve the // composition and reinterpret the key press in edit mode. DraftEditorCompositionHandler.resolveComposition(editor); editor._onKeyDown(e); return; } if (e.which === Keys.RIGHT || e.which === Keys.LEFT) { e.preventDefault(); } }
n/a
function onKeyPress(editor, e) { if (e.which === Keys.RETURN) { e.preventDefault(); } }
n/a
function resolveComposition(editor) { if (stillComposing) { return; } resolved = true; var composedChars = textInputData; textInputData = ''; var editorState = EditorState.set(editor._latestEditorState, { inCompositionMode: false }); var currentStyle = editorState.getCurrentInlineStyle(); var entityKey = getEntityKeyForSelection(editorState.getCurrentContent(), editorState.getSelection()); var mustReset = !composedChars || isSelectionAtLeafStart(editorState) || currentStyle.size > 0 || entityKey !== null; if (mustReset) { editor.restoreEditorDOM(); } editor.exitCurrentMode(); if (composedChars) { // If characters have been composed, re-rendering with the update // is sufficient to reset the editor. var contentState = DraftModifier.replaceText(editorState.getCurrentContent(), editorState.getSelection(), composedChars, currentStyle , entityKey); editor.update(EditorState.push(editorState, contentState, 'insert-characters')); return; } if (mustReset) { editor.update(EditorState.set(editorState, { nativelyRenderedContent: null, forceSelection: true })); } }
...
* Google Input Tools on Windows 8.1 fires `compositionend` three times.
*/
onCompositionEnd: function onCompositionEnd(editor) {
resolved = false;
stillComposing = false;
setTimeout(function () {
if (!resolved) {
DraftEditorCompositionHandler.resolveComposition(editor);
}
}, RESOLVE_DELAY);
},
/**
* In Safari, keydown events may fire when committing compositions. If
* the arrow keys are used to commit, prevent default so that the cursor
...
function DraftEditorContents() { _classCallCheck(this, DraftEditorContents); return _possibleConstructorReturn(this, _React$Component.apply(this, arguments)); }
n/a
function render() { var _props = this.props; var blockRenderMap = _props.blockRenderMap; var blockRendererFn = _props.blockRendererFn; var customStyleMap = _props.customStyleMap; var customStyleFn = _props.customStyleFn; var editorState = _props.editorState; var content = editorState.getCurrentContent(); var selection = editorState.getSelection(); var forceSelection = editorState.mustForceSelection(); var decorator = editorState.getDecorator(); var directionMap = nullthrows(editorState.getDirectionMap()); var blocksAsArray = content.getBlocksAsArray(); var processedBlocks = []; var currentDepth = null; var lastWrapperTemplate = null; for (var ii = 0; ii < blocksAsArray.length; ii++) { var _block = blocksAsArray[ii]; var key = _block.getKey(); var blockType = _block.getType(); var customRenderer = blockRendererFn(_block); var CustomComponent = void 0, customProps = void 0, customEditable = void 0; if (customRenderer) { CustomComponent = customRenderer.component; customProps = customRenderer.props; customEditable = customRenderer.editable; } var direction = directionMap.get(key); var offsetKey = DraftOffsetKey.encode(key, 0, 0); var componentProps = { contentState: content, block: _block, blockProps: customProps, customStyleMap: customStyleMap, customStyleFn: customStyleFn, decorator: decorator, direction: direction, forceSelection: forceSelection, key: key, offsetKey: offsetKey, selection: selection, tree: editorState.getBlockTree(key) }; var configForType = blockRenderMap.get(blockType); var wrapperTemplate = configForType.wrapper; var _Element = configForType.element || blockRenderMap.get('unstyled').element; var depth = _block.getDepth(); var className = this.props.blockStyleFn(_block); // List items are special snowflakes, since we handle nesting and // counters manually. if (_Element === 'li') { var shouldResetCount = lastWrapperTemplate !== wrapperTemplate || currentDepth === null || depth > currentDepth; className = joinClasses(className, getListItemClasses(blockType, depth, shouldResetCount, direction)); } var Component = CustomComponent || DraftEditorBlock; var childProps = { className: className, 'data-block': true, 'data-editor': this.props.editorKey, 'data-offset-key': offsetKey, key: key }; if (customEditable !== undefined) { childProps = _extends({}, childProps, { contentEditable: customEditable, suppressContentEditableWarning: true }); } var child = React.createElement(_Element, childProps, React.createElement(Component, componentProps)); processedBlocks.push({ block: child, wrapperTemplate: wrapperTemplate, key: key, offsetKey: offsetKey }); if (wrapperTemplate) { currentDepth = _block.getDepth(); } else { currentDepth = null; } lastWrapperTemplate = wrapperTemplate; } // Group contiguous runs of blocks that have the same wrapperTemplate var outputBlocks = []; for (var _ii = 0; _ii < processedBlocks.length;) { var info = processedBlocks[_ii]; if (info.wrapperTemplate) { var blocks = []; do { blocks.push(processedBlocks[_ii].block); _ii++; } while (_ii < processedBlocks.length && processedBlocks[_ii].wrapperTemplate === info.wrapperTemplate); var wrapperElement = React.cloneElement(info.wrapperTemplate, { key: info.key + '-wrap', 'data-offset-key': info.offsetKey }, blocks); outputBlocks.push(wrapperElement); } else { outputBlocks.push(info.block); _ii++; } } return React.createElement( 'div', { 'data-contents': 'true' }, outputBlocks ); }
...
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange} />
);
}
}
ReactDOM.render(
<MyEditor />,
document.getElementById('container')
);
```
Because Draft.js supports unicode, you must have the following meta tag in the `<head>` `</head>` block of your
HTML file:
...
function shouldComponentUpdate(nextProps) { var prevEditorState = this.props.editorState; var nextEditorState = nextProps.editorState; var prevDirectionMap = prevEditorState.getDirectionMap(); var nextDirectionMap = nextEditorState.getDirectionMap(); // Text direction has changed for one or more blocks. We must re-render. if (prevDirectionMap !== nextDirectionMap) { return true; } var didHaveFocus = prevEditorState.getSelection().getHasFocus(); var nowHasFocus = nextEditorState.getSelection().getHasFocus(); if (didHaveFocus !== nowHasFocus) { return true; } var nextNativeContent = nextEditorState.getNativelyRenderedContent(); var wasComposing = prevEditorState.isInCompositionMode(); var nowComposing = nextEditorState.isInCompositionMode(); // If the state is unchanged or we're currently rendering a natively // rendered state, there's nothing new to be done. if (prevEditorState === nextEditorState || nextNativeContent !== null && nextEditorState.getCurrentContent() === nextNativeContent || wasComposing && nowComposing) { return false; } var prevContent = prevEditorState.getCurrentContent(); var nextContent = nextEditorState.getCurrentContent(); var prevDecorator = prevEditorState.getDecorator(); var nextDecorator = nextEditorState.getDecorator(); return wasComposing !== nowComposing || prevContent !== nextContent || prevDecorator !== nextDecorator || nextEditorState.mustForceSelection (); }
n/a
function onDragEnd(editor) { editor.exitCurrentMode(); }
n/a
function onDrop(editor, e) { var data = new DataTransfer(e.nativeEvent.dataTransfer); var editorState = editor._latestEditorState; var dropSelection = getSelectionForEvent(e.nativeEvent, editorState); e.preventDefault(); editor.exitCurrentMode(); if (dropSelection == null) { return; } var files = data.getFiles(); if (files.length > 0) { if (editor.props.handleDroppedFiles && isEventHandled(editor.props.handleDroppedFiles(dropSelection, files))) { return; } getTextContentFromFiles(files, function (fileText) { fileText && editor.update(insertTextAtSelection(editorState, nullthrows(dropSelection), // flow wtf fileText)); }); return; } var dragType = editor._internalDrag ? 'internal' : 'external'; if (editor.props.handleDrop && isEventHandled(editor.props.handleDrop(dropSelection, data, dragType))) { return; } if (editor._internalDrag) { editor.update(moveText(editorState, dropSelection)); return; } editor.update(insertTextAtSelection(editorState, dropSelection, data.getText())); }
n/a
function editOnBeforeInput(editor, e) { if (editor._pendingStateFromBeforeInput !== undefined) { editor.update(editor._pendingStateFromBeforeInput); editor._pendingStateFromBeforeInput = undefined; } var chars = e.data; // In some cases (ex: IE ideographic space insertion) no character data // is provided. There's nothing to do when this happens. if (!chars) { return; } // Allow the top-level component to handle the insertion manually. This is // useful when triggering interesting behaviors for a character insertion, // Simple examples: replacing a raw text ':)' with a smile emoji or image // decorator, or setting a block to be a list item after typing '- ' at the // start of the block. if (editor.props.handleBeforeInput && isEventHandled(editor.props.handleBeforeInput(chars))) { e.preventDefault(); return; } // If selection is collapsed, conditionally allow native behavior. This // reduces re-renders and preserves spellcheck highlighting. If the selection // is not collapsed, we will re-render. var editorState = editor._latestEditorState; var selection = editorState.getSelection(); if (!selection.isCollapsed()) { e.preventDefault(); editor.update(replaceText(editorState, chars, editorState.getCurrentInlineStyle(), getEntityKeyForSelection(editorState.getCurrentContent (), editorState.getSelection()))); return; } var mayAllowNative = !isSelectionAtLeafStart(editorState); var newEditorState = replaceText(editorState, chars, editorState.getCurrentInlineStyle(), getEntityKeyForSelection(editorState .getCurrentContent(), editorState.getSelection())); if (!mayAllowNative) { e.preventDefault(); editor.update(newEditorState); return; } var anchorKey = selection.getAnchorKey(); var anchorTree = editorState.getBlockTree(anchorKey); // Check the old and new "fingerprints" of the current block to determine // whether this insertion requires any addition or removal of text nodes, // in which case we would prevent the native character insertion. var originalFingerprint = BlockTree.getFingerprint(anchorTree); var newFingerprint = BlockTree.getFingerprint(newEditorState.getBlockTree(anchorKey)); if (mustPreventDefaultForCharacter(chars) || originalFingerprint !== newFingerprint || nullthrows(newEditorState.getDirectionMap ()).get(anchorKey) !== nullthrows(editorState.getDirectionMap()).get(anchorKey)) { e.preventDefault(); editor.update(newEditorState); } else { newEditorState = EditorState.set(newEditorState, { nativelyRenderedContent: newEditorState.getCurrentContent() }); // The native event is allowed to occur. To allow user onChange handlers to // change the inserted text, we wait until the text is actually inserted // before we actually update our state. That way when we rerender, the text // we see in the DOM will already have been inserted properly. editor._pendingStateFromBeforeInput = newEditorState; setImmediate(function () { if (editor._pendingStateFromBeforeInput !== undefined) { editor.update(editor._pendingStateFromBeforeInput); editor._pendingStateFromBeforeInput = undefined; } }); } }
n/a
function editOnBlur(editor, e) { // Webkit has a bug in which blurring a contenteditable by clicking on // other active elements will trigger the `blur` event but will not remove // the DOM selection from the contenteditable. We therefore force the // issue to be certain, checking whether the active element is `body` // to force it when blurring occurs within the window (as opposed to // clicking to another tab or window). if (isWebKit && getActiveElement() === document.body) { global.getSelection().removeAllRanges(); } var editorState = editor._latestEditorState; var currentSelection = editorState.getSelection(); if (!currentSelection.getHasFocus()) { return; } var selection = currentSelection.set('hasFocus', false); editor.props.onBlur && editor.props.onBlur(e); editor.update(EditorState.acceptSelection(editorState, selection)); }
...
var editorState = editor._latestEditorState;
var currentSelection = editorState.getSelection();
if (!currentSelection.getHasFocus()) {
return;
}
var selection = currentSelection.set('hasFocus', false);
editor.props.onBlur && editor.props.onBlur(e);
editor.update(EditorState.acceptSelection(editorState, selection));
}
module.exports = editOnBlur;
...
function editOnCompositionStart(editor, e) { editor.setMode('composite'); editor.update(EditorState.set(editor._latestEditorState, { inCompositionMode: true })); // Allow composition handler to interpret the compositionstart event editor._onCompositionStart(e); }
n/a
function editOnCopy(editor, e) { var editorState = editor._latestEditorState; var selection = editorState.getSelection(); // No selection, so there's nothing to copy. if (selection.isCollapsed()) { e.preventDefault(); return; } editor.setClipboard(getFragmentFromSelection(editor._latestEditorState)); }
n/a
function editOnCut(editor, e) { var editorState = editor._latestEditorState; var selection = editorState.getSelection(); // No selection, so there's nothing to cut. if (selection.isCollapsed()) { e.preventDefault(); return; } // Track the current scroll position so that it can be forced back in place // after the editor regains control of the DOM. var scrollParent = Style.getScrollParent(e.target); var _getScrollPosition = getScrollPosition(scrollParent); var x = _getScrollPosition.x; var y = _getScrollPosition.y; var fragment = getFragmentFromSelection(editorState); editor.setClipboard(fragment); // Set `cut` mode to disable all event handling temporarily. editor.setMode('cut'); // Let native `cut` behavior occur, then recover control. setTimeout(function () { editor.restoreEditorDOM({ x: x, y: y }); editor.exitCurrentMode(); editor.update(removeFragment(editorState)); }, 0); }
n/a
function editOnDragOver(editor, e) { editor._internalDrag = false; editor.setMode('drag'); e.preventDefault(); }
n/a
function editOnDragStart(editor) { editor._internalDrag = true; editor.setMode('drag'); }
n/a
function editOnFocus(editor, e) { var editorState = editor._latestEditorState; var currentSelection = editorState.getSelection(); if (currentSelection.getHasFocus()) { return; } var selection = currentSelection.set('hasFocus', true); editor.props.onFocus && editor.props.onFocus(e); // When the tab containing this text editor is hidden and the user does a // find-in-page in a _different_ tab, Chrome on Mac likes to forget what the // selection was right after sending this focus event and (if you let it) // moves the cursor back to the beginning of the editor, so we force the // selection here instead of simply accepting it in order to preserve the // old cursor position. See https://crbug.com/540004. editor.update(EditorState.forceSelection(editorState, selection)); }
...
var editorState = editor._latestEditorState;
var currentSelection = editorState.getSelection();
if (currentSelection.getHasFocus()) {
return;
}
var selection = currentSelection.set('hasFocus', true);
editor.props.onFocus && editor.props.onFocus(e);
// When the tab containing this text editor is hidden and the user does a
// find-in-page in a _different_ tab, Chrome on Mac likes to forget what the
// selection was right after sending this focus event and (if you let it)
// moves the cursor back to the beginning of the editor, so we force the
// selection here instead of simply accepting it in order to preserve the
// old cursor position. See https://crbug.com/540004.
...
function editOnInput(editor) { if (editor._pendingStateFromBeforeInput !== undefined) { editor.update(editor._pendingStateFromBeforeInput); editor._pendingStateFromBeforeInput = undefined; } var domSelection = global.getSelection(); var anchorNode = domSelection.anchorNode; var isCollapsed = domSelection.isCollapsed; if (anchorNode.nodeType !== Node.TEXT_NODE) { return; } var domText = anchorNode.textContent; var editorState = editor._latestEditorState; var offsetKey = nullthrows(findAncestorOffsetKey(anchorNode)); var _DraftOffsetKey$decod = DraftOffsetKey.decode(offsetKey); var blockKey = _DraftOffsetKey$decod.blockKey; var decoratorKey = _DraftOffsetKey$decod.decoratorKey; var leafKey = _DraftOffsetKey$decod.leafKey; var _editorState$getBlock = editorState.getBlockTree(blockKey).getIn([decoratorKey, 'leaves', leafKey]); var start = _editorState$getBlock.start; var end = _editorState$getBlock.end; var content = editorState.getCurrentContent(); var block = content.getBlockForKey(blockKey); var modelText = block.getText().slice(start, end); // Special-case soft newlines here. If the DOM text ends in a soft newline, // we will have manually inserted an extra soft newline in DraftEditorLeaf. // We want to remove this extra newline for the purpose of our comparison // of DOM and model text. if (domText.endsWith(DOUBLE_NEWLINE)) { domText = domText.slice(0, -1); } // No change -- the DOM is up to date. Nothing to do here. if (domText === modelText) { return; } var selection = editorState.getSelection(); // We'll replace the entire leaf with the text content of the target. var targetRange = selection.merge({ anchorOffset: start, focusOffset: end, isBackward: false }); var entityKey = block.getEntityAt(start); var entity = entityKey && content.getEntity(entityKey); var entityType = entity && entity.getMutability(); var preserveEntity = entityType === 'MUTABLE'; // Immutable or segmented entities cannot properly be handled by the // default browser undo, so we have to use a different change type to // force using our internal undo method instead of falling through to the // native browser undo. var changeType = preserveEntity ? 'spellcheck-change' : 'apply-entity'; var newContent = DraftModifier.replaceText(content, targetRange, domText, block.getInlineStyleAt(start), preserveEntity ? block .getEntityAt(start) : null); var anchorOffset, focusOffset, startOffset, endOffset; if (isGecko) { // Firefox selection does not change while the context menu is open, so // we preserve the anchor and focus values of the DOM selection. anchorOffset = domSelection.anchorOffset; focusOffset = domSelection.focusOffset; startOffset = start + Math.min(anchorOffset, focusOffset); endOffset = startOffset + Math.abs(anchorOffset - focusOffset); anchorOffset = startOffset; focusOffset = endOffset; } else { // Browsers other than Firefox may adjust DOM selection while the context // menu is open, and Safari autocorrect is prone to providing an inaccurate // DOM selection. Don't trust it. Instead, use our existing SelectionState // and adjust it based on the number of characters changed during the // mutation. var charDelta = domText.length - modelText.length; startOffset = selection.getStartOffset(); endOffset = selection.getEndOffset(); anchorOffset = isCollapsed ? endOffset + charDelta : startOffset; focusOffset = endOffset + charDelta; } // Segmented entities are completely or partially removed when their // text content changes. For this case we do not want any text to be selected // after the change, so we are not merging the selection. var contentWithAdjustedDOMSelection = newContent.merge({ selectionBefore: content.getSelectionAfter(), selectionAfter: selection.merge({ anchorOffset: anchorOffset, focusOffset: focusOffset }) }); editor.update(EditorState.push(editorState, contentWithAdjustedDOMSelection, chang ...
n/a
function editOnKeyDown(editor, e) { var keyCode = e.which; var editorState = editor._latestEditorState; switch (keyCode) { case Keys.RETURN: e.preventDefault(); // The top-level component may manually handle newline insertion. If // no special handling is performed, fall through to command handling. if (editor.props.handleReturn && isEventHandled(editor.props.handleReturn(e))) { return; } break; case Keys.ESC: e.preventDefault(); editor.props.onEscape && editor.props.onEscape(e); return; case Keys.TAB: editor.props.onTab && editor.props.onTab(e); return; case Keys.UP: editor.props.onUpArrow && editor.props.onUpArrow(e); return; case Keys.DOWN: editor.props.onDownArrow && editor.props.onDownArrow(e); return; case Keys.SPACE: // Handling for OSX where option + space scrolls. if (isChrome && isOptionKeyCommand(e)) { e.preventDefault(); // Insert a nbsp into the editor. var contentState = DraftModifier.replaceText(editorState.getCurrentContent(), editorState.getSelection(), '\xA0'); editor.update(EditorState.push(editorState, contentState, 'insert-characters')); return; } } var command = editor.props.keyBindingFn(e); // If no command is specified, allow keydown event to continue. if (!command) { return; } if (command === 'undo') { // Since undo requires some special updating behavior to keep the editor // in sync, handle it separately. keyCommandUndo(e, editorState, editor.update); return; } // At this point, we know that we're handling a command of some kind, so // we don't want to insert a character following the keydown. e.preventDefault(); // Allow components higher up the tree to handle the command first. if (editor.props.handleKeyCommand && isEventHandled(editor.props.handleKeyCommand(command))) { return; } var newState = onKeyCommand(command, editorState); if (newState !== editorState) { editor.update(newState); } }
n/a
function editOnPaste(editor, e) { e.preventDefault(); var data = new DataTransfer(e.clipboardData); // Get files, unless this is likely to be a string the user wants inline. if (!data.isRichText()) { var files = data.getFiles(); var defaultFileText = data.getText(); if (files.length > 0) { // Allow customized paste handling for images, etc. Otherwise, fall // through to insert text contents into the editor. if (editor.props.handlePastedFiles && isEventHandled(editor.props.handlePastedFiles(files))) { return; } getTextContentFromFiles(files, function ( /*string*/fileText) { fileText = fileText || defaultFileText; if (!fileText) { return; } var editorState = editor._latestEditorState; var blocks = splitTextIntoTextBlocks(fileText); var character = CharacterMetadata.create({ style: editorState.getCurrentInlineStyle(), entity: getEntityKeyForSelection(editorState.getCurrentContent(), editorState.getSelection()) }); var text = DraftPasteProcessor.processText(blocks, character); var fragment = BlockMapBuilder.createFromArray(text); var withInsertedText = DraftModifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), fragment ); editor.update(EditorState.push(editorState, withInsertedText, 'insert-fragment')); }); return; } } var textBlocks = []; var text = data.getText(); var html = data.getHTML(); if (editor.props.handlePastedText && isEventHandled(editor.props.handlePastedText(text, html))) { return; } if (text) { textBlocks = splitTextIntoTextBlocks(text); } if (!editor.props.stripPastedStyles) { // If the text from the paste event is rich content that matches what we // already have on the internal clipboard, assume that we should just use // the clipboard fragment for the paste. This will allow us to preserve // styling and entities, if any are present. Note that newlines are // stripped during comparison -- this is because copy/paste within the // editor in Firefox and IE will not include empty lines. The resulting // paste will preserve the newlines correctly. var internalClipboard = editor.getClipboard(); if (data.isRichText() && internalClipboard) { if ( // If the editorKey is present in the pasted HTML, it should be safe to // assume this is an internal paste. html.indexOf(editor.getEditorKey()) !== -1 || // The copy may have been made within a single block, in which case the // editor key won't be part of the paste. In this case, just check // whether the pasted text matches the internal clipboard. textBlocks.length === 1 && internalClipboard.size === 1 && internalClipboard.first().getText() === text) { editor.update(insertFragment(editor._latestEditorState, internalClipboard)); return; } } else if (internalClipboard && data.types.includes('com.apple.webarchive') && !data.types.includes('text/html') && areTextBlocksAndClipboardEqual (textBlocks, internalClipboard)) { // Safari does not properly store text/html in some cases. // Use the internalClipboard if present and equal to what is on // the clipboard. See https://bugs.webkit.org/show_bug.cgi?id=19893. editor.update(insertFragment(editor._latestEditorState, internalClipboard)); return; } // If there is html paste data, try to parse that. if (html) { var htmlFragment = DraftPasteProcessor.processHTML(html, editor.props.blockRenderMap); if (htmlFragment) { var contentBlocks = htmlFragment.contentBlocks; var entityMap = htmlFragment.entityMap; if (contentBlocks) { var htmlMap = BlockMapBuilder.createFromArray(contentBlocks); editor.update(insertFragment(editor._latestEditorState, htmlMap, entityMap)); return; } } } // Otherwise, create a new fragment from our pasted text. ...
n/a
function editOnSelect(editor) { if (editor._blockSelectEvents || editor._latestEditorState !== editor.props.editorState) { return; } var editorState = editor.props.editorState; var documentSelection = getDraftEditorSelection(editorState, ReactDOM.findDOMNode(editor.refs.editorContainer).firstChild); var updatedSelectionState = documentSelection.selectionState; if (updatedSelectionState !== editorState.getSelection()) { if (documentSelection.needsRecovery) { editorState = EditorState.forceSelection(editorState, updatedSelectionState); } else { editorState = EditorState.acceptSelection(editorState, updatedSelectionState); } editor.update(editorState); } }
n/a
function DraftEditorLeaf() { _classCallCheck(this, DraftEditorLeaf); return _possibleConstructorReturn(this, _React$Component.apply(this, arguments)); }
n/a
function _setSelection() { var selection = this.props.selection; // If selection state is irrelevant to the parent block, no-op. if (selection == null || !selection.getHasFocus()) { return; } var _props = this.props; var blockKey = _props.blockKey; var start = _props.start; var text = _props.text; var end = start + text.length; if (!selection.hasEdgeWithin(blockKey, start, end)) { return; } // Determine the appropriate target node for selection. If the child // is not a text node, it is a <br /> spacer. In this case, use the // <span> itself as the selection target. var node = ReactDOM.findDOMNode(this); var child = node.firstChild; var targetNode = void 0; if (child.nodeType === Node.TEXT_NODE) { targetNode = child; } else if (child.tagName === 'BR') { targetNode = node; } else { targetNode = child.firstChild; } setDraftEditorSelection(selection, targetNode, blockKey, start, end); }
...
};
DraftEditorLeaf.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps) {
return ReactDOM.findDOMNode(this.refs.leaf).textContent !== nextProps.text || nextProps.styleSet !== this.props.styleSet ||
nextProps.forceSelection;
};
DraftEditorLeaf.prototype.componentDidUpdate = function componentDidUpdate() {
this._setSelection();
};
DraftEditorLeaf.prototype.componentDidMount = function componentDidMount() {
this._setSelection();
};
DraftEditorLeaf.prototype.render = function render() {
...
function componentDidMount() { this._setSelection(); }
n/a
function componentDidUpdate() { this._setSelection(); }
n/a
function render() { var text = this.props.text; // If the leaf is at the end of its block and ends in a soft newline, append // an extra line feed character. Browsers collapse trailing newline // characters, which leaves the cursor in the wrong place after a // shift+enter. The extra character repairs this. if (text.endsWith('\n') && this.props.isLast) { text += '\n'; } var _props2 = this.props; var customStyleMap = _props2.customStyleMap; var customStyleFn = _props2.customStyleFn; var offsetKey = _props2.offsetKey; var styleSet = _props2.styleSet; var styleObj = styleSet.reduce(function (map, styleName) { var mergedStyles = {}; var style = customStyleMap[styleName]; if (style !== undefined && map.textDecoration !== style.textDecoration) { // .trim() is necessary for IE9/10/11 and Edge mergedStyles.textDecoration = [map.textDecoration, style.textDecoration].join(' ').trim(); } return _assign(map, style, mergedStyles); }, {}); if (customStyleFn) { var newStyles = customStyleFn(styleSet); styleObj = _assign(styleObj, newStyles); } return React.createElement( 'span', { 'data-offset-key': offsetKey, ref: 'leaf', style: styleObj }, React.createElement( DraftEditorTextNode, null, text ) ); }
...
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange} />
);
}
}
ReactDOM.render(
<MyEditor />,
document.getElementById('container')
);
```
Because Draft.js supports unicode, you must have the following meta tag in the `<head>` `</head>` block of your
HTML file:
...
function shouldComponentUpdate(nextProps) { return ReactDOM.findDOMNode(this.refs.leaf).textContent !== nextProps.text || nextProps.styleSet !== this.props.styleSet || nextProps .forceSelection; }
n/a
function DraftEditorPlaceholder() { _classCallCheck(this, DraftEditorPlaceholder); return _possibleConstructorReturn(this, _React$Component.apply(this, arguments)); }
n/a
function render() { var hasFocus = this.props.editorState.getSelection().getHasFocus(); var className = cx({ 'public/DraftEditorPlaceholder/root': true, 'public/DraftEditorPlaceholder/hasFocus': hasFocus }); return React.createElement( 'div', { className: className }, React.createElement( 'div', { className: cx('public/DraftEditorPlaceholder/inner'), id: this.props.accessibilityID }, this.props.text ) ); }
...
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange} />
);
}
}
ReactDOM.render(
<MyEditor />,
document.getElementById('container')
);
```
Because Draft.js supports unicode, you must have the following meta tag in the `<head>` `</head>` block of your
HTML file:
...
function shouldComponentUpdate(nextProps) { return this.props.text !== nextProps.text || this.props.editorState.getSelection().getHasFocus() !== nextProps.editorState.getSelection ().getHasFocus(); }
n/a
function DraftEditorTextNode(props) { _classCallCheck(this, DraftEditorTextNode); var _this = _possibleConstructorReturn(this, _React$Component.call(this, props)); _this._forceFlag = false; return _this; }
n/a
function componentWillUpdate() { // By flipping this flag, we also keep flipping keys which forces // React to remount this node every time it rerenders. this._forceFlag = !this._forceFlag; }
n/a
function render() { if (this.props.children === '') { return this._forceFlag ? NEWLINE_A : NEWLINE_B; } return React.createElement( 'span', { key: this._forceFlag ? 'A' : 'B', 'data-text': 'true' }, this.props.children ); }
...
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange} />
);
}
}
ReactDOM.render(
<MyEditor />,
document.getElementById('container')
);
```
Because Draft.js supports unicode, you must have the following meta tag in the `<head>` `</head>` block of your
HTML file:
...
function shouldComponentUpdate(nextProps) { var node = ReactDOM.findDOMNode(this); var shouldBeNewline = nextProps.children === ''; if (shouldBeNewline) { return !isNewline(node); } return node.textContent !== nextProps.children; }
n/a
function getRemovalRange(selectionStart, selectionEnd, text, entityStart, direction) { var segments = text.split(' '); segments = segments.map(function ( /*string*/segment, /*number*/ii) { if (direction === 'forward') { if (ii > 0) { return ' ' + segment; } } else if (ii < segments.length - 1) { return segment + ' '; } return segment; }); var segmentStart = entityStart; var segmentEnd; var segment; var removalStart = null; var removalEnd = null; for (var jj = 0; jj < segments.length; jj++) { segment = segments[jj]; segmentEnd = segmentStart + segment.length; // Our selection overlaps this segment. if (selectionStart < segmentEnd && segmentStart < selectionEnd) { if (removalStart !== null) { removalEnd = segmentEnd; } else { removalStart = segmentStart; removalEnd = segmentEnd; } } else if (removalStart !== null) { break; } segmentStart = segmentEnd; } var entityEnd = entityStart + text.length; var atStart = removalStart === entityStart; var atEnd = removalEnd === entityEnd; if (!atStart && atEnd || atStart && !atEnd) { if (direction === 'forward') { if (removalEnd !== entityEnd) { removalEnd++; } } else if (removalStart !== entityStart) { removalStart--; } } return { start: removalStart, end: removalEnd }; }
...
focusOffset: entityRange.end,
isBackward: false
});
}
// For `SEGMENTED` entity types, determine the appropriate segment to
// remove.
var removalRange = DraftEntitySegments.getRemovalRange(start, end, block.getText().
slice(entityRange.start, entityRange.end), entityRange.start, direction);
return selectionState.merge({
anchorOffset: removalRange.start,
focusOffset: removalRange.end,
isBackward: false
});
}
...
function decode(offsetKey) { var _offsetKey$split = offsetKey.split(KEY_DELIMITER); var blockKey = _offsetKey$split[0]; var decoratorKey = _offsetKey$split[1]; var leafKey = _offsetKey$split[2]; return { blockKey: blockKey, decoratorKey: parseInt(decoratorKey, 10), leafKey: parseInt(leafKey, 10) }; }
...
/*eslint-disable no-console */
console.warn('Invalid selection state.', arguments, editorState.toJS());
/*eslint-enable no-console */
return selection;
}
}
var anchorPath = DraftOffsetKey.decode(anchorKey);
var anchorBlockKey = anchorPath.blockKey;
var anchorLeaf = editorState.getBlockTree(anchorBlockKey).getIn([anchorPath.decoratorKey, 'leaves', anchorPath.leafKey
]);
var focusPath = DraftOffsetKey.decode(focusKey);
var focusBlockKey = focusPath.blockKey;
var focusLeaf = editorState.getBlockTree(focusBlockKey).getIn([focusPath.decoratorKey, 'leaves', focusPath.leafKey]);
...
function encode(blockKey, decoratorKey, leafKey) { return blockKey + KEY_DELIMITER + decoratorKey + KEY_DELIMITER + leafKey; }
...
var lastLeafSet = this.props.tree.size - 1;
var hasSelection = isBlockOnSelectionEdge(this.props.selection, blockKey);
return this.props.tree.map(function (leafSet, ii) {
var leavesForLeafSet = leafSet.get('leaves');
var lastLeaf = leavesForLeafSet.size - 1;
var leaves = leavesForLeafSet.map(function (leaf, jj) {
var offsetKey = DraftOffsetKey.encode(blockKey, ii, jj);
var start = leaf.get('start');
var end = leaf.get('end');
return React.createElement(DraftEditorLeaf, {
key: offsetKey,
offsetKey: offsetKey,
blockKey: blockKey,
start: start,
...
function processHTML(html, blockRenderMap) { return convertFromHTMLtoContentBlocks(html, getSafeBodyFromHTML, blockRenderMap); }
...
// the clipboard. See https://bugs.webkit.org/show_bug.cgi?id=19893.
editor.update(insertFragment(editor._latestEditorState, internalClipboard));
return;
}
// If there is html paste data, try to parse that.
if (html) {
var htmlFragment = DraftPasteProcessor.processHTML(html, editor.props.blockRenderMap
);
if (htmlFragment) {
var contentBlocks = htmlFragment.contentBlocks;
var entityMap = htmlFragment.entityMap;
if (contentBlocks) {
var htmlMap = BlockMapBuilder.createFromArray(contentBlocks);
editor.update(insertFragment(editor._latestEditorState, htmlMap, entityMap));
...
function processText(textBlocks, character) { return textBlocks.map(function (textLine) { textLine = sanitizeDraftText(textLine); return new ContentBlock({ key: generateRandomKey(), type: 'unstyled', text: textLine, characterList: List(Repeat(character, textLine.length)) }); }); }
...
var editorState = editor._latestEditorState;
var blocks = splitTextIntoTextBlocks(fileText);
var character = CharacterMetadata.create({
style: editorState.getCurrentInlineStyle(),
entity: getEntityKeyForSelection(editorState.getCurrentContent(), editorState.getSelection())
});
var text = DraftPasteProcessor.processText(blocks, character);
var fragment = BlockMapBuilder.createFromArray(text);
var withInsertedText = DraftModifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), fragment
);
editor.update(EditorState.push(editorState, withInsertedText, 'insert-fragment'));
});
...
function getBackward(text) { return getRemovableWord(text, true); }
...
// If there are no words before the cursor, remove the preceding newline.
if (offset === 0) {
return moveSelectionBackward(strategyState, 1);
}
var key = selection.getStartKey();
var content = strategyState.getCurrentContent();
var text = content.getBlockForKey(key).getText().slice(0, offset);
var toRemove = DraftRemovableWord.getBackward(text);
return moveSelectionBackward(strategyState, toRemove.length || 1);
}, 'backward');
if (afterRemoval === editorState.getCurrentContent()) {
return editorState;
}
...
function getForward(text) { return getRemovableWord(text, false); }
...
function keyCommandDeleteWord(editorState) {
var afterRemoval = removeTextWithStrategy(editorState, function (strategyState) {
var selection = strategyState.getSelection();
var offset = selection.getStartOffset();
var key = selection.getStartKey();
var content = strategyState.getCurrentContent();
var text = content.getBlockForKey(key).getText().slice(offset);
var toRemove = DraftRemovableWord.getForward(text);
// If there are no words in front of the cursor, remove the newline.
return moveSelectionForward(strategyState, toRemove.length || 1);
}, 'forward');
if (afterRemoval === editorState.getCurrentContent()) {
return editorState;
...
function stringify(key) { return '_' + String(key); }
...
var rawBlocks = [];
contentState.getBlockMap().forEach(function (block, blockKey) {
block.findEntityRanges(function (character) {
return character.getEntity() !== null;
}, function (start) {
// Stringify to maintain order of otherwise numeric keys.
var stringifiedEntityKey = DraftStringKey.stringify(block.getEntityAt(start));
if (!entityStorageMap.hasOwnProperty(stringifiedEntityKey)) {
entityStorageMap[stringifiedEntityKey] = '' + entityStorageKey++;
}
});
rawBlocks.push({
key: blockKey,
...
function unstringify(key) { return key.slice(1); }
...
});
// Flip storage map so that our storage keys map to global
// DraftEntity keys.
var entityKeys = Object.keys(entityStorageMap);
var flippedStorageMap = {};
entityKeys.forEach(function (key, jj) {
var entity = contentState.getEntity(DraftStringKey.unstringify(key));
flippedStorageMap[jj] = {
type: entity.getType(),
mutability: entity.getMutability(),
data: entity.getData()
};
});
...
function DraftEditor(props) { _classCallCheck(this, DraftEditor); var _this = _possibleConstructorReturn(this, _React$Component.call(this, props)); _this._blockSelectEvents = false; _this._clipboard = null; _this._handler = null; _this._dragCount = 0; _this._editorKey = generateRandomKey(); _this._placeholderAccessibilityID = 'placeholder-' + _this._editorKey; _this._latestEditorState = props.editorState; _this._onBeforeInput = _this._buildHandler('onBeforeInput'); _this._onBlur = _this._buildHandler('onBlur'); _this._onCharacterData = _this._buildHandler('onCharacterData'); _this._onCompositionEnd = _this._buildHandler('onCompositionEnd'); _this._onCompositionStart = _this._buildHandler('onCompositionStart'); _this._onCopy = _this._buildHandler('onCopy'); _this._onCut = _this._buildHandler('onCut'); _this._onDragEnd = _this._buildHandler('onDragEnd'); _this._onDragOver = _this._buildHandler('onDragOver'); _this._onDragStart = _this._buildHandler('onDragStart'); _this._onDrop = _this._buildHandler('onDrop'); _this._onInput = _this._buildHandler('onInput'); _this._onFocus = _this._buildHandler('onFocus'); _this._onKeyDown = _this._buildHandler('onKeyDown'); _this._onKeyPress = _this._buildHandler('onKeyPress'); _this._onKeyUp = _this._buildHandler('onKeyUp'); _this._onMouseDown = _this._buildHandler('onMouseDown'); _this._onMouseUp = _this._buildHandler('onMouseUp'); _this._onPaste = _this._buildHandler('onPaste'); _this._onSelect = _this._buildHandler('onSelect'); // Manual binding for public and internal methods. _this.focus = _this._focus.bind(_this); _this.blur = _this._blur.bind(_this); _this.setMode = _this._setMode.bind(_this); _this.exitCurrentMode = _this._exitCurrentMode.bind(_this); _this.restoreEditorDOM = _this._restoreEditorDOM.bind(_this); _this.setClipboard = _this._setClipboard.bind(_this); _this.getClipboard = _this._getClipboard.bind(_this); _this.getEditorKey = function () { return _this._editorKey; }; _this.update = _this._update.bind(_this); _this.onDragEnter = _this._onDragEnter.bind(_this); _this.onDragLeave = _this._onDragLeave.bind(_this); // See `_restoreEditorDOM()`. _this.state = { containerKey: 0 }; return _this; }
n/a
blockRendererFn = function () { return arg; }
n/a
blockStyleFn = function () { return arg; }
...
var configForType = blockRenderMap.get(blockType);
var wrapperTemplate = configForType.wrapper;
var _Element = configForType.element || blockRenderMap.get('unstyled').element;
var depth = _block.getDepth();
var className = this.props.blockStyleFn(_block);
// List items are special snowflakes, since we handle nesting and
// counters manually.
if (_Element === 'li') {
var shouldResetCount = lastWrapperTemplate !== wrapperTemplate || currentDepth === null || depth > currentDepth;
className = joinClasses(className, getListItemClasses(blockType, depth, shouldResetCount, direction));
}
...
function getDefaultKeyBinding(e) { switch (e.keyCode) { case 66: // B return hasCommandModifier(e) ? 'bold' : null; case 68: // D return isCtrlKeyCommand(e) ? 'delete' : null; case 72: // H return isCtrlKeyCommand(e) ? 'backspace' : null; case 73: // I return hasCommandModifier(e) ? 'italic' : null; case 74: // J return hasCommandModifier(e) ? 'code' : null; case 75: // K return !isWindows && isCtrlKeyCommand(e) ? 'secondary-cut' : null; case 77: // M return isCtrlKeyCommand(e) ? 'split-block' : null; case 79: // O return isCtrlKeyCommand(e) ? 'split-block' : null; case 84: // T return isOSX && isCtrlKeyCommand(e) ? 'transpose-characters' : null; case 85: // U return hasCommandModifier(e) ? 'underline' : null; case 87: // W return isOSX && isCtrlKeyCommand(e) ? 'backspace-word' : null; case 89: // Y if (isCtrlKeyCommand(e)) { return isWindows ? 'redo' : 'secondary-paste'; } return null; case 90: // Z return getZCommand(e) || null; case Keys.RETURN: return 'split-block'; case Keys.DELETE: return getDeleteCommand(e); case Keys.BACKSPACE: return getBackspaceCommand(e); // LEFT/RIGHT handlers serve as a workaround for a Firefox bug. case Keys.LEFT: return shouldFixFirefoxMovement && hasCommandModifier(e) ? 'move-selection-to-start-of-block' : null; case Keys.RIGHT: return shouldFixFirefoxMovement && hasCommandModifier(e) ? 'move-selection-to-end-of-block' : null; default: return null; } }
...
// Insert a nbsp into the editor.
var contentState = DraftModifier.replaceText(editorState.getCurrentContent(), editorState.getSelection(), '\xA0');
editor.update(EditorState.push(editorState, contentState, 'insert-characters'));
return;
}
}
var command = editor.props.keyBindingFn(e);
// If no command is specified, allow keydown event to continue.
if (!command) {
return;
}
if (command === 'undo') {
...
function _blur() { ReactDOM.findDOMNode(this.refs.editor).blur(); }
n/a
function _buildHandler(eventName) { var _this2 = this; return function (e) { if (!_this2.props.readOnly) { var method = _this2._handler && _this2._handler[eventName]; method && method(_this2, e); } }; }
...
_this._clipboard = null;
_this._handler = null;
_this._dragCount = 0;
_this._editorKey = generateRandomKey();
_this._placeholderAccessibilityID = 'placeholder-' + _this._editorKey;
_this._latestEditorState = props.editorState;
_this._onBeforeInput = _this._buildHandler('onBeforeInput');
_this._onBlur = _this._buildHandler('onBlur');
_this._onCharacterData = _this._buildHandler('onCharacterData');
_this._onCompositionEnd = _this._buildHandler('onCompositionEnd');
_this._onCompositionStart = _this._buildHandler('onCompositionStart');
_this._onCopy = _this._buildHandler('onCopy');
_this._onCut = _this._buildHandler('onCut');
_this._onDragEnd = _this._buildHandler('onDragEnd');
...
function _exitCurrentMode() { this.setMode('edit'); }
n/a
function _focus(scrollPosition) { var editorState = this.props.editorState; var alreadyHasFocus = editorState.getSelection().getHasFocus(); var editorNode = ReactDOM.findDOMNode(this.refs.editor); var scrollParent = Style.getScrollParent(editorNode); var _ref = scrollPosition || getScrollPosition(scrollParent); var x = _ref.x; var y = _ref.y; editorNode.focus(); if (scrollParent === window) { window.scrollTo(x, y); } else { Scroll.setTop(scrollParent, y); } // On Chrome and Safari, calling focus on contenteditable focuses the // cursor at the first character. This is something you don't expect when // you're clicking on an input element but not directly on a character. // Put the cursor back where it was before the blur. if (!alreadyHasFocus) { this.update(EditorState.forceSelection(editorState, editorState.getSelection())); } }
...
*/
DraftEditor.prototype._restoreEditorDOM = function _restoreEditorDOM(scrollPosition) {
var _this3 = this;
this.setState({ containerKey: this.state.containerKey + 1 }, function () {
_this3._focus(scrollPosition);
});
};
/**
* Used via `this.setClipboard(...)`.
*
* Set the clipboard state for a cut/copy event.
...
function _getClipboard() { return this._clipboard; }
n/a
function _onDragEnter() { this._dragCount++; }
n/a
function _onDragLeave() { this._dragCount--; if (this._dragCount === 0) { this.exitCurrentMode(); } }
n/a
function _renderPlaceholder() { if (this._showPlaceholder()) { return React.createElement(DraftEditorPlaceholder, { text: nullthrows(this.props.placeholder), editorState: this.props.editorState, textAlignment: this.props.textAlignment, accessibilityID: this._placeholderAccessibilityID }); } return null; }
...
whiteSpace: 'pre-wrap',
wordWrap: 'break-word'
};
return React.createElement(
'div',
{ className: rootClass },
this._renderPlaceholder(),
React.createElement(
'div',
{
className: cx('DraftEditor/editorContainer'),
key: 'editor' + this.state.containerKey,
ref: 'editorContainer' },
React.createElement(
...
function _restoreEditorDOM(scrollPosition) { var _this3 = this; this.setState({ containerKey: this.state.containerKey + 1 }, function () { _this3._focus(scrollPosition); }); }
n/a
function _setClipboard(clipboard) { this._clipboard = clipboard; }
n/a
function _setMode(mode) { this._handler = handlerMap[mode]; }
n/a
function _showPlaceholder() { return !!this.props.placeholder && !this.props.editorState.isInCompositionMode() && !this.props.editorState.getCurrentContent(). hasText(); }
...
};
DraftEditor.prototype._showPlaceholder = function _showPlaceholder() {
return !!this.props.placeholder && !this.props.editorState.isInCompositionMode() && !this.props.editorState
.getCurrentContent().hasText();
};
DraftEditor.prototype._renderPlaceholder = function _renderPlaceholder() {
if (this._showPlaceholder()) {
return React.createElement(DraftEditorPlaceholder, {
text: nullthrows(this.props.placeholder),
editorState: this.props.editorState,
textAlignment: this.props.textAlignment,
accessibilityID: this._placeholderAccessibilityID
});
}
...
function _update(editorState) { this._latestEditorState = editorState; this.props.onChange(editorState); }
n/a
function componentDidMount() {
this.setMode('edit');
/**
* IE has a hardcoded "feature" that attempts to convert link text into
* anchors in contentEditable DOM. This breaks the editor's expectations of
* the DOM, and control is lost. Disable it to make IE behave.
* See: http://blogs.msdn.com/b/ieinternals/archive/2010/09/15/
* ie9-beta-minor-change-list.aspx
*/
if (isIE) {
document.execCommand('AutoUrlDetect', false, false);
}
}
n/a
function componentDidUpdate() { this._blockSelectEvents = false; }
n/a
function componentWillUpdate(nextProps) { this._blockSelectEvents = true; this._latestEditorState = nextProps.editorState; }
n/a
function render() { var _props = this.props; var readOnly = _props.readOnly; var textAlignment = _props.textAlignment; var rootClass = cx({ 'DraftEditor/root': true, 'DraftEditor/alignLeft': textAlignment === 'left', 'DraftEditor/alignRight': textAlignment === 'right', 'DraftEditor/alignCenter': textAlignment === 'center' }); var contentStyle = { outline: 'none', whiteSpace: 'pre-wrap', wordWrap: 'break-word' }; return React.createElement( 'div', { className: rootClass }, this._renderPlaceholder(), React.createElement( 'div', { className: cx('DraftEditor/editorContainer'), key: 'editor' + this.state.containerKey, ref: 'editorContainer' }, React.createElement( 'div', { 'aria-activedescendant': readOnly ? null : this.props.ariaActiveDescendantID, 'aria-autocomplete': readOnly ? null : this.props.ariaAutoComplete, 'aria-describedby': this._showPlaceholder() ? this._placeholderAccessibilityID : null, 'aria-expanded': readOnly ? null : this.props.ariaExpanded, 'aria-haspopup': readOnly ? null : this.props.ariaHasPopup, 'aria-label': this.props.ariaLabel, 'aria-owns': readOnly ? null : this.props.ariaOwneeID, className: cx('public/DraftEditor/content'), contentEditable: !readOnly, 'data-testid': this.props.webDriverTestID, onBeforeInput: this._onBeforeInput, onBlur: this._onBlur, onCompositionEnd: this._onCompositionEnd, onCompositionStart: this._onCompositionStart, onCopy: this._onCopy, onCut: this._onCut, onDragEnd: this._onDragEnd, onDragEnter: this.onDragEnter, onDragLeave: this.onDragLeave, onDragOver: this._onDragOver, onDragStart: this._onDragStart, onDrop: this._onDrop, onFocus: this._onFocus, onInput: this._onInput, onKeyDown: this._onKeyDown, onKeyPress: this._onKeyPress, onKeyUp: this._onKeyUp, onMouseUp: this._onMouseUp, onPaste: this._onPaste, onSelect: this._onSelect, ref: 'editor', role: readOnly ? null : this.props.role || 'textbox', spellCheck: allowSpellCheck && this.props.spellCheck, style: contentStyle, suppressContentEditableWarning: true, tabIndex: this.props.tabIndex }, React.createElement(DraftEditorContents, { blockRenderMap: this.props.blockRenderMap, blockRendererFn: this.props.blockRendererFn, blockStyleFn: this.props.blockStyleFn, customStyleMap: _extends({}, DefaultDraftInlineStyle, this.props.customStyleMap), customStyleFn: this.props.customStyleFn, editorKey: this._editorKey, editorState: this.props.editorState }) ) ) ); }
...
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange} />
);
}
}
ReactDOM.render(
<MyEditor />,
document.getElementById('container')
);
```
Because Draft.js supports unicode, you must have the following meta tag in the `<head>` `</head>` block of your
HTML file:
...
function getDirectionMap(content, prevBidiMap) { if (!bidiService) { bidiService = new UnicodeBidiService(); } else { bidiService.reset(); } var blockMap = content.getBlockMap(); var nextBidi = blockMap.valueSeq().map(function (block) { return nullthrows(bidiService).getDirection(block.getText()); }); var bidiMap = OrderedMap(blockMap.keySeq().zip(nextBidi)); if (prevBidiMap != null && Immutable.is(prevBidiMap, bidiMap)) { return prevBidiMap; } return bidiMap; }
...
EditorState.create = function create(config) {
var currentContent = config.currentContent;
var decorator = config.decorator;
var recordConfig = _extends({}, config, {
treeMap: generateNewTreeMap(currentContent, decorator),
directionMap: EditorBidiService.getDirectionMap(currentContent)
});
return new EditorState(new EditorStateRecord(recordConfig));
};
EditorState.set = function set(editorState, put) {
var map = editorState.getImmutable().withMutations(function (state) {
var existingDecorator = state.get('decorator');
...
function DraftEditorBlock() { _classCallCheck(this, DraftEditorBlock); return _possibleConstructorReturn(this, _React$Component.apply(this, arguments)); }
n/a
function _renderChildren() { var _this2 = this; var block = this.props.block; var blockKey = block.getKey(); var text = block.getText(); var lastLeafSet = this.props.tree.size - 1; var hasSelection = isBlockOnSelectionEdge(this.props.selection, blockKey); return this.props.tree.map(function (leafSet, ii) { var leavesForLeafSet = leafSet.get('leaves'); var lastLeaf = leavesForLeafSet.size - 1; var leaves = leavesForLeafSet.map(function (leaf, jj) { var offsetKey = DraftOffsetKey.encode(blockKey, ii, jj); var start = leaf.get('start'); var end = leaf.get('end'); return React.createElement(DraftEditorLeaf, { key: offsetKey, offsetKey: offsetKey, blockKey: blockKey, start: start, selection: hasSelection ? _this2.props.selection : undefined, forceSelection: _this2.props.forceSelection, text: text.slice(start, end), styleSet: block.getInlineStyleAt(start), customStyleMap: _this2.props.customStyleMap, customStyleFn: _this2.props.customStyleFn, isLast: ii === lastLeafSet && jj === lastLeaf }); }).toArray(); var decoratorKey = leafSet.get('decoratorKey'); if (decoratorKey == null) { return leaves; } if (!_this2.props.decorator) { return leaves; } var decorator = nullthrows(_this2.props.decorator); var DecoratorComponent = decorator.getComponentForKey(decoratorKey); if (!DecoratorComponent) { return leaves; } var decoratorProps = decorator.getPropsForKey(decoratorKey); var decoratorOffsetKey = DraftOffsetKey.encode(blockKey, ii, 0); var decoratedText = text.slice(leavesForLeafSet.first().get('start'), leavesForLeafSet.last().get('end')); // Resetting dir to the same value on a child node makes Chrome/Firefox // confused on cursor movement. See http://jsfiddle.net/d157kLck/3/ var dir = UnicodeBidiDirection.getHTMLDirIfDifferent(UnicodeBidi.getDirection(decoratedText), _this2.props.direction); return React.createElement( DecoratorComponent, _extends({}, decoratorProps, { contentState: _this2.props.contentState, decoratedText: decoratedText, dir: dir, key: decoratorOffsetKey, entityKey: block.getEntityAt(leafSet.get('start')), offsetKey: decoratorOffsetKey }), leaves ); }).toArray(); }
...
'public/DraftStyleDefault/ltr': direction === 'LTR',
'public/DraftStyleDefault/rtl': direction === 'RTL'
});
return React.createElement(
'div',
{ 'data-offset-key': offsetKey, className: className },
this._renderChildren()
);
};
return DraftEditorBlock;
}(React.Component);
/**
...
function componentDidMount() { var selection = this.props.selection; var endKey = selection.getEndKey(); if (!selection.getHasFocus() || endKey !== this.props.block.getKey()) { return; } var blockNode = ReactDOM.findDOMNode(this); var scrollParent = Style.getScrollParent(blockNode); var scrollPosition = getScrollPosition(scrollParent); var scrollDelta; if (scrollParent === window) { var nodePosition = getElementPosition(blockNode); var nodeBottom = nodePosition.y + nodePosition.height; var viewportHeight = getViewportDimensions().height; scrollDelta = nodeBottom - viewportHeight; if (scrollDelta > 0) { window.scrollTo(scrollPosition.x, scrollPosition.y + scrollDelta + SCROLL_BUFFER); } } else { var blockBottom = blockNode.offsetHeight + blockNode.offsetTop; var scrollBottom = scrollParent.offsetHeight + scrollPosition.y; scrollDelta = blockBottom - scrollBottom; if (scrollDelta > 0) { Scroll.setTop(scrollParent, Scroll.getTop(scrollParent) + scrollDelta + SCROLL_BUFFER); } } }
n/a
function render() { var _props = this.props; var direction = _props.direction; var offsetKey = _props.offsetKey; var className = cx({ 'public/DraftStyleDefault/block': true, 'public/DraftStyleDefault/ltr': direction === 'LTR', 'public/DraftStyleDefault/rtl': direction === 'RTL' }); return React.createElement( 'div', { 'data-offset-key': offsetKey, className: className }, this._renderChildren() ); }
...
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange} />
);
}
}
ReactDOM.render(
<MyEditor />,
document.getElementById('container')
);
```
Because Draft.js supports unicode, you must have the following meta tag in the `<head>` `</head>` block of your
HTML file:
...
function shouldComponentUpdate(nextProps) { return this.props.block !== nextProps.block || this.props.tree !== nextProps.tree || this.props.direction !== nextProps.direction || isBlockOnSelectionEdge(nextProps.selection, nextProps.block.getKey()) && nextProps.forceSelection; }
n/a
function EditorState(immutable) { _classCallCheck(this, EditorState); this._immutable = immutable; }
n/a
function acceptSelection(editorState, selection) { return updateSelection(editorState, selection, false); }
...
EditorState.moveSelectionToEnd = function moveSelectionToEnd(editorState) {
var content = editorState.getCurrentContent();
var lastBlock = content.getLastBlock();
var lastKey = lastBlock.getKey();
var length = lastBlock.getLength();
return EditorState.acceptSelection(editorState, new SelectionState({
anchorKey: lastKey,
anchorOffset: length,
focusKey: lastKey,
focusOffset: length,
isBackward: false
}));
};
...
function create(config) { var currentContent = config.currentContent; var decorator = config.decorator; var recordConfig = _extends({}, config, { treeMap: generateNewTreeMap(currentContent, decorator), directionMap: EditorBidiService.getDirectionMap(currentContent) }); return new EditorState(new EditorStateRecord(recordConfig)); }
...
var EditorState = function () {
EditorState.createEmpty = function createEmpty(decorator) {
return EditorState.createWithContent(ContentState.createFromText(''), decorator);
};
EditorState.createWithContent = function createWithContent(contentState, decorator) {
var firstKey = contentState.getBlockMap().first().getKey();
return EditorState.create({
currentContent: contentState,
undoStack: Stack(),
redoStack: Stack(),
decorator: decorator || null,
selection: SelectionState.createEmpty(firstKey)
});
};
...
function createEmpty(decorator) { return EditorState.createWithContent(ContentState.createFromText(''), decorator); }
...
import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState} from 'draft-js';
class MyEditor extends React.Component {
constructor(props) {
super(props);
this.state = {editorState: EditorState.createEmpty()};
this.onChange = (editorState) => this.setState({editorState});
}
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange} />
);
}
...
function createWithContent(contentState, decorator) { var firstKey = contentState.getBlockMap().first().getKey(); return EditorState.create({ currentContent: contentState, undoStack: Stack(), redoStack: Stack(), decorator: decorator || null, selection: SelectionState.createEmpty(firstKey) }); }
...
undoStack: Stack()
};
var EditorStateRecord = Record(defaultRecord);
var EditorState = function () {
EditorState.createEmpty = function createEmpty(decorator) {
return EditorState.createWithContent(ContentState.createFromText(''),
decorator);
};
EditorState.createWithContent = function createWithContent(contentState, decorator) {
var firstKey = contentState.getBlockMap().first().getKey();
return EditorState.create({
currentContent: contentState,
undoStack: Stack(),
...
function forceSelection(editorState, selection) { if (!selection.getHasFocus()) { selection = selection.set('hasFocus', true); } return updateSelection(editorState, selection, true); }
...
* where we want to programmatically focus the input and it makes sense
* to allow the user to continue working seamlessly.
*/
EditorState.moveFocusToEnd = function moveFocusToEnd(editorState) {
var afterSelectionMove = EditorState.moveSelectionToEnd(editorState);
return EditorState.forceSelection(afterSelectionMove, afterSelectionMove.getSelection
());
};
/**
* Push the current ContentState onto the undo stack if it should be
* considered a boundary state, and set the provided ContentState as the
* new current content.
*/
...
function moveFocusToEnd(editorState) { var afterSelectionMove = EditorState.moveSelectionToEnd(editorState); return EditorState.forceSelection(afterSelectionMove, afterSelectionMove.getSelection()); }
n/a
function moveSelectionToEnd(editorState) { var content = editorState.getCurrentContent(); var lastBlock = content.getLastBlock(); var lastKey = lastBlock.getKey(); var length = lastBlock.getLength(); return EditorState.acceptSelection(editorState, new SelectionState({ anchorKey: lastKey, anchorOffset: length, focusKey: lastKey, focusOffset: length, isBackward: false })); }
...
* Force focus to the end of the editor. This is useful in scenarios
* where we want to programmatically focus the input and it makes sense
* to allow the user to continue working seamlessly.
*/
EditorState.moveFocusToEnd = function moveFocusToEnd(editorState) {
var afterSelectionMove = EditorState.moveSelectionToEnd(editorState);
return EditorState.forceSelection(afterSelectionMove, afterSelectionMove.getSelection());
};
/**
* Push the current ContentState onto the undo stack if it should be
* considered a boundary state, and set the provided ContentState as the
* new current content.
...
function push(editorState, contentState, changeType) { if (editorState.getCurrentContent() === contentState) { return editorState; } var forceSelection = changeType !== 'insert-characters'; var directionMap = EditorBidiService.getDirectionMap(contentState, editorState.getDirectionMap()); if (!editorState.getAllowUndo()) { return EditorState.set(editorState, { currentContent: contentState, directionMap: directionMap, lastChangeType: changeType, selection: contentState.getSelectionAfter(), forceSelection: forceSelection, inlineStyleOverride: null }); } var selection = editorState.getSelection(); var currentContent = editorState.getCurrentContent(); var undoStack = editorState.getUndoStack(); var newContent = contentState; if (selection !== currentContent.getSelectionAfter() || mustBecomeBoundary(editorState, changeType)) { undoStack = undoStack.push(currentContent); newContent = newContent.set('selectionBefore', selection); } else if (changeType === 'insert-characters' || changeType === 'backspace-character' || changeType === 'delete-character') { // Preserve the previous selection. newContent = newContent.set('selectionBefore', currentContent.getSelectionBefore()); } var inlineStyleOverride = editorState.getInlineStyleOverride(); // Don't discard inline style overrides on block type or depth changes. if (changeType !== 'adjust-depth' && changeType !== 'change-block-type') { inlineStyleOverride = null; } var editorStateChanges = { currentContent: newContent, directionMap: directionMap, undoStack: undoStack, redoStack: Stack(), lastChangeType: changeType, selection: contentState.getSelectionAfter(), forceSelection: forceSelection, inlineStyleOverride: inlineStyleOverride }; return EditorState.set(editorState, editorStateChanges); }
...
var selection = editorState.getSelection();
var currentContent = editorState.getCurrentContent();
var undoStack = editorState.getUndoStack();
var newContent = contentState;
if (selection !== currentContent.getSelectionAfter() || mustBecomeBoundary(editorState, changeType)) {
undoStack = undoStack.push(currentContent);
newContent = newContent.set('selectionBefore', selection);
} else if (changeType === 'insert-characters' || changeType === 'backspace-character' || changeType ===
x27;delete-character') {
// Preserve the previous selection.
newContent = newContent.set('selectionBefore', currentContent.getSelectionBefore());
}
var inlineStyleOverride = editorState.getInlineStyleOverride();
...
function redo(editorState) { if (!editorState.getAllowUndo()) { return editorState; } var redoStack = editorState.getRedoStack(); var newCurrentContent = redoStack.peek(); if (!newCurrentContent) { return editorState; } var currentContent = editorState.getCurrentContent(); var directionMap = EditorBidiService.getDirectionMap(newCurrentContent, editorState.getDirectionMap()); return EditorState.set(editorState, { currentContent: newCurrentContent, directionMap: directionMap, undoStack: editorState.getUndoStack().push(currentContent), redoStack: redoStack.shift(), forceSelection: true, inlineStyleOverride: null, lastChangeType: 'redo', nativelyRenderedContent: null, selection: newCurrentContent.getSelectionAfter() }); }
...
/**
* Map a `DraftEditorCommand` command value to a corresponding function.
*/
function onKeyCommand(command, editorState) {
switch (command) {
case 'redo':
return EditorState.redo(editorState);
case 'delete':
return keyCommandPlainDelete(editorState);
case 'delete-word':
return keyCommandDeleteWord(editorState);
case 'backspace':
return keyCommandPlainBackspace(editorState);
case 'backspace-word':
...
function set(editorState, put) { var map = editorState.getImmutable().withMutations(function (state) { var existingDecorator = state.get('decorator'); var decorator = existingDecorator; if (put.decorator === null) { decorator = null; } else if (put.decorator) { decorator = put.decorator; } var newContent = put.currentContent || editorState.getCurrentContent(); if (decorator !== existingDecorator) { var treeMap = state.get('treeMap'); var newTreeMap; if (decorator && existingDecorator) { newTreeMap = regenerateTreeForNewDecorator(newContent, newContent.getBlockMap(), treeMap, decorator, existingDecorator); } else { newTreeMap = generateNewTreeMap(newContent, decorator); } state.merge({ decorator: decorator, treeMap: newTreeMap, nativelyRenderedContent: null }); return; } var existingContent = editorState.getCurrentContent(); if (newContent !== existingContent) { state.set('treeMap', regenerateTreeForNewBlocks(editorState, newContent.getBlockMap(), newContent.getEntityMap(), decorator )); } state.merge(put); }); return new EditorState(map); }
...
nativelyRenderedContent: null
});
return;
}
var existingContent = editorState.getCurrentContent();
if (newContent !== existingContent) {
state.set('treeMap', regenerateTreeForNewBlocks(editorState, newContent
.getBlockMap(), newContent.getEntityMap(), decorator));
}
state.merge(put);
});
return new EditorState(map);
};
...
function setInlineStyleOverride(editorState, inlineStyleOverride) { return EditorState.set(editorState, { inlineStyleOverride: inlineStyleOverride }); }
...
var selection = editorState.getSelection();
var currentStyle = editorState.getCurrentInlineStyle();
// If the selection is collapsed, toggle the specified style on or off and
// set the result as the new inline style override. This will then be
// used as the inline style for the next character to be inserted.
if (selection.isCollapsed()) {
return EditorState.setInlineStyleOverride(editorState, currentStyle.has(inlineStyle
) ? currentStyle.remove(inlineStyle) : currentStyle.add(inlineStyle));
}
// If characters are selected, immediately apply or remove the
// inline style on the document state itself.
var content = editorState.getCurrentContent();
var newContent;
...
function undo(editorState) { if (!editorState.getAllowUndo()) { return editorState; } var undoStack = editorState.getUndoStack(); var newCurrentContent = undoStack.peek(); if (!newCurrentContent) { return editorState; } var currentContent = editorState.getCurrentContent(); var directionMap = EditorBidiService.getDirectionMap(newCurrentContent, editorState.getDirectionMap()); return EditorState.set(editorState, { currentContent: newCurrentContent, directionMap: directionMap, undoStack: undoStack.shift(), redoStack: editorState.getRedoStack().push(currentContent), forceSelection: true, inlineStyleOverride: null, lastChangeType: 'undo', nativelyRenderedContent: null, selection: currentContent.getSelectionBefore() }); }
...
*/
'use strict';
var EditorState = require('./EditorState');
function keyCommandUndo(e, editorState, updateFn) {
var undoneState = EditorState.undo(editorState);
// If the last change to occur was a spellcheck change, allow the undo
// event to fall through to the browser. This allows the browser to record
// the unwanted change, which should soon lead it to learn not to suggest
// the correction again.
if (editorState.getLastChangeType() === 'spellcheck-change') {
var nativelyRenderedContent = undoneState.getCurrentContent();
...
function getAllowUndo() { return this.getImmutable().get('allowUndo'); }
...
if (editorState.getCurrentContent() === contentState) {
return editorState;
}
var forceSelection = changeType !== 'insert-characters';
var directionMap = EditorBidiService.getDirectionMap(contentState, editorState.getDirectionMap());
if (!editorState.getAllowUndo()) {
return EditorState.set(editorState, {
currentContent: contentState,
directionMap: directionMap,
lastChangeType: changeType,
selection: contentState.getSelectionAfter(),
forceSelection: forceSelection,
inlineStyleOverride: null
...
function getBlockTree(blockKey) { return this.getImmutable().getIn(['treeMap', blockKey]); }
...
/*eslint-enable no-console */
return selection;
}
}
var anchorPath = DraftOffsetKey.decode(anchorKey);
var anchorBlockKey = anchorPath.blockKey;
var anchorLeaf = editorState.getBlockTree(anchorBlockKey).getIn([anchorPath.decoratorKey
, 'leaves', anchorPath.leafKey]);
var focusPath = DraftOffsetKey.decode(focusKey);
var focusBlockKey = focusPath.blockKey;
var focusLeaf = editorState.getBlockTree(focusBlockKey).getIn([focusPath.decoratorKey, 'leaves', focusPath.leafKey]);
var anchorLeafStart = anchorLeaf.get('start');
var focusLeafStart = focusLeaf.get('start');
...
function getCurrentContent() { return this.getImmutable().get('currentContent'); }
...
var decorator = existingDecorator;
if (put.decorator === null) {
decorator = null;
} else if (put.decorator) {
decorator = put.decorator;
}
var newContent = put.currentContent || editorState.getCurrentContent();
if (decorator !== existingDecorator) {
var treeMap = state.get('treeMap');
var newTreeMap;
if (decorator && existingDecorator) {
newTreeMap = regenerateTreeForNewDecorator(newContent, newContent.getBlockMap(), treeMap, decorator, existingDecorator
);
} else {
...
function getCurrentInlineStyle() { var override = this.getInlineStyleOverride(); if (override != null) { return override; } var content = this.getCurrentContent(); var selection = this.getSelection(); if (selection.isCollapsed()) { return getInlineStyleForCollapsedSelection(content, selection); } return getInlineStyleForNonCollapsedSelection(content, selection); }
...
var composedChars = textInputData;
textInputData = '';
var editorState = EditorState.set(editor._latestEditorState, {
inCompositionMode: false
});
var currentStyle = editorState.getCurrentInlineStyle();
var entityKey = getEntityKeyForSelection(editorState.getCurrentContent(), editorState.getSelection());
var mustReset = !composedChars || isSelectionAtLeafStart(editorState) || currentStyle.size > 0 || entityKey !== null;
if (mustReset) {
editor.restoreEditorDOM();
}
...
function getDecorator() { return this.getImmutable().get('decorator'); }
...
// rendered state, there's nothing new to be done.
if (prevEditorState === nextEditorState || nextNativeContent !== null && nextEditorState.getCurrentContent() ===
nextNativeContent || wasComposing && nowComposing) {
return false;
}
var prevContent = prevEditorState.getCurrentContent();
var nextContent = nextEditorState.getCurrentContent();
var prevDecorator = prevEditorState.getDecorator();
var nextDecorator = nextEditorState.getDecorator();
return wasComposing !== nowComposing || prevContent !== nextContent || prevDecorator !== nextDecorator || nextEditorState.mustForceSelection
();
};
DraftEditorContents.prototype.render = function render() {
var _props = this.props;
var blockRenderMap = _props.blockRenderMap;
...
function getDirectionMap() { return this.getImmutable().get('directionMap'); }
...
EditorState.create = function create(config) {
var currentContent = config.currentContent;
var decorator = config.decorator;
var recordConfig = _extends({}, config, {
treeMap: generateNewTreeMap(currentContent, decorator),
directionMap: EditorBidiService.getDirectionMap(currentContent)
});
return new EditorState(new EditorStateRecord(recordConfig));
};
EditorState.set = function set(editorState, put) {
var map = editorState.getImmutable().withMutations(function (state) {
var existingDecorator = state.get('decorator');
...
function getImmutable() { return this._immutable; }
...
treeMap: generateNewTreeMap(currentContent, decorator),
directionMap: EditorBidiService.getDirectionMap(currentContent)
});
return new EditorState(new EditorStateRecord(recordConfig));
};
EditorState.set = function set(editorState, put) {
var map = editorState.getImmutable().withMutations(function (state) {
var existingDecorator = state.get('decorator');
var decorator = existingDecorator;
if (put.decorator === null) {
decorator = null;
} else if (put.decorator) {
decorator = put.decorator;
}
...
function getInlineStyleOverride() { return this.getImmutable().get('inlineStyleOverride'); }
...
* Get the appropriate inline style for the editor state. If an
* override is in place, use it. Otherwise, the current style is
* based on the location of the selection state.
*/
EditorState.prototype.getCurrentInlineStyle = function getCurrentInlineStyle() {
var override = this.getInlineStyleOverride();
if (override != null) {
return override;
}
var content = this.getCurrentContent();
var selection = this.getSelection();
...
function getLastChangeType() { return this.getImmutable().get('lastChangeType'); }
...
/**
* Return whether a change should be considered a boundary state, given
* the previous change type. Allows us to discard potential boundary states
* during standard typing or deletion behavior.
*/
function mustBecomeBoundary(editorState, changeType) {
var lastChangeType = editorState.getLastChangeType();
return changeType !== lastChangeType || changeType !== 'insert-characters' && changeType !== 'backspace
-character' && changeType !== 'delete-character';
}
function getInlineStyleForCollapsedSelection(content, selection) {
var startKey = selection.getStartKey();
var startOffset = selection.getStartOffset();
var startBlock = content.getBlockForKey(startKey);
...
function getNativelyRenderedContent() { return this.getImmutable().get('nativelyRenderedContent'); }
...
var didHaveFocus = prevEditorState.getSelection().getHasFocus();
var nowHasFocus = nextEditorState.getSelection().getHasFocus();
if (didHaveFocus !== nowHasFocus) {
return true;
}
var nextNativeContent = nextEditorState.getNativelyRenderedContent();
var wasComposing = prevEditorState.isInCompositionMode();
var nowComposing = nextEditorState.isInCompositionMode();
// If the state is unchanged or we're currently rendering a natively
// rendered state, there's nothing new to be done.
if (prevEditorState === nextEditorState || nextNativeContent !== null && nextEditorState.getCurrentContent() ===
nextNativeContent || wasComposing && nowComposing) {
...
function getRedoStack() { return this.getImmutable().get('redoStack'); }
...
var currentContent = editorState.getCurrentContent();
var directionMap = EditorBidiService.getDirectionMap(newCurrentContent, editorState.getDirectionMap());
return EditorState.set(editorState, {
currentContent: newCurrentContent,
directionMap: directionMap,
undoStack: undoStack.shift(),
redoStack: editorState.getRedoStack().push(currentContent),
forceSelection: true,
inlineStyleOverride: null,
lastChangeType: 'undo',
nativelyRenderedContent: null,
selection: currentContent.getSelectionBefore()
});
};
...
function getSelection() { return this.getImmutable().get('selection'); }
...
EditorState.prototype.getCurrentInlineStyle = function getCurrentInlineStyle() {
var override = this.getInlineStyleOverride();
if (override != null) {
return override;
}
var content = this.getCurrentContent();
var selection = this.getSelection();
if (selection.isCollapsed()) {
return getInlineStyleForCollapsedSelection(content, selection);
}
return getInlineStyleForNonCollapsedSelection(content, selection);
};
...
function getUndoStack() { return this.getImmutable().get('undoStack'); }
...
forceSelection: forceSelection,
inlineStyleOverride: null
});
}
var selection = editorState.getSelection();
var currentContent = editorState.getCurrentContent();
var undoStack = editorState.getUndoStack();
var newContent = contentState;
if (selection !== currentContent.getSelectionAfter() || mustBecomeBoundary(editorState, changeType)) {
undoStack = undoStack.push(currentContent);
newContent = newContent.set('selectionBefore', selection);
} else if (changeType === 'insert-characters' || changeType === 'backspace-character' || changeType ===
x27;delete-character') {
// Preserve the previous selection.
...
function isInCompositionMode() { return this.getImmutable().get('inCompositionMode'); }
...
var method = _this2._handler && _this2._handler[eventName];
method && method(_this2, e);
}
};
};
DraftEditor.prototype._showPlaceholder = function _showPlaceholder() {
return !!this.props.placeholder && !this.props.editorState.isInCompositionMode
() && !this.props.editorState.getCurrentContent().hasText();
};
DraftEditor.prototype._renderPlaceholder = function _renderPlaceholder() {
if (this._showPlaceholder()) {
return React.createElement(DraftEditorPlaceholder, {
text: nullthrows(this.props.placeholder),
editorState: this.props.editorState,
...
function isSelectionAtEndOfContent() { var content = this.getCurrentContent(); var blockMap = content.getBlockMap(); var last = blockMap.last(); var end = last.getLength(); return this.getSelection().hasEdgeWithin(last.getKey(), end, end); }
...
*/
function removeTextWithStrategy(editorState, strategy, direction) {
var selection = editorState.getSelection();
var content = editorState.getCurrentContent();
var target = selection;
if (selection.isCollapsed()) {
if (direction === 'forward') {
if (editorState.isSelectionAtEndOfContent()) {
return content;
}
} else if (editorState.isSelectionAtStartOfContent()) {
return content;
}
target = strategy(editorState);
...
function isSelectionAtStartOfContent() { var firstKey = this.getCurrentContent().getBlockMap().first().getKey(); return this.getSelection().hasEdgeWithin(firstKey, 0, 0); }
...
var content = editorState.getCurrentContent();
var target = selection;
if (selection.isCollapsed()) {
if (direction === 'forward') {
if (editorState.isSelectionAtEndOfContent()) {
return content;
}
} else if (editorState.isSelectionAtStartOfContent()) {
return content;
}
target = strategy(editorState);
if (target === selection) {
return content;
}
...
function mustForceSelection() { return this.getImmutable().get('forceSelection'); }
... return false; } var prevContent = prevEditorState.getCurrentContent(); var nextContent = nextEditorState.getCurrentContent(); var prevDecorator = prevEditorState.getDecorator(); var nextDecorator = nextEditorState.getDecorator(); return wasComposing !== nowComposing || prevContent !== nextContent || prevDecorator !== nextDecorator || nextEditorState.< span class="apidocCodeKeywordSpan">mustForceSelection(); }; DraftEditorContents.prototype.render = function render() { var _props = this.props; var blockRenderMap = _props.blockRenderMap; var blockRendererFn = _props.blockRendererFn; var customStyleMap = _props.customStyleMap; ...
function toJS() { return this.getImmutable().toJS(); }
...
state.merge(put);
});
return new EditorState(map);
};
EditorState.prototype.toJS = function toJS() {
return this.getImmutable().toJS();
};
EditorState.prototype.getAllowUndo = function getAllowUndo() {
return this.getImmutable().get('allowUndo');
};
EditorState.prototype.getCurrentContent = function getCurrentContent() {
...
function __add(instance) { var key = '' + ++instanceKey; instances = instances.set(key, instance); return key; }
...
// TODO: update this when we fully remove DraftEntity
DraftEntity.__replaceData(key, newData);
return this;
};
ContentState.prototype.addEntity = function addEntity(instance) {
// TODO: update this when we fully remove DraftEntity
DraftEntity.__add(instance);
return this;
};
ContentState.prototype.getEntity = function getEntity(key) {
// TODO: update this when we fully remove DraftEntity
return DraftEntity.__get(key);
};
...
function __create(type, mutability, data) { return DraftEntity.__add(new DraftEntityInstance({ type: type, mutability: mutability, data: data || {} })); }
...
ContentState.prototype.hasText = function hasText() {
var blockMap = this.getBlockMap();
return blockMap.size > 1 || blockMap.first().getLength() > 0;
};
ContentState.prototype.createEntity = function createEntity(type, mutability, data) {
// TODO: update this when we fully remove DraftEntity
DraftEntity.__create(type, mutability, data);
return this;
};
ContentState.prototype.mergeEntityData = function mergeEntityData(key, toMerge) {
// TODO: update this when we fully remove DraftEntity
DraftEntity.__mergeData(key, toMerge);
return this;
...
function __get(key) { var instance = instances.get(key); !!!instance ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Unknown DraftEntity key.') : invariant(false) : void 0 ; return instance; }
...
// TODO: update this when we fully remove DraftEntity
DraftEntity.__add(instance);
return this;
};
ContentState.prototype.getEntity = function getEntity(key) {
// TODO: update this when we fully remove DraftEntity
return DraftEntity.__get(key);
};
ContentState.createFromBlockArray = function createFromBlockArray(
// TODO: update flow type when we completely deprecate the old entity API
blocks, entityMap) {
// TODO: remove this when we completely deprecate the old entity API
var theBlocks = Array.isArray(blocks) ? blocks : blocks.contentBlocks;
...
function __getLastCreatedEntityKey() { return '' + instanceKey; }
...
return this.getBlockMap().map(function (block) {
return block ? block.getText() : '';
}).join(delimiter || '\n');
};
ContentState.prototype.getLastCreatedEntityKey = function getLastCreatedEntityKey() {
// TODO: update this when we fully remove DraftEntity
return DraftEntity.__getLastCreatedEntityKey();
};
ContentState.prototype.hasText = function hasText() {
var blockMap = this.getBlockMap();
return blockMap.size > 1 || blockMap.first().getLength() > 0;
};
...
function __mergeData(key, toMerge) { var instance = DraftEntity.__get(key); var newData = _extends({}, instance.getData(), toMerge); var newInstance = instance.set('data', newData); instances = instances.set(key, newInstance); return newInstance; }
...
// TODO: update this when we fully remove DraftEntity
DraftEntity.__create(type, mutability, data);
return this;
};
ContentState.prototype.mergeEntityData = function mergeEntityData(key, toMerge) {
// TODO: update this when we fully remove DraftEntity
DraftEntity.__mergeData(key, toMerge);
return this;
};
ContentState.prototype.replaceEntityData = function replaceEntityData(key, newData) {
// TODO: update this when we fully remove DraftEntity
DraftEntity.__replaceData(key, newData);
return this;
...
function __replaceData(key, newData) { var instance = DraftEntity.__get(key); var newInstance = instance.set('data', newData); instances = instances.set(key, newInstance); return newInstance; }
...
// TODO: update this when we fully remove DraftEntity
DraftEntity.__mergeData(key, toMerge);
return this;
};
ContentState.prototype.replaceEntityData = function replaceEntityData(key, newData) {
// TODO: update this when we fully remove DraftEntity
DraftEntity.__replaceData(key, newData);
return this;
};
ContentState.prototype.addEntity = function addEntity(instance) {
// TODO: update this when we fully remove DraftEntity
DraftEntity.__add(instance);
return this;
...
function add(instance) { logWarning('DraftEntity.add', 'contentState.addEntity'); return DraftEntity.__add(instance); }
...
var withoutEntities = removeEntitiesAtEdges(contentState, selectionState);
var withoutText = removeRangeFromContentState(withoutEntities, selectionState);
return splitBlockInContentState(withoutText, withoutText.getSelectionAfter());
},
applyInlineStyle: function applyInlineStyle(contentState, selectionState, inlineStyle) {
return ContentStateInlineStyle.add(contentState, selectionState, inlineStyle);
},
removeInlineStyle: function removeInlineStyle(contentState, selectionState, inlineStyle) {
return ContentStateInlineStyle.remove(contentState, selectionState, inlineStyle);
},
setBlockType: function setBlockType(contentState, selectionState, blockType) {
...
function create(type, mutability, data) { logWarning('DraftEntity.create', 'contentState.createEntity'); return DraftEntity.__create(type, mutability, data); }
...
var EditorState = function () {
EditorState.createEmpty = function createEmpty(decorator) {
return EditorState.createWithContent(ContentState.createFromText(''), decorator);
};
EditorState.createWithContent = function createWithContent(contentState, decorator) {
var firstKey = contentState.getBlockMap().first().getKey();
return EditorState.create({
currentContent: contentState,
undoStack: Stack(),
redoStack: Stack(),
decorator: decorator || null,
selection: SelectionState.createEmpty(firstKey)
});
};
...
function get(key) { logWarning('DraftEntity.get', 'contentState.getEntity'); return DraftEntity.__get(key); }
...
directionMap: EditorBidiService.getDirectionMap(currentContent)
});
return new EditorState(new EditorStateRecord(recordConfig));
};
EditorState.set = function set(editorState, put) {
var map = editorState.getImmutable().withMutations(function (state) {
var existingDecorator = state.get('decorator');
var decorator = existingDecorator;
if (put.decorator === null) {
decorator = null;
} else if (put.decorator) {
decorator = put.decorator;
}
...
function getLastCreatedEntityKey() { logWarning('DraftEntity.getLastCreatedEntityKey', 'contentState.getLastCreatedEntityKey'); return DraftEntity.__getLastCreatedEntityKey(); }
n/a
function mergeData(key, toMerge) { logWarning('DraftEntity.mergeData', 'contentState.mergeEntityData'); return DraftEntity.__mergeData(key, toMerge); }
n/a
function replaceData(key, newData) { logWarning('DraftEntity.replaceData', 'contentState.replaceEntityData'); return DraftEntity.__replaceData(key, newData); }
n/a
function DraftEntityInstance() { _classCallCheck(this, DraftEntityInstance); return _possibleConstructorReturn(this, _DraftEntityInstanceR.apply(this, arguments)); }
n/a
function getData() { return this.get('data'); }
...
return modifyBlockForContentState(contentState, selectionState, function (block) {
return block.merge({ data: blockData });
});
},
mergeBlockData: function mergeBlockData(contentState, selectionState, blockData) {
return modifyBlockForContentState(contentState, selectionState, function (block) {
return block.merge({ data: block.getData().merge(blockData) });
});
},
applyEntity: function applyEntity(contentState, selectionState, entityKey) {
var withoutEntities = removeEntitiesAtEdges(contentState, selectionState);
return applyEntityToContentState(withoutEntities, selectionState, entityKey);
}
...
function getMutability() { return this.get('mutability'); }
...
/**
* Determine whether an entity key corresponds to a `MUTABLE` entity. If so,
* return it. If not, return null.
*/
function filterKey(entityMap, entityKey) {
if (entityKey) {
var entity = entityMap.__get(entityKey);
return entity.getMutability() === 'MUTABLE' ? entityKey : null;
}
return null;
}
module.exports = getEntityKeyForSelection;
/***/ },
...
function getType() { return this.get('type'); }
...
var processedBlocks = [];
var currentDepth = null;
var lastWrapperTemplate = null;
for (var ii = 0; ii < blocksAsArray.length; ii++) {
var _block = blocksAsArray[ii];
var key = _block.getKey();
var blockType = _block.getType();
var customRenderer = blockRendererFn(_block);
var CustomComponent = void 0,
customProps = void 0,
customEditable = void 0;
if (customRenderer) {
CustomComponent = customRenderer.component;
...
function hasCommandModifier(e) { return isOSX ? !!e.metaKey && !e.altKey : KeyBindingUtil.isCtrlKeyCommand(e); }
n/a
function isCtrlKeyCommand(e) { return !!e.ctrlKey && !e.altKey; }
...
},
isOptionKeyCommand: function isOptionKeyCommand(e) {
return isOSX && e.altKey;
},
hasCommandModifier: function hasCommandModifier(e) {
return isOSX ? !!e.metaKey && !e.altKey : KeyBindingUtil.isCtrlKeyCommand(e);
}
};
module.exports = KeyBindingUtil;
/***/ },
/* 26 */
...
function isOptionKeyCommand(e) { return isOSX && e.altKey; }
n/a
function applyEntity(contentState, selectionState, entityKey) { var withoutEntities = removeEntitiesAtEdges(contentState, selectionState); return applyEntityToContentState(withoutEntities, selectionState, entityKey); }
...
var start = _getRemovalRange.start;
var end = _getRemovalRange.end;
var current;
while (start < end) {
current = chars.get(start);
chars = chars.set(start, CharacterMetadata.applyEntity(current, null));
start++;
}
return block.set('characterList', chars);
}
}
return block;
...
function applyInlineStyle(contentState, selectionState, inlineStyle) { return ContentStateInlineStyle.add(contentState, selectionState, inlineStyle); }
...
var newContent;
// If the style is already present for the selection range, remove it.
// Otherwise, apply it.
if (currentStyle.has(inlineStyle)) {
newContent = DraftModifier.removeInlineStyle(content, selection, inlineStyle);
} else {
newContent = DraftModifier.applyInlineStyle(content, selection, inlineStyle);
}
return EditorState.push(editorState, newContent, 'change-inline-style');
},
toggleLink: function toggleLink(editorState, targetSelection, entityKey) {
var withoutLink = DraftModifier.applyEntity(editorState.getCurrentContent(), targetSelection, entityKey);
...
function insertText(contentState, targetRange, text, inlineStyle, entityKey) { !targetRange.isCollapsed() ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Target range must be collapsed for `insertText `.') : invariant(false) : void 0; return DraftModifier.replaceText(contentState, targetRange, text, inlineStyle, entityKey); }
...
return EditorState.push(editorState, newContentState, 'insert-fragment');
}
/**
* Insert text at a specified selection.
*/
function insertTextAtSelection(editorState, selection, text) {
var newContentState = DraftModifier.insertText(editorState.getCurrentContent(), selection
, text, editorState.getCurrentInlineStyle());
return EditorState.push(editorState, newContentState, 'insert-fragment');
}
module.exports = DraftEditorDragHandler;
/***/ },
/* 66 */
...
function mergeBlockData(contentState, selectionState, blockData) { return modifyBlockForContentState(contentState, selectionState, function (block) { return block.merge({ data: block.getData().merge(blockData) }); }); }
n/a
function moveText(contentState, removalRange, targetRange) { var movedFragment = getContentStateFragment(contentState, removalRange); var afterRemoval = DraftModifier.removeRange(contentState, removalRange, 'backward'); return DraftModifier.replaceWithFragment(afterRemoval, targetRange, movedFragment); }
...
editor.update(insertTextAtSelection(editorState, dropSelection, data.getText()));
}
};
function moveText(editorState, targetSelection) {
var newContentState = DraftModifier.moveText(editorState.getCurrentContent(), editorState
.getSelection(), targetSelection);
return EditorState.push(editorState, newContentState, 'insert-fragment');
}
/**
* Insert text at a specified selection.
*/
function insertTextAtSelection(editorState, selection, text) {
...
function removeInlineStyle(contentState, selectionState, inlineStyle) { return ContentStateInlineStyle.remove(contentState, selectionState, inlineStyle); }
...
// inline style on the document state itself.
var content = editorState.getCurrentContent();
var newContent;
// If the style is already present for the selection range, remove it.
// Otherwise, apply it.
if (currentStyle.has(inlineStyle)) {
newContent = DraftModifier.removeInlineStyle(content, selection, inlineStyle);
} else {
newContent = DraftModifier.applyInlineStyle(content, selection, inlineStyle);
}
return EditorState.push(editorState, newContent, 'change-inline-style');
},
...
function removeRange(contentState, rangeToRemove, removalDirection) { // Check whether the selection state overlaps with a single entity. // If so, try to remove the appropriate substring of the entity text. if (rangeToRemove.getAnchorKey() === rangeToRemove.getFocusKey()) { var key = rangeToRemove.getAnchorKey(); var startOffset = rangeToRemove.getStartOffset(); var endOffset = rangeToRemove.getEndOffset(); var block = contentState.getBlockForKey(key); var startEntity = block.getEntityAt(startOffset); var endEntity = block.getEntityAt(endOffset - 1); if (startEntity && startEntity === endEntity) { var adjustedRemovalRange = getCharacterRemovalRange(contentState.getEntityMap(), block, rangeToRemove, removalDirection); return removeRangeFromContentState(contentState, adjustedRemovalRange); } } var withoutEntities = removeEntitiesAtEdges(contentState, rangeToRemove); return removeRangeFromContentState(withoutEntities, rangeToRemove); }
...
!targetRange.isCollapsed() ? true ? invariant(false, 'Target range must be collapsed for `insertText`.') : invariant
(false) : void 0;
return DraftModifier.replaceText(contentState, targetRange, text, inlineStyle, entityKey);
},
moveText: function moveText(contentState, removalRange, targetRange) {
var movedFragment = getContentStateFragment(contentState, removalRange);
var afterRemoval = DraftModifier.removeRange(contentState, removalRange, 'backward
');
return DraftModifier.replaceWithFragment(afterRemoval, targetRange, movedFragment);
},
replaceWithFragment: function replaceWithFragment(contentState, targetRange, fragment) {
var withoutEntities = removeEntitiesAtEdges(contentState, targetRange);
var withoutText = removeRangeFromContentState(withoutEntities, targetRange);
...
function replaceText(contentState, rangeToReplace, text, inlineStyle, entityKey) { var withoutEntities = removeEntitiesAtEdges(contentState, rangeToReplace); var withoutText = removeRangeFromContentState(withoutEntities, rangeToReplace); var character = CharacterMetadata.create({ style: inlineStyle || OrderedSet(), entity: entityKey || null }); return insertTextIntoContentState(withoutText, withoutText.getSelectionAfter(), text, character); }
...
});
return insertTextIntoContentState(withoutText, withoutText.getSelectionAfter(), text, character);
},
insertText: function insertText(contentState, targetRange, text, inlineStyle, entityKey) {
!targetRange.isCollapsed() ? true ? invariant(false, 'Target range must be collapsed for `insertText`.') : invariant
(false) : void 0;
return DraftModifier.replaceText(contentState, targetRange, text, inlineStyle, entityKey
);
},
moveText: function moveText(contentState, removalRange, targetRange) {
var movedFragment = getContentStateFragment(contentState, removalRange);
var afterRemoval = DraftModifier.removeRange(contentState, removalRange, 'backward');
...
function replaceWithFragment(contentState, targetRange, fragment) { var withoutEntities = removeEntitiesAtEdges(contentState, targetRange); var withoutText = removeRangeFromContentState(withoutEntities, targetRange); return insertFragmentIntoContentState(withoutText, withoutText.getSelectionAfter(), fragment); }
...
},
moveText: function moveText(contentState, removalRange, targetRange) {
var movedFragment = getContentStateFragment(contentState, removalRange);
var afterRemoval = DraftModifier.removeRange(contentState, removalRange, 'backward');
return DraftModifier.replaceWithFragment(afterRemoval, targetRange, movedFragment
);
},
replaceWithFragment: function replaceWithFragment(contentState, targetRange, fragment) {
var withoutEntities = removeEntitiesAtEdges(contentState, targetRange);
var withoutText = removeRangeFromContentState(withoutEntities, targetRange);
return insertFragmentIntoContentState(withoutText, withoutText.getSelectionAfter(), fragment);
...
function setBlockData(contentState, selectionState, blockData) { return modifyBlockForContentState(contentState, selectionState, function (block) { return block.merge({ data: blockData }); }); }
n/a
function setBlockType(contentState, selectionState, blockType) { return modifyBlockForContentState(contentState, selectionState, function (block) { return block.merge({ type: blockType, depth: 0 }); }); }
...
var afterRemoval = DraftModifier.removeRange(contentState, selectionState, 'backward');
var targetSelection = afterRemoval.getSelectionAfter();
var afterSplit = DraftModifier.splitBlock(afterRemoval, targetSelection);
var insertionTarget = afterSplit.getSelectionAfter();
var asAtomicBlock = DraftModifier.setBlockType(afterSplit, insertionTarget, '
;atomic');
var charData = CharacterMetadata.create({ entity: entityKey });
var fragmentArray = [new ContentBlock({
key: generateRandomKey(),
type: 'atomic',
text: character,
...
function splitBlock(contentState, selectionState) { var withoutEntities = removeEntitiesAtEdges(contentState, selectionState); var withoutText = removeRangeFromContentState(withoutEntities, selectionState); return splitBlockInContentState(withoutText, withoutText.getSelectionAfter()); }
...
insertAtomicBlock: function insertAtomicBlock(editorState, entityKey, character) {
var contentState = editorState.getCurrentContent();
var selectionState = editorState.getSelection();
var afterRemoval = DraftModifier.removeRange(contentState, selectionState, 'backward');
var targetSelection = afterRemoval.getSelectionAfter();
var afterSplit = DraftModifier.splitBlock(afterRemoval, targetSelection);
var insertionTarget = afterSplit.getSelectionAfter();
var asAtomicBlock = DraftModifier.setBlockType(afterSplit, insertionTarget, 'atomic');
var charData = CharacterMetadata.create({ entity: entityKey });
var fragmentArray = [new ContentBlock({
...
function currentBlockContainsLink(editorState) { var selection = editorState.getSelection(); var contentState = editorState.getCurrentContent(); var entityMap = contentState.getEntityMap(); return contentState.getBlockForKey(selection.getAnchorKey()).getCharacterList().slice(selection.getStartOffset(), selection.getEndOffset ()).some(function (v) { var entity = v.getEntity(); return !!entity && entityMap.__get(entity).getType() === 'LINK'; }); }
n/a
function getCurrentBlockType(editorState) { var selection = editorState.getSelection(); return editorState.getCurrentContent().getBlockForKey(selection.getStartKey()).getType(); }
n/a
function getDataObjectForLinkURL(uri) { return { url: uri.toString() }; }
n/a
function handleKeyCommand(editorState, command) { switch (command) { case 'bold': return RichTextEditorUtil.toggleInlineStyle(editorState, 'BOLD'); case 'italic': return RichTextEditorUtil.toggleInlineStyle(editorState, 'ITALIC'); case 'underline': return RichTextEditorUtil.toggleInlineStyle(editorState, 'UNDERLINE'); case 'code': return RichTextEditorUtil.toggleCode(editorState); case 'backspace': case 'backspace-word': case 'backspace-to-start-of-line': return RichTextEditorUtil.onBackspace(editorState); case 'delete': case 'delete-word': case 'delete-to-end-of-block': return RichTextEditorUtil.onDelete(editorState); default: // they may have custom editor commands; ignore those return null; } }
...
}
// At this point, we know that we're handling a command of some kind, so
// we don't want to insert a character following the keydown.
e.preventDefault();
// Allow components higher up the tree to handle the command first.
if (editor.props.handleKeyCommand && isEventHandled(editor.props.handleKeyCommand
span>(command))) {
return;
}
var newState = onKeyCommand(command, editorState);
if (newState !== editorState) {
editor.update(newState);
}
...
function insertSoftNewline(editorState) { var contentState = DraftModifier.insertText(editorState.getCurrentContent(), editorState.getSelection(), '\n', editorState.getCurrentInlineStyle (), null); var newEditorState = EditorState.push(editorState, contentState, 'insert-characters'); return EditorState.forceSelection(newEditorState, contentState.getSelectionAfter()); }
n/a
function onBackspace(editorState) { var selection = editorState.getSelection(); if (!selection.isCollapsed() || selection.getAnchorOffset() || selection.getFocusOffset()) { return null; } // First, try to remove a preceding atomic block. var content = editorState.getCurrentContent(); var startKey = selection.getStartKey(); var blockBefore = content.getBlockBefore(startKey); if (blockBefore && blockBefore.getType() === 'atomic') { var blockMap = content.getBlockMap()['delete'](blockBefore.getKey()); var withoutAtomicBlock = content.merge({ blockMap: blockMap, selectionAfter: selection }); if (withoutAtomicBlock !== content) { return EditorState.push(editorState, withoutAtomicBlock, 'remove-range'); } } // If that doesn't succeed, try to remove the current block style. var withoutBlockStyle = RichTextEditorUtil.tryToRemoveBlockStyle(editorState); if (withoutBlockStyle) { return EditorState.push(editorState, withoutBlockStyle, 'change-block-type'); } return null; }
...
case 'underline':
return RichTextEditorUtil.toggleInlineStyle(editorState, 'UNDERLINE');
case 'code':
return RichTextEditorUtil.toggleCode(editorState);
case 'backspace':
case 'backspace-word':
case 'backspace-to-start-of-line':
return RichTextEditorUtil.onBackspace(editorState);
case 'delete':
case 'delete-word':
case 'delete-to-end-of-block':
return RichTextEditorUtil.onDelete(editorState);
default:
// they may have custom editor commands; ignore those
return null;
...
function onDelete(editorState) { var selection = editorState.getSelection(); if (!selection.isCollapsed()) { return null; } var content = editorState.getCurrentContent(); var startKey = selection.getStartKey(); var block = content.getBlockForKey(startKey); var length = block.getLength(); // The cursor is somewhere within the text. Behave normally. if (selection.getStartOffset() < length) { return null; } var blockAfter = content.getBlockAfter(startKey); if (!blockAfter || blockAfter.getType() !== 'atomic') { return null; } var atomicBlockTarget = selection.merge({ focusKey: blockAfter.getKey(), focusOffset: blockAfter.getLength() }); var withoutAtomicBlock = DraftModifier.removeRange(content, atomicBlockTarget, 'forward'); if (withoutAtomicBlock !== content) { return EditorState.push(editorState, withoutAtomicBlock, 'remove-range'); } return null; }
...
case 'backspace':
case 'backspace-word':
case 'backspace-to-start-of-line':
return RichTextEditorUtil.onBackspace(editorState);
case 'delete':
case 'delete-word':
case 'delete-to-end-of-block':
return RichTextEditorUtil.onDelete(editorState);
default:
// they may have custom editor commands; ignore those
return null;
}
},
insertSoftNewline: function insertSoftNewline(editorState) {
...
function onTab(event, editorState, maxDepth) { var selection = editorState.getSelection(); var key = selection.getAnchorKey(); if (key !== selection.getFocusKey()) { return editorState; } var content = editorState.getCurrentContent(); var block = content.getBlockForKey(key); var type = block.getType(); if (type !== 'unordered-list-item' && type !== 'ordered-list-item') { return editorState; } event.preventDefault(); // Only allow indenting one level beyond the block above, and only if // the block above is a list item as well. var blockAbove = content.getBlockBefore(key); if (!blockAbove) { return editorState; } var typeAbove = blockAbove.getType(); if (typeAbove !== 'unordered-list-item' && typeAbove !== 'ordered-list-item') { return editorState; } var depth = block.getDepth(); if (!event.shiftKey && depth === maxDepth) { return editorState; } maxDepth = Math.min(blockAbove.getDepth() + 1, maxDepth); var withAdjustment = adjustBlockDepthForContentState(content, selection, event.shiftKey ? -1 : 1, maxDepth); return EditorState.push(editorState, withAdjustment, 'adjust-depth'); }
...
}
break;
case Keys.ESC:
e.preventDefault();
editor.props.onEscape && editor.props.onEscape(e);
return;
case Keys.TAB:
editor.props.onTab && editor.props.onTab(e);
return;
case Keys.UP:
editor.props.onUpArrow && editor.props.onUpArrow(e);
return;
case Keys.DOWN:
editor.props.onDownArrow && editor.props.onDownArrow(e);
return;
...
function toggleBlockType(editorState, blockType) { var selection = editorState.getSelection(); var startKey = selection.getStartKey(); var endKey = selection.getEndKey(); var content = editorState.getCurrentContent(); var target = selection; // Triple-click can lead to a selection that includes offset 0 of the // following block. The `SelectionState` for this case is accurate, but // we should avoid toggling block type for the trailing block because it // is a confusing interaction. if (startKey !== endKey && selection.getEndOffset() === 0) { var blockBefore = nullthrows(content.getBlockBefore(endKey)); endKey = blockBefore.getKey(); target = target.merge({ anchorKey: startKey, anchorOffset: selection.getStartOffset(), focusKey: endKey, focusOffset: blockBefore.getLength(), isBackward: false }); } var hasAtomicBlock = content.getBlockMap().skipWhile(function (_, k) { return k !== startKey; }).reverse().skipWhile(function (_, k) { return k !== endKey; }).some(function (v) { return v.getType() === 'atomic'; }); if (hasAtomicBlock) { return editorState; } var typeToSet = content.getBlockForKey(startKey).getType() === blockType ? 'unstyled' : blockType; return EditorState.push(editorState, DraftModifier.setBlockType(content, target, typeToSet), 'change-block-type'); }
...
toggleCode: function toggleCode(editorState) {
var selection = editorState.getSelection();
var anchorKey = selection.getAnchorKey();
var focusKey = selection.getFocusKey();
if (selection.isCollapsed() || anchorKey !== focusKey) {
return RichTextEditorUtil.toggleBlockType(editorState, 'code-block');
}
return RichTextEditorUtil.toggleInlineStyle(editorState, 'CODE');
},
/**
* Toggle the specified inline style for the selection. If the
...
function toggleCode(editorState) { var selection = editorState.getSelection(); var anchorKey = selection.getAnchorKey(); var focusKey = selection.getFocusKey(); if (selection.isCollapsed() || anchorKey !== focusKey) { return RichTextEditorUtil.toggleBlockType(editorState, 'code-block'); } return RichTextEditorUtil.toggleInlineStyle(editorState, 'CODE'); }
...
case 'bold':
return RichTextEditorUtil.toggleInlineStyle(editorState, 'BOLD');
case 'italic':
return RichTextEditorUtil.toggleInlineStyle(editorState, 'ITALIC');
case 'underline':
return RichTextEditorUtil.toggleInlineStyle(editorState, 'UNDERLINE');
case 'code':
return RichTextEditorUtil.toggleCode(editorState);
case 'backspace':
case 'backspace-word':
case 'backspace-to-start-of-line':
return RichTextEditorUtil.onBackspace(editorState);
case 'delete':
case 'delete-word':
case 'delete-to-end-of-block':
...
function toggleInlineStyle(editorState, inlineStyle) { var selection = editorState.getSelection(); var currentStyle = editorState.getCurrentInlineStyle(); // If the selection is collapsed, toggle the specified style on or off and // set the result as the new inline style override. This will then be // used as the inline style for the next character to be inserted. if (selection.isCollapsed()) { return EditorState.setInlineStyleOverride(editorState, currentStyle.has(inlineStyle) ? currentStyle.remove(inlineStyle) : currentStyle .add(inlineStyle)); } // If characters are selected, immediately apply or remove the // inline style on the document state itself. var content = editorState.getCurrentContent(); var newContent; // If the style is already present for the selection range, remove it. // Otherwise, apply it. if (currentStyle.has(inlineStyle)) { newContent = DraftModifier.removeInlineStyle(content, selection, inlineStyle); } else { newContent = DraftModifier.applyInlineStyle(content, selection, inlineStyle); } return EditorState.push(editorState, newContent, 'change-inline-style'); }
...
getDataObjectForLinkURL: function getDataObjectForLinkURL(uri) {
return { url: uri.toString() };
},
handleKeyCommand: function handleKeyCommand(editorState, command) {
switch (command) {
case 'bold':
return RichTextEditorUtil.toggleInlineStyle(editorState, 'BOLD');
case 'italic':
return RichTextEditorUtil.toggleInlineStyle(editorState, 'ITALIC');
case 'underline':
return RichTextEditorUtil.toggleInlineStyle(editorState, 'UNDERLINE');
case 'code':
return RichTextEditorUtil.toggleCode(editorState);
case 'backspace':
...
function toggleLink(editorState, targetSelection, entityKey) { var withoutLink = DraftModifier.applyEntity(editorState.getCurrentContent(), targetSelection, entityKey); return EditorState.push(editorState, withoutLink, 'apply-entity'); }
n/a
function tryToRemoveBlockStyle(editorState) { var selection = editorState.getSelection(); var offset = selection.getAnchorOffset(); if (selection.isCollapsed() && offset === 0) { var key = selection.getAnchorKey(); var content = editorState.getCurrentContent(); var block = content.getBlockForKey(key); if (block.getLength() > 0) { return null; } var type = block.getType(); var blockBefore = content.getBlockBefore(key); if (type === 'code-block' && blockBefore && blockBefore.getType() === 'code-block') { return null; } if (type !== 'unstyled') { return DraftModifier.setBlockType(content, selection, 'unstyled'); } } return null; }
...
var withoutAtomicBlock = content.merge({ blockMap: blockMap, selectionAfter: selection });
if (withoutAtomicBlock !== content) {
return EditorState.push(editorState, withoutAtomicBlock, 'remove-range');
}
}
// If that doesn't succeed, try to remove the current block style.
var withoutBlockStyle = RichTextEditorUtil.tryToRemoveBlockStyle(editorState);
if (withoutBlockStyle) {
return EditorState.push(editorState, withoutBlockStyle, 'change-block-type');
}
return null;
},
...
function cut(editorState) { var content = editorState.getCurrentContent(); var selection = editorState.getSelection(); var targetRange = null; if (selection.isCollapsed()) { var anchorKey = selection.getAnchorKey(); var blockEnd = content.getBlockForKey(anchorKey).getLength(); if (blockEnd === selection.getAnchorOffset()) { return editorState; } targetRange = selection.set('focusOffset', blockEnd); } else { targetRange = selection; } targetRange = nullthrows(targetRange); clipboard = getContentStateFragment(content, targetRange); var afterRemoval = DraftModifier.removeRange(content, targetRange, 'forward'); if (afterRemoval === content) { return editorState; } return EditorState.push(editorState, afterRemoval, 'remove-range'); }
...
case 'transpose-characters':
return keyCommandTransposeCharacters(editorState);
case 'move-selection-to-start-of-block':
return keyCommandMoveSelectionToStartOfBlock(editorState);
case 'move-selection-to-end-of-block':
return keyCommandMoveSelectionToEndOfBlock(editorState);
case 'secondary-cut':
return SecondaryClipboard.cut(editorState);
case 'secondary-paste':
return SecondaryClipboard.paste(editorState);
default:
return editorState;
}
}
...
function paste(editorState) { if (!clipboard) { return editorState; } var newContent = DraftModifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), clipboard); return EditorState.push(editorState, newContent, 'insert-fragment'); }
...
case 'move-selection-to-start-of-block':
return keyCommandMoveSelectionToStartOfBlock(editorState);
case 'move-selection-to-end-of-block':
return keyCommandMoveSelectionToEndOfBlock(editorState);
case 'secondary-cut':
return SecondaryClipboard.cut(editorState);
case 'secondary-paste':
return SecondaryClipboard.paste(editorState);
default:
return editorState;
}
}
/**
* Intercept keydown behavior to handle keys and commands manually, if desired.
...
function SelectionState() { _classCallCheck(this, SelectionState); return _possibleConstructorReturn(this, _SelectionStateRecord.apply(this, arguments)); }
n/a
function createEmpty(key) { return new SelectionState({ anchorKey: key, anchorOffset: 0, focusKey: key, focusOffset: 0, isBackward: false, hasFocus: false }); }
...
import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState} from 'draft-js';
class MyEditor extends React.Component {
constructor(props) {
super(props);
this.state = {editorState: EditorState.createEmpty()};
this.onChange = (editorState) => this.setState({editorState});
}
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange} />
);
}
...
function getAnchorKey() { return this.get('anchorKey'); }
...
return insertFragmentIntoContentState(withoutText, withoutText.getSelectionAfter(), fragment);
},
removeRange: function removeRange(contentState, rangeToRemove, removalDirection) {
// Check whether the selection state overlaps with a single entity.
// If so, try to remove the appropriate substring of the entity text.
if (rangeToRemove.getAnchorKey() === rangeToRemove.getFocusKey()) {
var key = rangeToRemove.getAnchorKey();
var startOffset = rangeToRemove.getStartOffset();
var endOffset = rangeToRemove.getEndOffset();
var block = contentState.getBlockForKey(key);
var startEntity = block.getEntityAt(startOffset);
var endEntity = block.getEntityAt(endOffset - 1);
...
function getAnchorOffset() { return this.get('anchorOffset'); }
...
function SelectionState() {
_classCallCheck(this, SelectionState);
return _possibleConstructorReturn(this, _SelectionStateRecord.apply(this, arguments));
}
SelectionState.prototype.serialize = function serialize() {
return 'Anchor: ' + this.getAnchorKey() + ':' + this.getAnchorOffset
() + ', ' + 'Focus: ' + this.getFocusKey() + ':' + this.getFocusOffset() + ', ' +
x27;Is Backward: ' + String(this.getIsBackward()) + ', ' + 'Has Focus: ' + String(this.getHasFocus());
};
SelectionState.prototype.getAnchorKey = function getAnchorKey() {
return this.get('anchorKey');
};
SelectionState.prototype.getAnchorOffset = function getAnchorOffset() {
...
function getEndKey() { return this.getIsBackward() ? this.getAnchorKey() : this.getFocusKey(); }
...
var generateRandomKey = __webpack_require__(7);
var removeEntitiesAtEdges = __webpack_require__(53);
function getContentStateFragment(contentState, selectionState) {
var startKey = selectionState.getStartKey();
var startOffset = selectionState.getStartOffset();
var endKey = selectionState.getEndKey();
var endOffset = selectionState.getEndOffset();
// Edge entities should be stripped to ensure that we don't preserve
// invalid partial entities when the fragment is reused. We do, however,
// preserve entities that are entirely within the selection range.
var contentWithoutEdgeEntities = removeEntitiesAtEdges(contentState, selectionState);
...
function getEndOffset() { return this.getIsBackward() ? this.getAnchorOffset() : this.getFocusOffset(); }
...
removeRange: function removeRange(contentState, rangeToRemove, removalDirection) {
// Check whether the selection state overlaps with a single entity.
// If so, try to remove the appropriate substring of the entity text.
if (rangeToRemove.getAnchorKey() === rangeToRemove.getFocusKey()) {
var key = rangeToRemove.getAnchorKey();
var startOffset = rangeToRemove.getStartOffset();
var endOffset = rangeToRemove.getEndOffset();
var block = contentState.getBlockForKey(key);
var startEntity = block.getEntityAt(startOffset);
var endEntity = block.getEntityAt(endOffset - 1);
if (startEntity && startEntity === endEntity) {
var adjustedRemovalRange = getCharacterRemovalRange(contentState.getEntityMap(), block, rangeToRemove, removalDirection
);
return removeRangeFromContentState(contentState, adjustedRemovalRange);
...
function getFocusKey() { return this.get('focusKey'); }
...
return insertFragmentIntoContentState(withoutText, withoutText.getSelectionAfter(), fragment);
},
removeRange: function removeRange(contentState, rangeToRemove, removalDirection) {
// Check whether the selection state overlaps with a single entity.
// If so, try to remove the appropriate substring of the entity text.
if (rangeToRemove.getAnchorKey() === rangeToRemove.getFocusKey()) {
var key = rangeToRemove.getAnchorKey();
var startOffset = rangeToRemove.getStartOffset();
var endOffset = rangeToRemove.getEndOffset();
var block = contentState.getBlockForKey(key);
var startEntity = block.getEntityAt(startOffset);
var endEntity = block.getEntityAt(endOffset - 1);
...
function getFocusOffset() { return this.get('focusOffset'); }
...
function SelectionState() {
_classCallCheck(this, SelectionState);
return _possibleConstructorReturn(this, _SelectionStateRecord.apply(this, arguments));
}
SelectionState.prototype.serialize = function serialize() {
return 'Anchor: ' + this.getAnchorKey() + ':' + this.getAnchorOffset() + ', ' + 'Focus:
x27; + this.getFocusKey() + ':' + this.getFocusOffset() + ', ' +
x27;Is Backward: ' + String(this.getIsBackward()) + ', ' + 'Has Focus: ' + String(this.getHasFocus());
};
SelectionState.prototype.getAnchorKey = function getAnchorKey() {
return this.get('anchorKey');
};
SelectionState.prototype.getAnchorOffset = function getAnchorOffset() {
...
function getHasFocus() { return this.get('hasFocus'); }
...
* This method should be used in cases where you need to explicitly
* move the DOM selection from one place to another without a change
* in ContentState.
*/
EditorState.forceSelection = function forceSelection(editorState, selection) {
if (!selection.getHasFocus()) {
selection = selection.set('hasFocus', true);
}
return updateSelection(editorState, selection, true);
};
/**
* Move selection to the end of the editor without forcing focus.
...
function getIsBackward() { return this.get('isBackward'); }
...
function SelectionState() {
_classCallCheck(this, SelectionState);
return _possibleConstructorReturn(this, _SelectionStateRecord.apply(this, arguments));
}
SelectionState.prototype.serialize = function serialize() {
return 'Anchor: ' + this.getAnchorKey() + ':' + this.getAnchorOffset() + ', ' + 'Focus:
x27; + this.getFocusKey() + ':' + this.getFocusOffset() + ', ' + 'Is Backward: ' + String(this.getIsBackward()) + ', ' + 'Has Focus: ' + String(this.getHasFocus());
};
SelectionState.prototype.getAnchorKey = function getAnchorKey() {
return this.get('anchorKey');
};
SelectionState.prototype.getAnchorOffset = function getAnchorOffset() {
...
function getStartKey() { return this.getIsBackward() ? this.getFocusKey() : this.getAnchorKey(); }
...
*/
function mustBecomeBoundary(editorState, changeType) {
var lastChangeType = editorState.getLastChangeType();
return changeType !== lastChangeType || changeType !== 'insert-characters' && changeType !== 'backspace
-character' && changeType !== 'delete-character';
}
function getInlineStyleForCollapsedSelection(content, selection) {
var startKey = selection.getStartKey();
var startOffset = selection.getStartOffset();
var startBlock = content.getBlockForKey(startKey);
// If the cursor is not at the start of the block, look backward to
// preserve the style of the preceding character.
if (startOffset > 0) {
return startBlock.getInlineStyleAt(startOffset - 1);
...
function getStartOffset() { return this.getIsBackward() ? this.getFocusOffset() : this.getAnchorOffset(); }
...
function mustBecomeBoundary(editorState, changeType) {
var lastChangeType = editorState.getLastChangeType();
return changeType !== lastChangeType || changeType !== 'insert-characters' && changeType !== 'backspace
-character' && changeType !== 'delete-character';
}
function getInlineStyleForCollapsedSelection(content, selection) {
var startKey = selection.getStartKey();
var startOffset = selection.getStartOffset();
var startBlock = content.getBlockForKey(startKey);
// If the cursor is not at the start of the block, look backward to
// preserve the style of the preceding character.
if (startOffset > 0) {
return startBlock.getInlineStyleAt(startOffset - 1);
}
...
function hasEdgeWithin(blockKey, start, end) { var anchorKey = this.getAnchorKey(); var focusKey = this.getFocusKey(); if (anchorKey === focusKey && anchorKey === blockKey) { var selectionStart = this.getStartOffset(); var selectionEnd = this.getEndOffset(); return start <= selectionEnd && selectionStart <= end; } if (blockKey !== anchorKey && blockKey !== focusKey) { return false; } var offsetToCheck = blockKey === anchorKey ? this.getAnchorOffset() : this.getFocusOffset(); return start <= offsetToCheck && end >= offsetToCheck; }
...
EditorState.prototype.getBlockTree = function getBlockTree(blockKey) {
return this.getImmutable().getIn(['treeMap', blockKey]);
};
EditorState.prototype.isSelectionAtStartOfContent = function isSelectionAtStartOfContent() {
var firstKey = this.getCurrentContent().getBlockMap().first().getKey();
return this.getSelection().hasEdgeWithin(firstKey, 0, 0);
};
EditorState.prototype.isSelectionAtEndOfContent = function isSelectionAtEndOfContent() {
var content = this.getCurrentContent();
var blockMap = content.getBlockMap();
var last = blockMap.last();
var end = last.getLength();
...
function isCollapsed() { return this.getAnchorKey() === this.getFocusKey() && this.getAnchorOffset() === this.getFocusOffset(); }
...
if (override != null) {
return override;
}
var content = this.getCurrentContent();
var selection = this.getSelection();
if (selection.isCollapsed()) {
return getInlineStyleForCollapsedSelection(content, selection);
}
return getInlineStyleForNonCollapsedSelection(content, selection);
};
EditorState.prototype.getBlockTree = function getBlockTree(blockKey) {
...
function serialize() { return 'Anchor: ' + this.getAnchorKey() + ':' + this.getAnchorOffset() + ', ' + 'Focus: ' + this.getFocusKey() + ':' + this.getFocusOffset () + ', ' + 'Is Backward: ' + String(this.getIsBackward()) + ', ' + 'Has Focus: ' + String(this.getHasFocus()); }
n/a