/**
* @class
* @classdesc Plugins Management.
* @name PluginsManager
* @param parent {object} Instance of the Tag used
* @public
*/
var PluginsManager = function (parent) {
'use strict';
var self = this,
pluginsLoaded = {},
/**
* Dictionary indexed by plugin names indicating those currently being lazyloaded (true) and
* those that were in this case (false).
* example:
* {
* 'Page': true,
* 'Click': false,
* 'SalesTracker': true
* }
* Here Page, Click and SalesTracker are "Lazyloadable" and all were called. Page and SalesTracker are still loading
* @private
*/
pluginsInLazyloading = {},
/**
* Gives the number of plugins loading
* @private
*/
pluginsInLazyloadingCount = 0,
/**
* Dictionary indexed by plugin names indicating those that have one or more methods
* which have been called and are waiting for execution (awaiting the end of plugin loading)
* example:
* {
* 'Page': 3,
* 'SalesTracker': 1
* }
* Page and SalesTracker have not yet been loaded but some of their methods have been called.
* Three times for the page (this may be the same method) once for SalesTracker
* @private
*/
ExecWaitingLazyloading = {},
/**
* Gives the number of methods waiting to be executed
* @private
*/
ExecWaitingLazyloadingCount = 0;
/**
* Load a plugin.
* @name load
* @memberof PluginsManager#
* @function
* @param name {string} Plugin's name
* @param Plugin {function} Plugin's code
* @returns parent {object} Instance of the Tag used
* @public
*/
var _load = self['load'] = function (name, Plugin) {
if (typeof(Plugin) === 'function') {
var allowed = (typeof parent.getConfig.plgAllowed === 'undefined') ||
(parent.getConfig.plgAllowed.length === 0) ||
(parent.getConfig.plgAllowed.indexOf(name) > -1);
if (allowed) {
pluginsLoaded[name] = new Plugin(parent);
_lazyloadingFinished(name);//dans le cas où ce plugin a été chargé en différé
parent.emit('Tracker:Plugin:Load:' + name + ':Ok', {lvl: 'INFO'});
}
else {
parent.emit('Tracker:Plugin:Load:' + name + ':Error', {
lvl: 'ERROR',
msg: 'Plugin not allowed',
details: {}
});
}
}
else {
parent.emit('Tracker:Plugin:Load:' + name + ':Error', {
lvl: 'ERROR',
msg: 'not a function',
details: {obj: Plugin}
});
}
return parent;
};
/**
* Unload a plugin.
* @name unload
* @memberof PluginsManager#
* @function
* @param name {string} Plugin's name
* @returns parent {object} Instance of the Tag used
* @public
*/
var _unload = self['unload'] = function (name) {
if (_isLoaded(name)) {
pluginsLoaded[name] = undefined;
parent.emit('Tracker:Plugin:Unload:' + name + ':Ok', {lvl:'INFO'});
}
else {
parent.emit('Tracker:Plugin:Unload:' + name + ':Error', {lvl: 'ERROR', msg: 'not a known plugin'});
}
return parent;
};
/**
* Check if a plugin is loaded.
* @memberof PluginsManager#
* @param name {string} Plugin's name
* @returns true|false {boolean} True if the plugin is loaded
* @private
*/
var _isLoaded = function (name) {
if (pluginsLoaded[name]) {
return true;
} else {
return false;
}
};
/**
* Check if some plugin is loading.
* @name isLazyloading
* @memberof PluginsManager#
* @function
* @param name {string} Specific plugin's name
* @returns true|false {boolean} True if one or the specific plugin is loading
* @public
*/
var _isLazyloading = self['isLazyloading'] = function (name) {
if (name) {
return pluginsInLazyloading[name] === true;
} else {
return pluginsInLazyloadingCount !== 0;
}
};
/**
* Lazyload a plugin if possible.
* @memberof PluginsManager#
* @param name {string} Plugin's name
* @returns true|false {boolean} False if the plugin can't be loaded (not planned for, already loaded or already in progress).
* @private
*/
var _lazyLoadIfPossible = function (name) {
if (_isLazyLoadable(name)) {
_lazyLoad(name);
return true;
} else {
return false;
}
};
/**
* Ask the lazyloading of a plugin.
* @memberof PluginsManager#
* @param name {string} Plugin's name
* @private
*/
var _lazyLoad = function (name) {
pluginsInLazyloading[name] = true;
pluginsInLazyloadingCount++;
ATInternet.Utils.loadScript({url: parent.getConfig('lazyLoadingPath') + name + '.js'});
};
/**
* Mark a plugin's loading as finished (if exists).
* @memberof PluginsManager#
* @param name {string} Plugin's name
* @private
*/
var _lazyloadingFinished = function (name) {
if (pluginsInLazyloading[name] && _isLoaded(name)) {
pluginsInLazyloading[name] = false;
pluginsInLazyloadingCount--;
if (_isLoaded(name + "_ll")) {
_unload(name + "_ll");
}
if (pluginsInLazyloadingCount === 0) {
parent.emit('Tracker:Plugin:Lazyload:File:Complete', {lvl: 'INFO', msg: 'LazyLoading triggers are finished'});
}
}
};
/**
* Indicate if the plugin is lazyloadable and not not loaded yet.
* @memberof PluginsManager#
* @param name {string} Plugin's name
* @returns {boolean}
* @private
*/
var _isLazyLoadable = function (name) {
return !_isLoaded(name) && !_isLazyloading(name) && _isLoaded(name + '_ll');
};
/**
* Add a marker showing the method's call of a plugin which is lazyloading.
* @memberof PluginsManager#
* @param plg {string} Plugin's name
* @private
*/
var _addExecWaitingLazyloading = function (plg) {
if (!ExecWaitingLazyloading[plg]) {
ExecWaitingLazyloading[plg] = 1;
} else {
ExecWaitingLazyloading[plg]++;
}
ExecWaitingLazyloadingCount++;
};
/**
* Indicate that at least one method is waiting the end of its plugin's lazyloading.
* @name isExecWaitingLazyloading
* @memberof PluginsManager#
* @function
* @returns {number}
* @public
*/
var _IsExecWaitingLazyloading = self["isExecWaitingLazyloading"] = function () {
return ExecWaitingLazyloadingCount !== 0;
};
/**
* Delete a marker showing the method's call of a plugin which is lazyloading.
* @memberof PluginsManager#
* @param plg {string} Plugin's name
* @private
*/
var _delExecWaitingLazyloading = function (plg) {
ExecWaitingLazyloading[plg]--;
ExecWaitingLazyloadingCount--;
if (ExecWaitingLazyloadingCount === 0) {
parent.emit('Tracker:Plugin:Lazyload:Exec:Complete', {lvl: 'INFO', msg: 'All exec waiting for lazyloading are done'});
}
};
/**
* Execute a plugin method.
* @name exec
* @memberof PluginsManager#
* @function
* @param pluginName {string} Plugin (name) that you want to use
* @param method {string} Plugin's method that you want to use
* @param args {Array} Arguments to the method you want to use
* @param callback {function} Method which be triggered once the plugin's method has finished, it will take one argument which is what the plugin's method returned
* @public
*/
parent['exec'] = self['exec'] = function (pluginName, method, args, callback) {
var result = null;
var _process = function (plg, mt, arg, cb) {
var tb = mt.split('.');
if (_isLoaded(plg) && pluginsLoaded[plg][tb[0]]) {
if ((tb.length > 1) && pluginsLoaded[plg][tb[0]][tb[1]]) {
result = pluginsLoaded[plg][tb[0]][tb[1]].apply(pluginsLoaded[plg], arg);
}
else {
result = pluginsLoaded[plg][tb[0]].apply(pluginsLoaded[plg], arg);
}
}
cb && cb(result);
};
var _waiting_lazyloadin_process = function (pluginName, method, args, callback) {
_addExecWaitingLazyloading(pluginName);
parent['onTrigger']('Tracker:Plugin:Load:' + pluginName + ':Ok', function () {
_process(pluginName, method, args, function (result) {
_delExecWaitingLazyloading(pluginName);
callback && callback(result);
});
}, true);
};
if (_isLazyLoadable(pluginName)) {
_waiting_lazyloadin_process(pluginName, method, args, callback);
_lazyLoad(pluginName);
}
else if (_isLazyloading(pluginName)) {
_waiting_lazyloadin_process(pluginName, method, args, callback);
}
else {
_process(pluginName, method, args, callback);
}
};
/**
* Execute a callback only when all required plugins are loaded.
* @name waitForDependencies
* @memberof PluginsManager#
* @function
* @param dependencies {Array} Names of the required plugins
* @param callback {function} Method which be triggered once the dependencies will be loaded
* @public
*/
self['waitForDependencies'] = function (dependencies, callback) {
var _missingDependencies = function (listDep) {
var missing = {'mcount': 0, 'plugins': {}};
for (var i = 0; i < listDep.length; i++) {
if (!pluginsLoaded.hasOwnProperty(listDep[i])) {
missing.mcount++;
missing.plugins[listDep[i]] = true;
}
}
return missing;
};
var missing = _missingDependencies(dependencies);
if (missing.mcount === 0) {
parent.emit('Tracker:Plugin:Dependencies:Loaded',{
lvl:'INFO',
details:{dependencies: dependencies}
});
callback();
} else {
for (var name in missing.plugins) {
if (missing.plugins.hasOwnProperty(name)) {
parent.emit('Tracker:Plugin:Dependencies:Error',{
lvl:'WARNING',
msg:'Missing plugin ' + name
});
//on s'inscrit sur l'évnènement de chargement du plugin manquant
parent['onTrigger']('Tracker:Plugin:Load:' + name, function (trig, data) {
var trigArr = trig.split(":");
var plugName = trigArr[3];
var state = trigArr[4];
if (state === 'Ok') {
missing.plugins[plugName] = false;
missing.mcount--;
//si tous les plugins attendus ont tous été chargé on lance le callback
if (missing.mcount === 0) {
callback();
}
} else { //'Error'
//TODO
}
}, true);
//Si le plugins est lazyloadable (et qu'il n'est pas en cours de lazylaoding) on demande son chargement
_lazyLoadIfPossible(name);
}
}
}
};
/**
* Create an instance of each plugin already in the prototype.
* Essential for the inclusion of the plugins already loaded.
* @name init
* @memberof PluginsManager#
* @function
* @public
*/
self['init'] = function () {
for (var key in ATInternet.Tracker.pluginProtos) {
if (ATInternet.Tracker.pluginProtos.hasOwnProperty(key)) {
_load(key, ATInternet.Tracker.pluginProtos[key]);
}
}
};
/* @if test */
self['isLoaded'] = _isLoaded;
self['pluginsLoaded'] = function() {return pluginsLoaded};
/* @endif */
};