|
|
var loadjs = (function () { /** * Global dependencies. * @global {Object} document - DOM */
var devnull = function () { }, bundleIdCache = {}, bundleResultCache = {}, bundleCallbackQueue = {};
/** * Subscribe to bundle load event. * @param {String[]} bundleIds - Bundle ids * @param {Function} callbackFn - The callback function */ function subscribe(bundleIds, callbackFn) { // listify
bundleIds = bundleIds.push ? bundleIds : [bundleIds];
var depsNotFound = [], i = bundleIds.length, numWaiting = i, fn, bundleId, r, q;
// define callback function
fn = function (bundleId, pathsNotFound) { if (pathsNotFound.length) depsNotFound.push(bundleId);
numWaiting--; if (!numWaiting) callbackFn(depsNotFound); };
// register callback
while (i--) { bundleId = bundleIds[i];
// execute callback if in result cache
r = bundleResultCache[bundleId]; if (r) { fn(bundleId, r); continue; }
// add to callback queue
q = bundleCallbackQueue[bundleId] = bundleCallbackQueue[bundleId] || []; q.push(fn); } }
/** * Publish bundle load event. * @param {String} bundleId - Bundle id * @param {string[]} pathsNotFound - List of files not found */ function publish(bundleId, pathsNotFound) { // exit if id isn't defined
if (!bundleId) return;
var q = bundleCallbackQueue[bundleId];
// cache result
bundleResultCache[bundleId] = pathsNotFound;
// exit if queue is empty
if (!q) return;
// empty callback queue
while (q.length) { q[0](bundleId, pathsNotFound); q.splice(0, 1); } }
/** * Execute callbacks. * @param {Object} args - The callback args * @param {Object} depsNotFound - List of dependencies not found */ function executeCallbacks(args, depsNotFound) { // accept function as argument
if (args.call) args = { success: args };
// success and error callbacks
if (depsNotFound.length) (args.error || devnull)(depsNotFound); else (args.success || devnull)(args); }
/** * Load individual file. * @param {String} path - The file path * @param {Function} callbackFn - The callback function * @param {Object} args - * @param {Number} numTries - */ function loadFile(path, callbackFn, args, numTries) { var doc = document, async = args.async, maxTries = (args.numRetries || 0) + 1, beforeCallbackFn = args.before || devnull, pathStripped = path.replace(/^(css|img)!/, ''), isCss, e;
numTries = numTries || 0;
if (/(^css!|\.css$)/.test(path)) { isCss = true;
// css
e = doc.createElement('link'); e.rel = 'stylesheet'; e.href = pathStripped; //.replace(/^css!/, ''); // remove "css!" prefix
} else if (/(^img!|\.(png|gif|jpg|svg)$)/.test(path)) { // image
e = doc.createElement('img'); e.src = pathStripped; } else { // javascript
e = doc.createElement('script'); e.src = path; e.async = async === undefined ? true : async; }
e.onload = e.onerror = e.onbeforeload = function (ev) { var result = ev.type[0];
// Note: The following code isolates IE using `hideFocus` and treats empty
// stylesheets as failures to get around lack of onerror support
if (isCss && 'hideFocus' in e) { try { if (!e.sheet.cssText.length) result = 'e'; } catch (x) { // sheets objects created from load errors don't allow access to
// `cssText`
result = 'e'; } }
// handle retries in case of load failure
if (result === 'e') { // increment counter
numTries += 1;
// exit function and try again
if (numTries < maxTries) { return loadFile(path, callbackFn, args, numTries); } }
// execute callback
callbackFn(path, result, ev.defaultPrevented); };
// add to document (unless callback returns `false`)
if (beforeCallbackFn(path, e) !== false) doc.head.appendChild(e); }
/** * Load multiple files. * @param {Object} paths - The file paths * @param {Function} callbackFn - The callback function * @param {Object} args - */ function loadFiles(paths, callbackFn, args) { // listify paths
paths = paths.push ? paths : [paths];
var numWaiting = paths.length, x = numWaiting, pathsNotFound = [], fn, i;
// define callback function
fn = function (path, result, defaultPrevented) { // handle error
if (result === 'e') pathsNotFound.push(path);
// handle beforeload event. If defaultPrevented then that means the load
// will be blocked (ex. Ghostery/ABP on Safari)
if (result === 'b') { if (defaultPrevented) pathsNotFound.push(path); else return; }
numWaiting--; if (!numWaiting) callbackFn(pathsNotFound); };
// load scripts
for (i = 0; i < x; i++) loadFile(paths[i], fn, args); }
/** * Initiate script load and register bundle. * @param {(string|string[])} paths - The file paths * @param {(string|Function)} [arg1] - The bundleId or success callback * @param {Function} [arg2] - The success or error callback * @param {Function} [arg3] - The error callback */ function loadjs(paths, arg1, arg2) { var bundleId, args;
// bundleId (if string)
if (arg1 && arg1.trim) bundleId = arg1;
// args (default is {})
args = (bundleId ? arg2 : arg1) || {};
// throw error if bundle is already defined
if (bundleId) { if (bundleId in bundleIdCache) { throw "LoadJS"; } else { bundleIdCache[bundleId] = true; } }
// load scripts
loadFiles(paths, function (pathsNotFound) { // execute callbacks
executeCallbacks(args, pathsNotFound);
// publish bundle load event
publish(bundleId, pathsNotFound); }, args); }
/** * Execute callbacks when dependencies have been satisfied. * @param {Object} deps - List of bundle ids * @param {Object} args - success/error arguments * @return {Object} loadjs - */ loadjs.ready = function ready(deps, args) { args = args || function () { }; // subscribe to bundle load event
subscribe(deps, function (depsNotFound) { // execute callbacks
executeCallbacks(args, depsNotFound); });
return loadjs; };
/** * Manually satisfy bundle dependencies. * @param {String} bundleId - The bundle id */ loadjs.done = function done(bundleId) { publish(bundleId, []); };
/** * Reset loadjs dependencies statuses */ loadjs.reset = function reset() { bundleIdCache = {}; bundleResultCache = {}; bundleCallbackQueue = {}; };
/** * Determine if bundle has already been defined * @param {String} bundleId - The bundle id * @return {String} bundleId - The bundle id */ loadjs.isDefined = function isDefined(bundleId) { return bundleId in bundleIdCache; };
// export
return loadjs; })();
|