/** * Copyright 2013-2015, 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 ReactInputSelection */ 'use strict'; var ReactDOMSelection = require("./ReactDOMSelection"); var containsNode = require("./containsNode"); var focusNode = require("./focusNode"); var getActiveElement = require("./getActiveElement"); function isInDocument(node) { return containsNode(document.documentElement, node); } /** * @ReactInputSelection: React input selection module. Based on Selection.js, * but modified to be suitable for react and has a couple of bug fixes (doesn't * assume buttons have range selections allowed). * Input selection module for React. */ var ReactInputSelection = { hasSelectionCapabilities: function(elem) { return elem && ( ((elem.nodeName === 'INPUT' && elem.type === 'text') || elem.nodeName === 'TEXTAREA' || elem.contentEditable === 'true') ); }, getSelectionInformation: function() { var focusedElem = getActiveElement(); return { focusedElem: focusedElem, selectionRange: ReactInputSelection.hasSelectionCapabilities(focusedElem) ? ReactInputSelection.getSelection(focusedElem) : null }; }, /** * @restoreSelection: If any selection information was potentially lost, * restore it. This is useful when performing operations that could remove dom * nodes and place them back in, resulting in focus being lost. */ restoreSelection: function(priorSelectionInformation) { var curFocusedElem = getActiveElement(); var priorFocusedElem = priorSelectionInformation.focusedElem; var priorSelectionRange = priorSelectionInformation.selectionRange; if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) { if (ReactInputSelection.hasSelectionCapabilities(priorFocusedElem)) { ReactInputSelection.setSelection( priorFocusedElem, priorSelectionRange ); } focusNode(priorFocusedElem); } }, /** * @getSelection: Gets the selection bounds of a focused textarea, input or * contentEditable node. * -@input: Look up selection bounds of this input * -@return {start: selectionStart, end: selectionEnd} */ getSelection: function(input) { var selection; if ('selectionStart' in input) { // Modern browser with input or textarea. selection = { start: input.selectionStart, end: input.selectionEnd }; } else if (document.selection && input.nodeName === 'INPUT') { // IE8 input. var range = document.selection.createRange(); // There can only be one selection per document in IE, so it must // be in our element. if (range.parentElement() === input) { selection = { start: -range.moveStart('character', -input.value.length), end: -range.moveEnd('character', -input.value.length) }; } } else { // Content editable or old IE textarea. selection = ReactDOMSelection.getOffsets(input); } return selection || {start: 0, end: 0}; }, /** * @setSelection: Sets the selection bounds of a textarea or input and focuses * the input. * -@input Set selection bounds of this input or textarea * -@offsets Object of same form that is returned from get* */ setSelection: function(input, offsets) { var start = offsets.start; var end = offsets.end; if (typeof end === 'undefined') { end = start; } if ('selectionStart' in input) { input.selectionStart = start; input.selectionEnd = Math.min(end, input.value.length); } else if (document.selection && input.nodeName === 'INPUT') { var range = input.createTextRange(); range.collapse(true); range.moveStart('character', start); range.moveEnd('character', end - start); range.select(); } else { ReactDOMSelection.setOffsets(input, offsets); } } }; module.exports = ReactInputSelection;