8
0

plugincollection.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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 Plugin from './plugin.js';
  7. import CKEditorError from './ckeditorerror.js';
  8. import log from './log.js';
  9. import load from '../load.js';
  10. /**
  11. * Manages a list of CKEditor plugins, including loading, resolving dependencies and initialization.
  12. *
  13. * @memberOf core
  14. */
  15. export default class PluginCollection {
  16. /**
  17. * Creates an instance of the PluginCollection class, initializing it with a set of plugins.
  18. *
  19. * @param {core.Editor} editor
  20. */
  21. constructor( editor ) {
  22. /**
  23. * @protected
  24. * @member {core.Editor} core.PluginCollection#_editor
  25. */
  26. this._editor = editor;
  27. /**
  28. * @protected
  29. * @member {Map} core.PluginCollection#_plugins
  30. */
  31. this._plugins = new Map();
  32. }
  33. /**
  34. * Collection iterator. Returns `[ key, plugin ]` pairs. Plugins which are
  35. * kept in the collection twice (under their name and class) will be returned twice.
  36. */
  37. [ Symbol.iterator ]() {
  38. return this._plugins[ Symbol.iterator ]();
  39. }
  40. /**
  41. * Gets the plugin instance by its name or class.
  42. *
  43. * @param {String/Function} key The name of the plugin or the class.
  44. * @returns {core.Plugin}
  45. */
  46. get( key ) {
  47. return this._plugins.get( key );
  48. }
  49. /**
  50. * Loads a set of plugins and add them to the collection.
  51. *
  52. * @param {String[]} plugins An array of plugins to load.
  53. * @returns {Promise} A promise which gets resolved once all plugins are loaded and available into the
  54. * collection.
  55. * @param {core.Plugin[]} returns.loadedPlugins The array of loaded plugins.
  56. */
  57. load( plugins ) {
  58. const that = this;
  59. const editor = this._editor;
  60. const loading = new Set();
  61. const loaded = [];
  62. return Promise.all( plugins.map( loadPlugin ) )
  63. .then( () => loaded );
  64. function loadPlugin( pluginClassOrName ) {
  65. // The plugin is already loaded or being loaded - do nothing.
  66. if ( that.get( pluginClassOrName ) || loading.has( pluginClassOrName ) ) {
  67. return;
  68. }
  69. let promise = ( typeof pluginClassOrName == 'string' ) ?
  70. loadPluginByName( pluginClassOrName ) :
  71. loadPluginByClass( pluginClassOrName );
  72. return promise
  73. .catch( ( err ) => {
  74. /**
  75. * It was not possible to load the plugin.
  76. *
  77. * @error plugincollection-load
  78. * @param {String} plugin The name of the plugin that could not be loaded.
  79. */
  80. log.error( 'plugincollection-load: It was not possible to load the plugin.', { plugin: pluginClassOrName } );
  81. throw err;
  82. } );
  83. }
  84. function loadPluginByName( pluginName ) {
  85. return load( PluginCollection.getPluginPath( pluginName ) )
  86. .then( ( PluginModule ) => {
  87. return loadPluginByClass( PluginModule.default, pluginName );
  88. } );
  89. }
  90. function loadPluginByClass( PluginClass, pluginName ) {
  91. return new Promise( ( resolve ) => {
  92. loading.add( PluginClass );
  93. assertIsPlugin( PluginClass );
  94. if ( PluginClass.requires ) {
  95. PluginClass.requires.forEach( loadPlugin );
  96. }
  97. const plugin = new PluginClass( editor );
  98. that._add( PluginClass, plugin );
  99. loaded.push( plugin );
  100. // Expose the plugin also by its name if loaded through load() by name.
  101. if ( pluginName ) {
  102. that._add( pluginName, plugin );
  103. }
  104. resolve();
  105. } );
  106. }
  107. function assertIsPlugin( LoadedPlugin ) {
  108. if ( !( LoadedPlugin.prototype instanceof Plugin ) ) {
  109. /**
  110. * The loaded plugin module is not an instance of Plugin.
  111. *
  112. * @error plugincollection-instance
  113. * @param {LoadedPlugin} plugin The class which is meant to be loaded as a plugin.
  114. */
  115. throw new CKEditorError(
  116. 'plugincollection-instance: The loaded plugin module is not an instance of Plugin.',
  117. { plugin: LoadedPlugin }
  118. );
  119. }
  120. }
  121. }
  122. /**
  123. * Resolves a simplified plugin name to a real path. The returned
  124. * paths are relative to the main `ckeditor.js` file, but they do not start with `./`.
  125. *
  126. * For instance:
  127. *
  128. * * `foo` will be transformed to `ckeditor5/foo/foo.js`,
  129. * * `core/editor` to `ckeditor5/core/editor.js` and
  130. * * `foo/bar/bom` to `ckeditor5/foo/bar/bom.js`.
  131. *
  132. * @param {String} name
  133. * @returns {String} Path to the module.
  134. */
  135. static getPluginPath( name ) {
  136. // Resolve shortened feature names to `featureName/featureName`.
  137. if ( name.indexOf( '/' ) < 0 ) {
  138. name = name + '/' + name;
  139. }
  140. return 'ckeditor5/' + name + '.js';
  141. }
  142. /**
  143. * Adds the plugin to the collection. Exposed mainly for testing purposes.
  144. *
  145. * @protected
  146. * @param {String/Function} key The name or the plugin class.
  147. * @param {core.Plugin} plugin The instance of the plugin.
  148. */
  149. _add( key, plugin ) {
  150. this._plugins.set( key, plugin );
  151. }
  152. }