/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
define([
'underscore',
'domReady!'
], function (_) {
'use strict';
var context = require.s.contexts._,
execCb = context.execCb,
registry = context.registry,
callbacks = [],
retries = 10,
updateDelay = 1,
ready,
update;
/**
* Checks if provided callback already exists in the callbacks list.
*
* @param {Object} callback - Callback object to be checked.
* @returns {Boolean}
*/
function isSubscribed(callback) {
return !!_.findWhere(callbacks, callback);
}
/**
* Checks if provided module is rejected during load.
*
* @param {Object} module - Module to be checked.
* @return {Boolean}
*/
function isRejected(module) {
return registry[module.id] && (registry[module.id].inited || registry[module.id].error);
}
/**
* Checks if provided module had path fallback triggered.
*
* @param {Object} module - Module to be checked.
* @return {Boolean}
*/
function isPathFallback(module) {
return registry[module.id] && registry[module.id].events.error;
}
/**
* Checks if provided module has unresolved dependencies.
*
* @param {Object} module - Module to be checked.
* @returns {Boolean}
*/
function isPending(module) {
if (!module.depCount) {
return false;
}
return module.depCount >
_.filter(module.depMaps, isRejected).length + _.filter(module.depMaps, isPathFallback).length;
}
/**
* Checks if requirejs's registry object contains pending modules.
*
* @returns {Boolean}
*/
function hasPending() {
return _.some(registry, isPending);
}
/**
* Checks if 'resolver' module is in ready
* state and that there are no pending modules.
*
* @returns {Boolean}
*/
function isReady() {
return ready && !hasPending();
}
/**
* Invokes provided callback handler.
*
* @param {Object} callback
*/
function invoke(callback) {
callback.handler.call(callback.ctx);
}
/**
* Sets 'resolver' module to a ready state
* and invokes pending callbacks.
*/
function resolve() {
ready = true;
callbacks.splice(0).forEach(invoke);
}
/**
* Drops 'ready' flag and runs the update process.
*/
function tick() {
ready = false;
update(retries);
}
/**
* Adds callback which will be invoked
* when all of the pending modules are initiated.
*
* @param {Function} handler - 'Ready' event handler function.
* @param {Object} [ctx] - Optional context with which handler
* will be invoked.
*/
function subscribe(handler, ctx) {
var callback = {
handler: handler,
ctx: ctx
};
if (!isSubscribed(callback)) {
callbacks.push(callback);
if (isReady()) {
_.defer(tick);
}
}
}
/**
* Checks for all modules to be initiated
* and invokes pending callbacks if it's so.
*
* @param {Number} [retry] - Number of retries
* that will be used to repeat the 'update' function
* invokation in case if there are no pending requests.
*/
update = _.debounce(function (retry) {
if (!hasPending()) {
retry ? update(--retry) : resolve();
}
}, updateDelay);
/**
* Overrides requirejs's original 'execCb' method
* in order to track pending modules.
*
* @returns {*} Result of original method call.
*/
context.execCb = function () {
var exported = execCb.apply(context, arguments);
tick();
return exported;
};
return subscribe;
});
|