document.js 7.8 KB

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