forked from Gitlink/forgeplus-react
228 lines
8.2 KiB
JavaScript
228 lines
8.2 KiB
JavaScript
/**
|
|
* Copyright (c) 2013-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule convertFromRawToDraftState
|
|
* @format
|
|
*
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var _assign = require('object-assign');
|
|
|
|
var _extends = _assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
var ContentBlock = require('./ContentBlock');
|
|
var ContentBlockNode = require('./ContentBlockNode');
|
|
var ContentState = require('./ContentState');
|
|
var DraftEntity = require('./DraftEntity');
|
|
var DraftFeatureFlags = require('./DraftFeatureFlags');
|
|
var DraftTreeAdapter = require('./DraftTreeAdapter');
|
|
var Immutable = require('immutable');
|
|
var SelectionState = require('./SelectionState');
|
|
|
|
var createCharacterList = require('./createCharacterList');
|
|
var decodeEntityRanges = require('./decodeEntityRanges');
|
|
var decodeInlineStyleRanges = require('./decodeInlineStyleRanges');
|
|
var generateRandomKey = require('./generateRandomKey');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
var experimentalTreeDataSupport = DraftFeatureFlags.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 _extends({}, range, { key: entityMap[range.key] });
|
|
})));
|
|
};
|
|
|
|
var addKeyIfMissing = function addKeyIfMissing(block) {
|
|
return _extends({}, 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 _extends({}, 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(_extends({}, 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(_extends({}, 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 = Array.isArray(rawState.blocks[0].children);
|
|
var rawBlocks = experimentalTreeDataSupport && !isTreeRawBlock ? DraftTreeAdapter.fromRawStateToRawTreeState(rawState).blocks : rawState.blocks;
|
|
|
|
if (!experimentalTreeDataSupport) {
|
|
return decodeContentBlocks(isTreeRawBlock ? DraftTreeAdapter.fromRawTreeStateToRawState(rawState).blocks : rawBlocks, entityMap);
|
|
}
|
|
|
|
return decodeContentBlockNodes(rawBlocks, entityMap);
|
|
};
|
|
|
|
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; |