@@ -35,7 +35,8 @@ angular.module('mm.core')
3535 remoteAddonCssFilename = 'styles.css' ,
3636 pathWildcardRegex = / \$ A D D O N P A T H \$ / g,
3737 headEl = angular . element ( document . querySelector ( 'head' ) ) ,
38- loadedAddons = [ ] ;
38+ loadedAddons = [ ] ,
39+ loadedModules = [ ] ;
3940
4041 /**
4142 * Download a remote addon if it's not downloaded already.
@@ -104,12 +105,12 @@ angular.module('mm.core')
104105 * @ngdoc method
105106 * @name $mmAddonManager#downloadRemoteAddons
106107 * @param {String } [siteId] Site ID. If not defined, current site.
107- * @return {Promise } Promise resolved when done. Returns the list of downloaded addons.
108+ * @return {Promise } Promise resolved when done. Returns the list of downloaded addons (object) .
108109 */
109110 self . downloadRemoteAddons = function ( siteId ) {
110111 siteId = siteId || $mmSite . getId ( ) ;
111112
112- var downloaded = [ ] ,
113+ var downloaded = { } ,
113114 preSets = { } ;
114115
115116 return $mmSitesManager . getSite ( siteId ) . then ( function ( site ) {
@@ -120,7 +121,7 @@ angular.module('mm.core')
120121
121122 angular . forEach ( data . plugins , function ( addon ) {
122123 promises . push ( self . downloadRemoteAddon ( addon , siteId ) . then ( function ( ) {
123- downloaded . push ( addon ) ;
124+ downloaded [ addon . addon ] = addon ;
124125 } ) ) ;
125126 } ) ;
126127
@@ -261,17 +262,99 @@ angular.module('mm.core')
261262 * @module mm.core
262263 * @ngdoc method
263264 * @name $mmAddonManager#loadRemoteAddons
264- * @param {Object[] } addons Addons to load.
265- * @return {Promise } Promise resolved when all have been loaded.
265+ * @param {Object } addons Addons to load.
266+ * @return {Promise } Promise resolved when all have been loaded.
266267 */
267268 self . loadRemoteAddons = function ( addons ) {
268269 var promises = [ ] ;
270+
271+ // Update the list of loaded modules in the app.
272+ loadedModules = $ocLazyLoad . getModules ( ) ;
273+
274+ // Load each addon if all dependencies are found. Addons will be loaded in dependency order.
269275 angular . forEach ( addons , function ( addon ) {
270- promises . push ( self . loadRemoteAddon ( addon ) ) ;
276+ self . setRemoteAddonLoadPromise ( addons , addon ) ;
277+ if ( addon . loadPromise ) {
278+ promises . push ( addon . loadPromise ) ;
279+ }
271280 } ) ;
281+
272282 return $mmUtil . allPromises ( promises ) ;
273283 } ;
274284
285+ /**
286+ * Set the promise to load a remote addon. The promise will be stored in 'addon.loadPromise'.
287+ * This promise will depend on the dependencies promises, so an addon won't be loaded until all dependencies are.
288+ * If a dependency isn't found then 'addon.loadPromise' will be set to false.
289+ *
290+ * @module mm.core
291+ * @ngdoc method
292+ * @name $mmAddonManager#setRemoteAddonLoadPromise
293+ * @param {Object[] } addons List of all addons to load.
294+ * @param {Object } addon Addon.
295+ * @param {String[] } [dependants] List of addons that depend on this addon. Will be built on recursive calls.
296+ * This is to prevent circular dependencies.
297+ * @return {Void }
298+ */
299+ self . setRemoteAddonLoadPromise = function ( addons , addon , dependants ) {
300+ if ( typeof addon . loadPromise != 'undefined' ) {
301+ // Already set.
302+ return ;
303+ }
304+
305+ dependants = dependants || [ ] ;
306+
307+ var promises = [ ] ,
308+ stop = false ;
309+
310+ angular . forEach ( addon . dependencies , function ( dependency ) {
311+ if ( stop ) {
312+ // A dependency wasn't found, no need to calculate it any further.
313+ return ;
314+ }
315+
316+ if ( dependency == addon . addon ) {
317+ // A plugin can't depend on itself, ignore it.
318+ return ;
319+ }
320+
321+ if ( dependants . indexOf ( dependency ) != - 1 ) {
322+ // Circular dependency! Stop.
323+ stop = true ;
324+ return ;
325+ }
326+
327+ if ( ! addons [ dependency ] ) {
328+ // Dependency not found in remote addons. Search in app addons.
329+ if ( dependency . indexOf ( 'mm.addons.' ) == - 1 ) {
330+ dependency = 'mm.addons.' + dependency ;
331+ }
332+
333+ if ( loadedModules . indexOf ( dependency ) == - 1 ) {
334+ // Not found in app either.
335+ stop = true ;
336+ }
337+ } else {
338+ // Set the load promise of the dependency if it hasn't been set already.
339+ self . setRemoteAddonLoadPromise ( addons , addons [ dependency ] , dependants . concat ( addon . addon ) ) ;
340+ if ( ! addons [ dependency ] . loadPromise ) {
341+ // Dependency cannot be loaded, don't load this addon either.
342+ stop = true ;
343+ } else {
344+ promises . push ( addons [ dependency ] . loadPromise ) ;
345+ }
346+ }
347+ } ) ;
348+
349+ if ( ! stop ) {
350+ addon . loadPromise = $q . all ( promises ) . then ( function ( ) {
351+ return self . loadRemoteAddon ( addon ) ;
352+ } ) ;
353+ } else {
354+ addon . loadPromise = false ;
355+ }
356+ } ;
357+
275358 /**
276359 * Set status of a remote addon.
277360 *
0 commit comments