react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / browser / ui / ReactDOMSelection.js
81155 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule ReactDOMSelection9*/1011"use strict";1213var ExecutionEnvironment = require('ExecutionEnvironment');1415var getNodeForCharacterOffset = require('getNodeForCharacterOffset');16var getTextContentAccessor = require('getTextContentAccessor');1718/**19* While `isCollapsed` is available on the Selection object and `collapsed`20* is available on the Range object, IE11 sometimes gets them wrong.21* If the anchor/focus nodes and offsets are the same, the range is collapsed.22*/23function isCollapsed(anchorNode, anchorOffset, focusNode, focusOffset) {24return anchorNode === focusNode && anchorOffset === focusOffset;25}2627/**28* Get the appropriate anchor and focus node/offset pairs for IE.29*30* The catch here is that IE's selection API doesn't provide information31* about whether the selection is forward or backward, so we have to32* behave as though it's always forward.33*34* IE text differs from modern selection in that it behaves as though35* block elements end with a new line. This means character offsets will36* differ between the two APIs.37*38* @param {DOMElement} node39* @return {object}40*/41function getIEOffsets(node) {42var selection = document.selection;43var selectedRange = selection.createRange();44var selectedLength = selectedRange.text.length;4546// Duplicate selection so we can move range without breaking user selection.47var fromStart = selectedRange.duplicate();48fromStart.moveToElementText(node);49fromStart.setEndPoint('EndToStart', selectedRange);5051var startOffset = fromStart.text.length;52var endOffset = startOffset + selectedLength;5354return {55start: startOffset,56end: endOffset57};58}5960/**61* @param {DOMElement} node62* @return {?object}63*/64function getModernOffsets(node) {65var selection = window.getSelection && window.getSelection();6667if (!selection || selection.rangeCount === 0) {68return null;69}7071var anchorNode = selection.anchorNode;72var anchorOffset = selection.anchorOffset;73var focusNode = selection.focusNode;74var focusOffset = selection.focusOffset;7576var currentRange = selection.getRangeAt(0);7778// If the node and offset values are the same, the selection is collapsed.79// `Selection.isCollapsed` is available natively, but IE sometimes gets80// this value wrong.81var isSelectionCollapsed = isCollapsed(82selection.anchorNode,83selection.anchorOffset,84selection.focusNode,85selection.focusOffset86);8788var rangeLength = isSelectionCollapsed ? 0 : currentRange.toString().length;8990var tempRange = currentRange.cloneRange();91tempRange.selectNodeContents(node);92tempRange.setEnd(currentRange.startContainer, currentRange.startOffset);9394var isTempRangeCollapsed = isCollapsed(95tempRange.startContainer,96tempRange.startOffset,97tempRange.endContainer,98tempRange.endOffset99);100101var start = isTempRangeCollapsed ? 0 : tempRange.toString().length;102var end = start + rangeLength;103104// Detect whether the selection is backward.105var detectionRange = document.createRange();106detectionRange.setStart(anchorNode, anchorOffset);107detectionRange.setEnd(focusNode, focusOffset);108var isBackward = detectionRange.collapsed;109110return {111start: isBackward ? end : start,112end: isBackward ? start : end113};114}115116/**117* @param {DOMElement|DOMTextNode} node118* @param {object} offsets119*/120function setIEOffsets(node, offsets) {121var range = document.selection.createRange().duplicate();122var start, end;123124if (typeof offsets.end === 'undefined') {125start = offsets.start;126end = start;127} else if (offsets.start > offsets.end) {128start = offsets.end;129end = offsets.start;130} else {131start = offsets.start;132end = offsets.end;133}134135range.moveToElementText(node);136range.moveStart('character', start);137range.setEndPoint('EndToStart', range);138range.moveEnd('character', end - start);139range.select();140}141142/**143* In modern non-IE browsers, we can support both forward and backward144* selections.145*146* Note: IE10+ supports the Selection object, but it does not support147* the `extend` method, which means that even in modern IE, it's not possible148* to programatically create a backward selection. Thus, for all IE149* versions, we use the old IE API to create our selections.150*151* @param {DOMElement|DOMTextNode} node152* @param {object} offsets153*/154function setModernOffsets(node, offsets) {155if (!window.getSelection) {156return;157}158159var selection = window.getSelection();160var length = node[getTextContentAccessor()].length;161var start = Math.min(offsets.start, length);162var end = typeof offsets.end === 'undefined' ?163start : Math.min(offsets.end, length);164165// IE 11 uses modern selection, but doesn't support the extend method.166// Flip backward selections, so we can set with a single range.167if (!selection.extend && start > end) {168var temp = end;169end = start;170start = temp;171}172173var startMarker = getNodeForCharacterOffset(node, start);174var endMarker = getNodeForCharacterOffset(node, end);175176if (startMarker && endMarker) {177var range = document.createRange();178range.setStart(startMarker.node, startMarker.offset);179selection.removeAllRanges();180181if (start > end) {182selection.addRange(range);183selection.extend(endMarker.node, endMarker.offset);184} else {185range.setEnd(endMarker.node, endMarker.offset);186selection.addRange(range);187}188}189}190191var useIEOffsets = ExecutionEnvironment.canUseDOM && document.selection;192193var ReactDOMSelection = {194/**195* @param {DOMElement} node196*/197getOffsets: useIEOffsets ? getIEOffsets : getModernOffsets,198199/**200* @param {DOMElement|DOMTextNode} node201* @param {object} offsets202*/203setOffsets: useIEOffsets ? setIEOffsets : setModernOffsets204};205206module.exports = ReactDOMSelection;207208209