/**
 * Get the key from the node's nearest offset-aware ancestor.
 */
function findAncestorOffsetKey(node: Node): ?string {
  while (node && node !== document.documentElement) {
    var key = getSelectionOffsetKeyForNode(node);
    if (key != null) {
      return key;
    }
    node = node.parentNode;
  }
  return null;
}
/**
 * Get the key from the node's nearest offset-aware ancestor.
 */
function findAncestorOffsetKey(node: Node): ?string {
  let searchNode = node;
  while (searchNode && searchNode !== document.documentElement) {
    const key = getSelectionOffsetKeyForNode(searchNode);
    if (key != null) {
      return key;
    }
    searchNode = searchNode.parentNode;
  }
  return null;
}
/**
 * Identify the last leaf descendant for the given node.
 */
function getLastLeaf(node: any): Node {
  while (
    node.lastChild &&
    // data-blocks has no offset
    ((node.lastChild instanceof Element &&
      node.lastChild.getAttribute('data-blocks') === 'true') ||
      getSelectionOffsetKeyForNode(node.lastChild))
  ) {
    node = node.lastChild;
  }
  return node;
}
function getPointForNonTextNode(
  editorRoot: ?HTMLElement,
  startNode: Node,
  childOffset: number,
): SelectionPoint {
  let node = startNode;
  var offsetKey: ?string = findAncestorOffsetKey(node);

  invariant(
    offsetKey != null ||
      (editorRoot && (editorRoot === node || editorRoot.firstChild === node)),
    'Unknown node in selection range.',
  );

  // If the editorRoot is the selection, step downward into the content
  // wrapper.
  if (editorRoot === node) {
    node = node.firstChild;
    invariant(
      node instanceof Element && node.getAttribute('data-contents') === 'true',
      'Invalid DraftEditorContents structure.',
    );
    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: ?string = null;
    if (offsetKey != null) {
      key = offsetKey;
    } else {
      var firstLeaf = getFirstLeaf(node);
      key = nullthrows(getSelectionOffsetKeyForNode(firstLeaf));
    }
    return {key, offset: 0};
  }

  var nodeBeforeCursor = node.childNodes[childOffset - 1];
  var leafKey: ?string = null;
  var textLength: ?number = 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,
  };
}
/**
 * Identify the last leaf descendant for the given node.
 */
function getLastLeaf(node: Node): Node {
  while (node.lastChild && getSelectionOffsetKeyForNode(node.lastChild)) {
    node = node.lastChild;
  }
  return node;
}