treeview.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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 EmitterMixin from '../../utils/emittermixin.js';
  7. import Renderer from './renderer.js';
  8. import DomConverter from './domconverter.js';
  9. import Writer from './writer.js';
  10. import mix from '../../utils/mix.js';
  11. /**
  12. * TreeView class creates an abstract layer over the content editable area.
  13. * It combines the actual tree of view elements, tree of DOM elements,
  14. * {@link engine.treeView.DomConverter DOM Converter}, {@link engine.treeView.Renderer renderer} and all
  15. * {@link engine.treeView.Observer observers}.
  16. *
  17. * If you want to only transform the tree of view elements to the DOM elements you can use the
  18. * {@link engine.treeView.DomConverter DomConverter}.
  19. *
  20. * @memberOf engine.treeView
  21. * @mixes utils.EmitterMixin
  22. */
  23. export default class TreeView {
  24. /**
  25. * Creates a TreeView instance.
  26. */
  27. constructor() {
  28. /**
  29. * Roots of the DOM tree. Map on the `HTMLElement`s with roots names as keys.
  30. *
  31. * @readonly
  32. * @member {Map} engine.treeView.TreeView#domRoots
  33. */
  34. this.domRoots = new Map();
  35. /**
  36. * Tree View writer.
  37. *
  38. * @readonly
  39. * @member {engine.treeView.Writer} engine.treeView.TreeView#writer
  40. */
  41. this.writer = new Writer();
  42. /**
  43. * Instance of the {@link engine.treeView.DomConverter domConverter} use by
  44. * {@link engine.treeView.TreeView#renderer renderer} and {@link engine.treeView.observer.Observer observers}.
  45. *
  46. * @readonly
  47. * @member {engine.treeView.DomConverter} engine.treeView.TreeView#domConverter
  48. */
  49. this.domConverter = new DomConverter();
  50. /**
  51. * Roots of the view tree. Map of the {engine.treeView.Element view elements} with roots names as keys.
  52. *
  53. * @readonly
  54. * @member {Map} engine.treeView.TreeView#viewRoots
  55. */
  56. this.viewRoots = new Map();
  57. /**
  58. * Instance of the {@link engine.treeView.TreeView#renderer renderer}.
  59. *
  60. * @readonly
  61. * @member {engine.treeView.Renderer} engine.treeView.TreeView#renderer
  62. */
  63. this.renderer = new Renderer( this.domConverter );
  64. /**
  65. * Set of registered {@link engine.treeView.Observer observers}.
  66. *
  67. * @protected
  68. * @member {Set.<engine.treeView.Observer>} engine.treeView.TreeView_#observers
  69. */
  70. this._observers = new Set();
  71. }
  72. /**
  73. * Creates observer of the given type if not yet created, {@link engine.treeView.Observer#enable enables} it
  74. * and {@link engine.treeView.observer.Observer#observe attaches} to all existing and future
  75. * {@link engine.treeView.TreeView#domRoots DOM roots}.
  76. *
  77. * Note: Observers are recognized by their constructor (classes). A single observer will be instantiated and used only
  78. * when registered for the first time. This means that features and other components can register a single observer
  79. * multiple times without caring whether it has been already added or not.
  80. *
  81. * @param {Function} Observer The constructor of an observer to add.
  82. * Should create an instance inheriting from {@link engine.treeView.observer.Observer}.
  83. */
  84. addObserver( Observer ) {
  85. if ( this._hasObserver( Observer ) ) {
  86. return;
  87. }
  88. const observer = new Observer( this );
  89. this._observers.add( observer );
  90. for ( let [ name, domElement ] of this.domRoots ) {
  91. observer.observe( domElement, name );
  92. }
  93. observer.enable();
  94. }
  95. /**
  96. * Creates a root for the HTMLElement. It adds elements to {@link engine.treeView.TreeView#domRoots} and
  97. * {@link engine.treeView.TreeView#viewRoots}.
  98. *
  99. * The constructor copies the element name and attributes to create the
  100. * root of the view, but does not copy its children. This means that while
  101. * {@link engine.treeView.TreeView#render rendering}, the whole content of this
  102. * root element will be removed but the root name and attributes will be preserved.
  103. *
  104. * @param {HTMLElement} domRoot DOM element in which the tree view should do change.
  105. * @param {String} name Name of the root.
  106. * @returns {engine.treeView.element} The created view root element.
  107. */
  108. createRoot( domRoot, name ) {
  109. const viewRoot = this.domConverter.domToView( domRoot, { bind: true, withChildren: false } );
  110. viewRoot.setTreeView( this );
  111. // Mark changed nodes in the renderer.
  112. viewRoot.on( 'change', ( evt, type, node ) => {
  113. this.renderer.markToSync( type, node );
  114. } );
  115. this.renderer.markToSync( 'CHILDREN', viewRoot );
  116. this.domRoots.set( name, domRoot );
  117. this.viewRoots.set( name, viewRoot );
  118. for ( let observer of this._observers ) {
  119. observer.observe( domRoot, name );
  120. }
  121. return viewRoot;
  122. }
  123. /**
  124. * Renders all changes. In order to avoid triggering the observers (e.g. mutations) all observers all detached
  125. * before rendering and reattached after that.
  126. */
  127. render() {
  128. for ( let observer of this._observers ) {
  129. observer.disable();
  130. }
  131. this.renderer.render();
  132. for ( let observer of this._observers ) {
  133. observer.enable();
  134. }
  135. }
  136. /**
  137. * Checks whether the given observer was already added.
  138. *
  139. * @private
  140. * @param {Function} Observer The observer constructor to check.
  141. */
  142. _hasObserver( Observer ) {
  143. return Array.from( this._observers ).some( ( observer ) => observer.constructor === Observer );
  144. }
  145. }
  146. mix( TreeView, EmitterMixin );
  147. /**
  148. * Enum representing type of the change.
  149. *
  150. * Possible values:
  151. *
  152. * * `CHILDREN` - for child list changes,
  153. * * `ATTRIBUTES` - for element attributes changes,
  154. * * `TEXT` - for text nodes changes.
  155. *
  156. * @typedef {String} engine.treeView.ChangeType
  157. */