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

3 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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. * @flow strict-local
  9. * @emails oncall+draft_js
  10. */
  11. 'use strict';
  12. const UserAgent = require("fbjs/lib/UserAgent");
  13. const findAncestorOffsetKey = require("./findAncestorOffsetKey");
  14. const getWindowForNode = require("./getWindowForNode");
  15. const Immutable = require("immutable");
  16. const invariant = require("fbjs/lib/invariant");
  17. const nullthrows = require("fbjs/lib/nullthrows");
  18. const {
  19. Map
  20. } = Immutable;
  21. type MutationRecordT = MutationRecord | {|
  22. type: 'characterData',
  23. target: Node,
  24. removedNodes?: void,
  25. |}; // Heavily based on Prosemirror's DOMObserver https://github.com/ProseMirror/prosemirror-view/blob/master/src/domobserver.js
  26. const DOM_OBSERVER_OPTIONS = {
  27. subtree: true,
  28. characterData: true,
  29. childList: true,
  30. characterDataOldValue: false,
  31. attributes: false
  32. }; // IE11 has very broken mutation observers, so we also listen to DOMCharacterDataModified
  33. const USE_CHAR_DATA = UserAgent.isBrowser('IE <= 11');
  34. class DOMObserver {
  35. observer: ?MutationObserver;
  36. container: HTMLElement;
  37. mutations: Map<string, string>;
  38. onCharData: ?({
  39. target: EventTarget,
  40. type: string,
  41. ...
  42. }) => void;
  43. constructor(container: HTMLElement) {
  44. this.container = container;
  45. this.mutations = Map();
  46. const containerWindow = getWindowForNode(container);
  47. if (containerWindow.MutationObserver && !USE_CHAR_DATA) {
  48. this.observer = new containerWindow.MutationObserver(mutations => this.registerMutations(mutations));
  49. } else {
  50. this.onCharData = e => {
  51. invariant(e.target instanceof Node, 'Expected target to be an instance of Node');
  52. this.registerMutation({
  53. type: 'characterData',
  54. target: e.target
  55. });
  56. };
  57. }
  58. }
  59. start(): void {
  60. if (this.observer) {
  61. this.observer.observe(this.container, DOM_OBSERVER_OPTIONS);
  62. } else {
  63. /* $FlowFixMe(>=0.68.0 site=www,mobile) This event type is not defined
  64. * by Flow's standard library */
  65. this.container.addEventListener('DOMCharacterDataModified', this.onCharData);
  66. }
  67. }
  68. stopAndFlushMutations(): Map<string, string> {
  69. const {
  70. observer
  71. } = this;
  72. if (observer) {
  73. this.registerMutations(observer.takeRecords());
  74. observer.disconnect();
  75. } else {
  76. /* $FlowFixMe(>=0.68.0 site=www,mobile) This event type is not defined
  77. * by Flow's standard library */
  78. this.container.removeEventListener('DOMCharacterDataModified', this.onCharData);
  79. }
  80. const mutations = this.mutations;
  81. this.mutations = Map();
  82. return mutations;
  83. }
  84. registerMutations(mutations: Array<MutationRecord>): void {
  85. for (let i = 0; i < mutations.length; i++) {
  86. this.registerMutation(mutations[i]);
  87. }
  88. }
  89. getMutationTextContent(mutation: MutationRecordT): ?string {
  90. const {
  91. type,
  92. target,
  93. removedNodes
  94. } = mutation;
  95. if (type === 'characterData') {
  96. // When `textContent` is '', there is a race condition that makes
  97. // getting the offsetKey from the target not possible.
  98. // These events are also followed by a `childList`, which is the one
  99. // we are able to retrieve the offsetKey and apply the '' text.
  100. if (target.textContent !== '') {
  101. // IE 11 considers the enter keypress that concludes the composition
  102. // as an input char. This strips that newline character so the draft
  103. // state does not receive spurious newlines.
  104. if (USE_CHAR_DATA) {
  105. return target.textContent.replace('\n', '');
  106. }
  107. return target.textContent;
  108. }
  109. } else if (type === 'childList') {
  110. if (removedNodes && removedNodes.length) {
  111. // `characterData` events won't happen or are ignored when
  112. // removing the last character of a leaf node, what happens
  113. // instead is a `childList` event with a `removedNodes` array.
  114. // For this case the textContent should be '' and
  115. // `DraftModifier.replaceText` will make sure the content is
  116. // updated properly.
  117. return '';
  118. } else if (target.textContent !== '') {
  119. // Typing Chinese in an empty block in MS Edge results in a
  120. // `childList` event with non-empty textContent.
  121. // See https://github.com/facebook/draft-js/issues/2082
  122. return target.textContent;
  123. }
  124. }
  125. return null;
  126. }
  127. registerMutation(mutation: MutationRecordT): void {
  128. const textContent = this.getMutationTextContent(mutation);
  129. if (textContent != null) {
  130. const offsetKey = nullthrows(findAncestorOffsetKey(mutation.target));
  131. this.mutations = this.mutations.set(offsetKey, textContent);
  132. }
  133. }
  134. }
  135. module.exports = DOMObserver;