source: [view]
define("dojox/rpc/OfflineRest", ["dojo", "dojox", "dojox/data/ClientFilter", "dojox/rpc/Rest", "dojox/storage"], function(dojo, dojox) {
// summary:
// Makes the REST service be able to store changes in local
// storage so it can be used offline automatically.
var Rest = dojox.rpc.Rest;
var namespace = "dojox_rpc_OfflineRest";
var loaded;
var index = Rest._index;
dojox.storage.manager.addOnLoad(function(){
// now that we are loaded we need to save everything in the index
loaded = dojox.storage.manager.available;
for(var i in index){
saveObject(index[i], i);
}
});
var dontSave;
function getStorageKey(key){
// returns a key that is safe to use in storage
return key.replace(/[^0-9A-Za-z_]/g,'_');
}
function saveObject(object,id){
// save the object into local storage
if(loaded && !dontSave && (id || (object && object.__id))){
dojox.storage.put(
getStorageKey(id||object.__id),
typeof object=='object'?dojox.json.ref.toJson(object):object, // makeshift technique to determine if the object is json object or not
function(){},
namespace);
}
}
function isNetworkError(error){
// determine if the error was a network error and should be saved offline
// or if it was a server error and not a result of offline-ness
return error instanceof Error && (error.status == 503 || error.status > 12000 || !error.status); // TODO: Make the right error determination
}
function sendChanges(){
// periodical try to save our dirty data
if(loaded){
var dirty = dojox.storage.get("dirty",namespace);
if(dirty){
for (var dirtyId in dirty){
commitDirty(dirtyId,dirty);
}
}
}
}
var OfflineRest;
function sync(){
OfflineRest.sendChanges();
OfflineRest.downloadChanges();
}
var syncId = setInterval(sync,15000);
dojo.connect(document, "ononline", sync);
OfflineRest = dojox.rpc.OfflineRest = {
turnOffAutoSync: function(){
clearInterval(syncId);
},
sync: sync,
sendChanges: sendChanges,
downloadChanges: function(){
},
addStore: function(/*data-store*/store,/*query?*/baseQuery){
// summary:
// Adds a store to the monitored store for local storage
// store:
// Store to add
// baseQuery:
// This is the base query to should be used to load the items for
// the store. Generally you want to load all the items that should be
// available when offline.
OfflineRest.stores.push(store);
store.fetch({queryOptions:{cache:true},query:baseQuery,onComplete:function(results,args){
store._localBaseResults = results;
store._localBaseFetch = args;
}});
}
};
OfflineRest.stores = [];
var defaultGet = Rest._get;
Rest._get = function(service, id){
// We specifically do NOT want the paging information to be used by the default handler,
// this is because online apps want to minimize the data transfer,
// but an offline app wants the opposite, as much data as possible transferred to
// the client side
try{
// if we are reloading the application with local dirty data in an online environment
// we want to make sure we save the changes first, so that we get up-to-date
// information from the server
sendChanges();
if(window.navigator && navigator.onLine===false){
// we force an error if we are offline in firefox, otherwise it will silently load it from the cache
throw new Error();
}
var dfd = defaultGet(service, id);
}catch(e){
dfd = new dojo.Deferred();
dfd.errback(e);
}
var sync = dojox.rpc._sync;
dfd.addCallback(function(result){
saveObject(result, service._getRequest(id).url);
return result;
});
dfd.addErrback(function(error){
if(loaded){
// if the storage is loaded, we can go ahead and get the object out of storage
if(isNetworkError(error)){
var loadedObjects = {};
// network error, load from local storage
var byId = function(id,backup){
if(loadedObjects[id]){
return backup;
}
var result = dojo.fromJson(dojox.storage.get(getStorageKey(id),namespace)) || backup;
loadedObjects[id] = result;
for(var i in result){
var val = result[i]; // resolve references if we can
id = val && val.$ref;
if (id){
if(id.substring && id.substring(0,4) == "cid:"){
// strip the cid scheme, we should be able to resolve it locally
id = id.substring(4);
}
result[i] = byId(id,val);
}
}
if (result instanceof Array){
//remove any deleted items
for (i = 0;i if (result[i]===undefined){
result.splice(i--,1);
}
}
}
return result;
};
dontSave = true; // we don't want to be resaving objects when loading from local storage
//TODO: Should this reuse something from dojox.rpc.Rest
var result = byId(service._getRequest(id).url);
if(!result){// if it is not found we have to just return the error
return error;
}
dontSave = false;
return result;
}
else{
return error; // server error, let the error propagate
}
}
else{
if(sync){
return new Error("Storage manager not loaded, can not continue");
}
// we are not loaded, so we need to defer until we are loaded
dfd = new dojo.Deferred();
dfd.addCallback(arguments.callee);
dojox.storage.manager.addOnLoad(function(){
dfd.callback();
});
return dfd;
}
});
return dfd;