controller.js 5.1 KB

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