313 lines
8.2 KiB
JavaScript
313 lines
8.2 KiB
JavaScript
/** This class allows the user to load data files from the server using the browser's
|
|
* native XML loader class.
|
|
*/
|
|
Base.XMLLoader = Base.Comp.inherits({
|
|
|
|
/** This property indicates whether the instance is currently loading data
|
|
* from the server.
|
|
*/
|
|
isLoading: false,
|
|
|
|
/** This property contains the native XML loader object when data is being
|
|
* loaded or null when the loader isn't active.
|
|
*/
|
|
xmlObject: null,
|
|
|
|
/** This property contains the loader's timer when data is being retrieved.
|
|
* The timer will be used to force the loader object to timeout. When no
|
|
* data is being loaded, the property contains the null value.
|
|
*/
|
|
timer: null,
|
|
|
|
|
|
/** The constructor initialises the component itself and sets default values
|
|
* for the URI, method and timeout to use.
|
|
* @param uri a string containing the URI from which the data is
|
|
* to be fetched
|
|
* @param usePost a boolean indicating whether the loader should use
|
|
* the HTTP POST method (true) or GET method
|
|
* @param timeout an optionnal integer containing the amount of seconds
|
|
* before the loader times out; this defaults to
|
|
* 20 seconds if the parameter is missing
|
|
*/
|
|
constructor: function (uri, usePost, timeout) {
|
|
this.base();
|
|
|
|
this.addEvent('Aborted');
|
|
this.addEvent('Loaded');
|
|
this.addEvent('NetworkError');
|
|
this.addEvent('ServerError');
|
|
this.addEvent('Timeout');
|
|
this.addEvent('Unsupported');
|
|
|
|
this.addSlot('load');
|
|
this.addSlot('abort');
|
|
this.addSlot('timedOut');
|
|
|
|
this.uri = uri;
|
|
this.usePost = usePost;
|
|
this.timeout = timeout ? timeout : 20;
|
|
},
|
|
|
|
/** The destructor starts by aborting the loader's operation, then calls
|
|
* the standard component destructor.
|
|
*/
|
|
destroy: function () {
|
|
this.abort();
|
|
this.base();
|
|
},
|
|
|
|
|
|
/** This method allows the user to change the parameters such as the URI, the
|
|
* method to be used and the timeout value. It will only be able to do so if
|
|
* the loader isn't operating.
|
|
* @param uri the new data URI (ignored if null)
|
|
* @param usePost the new method to use (ignored if null)
|
|
* @param timeout the new value for the loader's timeout (ignored if
|
|
* null)
|
|
* @returns true if the values were set, false if they weren't
|
|
*/
|
|
changeParameters: function (uri, usePost, timeout) {
|
|
if (this.isLoading) {
|
|
return false;
|
|
}
|
|
|
|
this.uri = uri ? uri : this.uri;
|
|
this.usePost = (typeof usePost == 'boolean') ? usePost : this.usePost;
|
|
this.timeout = timeout ? timeout : 20;
|
|
|
|
return true;
|
|
},
|
|
|
|
|
|
/** This method loads the data using the object's URI and method using the
|
|
* specified parameters.
|
|
* @param parameters a hashtable associating to each of the parameters'
|
|
* name a value that is either a string or an
|
|
* array
|
|
* @returns true if the loader was successfully started
|
|
*/
|
|
load: function (parameters) {
|
|
// Make sure we're not already loading
|
|
if (this.isLoading) {
|
|
return false;
|
|
}
|
|
this.isLoading = true;
|
|
Base.Log.write('Base.XMLLoader.load(): ' + (this.usePost ? 'POST' : 'GET') + ' ' + this.uri);
|
|
|
|
// Initialise the native loader object
|
|
var obj = Base.XMLLoader.makeNativeInstance();
|
|
if (!obj) {
|
|
this.isLoading = false;
|
|
Base.Log.write('Base.XMLLoader.load(): XMLHttpRequest unsupported');
|
|
this.onUnsupported();
|
|
return false;
|
|
}
|
|
|
|
// Generate the parameters string and the URI
|
|
var uri = this.uri, ps;
|
|
if (parameters) {
|
|
ps = Base.XMLLoader.encodeParameters(parameters);
|
|
if (!this.usePost) {
|
|
uri += '?' + ps;
|
|
}
|
|
} else {
|
|
ps = "";
|
|
}
|
|
|
|
/*
|
|
// Safari and IE are stupid
|
|
if (Base.Browser.get().safari || Base.Browser.get().IE) {
|
|
if (this.usePost || ps == '') {
|
|
uri += '?stoopid_browsa=' + (new Date()).getTime();
|
|
} else {
|
|
uri += '&stoopid_browsa=' + (new Date()).getTime();
|
|
}
|
|
}
|
|
*/
|
|
|
|
// Get ready to send the request
|
|
try {
|
|
obj.open(this.usePost ? "POST" : "GET", uri, true);
|
|
} catch (e) {
|
|
// We got a permission problem
|
|
this.isLoading = false;
|
|
Base.Log.write('Base.XMLLoader.load(): XMLHttpRequest.open() failed, possible permission problem');
|
|
Base.Log.write('Base.XMLLoader.load(): exception was ' + (e.toString ? e.toString() : e));
|
|
this.onUnsupported();
|
|
return false;
|
|
}
|
|
if (this.usePost) {
|
|
obj.setRequestHeader("Method", "POST " + uri + " HTTP/1.1");
|
|
obj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
|
}
|
|
// Safari is still stupid
|
|
if (Base.Browser.get().safari) {
|
|
obj.setRequestHeader('If-Modified-Since', 'Wed, 15 Nov 1995 00:00:00 GMT');
|
|
obj.setRequestHeader("Cache-Control", "no-cache");
|
|
}
|
|
|
|
// Initialise the timer
|
|
this.timer = new Base.Timer(this.timeout * 1000);
|
|
this.timer.bindEvent('Tick', 'timedOut', this);
|
|
|
|
// Prepare the callback
|
|
var _id = this._cid;
|
|
obj.onreadystatechange = function () {
|
|
if (typeof Base != 'undefined') {
|
|
Base.Comp.get(_id).loaderEvent();
|
|
}
|
|
};
|
|
|
|
// Start the timer and send the request
|
|
this.xmlObject = obj;
|
|
this.timer.start();
|
|
obj.send((parameters && this.usePost) ? ps : null);
|
|
return true;
|
|
},
|
|
|
|
/** This method cancels the current operation if there is one.
|
|
*/
|
|
abort: function () {
|
|
if (!this.isLoading) {
|
|
return;
|
|
}
|
|
|
|
this.doAbort();
|
|
this.onAborted();
|
|
},
|
|
|
|
|
|
/** This method is used as a callback for the native loader object.
|
|
*/
|
|
loaderEvent: function () {
|
|
if (!this.isLoading) {
|
|
return;
|
|
}
|
|
|
|
var obj = this.xmlObject;
|
|
try {
|
|
if (obj.readyState != 4) {
|
|
return;
|
|
}
|
|
|
|
if (obj.status != 200) {
|
|
this.doAbort();
|
|
Base.Log.write('Base.XMLLoader.load(): server returned ' + obj.status);
|
|
this.onServerError(obj.status);
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
this.doAbort();
|
|
Base.Log.write('Base.XMLLoader.load(): exception ' + (e.toString ? e.toString() : e) + ' while receiving');
|
|
this.onNetworkError(e);
|
|
return;
|
|
}
|
|
|
|
var _r = obj.responseText;
|
|
this.doAbort();
|
|
this.onLoaded(_r);
|
|
},
|
|
|
|
/** This method is responsible for deleting existing objects such as
|
|
* the native loader and the timer component.
|
|
*/
|
|
doAbort: function () {
|
|
this.isLoading = false;
|
|
|
|
if (this.xmlObject) {
|
|
this.xmlObject.abort();
|
|
delete this.xmlObject;
|
|
this.xmlObject = null;
|
|
}
|
|
|
|
if (this.timer) {
|
|
this.timer.destroy();
|
|
this.timer = null;
|
|
}
|
|
},
|
|
|
|
/** This method is called by the timer when the time imparted for loading the
|
|
* data expires.
|
|
*/
|
|
timedOut: function () {
|
|
if (!this.isLoading) {
|
|
return;
|
|
}
|
|
Base.Log.write('Base.XMLLoader: timeout :(');
|
|
this.doAbort();
|
|
this.onTimeout();
|
|
}
|
|
|
|
}, {
|
|
|
|
/** This class property stores the code to be used to generate native
|
|
* loader objects. If it is empty, the code hasn't been determined yet,
|
|
* and the "E" value indicates an error.
|
|
*/
|
|
objCode: "",
|
|
|
|
/** This class method generates native loader objects. If the objCode
|
|
* class property has been initialised, it uses its content to generate
|
|
* the new object. Otherwise it explores available options to try and
|
|
* generate a native loader object.
|
|
* @returns the native loader instance on success, null on failure
|
|
*/
|
|
makeNativeInstance: function () {
|
|
var obj;
|
|
|
|
if (this.objCode == "E") {
|
|
return null;
|
|
}
|
|
|
|
if (this.objCode != "") {
|
|
eval('obj=' + this.objCode);
|
|
} else {
|
|
try {
|
|
obj = new ActiveXObject("Msxml2.XMLHTTP");
|
|
this.objCode = 'new ActiveXObject("Msxml2.XMLHTTP")';
|
|
} catch (e) {
|
|
try {
|
|
obj = new ActiveXObject("Microsoft.XMLHTTP");
|
|
this.objCode = 'new ActiveXObject("Microsoft.XMLHTTP")';
|
|
} catch (oc) {
|
|
obj = null;
|
|
}
|
|
}
|
|
if (!obj && typeof XMLHttpRequest != "undefined") {
|
|
obj = new XMLHttpRequest();
|
|
this.objCode = "new XMLHttpRequest()";
|
|
}
|
|
}
|
|
|
|
if (!obj && typeof obj == "object" && this.objCode != "E") {
|
|
this.objCode = "E";
|
|
obj = null;
|
|
}
|
|
|
|
return obj;
|
|
},
|
|
|
|
/** This class method encodes a hashtable containing request parameters into
|
|
* a string usable when loading the data.
|
|
* @param parameters the hashtable containing the parameters to be encoded.
|
|
* @returns the encoded string
|
|
*/
|
|
encodeParameters: function (parameters) {
|
|
var mk = new Array(), pl = parameters.keys();
|
|
for (var i in pl) {
|
|
var _k = pl[i], _pn = encodeURIComponent(_k), _v = parameters.get(_k);
|
|
|
|
if (_v instanceof Array) {
|
|
for (var j in _v) {
|
|
mk.push(_pn + '[]=' + encodeURIComponent(_v[j]));
|
|
}
|
|
} else {
|
|
mk.push(_pn + '=' + encodeURIComponent(_v));
|
|
}
|
|
}
|
|
|
|
return mk.join('&');
|
|
}
|
|
|
|
});
|