You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
279 lines
8.0 KiB
279 lines
8.0 KiB
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;
|
|
})();
|