8
0

document.js 7.6 KB

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