Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

292 строки
9.9 KiB

  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  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. * @format
  8. *
  9. * @emails oncall+draft_js
  10. */
  11. 'use strict';
  12. var DraftEffects = require("./DraftEffects");
  13. var DraftJsDebugLogging = require("./DraftJsDebugLogging");
  14. var UserAgent = require("fbjs/lib/UserAgent");
  15. var containsNode = require("fbjs/lib/containsNode");
  16. var getActiveElement = require("fbjs/lib/getActiveElement");
  17. var getCorrectDocumentFromNode = require("./getCorrectDocumentFromNode");
  18. var invariant = require("fbjs/lib/invariant");
  19. var isElement = require("./isElement");
  20. var isIE = UserAgent.isBrowser('IE');
  21. function getAnonymizedDOM(node, getNodeLabels) {
  22. if (!node) {
  23. return '[empty]';
  24. }
  25. var anonymized = anonymizeTextWithin(node, getNodeLabels);
  26. if (anonymized.nodeType === Node.TEXT_NODE) {
  27. return anonymized.textContent;
  28. }
  29. !isElement(anonymized) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Node must be an Element if it is not a text node.') : invariant(false) : void 0;
  30. var castedElement = anonymized;
  31. return castedElement.outerHTML;
  32. }
  33. function anonymizeTextWithin(node, getNodeLabels) {
  34. var labels = getNodeLabels !== undefined ? getNodeLabels(node) : [];
  35. if (node.nodeType === Node.TEXT_NODE) {
  36. var length = node.textContent.length;
  37. return getCorrectDocumentFromNode(node).createTextNode('[text ' + length + (labels.length ? ' | ' + labels.join(', ') : '') + ']');
  38. }
  39. var clone = node.cloneNode();
  40. if (clone.nodeType === 1 && labels.length) {
  41. clone.setAttribute('data-labels', labels.join(', '));
  42. }
  43. var childNodes = node.childNodes;
  44. for (var ii = 0; ii < childNodes.length; ii++) {
  45. clone.appendChild(anonymizeTextWithin(childNodes[ii], getNodeLabels));
  46. }
  47. return clone;
  48. }
  49. function getAnonymizedEditorDOM(node, getNodeLabels) {
  50. // grabbing the DOM content of the Draft editor
  51. var currentNode = node; // this should only be used after checking with isElement
  52. var castedNode = currentNode;
  53. while (currentNode) {
  54. if (isElement(currentNode) && castedNode.hasAttribute('contenteditable')) {
  55. // found the Draft editor container
  56. return getAnonymizedDOM(currentNode, getNodeLabels);
  57. } else {
  58. currentNode = currentNode.parentNode;
  59. castedNode = currentNode;
  60. }
  61. }
  62. return 'Could not find contentEditable parent of node';
  63. }
  64. function getNodeLength(node) {
  65. return node.nodeValue === null ? node.childNodes.length : node.nodeValue.length;
  66. }
  67. /**
  68. * In modern non-IE browsers, we can support both forward and backward
  69. * selections.
  70. *
  71. * Note: IE10+ supports the Selection object, but it does not support
  72. * the `extend` method, which means that even in modern IE, it's not possible
  73. * to programatically create a backward selection. Thus, for all IE
  74. * versions, we use the old IE API to create our selections.
  75. */
  76. function setDraftEditorSelection(selectionState, node, blockKey, nodeStart, nodeEnd) {
  77. // It's possible that the editor has been removed from the DOM but
  78. // our selection code doesn't know it yet. Forcing selection in
  79. // this case may lead to errors, so just bail now.
  80. var documentObject = getCorrectDocumentFromNode(node);
  81. if (!containsNode(documentObject.documentElement, node)) {
  82. return;
  83. }
  84. var selection = documentObject.defaultView.getSelection();
  85. var anchorKey = selectionState.getAnchorKey();
  86. var anchorOffset = selectionState.getAnchorOffset();
  87. var focusKey = selectionState.getFocusKey();
  88. var focusOffset = selectionState.getFocusOffset();
  89. var isBackward = selectionState.getIsBackward(); // IE doesn't support backward selection. Swap key/offset pairs.
  90. if (!selection.extend && isBackward) {
  91. var tempKey = anchorKey;
  92. var tempOffset = anchorOffset;
  93. anchorKey = focusKey;
  94. anchorOffset = focusOffset;
  95. focusKey = tempKey;
  96. focusOffset = tempOffset;
  97. isBackward = false;
  98. }
  99. var hasAnchor = anchorKey === blockKey && nodeStart <= anchorOffset && nodeEnd >= anchorOffset;
  100. var hasFocus = focusKey === blockKey && nodeStart <= focusOffset && nodeEnd >= focusOffset; // If the selection is entirely bound within this node, set the selection
  101. // and be done.
  102. if (hasAnchor && hasFocus) {
  103. selection.removeAllRanges();
  104. addPointToSelection(selection, node, anchorOffset - nodeStart, selectionState);
  105. addFocusToSelection(selection, node, focusOffset - nodeStart, selectionState);
  106. return;
  107. }
  108. if (!isBackward) {
  109. // If the anchor is within this node, set the range start.
  110. if (hasAnchor) {
  111. selection.removeAllRanges();
  112. addPointToSelection(selection, node, anchorOffset - nodeStart, selectionState);
  113. } // If the focus is within this node, we can assume that we have
  114. // already set the appropriate start range on the selection, and
  115. // can simply extend the selection.
  116. if (hasFocus) {
  117. addFocusToSelection(selection, node, focusOffset - nodeStart, selectionState);
  118. }
  119. } else {
  120. // If this node has the focus, set the selection range to be a
  121. // collapsed range beginning here. Later, when we encounter the anchor,
  122. // we'll use this information to extend the selection.
  123. if (hasFocus) {
  124. selection.removeAllRanges();
  125. addPointToSelection(selection, node, focusOffset - nodeStart, selectionState);
  126. } // If this node has the anchor, we may assume that the correct
  127. // focus information is already stored on the selection object.
  128. // We keep track of it, reset the selection range, and extend it
  129. // back to the focus point.
  130. if (hasAnchor) {
  131. var storedFocusNode = selection.focusNode;
  132. var storedFocusOffset = selection.focusOffset;
  133. selection.removeAllRanges();
  134. addPointToSelection(selection, node, anchorOffset - nodeStart, selectionState);
  135. addFocusToSelection(selection, storedFocusNode, storedFocusOffset, selectionState);
  136. }
  137. }
  138. }
  139. /**
  140. * Extend selection towards focus point.
  141. */
  142. function addFocusToSelection(selection, node, offset, selectionState) {
  143. var activeElement = getActiveElement();
  144. if (selection.extend && containsNode(activeElement, node)) {
  145. // If `extend` is called while another element has focus, an error is
  146. // thrown. We therefore disable `extend` if the active element is somewhere
  147. // other than the node we are selecting. This should only occur in Firefox,
  148. // since it is the only browser to support multiple selections.
  149. // See https://bugzilla.mozilla.org/show_bug.cgi?id=921444.
  150. // logging to catch bug that is being reported in t16250795
  151. if (offset > getNodeLength(node)) {
  152. // the call to 'selection.extend' is about to throw
  153. DraftJsDebugLogging.logSelectionStateFailure({
  154. anonymizedDom: getAnonymizedEditorDOM(node),
  155. extraParams: JSON.stringify({
  156. offset: offset
  157. }),
  158. selectionState: JSON.stringify(selectionState.toJS())
  159. });
  160. } // logging to catch bug that is being reported in t18110632
  161. var nodeWasFocus = node === selection.focusNode;
  162. try {
  163. selection.extend(node, offset);
  164. } catch (e) {
  165. DraftJsDebugLogging.logSelectionStateFailure({
  166. anonymizedDom: getAnonymizedEditorDOM(node, function (n) {
  167. var labels = [];
  168. if (n === activeElement) {
  169. labels.push('active element');
  170. }
  171. if (n === selection.anchorNode) {
  172. labels.push('selection anchor node');
  173. }
  174. if (n === selection.focusNode) {
  175. labels.push('selection focus node');
  176. }
  177. return labels;
  178. }),
  179. extraParams: JSON.stringify({
  180. activeElementName: activeElement ? activeElement.nodeName : null,
  181. nodeIsFocus: node === selection.focusNode,
  182. nodeWasFocus: nodeWasFocus,
  183. selectionRangeCount: selection.rangeCount,
  184. selectionAnchorNodeName: selection.anchorNode ? selection.anchorNode.nodeName : null,
  185. selectionAnchorOffset: selection.anchorOffset,
  186. selectionFocusNodeName: selection.focusNode ? selection.focusNode.nodeName : null,
  187. selectionFocusOffset: selection.focusOffset,
  188. message: e ? '' + e : null,
  189. offset: offset
  190. }, null, 2),
  191. selectionState: JSON.stringify(selectionState.toJS(), null, 2)
  192. }); // allow the error to be thrown -
  193. // better than continuing in a broken state
  194. throw e;
  195. }
  196. } else {
  197. // IE doesn't support extend. This will mean no backward selection.
  198. // Extract the existing selection range and add focus to it.
  199. // Additionally, clone the selection range. IE11 throws an
  200. // InvalidStateError when attempting to access selection properties
  201. // after the range is detached.
  202. if (selection.rangeCount > 0) {
  203. var range = selection.getRangeAt(0);
  204. range.setEnd(node, offset);
  205. selection.addRange(range.cloneRange());
  206. }
  207. }
  208. }
  209. function addPointToSelection(selection, node, offset, selectionState) {
  210. var range = getCorrectDocumentFromNode(node).createRange(); // logging to catch bug that is being reported in t16250795
  211. if (offset > getNodeLength(node)) {
  212. // in this case we know that the call to 'range.setStart' is about to throw
  213. DraftJsDebugLogging.logSelectionStateFailure({
  214. anonymizedDom: getAnonymizedEditorDOM(node),
  215. extraParams: JSON.stringify({
  216. offset: offset
  217. }),
  218. selectionState: JSON.stringify(selectionState.toJS())
  219. });
  220. DraftEffects.handleExtensionCausedError();
  221. }
  222. range.setStart(node, offset); // IE sometimes throws Unspecified Error when trying to addRange
  223. if (isIE) {
  224. try {
  225. selection.addRange(range);
  226. } catch (e) {
  227. if (process.env.NODE_ENV !== "production") {
  228. /* eslint-disable-next-line no-console */
  229. console.warn('Call to selection.addRange() threw exception: ', e);
  230. }
  231. }
  232. } else {
  233. selection.addRange(range);
  234. }
  235. }
  236. module.exports = {
  237. setDraftEditorSelection: setDraftEditorSelection,
  238. addFocusToSelection: addFocusToSelection
  239. };