import { __require as requireFindAncestorOffsetKey } from './findAncestorOffsetKey.js';
import { __require as requireGetSelectionOffsetKeyForNode } from './getSelectionOffsetKeyForNode.js';
import { __require as requireGetUpdatedSelectionState } from './getUpdatedSelectionState.js';
import { __require as requireInvariant } from '../../../../fbjs@2.0.0/node_modules/fbjs/lib/invariant.js';
import { __require as requireIsElement } from './isElement.js';
import { __require as requireNullthrows } from '../../../../fbjs@2.0.0/node_modules/fbjs/lib/nullthrows.js';

/**
 * 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
 */

var getDraftEditorSelectionWithNodes_1;
var hasRequiredGetDraftEditorSelectionWithNodes;

function requireGetDraftEditorSelectionWithNodes () {
	if (hasRequiredGetDraftEditorSelectionWithNodes) return getDraftEditorSelectionWithNodes_1;
	hasRequiredGetDraftEditorSelectionWithNodes = 1;

	var findAncestorOffsetKey = requireFindAncestorOffsetKey();

	var getSelectionOffsetKeyForNode = requireGetSelectionOffsetKeyForNode();

	var getUpdatedSelectionState = requireGetUpdatedSelectionState();

	var invariant = requireInvariant();

	var isElement = requireIsElement();

	var nullthrows = requireNullthrows();

	/**
	 * Convert the current selection range to an anchor/focus pair of offset keys
	 * and values that can be interpreted by components.
	 */
	function getDraftEditorSelectionWithNodes(editorState, root, anchorNode, anchorOffset, focusNode, focusOffset) {
	  var anchorIsTextNode = anchorNode.nodeType === Node.TEXT_NODE;
	  var focusIsTextNode = focusNode.nodeType === Node.TEXT_NODE; // If the selection range lies only on text nodes, the task is simple.
	  // Find the nearest offset-aware elements and use the
	  // offset values supplied by the selection range.

	  if (anchorIsTextNode && focusIsTextNode) {
	    return {
	      selectionState: getUpdatedSelectionState(editorState, nullthrows(findAncestorOffsetKey(anchorNode)), anchorOffset, nullthrows(findAncestorOffsetKey(focusNode)), focusOffset),
	      needsRecovery: false
	    };
	  }

	  var anchorPoint = null;
	  var focusPoint = null;
	  var needsRecovery = true; // An element is selected. Convert this selection range into leaf offset
	  // keys and offset values for consumption at the component level. This
	  // is common in Firefox, where select-all and triple click behavior leads
	  // to entire elements being selected.
	  //
	  // Note that we use the `needsRecovery` parameter in the callback here. This
	  // is because when certain elements are selected, the behavior for subsequent
	  // cursor movement (e.g. via arrow keys) is uncertain and may not match
	  // expectations at the component level. For example, if an entire <div> is
	  // selected and the user presses the right arrow, Firefox keeps the selection
	  // on the <div>. If we allow subsequent keypresses to insert characters
	  // natively, they will be inserted into a browser-created text node to the
	  // right of that <div>. This is obviously undesirable.
	  //
	  // With the `needsRecovery` flag, we inform the caller that it is responsible
	  // for manually setting the selection state on the rendered document to
	  // ensure proper selection state maintenance.

	  if (anchorIsTextNode) {
	    anchorPoint = {
	      key: nullthrows(findAncestorOffsetKey(anchorNode)),
	      offset: anchorOffset
	    };
	    focusPoint = getPointForNonTextNode(root, focusNode, focusOffset);
	  } else if (focusIsTextNode) {
	    focusPoint = {
	      key: nullthrows(findAncestorOffsetKey(focusNode)),
	      offset: focusOffset
	    };
	    anchorPoint = getPointForNonTextNode(root, anchorNode, anchorOffset);
	  } else {
	    anchorPoint = getPointForNonTextNode(root, anchorNode, anchorOffset);
	    focusPoint = getPointForNonTextNode(root, focusNode, focusOffset); // If the selection is collapsed on an empty block, don't force recovery.
	    // This way, on arrow key selection changes, the browser can move the
	    // cursor from a non-zero offset on one block, through empty blocks,
	    // to a matching non-zero offset on other text blocks.

	    if (anchorNode === focusNode && anchorOffset === focusOffset) {
	      needsRecovery = !!anchorNode.firstChild && anchorNode.firstChild.nodeName !== 'BR';
	    }
	  }

	  return {
	    selectionState: getUpdatedSelectionState(editorState, anchorPoint.key, anchorPoint.offset, focusPoint.key, focusPoint.offset),
	    needsRecovery: needsRecovery
	  };
	}
	/**
	 * Identify the first leaf descendant for the given node.
	 */


	function getFirstLeaf(node) {
	  while (node.firstChild && ( // data-blocks has no offset
	  isElement(node.firstChild) && node.firstChild.getAttribute('data-blocks') === 'true' || getSelectionOffsetKeyForNode(node.firstChild))) {
	    node = node.firstChild;
	  }

	  return node;
	}
	/**
	 * Identify the last leaf descendant for the given node.
	 */


	function getLastLeaf(node) {
	  while (node.lastChild && ( // data-blocks has no offset
	  isElement(node.lastChild) && node.lastChild.getAttribute('data-blocks') === 'true' || getSelectionOffsetKeyForNode(node.lastChild))) {
	    node = node.lastChild;
	  }

	  return node;
	}

	function getPointForNonTextNode(editorRoot, startNode, childOffset) {
	  var node = startNode;
	  var offsetKey = findAncestorOffsetKey(node);
	  !(offsetKey != null || editorRoot && (editorRoot === node || editorRoot.firstChild === node)) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Unknown node in selection range.') : invariant(false) : void 0; // If the editorRoot is the selection, step downward into the content
	  // wrapper.

	  if (editorRoot === node) {
	    node = node.firstChild;
	    !isElement(node) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Invalid DraftEditorContents node.') : invariant(false) : void 0;
	    var castedNode = node; // assignment only added for flow :/
	    // otherwise it throws in line 200 saying that node can be null or undefined

	    node = castedNode;
	    !(node.getAttribute('data-contents') === 'true') ? process.env.NODE_ENV !== "production" ? invariant(false, 'Invalid DraftEditorContents structure.') : invariant(false) : void 0;

	    if (childOffset > 0) {
	      childOffset = node.childNodes.length;
	    }
	  } // If the child offset is zero and we have an offset key, we're done.
	  // If there's no offset key because the entire editor is selected,
	  // find the leftmost ("first") leaf in the tree and use that as the offset
	  // key.


	  if (childOffset === 0) {
	    var key = null;

	    if (offsetKey != null) {
	      key = offsetKey;
	    } else {
	      var firstLeaf = getFirstLeaf(node);
	      key = nullthrows(getSelectionOffsetKeyForNode(firstLeaf));
	    }

	    return {
	      key: key,
	      offset: 0
	    };
	  }

	  var nodeBeforeCursor = node.childNodes[childOffset - 1];
	  var leafKey = null;
	  var textLength = null;

	  if (!getSelectionOffsetKeyForNode(nodeBeforeCursor)) {
	    // Our target node may be a leaf or a text node, in which case we're
	    // already where we want to be and can just use the child's length as
	    // our offset.
	    leafKey = nullthrows(offsetKey);
	    textLength = getTextContentLength(nodeBeforeCursor);
	  } else {
	    // Otherwise, we'll look at the child to the left of the cursor and find
	    // the last leaf node in its subtree.
	    var lastLeaf = getLastLeaf(nodeBeforeCursor);
	    leafKey = nullthrows(getSelectionOffsetKeyForNode(lastLeaf));
	    textLength = getTextContentLength(lastLeaf);
	  }

	  return {
	    key: leafKey,
	    offset: textLength
	  };
	}
	/**
	 * Return the length of a node's textContent, regarding single newline
	 * characters as zero-length. This allows us to avoid problems with identifying
	 * the correct selection offset for empty blocks in IE, in which we
	 * render newlines instead of break tags.
	 */


	function getTextContentLength(node) {
	  var textContent = node.textContent;
	  return textContent === '\n' ? 0 : textContent.length;
	}

	getDraftEditorSelectionWithNodes_1 = getDraftEditorSelectionWithNodes;
	return getDraftEditorSelectionWithNodes_1;
}

export { requireGetDraftEditorSelectionWithNodes as __require };
//# sourceMappingURL=getDraftEditorSelectionWithNodes.js.map
