@qooxdoo/framework
Version:
The JS Framework for Coders
291 lines (256 loc) • 7.67 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2016 Visionet GmbH, http://www.visionet.de
2016 OETIKER+PARTNER AG, https://www.oetiker.ch
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Dietrich Streifert (level420)
* Tobias Oetiker (oetiker)
************************************************************************ */
/**
* Dynamically load non qx scripts. This class is aware of all scripts that have
* been loaded using its instances, so if two instances load jquery, it will only
* be loaded once, and the second instance will wait for the jquery to be loaded
* before continuing to load additional scripts.
*
* Usage example:
*
* <pre>
* ... assets ...
* /**
* * @asset(myapp/jquery/*)
* * @asset(myapp/highcharts/*)
* *
* * @ignore(jQuery.*)
* * @ignore(Highcharts.*)
* ...
*
*
* // in debug mode load the uncompressed unobfuscated scripts
* var src = '';
* var min = '.min';
* if (qx.core.Environment.get("qx.debug")) {
* src = '.src';
* min = '';
* }
*
* // initialize the script loading
* var dynLoader = new qx.util.DynamicScriptLoader([
* "myapp/jquery/jquery"+min+".js",
* "myapp/highcharts/highcharts"+src+".js",
* "myapp/highcharts/highcharts-more"+src+".js",
* "myapp/highcharts/highcharts-modifications.js"
* ]);
*
*
* dynLoader.addListenerOnce('ready',function(e){
* console.log("all scripts have been loaded!");
* });
*
* dynLoader.addListener('failed',function(e){
* var data = e.getData();
* console.log("failed to load "+data.script);
* });
*
* dynLoader.start();
*
* </pre>
*/
qx.Class.define("qx.util.DynamicScriptLoader", {
extend: qx.core.Object,
/**
* Create a loader for the given scripts.
*
* @param scriptArr {Array|String} the uri name(s) of the script(s) to load
*/
construct(scriptArr) {
super();
this.__started = false;
this.__QUEUE = qx.lang.Type.isString(scriptArr)
? [scriptArr]
: qx.lang.Array.clone(scriptArr);
},
/*
*****************************************************************************
EVENTS
*****************************************************************************
*/
events: {
/**
* fired when a script is loaded successfully. The data contains 'script' and 'status' keys.
*/
loaded: "qx.event.type.Data",
/**
* fired when a specific script fails loading. The data contains 'script' and 'status' keys.
*/
failed: "qx.event.type.Data",
/**
* fired when all given scripts are loaded, each time loadScriptsDynamic is called.
*/
ready: "qx.event.type.Event"
},
statics: {
/**
* Map of scripts being added at the present time. Key is script name; value is instance of this class which
* is loading it.
*/
__IN_PROGRESS: {},
/**
* Map of scripts that have fully loaded. Key is script name; value is true
*/
__LOADED: {}
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members: {
/**
* Array of the scripts to be loaded
*/
__QUEUE: null,
/**
* True if start has been called.
*/
__started: null,
/**
* Start loading scripts. This may only be called once!
* @return {Promise?} a promise which will be resolved after load of all scripts if promise support is enabled; nothing (undefined) if promises are not enabled.
*/
start: qx.core.Environment.select("qx.promise", {
true() {
return new qx.Promise(function (resolve, reject) {
this.addListenerOnce("ready", resolve, this);
this.addListenerOnce("failed", e => {
reject(new Error(e.getData()));
});
if (this.isDisposed()) {
reject(new Error("disposed"));
}
if (this.__started) {
reject(new Error("you can only call start once per instance"));
}
this.__started = true;
this.__loadScripts();
}, this);
},
false() {
if (this.isDisposed()) {
return;
}
if (this.__started) {
throw new Error("you can only call start once per instance");
}
this.__started = true;
this.__loadScripts();
}
}),
/**
* Chain loading scripts.
*
* Recursively called until the array of scripts is consumed
*
*/
__loadScripts() {
var DynamicScriptLoader = qx.util.DynamicScriptLoader;
var script;
var dynLoader;
var id1, id2;
var uri;
var loader;
script = this.__QUEUE.shift();
if (!script) {
this.fireEvent("ready");
return;
}
if (DynamicScriptLoader.__LOADED[script]) {
this.fireDataEvent("loaded", {
script: script,
status: "preloaded"
});
this.__loadScripts();
return;
}
dynLoader = DynamicScriptLoader.__IN_PROGRESS[script];
if (dynLoader) {
id1 = dynLoader.addListener("loaded", e => {
if (this.isDisposed()) {
return;
}
var data = e.getData();
if (data.script === script) {
dynLoader.removeListenerById(id2);
dynLoader.removeListenerById(id1);
this.fireDataEvent("loaded", data);
this.__loadScripts();
}
});
id2 = dynLoader.addListener("failed", e => {
if (this.isDisposed()) {
return;
}
var data = e.getData();
dynLoader.removeListenerById(id1);
dynLoader.removeListenerById(id2);
this.fireDataEvent("failed", {
script: script,
status:
"loading of " +
data.script +
" failed while waiting for " +
script
});
});
return;
}
uri = qx.util.ResourceManager.getInstance().toUri(script);
loader = new qx.bom.request.Script();
loader.on(
"load",
function (request) {
if (this.isDisposed()) {
return;
}
DynamicScriptLoader.__LOADED[script] = true;
delete DynamicScriptLoader.__IN_PROGRESS[script];
this.fireDataEvent("loaded", {
script: script,
status: request.status
});
this.__loadScripts();
},
this
);
var onError = function (request) {
if (this.isDisposed()) {
return;
}
delete DynamicScriptLoader.__IN_PROGRESS[script];
this.fireDataEvent("failed", {
script: script,
status: request.status
});
};
loader.on("error", onError, this);
loader.on("timeout", onError, this);
// this.debug("Loading " + script + " started");
loader.open("GET", uri);
DynamicScriptLoader.__IN_PROGRESS[script] = this;
loader.send();
}
},
destruct() {
var DynamicScriptLoader = qx.util.DynamicScriptLoader;
for (var key in DynamicScriptLoader.__IN_PROGRESS) {
if (DynamicScriptLoader.__IN_PROGRESS[key] === this) {
delete DynamicScriptLoader.__IN_PROGRESS[key];
}
}
this.__QUEUE = undefined;
}
});