d2-ui
Version:
133 lines (118 loc) • 4.94 kB
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 setDraftEditorSelection
* @typechecks
*
*/
/**
* In modern non-IE browsers, we can support both forward and backward
* selections.
*
* Note: IE10+ supports the Selection object, but it does not support
* the `extend` method, which means that even in modern IE, it's not possible
* to programatically create a backward selection. Thus, for all IE
* versions, we use the old IE API to create our selections.
*/
;
var containsNode = require('fbjs/lib/containsNode');
var getActiveElement = require('fbjs/lib/getActiveElement');
function setDraftEditorSelection(selectionState, node, blockKey, nodeStart, nodeEnd) {
// It's possible that the editor has been removed from the DOM but
// our selection code doesn't know it yet. Forcing selection in
// this case may lead to errors, so just bail now.
if (!containsNode(document.documentElement, node)) {
return;
}
var selection = global.getSelection();
var anchorKey = selectionState.getAnchorKey();
var anchorOffset = selectionState.getAnchorOffset();
var focusKey = selectionState.getFocusKey();
var focusOffset = selectionState.getFocusOffset();
var isBackward = selectionState.getIsBackward();
// IE doesn't support backward selection. Swap key/offset pairs.
if (!selection.extend && isBackward) {
var tempKey = anchorKey;
var tempOffset = anchorOffset;
anchorKey = focusKey;
anchorOffset = focusOffset;
focusKey = tempKey;
focusOffset = tempOffset;
isBackward = false;
}
var hasAnchor = anchorKey === blockKey && nodeStart <= anchorOffset && nodeEnd >= anchorOffset;
var hasFocus = focusKey === blockKey && nodeStart <= focusOffset && nodeEnd >= focusOffset;
// If the selection is entirely bound within this node, set the selection
// and be done.
if (hasAnchor && hasFocus) {
selection.removeAllRanges();
addPointToSelection(selection, node, anchorOffset - nodeStart);
addFocusToSelection(selection, node, focusOffset - nodeStart);
return;
}
if (!isBackward) {
// If the anchor is within this node, set the range start.
if (hasAnchor) {
selection.removeAllRanges();
addPointToSelection(selection, node, anchorOffset - nodeStart);
}
// If the focus is within this node, we can assume that we have
// already set the appropriate start range on the selection, and
// can simply extend the selection.
if (hasFocus) {
addFocusToSelection(selection, node, focusOffset - nodeStart);
}
} else {
// If this node has the focus, set the selection range to be a
// collapsed range beginning here. Later, when we encounter the anchor,
// we'll use this information to extend the selection.
if (hasFocus) {
selection.removeAllRanges();
addPointToSelection(selection, node, focusOffset - nodeStart);
}
// If this node has the anchor, we may assume that the correct
// focus information is already stored on the selection object.
// We keep track of it, reset the selection range, and extend it
// back to the focus point.
if (hasAnchor) {
var storedFocusNode = selection.focusNode;
var storedFocusOffset = selection.focusOffset;
selection.removeAllRanges();
addPointToSelection(selection, node, anchorOffset - nodeStart);
addFocusToSelection(selection, storedFocusNode, storedFocusOffset);
}
}
}
/**
* Extend selection towards focus point.
*/
function addFocusToSelection(selection, node, offset) {
if (selection.extend && containsNode(getActiveElement(), node)) {
// If `extend` is called while another element has focus, an error is
// thrown. We therefore disable `extend` if the active element is somewhere
// other than the node we are selecting. This should only occur in Firefox,
// since it is the only browser to support multiple selections.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=921444.
selection.extend(node, offset);
} else {
// IE doesn't support extend. This will mean no backward selection.
// Extract the existing selection range and add focus to it.
// Additionally, clone the selection range. IE11 throws an
// InvalidStateError when attempting to access selection properties
// after the range is detached.
var range = selection.getRangeAt(0);
range.setEnd(node, offset);
selection.addRange(range.cloneRange());
}
}
function addPointToSelection(selection, node, offset) {
var range = document.createRange();
range.setStart(node, offset);
selection.addRange(range);
}
module.exports = setDraftEditorSelection;