lwb5-in-2025/misc/Legacy Worlds.wdgt/lib/Base/XMLLoader.js

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('&');
}
});