@qooxdoo/framework
Version:
The JS Framework for Coders
262 lines (226 loc) • 7.09 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2009 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Fabian Jakobs (fjakobs)
* Martin Wittemann (martinwittemann)
************************************************************************ */
/**
* The Package wraps a list of related script URLs, which are required by one
* or more parts.
*
* @internal
* @ignore(qx.util.ResourceManager)
*/
qx.Bootstrap.define("qx.io.part.Package", {
/**
* @param urls {String[]} A list of script URLs
* @param id {var} Unique package hash key
* @param loaded {Boolean?false} Whether the package is already loaded
*/
construct(urls, id, loaded) {
this.__readyState = loaded ? "complete" : "initialized";
this.__urls = urls;
this.__id = id;
},
members: {
__readyState: null,
__urls: null,
__id: null,
__closure: null,
__loadWithClosure: null,
__timeoutId: null,
__notifyPackageResult: null,
/**
* Get the package ID.
*
* @return {String} The package id
*/
getId() {
return this.__id;
},
/**
* Get the ready state of the package. The value is one of
* <ul>
* <li>
* <b>initialized</b>: The package is initialized. The {@link #load}
* method has not yet been called
* </li>
* <li><b>loading</b>: The package is still loading.</li>
* <li><b>complete</b>: The package has been loaded successfully</li>
* <li><b>cached</b>: The package is loaded but is not executed
* (for closure parts)</li>
* </li>
*
* @return {String} The ready state.
*/
getReadyState() {
return this.__readyState;
},
/**
* Returns the urlsstored stored in the package.
*
* @internal
* @return {String[]} An array of urls of this package.
*/
getUrls() {
return this.__urls;
},
/**
* Method for storing the closure for this package. This is only relevant
* if a {@link qx.io.part.ClosurePart} is used.
*
* @param closure {Function} The code of this package wrapped in a closure.
*/
saveClosure(closure) {
if (this.__readyState == "error") {
return;
}
this.__closure = closure;
if (!this.__loadWithClosure) {
this.execute();
} else {
clearTimeout(this.__timeoutId);
this.__readyState = "cached";
this.__notifyPackageResult(this);
}
},
/**
* Executes the stored closure. This is only relevant if a
* {@link qx.io.part.ClosurePart} is used.
*/
execute() {
if (this.__closure) {
this.__closure();
delete this.__closure;
}
if (qx.$$packageData[this.__id]) {
this.__importPackageData(qx.$$packageData[this.__id]);
delete qx.$$packageData[this.__id];
}
this.__readyState = "complete";
},
/**
* Load method if the package loads a closure. This is only relevant if a
* {@link qx.io.part.ClosurePart} is used.
*
* @param notifyPackageResult {Function} The callback if all scripts are
* done loading in this package.
* @param self {Object?} The context of the callback.
*/
loadClosure(notifyPackageResult, self) {
if (this.__readyState !== "initialized") {
return;
}
this.__loadWithClosure = true;
this.__readyState = "loading";
this.__notifyPackageResult = qx.Bootstrap.bind(notifyPackageResult, self);
this.__loadScriptList(
this.__urls,
function () {},
function () {
this.__readyState = "error";
notifyPackageResult.call(self, this);
},
this
);
var pkg = this;
this.__timeoutId = setTimeout(function () {
pkg.__readyState = "error";
notifyPackageResult.call(self, pkg);
}, qx.Part.TIMEOUT);
},
/**
* Load the part's script URLs in the correct order.
*
* @param notifyPackageResult {Function} The callback if all scripts are
* done loading in this package.
* @param self {Object?} The context of the callback.
*/
load(notifyPackageResult, self) {
if (this.__readyState !== "initialized") {
return;
}
this.__loadWithClosure = false;
this.__readyState = "loading";
this.__loadScriptList(
this.__urls,
function () {
this.__readyState = "complete";
this.execute();
notifyPackageResult.call(self, this);
},
function () {
this.__readyState = "error";
notifyPackageResult.call(self, this);
},
this
);
},
/**
* Loads a list of scripts in the correct order.
*
* @param urlList {String[]} List of script urls
* @param callback {Function} Function to execute on completion
* @param errBack {Function} Function to execute on error
* @param self {Object?window} Context to execute the given function in
*/
__loadScriptList(urlList, callback, errBack, self) {
if (urlList.length == 0) {
callback.call(self);
return;
}
var urlsLoaded = 0;
var self = this;
var loadScripts = function (urls) {
if (urlsLoaded >= urlList.length) {
callback.call(self);
return;
}
var loader = new qx.bom.request.Script();
loader.open("GET", urls.shift());
loader.onload = function () {
urlsLoaded += 1;
loader.dispose();
// Important to use engine detection directly to keep the minimal
// package size small [BUG #5068]
if (qx.bom.client.Engine.getName() == "webkit") {
// force asynchronous load
// Safari fails with an "maximum recursion depth exceeded" error if it is
// called sync.
setTimeout(function () {
loadScripts.call(self, urls, callback, self);
}, 0);
} else {
loadScripts.call(self, urls, callback, self);
}
};
loader.onerror = function () {
if (self.__readyState == "loading") {
clearTimeout(self.__timeoutId);
loader.dispose();
return errBack.call(self);
}
};
// Force loading script asynchronously (IE may load synchronously)
window.setTimeout(function () {
loader.send();
});
};
loadScripts(urlList.concat());
},
/**
* Import the data of a package. The function is defined in the loader
* script.
*
* @signature function(packageData)
* @param packageData {Map} Map of package data categories ("resources",...)
*/
__importPackageData: qx.$$loader.importPackageData
}
});