draft-js
Version:
A React framework for building text editors.
226 lines (170 loc) • 8.98 kB
JavaScript
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*
* @emails oncall+draft_js
*/
;
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var ContentBlock = require("./ContentBlock");
var ContentBlockNode = require("./ContentBlockNode");
var ContentState = require("./ContentState");
var DraftEntity = require("./DraftEntity");
var DraftTreeAdapter = require("./DraftTreeAdapter");
var DraftTreeInvariants = require("./DraftTreeInvariants");
var SelectionState = require("./SelectionState");
var createCharacterList = require("./createCharacterList");
var decodeEntityRanges = require("./decodeEntityRanges");
var decodeInlineStyleRanges = require("./decodeInlineStyleRanges");
var generateRandomKey = require("./generateRandomKey");
var gkx = require("./gkx");
var Immutable = require("immutable");
var invariant = require("fbjs/lib/invariant");
var experimentalTreeDataSupport = gkx('draft_tree_data_support');
var List = Immutable.List,
Map = Immutable.Map,
OrderedMap = Immutable.OrderedMap;
var decodeBlockNodeConfig = function decodeBlockNodeConfig(block, entityMap) {
var key = block.key,
type = block.type,
data = block.data,
text = block.text,
depth = block.depth;
var blockNodeConfig = {
text: text,
depth: depth || 0,
type: type || 'unstyled',
key: key || generateRandomKey(),
data: Map(data),
characterList: decodeCharacterList(block, entityMap)
};
return blockNodeConfig;
};
var decodeCharacterList = function decodeCharacterList(block, entityMap) {
var text = block.text,
rawEntityRanges = block.entityRanges,
rawInlineStyleRanges = block.inlineStyleRanges;
var entityRanges = rawEntityRanges || [];
var inlineStyleRanges = rawInlineStyleRanges || []; // Translate entity range keys to the DraftEntity map.
return createCharacterList(decodeInlineStyleRanges(text, inlineStyleRanges), decodeEntityRanges(text, entityRanges.filter(function (range) {
return entityMap.hasOwnProperty(range.key);
}).map(function (range) {
return _objectSpread({}, range, {
key: entityMap[range.key]
});
})));
};
var addKeyIfMissing = function addKeyIfMissing(block) {
return _objectSpread({}, block, {
key: block.key || generateRandomKey()
});
};
/**
* Node stack is responsible to ensure we traverse the tree only once
* in depth order, while also providing parent refs to inner nodes to
* construct their links.
*/
var updateNodeStack = function updateNodeStack(stack, nodes, parentRef) {
var nodesWithParentRef = nodes.map(function (block) {
return _objectSpread({}, block, {
parentRef: parentRef
});
}); // since we pop nodes from the stack we need to insert them in reverse
return stack.concat(nodesWithParentRef.reverse());
};
/**
* This will build a tree draft content state by creating the node
* reference links into a single tree walk. Each node has a link
* reference to "parent", "children", "nextSibling" and "prevSibling"
* blockMap will be created using depth ordering.
*/
var decodeContentBlockNodes = function decodeContentBlockNodes(blocks, entityMap) {
return blocks // ensure children have valid keys to enable sibling links
.map(addKeyIfMissing).reduce(function (blockMap, block, index) {
!Array.isArray(block.children) ? process.env.NODE_ENV !== "production" ? invariant(false, 'invalid RawDraftContentBlock can not be converted to ContentBlockNode') : invariant(false) : void 0; // ensure children have valid keys to enable sibling links
var children = block.children.map(addKeyIfMissing); // root level nodes
var contentBlockNode = new ContentBlockNode(_objectSpread({}, decodeBlockNodeConfig(block, entityMap), {
prevSibling: index === 0 ? null : blocks[index - 1].key,
nextSibling: index === blocks.length - 1 ? null : blocks[index + 1].key,
children: List(children.map(function (child) {
return child.key;
}))
})); // push root node to blockMap
blockMap = blockMap.set(contentBlockNode.getKey(), contentBlockNode); // this stack is used to ensure we visit all nodes respecting depth ordering
var stack = updateNodeStack([], children, contentBlockNode); // start computing children nodes
while (stack.length > 0) {
// we pop from the stack and start processing this node
var node = stack.pop(); // parentRef already points to a converted ContentBlockNode
var parentRef = node.parentRef;
var siblings = parentRef.getChildKeys();
var _index = siblings.indexOf(node.key);
var isValidBlock = Array.isArray(node.children);
if (!isValidBlock) {
!isValidBlock ? process.env.NODE_ENV !== "production" ? invariant(false, 'invalid RawDraftContentBlock can not be converted to ContentBlockNode') : invariant(false) : void 0;
break;
} // ensure children have valid keys to enable sibling links
var _children = node.children.map(addKeyIfMissing);
var _contentBlockNode = new ContentBlockNode(_objectSpread({}, decodeBlockNodeConfig(node, entityMap), {
parent: parentRef.getKey(),
children: List(_children.map(function (child) {
return child.key;
})),
prevSibling: _index === 0 ? null : siblings.get(_index - 1),
nextSibling: _index === siblings.size - 1 ? null : siblings.get(_index + 1)
})); // push node to blockMap
blockMap = blockMap.set(_contentBlockNode.getKey(), _contentBlockNode); // this stack is used to ensure we visit all nodes respecting depth ordering
stack = updateNodeStack(stack, _children, _contentBlockNode);
}
return blockMap;
}, OrderedMap());
};
var decodeContentBlocks = function decodeContentBlocks(blocks, entityMap) {
return OrderedMap(blocks.map(function (block) {
var contentBlock = new ContentBlock(decodeBlockNodeConfig(block, entityMap));
return [contentBlock.getKey(), contentBlock];
}));
};
var decodeRawBlocks = function decodeRawBlocks(rawState, entityMap) {
var isTreeRawBlock = rawState.blocks.find(function (block) {
return Array.isArray(block.children) && block.children.length > 0;
});
var rawBlocks = experimentalTreeDataSupport && !isTreeRawBlock ? DraftTreeAdapter.fromRawStateToRawTreeState(rawState).blocks : rawState.blocks;
if (!experimentalTreeDataSupport) {
return decodeContentBlocks(isTreeRawBlock ? DraftTreeAdapter.fromRawTreeStateToRawState(rawState).blocks : rawBlocks, entityMap);
}
var blockMap = decodeContentBlockNodes(rawBlocks, entityMap); // in dev mode, check that the tree invariants are met
if (process.env.NODE_ENV !== "production") {
!DraftTreeInvariants.isValidTree(blockMap) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Should be a valid tree') : invariant(false) : void 0;
}
return blockMap;
};
var decodeRawEntityMap = function decodeRawEntityMap(rawState) {
var rawEntityMap = rawState.entityMap;
var entityMap = {}; // TODO: Update this once we completely remove DraftEntity
Object.keys(rawEntityMap).forEach(function (rawEntityKey) {
var _rawEntityMap$rawEnti = rawEntityMap[rawEntityKey],
type = _rawEntityMap$rawEnti.type,
mutability = _rawEntityMap$rawEnti.mutability,
data = _rawEntityMap$rawEnti.data; // get the key reference to created entity
entityMap[rawEntityKey] = DraftEntity.__create(type, mutability, data || {});
});
return entityMap;
};
var convertFromRawToDraftState = function convertFromRawToDraftState(rawState) {
!Array.isArray(rawState.blocks) ? process.env.NODE_ENV !== "production" ? invariant(false, 'invalid RawDraftContentState') : invariant(false) : void 0; // decode entities
var entityMap = decodeRawEntityMap(rawState); // decode blockMap
var blockMap = decodeRawBlocks(rawState, entityMap); // create initial selection
var selectionState = blockMap.isEmpty() ? new SelectionState() : SelectionState.createEmpty(blockMap.first().getKey());
return new ContentState({
blockMap: blockMap,
entityMap: entityMap,
selectionBefore: selectionState,
selectionAfter: selectionState
});
};
module.exports = convertFromRawToDraftState;