UNPKG

4.15 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2013-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9'use strict';
10
11var ReactDOMSelection = require('./ReactDOMSelection');
12
13var containsNode = require('fbjs/lib/containsNode');
14var focusNode = require('fbjs/lib/focusNode');
15var getActiveElement = require('fbjs/lib/getActiveElement');
16
17function isInDocument(node) {
18 return containsNode(document.documentElement, node);
19}
20
21/**
22 * @ReactInputSelection: React input selection module. Based on Selection.js,
23 * but modified to be suitable for react and has a couple of bug fixes (doesn't
24 * assume buttons have range selections allowed).
25 * Input selection module for React.
26 */
27var ReactInputSelection = {
28 hasSelectionCapabilities: function (elem) {
29 var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
30 return nodeName && (nodeName === 'input' && elem.type === 'text' || nodeName === 'textarea' || elem.contentEditable === 'true');
31 },
32
33 getSelectionInformation: function () {
34 var focusedElem = getActiveElement();
35 return {
36 focusedElem: focusedElem,
37 selectionRange: ReactInputSelection.hasSelectionCapabilities(focusedElem) ? ReactInputSelection.getSelection(focusedElem) : null
38 };
39 },
40
41 /**
42 * @restoreSelection: If any selection information was potentially lost,
43 * restore it. This is useful when performing operations that could remove dom
44 * nodes and place them back in, resulting in focus being lost.
45 */
46 restoreSelection: function (priorSelectionInformation) {
47 var curFocusedElem = getActiveElement();
48 var priorFocusedElem = priorSelectionInformation.focusedElem;
49 var priorSelectionRange = priorSelectionInformation.selectionRange;
50 if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
51 if (ReactInputSelection.hasSelectionCapabilities(priorFocusedElem)) {
52 ReactInputSelection.setSelection(priorFocusedElem, priorSelectionRange);
53 }
54 focusNode(priorFocusedElem);
55 }
56 },
57
58 /**
59 * @getSelection: Gets the selection bounds of a focused textarea, input or
60 * contentEditable node.
61 * -@input: Look up selection bounds of this input
62 * -@return {start: selectionStart, end: selectionEnd}
63 */
64 getSelection: function (input) {
65 var selection;
66
67 if ('selectionStart' in input) {
68 // Modern browser with input or textarea.
69 selection = {
70 start: input.selectionStart,
71 end: input.selectionEnd
72 };
73 } else if (document.selection && input.nodeName && input.nodeName.toLowerCase() === 'input') {
74 // IE8 input.
75 var range = document.selection.createRange();
76 // There can only be one selection per document in IE, so it must
77 // be in our element.
78 if (range.parentElement() === input) {
79 selection = {
80 start: -range.moveStart('character', -input.value.length),
81 end: -range.moveEnd('character', -input.value.length)
82 };
83 }
84 } else {
85 // Content editable or old IE textarea.
86 selection = ReactDOMSelection.getOffsets(input);
87 }
88
89 return selection || { start: 0, end: 0 };
90 },
91
92 /**
93 * @setSelection: Sets the selection bounds of a textarea or input and focuses
94 * the input.
95 * -@input Set selection bounds of this input or textarea
96 * -@offsets Object of same form that is returned from get*
97 */
98 setSelection: function (input, offsets) {
99 var start = offsets.start;
100 var end = offsets.end;
101 if (end === undefined) {
102 end = start;
103 }
104
105 if ('selectionStart' in input) {
106 input.selectionStart = start;
107 input.selectionEnd = Math.min(end, input.value.length);
108 } else if (document.selection && input.nodeName && input.nodeName.toLowerCase() === 'input') {
109 var range = input.createTextRange();
110 range.collapse(true);
111 range.moveStart('character', start);
112 range.moveEnd('character', end - start);
113 range.select();
114 } else {
115 ReactDOMSelection.setOffsets(input, offsets);
116 }
117 }
118};
119
120module.exports = ReactInputSelection;
\No newline at end of file