plugincollection.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. /**
  7. * Manages a list of CKEditor plugins, including loading, initialization and destruction.
  8. *
  9. * @class PluginCollection
  10. * @extends Collection
  11. */
  12. CKEDITOR.define( [ 'mvc/collection', 'promise' ], function( Collection, Promise ) {
  13. var PluginCollection = Collection.extend( {
  14. /**
  15. * Creates an instance of the PluginCollection class, initializing it with a set of plugins.
  16. *
  17. * @constructor
  18. */
  19. constructor: function PluginCollection( editor ) {
  20. // Call the base constructor.
  21. Collection.apply( this );
  22. this._editor = editor;
  23. // The hash table used to store pointers to loaded plugins by name.
  24. this._names = {};
  25. },
  26. /**
  27. * Loads a set of plugins and add them to the collection.
  28. *
  29. * @param {String} plugins A comma separated list of plugin names to get loaded.
  30. * @returns {Promise} A promise which gets resolved once all plugins are loaded and available into the
  31. * collection.
  32. */
  33. load: function( plugins ) {
  34. var that = this;
  35. // The list of plugins which are being loaded (to avoid circular references issues).
  36. var loading = {};
  37. plugins = plugins ? plugins.split( ',' ) : [];
  38. // Creates a promise for the loading of each plugin and returns a main promise that resolves when all are
  39. // done.
  40. return Promise.all( plugins.map( pluginPromise ) );
  41. // Returns a promise that will load the plugin and add it to the collection before resolving.
  42. function pluginPromise( plugin ) {
  43. return new Promise( function( resolve, reject ) {
  44. // Do nothing if the plugin is already loaded (or if is being loaded right now).
  45. if ( that._names[ plugin ] || loading[ plugin ] ) {
  46. resolve();
  47. return;
  48. }
  49. CKEDITOR.require( [ 'plugin!' + plugin ],
  50. // Success callback.
  51. function( LoadedPlugin ) {
  52. var loadedPlugin = new LoadedPlugin( that._editor );
  53. loadedPlugin.name = plugin;
  54. loadedPlugin.path = CKEDITOR.getPluginPath( plugin );
  55. loadedPlugin.deps = getPluginDeps( plugin );
  56. loading[ plugin ] = true;
  57. // Resolve with a promise that resolves once all dependencies are loaded.
  58. resolve(
  59. Promise.all( loadedPlugin.deps.map( pluginPromise ) )
  60. .then( function() {
  61. // Once dependencies are loaded, add the new instance of the loaded plugin to
  62. // the collection. This guarantees that dependecies come first in the collection.
  63. that.add( loadedPlugin );
  64. } )
  65. );
  66. },
  67. // Error callback.
  68. function() {
  69. var err = new Error( 'It was not possible to load the "' + plugin + '" plugin.' );
  70. err.name = 'CKEditor Error';
  71. reject( err );
  72. }
  73. );
  74. } );
  75. }
  76. function getPluginDeps( name ) {
  77. // Get the list of AMD modules that the plugin depends on.
  78. var deps = CKEDITOR._dependencies[ 'plugin!' + name ] || [];
  79. deps = deps
  80. // Pick only dependencies that are other plugins.
  81. .filter( function( dep ) {
  82. return dep.indexOf( 'plugin!' ) === 0;
  83. } )
  84. // Remove the 'plugin!' prefix.
  85. .map( function( dep ) {
  86. return dep.substr( 7 );
  87. } );
  88. return deps;
  89. }
  90. },
  91. /**
  92. * Adds a plugin to the collection.
  93. *
  94. * The `name` property must be set to the plugin object before passing it to this function. Adding plugins
  95. * with the same name has no effect and silently fails.
  96. *
  97. * @param {Plugin} plugin The plugin to be added.
  98. */
  99. add: function( plugin ) {
  100. // Do nothing if the plugin is already loaded.
  101. if ( this._names[ plugin.name ] ) {
  102. return;
  103. }
  104. // Save a pointer to the plugin by its name.
  105. this._names[ plugin.name ] = plugin;
  106. // Call the original implementation.
  107. Collection.prototype.add.apply( this, arguments );
  108. },
  109. /**
  110. * Gets a plugin from the collection.
  111. *
  112. * @param {String} name The plugin name.
  113. * @returns {Plugin} The requested plugin, if available in the collection.
  114. */
  115. get: function( name ) {
  116. if ( typeof name != 'string' ) {
  117. return Collection.prototype.get.apply( this, arguments );
  118. }
  119. return this._names[ name ];
  120. }
  121. } );
  122. return PluginCollection;
  123. } );