Ajax requests in HTML5 offline applications
I’ve started having a look at writing offline applications. Its good fun to write using different technology and I am discovering that the whole HTML5 stack is still very immature. The stack I’d settled on for my first attempts is: HTML5, jQuery, jQueryMobile and Knockoutjs.
The first stumbling block I came across was that ajax calls work differently when in offline mode. It turns out that jQuery returns a status code of zero when the request is satisfied from the cache which upsets the usual code where we are looking for 200 (OK).
One possible solution suggested in the jQeryMobile documentation, is to attach an ajaxPrefilter that will set the isLocal flag to get jQuery to process the zero status correctly. However I did not want to alter the jQuery ajax mechanism completely, I wanted to just affected the ajax calls I was making. Also setting the isLocal flag does achieve the desired result at the moment however it may not work correctly in the future.
I decided to detect the process the zero status code myself using code like this.
function JsonDataSource(url,dataCallback) { var self = this; self.url = url; self.dataCallback = null; self.reloadFromServer = false; self.getData = function(reloadFromServer, dataCallback) { self.dataCallback = dataCallback; self.reloadFromServer = reloadFromServer; $.ajax({ type: "GET", url: self.url, cache: !reloadFromServer, contentType: "application/json", dataType: "text", error: function (jqXHR, textStatus, errorThrown) { self.processErrorResult(jqXHR, textStatus, errorThrown); }, success: function (data) { self.processDataResult(data,false) } }); }; self.processErrorResult = function (jqXHR, textStatus, errorThrown) { if (jqXHR.status === 0 && jqXHR.responseText.length > 0) { // the approved method of detecting the cache // is a zero return code and data being returned self.processDataResult(jqXHR.responseText, true); return; } self.errorResult(jqXHR,textStatus,errorThrown); }; self.errorResult = function (jqXHR, textStatus, errorThrown) { if (jqXHR.status === 0) { alert('Not connect.\n Verify Network.'); } else if (jqXHR.status == 404) { alert('Requested page not found. [404]'); } else if (jqXHR.status == 500) { alert('Internal Server Error [500].'); } else if (textStatus === 'parsererror') { alert('Requested JSON parse failed.'); } else if (textStatus === 'timeout') { alert('Time out error.'); } else if (textStatus === 'abort') { alert('Ajax request aborted.'); } else { alert('Uncaught Error (' + jqXHR.status + ') .\n' + jqXHR.responseText); } }; self.processDataResult = function (data,fromCache) { var object = eval('(' + data + ')'); self.dataCallback(object,fromCache,self.reloadFromServer); }; }
Then I can call it like this
self.catagoryDataSource = new JsonDataSource('viewmodel/catagory-json.txt'); self.loadCatagories = function (catagories, fromCache, reloadFromServer) { for (var i = 0; i < catagories.length; i++) { self.catagories.push( new Catagory(catagories[i].CatagoryId,catagories[i].Display)); } }; self.catagoryDataSource.getData(false, self.loadCatagories);
This struck me as a neater solution. This will load data from a text file that can either be obtained from the server of from the local cache if we are offline. I use eval rather than JSON.Parse as it is supported on more browsers and I have complete control over both ends of the link.