mutationobserver.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. import Observer from './observer.js';
  7. import ViewElement from '../element.js';
  8. import ViewText from '../text.js';
  9. import objectUtils from '../../lib/lodash/object.js';
  10. import EmitterMixin from '../../emittermixin.js';
  11. export default class MutationObserver extends Observer {
  12. constructor() {
  13. super();
  14. this.config = {
  15. childList: true,
  16. characterData: true,
  17. characterDataOldValue: true,
  18. subtree: true
  19. };
  20. }
  21. /**
  22. * @method init
  23. * @param {treeView.TreeView}
  24. */
  25. init( treeView ) {
  26. this.treeView = treeView;
  27. this.domRoot = treeView.domRoot;
  28. this._mutationObserver = new window.MutationObserver( this._onMutations.bind( this ) );
  29. }
  30. /**
  31. * @method attach
  32. */
  33. attach() {
  34. this._mutationObserver.observe( this.domRoot, this.config );
  35. }
  36. /**
  37. * @method detach
  38. */
  39. detach() {
  40. this._mutationObserver.disconnect();
  41. }
  42. _onMutations( domMutations ) {
  43. // Use map and set for deduplication.
  44. const mutatedTexts = new Map();
  45. const mutatedElements = new Set();
  46. for ( let mutation of domMutations ) {
  47. if ( mutation.type === 'childList' ) {
  48. const element = ViewElement.getCorespondingView( mutation.target );
  49. if ( element ) {
  50. mutatedElements.add( element );
  51. }
  52. }
  53. }
  54. for ( let mutation of domMutations ) {
  55. if ( mutation.type === 'characterData' ) {
  56. const text = ViewText.getCorespondingView( mutation.target );
  57. if ( text && !mutatedElements.has( text.parent ) ) {
  58. mutatedTexts.set( text, {
  59. type: 'text',
  60. oldText: text.getText(),
  61. newText: mutation.target.data,
  62. node: text
  63. } );
  64. }
  65. }
  66. }
  67. const viewMutations = [];
  68. for ( let mutatedText of mutatedTexts.values() ) {
  69. mutatedText.node.markToSync( 'TEXT_NEEDS_UPDATE' );
  70. viewMutations.push( mutatedText );
  71. }
  72. for ( let viewElement of mutatedElements ) {
  73. const domElement = viewElement.getCorespondingDom();
  74. const domChildren = domElement.childNodes;
  75. const viewChildren = viewElement.getChildren();
  76. const newViewChildren = [];
  77. for ( let i = 0; i < domChildren.length; i++ ) {
  78. newViewChildren.push( ViewElement.createFromDom( domChildren[ i ] ) );
  79. }
  80. viewElement.markToSync( 'CHILDREN_NEED_UPDATE' );
  81. viewMutations.push( {
  82. type: 'childNodes',
  83. oldChildren: viewChildren,
  84. newChildren: newViewChildren,
  85. node: viewElement
  86. } );
  87. }
  88. this.fire( 'mutations', viewMutations );
  89. this.treeView.render();
  90. }
  91. }
  92. objectUtils.extend( MutationObserver.prototype, EmitterMixin );