mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-30 18:52:42 +01:00
d06679a021
Summary: Fixes T5273. Two issues: - If a JX.Resource callback throws, we keep running it. Instead, make sure it gets cleaned up before raising an exception. - The countdown timer script doesn't recover gracefully if the node has been removed from the document by the time it runs. Instead, just bail if we can't find the countdown. Test Plan: Dumped `{Cxyz}` into a preview and got a countdown. Reviewers: yungsters Reviewed By: yungsters Subscribers: epriestley Maniphest Tasks: T5273 Differential Revision: https://secure.phabricator.com/D9399
185 lines
4.8 KiB
JavaScript
185 lines
4.8 KiB
JavaScript
/**
|
|
* @provides javelin-resource
|
|
* @requires javelin-util
|
|
* javelin-uri
|
|
* javelin-install
|
|
*
|
|
* @javelin
|
|
*/
|
|
|
|
JX.install('Resource', {
|
|
|
|
statics: {
|
|
|
|
_loading: {},
|
|
_loaded: {},
|
|
_links: [],
|
|
_callbacks: [],
|
|
|
|
/**
|
|
* Loads one or many static resources (JavaScript or CSS) and executes a
|
|
* callback once these resources have finished loading.
|
|
*
|
|
* @param string|array static resource or list of resources to be loaded
|
|
* @param function callback when resources have finished loading
|
|
*/
|
|
load: function(list, callback) {
|
|
var resources = {},
|
|
uri, resource, path, type;
|
|
|
|
list = JX.$AX(list);
|
|
|
|
// In the event there are no resources to wait on, call the callback and
|
|
// exit. NOTE: it's better to do this check outside this function and not
|
|
// call through JX.Resource, but it's not always easy/possible to do so
|
|
if (!list.length) {
|
|
setTimeout(callback, 0);
|
|
return;
|
|
}
|
|
|
|
for (var ii = 0; ii < list.length; ii++) {
|
|
uri = new JX.URI(list[ii]);
|
|
resource = uri.toString();
|
|
path = uri.getPath();
|
|
resources[resource] = true;
|
|
|
|
if (JX.Resource._loaded[resource]) {
|
|
setTimeout(JX.bind(JX.Resource, JX.Resource._complete, resource), 0);
|
|
} else if (!JX.Resource._loading[resource]) {
|
|
JX.Resource._loading[resource] = true;
|
|
if (path.indexOf('.css') == path.length - 4) {
|
|
JX.Resource._loadCSS(resource);
|
|
} else {
|
|
JX.Resource._loadJS(resource);
|
|
}
|
|
}
|
|
}
|
|
|
|
JX.Resource._callbacks.push({
|
|
resources: resources,
|
|
callback: callback
|
|
});
|
|
},
|
|
|
|
_loadJS: function(uri) {
|
|
var script = document.createElement('script');
|
|
var load_callback = function() {
|
|
JX.Resource._complete(uri);
|
|
};
|
|
var error_callback = function() {
|
|
JX.$E('Resource: JS file download failure: ' + uri);
|
|
};
|
|
|
|
JX.copy(script, {
|
|
type: 'text/javascript',
|
|
src: uri
|
|
});
|
|
|
|
script.onload = load_callback;
|
|
script.onerror = error_callback;
|
|
script.onreadystatechange = function() {
|
|
var state = this.readyState;
|
|
if (state == 'complete' || state == 'loaded') {
|
|
load_callback();
|
|
}
|
|
};
|
|
document.getElementsByTagName('head')[0].appendChild(script);
|
|
},
|
|
|
|
_loadCSS: function(uri) {
|
|
var link = JX.copy(document.createElement('link'), {
|
|
type: 'text/css',
|
|
rel: 'stylesheet',
|
|
href: uri,
|
|
'data-href': uri // don't trust href
|
|
});
|
|
document.getElementsByTagName('head')[0].appendChild(link);
|
|
|
|
JX.Resource._links.push(link);
|
|
if (!JX.Resource._timer) {
|
|
JX.Resource._timer = setInterval(JX.Resource._poll, 20);
|
|
}
|
|
},
|
|
|
|
_poll: function() {
|
|
var sheets = document.styleSheets,
|
|
ii = sheets.length,
|
|
links = JX.Resource._links;
|
|
|
|
// Cross Origin CSS loading
|
|
// http://yearofmoo.com/2011/03/cross-browser-stylesheet-preloading/
|
|
while (ii--) {
|
|
var link = sheets[ii],
|
|
owner = link.ownerNode || link.owningElement,
|
|
jj = links.length;
|
|
if (owner) {
|
|
while (jj--) {
|
|
if (owner == links[jj]) {
|
|
JX.Resource._complete(links[jj]['data-href']);
|
|
links.splice(jj, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!links.length) {
|
|
clearInterval(JX.Resource._timer);
|
|
JX.Resource._timer = null;
|
|
}
|
|
},
|
|
|
|
_complete: function(uri) {
|
|
var list = JX.Resource._callbacks,
|
|
current, ii;
|
|
|
|
delete JX.Resource._loading[uri];
|
|
JX.Resource._loaded[uri] = true;
|
|
|
|
var errors = [];
|
|
for (ii = 0; ii < list.length; ii++) {
|
|
current = list[ii];
|
|
delete current.resources[uri];
|
|
if (!JX.Resource._hasResources(current.resources)) {
|
|
try {
|
|
current.callback();
|
|
} catch (error) {
|
|
errors.push(error);
|
|
}
|
|
list.splice(ii--, 1);
|
|
}
|
|
}
|
|
|
|
if (errors.length) {
|
|
throw errors[0];
|
|
}
|
|
},
|
|
|
|
_hasResources: function(resources) {
|
|
for (var hasResources in resources) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
},
|
|
|
|
initialize: function() {
|
|
var list = JX.$A(document.getElementsByTagName('link')),
|
|
ii = list.length,
|
|
node;
|
|
while ((node = list[--ii])) {
|
|
if (node.type == 'text/css' && node.href) {
|
|
JX.Resource._loaded[(new JX.URI(node.href)).toString()] = true;
|
|
}
|
|
}
|
|
|
|
list = JX.$A(document.getElementsByTagName('script'));
|
|
ii = list.length;
|
|
while ((node = list[--ii])) {
|
|
if (node.type == 'text/javascript' && node.src) {
|
|
JX.Resource._loaded[(new JX.URI(node.src)).toString()] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
});
|