|
- /**
- * 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
- */
- 'use strict';
-
- var ContentBlockNode = require("./ContentBlockNode");
-
- var getNextDelimiterBlockKey = require("./getNextDelimiterBlockKey");
-
- var Immutable = require("immutable");
-
- var List = Immutable.List,
- Map = Immutable.Map;
-
- var transformBlock = function transformBlock(key, blockMap, func) {
- if (!key) {
- return;
- }
-
- var block = blockMap.get(key);
-
- if (!block) {
- return;
- }
-
- blockMap.set(key, func(block));
- };
- /**
- * Ancestors needs to be preserved when there are non selected
- * children to make sure we do not leave any orphans behind
- */
-
-
- var getAncestorsKeys = function getAncestorsKeys(blockKey, blockMap) {
- var parents = [];
-
- if (!blockKey) {
- return parents;
- }
-
- var blockNode = blockMap.get(blockKey);
-
- while (blockNode && blockNode.getParentKey()) {
- var parentKey = blockNode.getParentKey();
-
- if (parentKey) {
- parents.push(parentKey);
- }
-
- blockNode = parentKey ? blockMap.get(parentKey) : null;
- }
-
- return parents;
- };
- /**
- * Get all next delimiter keys until we hit a root delimiter and return
- * an array of key references
- */
-
-
- var getNextDelimitersBlockKeys = function getNextDelimitersBlockKeys(block, blockMap) {
- var nextDelimiters = [];
-
- if (!block) {
- return nextDelimiters;
- }
-
- var nextDelimiter = getNextDelimiterBlockKey(block, blockMap);
-
- while (nextDelimiter && blockMap.get(nextDelimiter)) {
- var _block = blockMap.get(nextDelimiter);
-
- nextDelimiters.push(nextDelimiter); // we do not need to keep checking all root node siblings, just the first occurance
-
- nextDelimiter = _block.getParentKey() ? getNextDelimiterBlockKey(_block, blockMap) : null;
- }
-
- return nextDelimiters;
- };
-
- var getNextValidSibling = function getNextValidSibling(block, blockMap, originalBlockMap) {
- if (!block) {
- return null;
- } // note that we need to make sure we refer to the original block since this
- // function is called within a withMutations
-
-
- var nextValidSiblingKey = originalBlockMap.get(block.getKey()).getNextSiblingKey();
-
- while (nextValidSiblingKey && !blockMap.get(nextValidSiblingKey)) {
- nextValidSiblingKey = originalBlockMap.get(nextValidSiblingKey).getNextSiblingKey() || null;
- }
-
- return nextValidSiblingKey;
- };
-
- var getPrevValidSibling = function getPrevValidSibling(block, blockMap, originalBlockMap) {
- if (!block) {
- return null;
- } // note that we need to make sure we refer to the original block since this
- // function is called within a withMutations
-
-
- var prevValidSiblingKey = originalBlockMap.get(block.getKey()).getPrevSiblingKey();
-
- while (prevValidSiblingKey && !blockMap.get(prevValidSiblingKey)) {
- prevValidSiblingKey = originalBlockMap.get(prevValidSiblingKey).getPrevSiblingKey() || null;
- }
-
- return prevValidSiblingKey;
- };
-
- var updateBlockMapLinks = function updateBlockMapLinks(blockMap, startBlock, endBlock, originalBlockMap) {
- return blockMap.withMutations(function (blocks) {
- // update start block if its retained
- transformBlock(startBlock.getKey(), blocks, function (block) {
- return block.merge({
- nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
- prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
- });
- }); // update endblock if its retained
-
- transformBlock(endBlock.getKey(), blocks, function (block) {
- return block.merge({
- nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
- prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
- });
- }); // update start block parent ancestors
-
- getAncestorsKeys(startBlock.getKey(), originalBlockMap).forEach(function (parentKey) {
- return transformBlock(parentKey, blocks, function (block) {
- return block.merge({
- children: block.getChildKeys().filter(function (key) {
- return blocks.get(key);
- }),
- nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
- prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
- });
- });
- }); // update start block next - can only happen if startBlock == endBlock
-
- transformBlock(startBlock.getNextSiblingKey(), blocks, function (block) {
- return block.merge({
- prevSibling: startBlock.getPrevSiblingKey()
- });
- }); // update start block prev
-
- transformBlock(startBlock.getPrevSiblingKey(), blocks, function (block) {
- return block.merge({
- nextSibling: getNextValidSibling(block, blocks, originalBlockMap)
- });
- }); // update end block next
-
- transformBlock(endBlock.getNextSiblingKey(), blocks, function (block) {
- return block.merge({
- prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
- });
- }); // update end block prev
-
- transformBlock(endBlock.getPrevSiblingKey(), blocks, function (block) {
- return block.merge({
- nextSibling: endBlock.getNextSiblingKey()
- });
- }); // update end block parent ancestors
-
- getAncestorsKeys(endBlock.getKey(), originalBlockMap).forEach(function (parentKey) {
- transformBlock(parentKey, blocks, function (block) {
- return block.merge({
- children: block.getChildKeys().filter(function (key) {
- return blocks.get(key);
- }),
- nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
- prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
- });
- });
- }); // update next delimiters all the way to a root delimiter
-
- getNextDelimitersBlockKeys(endBlock, originalBlockMap).forEach(function (delimiterKey) {
- return transformBlock(delimiterKey, blocks, function (block) {
- return block.merge({
- nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
- prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
- });
- });
- }); // if parent (startBlock) was deleted
-
- if (blockMap.get(startBlock.getKey()) == null && blockMap.get(endBlock.getKey()) != null && endBlock.getParentKey() === startBlock.getKey() && endBlock.getPrevSiblingKey() == null) {
- var prevSiblingKey = startBlock.getPrevSiblingKey(); // endBlock becomes next sibling of parent's prevSibling
-
- transformBlock(endBlock.getKey(), blocks, function (block) {
- return block.merge({
- prevSibling: prevSiblingKey
- });
- });
- transformBlock(prevSiblingKey, blocks, function (block) {
- return block.merge({
- nextSibling: endBlock.getKey()
- });
- }); // Update parent for previous parent's children, and children for that parent
-
- var prevSibling = prevSiblingKey ? blockMap.get(prevSiblingKey) : null;
- var newParentKey = prevSibling ? prevSibling.getParentKey() : null;
- startBlock.getChildKeys().forEach(function (childKey) {
- transformBlock(childKey, blocks, function (block) {
- return block.merge({
- parent: newParentKey // set to null if there is no parent
-
- });
- });
- });
-
- if (newParentKey != null) {
- var newParent = blockMap.get(newParentKey);
- transformBlock(newParentKey, blocks, function (block) {
- return block.merge({
- children: newParent.getChildKeys().concat(startBlock.getChildKeys())
- });
- });
- } // last child of deleted parent should point to next sibling
-
-
- transformBlock(startBlock.getChildKeys().find(function (key) {
- var block = blockMap.get(key);
- return block.getNextSiblingKey() === null;
- }), blocks, function (block) {
- return block.merge({
- nextSibling: startBlock.getNextSiblingKey()
- });
- });
- }
- });
- };
-
- var removeRangeFromContentState = function removeRangeFromContentState(contentState, selectionState) {
- if (selectionState.isCollapsed()) {
- return contentState;
- }
-
- var blockMap = contentState.getBlockMap();
- var startKey = selectionState.getStartKey();
- var startOffset = selectionState.getStartOffset();
- var endKey = selectionState.getEndKey();
- var endOffset = selectionState.getEndOffset();
- var startBlock = blockMap.get(startKey);
- var endBlock = blockMap.get(endKey); // we assume that ContentBlockNode and ContentBlocks are not mixed together
-
- var isExperimentalTreeBlock = startBlock instanceof ContentBlockNode; // used to retain blocks that should not be deleted to avoid orphan children
-
- var parentAncestors = [];
-
- if (isExperimentalTreeBlock) {
- var endBlockchildrenKeys = endBlock.getChildKeys();
- var endBlockAncestors = getAncestorsKeys(endKey, blockMap); // endBlock has unselected siblings so we can not remove its ancestors parents
-
- if (endBlock.getNextSiblingKey()) {
- parentAncestors = parentAncestors.concat(endBlockAncestors);
- } // endBlock has children so can not remove this block or any of its ancestors
-
-
- if (!endBlockchildrenKeys.isEmpty()) {
- parentAncestors = parentAncestors.concat(endBlockAncestors.concat([endKey]));
- } // we need to retain all ancestors of the next delimiter block
-
-
- parentAncestors = parentAncestors.concat(getAncestorsKeys(getNextDelimiterBlockKey(endBlock, blockMap), blockMap));
- }
-
- var characterList;
-
- if (startBlock === endBlock) {
- characterList = removeFromList(startBlock.getCharacterList(), startOffset, endOffset);
- } else {
- characterList = startBlock.getCharacterList().slice(0, startOffset).concat(endBlock.getCharacterList().slice(endOffset));
- }
-
- var modifiedStart = startBlock.merge({
- text: startBlock.getText().slice(0, startOffset) + endBlock.getText().slice(endOffset),
- characterList: characterList
- }); // If cursor (collapsed) is at the start of the first child, delete parent
- // instead of child
-
- var shouldDeleteParent = isExperimentalTreeBlock && startOffset === 0 && endOffset === 0 && endBlock.getParentKey() === startKey && endBlock.getPrevSiblingKey() == null;
- var newBlocks = shouldDeleteParent ? Map([[startKey, null]]) : blockMap.toSeq().skipUntil(function (_, k) {
- return k === startKey;
- }).takeUntil(function (_, k) {
- return k === endKey;
- }).filter(function (_, k) {
- return parentAncestors.indexOf(k) === -1;
- }).concat(Map([[endKey, null]])).map(function (_, k) {
- return k === startKey ? modifiedStart : null;
- });
- var updatedBlockMap = blockMap.merge(newBlocks).filter(function (block) {
- return !!block;
- }); // Only update tree block pointers if the range is across blocks
-
- if (isExperimentalTreeBlock && startBlock !== endBlock) {
- updatedBlockMap = updateBlockMapLinks(updatedBlockMap, startBlock, endBlock, blockMap);
- }
-
- return contentState.merge({
- blockMap: updatedBlockMap,
- selectionBefore: selectionState,
- selectionAfter: selectionState.merge({
- anchorKey: startKey,
- anchorOffset: startOffset,
- focusKey: startKey,
- focusOffset: startOffset,
- isBackward: false
- })
- });
- };
- /**
- * Maintain persistence for target list when removing characters on the
- * head and tail of the character list.
- */
-
-
- var removeFromList = function removeFromList(targetList, startOffset, endOffset) {
- if (startOffset === 0) {
- while (startOffset < endOffset) {
- targetList = targetList.shift();
- startOffset++;
- }
- } else if (endOffset === targetList.count()) {
- while (endOffset > startOffset) {
- targetList = targetList.pop();
- endOffset--;
- }
- } else {
- var head = targetList.slice(0, startOffset);
- var tail = targetList.slice(endOffset);
- targetList = head.concat(tail).toList();
- }
-
- return targetList;
- };
-
- module.exports = removeRangeFromContentState;
|