Ext.data.Protocol = Ext.extend(Object, { constructor: function(config) { this.remote = config.remoteStorageProxy; this.remote.on('exception',function(proxy,request,operation){ console.log('EXCEPTION'); // JCM should handle this properly... console.log(request); console.log(operation); }); }, sync: function(local, callback, scope) { // // JCM callback if something is going to take a long time... // JCM like changing the replica number // JCM or clearing after a generation change // if (callback===undefined) { callback= function(){}; } // JCM maybe should warn the caller... this.send_create_database(local.definition,function(operation) { var response= operation.response; switch (response.r) { case 'ok': // // The remote CSV describes the state of updated-ness of the // server this client is talking to. We add any replica numbers // that are new to us to our local CSV. // var remote_csv= response.csv; local.addReplicaNumbers(remote_csv); this.sync_datastore(local,remote_csv,callback,scope); break; case 'new_replica_number': // // A replica number collision, or re-initialization, has occured. // In either case we must change our local replica number. // local.setReplicaNumber(response.replica_number,function(){ this.sync(local,callback,scope); // JCM beware of infinite loop },this); break; case 'new_generation_number': // // The database generation has changed. We clear out the database, // and update the definition. // if (response.generation>local.definition.generation) { local.definition.set({generation:response.generation},function(){ local.clear(function(){ this.sync(local,callback,scope); // JCM beware of infinite loop },this); },this); } else { // local is the same, or greater than the server. } break; default: callback.call(scope); break; } },this); }, // @private sync_datastore: function(local, remote_csv, callback, scope) { // // JCM In theory... we could send and receive at the same time... // local.getUpdates(remote_csv,function(updates){ this.put_database_updates(local.definition,updates,function(operation){ if (remote_csv.dominates(local.csv)) { this.get_database_updates(local,callback,scope); } else { callback.call(scope); } },this); },this); }, send_create_database: function(definition,callback,scope) { var request= definition.encode(); this.sendRequest(definition.database_name,'edit',request,function(operation){ var response= operation.response; if (response.csv) { response.csv= new Ext.data.CSV().decode(response.csv); } callback.call(scope, operation); },this); }, put_database_updates: function(definition,updates,callback,scope) { // // When sending updates through the ScriptTagProxy the data is // encoded as a URL, so there is a browser imposed limit on the // length of that URL. So, let's be prudent and limit the amount // of data sent at a time. // var chunks= updates.chunks(1000); // JCM count of updates... should be K? Ext.data.array.forEachYielding(chunks,function(chunk,next_callback,next_scope){ this.send_put_database_updates(definition,chunk,function(operation){ //if (operation.response.r=='ok') { // keep going //} else { // JCM abort... but how? //} next_callback.call(next_scope); },this); },this,callback,scope); }, send_put_database_updates: function(definition,updates,callback,scope) { if (!updates.isEmpty()) { var request= { updates: Ext.encode(updates.encode()) }; this.sendRequest(definition.database_name,'put_updates',request,callback,scope); } else { var operation= this.encodeRequest({}); operation.response= {r:"ok"}; callback.call(scope, operation); } }, get_database_updates: function(local,callback,scope) { this.send_get_database_updates(local.definition,local.csv,function(operation){ // // JCM perhaps an 'event' should be fired for each object changed // JCM which serves as a trigger for the UI to update // var response= operation.response; if (response.r=='ok') { local.putUpdates(response.updates,function() { if (response.remaining>0 && !response.updates.isEmpty()) { this.get_database_updates(local,callback,scope); } else { callback.call(scope); } },this); } else { callback.call(scope); } },this); }, send_get_database_updates: function(definition,csv,callback,scope) { var request= { csv: csv.encode() }; this.sendRequest(definition.database_name,'get_updates',request,function(operation){ var response= operation.response; response.updates= new Ext.data.Updates().decode(response.updates); callback.call(scope, operation); },this); }, sendRequest: function(database_name,method,request,callback,scope) { var operation= this.encodeRequest(database_name,method,request); var debug= true; // JCM if (debug) { console.log("local ->",this.remote.url,method,Ext.encode(request)); } this.remote.read(operation,function(operation){ if (debug) { console.log(" sent",operation.request.url.length,"bytes -",operation.request.url); console.log(" <= ",method,Ext.encode(operation.response)); } callback.call(scope, operation); },this); }, encodeRequest: function(database_name,method,request) { var text= Ext.encode(request); var url= this.remote.url+"database/"+database_name+"/"+method; return new Ext.data.Operation({ filters: [], action: 'read', url: url, params: request }); return operation; } });