Commit 77635b7a authored by Tristan Cavelier's avatar Tristan Cavelier

Adding Qunit tests and corrections to conflict and crypt storage.

parent b43c7be2
/*! JIO Storage - v0.1.0 - 2012-06-26 /*! JIO Storage - v0.1.0 - 2012-06-27
* Copyright (c) 2012 Nexedi; Licensed */ * Copyright (c) 2012 Nexedi; Licensed */
(function(LocalOrCookieStorage, $, Base64, sjcl, hex_sha256, Jio) { (function(LocalOrCookieStorage, $, Base64, sjcl, hex_sha256, Jio) {
...@@ -160,9 +160,9 @@ var newLocalStorage = function ( spec, my ) { ...@@ -160,9 +160,9 @@ var newLocalStorage = function ( spec, my ) {
'jio/local/'+priv.username+'/'+ 'jio/local/'+priv.username+'/'+
priv.applicationname+'/'+command.getPath()); priv.applicationname+'/'+command.getPath());
if (!doc) { if (!doc) {
that.fail(command,{status:404,statusText:'Not Found.', that.fail({status:404,statusText:'Not Found.',
message:'Document "'+ command.getPath() + message:'Document "'+ command.getPath() +
'" not found in localStorage.'}); '" not found in localStorage.'});
} else { } else {
if (command.getOption('metadata_only')) { if (command.getOption('metadata_only')) {
delete doc.content; delete doc.content;
...@@ -870,22 +870,24 @@ Jio.addStorageType ('indexed', newIndexStorage); ...@@ -870,22 +870,24 @@ Jio.addStorageType ('indexed', newIndexStorage);
var newCryptedStorage = function ( spec, my ) { var newCryptedStorage = function ( spec, my ) {
var that = Jio.storage( spec, my, 'handler' ), priv = {}; var that = Jio.storage( spec, my, 'handler' ), priv = {};
var is_valid_storage = spec.storage || false;
priv.username = spec.username || ''; priv.username = spec.username || '';
priv.password = spec.password || ''; priv.password = spec.password || '';
priv.secondstorage_spec = spec.storage || {type:'base'}; priv.secondstorage_spec = spec.storage || {type:'base'};
priv.secondstorage_string = JSON.stringify (priv.secondstorage_string);
var super_serialized = that.serialized; var super_serialized = that.serialized;
that.serialized = function () { that.serialized = function () {
var o = super_serialized(); var o = super_serialized();
o.username = priv.username; o.username = priv.username;
o.password = priv.password; o.password = priv.password;
o.storage = priv.secondstorage_string;
return o; return o;
}; };
that.validateState = function () { that.validateState = function () {
if (priv.username && if (priv.username && is_valid_storage) {
JSON.stringify (priv.secondstorage_spec) ===
JSON.stringify ({type:'base'})) {
return ''; return '';
} }
return 'Need at least two parameters: "username" and "storage".'; return 'Need at least two parameters: "username" and "storage".';
...@@ -913,8 +915,8 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -913,8 +915,8 @@ var newCryptedStorage = function ( spec, my ) {
priv.encrypt = function (data,callback,index) { priv.encrypt = function (data,callback,index) {
// end with a callback in order to improve encrypt to an // end with a callback in order to improve encrypt to an
// asynchronous encryption. // asynchronous encryption.
var tmp = sjcl.encrypt (that.getStorageUserName()+':'+ var tmp = sjcl.encrypt (priv.username+':'+
that.getStoragePassword(), data, priv.password, data,
priv.encrypt_param_object); priv.encrypt_param_object);
callback(JSON.parse(tmp).ct,index); callback(JSON.parse(tmp).ct,index);
}; };
...@@ -923,8 +925,8 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -923,8 +925,8 @@ var newCryptedStorage = function ( spec, my ) {
param.ct = data || ''; param.ct = data || '';
param = JSON.stringify (param); param = JSON.stringify (param);
try { try {
tmp = sjcl.decrypt (that.getStorageUserName()+':'+ tmp = sjcl.decrypt (priv.username+':'+
that.getStoragePassword(), priv.password,
param); param);
} catch (e) { } catch (e) {
callback({status:0,statusText:'Decrypt Fail', callback({status:0,statusText:'Decrypt Fail',
...@@ -939,7 +941,7 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -939,7 +941,7 @@ var newCryptedStorage = function ( spec, my ) {
* @method saveDocument * @method saveDocument
*/ */
that.saveDocument = function (command) { that.saveDocument = function (command) {
var new_file_name, newfilecontent, var new_file_name, new_file_content,
_1 = function () { _1 = function () {
priv.encrypt(command.getPath(),function(res) { priv.encrypt(command.getPath(),function(res) {
new_file_name = res; new_file_name = res;
...@@ -948,21 +950,21 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -948,21 +950,21 @@ var newCryptedStorage = function ( spec, my ) {
}, },
_2 = function () { _2 = function () {
priv.encrypt(command.getContent(),function(res) { priv.encrypt(command.getContent(),function(res) {
newfilecontent = res; new_file_content = res;
_3(); _3();
}); });
}, },
_3 = function () { _3 = function () {
var settings = that.cloneOption(), newcommand, newstorage; var settings = command.cloneOption(), newcommand;
settings.onResponse = function (){}; settings.onResponse = function (){};
settings.onDone = function () { that.done(); }; settings.onDone = function () { that.done(); };
settings.onFail = function (r) { that.fail(r); }; settings.onFail = function (r) { that.fail(r); };
newcommand = that.newCommand( newcommand = that.newCommand(
{path:new_file_name, 'saveDocument',
content:newfilecontent, {path:new_file_name,content:new_file_content,option:settings});
option:settings}); that.addJob (
newstorage = that.newStorage( priv.secondstorage_spec ); that.newStorage( priv.secondstorage_spec ),
that.addJob ( newstorage, newcommand ); newcommand );
}; };
_1(); _1();
}; // end saveDocument }; // end saveDocument
...@@ -980,22 +982,22 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -980,22 +982,22 @@ var newCryptedStorage = function ( spec, my ) {
}); });
}, },
_2 = function () { _2 = function () {
var settings = command.cloneOption(), newcommand, newstorage; var settings = command.cloneOption(), newcommand;
settings.onResponse = function(){}; settings.onResponse = function(){};
settings.onFail = loadOnFail; settings.onFail = loadOnFail;
settings.onDone = loadOnDone; settings.onDone = loadOnDone;
newcommand = that.newCommand ( newcommand = that.newCommand (
{path:new_file_name, 'loadDocument',
option:settings}); {path:new_file_name,option:settings});
newstorage = that.newStorage ( priv.secondstorage_spec ); that.addJob (
that.addJob ( newstorage, newcommand ); that.newStorage ( priv.secondstorage_spec ), newcommand );
}, },
loadOnDone = function (result) { loadOnDone = function (result) {
result.name = command.getPath(); result.name = command.getPath();
if (command.getOption('metadata_only')) { if (command.getOption('metadata_only')) {
that.done(result); that.done(result);
} else { } else {
priv.decrypt (result.content,function(res){ priv.decrypt (result.content, function(res){
if (typeof res === 'object') { if (typeof res === 'object') {
that.fail({status:0,statusText:'Decrypt Fail', that.fail({status:0,statusText:'Decrypt Fail',
message:'Unable to decrypt'}); message:'Unable to decrypt'});
...@@ -1009,11 +1011,11 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -1009,11 +1011,11 @@ var newCryptedStorage = function ( spec, my ) {
}); });
} }
}, },
loadOnFail = function (result) { loadOnFail = function (error) {
// NOTE : we can re create an error object instead of // NOTE : we can re create an error object instead of
// keep the old ex:status=404,message="document 1y59gyl8g // keep the old ex:status=404,message="document 1y59gyl8g
// not found in localStorage"... // not found in localStorage"...
that.fail(result); that.fail(error);
}; };
_1(); _1();
}; // end loadDocument }; // end loadDocument
...@@ -1025,16 +1027,16 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -1025,16 +1027,16 @@ var newCryptedStorage = function ( spec, my ) {
that.getDocumentList = function (command) { that.getDocumentList = function (command) {
var new_job, i, l, cpt = 0, array, ok = true, var new_job, i, l, cpt = 0, array, ok = true,
_1 = function () { _1 = function () {
var newcommand = command.clone(), var newcommand = command.clone();
newstorage = that.newStorage ( priv.secondstorage_spec );
newcommand.onResponseDo (getListOnResponse); newcommand.onResponseDo (getListOnResponse);
newcommand.onDoneDo (function(){}); newcommand.onDoneDo (function(){});
newcommand.onFailDo (function(){}); newcommand.onFailDo (function(){});
that.addJob ( new_job ); that.addJob (
that.newStorage ( priv.secondstorage_spec ), newcommand );
}, },
getListOnResponse = function (result) { getListOnResponse = function (result) {
if (result.status.isDone()) { if (result.status.isDone()) {
array = result.return_value; array = result.value;
for (i = 0, l = array.length; i < l; i+= 1) { for (i = 0, l = array.length; i < l; i+= 1) {
// cpt--; // cpt--;
priv.decrypt (array[i].name, priv.decrypt (array[i].name,
...@@ -1051,6 +1053,7 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -1051,6 +1053,7 @@ var newCryptedStorage = function ( spec, my ) {
cpt++; cpt++;
if (typeof res === 'object') { if (typeof res === 'object') {
if (ok) { if (ok) {
ok = false;
that.fail({status:0,statusText:'Decrypt Fail', that.fail({status:0,statusText:'Decrypt Fail',
message:'Unable to decrypt.'}); message:'Unable to decrypt.'});
} }
...@@ -1070,23 +1073,27 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -1070,23 +1073,27 @@ var newCryptedStorage = function ( spec, my ) {
* Removes a document. * Removes a document.
* @method removeDocument * @method removeDocument
*/ */
that.removeDocument = function () { that.removeDocument = function (command) {
var new_job, new_file_name, var new_file_name,
_1 = function () { _1 = function () {
priv.encrypt(that.getFileName(),function(res) { priv.encrypt(command.getPath(),function(res) {
new_file_name = res; new_file_name = res;
_2(); _2();
}); });
}, },
_2 = function () { _2 = function () {
new_job = that.cloneJob(); var cloned_option = command.cloneOption();
new_job.name = new_file_name; cloned_option.onResponse = removeOnResponse;
new_job.storage = that.getSecondStorage(); cloned_option.onFail = function () {};
new_job.onResponse = removeOnResponse; cloned_option.onDone = function () {};
that.addJob(new_job); that.addJob(that.newStorage(priv.secondstorage_spec),
that.newCommand(
'removeDocument',
{path:new_file_name,
option:cloned_option}));
}, },
removeOnResponse = function (result) { removeOnResponse = function (result) {
if (result.status === 'done') { if (result.status.isDone()) {
that.done(); that.done();
} else { } else {
that.fail(result.error); that.fail(result.error);
...@@ -1108,20 +1115,22 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -1108,20 +1115,22 @@ var newConflictManagerStorage = function ( spec, my ) {
priv.secondstorage_spec = spec.storage || {type:'base'}; priv.secondstorage_spec = spec.storage || {type:'base'};
priv.secondstorage_string = JSON.stringify (priv.secondstorage_spec); priv.secondstorage_string = JSON.stringify (priv.secondstorage_spec);
var local_namespace = 'jio/conflictmanager/'+priv.secondstorage_string+'/'; var local_namespace = 'jio/conflictmanager/'+priv.username+'/'+
priv.secondstorage_string+'/';
var super_serialized = that.serialized; var super_serialized = that.serialized;
that.serialized = function () { that.serialized = function () {
var o = super_serialized(); var o = super_serialized();
o.username = priv.username;
o.storage = priv.secondstorage_spec; o.storage = priv.secondstorage_spec;
return o; return o;
}; };
that.validateState = function () { that.validateState = function () {
if (!priv.username || storage_exists) { if (priv.username && storage_exists) {
return 'Need at least two parameter: "owner" and "storage".'; return '';
} }
return ''; return 'Need at least two parameter: "username" and "storage".';
}; };
priv.removeValuesFromArrayWhere = function (array,fun) { priv.removeValuesFromArrayWhere = function (array,fun) {
...@@ -1134,14 +1143,9 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -1134,14 +1143,9 @@ var newConflictManagerStorage = function ( spec, my ) {
return newarray; return newarray;
}; };
var super_fail = that.fail;
that.fail = function (command,error) {
command.setMaxRetry(1);
super_fail(error);
};
priv.loadMetadataFromDistant = function (command,path,onDone,onFail) { priv.loadMetadataFromDistant = function (command,path,onDone,onFail) {
var cloned_option = command.cloneOption (); var cloned_option = command.cloneOption ();
cloned_option.metadata_only = false;
cloned_option.onResponse = function () {}; cloned_option.onResponse = function () {};
cloned_option.onFail = onFail; cloned_option.onFail = onFail;
cloned_option.onDone = onDone; cloned_option.onDone = onDone;
...@@ -1166,252 +1170,268 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -1166,252 +1170,268 @@ var newConflictManagerStorage = function ( spec, my ) {
newcommand ); newcommand );
}; };
priv.newAsyncModule = function () {
var async = {};
async.call = function (obj,function_name,arglist) {
obj._wait = obj._wait || {};
if (obj._wait[function_name]) {
obj._wait[function_name]--;
return function () {};
}
// ok if undef or 0
arglist = arglist || [];
return obj[function_name].apply(obj[function_name],arglist);
};
async.neverCall = function (obj,function_name) {
obj._wait = obj._wait || {};
obj._wait[function_name] = -1;
};
async.wait = function (obj,function_name,times) {
obj._wait = obj._wait || {};
obj._wait[function_name] = times;
};
async.end = function () {
async.call = function(){};
};
return async;
};
/** /**
* Save a document and can manage conflicts. * Save a document and can manage conflicts.
* @method saveDocument * @method saveDocument
*/ */
that.saveDocument = function (command) { that.saveDocument = function (command) {
var metadata_file_name = command.getPath() + '.metadata', var o = {}, am = priv.newAsyncModule(),
now = new Date(), metadata_file_name = command.getPath() + '.metadata',
local_metadata_file_name = local_namespace + metadata_file_name, local_metadata_file_name = local_namespace + metadata_file_name,
local_file_metadata = {}, // local file.metadata local_file_metadata = {}, // local file.metadata
command_file_metadata = {}, // distant file.metadata command_file_metadata = {}, // distant file.metadata
run_index = 0, previous_revision = 0, previous_revision = 0,
end = false, is_a_new_file = false, is_a_new_file = false,
local_file_hash = hex_sha256 (command.getContent()), local_file_hash = hex_sha256 (command.getContent()),
run = function (index) { now = new Date();
switch (index) {
case 0: o.updateLocalMetadata = function () {
run_index = 3; var new_owner_object = {revision:0,hash:'',
run (2); last_modified:0,
run (1); creation_date:now.getTime()};
break; local_file_metadata =
case 1: // update local metadata LocalOrCookieStorage.getItem (local_metadata_file_name);
var new_owner_object = {revision:0,hash:'', if ( local_file_metadata ) {
last_modified:0, // if metadata already exists
creation_date:now.getTime()}; if ( !local_file_metadata.owner[priv.username] ) {
local_file_metadata =
LocalOrCookieStorage.getItem (local_metadata_file_name);
if ( local_file_metadata ) {
// if metadata already exists
if ( !local_file_metadata.owner[priv.username] ) {
local_file_metadata.owner[priv.username] =
new_owner_object;
}
} else {
local_file_metadata = {
winner: {},
owner: {},
conflict_list: []
};
local_file_metadata.winner = {
revision:0,owner:priv.username,hash:''};
local_file_metadata.owner[priv.username] = local_file_metadata.owner[priv.username] =
new_owner_object; new_owner_object;
} }
run_index ++; run (run_index); } else {
break; local_file_metadata = {
case 2: // load metadata from distant winner: {},
priv.loadMetadataFromDistant ( owner: {},
command,metadata_file_name, conflict_list: []
function (result) { };
command_file_metadata = JSON.parse (result.content); local_file_metadata.winner = {
run_index ++; run (run_index); revision:0,owner:priv.username,hash:''};
},function (error) { local_file_metadata.owner[priv.username] =
if (error.status === 404) { new_owner_object;
command_file_metadata = local_file_metadata; }
is_a_new_file = true; am.call(o,'checkForConflicts');
run_index ++; run (run_index); };
} else { o.loadMetadataFromDistant = function () {
run_index = (-10); priv.loadMetadataFromDistant (
end = true; command,metadata_file_name,
that.fail(command,error); function (result) {
} command_file_metadata = JSON.parse (result.content);
}); am.call(o,'checkForConflicts');
break; },function (error) {
case 5: // check conflicts if (error.status === 404) {
var updateMetadataCommon = function () { command_file_metadata = local_file_metadata;
var original_creation_date; is_a_new_file = true;
am.call(o,'checkForConflicts');
if (is_a_new_file || !command_file_metadata.owner[
command_file_metadata.winner.owner]) {
original_creation_date = now.getTime();
} else { } else {
original_creation_date = command_file_metadata.owner[ am.call(o,'fail',[error]);
command_file_metadata.winner.owner].
creation_date || now.getTime();
} }
});
};
o.checkForConflicts = function () {
var updateMetadataCommon = function () {
var original_creation_date;
if (command_file_metadata.owner[priv.username]) { if (is_a_new_file || !command_file_metadata.owner[
previous_revision = command_file_metadata.owner[ command_file_metadata.winner.owner]) {
priv.username].revision; original_creation_date = now.getTime();
} else { } else {
command_file_metadata.owner[priv.username] = {}; original_creation_date = command_file_metadata.owner[
} command_file_metadata.winner.owner].
command_file_metadata.owner[priv.username]. creation_date || now.getTime();
last_modified = now.getTime();
command_file_metadata.owner[priv.username].
creation_date = original_creation_date;
command_file_metadata.owner[priv.username].hash =
local_file_hash;
};
var updateCommandMetadataNotOnConflict = function () {
updateMetadataCommon ();
command_file_metadata.winner.owner = priv.username;
command_file_metadata.winner.revision ++;
command_file_metadata.winner.hash = local_file_hash;
command_file_metadata.owner[priv.username].revision =
command_file_metadata.winner.revision;
};
var updateCommandMetadataOnConflict = function () {
updateMetadataCommon ();
command_file_metadata.owner[priv.username].revision ++;
};
// if this is a new file
if (is_a_new_file) {
updateCommandMetadataNotOnConflict();
LocalOrCookieStorage.setItem (local_metadata_file_name,
command_file_metadata);
run_index = (98);
run (6); // save metadata
run (7); // save document revision
break;
} }
// if no conflict
if (local_file_metadata.winner.revision === if (command_file_metadata.owner[priv.username]) {
command_file_metadata.winner.revision && previous_revision = command_file_metadata.owner[
local_file_metadata.winner.hash === priv.username].revision;
command_file_metadata.winner.hash) {
// OK! Now, update distant metadata, store them and save
updateCommandMetadataNotOnConflict();
LocalOrCookieStorage.setItem (local_metadata_file_name,
command_file_metadata);
run_index = 98;
run (6); // save metadata
run (7); // save document revision
} else { } else {
// if conflict command_file_metadata.owner[priv.username] = {};
var conflict_object = {
label: 'revision',
path: command.getPath(),
conflict_owner: {
name: command_file_metadata.winner.owner,
revision: command_file_metadata.winner.revision,
hash: command_file_metadata.winner.hash}
},
// gen hash
conflict_hash = hex_sha256 (JSON.stringify (
conflict_object));
conflict_object.hash = conflict_hash;
// browse known conflict list
var i, known_conflict_list =
command.getOption('known_conflict_list') || [];
var compare_fun = function (v) {
return (v.hash === conflict_hash);
};
for (i = 0; i < known_conflict_list.length; i+= 1) {
// if known conflict
if (known_conflict_list[i].hash ===
conflict_hash) {
command_file_metadata.conflict_list =
priv.removeValuesFromArrayWhere(
command_file_metadata.conflict_list,
compare_fun);
updateCommandMetadataNotOnConflict();
run_index = 98;
run (6);
run (7);
return;
}
}
updateCommandMetadataOnConflict();
// if unknown conflict
command_file_metadata.conflict_list.push (conflict_object);
run_index = (-10);
end = true;
run (6); // save metadata
run (7); // save document revision
that.fail(command); // TODO
command.getOption('onConflict')(conflict_object);
} }
break; command_file_metadata.owner[priv.username].
case 6: // save metadata last_modified = now.getTime();
priv.saveMetadataToDistant ( command_file_metadata.owner[priv.username].
command,metadata_file_name,command_file_metadata, creation_date = original_creation_date;
function () { command_file_metadata.owner[priv.username].hash =
run_index ++; run (run_index); local_file_hash;
},function (error) { };
run_index = (-10); var updateCommandMetadataNotOnConflict = function () {
end = true; updateMetadataCommon ();
that.fail(command,error); command_file_metadata.winner.owner = priv.username;
}); command_file_metadata.winner.revision ++;
break; command_file_metadata.winner.hash = local_file_hash;
case 7: // save document revision command_file_metadata.owner[priv.username].revision =
(function () { command_file_metadata.winner.revision;
var cloned_option = command.cloneOption (); };
cloned_option.onResponse = function () {}; var updateCommandMetadataOnConflict = function () {
cloned_option.onFail = function (error) { updateMetadataCommon ();
run_index = (-10); command_file_metadata.owner[priv.username].revision ++;
end = true; };
that.fail(command,error); // if this is a new file
}; if (is_a_new_file) {
cloned_option.onDone = function () { updateCommandMetadataNotOnConflict();
run (8); LocalOrCookieStorage.setItem (local_metadata_file_name,
}; command_file_metadata);
var newcommand = that.newCommand( am.wait(o,'done',1);
'saveDocument', am.call(o,'saveMetadata');
{path:command.getPath() + '.' + am.call(o,'saveRevision');
command_file_metadata.owner[priv.username].revision + return;
'.' + priv.username, }
content:command.getContent(), // if no conflict
option:cloned_option}); if (local_file_metadata.winner.revision ===
newcommand.setMaxRetry (0); // inf command_file_metadata.winner.revision &&
that.addJob ( that.newStorage (priv.secondstorage_spec), local_file_metadata.winner.hash ===
newcommand ); command_file_metadata.winner.hash) {
}()); // OK! Now, update distant metadata, store them and save
break; updateCommandMetadataNotOnConflict();
case 8: LocalOrCookieStorage.setItem (local_metadata_file_name,
(function () { command_file_metadata);
if ( previous_revision !== 0 && ( am.wait(o,'done',1);
!command_file_metadata.owner[priv.username] || am.call(o,'saveMetadata');
previous_revision !== am.call(o,'saveRevision');
command_file_metadata.owner[ } else {
priv.username].revision ) ) { // if conflict
var cloned_option = command.cloneOption (); var conflict_object = {
cloned_option.onResponse = function () {}; label: 'revision',
cloned_option.onFail = function (error) { path: command.getPath(),
run_index = (-10); method: 'saveDocument',
end = true; owner: priv.username,
that.fail(command,error); conflict_owner: {
}; name: command_file_metadata.winner.owner,
cloned_option.onDone = function () { revision: command_file_metadata.winner.revision,
run_index ++; run (run_index); hash: command_file_metadata.winner.hash}
}; },
var newcommand = that.newCommand( // gen hash
'removeDocument', conflict_hash = hex_sha256 (JSON.stringify (
{path:command.getPath() + '.' + conflict_object));
previous_revision + '.' + priv.username, conflict_object.hash = conflict_hash;
option:cloned_option}); // browse known conflict list
newcommand.setMaxRetry (0); // inf var i, known_conflict_list =
that.addJob ( that.newStorage (priv.secondstorage_spec), command.getOption('known_conflict_list') || [];
newcommand ); var compare_fun = function (v) {
} else { return (v.hash === conflict_hash);
run_index ++; run (run_index); };
for (i = 0; i < known_conflict_list.length; i+= 1) {
// if known conflict
if (known_conflict_list[i].hash ===
conflict_hash) {
command_file_metadata.conflict_list =
priv.removeValuesFromArrayWhere(
command_file_metadata.conflict_list,
compare_fun);
updateCommandMetadataNotOnConflict();
am.wait(o,'done',1);
am.call(o,'saveMetadata');
am.call(o,'saveRevision');
return;
} }
}());
break;
case 100:
if (!end) {
end = true;
that.done();
return;
} }
break; updateCommandMetadataOnConflict();
default: // if unknown conflict
break; command_file_metadata.conflict_list.push (conflict_object);
am.neverCall(o,'done');
am.call(o,'saveMetadata');
am.call(o,'saveRevision');
am.call(o,'fail',[
{status:0,statusText:'Revision Conflict',
message:'Someone has already modified this document.'}]);
var onConflict = command.getOption('onConflict') ||
function (){};
onConflict(conflict_object);
} }
}; };
run (0); o.saveMetadata = function () {
priv.saveMetadataToDistant (
command,metadata_file_name,command_file_metadata,
function () {
am.call(o,'done');
},function (error) {
am.call(o,'fail',[error]);
});
};
o.saveRevision = function () {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
am.call(o,'fail',[error]);
};
cloned_option.onDone = function () {
am.call(o,'deletePreviousRevision');
};
var newcommand = that.newCommand(
'saveDocument',
{path:command.getPath() + '.' +
command_file_metadata.owner[priv.username].revision +
'.' + priv.username,
content:command.getContent(),
option:cloned_option});
newcommand.setMaxRetry (0); // inf
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
};
o.deletePreviousRevision = function () {
if ( previous_revision !== 0 && (
!command_file_metadata.owner[priv.username] ||
previous_revision !==
command_file_metadata.owner[
priv.username].revision ) ) {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
am.call(o,'fail',[error]);
};
cloned_option.onDone = function () {
am.call(o,'done');
};
var newcommand = that.newCommand(
'removeDocument',
{path:command.getPath() + '.' +
previous_revision + '.' + priv.username,
option:cloned_option});
newcommand.setMaxRetry (0); // inf
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
} else {
am.call(o,'done');
}
};
o.fail = function (error) {
am.neverCall(o,'fail');
am.neverCall(o,'done');
command.setMaxRetry(1);
that.fail(error);
};
o.done = function () {
am.neverCall(o,'done');
am.neverCall(o,'fail');
that.done();
};
am.wait(o,'checkForConflicts',1);
am.call(o,'loadMetadataFromDistant');
am.call(o,'updateLocalMetadata');
}; };
/** /**
...@@ -1420,104 +1440,97 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -1420,104 +1440,97 @@ var newConflictManagerStorage = function ( spec, my ) {
* @method loadDocument * @method loadDocument
*/ */
that.loadDocument = function (command) { that.loadDocument = function (command) {
var metadata_file_name = command.getPath() + '.metadata', var o = {}, am = priv.newAsyncModule(),
metadata_file_name = command.getPath() + '.metadata',
local_metadata_file_name = local_namespace + metadata_file_name, local_metadata_file_name = local_namespace + metadata_file_name,
command_file_metadata = {}, // distant file.metadata command_file_metadata = {}, // distant file.metadata
run_index = 0, owner = '', loaded_file;
end = false, owner = '', loaded_file,
run = function (index) { o.loadMetadataFromDistant = function () {
switch (index) { priv.loadMetadataFromDistant (
case 0: // load metadata file from distant command,metadata_file_name,
priv.loadMetadataFromDistant ( function (result) {
command,metadata_file_name, command_file_metadata = JSON.parse (result.content);
function (result) { owner = command.getOption('owner');
command_file_metadata = JSON.parse (result.content); am.wait(o,'done',1);
owner = command.getOption('owner'); // if owner
run_index = 98; if (owner) {
// if owner am.call(o,'loadOwner');
if (owner) { } else {
run (3); // if no owner
} else { am.call(o,'loadWinner');
// if no owner
run (2);
}
run (1);
},function (error) {
run_index = (-10);
end = true;
that.fail(command,error);
});
break;
case 1: // update local metadata
LocalOrCookieStorage.setItem (local_metadata_file_name,
command_file_metadata);
run_index ++; run (run_index);
break;
case 2: // load winner
(function () {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
run_index = (-10);
end = true;
that.fail(command,error);
};
cloned_option.onDone = function (result) {
loaded_file = result;
loaded_file.name = command.getPath();
run_index ++; run (run_index);
};
var newcommand = that.newCommand(
'loadDocument',
{path:command.getPath() + '.' +
command_file_metadata.winner.revision +
'.' + command_file_metadata.winner.owner,
option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
}());
break;
case 3: // load owner
(function () {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
run_index = (-10);
end = true;
that.fail(command,error);
};
cloned_option.onDone = function (result) {
loaded_file = result;
loaded_file.name = command.getPath();
run_index ++; run (run_index);
};
if (!command_file_metadata.owner[owner]) {
cloned_option.onFail ({status:404,
statusText:'Not Found',
message:'Document not found.'});
return;
} }
var newcommand = that.newCommand( am.call(o,'updateLocalMetadata');
'loadDocument', },function (error) {
{path:command.getPath() + '.' + am.end();
command_file_metadata.owner[owner].revision + am.call(o,'fail',[error]);
'.' + owner, });
option:cloned_option}); };
that.addJob ( that.newStorage (priv.secondstorage_spec), o.updateLocalMetadata = function () {
newcommand ); LocalOrCookieStorage.setItem (local_metadata_file_name,
}()); command_file_metadata);
break; am.call(o,'done');
case 100: };
if (!end) { o.loadWinner = function () {
end = true; var cloned_option = command.cloneOption ();
that.done(loaded_file); cloned_option.onResponse = function () {};
return; cloned_option.onFail = function (error) {
} am.call(o,'fail',[error]);
break; am.end();
default: break; };
cloned_option.onDone = function (result) {
loaded_file = result;
loaded_file.name = command.getPath();
am.call(o,'done');
};
var newcommand = that.newCommand(
'loadDocument',
{path:command.getPath() + '.' +
command_file_metadata.winner.revision +
'.' + command_file_metadata.winner.owner,
option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
};
o.loadOwner = function () {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
am.end();
am.call(o,'fail',[error]);
};
cloned_option.onDone = function (result) {
loaded_file = result;
loaded_file.name = command.getPath();
am.call(o,'done');
};
if (!command_file_metadata.owner[owner]) {
cloned_option.onFail ({status:404,
statusText:'Not Found',
message:'Document not found.'});
return;
} }
var newcommand = that.newCommand(
'loadDocument',
{path:command.getPath() + '.' +
command_file_metadata.owner[owner].revision +
'.' + owner,
option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
}; };
run (0); o.fail = function (error) {
am.neverCall(o,'fail');
am.neverCall(o,'done');
command.setMaxRetry(1);
that.fail(error);
};
o.done = function () {
am.neverCall(o,'done');
am.neverCall(o,'fail');
that.done(loaded_file);
};
am.call(o,'loadMetadataFromDistant');
}; };
/** /**
...@@ -1526,14 +1539,16 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -1526,14 +1539,16 @@ var newConflictManagerStorage = function ( spec, my ) {
* @method getDocumentList * @method getDocumentList
*/ */
that.getDocumentList = function (command) { that.getDocumentList = function (command) {
var command_file_metadata_list = [], // distant files metadata var o = {}, am = priv.newAsyncModule(),
command_file_metadata_list = [], // distant files metadata
result_list = [], result_list = [],
end = false, nb_loaded_file = 0, nb_loaded_file = 0;
_1 = function () { o.retreiveList = function () {
var cloned_option = command.cloneOption (); var cloned_option = command.cloneOption ();
cloned_option.metadata_only = false;
cloned_option.onResponse = function () {}; cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) { cloned_option.onFail = function (error) {
that.fail(command,error); am.call(o,'fail',[error]);
}; };
cloned_option.onDone = function (result) { cloned_option.onDone = function (result) {
var i; var i;
...@@ -1558,18 +1573,20 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -1558,18 +1573,20 @@ var newConflictManagerStorage = function ( spec, my ) {
} }
} }
if (command.getOption('metadata_only')) { if (command.getOption('metadata_only')) {
that.done(command_file_metadata_list); am.call(o,'done',[command_file_metadata_list]);
} else { } else {
if (result.length === 0) { if (result.length === 0) {
return that.done([]); return that.done([]);
} }
am.wait(o,'done',command_file_metadata_list.length-1);
for (i = 0; i < command_file_metadata_list.length; i+= 1) { for (i = 0; i < command_file_metadata_list.length; i+= 1) {
LocalOrCookieStorage.setItem ( LocalOrCookieStorage.setItem (
command_file_metadata_list[i].name + '.metadata', command_file_metadata_list[i].name + '.metadata',
result_list[i]); result_list[i]);
loadFile(command_file_metadata_list[i], am.call(o,'loadFile',[
result_list[i].winner.revision, command_file_metadata_list[i],
result_list[i].winner.owner); result_list[i].winner.revision,
result_list[i].winner.owner]);
} }
that.end(); that.end();
} }
...@@ -1580,24 +1597,17 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -1580,24 +1597,17 @@ var newConflictManagerStorage = function ( spec, my ) {
option:cloned_option}); option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec), that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand ); newcommand );
}, loadFile = function (doc,revision,owner) { };
o.loadFile = function (doc,revision,owner) {
var cloned_option = command.cloneOption (); var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {}; cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) { cloned_option.onFail = function (error) {
if (!end) { am.call(o,'fail',[error]);
end = true; am.end();
that.fail(command,error);
}
}; };
cloned_option.onDone = function (result) { cloned_option.onDone = function (result) {
if (!end) { doc.content = result.content;
doc.content = result.content; am.call(o,'done',[command_file_metadata_list]);
nb_loaded_file ++;
if (command_file_metadata_list.length === nb_loaded_file) {
end = true;
that.done(command_file_metadata_list);
}
}
}; };
var newcommand = that.newCommand( var newcommand = that.newCommand(
'loadDocument', 'loadDocument',
...@@ -1606,7 +1616,18 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -1606,7 +1616,18 @@ var newConflictManagerStorage = function ( spec, my ) {
that.addJob ( that.newStorage (priv.secondstorage_spec), that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand ); newcommand );
}; };
_1(); o.fail = function (error) {
am.neverCall(o,'fail');
am.neverCall(o,'done');
command.setMaxRetry(1);
that.fail(error);
};
o.done = function (value) {
am.neverCall(o,'done');
am.neverCall(o,'fail');
that.done(value);
};
am.call(o,'retreiveList');
}; };
/** /**
...@@ -1614,194 +1635,192 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -1614,194 +1635,192 @@ var newConflictManagerStorage = function ( spec, my ) {
* @method removeDocument * @method removeDocument
*/ */
that.removeDocument = function (command) { that.removeDocument = function (command) {
var metadata_file_name = command.getPath() + '.metadata', var o = {}, am = priv.newAsyncModule(),
metadata_file_name = command.getPath() + '.metadata',
local_metadata_file_name = local_namespace + metadata_file_name, local_metadata_file_name = local_namespace + metadata_file_name,
local_file_metadata = {}, local_file_metadata = {},
command_file_metadata = {}, // distant file.metadata command_file_metadata = {}, // distant file.metadata
run_index = 0, previous_revision = 0, previous_revision = 0,
end = false, is_a_new_file = false, is_a_new_file = false;
run = function (index) {
switch (index) { o.updateLocalMetadata = function () {
case 0: var new_owner_object = {revision:0,hash:'',
run_index = 3; last_modified:0,
run (2); creation_date:0};
run (1); local_file_metadata =
break; LocalOrCookieStorage.getItem (local_metadata_file_name);
case 1: // update local metadata if ( local_file_metadata ) {
var new_owner_object = {revision:0,hash:'', // if metadata already exists
last_modified:0, if ( !local_file_metadata.owner[priv.username] ) {
creation_date:0};
local_file_metadata =
LocalOrCookieStorage.getItem (local_metadata_file_name);
if ( local_file_metadata ) {
// if metadata already exists
if ( !local_file_metadata.owner[priv.username] ) {
local_file_metadata.owner[priv.username] =
new_owner_object;
}
} else {
local_file_metadata = {
winner: {},
owner: {},
conflict_list: []
};
local_file_metadata.winner = {
revision:0,owner:priv.username,hash:''};
local_file_metadata.owner[priv.username] = local_file_metadata.owner[priv.username] =
new_owner_object; new_owner_object;
} }
run_index ++; run (run_index); } else {
break; local_file_metadata = {
case 2: // load metadata from distant winner: {},
priv.loadMetadataFromDistant ( owner: {},
command,metadata_file_name, conflict_list: []
function (result) {
command_file_metadata = JSON.parse (result.content);
run_index++; run (run_index);
},function (error) {
if (error.status === 404) {
command_file_metadata = local_file_metadata;
is_a_new_file = true;
run_index++; run (run_index);
return;
}
run_index = (-10);
end = true;
that.fail(command,error);
});
break;
case 5:
var updateMetadataCommon = function () {
if (command_file_metadata.owner[priv.username]) {
previous_revision = command_file_metadata.owner[
priv.username].revision;
delete command_file_metadata.owner[priv.username];
}
};
var updateCommandMetadataNotOnConflict = function () {
updateMetadataCommon();
command_file_metadata.winner.owner = priv.username;
command_file_metadata.winner.revision = 0;
command_file_metadata.winner.hash = '';
};
var updateCommandMetadataOnConflict = function () {
updateMetadataCommon ();
}; };
// if this is a new file local_file_metadata.winner = {
if (is_a_new_file) { revision:0,owner:priv.username,hash:''};
LocalOrCookieStorage.deleteItem (local_metadata_file_name); local_file_metadata.owner[priv.username] =
return that.done(); new_owner_object;
} }
// if no conflict am.call(o,'checkForConflicts');
if (local_file_metadata.winner.revision === };
command_file_metadata.winner.revision && o.loadMetadataFromDistant = function () {
local_file_metadata.winner.hash === priv.loadMetadataFromDistant (
command_file_metadata.winner.hash) { command,metadata_file_name,
// OK! Now, update distant metadata, store them and remove function (result) {
updateCommandMetadataNotOnConflict(); command_file_metadata = JSON.parse (result.content);
LocalOrCookieStorage.setItem (local_metadata_file_name, am.call(o,'checkForConflicts');
command_file_metadata); },function (error) {
run_index = 98; if (error.status === 404) {
run (6); // save metadata command_file_metadata = local_file_metadata;
run (7); // remove document revision is_a_new_file = true;
} else { am.call(o,'checkForConflicts');
// if conflict return;
var conflict_object = {
label: 'revision',
path: command.getPath(),
conflict_owner: {
name: command_file_metadata.winner.owner,
revision: command_file_metadata.winner.revision,
hash: command_file_metadata.winner.hash}
},
// gen hash
conflict_hash = hex_sha256 (JSON.stringify (
conflict_object));
conflict_object.hash = conflict_hash;
// browse known conflict list
var i, known_conflict_list =
command.getOption('known_conflict_list') || [];
var compare_fun = function (v) {
return (v.hash === conflict_hash);
};
for (i = 0; i < known_conflict_list.length; i+= 1) {
// if known conflict
if (known_conflict_list[i].hash ===
conflict_hash) {
command_file_metadata.conflict_list =
priv.removeValuesFromArrayWhere(
command_file_metadata.conflict_list,
compare_fun);
updateCommandMetadataNotOnConflict();
run_index = 98;
run (6);
run (7);
return;
}
} }
updateCommandMetadataOnConflict(); am.call(o,'fail',[error]);
// if unknown conflict am.end();
command_file_metadata.conflict_list.push (conflict_object); });
run_index = (-10); };
end = true; o.checkForConflicts = function () {
run (6); // save metadata var updateMetadataCommon = function () {
run (7); // remove document revision if (command_file_metadata.owner[priv.username]) {
that.fail(command); // TODO previous_revision = command_file_metadata.owner[
command.getOption('onConflict')(conflict_object); priv.username].revision;
delete command_file_metadata.owner[priv.username];
} }
break; };
case 6: var updateCommandMetadataNotOnConflict = function () {
priv.saveMetadataToDistant ( updateMetadataCommon();
command,metadata_file_name,command_file_metadata, command_file_metadata.winner.owner = priv.username;
function () { command_file_metadata.winner.revision = 0;
run_index ++; run (run_index); command_file_metadata.winner.hash = '';
},function (error) { };
run_index = (-10); var updateCommandMetadataOnConflict = function () {
end = true; updateMetadataCommon ();
that.fail(command,error); };
}); // if this is a new file
break; if (is_a_new_file) {
case 7: LocalOrCookieStorage.deleteItem (local_metadata_file_name);
(function () { return am.call(o,'done');
if ( previous_revision !== 0 && ( }
!command_file_metadata.owner[priv.username] || // if no conflict
previous_revision !== if (local_file_metadata.winner.revision ===
command_file_metadata.owner[ command_file_metadata.winner.revision &&
priv.username].revision ) ) { local_file_metadata.winner.hash ===
var cloned_option = command.cloneOption (); command_file_metadata.winner.hash) {
cloned_option.onResponse = function () {}; // OK! Now, update distant metadata, store them and remove
cloned_option.onFail = function (error) { updateCommandMetadataNotOnConflict();
run_index = (-10); LocalOrCookieStorage.setItem (local_metadata_file_name,
end = true; command_file_metadata);
that.fail(command,error); am.wait(o,'done',1);
}; am.call(o,'saveMetadata');
cloned_option.onDone = function () { am.call(o,'removeRevision');
run_index ++; run (run_index); } else {
}; // if conflict
var newcommand = that.newCommand( var conflict_object = {
'removeDocument', label: 'revision',
{path:command.getPath() + '.' + path: command.getPath(),
previous_revision + '.' + priv.username, method: 'removeDocument',
option:cloned_option}); owner: priv.username,
newcommand.setMaxRetry (0); // inf conflict_owner: {
that.addJob ( that.newStorage (priv.secondstorage_spec), name: command_file_metadata.winner.owner,
newcommand ); revision: command_file_metadata.winner.revision,
} else { hash: command_file_metadata.winner.hash}
run_index ++; run (run_index); },
// gen hash
conflict_hash = hex_sha256 (JSON.stringify (
conflict_object));
conflict_object.hash = conflict_hash;
// browse known conflict list
var i, known_conflict_list =
command.getOption('known_conflict_list') || [];
var compare_fun = function (v) {
return (v.hash === conflict_hash);
};
for (i = 0; i < known_conflict_list.length; i+= 1) {
// if known conflict
if (known_conflict_list[i].hash ===
conflict_hash) {
command_file_metadata.conflict_list =
priv.removeValuesFromArrayWhere(
command_file_metadata.conflict_list,
compare_fun);
updateCommandMetadataNotOnConflict();
am.wait(o,'done',1);
am.call(o,'saveMetadata');
am.call(o,'removeRevision');
return;
} }
}());
break;
case 100:
if (!end) {
end = true;
that.done();
return;
} }
break; updateCommandMetadataOnConflict();
default: break; // if unknown conflict
command_file_metadata.conflict_list.push (conflict_object);
am.neverCall(o,'done');
am.call(o,'saveMetadata');
// am.call(o,'removeRevision');
am.call(o,'fail',[
{status:0,statusText:'Revision Conflict',
message:'Someone has already modified this document.'}]);
var onConflict = command.getOption('onConflict') ||
function (){};
onConflict(conflict_object);
} }
}; };
run (0); o.saveMetadata = function () {
priv.saveMetadataToDistant (
command,metadata_file_name,command_file_metadata,
function () {
am.call(o,'done');
},function (error) {
am.call(o,'fail',[error]);
am.end();
});
};
o.removeRevision = function () {
if ( previous_revision !== 0 && (
!command_file_metadata.owner[priv.username] ||
previous_revision !==
command_file_metadata.owner[
priv.username].revision ) ) {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
am.call(o,'fail',[error]);
am.end();
};
cloned_option.onDone = function () {
am.call(o,'done');
};
var newcommand = that.newCommand(
'removeDocument',
{path:command.getPath() + '.' +
previous_revision + '.' + priv.username,
option:cloned_option});
newcommand.setMaxRetry (0); // inf
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
} else {
am.call(o,'done');
}
};
o.fail = function (error) {
am.neverCall(o,'fail');
am.neverCall(o,'done');
command.setMaxRetry(1);
that.fail(error);
};
o.done = function () {
am.neverCall(o,'done');
am.neverCall(o,'fail');
that.done();
};
am.wait(o,'checkForConflicts',1);
am.call(o,'loadMetadataFromDistant');
am.call(o,'updateLocalMetadata');
}; };
return that; return that;
......
/*! JIO Storage - v0.1.0 - 2012-06-26 /*! JIO Storage - v0.1.0 - 2012-06-27
* Copyright (c) 2012 Nexedi; Licensed */ * Copyright (c) 2012 Nexedi; Licensed */
(function(a,b,c,d,e,f){var g=function(b,c){var d=f.storage(b,c,"base"),e={};e.username=b.username||"",e.applicationname=b.applicationname||"untitled";var g="jio/local_user_array",h="jio/local_file_name_array/"+e.username+"/"+e.applicationname,i=d.serialized;return d.serialized=function(){var a=i();return a.applicationname=e.applicationname,a.username=e.username,a},d.validateState=function(){return e.username?"":'Need at least one parameter: "username".'},e.getUserArray=function(){return a.getItem(g)||[]},e.addUser=function(b){var c=e.getUserArray();c.push(b),a.setItem(g,c)},e.userExists=function(a){var b=e.getUserArray(),c,d;for(c=0,d=b.length;c<d;c+=1)if(b[c]===a)return!0;return!1},e.getFileNameArray=function(){return a.getItem(h)||[]},e.addFileName=function(b){var c=e.getFileNameArray();c.push(b),a.setItem(h,c)},e.removeFileName=function(b){var c,d,f=e.getFileNameArray(),g=[];for(c=0,d=f.length;c<d;c+=1)f[c]!==b&&g.push(f[c]);a.setItem(h,g)},d.saveDocument=function(b){setTimeout(function(){var c=null,f="jio/local/"+e.username+"/"+e.applicationname+"/"+b.getPath();c=a.getItem(f),c?(c.last_modified=Date.now(),c.content=b.getContent()):(c={name:b.getPath(),content:b.getContent(),creation_date:Date.now(),last_modified:Date.now()},e.userExists(e.username)||e.addUser(e.username),e.addFileName(b.getPath())),a.setItem(f,c),d.done()},100)},d.loadDocument=function(b){setTimeout(function(){var c=null;c=a.getItem("jio/local/"+e.username+"/"+e.applicationname+"/"+b.getPath()),c?(b.getOption("metadata_only")&&delete c.content,d.done(c)):d.fail(b,{status:404,statusText:"Not Found.",message:'Document "'+b.getPath()+'" not found in localStorage.'})},100)},d.getDocumentList=function(b){setTimeout(function(){var c=[],f=[],g,h,i="key",j="jio/local/"+e.username+"/"+e.applicationname,k={};f=e.getFileNameArray();for(g=0,h=f.length;g<h;g+=1)k=a.getItem(j+"/"+f[g]),k&&(b.getOption("metadata_only")?c.push({name:k.name,creation_date:k.creation_date,last_modified:k.last_modified}):c.push({name:k.name,content:k.content,creation_date:k.creation_date,last_modified:k.last_modified}));d.done(c)},100)},d.removeDocument=function(b){setTimeout(function(){var c="jio/local/"+e.username+"/"+e.applicationname+"/"+b.getPath();a.deleteItem(c),e.removeFileName(b.getPath()),d.done()},100)},d};f.addStorageType("local",g);var h=function(a,d){var e=f.storage(a,d,"base"),g={};g.username=a.username||"",g.applicationname=a.applicationname||"untitled",g.url=a.url||"",g.password=a.password||"";var h=e.serialized;return e.serialized=function(){var a=h();return a.username=g.username,a.applicationname=g.applicationname,a.url=g.url,a.password=g.password,a},e.validateState=function(){return g.username&&g.url?"":'Need at least 2 parameters: "username" and "url".'},e.saveDocument=function(a){b.ajax({url:g.url+"/dav/"+g.username+"/"+g.applicationname+"/"+a.getPath(),type:"PUT",data:a.getContent(),async:!0,dataType:"text",headers:{Authorization:"Basic "+c.encode(g.username+":"+g.password)},success:function(){e.done()},error:function(b){b.message='Cannot save "'+a.getPath()+'" into DAVStorage.',e.fail(b)}})},e.loadDocument=function(a){var d={},f=function(){b.ajax({url:g.url+"/dav/"+g.username+"/"+g.applicationname+"/"+a.getPath(),type:"GET",async:!0,dataType:"text",headers:{Authorization:"Basic "+c.encode(g.username+":"+g.password)},success:function(a){d.content=a,e.done(d)},error:function(b){b.status===404?b.message='Document "'+a.getPath()+'" not found in localStorage.':b.message='Cannot load "'+a.getPath()+'" from DAVStorage.',e.fail(b)}})};d.name=a.getPath(),b.ajax({url:g.url+"/dav/"+g.username+"/"+g.applicationname+"/"+a.getPath(),type:"PROPFIND",async:!0,dataType:"xml",headers:{Authorization:"Basic "+c.encode(g.username+":"+g.password)},success:function(c){b(c).find("lp1\\:getlastmodified, getlastmodified").each(function(){d.last_modified=b(this).text()}),b(c).find("lp1\\:creationdate, creationdate").each(function(){d.creation_date=b(this).text()}),a.getOption("metadata_only")?e.done(d):f()},error:function(b){b.message='Cannot load "'+a.getPath()+'" informations from DAVStorage.',e.fail(b)}})},e.getDocumentList=function(a){var d=[],f={},h=[];b.ajax({url:g.url+"/dav/"+g.username+"/"+g.applicationname+"/",async:!0,type:"PROPFIND",dataType:"xml",headers:{Authorization:"Basic "+c.encode(g.username+":"+g.password),Depth:"1"},success:function(a){b(a).find("D\\:response, response").each(function(a,c){if(a>0){f={},b(c).find("D\\:href, href").each(function(){h=b(this).text().split("/"),f.name=h[h.length-1]?h[h.length-1]:h[h.length-2]+"/"});if(f.name===".htaccess"||f.name===".htpasswd")return;b(c).find("lp1\\:getlastmodified, getlastmodified").each(function(){f.last_modified=b(this).text()}),b(c).find("lp1\\:creationdate, creationdate").each(function(){f.creation_date=b(this).text()}),d.push(f)}}),e.done(d)},error:function(a){a.message="Cannot get a document list from DAVStorage.",e.fail(a)}})},e.removeDocument=function(a){b.ajax({url:g.url+"/dav/"+g.username+"/"+g.applicationname+"/"+a.getPath(),type:"DELETE",async:!0,headers:{Authorization:"Basic "+c.encode(g.username+":"+g.password)},success:function(){e.done()},error:function(a){a.status===404?e.done():(a.message='Cannot remove "'+e.getFileName()+'" from DAVStorage.',e.fail(a))}})},e};f.addStorageType("dav",h);var i=function(a,b){var c=f.storage(a,b,"handler"),d={};d.return_value_array=[],d.storagelist=a.storagelist||[],d.nb_storage=d.storagelist.length;var e=c.serialized;return c.serialized=function(){var a=e();return a.storagelist=d.storagelist,a},c.validateState=function(){return d.storagelist.length===0?'Need at least one parameter: "storagelist" containing at least one storage.':""},d.isTheLast=function(){return d.return_value_array.length===d.nb_storage},d.doJob=function(a,b){var e=!1,f=[],g,h=function(a){d.return_value_array.push(a)},i=function(a){e||(f.push(a),d.isTheLast()&&c.fail({status:207,statusText:"Multi-Status",message:b,array:f}))},j=function(a){e||(e=!0,c.done(a))};for(g=0;g<d.nb_storage;g+=1){var k=a.clone(),l=c.newStorage(d.storagelist[g]);k.onResponseDo(h),k.onFailDo(i),k.onDoneDo(j),c.addJob(l,k)}a.setMaxRetry(1)},c.saveDocument=function(a){d.doJob(a,'All save "'+a.getPath()+'" requests have failed.'),c.end()},c.loadDocument=function(a){d.doJob(a,'All load "'+a.getPath()+'" requests have failed.'),c.end()},c.getDocumentList=function(a){d.doJob(a,"All get document list requests have failed."),c.end()},c.removeDocument=function(a){d.doJob(a,'All remove "'+a.getPath()+'" requests have failed.'),c.end()},c};f.addStorageType("replicate",i);var j=function(b,c){var d=f.storage(b,c,"handler"),e={},g=b.storage||!1;e.secondstorage_spec=b.storage||{type:"base"},e.secondstorage_string=JSON.stringify(e.secondstorage_spec);var h="jio/indexed_storage_array",i="jio/indexed_file_array/"+e.secondstorage_string,j=d.serialized;return d.serialized=function(){var a=j();return a.storage=e.secondstorage_spec,a},d.validateState=function(){return g?"":'Need at least one parameter: "storage" containing storage specifications.'},e.isStorageArrayIndexed=function(){return a.getItem(h)?!0:!1},e.getIndexedStorageArray=function(){return a.getItem(h)||[]},e.indexStorage=function(b){var c=e.getIndexedStorageArray();c.push(typeof b=="string"?b:JSON.stringify(b)),a.setItem(h,c)},e.isAnIndexedStorage=function(a){var b=typeof a=="string"?a:JSON.stringify(a),c,d,f=e.getIndexedStorageArray();for(c=0,d=f.length;c<d;c+=1)if(JSON.stringify(f[c])===b)return!0;return!1},e.fileArrayExists=function(){return a.getItem(i)?!0:!1},e.getFileArray=function(){return a.getItem(i)||[]},e.setFileArray=function(b){return a.setItem(i,b)},e.isFileIndexed=function(a){var b,c,d=e.getFileArray();for(b=0,c=d.length;b<c;b+=1)if(d[b].name===a)return!0;return!1},e.addFile=function(b){var c=e.getFileArray();c.push(b),a.setItem(i,c)},e.removeFile=function(b){var c,d,f=e.getFileArray(),g=[];for(c=0,d=f.length;c<d;c+=1)f[c].name!==b&&g.push(f[c]);a.setItem(i,g)},e.update=function(){var a=function(a){e.isAnIndexedStorage(e.secondstorage_string)||e.indexStorage(e.secondstorage_string),e.setFileArray(a)};d.addJob(d.newStorage(e.secondstorage_spec),d.newCommand("getDocumentList",{path:".",option:{onDone:a,max_retry:3}}))},d.saveDocument=function(a){var b=a.clone();b.onResponseDo(function(){}),b.onDoneDo(function(b){e.isFileIndexed(a.getPath())||e.addFile({name:a.getPath(),last_modified:0,creation_date:0}),e.update(),d.done()}),b.onFailDo(function(a){d.fail(a)}),d.addJob(d.newStorage(e.secondstorage_spec),b)},d.loadDocument=function(a){var b,c,f,g,h=function(a){d.done(a)},i=function(a){d.fail(a)},j=function(){var b=a.clone();b.onResponseDo(function(){}),b.onFailDo(i),b.onDoneDo(h),d.addJob(d.newStorage(e.secondstorage_spec),b)};e.update(),a.getOption("metadata_only")?setTimeout(function(){if(e.fileArrayExists()){b=e.getFileArray();for(c=0,f=b.length;c<f;c+=1)if(b[c].name===a.getPath())return d.done(b[c])}else j()},100):j()},d.getDocumentList=function(a){var b,c,f=!1;e.update(),a.getOption("metadata_only")?(b=setInterval(function(){f&&(d.fail({status:0,statusText:"Timeout",message:"The request has timed out."}),clearInterval(b)),e.fileArrayExists()&&(d.done(e.getFileArray()),clearInterval(b))},100),setTimeout(function(){f=!0},1e4)):(c=a.clone(),c.onDoneDo(function(a){d.done(a)}),c.onFailDo(function(a){d.fail(a)}),d.addJob(d.newStorage(e.secondstorage_spec),c))},d.removeDocument=function(a){var b=a.clone();b.onResponseDo(function(){}),b.onDoneDo(function(b){e.removeFile(a.getPath()),e.update(),d.done()}),b.onFailDo(function(a){d.fail(a)}),d.addJob(d.newStorage(e.secondstorage_spec),b)},d};f.addStorageType("indexed",j);var k=function(a,c){var e=f.storage(a,c,"handler"),g={};g.username=a.username||"",g.password=a.password||"",g.secondstorage_spec=a.storage||{type:"base"};var h=e.serialized;return e.serialized=function(){var a=h();return a.username=g.username,a.password=g.password,a},e.validateState=function(){return g.username&&JSON.stringify(g.secondstorage_spec)===JSON.stringify({type:"base"})?"":'Need at least two parameters: "username" and "storage".'},g.encrypt_param_object={iv:"kaprWwY/Ucr7pumXoTHbpA",v:1,iter:1e3,ks:256,ts:128,mode:"ccm",adata:"",cipher:"aes",salt:"K4bmZG9d704"},g.decrypt_param_object={iv:"kaprWwY/Ucr7pumXoTHbpA",ks:256,ts:128,salt:"K4bmZG9d704"},g.encrypt=function(a,b,c){var f=d.encrypt(e.getStorageUserName()+":"+e.getStoragePassword(),a,g.encrypt_param_object);b(JSON.parse(f).ct,c)},g.decrypt=function(a,c,f,h){var i,j=b.extend(!0,{},g.decrypt_param_object);j.ct=a||"",j=JSON.stringify(j);try{i=d.decrypt(e.getStorageUserName()+":"+e.getStoragePassword(),j)}catch(k){c({status:0,statusText:"Decrypt Fail",message:"Unable to decrypt."},f,h);return}c(i,f,h)},e.saveDocument=function(a){var b,c,d=function(){g.encrypt(a.getPath(),function(a){b=a,f()})},f=function(){g.encrypt(a.getContent(),function(a){c=a,h()})},h=function(){var a=e.cloneOption(),d,f;a.onResponse=function(){},a.onDone=function(){e.done()},a.onFail=function(a){e.fail(a)},d=e.newCommand({path:b,content:c,option:a}),f=e.newStorage(g.secondstorage_spec),e.addJob(f,d)};d()},e.loadDocument=function(a){var b,c,d=function(){g.encrypt(a.getPath(),function(a){b=a,f()})},f=function(){var c=a.cloneOption(),d,f;c.onResponse=function(){},c.onFail=i,c.onDone=h,d=e.newCommand({path:b,option:c}),f=e.newStorage(g.secondstorage_spec),e.addJob(f,d)},h=function(b){b.name=a.getPath(),a.getOption("metadata_only")?e.done(b):g.decrypt(b.content,function(a){typeof a=="object"?e.fail({status:0,statusText:"Decrypt Fail",message:"Unable to decrypt"}):(b.content=a,e.done(b))})},i=function(a){e.fail(a)};d()},e.getDocumentList=function(a){var b,c,d,f=0,h,i=!0,j=function(){var c=a.clone(),d=e.newStorage(g.secondstorage_spec);c.onResponseDo(k),c.onDoneDo(function(){}),c.onFailDo(function(){}),e.addJob(b)},k=function(a){if(a.status.isDone()){h=a.return_value;for(c=0,d=h.length;c<d;c+=1)g.decrypt(h[c].name,l,c,"name")}else e.fail(a.error)},l=function(a,b,c){var g;f++;if(typeof a=="object"){i&&e.fail({status:0,statusText:"Decrypt Fail",message:"Unable to decrypt."}),i=!1;return}h[b][c]=a,f===d&&i&&e.done(h)};j()},e.removeDocument=function(){var a,b,c=function(){g.encrypt(e.getFileName(),function(a){b=a,d()})},d=function(){a=e.cloneJob(),a.name=b,a.storage=e.getSecondStorage(),a.onResponse=f,e.addJob(a)},f=function(a){a.status==="done"?e.done():e.fail(a.error)};c()},e};f.addStorageType("crypt",k);var l=function(b,c){var d=f.storage(b,c,"handler"),g={};b=b||{},c=c||{},g.username=b.username||"";var h=b.storage?!0:!1;g.secondstorage_spec=b.storage||{type:"base"},g.secondstorage_string=JSON.stringify(g.secondstorage_spec);var i="jio/conflictmanager/"+g.secondstorage_string+"/",j=d.serialized;d.serialized=function(){var a=j();return a.storage=g.secondstorage_spec,a},d.validateState=function(){return!g.username||h?'Need at least two parameter: "owner" and "storage".':""},g.removeValuesFromArrayWhere=function(a,b){var c,d=[];for(c=0;c<a.length;c+=1)b(a[c])||d.push(a[c]);return d};var k=d.fail;return d.fail=function(a,b){a.setMaxRetry(1),k(b)},g.loadMetadataFromDistant=function(a,b,c,e){var f=a.cloneOption();f.onResponse=function(){},f.onFail=e,f.onDone=c;var h=d.newCommand("loadDocument",{path:b,option:f});d.addJob(d.newStorage(g.secondstorage_spec),h)},g.saveMetadataToDistant=function(a,b,c,e,f){var h=a.cloneOption();h.onResponse=function(){},h.onFail=f,h.onDone=e;var i=d.newCommand("saveDocument",{path:b,content:JSON.stringify(c),option:h});i.setMaxRetry(0),d.addJob(d.newStorage(g.secondstorage_spec),i)},d.saveDocument=function(b){var c=b.getPath()+".metadata",f=new Date,h=i+c,j={},k={},l=0,m=0,n=!1,o=!1,p=e(b.getContent()),q=function(i){switch(i){case 0:l=3,q(2),q(1);break;case 1:var r={revision:0,hash:"",last_modified:0,creation_date:f.getTime()};j=a.getItem(h),j?j.owner[g.username]||(j.owner[g.username]=r):(j={winner:{},owner:{},conflict_list:[]},j.winner={revision:0,owner:g.username,hash:""},j.owner[g.username]=r),l++,q(l);break;case 2:g.loadMetadataFromDistant(b,c,function(a){k=JSON.parse(a.content),l++,q(l)},function(a){a.status===404?(k=j,o=!0,l++,q(l)):(l=-10,n=!0,d.fail(b,a))});break;case 5:var s=function(){var a;o||!k.owner[k.winner.owner]?a=f.getTime():a=k.owner[k.winner.owner].creation_date||f.getTime(),k.owner[g.username]?m=k.owner[g.username].revision:k.owner[g.username]={},k.owner[g.username].last_modified=f.getTime(),k.owner[g.username].creation_date=a,k.owner[g.username].hash=p},t=function(){s(),k.winner.owner=g.username,k.winner.revision++,k.winner.hash=p,k.owner[g.username].revision=k.winner.revision},u=function(){s(),k.owner[g.username].revision++};if(o){t(),a.setItem(h,k),l=98,q(6),q(7);break}if(j.winner.revision===k.winner.revision&&j.winner.hash===k.winner.hash)t(),a.setItem(h,k),l=98,q(6),q(7);else{var v={label:"revision",path:b.getPath(),conflict_owner:{name:k.winner.owner,revision:k.winner.revision,hash:k.winner.hash}},w=e(JSON.stringify(v));v.hash=w;var x,y=b.getOption("known_conflict_list")||[],z=function(a){return a.hash===w};for(x=0;x<y.length;x+=1)if(y[x].hash===w){k.conflict_list=g.removeValuesFromArrayWhere(k.conflict_list,z),t(),l=98,q(6),q(7);return}u(),k.conflict_list.push(v),l=-10,n=!0,q(6),q(7),d.fail(b),b.getOption("onConflict")(v)}break;case 6:g.saveMetadataToDistant(b,c,k,function(){l++,q(l)},function(a){l=-10,n=!0,d.fail(b,a)});break;case 7:(function(){var a=b.cloneOption();a.onResponse=function(){},a.onFail=function(a){l=-10,n=!0,d.fail(b,a)},a.onDone=function(){q(8)};var c=d.newCommand("saveDocument",{path:b.getPath()+"."+k.owner[g.username].revision+"."+g.username,content:b.getContent(),option:a});c.setMaxRetry(0),d.addJob(d.newStorage(g.secondstorage_spec),c)})();break;case 8:(function(){if(m===0||!!k.owner[g.username]&&m===k.owner[g.username].revision)l++,q(l);else{var a=b.cloneOption();a.onResponse=function(){},a.onFail=function(a){l=-10,n=!0,d.fail(b,a)},a.onDone=function(){l++,q(l)};var c=d.newCommand("removeDocument",{path:b.getPath()+"."+m+"."+g.username,option:a});c.setMaxRetry(0),d.addJob(d.newStorage(g.secondstorage_spec),c)}})();break;case 100:if(!n){n=!0,d.done();return}break;default:}};q(0)},d.loadDocument=function(b){var c=b.getPath()+".metadata",e=i+c,f={},h=0,j=!1,k="",l,m=function(i){switch(i){case 0:g.loadMetadataFromDistant(b,c,function(a){f=JSON.parse(a.content),k=b.getOption("owner"),h=98,k?m(3):m(2),m(1)},function(a){h=-10,j=!0,d.fail(b,a)});break;case 1:a.setItem(e,f),h++,m(h);break;case 2:(function(){var a=b.cloneOption();a.onResponse=function(){},a.onFail=function(a){h=-10,j=!0,d.fail(b,a)},a.onDone=function(a){l=a,l.name=b.getPath(),h++,m(h)};var c=d.newCommand("loadDocument",{path:b.getPath()+"."+f.winner.revision+"."+f.winner.owner,option:a});d.addJob(d.newStorage(g.secondstorage_spec),c)})();break;case 3:(function(){var a=b.cloneOption();a.onResponse=function(){},a.onFail=function(a){h=-10,j=!0,d.fail(b,a)},a.onDone=function(a){l=a,l.name=b.getPath(),h++,m(h)};if(!f.owner[k]){a.onFail({status:404,statusText:"Not Found",message:"Document not found."});return}var c=d.newCommand("loadDocument",{path:b.getPath()+"."+f.owner[k].revision+"."+k,option:a});d.addJob(d.newStorage(g.secondstorage_spec),c)})();break;case 100:if(!j){j=!0,d.done(l);return}break;default:}};m(0)},d.getDocumentList=function(b){var c=[],e=[],f=!1,h=0,i=function(){var f=b.cloneOption();f.onResponse=function(){},f.onFail=function(a){d.fail(b,a)},f.onDone=function(f){var g;for(g=0;g<f.length;g+=1){var h=f[g].name.split(".")||[],i,k={};if(h[h.length-1]==="metadata"){try{i=JSON.parse(f[g].content)}catch(l){continue}e.push(i),h.length--,k.name=h.join("."),k.creation_date=i.owner[i.winner.owner].creation_date,k.last_modified=i.owner[i.winner.owner].last_modified,c.push(k)}}if(b.getOption("metadata_only"))d.done(c);else{if(f.length===0)return d.done([]);for(g=0;g<c.length;g+=1)a.setItem(c[g].name+".metadata",e[g]),j(c[g],e[g].winner.revision,e[g].winner.owner);d.end()}};var h=d.newCommand("getDocumentList",{path:b.getPath(),option:f});d.addJob(d.newStorage(g.secondstorage_spec),h)},j=function(a,e,i){var j=b.cloneOption();j.onResponse=function(){},j.onFail=function(a){f||(f=!0,d.fail(b,a))},j.onDone=function(b){f||(a.content=b.content,h++,c.length===h&&(f=!0,d.done(c)))};var k=d.newCommand("loadDocument",{path:a.name+"."+e+"."+i,option:j});d.addJob(d.newStorage(g.secondstorage_spec),k)};i()},d.removeDocument=function(b){var c=b.getPath()+".metadata",f=i+c,h={},j={},k=0,l=0,m=!1,n=!1,o=function(i){switch(i){case 0:k=3,o(2),o(1);break;case 1:var p={revision:0,hash:"",last_modified:0,creation_date:0};h=a.getItem(f),h?h.owner[g.username]||(h.owner[g.username]=p):(h={winner:{},owner:{},conflict_list:[]},h.winner={revision:0,owner:g.username,hash:""},h.owner[g.username]=p),k++,o(k);break;case 2:g.loadMetadataFromDistant(b,c,function(a){j=JSON.parse(a.content),k++,o(k)},function(a){if(a.status===404){j=h,n=!0,k++,o(k);return}k=-10,m=!0,d.fail(b,a)});break;case 5:var q=function(){j.owner[g.username]&&(l=j.owner[g.username].revision,delete j.owner[g.username])},r=function(){q(),j.winner.owner=g.username,j.winner.revision=0,j.winner.hash=""},s=function(){q()};if(n)return a.deleteItem(f),d.done();if(h.winner.revision===j.winner.revision&&h.winner.hash===j.winner.hash)r(),a.setItem(f,j),k=98,o(6),o(7);else{var t={label:"revision",path:b.getPath(),conflict_owner:{name:j.winner.owner,revision:j.winner.revision,hash:j.winner.hash}},u=e(JSON.stringify(t));t.hash=u;var v,w=b.getOption("known_conflict_list")||[],x=function(a){return a.hash===u};for(v=0;v<w.length;v+=1)if(w[v].hash===u){j.conflict_list=g.removeValuesFromArrayWhere(j.conflict_list,x),r(),k=98,o(6),o(7);return}s(),j.conflict_list.push(t),k=-10,m=!0,o(6),o(7),d.fail(b),b.getOption("onConflict")(t)}break;case 6:g.saveMetadataToDistant(b,c,j,function(){k++,o(k)},function(a){k=-10,m=!0,d.fail(b,a)});break;case 7:(function(){if(l===0||!!j.owner[g.username]&&l===j.owner[g.username].revision)k++,o(k);else{var a=b.cloneOption();a.onResponse=function(){},a.onFail=function(a){k=-10,m=!0,d.fail(b,a)},a.onDone=function(){k++,o(k)};var c=d.newCommand("removeDocument",{path:b.getPath()+"."+l+"."+g.username,option:a});c.setMaxRetry(0),d.addJob(d.newStorage(g.secondstorage_spec),c)}})();break;case 100:if(!m){m=!0,d.done();return}break;default:}};o(0)},d};f.addStorageType("conflictmanager",l)})(LocalOrCookieStorage,jQuery,Base64,sjcl,hex_sha256,jio); (function(a,b,c,d,e,f){var g=function(b,c){var d=f.storage(b,c,"base"),e={};e.username=b.username||"",e.applicationname=b.applicationname||"untitled";var g="jio/local_user_array",h="jio/local_file_name_array/"+e.username+"/"+e.applicationname,i=d.serialized;return d.serialized=function(){var a=i();return a.applicationname=e.applicationname,a.username=e.username,a},d.validateState=function(){return e.username?"":'Need at least one parameter: "username".'},e.getUserArray=function(){return a.getItem(g)||[]},e.addUser=function(b){var c=e.getUserArray();c.push(b),a.setItem(g,c)},e.userExists=function(a){var b=e.getUserArray(),c,d;for(c=0,d=b.length;c<d;c+=1)if(b[c]===a)return!0;return!1},e.getFileNameArray=function(){return a.getItem(h)||[]},e.addFileName=function(b){var c=e.getFileNameArray();c.push(b),a.setItem(h,c)},e.removeFileName=function(b){var c,d,f=e.getFileNameArray(),g=[];for(c=0,d=f.length;c<d;c+=1)f[c]!==b&&g.push(f[c]);a.setItem(h,g)},d.saveDocument=function(b){setTimeout(function(){var c=null,f="jio/local/"+e.username+"/"+e.applicationname+"/"+b.getPath();c=a.getItem(f),c?(c.last_modified=Date.now(),c.content=b.getContent()):(c={name:b.getPath(),content:b.getContent(),creation_date:Date.now(),last_modified:Date.now()},e.userExists(e.username)||e.addUser(e.username),e.addFileName(b.getPath())),a.setItem(f,c),d.done()},100)},d.loadDocument=function(b){setTimeout(function(){var c=null;c=a.getItem("jio/local/"+e.username+"/"+e.applicationname+"/"+b.getPath()),c?(b.getOption("metadata_only")&&delete c.content,d.done(c)):d.fail({status:404,statusText:"Not Found.",message:'Document "'+b.getPath()+'" not found in localStorage.'})},100)},d.getDocumentList=function(b){setTimeout(function(){var c=[],f=[],g,h,i="key",j="jio/local/"+e.username+"/"+e.applicationname,k={};f=e.getFileNameArray();for(g=0,h=f.length;g<h;g+=1)k=a.getItem(j+"/"+f[g]),k&&(b.getOption("metadata_only")?c.push({name:k.name,creation_date:k.creation_date,last_modified:k.last_modified}):c.push({name:k.name,content:k.content,creation_date:k.creation_date,last_modified:k.last_modified}));d.done(c)},100)},d.removeDocument=function(b){setTimeout(function(){var c="jio/local/"+e.username+"/"+e.applicationname+"/"+b.getPath();a.deleteItem(c),e.removeFileName(b.getPath()),d.done()},100)},d};f.addStorageType("local",g);var h=function(a,d){var e=f.storage(a,d,"base"),g={};g.username=a.username||"",g.applicationname=a.applicationname||"untitled",g.url=a.url||"",g.password=a.password||"";var h=e.serialized;return e.serialized=function(){var a=h();return a.username=g.username,a.applicationname=g.applicationname,a.url=g.url,a.password=g.password,a},e.validateState=function(){return g.username&&g.url?"":'Need at least 2 parameters: "username" and "url".'},e.saveDocument=function(a){b.ajax({url:g.url+"/dav/"+g.username+"/"+g.applicationname+"/"+a.getPath(),type:"PUT",data:a.getContent(),async:!0,dataType:"text",headers:{Authorization:"Basic "+c.encode(g.username+":"+g.password)},success:function(){e.done()},error:function(b){b.message='Cannot save "'+a.getPath()+'" into DAVStorage.',e.fail(b)}})},e.loadDocument=function(a){var d={},f=function(){b.ajax({url:g.url+"/dav/"+g.username+"/"+g.applicationname+"/"+a.getPath(),type:"GET",async:!0,dataType:"text",headers:{Authorization:"Basic "+c.encode(g.username+":"+g.password)},success:function(a){d.content=a,e.done(d)},error:function(b){b.status===404?b.message='Document "'+a.getPath()+'" not found in localStorage.':b.message='Cannot load "'+a.getPath()+'" from DAVStorage.',e.fail(b)}})};d.name=a.getPath(),b.ajax({url:g.url+"/dav/"+g.username+"/"+g.applicationname+"/"+a.getPath(),type:"PROPFIND",async:!0,dataType:"xml",headers:{Authorization:"Basic "+c.encode(g.username+":"+g.password)},success:function(c){b(c).find("lp1\\:getlastmodified, getlastmodified").each(function(){d.last_modified=b(this).text()}),b(c).find("lp1\\:creationdate, creationdate").each(function(){d.creation_date=b(this).text()}),a.getOption("metadata_only")?e.done(d):f()},error:function(b){b.message='Cannot load "'+a.getPath()+'" informations from DAVStorage.',e.fail(b)}})},e.getDocumentList=function(a){var d=[],f={},h=[];b.ajax({url:g.url+"/dav/"+g.username+"/"+g.applicationname+"/",async:!0,type:"PROPFIND",dataType:"xml",headers:{Authorization:"Basic "+c.encode(g.username+":"+g.password),Depth:"1"},success:function(a){b(a).find("D\\:response, response").each(function(a,c){if(a>0){f={},b(c).find("D\\:href, href").each(function(){h=b(this).text().split("/"),f.name=h[h.length-1]?h[h.length-1]:h[h.length-2]+"/"});if(f.name===".htaccess"||f.name===".htpasswd")return;b(c).find("lp1\\:getlastmodified, getlastmodified").each(function(){f.last_modified=b(this).text()}),b(c).find("lp1\\:creationdate, creationdate").each(function(){f.creation_date=b(this).text()}),d.push(f)}}),e.done(d)},error:function(a){a.message="Cannot get a document list from DAVStorage.",e.fail(a)}})},e.removeDocument=function(a){b.ajax({url:g.url+"/dav/"+g.username+"/"+g.applicationname+"/"+a.getPath(),type:"DELETE",async:!0,headers:{Authorization:"Basic "+c.encode(g.username+":"+g.password)},success:function(){e.done()},error:function(a){a.status===404?e.done():(a.message='Cannot remove "'+e.getFileName()+'" from DAVStorage.',e.fail(a))}})},e};f.addStorageType("dav",h);var i=function(a,b){var c=f.storage(a,b,"handler"),d={};d.return_value_array=[],d.storagelist=a.storagelist||[],d.nb_storage=d.storagelist.length;var e=c.serialized;return c.serialized=function(){var a=e();return a.storagelist=d.storagelist,a},c.validateState=function(){return d.storagelist.length===0?'Need at least one parameter: "storagelist" containing at least one storage.':""},d.isTheLast=function(){return d.return_value_array.length===d.nb_storage},d.doJob=function(a,b){var e=!1,f=[],g,h=function(a){d.return_value_array.push(a)},i=function(a){e||(f.push(a),d.isTheLast()&&c.fail({status:207,statusText:"Multi-Status",message:b,array:f}))},j=function(a){e||(e=!0,c.done(a))};for(g=0;g<d.nb_storage;g+=1){var k=a.clone(),l=c.newStorage(d.storagelist[g]);k.onResponseDo(h),k.onFailDo(i),k.onDoneDo(j),c.addJob(l,k)}a.setMaxRetry(1)},c.saveDocument=function(a){d.doJob(a,'All save "'+a.getPath()+'" requests have failed.'),c.end()},c.loadDocument=function(a){d.doJob(a,'All load "'+a.getPath()+'" requests have failed.'),c.end()},c.getDocumentList=function(a){d.doJob(a,"All get document list requests have failed."),c.end()},c.removeDocument=function(a){d.doJob(a,'All remove "'+a.getPath()+'" requests have failed.'),c.end()},c};f.addStorageType("replicate",i);var j=function(b,c){var d=f.storage(b,c,"handler"),e={},g=b.storage||!1;e.secondstorage_spec=b.storage||{type:"base"},e.secondstorage_string=JSON.stringify(e.secondstorage_spec);var h="jio/indexed_storage_array",i="jio/indexed_file_array/"+e.secondstorage_string,j=d.serialized;return d.serialized=function(){var a=j();return a.storage=e.secondstorage_spec,a},d.validateState=function(){return g?"":'Need at least one parameter: "storage" containing storage specifications.'},e.isStorageArrayIndexed=function(){return a.getItem(h)?!0:!1},e.getIndexedStorageArray=function(){return a.getItem(h)||[]},e.indexStorage=function(b){var c=e.getIndexedStorageArray();c.push(typeof b=="string"?b:JSON.stringify(b)),a.setItem(h,c)},e.isAnIndexedStorage=function(a){var b=typeof a=="string"?a:JSON.stringify(a),c,d,f=e.getIndexedStorageArray();for(c=0,d=f.length;c<d;c+=1)if(JSON.stringify(f[c])===b)return!0;return!1},e.fileArrayExists=function(){return a.getItem(i)?!0:!1},e.getFileArray=function(){return a.getItem(i)||[]},e.setFileArray=function(b){return a.setItem(i,b)},e.isFileIndexed=function(a){var b,c,d=e.getFileArray();for(b=0,c=d.length;b<c;b+=1)if(d[b].name===a)return!0;return!1},e.addFile=function(b){var c=e.getFileArray();c.push(b),a.setItem(i,c)},e.removeFile=function(b){var c,d,f=e.getFileArray(),g=[];for(c=0,d=f.length;c<d;c+=1)f[c].name!==b&&g.push(f[c]);a.setItem(i,g)},e.update=function(){var a=function(a){e.isAnIndexedStorage(e.secondstorage_string)||e.indexStorage(e.secondstorage_string),e.setFileArray(a)};d.addJob(d.newStorage(e.secondstorage_spec),d.newCommand("getDocumentList",{path:".",option:{onDone:a,max_retry:3}}))},d.saveDocument=function(a){var b=a.clone();b.onResponseDo(function(){}),b.onDoneDo(function(b){e.isFileIndexed(a.getPath())||e.addFile({name:a.getPath(),last_modified:0,creation_date:0}),e.update(),d.done()}),b.onFailDo(function(a){d.fail(a)}),d.addJob(d.newStorage(e.secondstorage_spec),b)},d.loadDocument=function(a){var b,c,f,g,h=function(a){d.done(a)},i=function(a){d.fail(a)},j=function(){var b=a.clone();b.onResponseDo(function(){}),b.onFailDo(i),b.onDoneDo(h),d.addJob(d.newStorage(e.secondstorage_spec),b)};e.update(),a.getOption("metadata_only")?setTimeout(function(){if(e.fileArrayExists()){b=e.getFileArray();for(c=0,f=b.length;c<f;c+=1)if(b[c].name===a.getPath())return d.done(b[c])}else j()},100):j()},d.getDocumentList=function(a){var b,c,f=!1;e.update(),a.getOption("metadata_only")?(b=setInterval(function(){f&&(d.fail({status:0,statusText:"Timeout",message:"The request has timed out."}),clearInterval(b)),e.fileArrayExists()&&(d.done(e.getFileArray()),clearInterval(b))},100),setTimeout(function(){f=!0},1e4)):(c=a.clone(),c.onDoneDo(function(a){d.done(a)}),c.onFailDo(function(a){d.fail(a)}),d.addJob(d.newStorage(e.secondstorage_spec),c))},d.removeDocument=function(a){var b=a.clone();b.onResponseDo(function(){}),b.onDoneDo(function(b){e.removeFile(a.getPath()),e.update(),d.done()}),b.onFailDo(function(a){d.fail(a)}),d.addJob(d.newStorage(e.secondstorage_spec),b)},d};f.addStorageType("indexed",j);var k=function(a,c){var e=f.storage(a,c,"handler"),g={},h=a.storage||!1;g.username=a.username||"",g.password=a.password||"",g.secondstorage_spec=a.storage||{type:"base"},g.secondstorage_string=JSON.stringify(g.secondstorage_string);var i=e.serialized;return e.serialized=function(){var a=i();return a.username=g.username,a.password=g.password,a.storage=g.secondstorage_string,a},e.validateState=function(){return g.username&&h?"":'Need at least two parameters: "username" and "storage".'},g.encrypt_param_object={iv:"kaprWwY/Ucr7pumXoTHbpA",v:1,iter:1e3,ks:256,ts:128,mode:"ccm",adata:"",cipher:"aes",salt:"K4bmZG9d704"},g.decrypt_param_object={iv:"kaprWwY/Ucr7pumXoTHbpA",ks:256,ts:128,salt:"K4bmZG9d704"},g.encrypt=function(a,b,c){var e=d.encrypt(g.username+":"+g.password,a,g.encrypt_param_object);b(JSON.parse(e).ct,c)},g.decrypt=function(a,c,e,f){var h,i=b.extend(!0,{},g.decrypt_param_object);i.ct=a||"",i=JSON.stringify(i);try{h=d.decrypt(g.username+":"+g.password,i)}catch(j){c({status:0,statusText:"Decrypt Fail",message:"Unable to decrypt."},e,f);return}c(h,e,f)},e.saveDocument=function(a){var b,c,d=function(){g.encrypt(a.getPath(),function(a){b=a,f()})},f=function(){g.encrypt(a.getContent(),function(a){c=a,h()})},h=function(){var d=a.cloneOption(),f;d.onResponse=function(){},d.onDone=function(){e.done()},d.onFail=function(a){e.fail(a)},f=e.newCommand("saveDocument",{path:b,content:c,option:d}),e.addJob(e.newStorage(g.secondstorage_spec),f)};d()},e.loadDocument=function(a){var b,c,d=function(){g.encrypt(a.getPath(),function(a){b=a,f()})},f=function(){var c=a.cloneOption(),d;c.onResponse=function(){},c.onFail=i,c.onDone=h,d=e.newCommand("loadDocument",{path:b,option:c}),e.addJob(e.newStorage(g.secondstorage_spec),d)},h=function(b){b.name=a.getPath(),a.getOption("metadata_only")?e.done(b):g.decrypt(b.content,function(a){typeof a=="object"?e.fail({status:0,statusText:"Decrypt Fail",message:"Unable to decrypt"}):(b.content=a,e.done(b))})},i=function(a){e.fail(a)};d()},e.getDocumentList=function(a){var b,c,d,f=0,h,i=!0,j=function(){var b=a.clone();b.onResponseDo(k),b.onDoneDo(function(){}),b.onFailDo(function(){}),e.addJob(e.newStorage(g.secondstorage_spec),b)},k=function(a){if(a.status.isDone()){h=a.value;for(c=0,d=h.length;c<d;c+=1)g.decrypt(h[c].name,l,c,"name")}else e.fail(a.error)},l=function(a,b,c){var g;f++;if(typeof a=="object"){i&&(i=!1,e.fail({status:0,statusText:"Decrypt Fail",message:"Unable to decrypt."})),i=!1;return}h[b][c]=a,f===d&&i&&e.done(h)};j()},e.removeDocument=function(a){var b,c=function(){g.encrypt(a.getPath(),function(a){b=a,d()})},d=function(){var c=a.cloneOption();c.onResponse=f,c.onFail=function(){},c.onDone=function(){},e.addJob(e.newStorage(g.secondstorage_spec),e.newCommand("removeDocument",{path:b,option:c}))},f=function(a){a.status.isDone()?e.done():e.fail(a.error)};c()},e};f.addStorageType("crypt",k);var l=function(b,c){var d=f.storage(b,c,"handler"),g={};b=b||{},c=c||{},g.username=b.username||"";var h=b.storage?!0:!1;g.secondstorage_spec=b.storage||{type:"base"},g.secondstorage_string=JSON.stringify(g.secondstorage_spec);var i="jio/conflictmanager/"+g.username+"/"+g.secondstorage_string+"/",j=d.serialized;return d.serialized=function(){var a=j();return a.username=g.username,a.storage=g.secondstorage_spec,a},d.validateState=function(){return g.username&&h?"":'Need at least two parameter: "username" and "storage".'},g.removeValuesFromArrayWhere=function(a,b){var c,d=[];for(c=0;c<a.length;c+=1)b(a[c])||d.push(a[c]);return d},g.loadMetadataFromDistant=function(a,b,c,e){var f=a.cloneOption();f.metadata_only=!1,f.onResponse=function(){},f.onFail=e,f.onDone=c;var h=d.newCommand("loadDocument",{path:b,option:f});d.addJob(d.newStorage(g.secondstorage_spec),h)},g.saveMetadataToDistant=function(a,b,c,e,f){var h=a.cloneOption();h.onResponse=function(){},h.onFail=f,h.onDone=e;var i=d.newCommand("saveDocument",{path:b,content:JSON.stringify(c),option:h});i.setMaxRetry(0),d.addJob(d.newStorage(g.secondstorage_spec),i)},g.newAsyncModule=function(){var a={};return a.call=function(a,b,c){return a._wait=a._wait||{},a._wait[b]?(a._wait[b]--,function(){}):(c=c||[],a[b].apply(a[b],c))},a.neverCall=function(a,b){a._wait=a._wait||{},a._wait[b]=-1},a.wait=function(a,b,c){a._wait=a._wait||{},a._wait[b]=c},a.end=function(){a.call=function(){}},a},d.saveDocument=function(b){var c={},f=g.newAsyncModule(),h=b.getPath()+".metadata",j=i+h,k={},l={},m=0,n=!1,o=e(b.getContent()),p=new Date;c.updateLocalMetadata=function(){var b={revision:0,hash:"",last_modified:0,creation_date:p.getTime()};k=a.getItem(j),k?k.owner[g.username]||(k.owner[g.username]=b):(k={winner:{},owner:{},conflict_list:[]},k.winner={revision:0,owner:g.username,hash:""},k.owner[g.username]=b),f.call(c,"checkForConflicts")},c.loadMetadataFromDistant=function(){g.loadMetadataFromDistant(b,h,function(a){l=JSON.parse(a.content),f.call(c,"checkForConflicts")},function(a){a.status===404?(l=k,n=!0,f.call(c,"checkForConflicts")):f.call(c,"fail",[a])})},c.checkForConflicts=function(){var d=function(){var a;n||!l.owner[l.winner.owner]?a=p.getTime():a=l.owner[l.winner.owner].creation_date||p.getTime(),l.owner[g.username]?m=l.owner[g.username].revision:l.owner[g.username]={},l.owner[g.username].last_modified=p.getTime(),l.owner[g.username].creation_date=a,l.owner[g.username].hash=o},h=function(){d(),l.winner.owner=g.username,l.winner.revision++,l.winner.hash=o,l.owner[g.username].revision=l.winner.revision},i=function(){d(),l.owner[g.username].revision++};if(n){h(),a.setItem(j,l),f.wait(c,"done",1),f.call(c,"saveMetadata"),f.call(c,"saveRevision");return}if(k.winner.revision===l.winner.revision&&k.winner.hash===l.winner.hash)h(),a.setItem(j,l),f.wait(c,"done",1),f.call(c,"saveMetadata"),f.call(c,"saveRevision");else{var q={label:"revision",path:b.getPath(),method:"saveDocument",owner:g.username,conflict_owner:{name:l.winner.owner,revision:l.winner.revision,hash:l.winner.hash}},r=e(JSON.stringify(q));q.hash=r;var s,t=b.getOption("known_conflict_list")||[],u=function(a){return a.hash===r};for(s=0;s<t.length;s+=1)if(t[s].hash===r){l.conflict_list=g.removeValuesFromArrayWhere(l.conflict_list,u),h(),f.wait(c,"done",1),f.call(c,"saveMetadata"),f.call(c,"saveRevision");return}i(),l.conflict_list.push(q),f.neverCall(c,"done"),f.call(c,"saveMetadata"),f.call(c,"saveRevision"),f.call(c,"fail",[{status:0,statusText:"Revision Conflict",message:"Someone has already modified this document."}]);var v=b.getOption("onConflict")||function(){};v(q)}},c.saveMetadata=function(){g.saveMetadataToDistant(b,h,l,function(){f.call(c,"done")},function(a){f.call(c,"fail",[a])})},c.saveRevision=function(){var a=b.cloneOption();a.onResponse=function(){},a.onFail=function(a){f.call(c,"fail",[a])},a.onDone=function(){f.call(c,"deletePreviousRevision")};var e=d.newCommand("saveDocument",{path:b.getPath()+"."+l.owner[g.username].revision+"."+g.username,content:b.getContent(),option:a});e.setMaxRetry(0),d.addJob(d.newStorage(g.secondstorage_spec),e)},c.deletePreviousRevision=function(){if(m===0||!!l.owner[g.username]&&m===l.owner[g.username].revision)f.call(c,"done");else{var a=b.cloneOption();a.onResponse=function(){},a.onFail=function(a){f.call(c,"fail",[a])},a.onDone=function(){f.call(c,"done")};var e=d.newCommand("removeDocument",{path:b.getPath()+"."+m+"."+g.username,option:a});e.setMaxRetry(0),d.addJob(d.newStorage(g.secondstorage_spec),e)}},c.fail=function(a){f.neverCall(c,"fail"),f.neverCall(c,"done"),b.setMaxRetry(1),d.fail(a)},c.done=function(){f.neverCall(c,"done"),f.neverCall(c,"fail"),d.done()},f.wait(c,"checkForConflicts",1),f.call(c,"loadMetadataFromDistant"),f.call(c,"updateLocalMetadata")},d.loadDocument=function(b){var c={},e=g.newAsyncModule(),f=b.getPath()+".metadata",h=i+f,j={},k="",l;c.loadMetadataFromDistant=function(){g.loadMetadataFromDistant(b,f,function(a){j=JSON.parse(a.content),k=b.getOption("owner"),e.wait(c,"done",1),k?e.call(c,"loadOwner"):e.call(c,"loadWinner"),e.call(c,"updateLocalMetadata")},function(a){e.end(),e.call(c,"fail",[a])})},c.updateLocalMetadata=function(){a.setItem(h,j),e.call(c,"done")},c.loadWinner=function(){var a=b.cloneOption();a.onResponse=function(){},a.onFail=function(a){e.call(c,"fail",[a]),e.end()},a.onDone=function(a){l=a,l.name=b.getPath(),e.call(c,"done")};var f=d.newCommand("loadDocument",{path:b.getPath()+"."+j.winner.revision+"."+j.winner.owner,option:a});d.addJob(d.newStorage(g.secondstorage_spec),f)},c.loadOwner=function(){var a=b.cloneOption();a.onResponse=function(){},a.onFail=function(a){e.end(),e.call(c,"fail",[a])},a.onDone=function(a){l=a,l.name=b.getPath(),e.call(c,"done")};if(!j.owner[k]){a.onFail({status:404,statusText:"Not Found",message:"Document not found."});return}var f=d.newCommand("loadDocument",{path:b.getPath()+"."+j.owner[k].revision+"."+k,option:a});d.addJob(d.newStorage(g.secondstorage_spec),f)},c.fail=function(a){e.neverCall(c,"fail"),e.neverCall(c,"done"),b.setMaxRetry(1),d.fail(a)},c.done=function(){e.neverCall(c,"done"),e.neverCall(c,"fail"),d.done(l)},e.call(c,"loadMetadataFromDistant")},d.getDocumentList=function(b){var c={},e=g.newAsyncModule(),f=[],h=[],i=0;c.retreiveList=function(){var i=b.cloneOption();i.metadata_only=!1,i.onResponse=function(){},i.onFail=function(a){e.call(c,"fail",[a])},i.onDone=function(g){var i;for(i=0;i<g.length;i+=1){var j=g[i].name.split(".")||[],k,l={};if(j[j.length-1]==="metadata"){try{k=JSON.parse(g[i].content)}catch(m){continue}h.push(k),j.length--,l.name=j.join("."),l.creation_date=k.owner[k.winner.owner].creation_date,l.last_modified=k.owner[k.winner.owner].last_modified,f.push(l)}}if(b.getOption("metadata_only"))e.call(c,"done",[f]);else{if(g.length===0)return d.done([]);e.wait(c,"done",f.length-1);for(i=0;i<f.length;i+=1)a.setItem(f[i].name+".metadata",h[i]),e.call(c,"loadFile",[f[i],h[i].winner.revision,h[i].winner.owner]);d.end()}};var j=d.newCommand("getDocumentList",{path:b.getPath(),option:i});d.addJob(d.newStorage(g.secondstorage_spec),j)},c.loadFile=function(a,h,i){var j=b.cloneOption();j.onResponse=function(){},j.onFail=function(a){e.call(c,"fail",[a]),e.end()},j.onDone=function(b){a.content=b.content,e.call(c,"done",[f])};var k=d.newCommand("loadDocument",{path:a.name+"."+h+"."+i,option:j});d.addJob(d.newStorage(g.secondstorage_spec),k)},c.fail=function(a){e.neverCall(c,"fail"),e.neverCall(c,"done"),b.setMaxRetry(1),d.fail(a)},c.done=function(a){e.neverCall(c,"done"),e.neverCall(c,"fail"),d.done(a)},e.call(c,"retreiveList")},d.removeDocument=function(b){var c={},f=g.newAsyncModule(),h=b.getPath()+".metadata",j=i+h,k={},l={},m=0,n=!1;c.updateLocalMetadata=function(){var b={revision:0,hash:"",last_modified:0,creation_date:0};k=a.getItem(j),k?k.owner[g.username]||(k.owner[g.username]=b):(k={winner:{},owner:{},conflict_list:[]},k.winner={revision:0,owner:g.username,hash:""},k.owner[g.username]=b),f.call(c,"checkForConflicts")},c.loadMetadataFromDistant=function(){g.loadMetadataFromDistant(b,h,function(a){l=JSON.parse(a.content),f.call(c,"checkForConflicts")},function(a){if(a.status===404){l=k,n=!0,f.call(c,"checkForConflicts");return}f.call(c,"fail",[a]),f.end()})},c.checkForConflicts=function(){var d=function(){l.owner[g.username]&&(m=l.owner[g.username].revision,delete l.owner[g.username])},h=function(){d(),l.winner.owner=g.username,l.winner.revision=0,l.winner.hash=""},i=function(){d()};if(n)return a.deleteItem(j),f.call(c,"done");if(k.winner.revision===l.winner.revision&&k.winner.hash===l.winner.hash)h(),a.setItem(j,l),f.wait(c,"done",1),f.call(c,"saveMetadata"),f.call(c,"removeRevision");else{var p={label:"revision",path:b.getPath(),method:"removeDocument",owner:g.username,conflict_owner:{name:l.winner.owner,revision:l.winner.revision,hash:l.winner.hash}},q=e(JSON.stringify(p));p.hash=q;var r,s=b.getOption("known_conflict_list")||[],t=function(a){return a.hash===q};for(r=0;r<s.length;r+=1)if(s[r].hash===q){l.conflict_list=g.removeValuesFromArrayWhere(l.conflict_list,t),h(),f.wait(c,"done",1),f.call(c,"saveMetadata"),f.call(c,"removeRevision");return}i(),l.conflict_list.push(p),f.neverCall(c,"done"),f.call(c,"saveMetadata"),f.call(c,"fail",[{status:0,statusText:"Revision Conflict",message:"Someone has already modified this document."}]);var u=b.getOption("onConflict")||function(){};u(p)}},c.saveMetadata=function(){g.saveMetadataToDistant(b,h,l,function(){f.call(c,"done")},function(a){f.call(c,"fail",[a]),f.end()})},c.removeRevision=function(){if(m===0||!!l.owner[g.username]&&m===l.owner[g.username].revision)f.call(c,"done");else{var a=b.cloneOption();a.onResponse=function(){},a.onFail=function(a){f.call(c,"fail",[a]),f.end()},a.onDone=function(){f.call(c,"done")};var e=d.newCommand("removeDocument",{path:b.getPath()+"."+m+"."+g.username,option:a});e.setMaxRetry(0),d.addJob(d.newStorage(g.secondstorage_spec),e)}},c.fail=function(a){f.neverCall(c,"fail"),f.neverCall(c,"done"),b.setMaxRetry(1),d.fail(a)},c.done=function(){f.neverCall(c,"done"),f.neverCall(c,"fail"),d.done()},f.wait(c,"checkForConflicts",1),f.call(c,"loadMetadataFromDistant"),f.call(c,"updateLocalMetadata")},d};f.addStorageType("conflictmanager",l)})(LocalOrCookieStorage,jQuery,Base64,sjcl,hex_sha256,jio);
\ No newline at end of file \ No newline at end of file
...@@ -8,20 +8,22 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -8,20 +8,22 @@ var newConflictManagerStorage = function ( spec, my ) {
priv.secondstorage_spec = spec.storage || {type:'base'}; priv.secondstorage_spec = spec.storage || {type:'base'};
priv.secondstorage_string = JSON.stringify (priv.secondstorage_spec); priv.secondstorage_string = JSON.stringify (priv.secondstorage_spec);
var local_namespace = 'jio/conflictmanager/'+priv.secondstorage_string+'/'; var local_namespace = 'jio/conflictmanager/'+priv.username+'/'+
priv.secondstorage_string+'/';
var super_serialized = that.serialized; var super_serialized = that.serialized;
that.serialized = function () { that.serialized = function () {
var o = super_serialized(); var o = super_serialized();
o.username = priv.username;
o.storage = priv.secondstorage_spec; o.storage = priv.secondstorage_spec;
return o; return o;
}; };
that.validateState = function () { that.validateState = function () {
if (!priv.username || storage_exists) { if (priv.username && storage_exists) {
return 'Need at least two parameter: "owner" and "storage".'; return '';
} }
return ''; return 'Need at least two parameter: "username" and "storage".';
}; };
priv.removeValuesFromArrayWhere = function (array,fun) { priv.removeValuesFromArrayWhere = function (array,fun) {
...@@ -34,14 +36,9 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -34,14 +36,9 @@ var newConflictManagerStorage = function ( spec, my ) {
return newarray; return newarray;
}; };
var super_fail = that.fail;
that.fail = function (command,error) {
command.setMaxRetry(1);
super_fail(error);
};
priv.loadMetadataFromDistant = function (command,path,onDone,onFail) { priv.loadMetadataFromDistant = function (command,path,onDone,onFail) {
var cloned_option = command.cloneOption (); var cloned_option = command.cloneOption ();
cloned_option.metadata_only = false;
cloned_option.onResponse = function () {}; cloned_option.onResponse = function () {};
cloned_option.onFail = onFail; cloned_option.onFail = onFail;
cloned_option.onDone = onDone; cloned_option.onDone = onDone;
...@@ -66,252 +63,268 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -66,252 +63,268 @@ var newConflictManagerStorage = function ( spec, my ) {
newcommand ); newcommand );
}; };
priv.newAsyncModule = function () {
var async = {};
async.call = function (obj,function_name,arglist) {
obj._wait = obj._wait || {};
if (obj._wait[function_name]) {
obj._wait[function_name]--;
return function () {};
}
// ok if undef or 0
arglist = arglist || [];
return obj[function_name].apply(obj[function_name],arglist);
};
async.neverCall = function (obj,function_name) {
obj._wait = obj._wait || {};
obj._wait[function_name] = -1;
};
async.wait = function (obj,function_name,times) {
obj._wait = obj._wait || {};
obj._wait[function_name] = times;
};
async.end = function () {
async.call = function(){};
};
return async;
};
/** /**
* Save a document and can manage conflicts. * Save a document and can manage conflicts.
* @method saveDocument * @method saveDocument
*/ */
that.saveDocument = function (command) { that.saveDocument = function (command) {
var metadata_file_name = command.getPath() + '.metadata', var o = {}, am = priv.newAsyncModule(),
now = new Date(), metadata_file_name = command.getPath() + '.metadata',
local_metadata_file_name = local_namespace + metadata_file_name, local_metadata_file_name = local_namespace + metadata_file_name,
local_file_metadata = {}, // local file.metadata local_file_metadata = {}, // local file.metadata
command_file_metadata = {}, // distant file.metadata command_file_metadata = {}, // distant file.metadata
run_index = 0, previous_revision = 0, previous_revision = 0,
end = false, is_a_new_file = false, is_a_new_file = false,
local_file_hash = hex_sha256 (command.getContent()), local_file_hash = hex_sha256 (command.getContent()),
run = function (index) { now = new Date();
switch (index) {
case 0: o.updateLocalMetadata = function () {
run_index = 3; var new_owner_object = {revision:0,hash:'',
run (2); last_modified:0,
run (1); creation_date:now.getTime()};
break; local_file_metadata =
case 1: // update local metadata LocalOrCookieStorage.getItem (local_metadata_file_name);
var new_owner_object = {revision:0,hash:'', if ( local_file_metadata ) {
last_modified:0, // if metadata already exists
creation_date:now.getTime()}; if ( !local_file_metadata.owner[priv.username] ) {
local_file_metadata =
LocalOrCookieStorage.getItem (local_metadata_file_name);
if ( local_file_metadata ) {
// if metadata already exists
if ( !local_file_metadata.owner[priv.username] ) {
local_file_metadata.owner[priv.username] =
new_owner_object;
}
} else {
local_file_metadata = {
winner: {},
owner: {},
conflict_list: []
};
local_file_metadata.winner = {
revision:0,owner:priv.username,hash:''};
local_file_metadata.owner[priv.username] = local_file_metadata.owner[priv.username] =
new_owner_object; new_owner_object;
} }
run_index ++; run (run_index); } else {
break; local_file_metadata = {
case 2: // load metadata from distant winner: {},
priv.loadMetadataFromDistant ( owner: {},
command,metadata_file_name, conflict_list: []
function (result) { };
command_file_metadata = JSON.parse (result.content); local_file_metadata.winner = {
run_index ++; run (run_index); revision:0,owner:priv.username,hash:''};
},function (error) { local_file_metadata.owner[priv.username] =
if (error.status === 404) { new_owner_object;
command_file_metadata = local_file_metadata; }
is_a_new_file = true; am.call(o,'checkForConflicts');
run_index ++; run (run_index); };
} else { o.loadMetadataFromDistant = function () {
run_index = (-10); priv.loadMetadataFromDistant (
end = true; command,metadata_file_name,
that.fail(command,error); function (result) {
} command_file_metadata = JSON.parse (result.content);
}); am.call(o,'checkForConflicts');
break; },function (error) {
case 5: // check conflicts if (error.status === 404) {
var updateMetadataCommon = function () { command_file_metadata = local_file_metadata;
var original_creation_date; is_a_new_file = true;
am.call(o,'checkForConflicts');
if (is_a_new_file || !command_file_metadata.owner[
command_file_metadata.winner.owner]) {
original_creation_date = now.getTime();
} else { } else {
original_creation_date = command_file_metadata.owner[ am.call(o,'fail',[error]);
command_file_metadata.winner.owner].
creation_date || now.getTime();
} }
});
};
o.checkForConflicts = function () {
var updateMetadataCommon = function () {
var original_creation_date;
if (command_file_metadata.owner[priv.username]) { if (is_a_new_file || !command_file_metadata.owner[
previous_revision = command_file_metadata.owner[ command_file_metadata.winner.owner]) {
priv.username].revision; original_creation_date = now.getTime();
} else { } else {
command_file_metadata.owner[priv.username] = {}; original_creation_date = command_file_metadata.owner[
} command_file_metadata.winner.owner].
command_file_metadata.owner[priv.username]. creation_date || now.getTime();
last_modified = now.getTime();
command_file_metadata.owner[priv.username].
creation_date = original_creation_date;
command_file_metadata.owner[priv.username].hash =
local_file_hash;
};
var updateCommandMetadataNotOnConflict = function () {
updateMetadataCommon ();
command_file_metadata.winner.owner = priv.username;
command_file_metadata.winner.revision ++;
command_file_metadata.winner.hash = local_file_hash;
command_file_metadata.owner[priv.username].revision =
command_file_metadata.winner.revision;
};
var updateCommandMetadataOnConflict = function () {
updateMetadataCommon ();
command_file_metadata.owner[priv.username].revision ++;
};
// if this is a new file
if (is_a_new_file) {
updateCommandMetadataNotOnConflict();
LocalOrCookieStorage.setItem (local_metadata_file_name,
command_file_metadata);
run_index = (98);
run (6); // save metadata
run (7); // save document revision
break;
} }
// if no conflict
if (local_file_metadata.winner.revision === if (command_file_metadata.owner[priv.username]) {
command_file_metadata.winner.revision && previous_revision = command_file_metadata.owner[
local_file_metadata.winner.hash === priv.username].revision;
command_file_metadata.winner.hash) {
// OK! Now, update distant metadata, store them and save
updateCommandMetadataNotOnConflict();
LocalOrCookieStorage.setItem (local_metadata_file_name,
command_file_metadata);
run_index = 98;
run (6); // save metadata
run (7); // save document revision
} else { } else {
// if conflict command_file_metadata.owner[priv.username] = {};
var conflict_object = {
label: 'revision',
path: command.getPath(),
conflict_owner: {
name: command_file_metadata.winner.owner,
revision: command_file_metadata.winner.revision,
hash: command_file_metadata.winner.hash}
},
// gen hash
conflict_hash = hex_sha256 (JSON.stringify (
conflict_object));
conflict_object.hash = conflict_hash;
// browse known conflict list
var i, known_conflict_list =
command.getOption('known_conflict_list') || [];
var compare_fun = function (v) {
return (v.hash === conflict_hash);
};
for (i = 0; i < known_conflict_list.length; i+= 1) {
// if known conflict
if (known_conflict_list[i].hash ===
conflict_hash) {
command_file_metadata.conflict_list =
priv.removeValuesFromArrayWhere(
command_file_metadata.conflict_list,
compare_fun);
updateCommandMetadataNotOnConflict();
run_index = 98;
run (6);
run (7);
return;
}
}
updateCommandMetadataOnConflict();
// if unknown conflict
command_file_metadata.conflict_list.push (conflict_object);
run_index = (-10);
end = true;
run (6); // save metadata
run (7); // save document revision
that.fail(command); // TODO
command.getOption('onConflict')(conflict_object);
} }
break; command_file_metadata.owner[priv.username].
case 6: // save metadata last_modified = now.getTime();
priv.saveMetadataToDistant ( command_file_metadata.owner[priv.username].
command,metadata_file_name,command_file_metadata, creation_date = original_creation_date;
function () { command_file_metadata.owner[priv.username].hash =
run_index ++; run (run_index); local_file_hash;
},function (error) { };
run_index = (-10); var updateCommandMetadataNotOnConflict = function () {
end = true; updateMetadataCommon ();
that.fail(command,error); command_file_metadata.winner.owner = priv.username;
}); command_file_metadata.winner.revision ++;
break; command_file_metadata.winner.hash = local_file_hash;
case 7: // save document revision command_file_metadata.owner[priv.username].revision =
(function () { command_file_metadata.winner.revision;
var cloned_option = command.cloneOption (); };
cloned_option.onResponse = function () {}; var updateCommandMetadataOnConflict = function () {
cloned_option.onFail = function (error) { updateMetadataCommon ();
run_index = (-10); command_file_metadata.owner[priv.username].revision ++;
end = true; };
that.fail(command,error); // if this is a new file
}; if (is_a_new_file) {
cloned_option.onDone = function () { updateCommandMetadataNotOnConflict();
run (8); LocalOrCookieStorage.setItem (local_metadata_file_name,
}; command_file_metadata);
var newcommand = that.newCommand( am.wait(o,'done',1);
'saveDocument', am.call(o,'saveMetadata');
{path:command.getPath() + '.' + am.call(o,'saveRevision');
command_file_metadata.owner[priv.username].revision + return;
'.' + priv.username, }
content:command.getContent(), // if no conflict
option:cloned_option}); if (local_file_metadata.winner.revision ===
newcommand.setMaxRetry (0); // inf command_file_metadata.winner.revision &&
that.addJob ( that.newStorage (priv.secondstorage_spec), local_file_metadata.winner.hash ===
newcommand ); command_file_metadata.winner.hash) {
}()); // OK! Now, update distant metadata, store them and save
break; updateCommandMetadataNotOnConflict();
case 8: LocalOrCookieStorage.setItem (local_metadata_file_name,
(function () { command_file_metadata);
if ( previous_revision !== 0 && ( am.wait(o,'done',1);
!command_file_metadata.owner[priv.username] || am.call(o,'saveMetadata');
previous_revision !== am.call(o,'saveRevision');
command_file_metadata.owner[ } else {
priv.username].revision ) ) { // if conflict
var cloned_option = command.cloneOption (); var conflict_object = {
cloned_option.onResponse = function () {}; label: 'revision',
cloned_option.onFail = function (error) { path: command.getPath(),
run_index = (-10); method: 'saveDocument',
end = true; owner: priv.username,
that.fail(command,error); conflict_owner: {
}; name: command_file_metadata.winner.owner,
cloned_option.onDone = function () { revision: command_file_metadata.winner.revision,
run_index ++; run (run_index); hash: command_file_metadata.winner.hash}
}; },
var newcommand = that.newCommand( // gen hash
'removeDocument', conflict_hash = hex_sha256 (JSON.stringify (
{path:command.getPath() + '.' + conflict_object));
previous_revision + '.' + priv.username, conflict_object.hash = conflict_hash;
option:cloned_option}); // browse known conflict list
newcommand.setMaxRetry (0); // inf var i, known_conflict_list =
that.addJob ( that.newStorage (priv.secondstorage_spec), command.getOption('known_conflict_list') || [];
newcommand ); var compare_fun = function (v) {
} else { return (v.hash === conflict_hash);
run_index ++; run (run_index); };
for (i = 0; i < known_conflict_list.length; i+= 1) {
// if known conflict
if (known_conflict_list[i].hash ===
conflict_hash) {
command_file_metadata.conflict_list =
priv.removeValuesFromArrayWhere(
command_file_metadata.conflict_list,
compare_fun);
updateCommandMetadataNotOnConflict();
am.wait(o,'done',1);
am.call(o,'saveMetadata');
am.call(o,'saveRevision');
return;
} }
}());
break;
case 100:
if (!end) {
end = true;
that.done();
return;
} }
break; updateCommandMetadataOnConflict();
default: // if unknown conflict
break; command_file_metadata.conflict_list.push (conflict_object);
am.neverCall(o,'done');
am.call(o,'saveMetadata');
am.call(o,'saveRevision');
am.call(o,'fail',[
{status:0,statusText:'Revision Conflict',
message:'Someone has already modified this document.'}]);
var onConflict = command.getOption('onConflict') ||
function (){};
onConflict(conflict_object);
} }
}; };
run (0); o.saveMetadata = function () {
priv.saveMetadataToDistant (
command,metadata_file_name,command_file_metadata,
function () {
am.call(o,'done');
},function (error) {
am.call(o,'fail',[error]);
});
};
o.saveRevision = function () {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
am.call(o,'fail',[error]);
};
cloned_option.onDone = function () {
am.call(o,'deletePreviousRevision');
};
var newcommand = that.newCommand(
'saveDocument',
{path:command.getPath() + '.' +
command_file_metadata.owner[priv.username].revision +
'.' + priv.username,
content:command.getContent(),
option:cloned_option});
newcommand.setMaxRetry (0); // inf
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
};
o.deletePreviousRevision = function () {
if ( previous_revision !== 0 && (
!command_file_metadata.owner[priv.username] ||
previous_revision !==
command_file_metadata.owner[
priv.username].revision ) ) {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
am.call(o,'fail',[error]);
};
cloned_option.onDone = function () {
am.call(o,'done');
};
var newcommand = that.newCommand(
'removeDocument',
{path:command.getPath() + '.' +
previous_revision + '.' + priv.username,
option:cloned_option});
newcommand.setMaxRetry (0); // inf
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
} else {
am.call(o,'done');
}
};
o.fail = function (error) {
am.neverCall(o,'fail');
am.neverCall(o,'done');
command.setMaxRetry(1);
that.fail(error);
};
o.done = function () {
am.neverCall(o,'done');
am.neverCall(o,'fail');
that.done();
};
am.wait(o,'checkForConflicts',1);
am.call(o,'loadMetadataFromDistant');
am.call(o,'updateLocalMetadata');
}; };
/** /**
...@@ -320,104 +333,97 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -320,104 +333,97 @@ var newConflictManagerStorage = function ( spec, my ) {
* @method loadDocument * @method loadDocument
*/ */
that.loadDocument = function (command) { that.loadDocument = function (command) {
var metadata_file_name = command.getPath() + '.metadata', var o = {}, am = priv.newAsyncModule(),
metadata_file_name = command.getPath() + '.metadata',
local_metadata_file_name = local_namespace + metadata_file_name, local_metadata_file_name = local_namespace + metadata_file_name,
command_file_metadata = {}, // distant file.metadata command_file_metadata = {}, // distant file.metadata
run_index = 0, owner = '', loaded_file;
end = false, owner = '', loaded_file,
run = function (index) { o.loadMetadataFromDistant = function () {
switch (index) { priv.loadMetadataFromDistant (
case 0: // load metadata file from distant command,metadata_file_name,
priv.loadMetadataFromDistant ( function (result) {
command,metadata_file_name, command_file_metadata = JSON.parse (result.content);
function (result) { owner = command.getOption('owner');
command_file_metadata = JSON.parse (result.content); am.wait(o,'done',1);
owner = command.getOption('owner'); // if owner
run_index = 98; if (owner) {
// if owner am.call(o,'loadOwner');
if (owner) { } else {
run (3); // if no owner
} else { am.call(o,'loadWinner');
// if no owner
run (2);
}
run (1);
},function (error) {
run_index = (-10);
end = true;
that.fail(command,error);
});
break;
case 1: // update local metadata
LocalOrCookieStorage.setItem (local_metadata_file_name,
command_file_metadata);
run_index ++; run (run_index);
break;
case 2: // load winner
(function () {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
run_index = (-10);
end = true;
that.fail(command,error);
};
cloned_option.onDone = function (result) {
loaded_file = result;
loaded_file.name = command.getPath();
run_index ++; run (run_index);
};
var newcommand = that.newCommand(
'loadDocument',
{path:command.getPath() + '.' +
command_file_metadata.winner.revision +
'.' + command_file_metadata.winner.owner,
option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
}());
break;
case 3: // load owner
(function () {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
run_index = (-10);
end = true;
that.fail(command,error);
};
cloned_option.onDone = function (result) {
loaded_file = result;
loaded_file.name = command.getPath();
run_index ++; run (run_index);
};
if (!command_file_metadata.owner[owner]) {
cloned_option.onFail ({status:404,
statusText:'Not Found',
message:'Document not found.'});
return;
} }
var newcommand = that.newCommand( am.call(o,'updateLocalMetadata');
'loadDocument', },function (error) {
{path:command.getPath() + '.' + am.end();
command_file_metadata.owner[owner].revision + am.call(o,'fail',[error]);
'.' + owner, });
option:cloned_option}); };
that.addJob ( that.newStorage (priv.secondstorage_spec), o.updateLocalMetadata = function () {
newcommand ); LocalOrCookieStorage.setItem (local_metadata_file_name,
}()); command_file_metadata);
break; am.call(o,'done');
case 100: };
if (!end) { o.loadWinner = function () {
end = true; var cloned_option = command.cloneOption ();
that.done(loaded_file); cloned_option.onResponse = function () {};
return; cloned_option.onFail = function (error) {
} am.call(o,'fail',[error]);
break; am.end();
default: break; };
cloned_option.onDone = function (result) {
loaded_file = result;
loaded_file.name = command.getPath();
am.call(o,'done');
};
var newcommand = that.newCommand(
'loadDocument',
{path:command.getPath() + '.' +
command_file_metadata.winner.revision +
'.' + command_file_metadata.winner.owner,
option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
};
o.loadOwner = function () {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
am.end();
am.call(o,'fail',[error]);
};
cloned_option.onDone = function (result) {
loaded_file = result;
loaded_file.name = command.getPath();
am.call(o,'done');
};
if (!command_file_metadata.owner[owner]) {
cloned_option.onFail ({status:404,
statusText:'Not Found',
message:'Document not found.'});
return;
} }
var newcommand = that.newCommand(
'loadDocument',
{path:command.getPath() + '.' +
command_file_metadata.owner[owner].revision +
'.' + owner,
option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
}; };
run (0); o.fail = function (error) {
am.neverCall(o,'fail');
am.neverCall(o,'done');
command.setMaxRetry(1);
that.fail(error);
};
o.done = function () {
am.neverCall(o,'done');
am.neverCall(o,'fail');
that.done(loaded_file);
};
am.call(o,'loadMetadataFromDistant');
}; };
/** /**
...@@ -426,14 +432,16 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -426,14 +432,16 @@ var newConflictManagerStorage = function ( spec, my ) {
* @method getDocumentList * @method getDocumentList
*/ */
that.getDocumentList = function (command) { that.getDocumentList = function (command) {
var command_file_metadata_list = [], // distant files metadata var o = {}, am = priv.newAsyncModule(),
command_file_metadata_list = [], // distant files metadata
result_list = [], result_list = [],
end = false, nb_loaded_file = 0, nb_loaded_file = 0;
_1 = function () { o.retreiveList = function () {
var cloned_option = command.cloneOption (); var cloned_option = command.cloneOption ();
cloned_option.metadata_only = false;
cloned_option.onResponse = function () {}; cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) { cloned_option.onFail = function (error) {
that.fail(command,error); am.call(o,'fail',[error]);
}; };
cloned_option.onDone = function (result) { cloned_option.onDone = function (result) {
var i; var i;
...@@ -458,18 +466,20 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -458,18 +466,20 @@ var newConflictManagerStorage = function ( spec, my ) {
} }
} }
if (command.getOption('metadata_only')) { if (command.getOption('metadata_only')) {
that.done(command_file_metadata_list); am.call(o,'done',[command_file_metadata_list]);
} else { } else {
if (result.length === 0) { if (result.length === 0) {
return that.done([]); return that.done([]);
} }
am.wait(o,'done',command_file_metadata_list.length-1);
for (i = 0; i < command_file_metadata_list.length; i+= 1) { for (i = 0; i < command_file_metadata_list.length; i+= 1) {
LocalOrCookieStorage.setItem ( LocalOrCookieStorage.setItem (
command_file_metadata_list[i].name + '.metadata', command_file_metadata_list[i].name + '.metadata',
result_list[i]); result_list[i]);
loadFile(command_file_metadata_list[i], am.call(o,'loadFile',[
result_list[i].winner.revision, command_file_metadata_list[i],
result_list[i].winner.owner); result_list[i].winner.revision,
result_list[i].winner.owner]);
} }
that.end(); that.end();
} }
...@@ -480,24 +490,17 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -480,24 +490,17 @@ var newConflictManagerStorage = function ( spec, my ) {
option:cloned_option}); option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec), that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand ); newcommand );
}, loadFile = function (doc,revision,owner) { };
o.loadFile = function (doc,revision,owner) {
var cloned_option = command.cloneOption (); var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {}; cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) { cloned_option.onFail = function (error) {
if (!end) { am.call(o,'fail',[error]);
end = true; am.end();
that.fail(command,error);
}
}; };
cloned_option.onDone = function (result) { cloned_option.onDone = function (result) {
if (!end) { doc.content = result.content;
doc.content = result.content; am.call(o,'done',[command_file_metadata_list]);
nb_loaded_file ++;
if (command_file_metadata_list.length === nb_loaded_file) {
end = true;
that.done(command_file_metadata_list);
}
}
}; };
var newcommand = that.newCommand( var newcommand = that.newCommand(
'loadDocument', 'loadDocument',
...@@ -506,7 +509,18 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -506,7 +509,18 @@ var newConflictManagerStorage = function ( spec, my ) {
that.addJob ( that.newStorage (priv.secondstorage_spec), that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand ); newcommand );
}; };
_1(); o.fail = function (error) {
am.neverCall(o,'fail');
am.neverCall(o,'done');
command.setMaxRetry(1);
that.fail(error);
};
o.done = function (value) {
am.neverCall(o,'done');
am.neverCall(o,'fail');
that.done(value);
};
am.call(o,'retreiveList');
}; };
/** /**
...@@ -514,194 +528,192 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -514,194 +528,192 @@ var newConflictManagerStorage = function ( spec, my ) {
* @method removeDocument * @method removeDocument
*/ */
that.removeDocument = function (command) { that.removeDocument = function (command) {
var metadata_file_name = command.getPath() + '.metadata', var o = {}, am = priv.newAsyncModule(),
metadata_file_name = command.getPath() + '.metadata',
local_metadata_file_name = local_namespace + metadata_file_name, local_metadata_file_name = local_namespace + metadata_file_name,
local_file_metadata = {}, local_file_metadata = {},
command_file_metadata = {}, // distant file.metadata command_file_metadata = {}, // distant file.metadata
run_index = 0, previous_revision = 0, previous_revision = 0,
end = false, is_a_new_file = false, is_a_new_file = false;
run = function (index) {
switch (index) { o.updateLocalMetadata = function () {
case 0: var new_owner_object = {revision:0,hash:'',
run_index = 3; last_modified:0,
run (2); creation_date:0};
run (1); local_file_metadata =
break; LocalOrCookieStorage.getItem (local_metadata_file_name);
case 1: // update local metadata if ( local_file_metadata ) {
var new_owner_object = {revision:0,hash:'', // if metadata already exists
last_modified:0, if ( !local_file_metadata.owner[priv.username] ) {
creation_date:0};
local_file_metadata =
LocalOrCookieStorage.getItem (local_metadata_file_name);
if ( local_file_metadata ) {
// if metadata already exists
if ( !local_file_metadata.owner[priv.username] ) {
local_file_metadata.owner[priv.username] =
new_owner_object;
}
} else {
local_file_metadata = {
winner: {},
owner: {},
conflict_list: []
};
local_file_metadata.winner = {
revision:0,owner:priv.username,hash:''};
local_file_metadata.owner[priv.username] = local_file_metadata.owner[priv.username] =
new_owner_object; new_owner_object;
} }
run_index ++; run (run_index); } else {
break; local_file_metadata = {
case 2: // load metadata from distant winner: {},
priv.loadMetadataFromDistant ( owner: {},
command,metadata_file_name, conflict_list: []
function (result) {
command_file_metadata = JSON.parse (result.content);
run_index++; run (run_index);
},function (error) {
if (error.status === 404) {
command_file_metadata = local_file_metadata;
is_a_new_file = true;
run_index++; run (run_index);
return;
}
run_index = (-10);
end = true;
that.fail(command,error);
});
break;
case 5:
var updateMetadataCommon = function () {
if (command_file_metadata.owner[priv.username]) {
previous_revision = command_file_metadata.owner[
priv.username].revision;
delete command_file_metadata.owner[priv.username];
}
};
var updateCommandMetadataNotOnConflict = function () {
updateMetadataCommon();
command_file_metadata.winner.owner = priv.username;
command_file_metadata.winner.revision = 0;
command_file_metadata.winner.hash = '';
};
var updateCommandMetadataOnConflict = function () {
updateMetadataCommon ();
}; };
// if this is a new file local_file_metadata.winner = {
if (is_a_new_file) { revision:0,owner:priv.username,hash:''};
LocalOrCookieStorage.deleteItem (local_metadata_file_name); local_file_metadata.owner[priv.username] =
return that.done(); new_owner_object;
} }
// if no conflict am.call(o,'checkForConflicts');
if (local_file_metadata.winner.revision === };
command_file_metadata.winner.revision && o.loadMetadataFromDistant = function () {
local_file_metadata.winner.hash === priv.loadMetadataFromDistant (
command_file_metadata.winner.hash) { command,metadata_file_name,
// OK! Now, update distant metadata, store them and remove function (result) {
updateCommandMetadataNotOnConflict(); command_file_metadata = JSON.parse (result.content);
LocalOrCookieStorage.setItem (local_metadata_file_name, am.call(o,'checkForConflicts');
command_file_metadata); },function (error) {
run_index = 98; if (error.status === 404) {
run (6); // save metadata command_file_metadata = local_file_metadata;
run (7); // remove document revision is_a_new_file = true;
} else { am.call(o,'checkForConflicts');
// if conflict return;
var conflict_object = {
label: 'revision',
path: command.getPath(),
conflict_owner: {
name: command_file_metadata.winner.owner,
revision: command_file_metadata.winner.revision,
hash: command_file_metadata.winner.hash}
},
// gen hash
conflict_hash = hex_sha256 (JSON.stringify (
conflict_object));
conflict_object.hash = conflict_hash;
// browse known conflict list
var i, known_conflict_list =
command.getOption('known_conflict_list') || [];
var compare_fun = function (v) {
return (v.hash === conflict_hash);
};
for (i = 0; i < known_conflict_list.length; i+= 1) {
// if known conflict
if (known_conflict_list[i].hash ===
conflict_hash) {
command_file_metadata.conflict_list =
priv.removeValuesFromArrayWhere(
command_file_metadata.conflict_list,
compare_fun);
updateCommandMetadataNotOnConflict();
run_index = 98;
run (6);
run (7);
return;
}
} }
updateCommandMetadataOnConflict(); am.call(o,'fail',[error]);
// if unknown conflict am.end();
command_file_metadata.conflict_list.push (conflict_object); });
run_index = (-10); };
end = true; o.checkForConflicts = function () {
run (6); // save metadata var updateMetadataCommon = function () {
run (7); // remove document revision if (command_file_metadata.owner[priv.username]) {
that.fail(command); // TODO previous_revision = command_file_metadata.owner[
command.getOption('onConflict')(conflict_object); priv.username].revision;
delete command_file_metadata.owner[priv.username];
} }
break; };
case 6: var updateCommandMetadataNotOnConflict = function () {
priv.saveMetadataToDistant ( updateMetadataCommon();
command,metadata_file_name,command_file_metadata, command_file_metadata.winner.owner = priv.username;
function () { command_file_metadata.winner.revision = 0;
run_index ++; run (run_index); command_file_metadata.winner.hash = '';
},function (error) { };
run_index = (-10); var updateCommandMetadataOnConflict = function () {
end = true; updateMetadataCommon ();
that.fail(command,error); };
}); // if this is a new file
break; if (is_a_new_file) {
case 7: LocalOrCookieStorage.deleteItem (local_metadata_file_name);
(function () { return am.call(o,'done');
if ( previous_revision !== 0 && ( }
!command_file_metadata.owner[priv.username] || // if no conflict
previous_revision !== if (local_file_metadata.winner.revision ===
command_file_metadata.owner[ command_file_metadata.winner.revision &&
priv.username].revision ) ) { local_file_metadata.winner.hash ===
var cloned_option = command.cloneOption (); command_file_metadata.winner.hash) {
cloned_option.onResponse = function () {}; // OK! Now, update distant metadata, store them and remove
cloned_option.onFail = function (error) { updateCommandMetadataNotOnConflict();
run_index = (-10); LocalOrCookieStorage.setItem (local_metadata_file_name,
end = true; command_file_metadata);
that.fail(command,error); am.wait(o,'done',1);
}; am.call(o,'saveMetadata');
cloned_option.onDone = function () { am.call(o,'removeRevision');
run_index ++; run (run_index); } else {
}; // if conflict
var newcommand = that.newCommand( var conflict_object = {
'removeDocument', label: 'revision',
{path:command.getPath() + '.' + path: command.getPath(),
previous_revision + '.' + priv.username, method: 'removeDocument',
option:cloned_option}); owner: priv.username,
newcommand.setMaxRetry (0); // inf conflict_owner: {
that.addJob ( that.newStorage (priv.secondstorage_spec), name: command_file_metadata.winner.owner,
newcommand ); revision: command_file_metadata.winner.revision,
} else { hash: command_file_metadata.winner.hash}
run_index ++; run (run_index); },
// gen hash
conflict_hash = hex_sha256 (JSON.stringify (
conflict_object));
conflict_object.hash = conflict_hash;
// browse known conflict list
var i, known_conflict_list =
command.getOption('known_conflict_list') || [];
var compare_fun = function (v) {
return (v.hash === conflict_hash);
};
for (i = 0; i < known_conflict_list.length; i+= 1) {
// if known conflict
if (known_conflict_list[i].hash ===
conflict_hash) {
command_file_metadata.conflict_list =
priv.removeValuesFromArrayWhere(
command_file_metadata.conflict_list,
compare_fun);
updateCommandMetadataNotOnConflict();
am.wait(o,'done',1);
am.call(o,'saveMetadata');
am.call(o,'removeRevision');
return;
} }
}());
break;
case 100:
if (!end) {
end = true;
that.done();
return;
} }
break; updateCommandMetadataOnConflict();
default: break; // if unknown conflict
command_file_metadata.conflict_list.push (conflict_object);
am.neverCall(o,'done');
am.call(o,'saveMetadata');
// am.call(o,'removeRevision');
am.call(o,'fail',[
{status:0,statusText:'Revision Conflict',
message:'Someone has already modified this document.'}]);
var onConflict = command.getOption('onConflict') ||
function (){};
onConflict(conflict_object);
} }
}; };
run (0); o.saveMetadata = function () {
priv.saveMetadataToDistant (
command,metadata_file_name,command_file_metadata,
function () {
am.call(o,'done');
},function (error) {
am.call(o,'fail',[error]);
am.end();
});
};
o.removeRevision = function () {
if ( previous_revision !== 0 && (
!command_file_metadata.owner[priv.username] ||
previous_revision !==
command_file_metadata.owner[
priv.username].revision ) ) {
var cloned_option = command.cloneOption ();
cloned_option.onResponse = function () {};
cloned_option.onFail = function (error) {
am.call(o,'fail',[error]);
am.end();
};
cloned_option.onDone = function () {
am.call(o,'done');
};
var newcommand = that.newCommand(
'removeDocument',
{path:command.getPath() + '.' +
previous_revision + '.' + priv.username,
option:cloned_option});
newcommand.setMaxRetry (0); // inf
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
} else {
am.call(o,'done');
}
};
o.fail = function (error) {
am.neverCall(o,'fail');
am.neverCall(o,'done');
command.setMaxRetry(1);
that.fail(error);
};
o.done = function () {
am.neverCall(o,'done');
am.neverCall(o,'fail');
that.done();
};
am.wait(o,'checkForConflicts',1);
am.call(o,'loadMetadataFromDistant');
am.call(o,'updateLocalMetadata');
}; };
return that; return that;
......
var newCryptedStorage = function ( spec, my ) { var newCryptedStorage = function ( spec, my ) {
var that = Jio.storage( spec, my, 'handler' ), priv = {}; var that = Jio.storage( spec, my, 'handler' ), priv = {};
var is_valid_storage = spec.storage || false;
priv.username = spec.username || ''; priv.username = spec.username || '';
priv.password = spec.password || ''; priv.password = spec.password || '';
priv.secondstorage_spec = spec.storage || {type:'base'}; priv.secondstorage_spec = spec.storage || {type:'base'};
priv.secondstorage_string = JSON.stringify (priv.secondstorage_string);
var super_serialized = that.serialized; var super_serialized = that.serialized;
that.serialized = function () { that.serialized = function () {
var o = super_serialized(); var o = super_serialized();
o.username = priv.username; o.username = priv.username;
o.password = priv.password; o.password = priv.password;
o.storage = priv.secondstorage_string;
return o; return o;
}; };
that.validateState = function () { that.validateState = function () {
if (priv.username && if (priv.username && is_valid_storage) {
JSON.stringify (priv.secondstorage_spec) ===
JSON.stringify ({type:'base'})) {
return ''; return '';
} }
return 'Need at least two parameters: "username" and "storage".'; return 'Need at least two parameters: "username" and "storage".';
...@@ -44,8 +46,8 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -44,8 +46,8 @@ var newCryptedStorage = function ( spec, my ) {
priv.encrypt = function (data,callback,index) { priv.encrypt = function (data,callback,index) {
// end with a callback in order to improve encrypt to an // end with a callback in order to improve encrypt to an
// asynchronous encryption. // asynchronous encryption.
var tmp = sjcl.encrypt (that.getStorageUserName()+':'+ var tmp = sjcl.encrypt (priv.username+':'+
that.getStoragePassword(), data, priv.password, data,
priv.encrypt_param_object); priv.encrypt_param_object);
callback(JSON.parse(tmp).ct,index); callback(JSON.parse(tmp).ct,index);
}; };
...@@ -54,8 +56,8 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -54,8 +56,8 @@ var newCryptedStorage = function ( spec, my ) {
param.ct = data || ''; param.ct = data || '';
param = JSON.stringify (param); param = JSON.stringify (param);
try { try {
tmp = sjcl.decrypt (that.getStorageUserName()+':'+ tmp = sjcl.decrypt (priv.username+':'+
that.getStoragePassword(), priv.password,
param); param);
} catch (e) { } catch (e) {
callback({status:0,statusText:'Decrypt Fail', callback({status:0,statusText:'Decrypt Fail',
...@@ -70,7 +72,7 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -70,7 +72,7 @@ var newCryptedStorage = function ( spec, my ) {
* @method saveDocument * @method saveDocument
*/ */
that.saveDocument = function (command) { that.saveDocument = function (command) {
var new_file_name, newfilecontent, var new_file_name, new_file_content,
_1 = function () { _1 = function () {
priv.encrypt(command.getPath(),function(res) { priv.encrypt(command.getPath(),function(res) {
new_file_name = res; new_file_name = res;
...@@ -79,21 +81,21 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -79,21 +81,21 @@ var newCryptedStorage = function ( spec, my ) {
}, },
_2 = function () { _2 = function () {
priv.encrypt(command.getContent(),function(res) { priv.encrypt(command.getContent(),function(res) {
newfilecontent = res; new_file_content = res;
_3(); _3();
}); });
}, },
_3 = function () { _3 = function () {
var settings = that.cloneOption(), newcommand, newstorage; var settings = command.cloneOption(), newcommand;
settings.onResponse = function (){}; settings.onResponse = function (){};
settings.onDone = function () { that.done(); }; settings.onDone = function () { that.done(); };
settings.onFail = function (r) { that.fail(r); }; settings.onFail = function (r) { that.fail(r); };
newcommand = that.newCommand( newcommand = that.newCommand(
{path:new_file_name, 'saveDocument',
content:newfilecontent, {path:new_file_name,content:new_file_content,option:settings});
option:settings}); that.addJob (
newstorage = that.newStorage( priv.secondstorage_spec ); that.newStorage( priv.secondstorage_spec ),
that.addJob ( newstorage, newcommand ); newcommand );
}; };
_1(); _1();
}; // end saveDocument }; // end saveDocument
...@@ -111,22 +113,22 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -111,22 +113,22 @@ var newCryptedStorage = function ( spec, my ) {
}); });
}, },
_2 = function () { _2 = function () {
var settings = command.cloneOption(), newcommand, newstorage; var settings = command.cloneOption(), newcommand;
settings.onResponse = function(){}; settings.onResponse = function(){};
settings.onFail = loadOnFail; settings.onFail = loadOnFail;
settings.onDone = loadOnDone; settings.onDone = loadOnDone;
newcommand = that.newCommand ( newcommand = that.newCommand (
{path:new_file_name, 'loadDocument',
option:settings}); {path:new_file_name,option:settings});
newstorage = that.newStorage ( priv.secondstorage_spec ); that.addJob (
that.addJob ( newstorage, newcommand ); that.newStorage ( priv.secondstorage_spec ), newcommand );
}, },
loadOnDone = function (result) { loadOnDone = function (result) {
result.name = command.getPath(); result.name = command.getPath();
if (command.getOption('metadata_only')) { if (command.getOption('metadata_only')) {
that.done(result); that.done(result);
} else { } else {
priv.decrypt (result.content,function(res){ priv.decrypt (result.content, function(res){
if (typeof res === 'object') { if (typeof res === 'object') {
that.fail({status:0,statusText:'Decrypt Fail', that.fail({status:0,statusText:'Decrypt Fail',
message:'Unable to decrypt'}); message:'Unable to decrypt'});
...@@ -140,11 +142,11 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -140,11 +142,11 @@ var newCryptedStorage = function ( spec, my ) {
}); });
} }
}, },
loadOnFail = function (result) { loadOnFail = function (error) {
// NOTE : we can re create an error object instead of // NOTE : we can re create an error object instead of
// keep the old ex:status=404,message="document 1y59gyl8g // keep the old ex:status=404,message="document 1y59gyl8g
// not found in localStorage"... // not found in localStorage"...
that.fail(result); that.fail(error);
}; };
_1(); _1();
}; // end loadDocument }; // end loadDocument
...@@ -156,16 +158,16 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -156,16 +158,16 @@ var newCryptedStorage = function ( spec, my ) {
that.getDocumentList = function (command) { that.getDocumentList = function (command) {
var new_job, i, l, cpt = 0, array, ok = true, var new_job, i, l, cpt = 0, array, ok = true,
_1 = function () { _1 = function () {
var newcommand = command.clone(), var newcommand = command.clone();
newstorage = that.newStorage ( priv.secondstorage_spec );
newcommand.onResponseDo (getListOnResponse); newcommand.onResponseDo (getListOnResponse);
newcommand.onDoneDo (function(){}); newcommand.onDoneDo (function(){});
newcommand.onFailDo (function(){}); newcommand.onFailDo (function(){});
that.addJob ( new_job ); that.addJob (
that.newStorage ( priv.secondstorage_spec ), newcommand );
}, },
getListOnResponse = function (result) { getListOnResponse = function (result) {
if (result.status.isDone()) { if (result.status.isDone()) {
array = result.return_value; array = result.value;
for (i = 0, l = array.length; i < l; i+= 1) { for (i = 0, l = array.length; i < l; i+= 1) {
// cpt--; // cpt--;
priv.decrypt (array[i].name, priv.decrypt (array[i].name,
...@@ -182,6 +184,7 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -182,6 +184,7 @@ var newCryptedStorage = function ( spec, my ) {
cpt++; cpt++;
if (typeof res === 'object') { if (typeof res === 'object') {
if (ok) { if (ok) {
ok = false;
that.fail({status:0,statusText:'Decrypt Fail', that.fail({status:0,statusText:'Decrypt Fail',
message:'Unable to decrypt.'}); message:'Unable to decrypt.'});
} }
...@@ -201,23 +204,27 @@ var newCryptedStorage = function ( spec, my ) { ...@@ -201,23 +204,27 @@ var newCryptedStorage = function ( spec, my ) {
* Removes a document. * Removes a document.
* @method removeDocument * @method removeDocument
*/ */
that.removeDocument = function () { that.removeDocument = function (command) {
var new_job, new_file_name, var new_file_name,
_1 = function () { _1 = function () {
priv.encrypt(that.getFileName(),function(res) { priv.encrypt(command.getPath(),function(res) {
new_file_name = res; new_file_name = res;
_2(); _2();
}); });
}, },
_2 = function () { _2 = function () {
new_job = that.cloneJob(); var cloned_option = command.cloneOption();
new_job.name = new_file_name; cloned_option.onResponse = removeOnResponse;
new_job.storage = that.getSecondStorage(); cloned_option.onFail = function () {};
new_job.onResponse = removeOnResponse; cloned_option.onDone = function () {};
that.addJob(new_job); that.addJob(that.newStorage(priv.secondstorage_spec),
that.newCommand(
'removeDocument',
{path:new_file_name,
option:cloned_option}));
}, },
removeOnResponse = function (result) { removeOnResponse = function (result) {
if (result.status === 'done') { if (result.status.isDone()) {
that.done(); that.done();
} else { } else {
that.fail(result.error); that.fail(result.error);
......
...@@ -1007,177 +1007,322 @@ test ('Remove document', function () { ...@@ -1007,177 +1007,322 @@ test ('Remove document', function () {
o.jio.stop(); o.jio.stop();
}); });
// module ('Jio CryptedStorage'); module ('Jio CryptedStorage');
// test ('Check name availability' , function () { test ('Document save' , function () {
// var o = {}, clock = this.sandbox.useFakeTimers(); var o = {}, clock = this.sandbox.useFakeTimers();
// o.jio=JIO.newJio({type:'crypted', o.jio=JIO.newJio({type:'crypt',
// storage:{type:'local', username:'cryptsave',
// user_name:'cryptcheck'}}, password:'mypwd',
// {ID:'jiotests'}); storage:{type:'local',
// o.f = function (result) { username:'cryptsavelocal',
// deepEqual (result.return_value,true,'name must be available'); applicationname:'jiotests'}});
// }; o.f = function (result) {
// this.spy(o,'f'); deepEqual (result.status.getLabel(),'done','save ok');
// o.jio.checkNameAvailability({user_name:'cryptcheck', };
// max_tries:1,onResponse:o.f}); this.spy(o,'f');
// clock.tick(1000); o.jio.saveDocument('testsave','contentoftest',{
// if (!o.f.calledOnce) { max_retry:1,onResponse:o.f});
// ok (false, 'no response / too much results'); clock.tick(1000);
// } if (!o.f.calledOnce) {
// o.jio.stop(); ok (false, 'no response / too much results');
// }); }
// encrypt 'testsave' with 'cryptsave:mypwd' password
o.tmp = LocalOrCookieStorage.getItem(
'jio/local/cryptsavelocal/jiotests/rZx5PJxttlf9QpZER/5x354bfX54QFa1');
if (o.tmp) {
delete o.tmp.last_modified;
delete o.tmp.creation_date;
}
deepEqual (o.tmp,
{name:'rZx5PJxttlf9QpZER/5x354bfX54QFa1',
content:'upZkPIpitF3QMT/DU5jM3gP0SEbwo1n81rMOfLE'},
'Check if the document is realy encrypted');
o.jio.stop();
});
// test ('Document save' , function () { test ('Document Load' , function () {
// var o = {}, clock = this.sandbox.useFakeTimers(); var o = {}, clock = this.sandbox.useFakeTimers();
// o.jio=JIO.newJio({type:'crypted', o.jio=JIO.newJio({type:'crypt',
// user_name:'cryptsave', username:'cryptload',
// password:'mypwd', password:'mypwd',
// storage:{type:'local', storage:{type:'local',
// user_name:'cryptsavelocal'}}, username:'cryptloadlocal',
// {ID:'jiotests'}); applicationname:'jiotests'}});
// o.f = function (result) { o.f = function (result) {
// deepEqual (result.status,'done','save ok'); if (result.status.isDone()) {
// }; deepEqual (result.value,
// this.spy(o,'f'); {name:'testload',
// o.jio.saveDocument({name:'testsave',content:'contentoftest', content:'contentoftest',
// max_tries:1,onResponse:o.f}); last_modified:500,
// clock.tick(1000); creation_date:500},
// if (!o.f.calledOnce) { 'load ok');
// ok (false, 'no response / too much results'); } else {
// } ok (false ,'cannot load');
// // encrypt 'testsave' with 'cryptsave:mypwd' password }
// o.tmp = LocalOrCookieStorage.getItem( };
// 'jio/local/cryptsavelocal/jiotests/rZx5PJxttlf9QpZER/5x354bfX54QFa1'); this.spy(o,'f');
// if (o.tmp) { // encrypt 'testload' with 'cryptload:mypwd' password
// delete o.tmp.last_modified; // and 'contentoftest' with 'cryptload:mypwd'
// delete o.tmp.creation_date; o.doc = {name:'hiG4H80pwkXCCrlLl1X0BD0BfWLZwDUX',
// } content:'kSulH8Qo105dSKHcY2hEBXWXC9b+3PCEFSm1k7k',
// deepEqual (o.tmp, last_modified:500,creation_date:500};
// {name:'rZx5PJxttlf9QpZER/5x354bfX54QFa1', addFileToLocalStorage('cryptloadlocal','jiotests',o.doc);
// content:'upZkPIpitF3QMT/DU5jM3gP0SEbwo1n81rMOfLE'}, o.jio.loadDocument('testload',{
// 'Check if the document is realy crypted'); max_retry:1,onResponse:o.f});
// o.jio.stop(); clock.tick(1000);
// }); if (!o.f.calledOnce) {
ok (false, 'no response / too much results');
}
o.jio.stop();
});
// test ('Document Load' , function () { test ('Get Document List', function () {
// var o = {}, clock = this.sandbox.useFakeTimers(); var o = {}, clock = this.sandbox.useFakeTimers();
// o.jio=JIO.newJio({type:'crypted', o.jio=JIO.newJio({type:'crypt',
// user_name:'cryptload', username:'cryptgetlist',
// password:'mypwd', password:'mypwd',
// storage:{type:'local', storage:{type:'local',
// user_name:'cryptloadlocal'}}, username:'cryptgetlistlocal',
// {ID:'jiotests'}); applicationname:'jiotests'}});
// o.f = function (result) { o.f = function (result) {
// if (result.status === 'done') { if (result.status.isDone()) {
// deepEqual (result.return_value, deepEqual (objectifyDocumentArray(result.return_value),
// {name:'testload', objectifyDocumentArray(o.doc_list),'Getting list');
// content:'contentoftest', } else {
// last_modified:500, console.warn (result);
// creation_date:500}, ok (false, 'Cannot get list');
// 'load ok'); }
// } else { };
// ok (false ,'cannot load'); this.spy(o,'f');
// } o.doc_list = [
// }; {name:'testgetlist1',last_modified:500,creation_date:200},
// this.spy(o,'f'); {name:'testgetlist2',last_modified:300,creation_date:300}
// // encrypt 'testload' with 'cryptload:mypwd' password ];
// // and 'contentoftest' with 'cryptload:mypwd' o.doc_encrypt_list = [
// LocalOrCookieStorage.setItem( {name:'541eX0WTMDw7rqIP7Ofxd1nXlPOtejxGnwOzMw',
// 'jio/local/cryptloadlocal/jiotests/hiG4H80pwkXCCrlLl1X0BD0BfWLZwDUX', content:'/4dBPUdmLolLfUaDxPPrhjRPdA',
// {name:'mRyQFcUvUKq6tLGUjBo34P3oc2LPxEju', last_modified:500,creation_date:200},
// content:'kSulH8Qo105dSKHcY2hEBXWXC9b+3PCEFSm1k7k', {name:'541eX0WTMDw7rqIMyJ5tx4YHWSyxJ5UjYvmtqw',
// last_modified:500,creation_date:500}); content:'/4FBALhweuyjxxD53eFQDSm4VA',
// o.jio.loadDocument({name:'testload', last_modified:300,creation_date:300}
// max_tries:1,onResponse:o.f}); ];
// clock.tick(1000); // encrypt with 'cryptgetlist:mypwd' as password
// if (!o.f.calledOnce) { LocalOrCookieStorage.setItem(
// ok (false, 'no response / too much results'); 'jio/local_file_name_array/cryptgetlistlocal/jiotests',
// } [o.doc_encrypt_list[0].name,o.doc_encrypt_list[1].name]);
// o.jio.stop(); LocalOrCookieStorage.setItem(
// LocalOrCookieStorage.deleteItem( 'jio/local/cryptgetlistlocal/jiotests/'+o.doc_encrypt_list[0].name,
// 'jio/local/cryptloadlocal/jiotests/hiG4H80pwkXCCrlLl1X0BD0BfWLZwDUX'); o.doc_encrypt_list[0]);
// }); LocalOrCookieStorage.setItem(
'jio/local/cryptgetlistlocal/jiotests/'+o.doc_encrypt_list[1].name,
o.doc_encrypt_list[1]);
o.jio.getDocumentList({max_retry:1,onResponse:o.f});
clock.tick (3000);
if (!o.f.calledOnce) {
if (o.f.called) {
ok (false, 'too much results');
} else {
ok (false, 'no response');
}
}
clock.tick(1000);
o.jio.stop();
});
// test ('Get Document List', function () { test ('Remove document', function () {
// var o = {}, clock = this.sandbox.useFakeTimers(); var o = {}, clock = this.sandbox.useFakeTimers();
// o.jio=JIO.newJio({type:'crypted', o.jio=JIO.newJio({type:'crypt',
// user_name:'cryptgetlist', username:'cryptremove',
// password:'mypwd', password:'mypwd',
// storage:{type:'local', storage:{type:'local',
// user_name:'cryptgetlistlocal'}}, username:'cryptremovelocal',
// {ID:'jiotests'}); applicationname:'jiotests'}});
// o.f = function (result) { o.f = function (result) {
// if (result.status === 'done') { deepEqual (result.status.getLabel(),'done','Document remove');
// deepEqual (objectifyDocumentArray(result.return_value), };
// objectifyDocumentArray(o.doc_list),'Getting list'); this.spy(o,'f');
// } else { // encrypt with 'cryptremove:mypwd' as password
// console.warn (result); o.doc = {name:'JqCLTjyxQqO9jwfxD/lyfGIX+qA',
// ok (false, 'Cannot get list'); content:'LKaLZopWgML6IxERqoJ2mUyyO',
// } last_modified:500,creation_date:500};
// }; o.jio.removeDocument('file',{max_retry:1,onResponse:o.f});
// this.spy(o,'f'); clock.tick(1000);
// o.doc_list = [ if (!o.f.calledOnce){
// {name:'testgetlist1',last_modified:500,creation_date:200}, ok (false, 'no response / too much results');
// {name:'testgetlist2',last_modified:300,creation_date:300} }
// ]; o.jio.stop();
// o.doc_encrypt_list = [ });
// {name:'541eX0WTMDw7rqIP7Ofxd1nXlPOtejxGnwOzMw',
// content:'/4dBPUdmLolLfUaDxPPrhjRPdA',
// last_modified:500,creation_date:200},
// {name:'541eX0WTMDw7rqIMyJ5tx4YHWSyxJ5UjYvmtqw',
// content:'/4FBALhweuyjxxD53eFQDSm4VA',
// last_modified:300,creation_date:300}
// ];
// // encrypt with 'cryptgetlist:mypwd' as password
// LocalOrCookieStorage.setItem(
// 'jio/local_file_name_array/cryptgetlistlocal/jiotests',
// [o.doc_encrypt_list[0].name,o.doc_encrypt_list[1].name]);
// LocalOrCookieStorage.setItem(
// 'jio/local/cryptgetlistlocal/jiotests/'+o.doc_encrypt_list[0].name,
// o.doc_encrypt_list[0]);
// LocalOrCookieStorage.setItem(
// 'jio/local/cryptgetlistlocal/jiotests/'+o.doc_encrypt_list[1].name,
// o.doc_encrypt_list[1]);
// o.jio.getDocumentList({max_tries:1,onResponse:o.f});
// clock.tick (2000);
// if (!o.f.calledOnce) {
// ok (false, 'no response / too much results');
// }
// clock.tick(1000);
// o.jio.stop();
// });
// test ('Remove document', function () { module ('Jio ConflictManagerStorage');
// var o = {}, clock = this.sandbox.useFakeTimers();
// o.jio=JIO.newJio({type:'crypted', test ('Simple methods', function () {
// user_name:'cryptremove', var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this;
// password:'mypwd', o.spy = function(res,value,message,before) {
// storage:{type:'local', o.f = function(result) {
// user_name:'cryptremovelocal'}}, if (res === 'status') {
// {ID:'jiotests'}); deepEqual (result.status.getLabel(),value,message);
// o.f = function (result) { } else {
// deepEqual (result.status,'done','Document remove'); if (before) { before (result[res]); }
// }; deepEqual (result[res],value,message);
// this.spy(o,'f'); }
// // encrypt with 'cryptremove:mypwd' as password };
// LocalOrCookieStorage.setItem( o.t.spy(o,'f');
// 'jio/local_file_name_array/cryptremovelocal/jiotests', };
// ["JqCLTjyxQqO9jwfxD/lyfGIX+qA"]); o.tick = function (value, tick) {
// LocalOrCookieStorage.setItem( o.clock.tick(tick || 1000);
// 'jio/local/cryptremovelocal/jiotests/JqCLTjyxQqO9jwfxD/lyfGIX+qA', if (!o.f.calledOnce) {
// {"name":"JqCLTjyxQqO9jwfxD/lyfGIX+qA", if (o.f.called) {
// "content":"LKaLZopWgML6IxERqoJ2mUyyO", ok(false, 'too much results');
// "creation_date":500, } else {
// "last_modified":500}); ok(false, 'no response');
// o.jio.removeDocument({name:'file',max_tries:1,onResponse:o.f}); }
// clock.tick(1000); }
// if (!o.f.calledOnce){ };
// ok (false, 'no response / too much results'); o.jio = JIO.newJio({type:'conflictmanager',
// } username:'methods',
// o.jio.stop(); storage:{type:'local',
// }); username:'conflictmethods',
applicationname:'jiotests'}});
o.spy('status','done','saving "file.doc" with owner "methods".');
o.jio.saveDocument('file.doc','content1methods',
{onResponse:o.f,max_retry:1});
o.tick();
o.spy('value',{name:'file.doc',content:'content1methods'},
'loading document.',function (o) {
if (!o) { return; }
if (o.last_modified) {
delete o.last_modified;
delete o.creation_date;
}
});
o.jio.loadDocument('file.doc',{onResponse:o.f,max_retry:1});
o.tick();
o.spy('value',[{name:'file.doc'}],
'getting list.',function (a) {
var i;
if (!a) { return; }
for (i = 0; i < a.length; i+= 1) {
delete a[i].last_modified;
delete a[i].creation_date;
}
});
o.jio.getDocumentList('.',{onResponse:o.f,max_retry:1});
o.tick();
o.spy('status','done','removing document');
o.jio.removeDocument('file.doc',{onResponse:o.f,max_retry:1});
o.tick();
o.spy('status','fail','loading document fail.');
o.jio.loadDocument('file.doc',{onResponse:o.f,max_retry:1});
o.tick();
o.jio.stop();
});
test ('Revision Conflicts' , function () {
var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this;
o.spy = function(res,value,message,function_name) {
function_name = function_name || 'f';
o[function_name] = function(result) {
if (res === 'true') {
return ok(true,message);
}
if (res === 'status') {
deepEqual (result.status.getLabel(),value,message);
} else {
deepEqual (result[res],value,message);
}
};
o.t.spy(o,function_name);
};
o.tick = function (tick, function_name) {
function_name = function_name || 'f'
o.clock.tick(tick || 1000);
if (!o[function_name].calledOnce) {
if (o[function_name].called) {
ok(false, 'too much results');
} else {
ok(false, 'no response');
}
}
};
o.jio_me = JIO.newJio({type:'conflictmanager',
username:'me',
storage:{type:'local',
username:'conflictrevision',
applicationname:'jiotests'}});
o.jio_him = JIO.newJio({type:'conflictmanager',
username:'him',
storage:{type:'local',
username:'conflictrevision',
applicationname:'jiotests'}});
o.spy('status','done','saving "file.doc" with owner "me",'+
' first revision, no conflict.');
o.jio_me.saveDocument('file.doc','content1me',{onResponse:o.f,max_retry:1});
o.tick();
o.spy('status','done','saving "file.doc" with owner "me",'+
' second revision, no conflict.');
o.jio_me.saveDocument('file.doc','content2me',{onResponse:o.f,max_retry:1});
o.tick();
o.spy('status','done','loading "file.doc" with owner "him",'+
' last revision, no conflict.');
o.jio_him.loadDocument('file.doc',{onResponse:o.f,max_retry:1});
o.tick();
o.spy('status','done','saving "file.doc" with owner "him",'+
' next revision, no conflict.');
o.jio_him.saveDocument('file.doc','content1him',
{onResponse:o.f,max_retry:1});
o.tick();
o.spy('status','fail','saving "file.doc" with owner "me",'+
' third revision, conflict!');
o.c = function (conflict_object) {
o.co = conflict_object;
ok (true,'onConflict callback called once');
};
o.t.spy(o,'c');
o.jio_me.saveDocument('file.doc','content3me',{
onResponse:o.f,max_retry:1,onConflict:o.c});
o.tick(undefined,'f');
o.tick(0,'c');
if (!o.co) { ok(false,'impossible to continue the tests'); }
o.spy('status','done','solving conflict and save "file.doc" with owner'+
' "me", forth revision, no conflict.');
o.jio_me.saveDocument('file.doc','content4me',{
onResponse:o.f,max_retry:1,known_conflict_list:[o.co]});
o.tick();
o.spy('status','done','removing "file.doc" with owner "me",'+
' no conflict.');
o.jio_me.removeDocument('file.doc',{onResponse:o.f,max_retry:1});
o.tick();
o.spy('status','fail','saving "file.doc" with owner "him",'+
' any revision, conflict!');
o.c = function (conflict_object) {
o.co = conflict_object;
ok (true,'onConflict callback called once');
};
o.t.spy(o,'c');
o.jio_him.saveDocument('file.doc','content4him',{
onResponse:o.f,max_retry:1,onConflict:o.c});
o.tick(undefined,'f');
o.tick(0,'c');
o.jio_me.stop();
o.jio_him.stop();
});
}; // end thisfun }; // end thisfun
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment