controller.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. import Collection from '../collection.js';
  7. import CKEditorError from '../ckeditorerror.js';
  8. import EmitterMixin from '../emittermixin.js';
  9. import utils from '../utils.js';
  10. /**
  11. * Basic Controller class.
  12. *
  13. * @memberOf core.ui
  14. * @mixes core.EmitterMixin
  15. */
  16. export default class Controller {
  17. /**
  18. * Creates an instance of the {@link core.ui.Controller} class.
  19. *
  20. * @param {core.ui.Model} [model] Model of this Controller.
  21. * @param {core.ui.View} [view] View instance of this Controller.
  22. */
  23. constructor( model, view ) {
  24. /**
  25. * Model of this controller.
  26. *
  27. * @member {core.ui.Model} core.ui.Controller#model
  28. */
  29. this.model = model || null;
  30. /**
  31. * Set `true` after {@link #init}.
  32. *
  33. * @member {Boolean} core.ui.Controller#ready
  34. */
  35. this.ready = false;
  36. /**
  37. * View of this controller.
  38. *
  39. * @member {core.ui.View} core.ui.Controller#view
  40. */
  41. this.view = view || null;
  42. /**
  43. * A collection of {@link ControllerCollection} instances containing
  44. * child controllers.
  45. *
  46. * @member {core.Collection} core.ui.Controller#collections
  47. */
  48. this.collections = new Collection( {
  49. idProperty: 'name'
  50. } );
  51. // Listen to {@link ControllerCollection#add} and {@link ControllerCollection#remove}
  52. // of newly added Collection to synchronize this controller's view and children
  53. // controllers' views in the future.
  54. this.collections.on( 'add', ( evt, collection ) => {
  55. // Set the {@link ControllerCollection#parent} to this controller.
  56. // It allows the collection to determine the {@link #ready} state of this controller
  57. // and accordingly initialize a child controller when added.
  58. collection.parent = this;
  59. this.listenTo( collection, 'add', ( evt, childController, index ) => {
  60. // Child view is added to corresponding region in this controller's view
  61. // when a new Controller joins the collection.
  62. if ( this.ready && childController.view ) {
  63. this.view.regions.get( collection.name ).views.add( childController.view, index );
  64. }
  65. } );
  66. this.listenTo( collection, 'remove', ( evt, childController ) => {
  67. // Child view is removed from corresponding region in this controller's view
  68. // when a new Controller is removed from the the collection.
  69. if ( this.ready && childController.view ) {
  70. this.view.regions.get( collection.name ).views.remove( childController.view );
  71. }
  72. } );
  73. } );
  74. this.collections.on( 'remove', ( evt, collection ) => {
  75. // Release the collection. Once removed from {@link #collections}, it can be
  76. // moved to another controller.
  77. collection.parent = null;
  78. this.stopListening( collection );
  79. } );
  80. }
  81. /**
  82. * Initializes the controller instance. The process includes:
  83. *
  84. * 1. Initialization of the child {@link #view}.
  85. * 2. Initialization of child controllers in {@link #collections}.
  86. * 3. Setting {@link #ready} flag `true`.
  87. *
  88. * @returns {Promise} A Promise resolved when the initialization process is finished.
  89. */
  90. init() {
  91. if ( this.ready ) {
  92. /**
  93. * This Controller already been initialized.
  94. *
  95. * @error ui-controller-init-reinit
  96. */
  97. throw new CKEditorError( 'ui-controller-init-reinit: This Controller already been initialized.' );
  98. }
  99. return Promise.resolve()
  100. .then( this._initView.bind( this ) )
  101. .then( this._initCollections.bind( this ) )
  102. .then( () => {
  103. this.ready = true;
  104. } );
  105. }
  106. /**
  107. * Destroys the controller instance. The process includes:
  108. *
  109. * 1. Destruction of the child {@link #view}.
  110. * 2. Destruction of child controllers in {@link #collections}.
  111. *
  112. * @returns {Promise} A Promise resolved when the destruction process is finished.
  113. */
  114. destroy() {
  115. let promises = [];
  116. let collection, childController;
  117. for ( collection of this.collections ) {
  118. for ( childController of collection ) {
  119. promises.push( childController.destroy() );
  120. collection.remove( childController );
  121. }
  122. this.collections.remove( collection );
  123. }
  124. if ( this.view ) {
  125. promises.push( Promise.resolve().then( () => {
  126. return this.view.destroy();
  127. } ) );
  128. }
  129. promises.push( Promise.resolve().then( () => {
  130. this.model = this.ready = this.view = this.collections = null;
  131. } ) );
  132. return Promise.all( promises );
  133. }
  134. /**
  135. * Initializes the {@link #view} of this controller instance.
  136. *
  137. * @protected
  138. * @returns {Promise} A Promise resolved when initialization process is finished.
  139. */
  140. _initView() {
  141. let promise = Promise.resolve();
  142. if ( this.view ) {
  143. promise = promise.then( this.view.init.bind( this.view ) );
  144. }
  145. return promise;
  146. }
  147. /**
  148. * Initializes the {@link #collections} of this controller instance.
  149. *
  150. * @protected
  151. * @returns {Promise} A Promise resolved when initialization process is finished.
  152. */
  153. _initCollections() {
  154. const promises = [];
  155. let collection, childController;
  156. for ( collection of this.collections ) {
  157. for ( childController of collection ) {
  158. if ( this.view && childController.view ) {
  159. this.view.regions.get( collection.name ).views.add( childController.view );
  160. }
  161. promises.push( childController.init() );
  162. }
  163. }
  164. return Promise.all( promises );
  165. }
  166. }
  167. utils.mix( Controller, EmitterMixin );