/* Remote data loader code By Sergey Chernyshev For more details see http://www.sergeychernyshev.com/javascript/remoteloader/ */ var SERGEYCHE = SERGEYCHE || { tools: {} }; SERGEYCHE.tools.add_js = function(url, bDefer) { // create SCRIPT tag var scrptElement = document.createElement("script"); // set its attributes scrptElement.setAttribute("type", "text/javascript"); scrptElement.setAttribute("language", "JavaScript"); scrptElement.setAttribute("src", url); if (bDefer) { scrptElement.setAttribute("defer", true); } // add this new element to the head tag document.getElementsByTagName("head")[0].appendChild(scrptElement); }; SERGEYCHE.remoteloader = { /* a queue of loader objects */ loaders: {}, callbacks: {}, /* Detach the loader and return it. It'll return null if loader is no longer in the queue Detaching will effectively cancel subsequent callback and timeout */ detach: function(request_id) { /* It would've been ideal if there was an atomic pop for hashes For now we'll assume that nothing happens between getting loader from the queue and removing it from */ var loader = this.loaders[request_id]; if (loader !== null) { /* removing from queue so next call to detach would return null */ delete this.loaders[request_id]; } return loader; }, cancel: function(request_id) { var loader = this.detach(request_id); if (!loader) { return; } if (typeof loader.onCancel === "function") { loader.onCancel(request_id); } }, /* @loader Loader object - must have following members url: URL to call (we can call only URLs b'cause it's a remote server) success: function to be called from SERGEYCHE.remoteloader.callback timeout: will call failure function if timed out (optional) failure: failure callback function to call in case of timeout (we can't really know if there is a problem - just timeout) optional, must be specified if timeout is set @request_id optional unique identifier of data request, will be used to identify a loading object. */ load: function(loader, request_id) { if (typeof loader === 'undefined' || typeof loader.url === 'undefined' || typeof loader.onSuccess !== 'function') { throw ('Loader object must have at least url property and onSuccess event handler'); } this.loaders[request_id] = loader; this.callbacks[request_id] = function(request_id) { return function(data) { var r = request_id; /* This one can be called after loader got detached That's why it's here and not in detach method */ delete SERGEYCHE.remoteloader.callbacks[r]; /* Calling callback function to */ return SERGEYCHE.remoteloader.callback(data, r); }; }(request_id); /* Run timeout function even if we don't have a onFailure handler At least it'll remove a loader object from memory */ if (loader.timeout > 0) { setTimeout("SERGEYCHE.remoteloader.timeout('" + request_id + "')", loader.timeout); } loader.request_id = request_id; /* Uncomment this to emulate timeout alert('waiting'); */ SERGEYCHE.tools.add_js(loader.url); }, /* @callback_data data returned by the call @request_id optional unique identifier of data request, will be used to identify a loader object. */ callback: function(callback_data, request_id) { var loader = this.detach(request_id); if (!loader) { return; } if (typeof loader.onSuccess === "function") { loader.onSuccess(callback_data, request_id); } }, /* @request_id unique identifier of data request, will be used to identify a loader object. */ timeout: function(request_id) { var loader = this.detach(request_id); if (!loader) { return; } if (typeof loader.onFailure === "function") { loader.onFailure(request_id); } } };