document.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /**
  2. * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /**
  6. * @module engine/view/document
  7. */
  8. import DocumentSelection from './documentselection';
  9. import Collection from '@ckeditor/ckeditor5-utils/src/collection';
  10. import mix from '@ckeditor/ckeditor5-utils/src/mix';
  11. import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
  12. // @if CK_DEBUG_ENGINE // const { logDocument } = require( '../dev-utils/utils' );
  13. /**
  14. * Document class creates an abstract layer over the content editable area, contains a tree of view elements and
  15. * {@link module:engine/view/documentselection~DocumentSelection view selection} associated with this document.
  16. *
  17. * @mixes module:utils/observablemixin~ObservableMixin
  18. */
  19. export default class Document {
  20. /**
  21. * Creates a Document instance.
  22. */
  23. constructor() {
  24. /**
  25. * Selection done on this document.
  26. *
  27. * @readonly
  28. * @member {module:engine/view/documentselection~DocumentSelection} module:engine/view/document~Document#selection
  29. */
  30. this.selection = new DocumentSelection();
  31. /**
  32. * Roots of the view tree. Collection of the {@link module:engine/view/element~Element view elements}.
  33. *
  34. * View roots are created as a result of binding between {@link module:engine/view/document~Document#roots} and
  35. * {@link module:engine/model/document~Document#roots} and this is handled by
  36. * {@link module:engine/controller/editingcontroller~EditingController}, so to create view root we need to create
  37. * model root using {@link module:engine/model/document~Document#createRoot}.
  38. *
  39. * @readonly
  40. * @member {module:utils/collection~Collection} module:engine/view/document~Document#roots
  41. */
  42. this.roots = new Collection( { idProperty: 'rootName' } );
  43. /**
  44. * Defines whether document is in read-only mode.
  45. *
  46. * When document is read-ony then all roots are read-only as well and caret placed inside this root is hidden.
  47. *
  48. * @observable
  49. * @member {Boolean} #isReadOnly
  50. */
  51. this.set( 'isReadOnly', false );
  52. /**
  53. * True if document is focused.
  54. *
  55. * This property is updated by the {@link module:engine/view/observer/focusobserver~FocusObserver}.
  56. * If the {@link module:engine/view/observer/focusobserver~FocusObserver} is disabled this property will not change.
  57. *
  58. * @readonly
  59. * @observable
  60. * @member {Boolean} module:engine/view/document~Document#isFocused
  61. */
  62. this.set( 'isFocused', false );
  63. /**
  64. * True if composition is in progress inside the document.
  65. *
  66. * This property is updated by the {@link module:engine/view/observer/compositionobserver~CompositionObserver}.
  67. * If the {@link module:engine/view/observer/compositionobserver~CompositionObserver} is disabled this property will not change.
  68. *
  69. * @readonly
  70. * @observable
  71. * @member {Boolean} module:engine/view/document~Document#isComposing
  72. */
  73. this.set( 'isComposing', false );
  74. /**
  75. * Post-fixer callbacks registered to the view document.
  76. *
  77. * @private
  78. * @member {Set}
  79. */
  80. this._postFixers = new Set();
  81. }
  82. /**
  83. * Gets a {@link module:engine/view/document~Document#roots view root element} with the specified name. If the name is not
  84. * specific "main" root is returned.
  85. *
  86. * @param {String} [name='main'] Name of the root.
  87. * @returns {module:engine/view/rooteditableelement~RootEditableElement|null} The view root element with the specified name
  88. * or null when there is no root of given name.
  89. */
  90. getRoot( name = 'main' ) {
  91. return this.roots.get( name );
  92. }
  93. /**
  94. * Allows registering post-fixer callbacks. A post-fixers mechanism allows to update the view tree just before it is rendered
  95. * to the DOM.
  96. *
  97. * Post-fixers are executed right after all changes from the outermost change block were applied but
  98. * before the {@link module:engine/view/view~View#event:render render event} is fired. If a post-fixer callback made
  99. * a change, it should return `true`. When this happens, all post-fixers are fired again to check if something else should
  100. * not be fixed in the new document tree state.
  101. *
  102. * View post-fixers are useful when you want to apply some fixes whenever the view structure changes. Keep in mind that
  103. * changes executed in a view post-fixer should not break model-view mapping.
  104. *
  105. * The types of changes which should be safe:
  106. *
  107. * * adding or removing attribute from elements,
  108. * * changes inside of {@link module:engine/view/uielement~UIElement UI elements},
  109. * * {@link module:engine/model/differ~Differ#refreshItem marking some of the model elements to be re-converted}.
  110. *
  111. * Try to avoid changes which touch view structure:
  112. *
  113. * * you should not add or remove nor wrap or unwrap any view elements,
  114. * * you should not change the editor data model in a view post-fixer.
  115. *
  116. * As a parameter, a post-fixer callback receives a {@link module:engine/view/downcastwriter~DowncastWriter downcast writer}.
  117. *
  118. * Typically, a post-fixer will look like this:
  119. *
  120. * editor.editing.view.document.registerPostFixer( writer => {
  121. * if ( checkSomeCondition() ) {
  122. * writer.doSomething();
  123. *
  124. * // Let other post-fixers know that something changed.
  125. * return true;
  126. * }
  127. * } );
  128. *
  129. * Note that nothing happens right after you register a post-fixer (e.g. execute such a code in the console).
  130. * That is because adding a post-fixer does not execute it.
  131. * The post-fixer will be executed as soon as any change in the document needs to cause its rendering.
  132. * If you want to re-render the editor's view after registering the post-fixer then you should do it manually by calling
  133. * {@link module:engine/view/view~View#forceRender `view.forceRender()`}.
  134. *
  135. * If you need to register a callback which is executed when DOM elements are already updated,
  136. * use {@link module:engine/view/view~View#event:render render event}.
  137. *
  138. * @param {Function} postFixer
  139. */
  140. registerPostFixer( postFixer ) {
  141. this._postFixers.add( postFixer );
  142. }
  143. /**
  144. * Destroys this instance. Makes sure that all observers are destroyed and listeners removed.
  145. */
  146. destroy() {
  147. this.roots.map( root => root.destroy() );
  148. this.stopListening();
  149. }
  150. /**
  151. * Performs post-fixer loops. Executes post-fixer callbacks as long as none of them has done any changes to the model.
  152. *
  153. * @protected
  154. * @param {module:engine/view/downcastwriter~DowncastWriter} writer
  155. */
  156. _callPostFixers( writer ) {
  157. let wasFixed = false;
  158. do {
  159. for ( const callback of this._postFixers ) {
  160. wasFixed = callback( writer );
  161. if ( wasFixed ) {
  162. break;
  163. }
  164. }
  165. } while ( wasFixed );
  166. }
  167. /**
  168. * Event fired whenever document content layout changes. It is fired whenever content is
  169. * {@link module:engine/view/view~View#event:render rendered}, but should be also fired by observers in case of
  170. * other actions which may change layout, for instance when image loads.
  171. *
  172. * @event layoutChanged
  173. */
  174. // @if CK_DEBUG_ENGINE // log( version ) {
  175. // @if CK_DEBUG_ENGINE // logDocument( this, version );
  176. // @if CK_DEBUG_ENGINE // }
  177. }
  178. mix( Document, ObservableMixin );
  179. /**
  180. * Enum representing type of the change.
  181. *
  182. * Possible values:
  183. *
  184. * * `children` - for child list changes,
  185. * * `attributes` - for element attributes changes,
  186. * * `text` - for text nodes changes.
  187. *
  188. * @typedef {String} module:engine/view/document~ChangeType
  189. */