| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- /**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
- import Collection from '../utils/collection.js';
- import ControllerCollection from './controllercollection.js';
- import CKEditorError from '../utils/ckeditorerror.js';
- import EmitterMixin from '../utils/emittermixin.js';
- import mix from '../utils/mix.js';
- const anon = '$anonymous';
- /**
- * Basic Controller class.
- *
- * @memberOf ui
- * @mixes utils.EmitterMixin
- */
- export default class Controller {
- /**
- * Creates an instance of the {@link ui.Controller} class.
- *
- * @param {ui.Model} [model] Model of this Controller.
- * @param {ui.View} [view] View instance of this Controller.
- */
- constructor( model, view ) {
- /**
- * Model of this controller.
- *
- * @member {ui.Model} ui.Controller#model
- */
- this.model = model || null;
- /**
- * Set `true` after {@link #init}.
- *
- * @member {Boolean} ui.Controller#ready
- */
- this.ready = false;
- /**
- * View of this controller.
- *
- * @member {ui.View} ui.Controller#view
- */
- this.view = view || null;
- /**
- * A collection of {@link ControllerCollection} instances containing
- * child controllers.
- *
- * @member {utils.Collection} ui.Controller#collections
- */
- this.collections = new Collection( {
- idProperty: 'name'
- } );
- /**
- * Anonymous collection of this controller instance. It groups child controllers
- * which are not to be handled by `Controller#collections`–to–`View#region`
- * automation. It also means their views must be handled individually
- * by the view, i.e. passed as members of {@link ui.TemplateDefinition#children}.
- *
- * @protected
- * @member {ui.ControllerCollection} ui.Controller#_anonymousCollection
- */
- this.collections.add( this._anonymousCollection = new ControllerCollection( anon ) );
- // Listen to {@link ControllerCollection#add} and {@link ControllerCollection#remove}
- // of newly added Collection to synchronize this controller's view and children
- // controllers' views in the future.
- this.collections.on( 'add', ( evt, collection ) => {
- // Set the {@link ControllerCollection#parent} to this controller.
- // It allows the collection to determine the {@link #ready} state of this controller
- // and accordingly initialize a child controller when added.
- collection.parent = this;
- this.listenTo( collection, 'add', ( evt, childController, index ) => {
- // Child view is added to corresponding region in this controller's view
- // when a new Controller joins the collection.
- if ( this.ready && childController.view ) {
- this.view.regions.get( collection.name ).views.add( childController.view, index );
- }
- } );
- this.listenTo( collection, 'remove', ( evt, childController ) => {
- // Child view is removed from corresponding region in this controller's view
- // when a new Controller is removed from the the collection.
- if ( this.ready && childController.view ) {
- this.view.regions.get( collection.name ).views.remove( childController.view );
- }
- } );
- } );
- this.collections.on( 'remove', ( evt, collection ) => {
- // Release the collection. Once removed from {@link #collections}, it can be
- // moved to another controller.
- collection.parent = null;
- this.stopListening( collection );
- } );
- }
- /**
- * Initializes the controller instance. The process includes:
- *
- * 1. Initialization of the child {@link #view}.
- * 2. Initialization of child controllers in {@link #collections}.
- * 3. Setting {@link #ready} flag `true`.
- *
- * @returns {Promise} A Promise resolved when the initialization process is finished.
- */
- init() {
- if ( this.ready ) {
- /**
- * This Controller already been initialized.
- *
- * @error ui-controller-init-reinit
- */
- throw new CKEditorError( 'ui-controller-init-reinit: This Controller already been initialized.' );
- }
- return Promise.resolve()
- .then( this._initView.bind( this ) )
- .then( this._initCollections.bind( this ) )
- .then( () => {
- this.ready = true;
- this.fire( 'ready' );
- } );
- }
- /**
- * Destroys the controller instance. The process includes:
- *
- * 1. Destruction of the child {@link #view}.
- * 2. Destruction of child controllers in {@link #collections}.
- *
- * @returns {Promise} A Promise resolved when the destruction process is finished.
- */
- destroy() {
- let promises = [];
- let collection, childController;
- for ( collection of this.collections ) {
- for ( childController of collection ) {
- promises.push( childController.destroy() );
- }
- collection.clear();
- }
- this.collections.clear();
- if ( this.view ) {
- promises.push( Promise.resolve().then( () => {
- return this.view.destroy();
- } ) );
- }
- promises.push( Promise.resolve().then( () => {
- this.model = this.ready = this.view = this.collections = null;
- } ) );
- return Promise.all( promises );
- }
- /**
- * Adds a new collection to {@link ui.Controller#collections}.
- *
- * @param {String} collectionName Name of the controller collection.
- * @returns {ui.ControllerCollection} The new collection instance.
- */
- addCollection( collectionName ) {
- const collection = new ControllerCollection( collectionName, this.view && this.view.locale );
- this.collections.add( collection );
- return collection;
- }
- /**
- * Adds a child {@link Controller} instance to {@link #collections} at given index.
- *
- * // Adds child to the specified collection. The collection name
- * // must correspond with the region name in parent.view#regions.
- * parent.add( 'collection-name', child );
- *
- * // Adds child to the specified collection at specific index.
- * // The collection name must correspond with the region name in parent.view#regions.
- * parent.add( 'collection-name', child, 3 );
- *
- * // Adds child to the {@link ui.Controller#_anonymousCollection} in the parent. In such case,
- * // parent#view must put the child#view in the correct place in parent.view#template
- * // because there's no association between the {@link ui.Controller#_anonymousCollection}
- * // and any of the regions.
- * parent.add( child );
- *
- * @param {String|ui.Controller} collectionNameOrController Name of the collection or the controller instance.
- * @param {ui.Controller} [controller] A controller instance to be added.
- * @param {Number} [index] An index in the collection.
- * @returns {Promise} A Promise resolved when the child {@link ui.Controller#init} is done.
- */
- add( ...args ) {
- if ( args[ 0 ] instanceof Controller ) {
- return this._anonymousCollection.add( ...args );
- } else {
- return this.collections.get( args[ 0 ] ).add( args[ 1 ], args[ 2 ] );
- }
- }
- /**
- * Removes a child {@link ui.Controller} instance from one of {@link ui.Controller#collections}.
- *
- * **Note**: To remove children from {@link ui.Controller#_anonymousCollection}, use the following syntax
- *
- * parent.remove( child );
- *
- * @param {String|ui.Controller} collectionNameOrController Name of the collection or the controller instance.
- * @param {ui.Controller|Number} [toRemove] A Controller instance or index to be removed.
- * @returns {Object} The removed item.
- */
- remove( collectionNameOrController, toRemove ) {
- if ( collectionNameOrController instanceof Controller ) {
- return this._anonymousCollection.remove( collectionNameOrController );
- } else {
- return this.collections.get( collectionNameOrController ).remove( toRemove );
- }
- }
- /**
- * Initializes the {@link #view} of this controller instance.
- *
- * @protected
- * @returns {Promise} A Promise resolved when initialization process is finished.
- */
- _initView() {
- let promise = Promise.resolve();
- if ( this.view ) {
- promise = promise.then( this.view.init.bind( this.view ) );
- }
- return promise;
- }
- /**
- * Initializes the {@link #collections} of this controller instance.
- *
- * @protected
- * @returns {Promise} A Promise resolved when initialization process is finished.
- */
- _initCollections() {
- const promises = [];
- let collection, childController;
- for ( collection of this.collections ) {
- for ( childController of collection ) {
- // Anonymous collection {@link ui.Controller#_anonymousCollection} does not allow
- // automated controller-to-view binding, because there's no such thing as
- // anonymous Region in the View instance.
- if ( !isAnonymous( collection ) && this.view && childController.view ) {
- this.view.regions.get( collection.name ).views.add( childController.view );
- }
- promises.push( childController.init() );
- }
- }
- return Promise.all( promises );
- }
- }
- mix( Controller, EmitterMixin );
- // Checks whether the collection is anonymous.
- //
- // @private
- // @param {ui.ControllerCollection} collection
- // @returns {Boolean}
- function isAnonymous( collection ) {
- return collection.name == anon;
- }
- /**
- * Fired when the controller is fully initialized.
- *
- * @event ui.Controller#ready
- */
|