document.js 6.3 KB

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