Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
officejs
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
officejs
Commits
a4b6f558
Commit
a4b6f558
authored
May 09, 2012
by
Tristan Cavelier
Committed by
Sebastien Robin
May 18, 2012
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Redesigning pseudo classes inheritance.
Add tests.
parent
39836f9f
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
753 additions
and
503 deletions
+753
-503
jiotest/jio.dummystorages.js
jiotest/jio.dummystorages.js
+123
-174
jiotest/jiotests.js
jiotest/jiotests.js
+123
-32
jiotest/unhosted/jio.js
jiotest/unhosted/jio.js
+175
-119
jiotest/unhosted/jio.storage.js
jiotest/unhosted/jio.storage.js
+332
-178
No files found.
jiotest/jio.dummystorages.js
View file @
a4b6f558
...
...
@@ -4,6 +4,7 @@
// - dummyallok
// - dummyallfail
// - dummyallnotfound
// - dummyall3tries
;(
function
(
Jio
)
{
// check dependencies
...
...
@@ -15,47 +16,39 @@
////////////////////////////////////////////////////////////////////////////
// globals
var
jioGlobalObj
=
Jio
.
getGlobalObject
()
;
var
jioGlobalObj
=
Jio
.
getGlobalObject
()
,
// end globals
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Dummy Storage 1 : all ok
var
DummyStorageAllOk
=
function
(
args
)
{
this
.
job
=
args
.
job
;
};
DummyStorageAllOk
.
prototype
=
{
checkNameAvailability
:
function
()
{
DummyStorageAllOk
=
function
(
args
)
{
var
that
=
Jio
.
newBaseStorage
(
args
);
that
.
checkNameAvailability
=
function
()
{
// The [job.userName] IS available.
var
t
=
this
;
// wait a little in order to simulate asynchronous operation
setTimeout
(
function
()
{
t
.
done
(
true
);
t
hat
.
done
(
true
);
},
100
);
}
,
// end userNameAvailable
}
;
// end userNameAvailable
saveDocument
:
function
()
{
that
.
saveDocument
=
function
()
{
// Tells us that the document is saved.
var
t
=
this
;
// wait a little in order to simulate asynchronous saving
setTimeout
(
function
()
{
t
.
done
();
t
hat
.
done
();
},
100
);
}
,
// end saveDocument
}
;
// end saveDocument
loadDocument
:
function
()
{
that
.
loadDocument
=
function
()
{
// Returns a document object containing all information of the
// document and its content.
// document object is {'fileName':string,'fileContent':string,
// 'creationDate':date,'lastModified':date}
var
t
=
this
;
// wait a little in order to simulate asynchronous operation
setTimeout
(
function
()
{
var
doc
=
{
...
...
@@ -63,19 +56,17 @@
'
fileName
'
:
'
file
'
,
'
creationDate
'
:
10000
,
'
lastModified
'
:
15000
};
t
.
done
(
doc
);
t
hat
.
done
(
doc
);
},
100
);
}
,
// end loadDocument
}
;
// end loadDocument
getDocumentList
:
function
()
{
that
.
getDocumentList
=
function
()
{
// It returns a document array containing all the user documents
// informations, but not their content.
// the list is [object,object] -> object = {'fileName':string,
// 'lastModified':date,'creationDate':date}
var
t
=
this
;
setTimeout
(
function
()
{
var
list
=
[
{
'
fileName
'
:
'
file
'
,
...
...
@@ -85,257 +76,215 @@
'
creationDate
'
:
20000
,
'
lastModified
'
:
25000
}];
t
.
done
(
list
);
t
hat
.
done
(
list
);
},
100
);
}
,
// end getDocumentList
}
;
// end getDocumentList
removeDocument
:
function
()
{
that
.
removeDocument
=
function
()
{
// Remove a document from the storage.
var
t
=
this
;
setTimeout
(
function
()
{
t
.
done
();
t
hat
.
done
();
},
100
);
}
};
// add key to storageObjectType of global jio
Jio
.
addStorageType
(
'
dummyallok
'
,
function
(
options
)
{
return
new
DummyStorageAllOk
(
options
);
});
return
that
;
},
// end Dummy Storage All Ok
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Dummy Storage 2 : all fail
var
DummyStorageAllFail
=
function
(
args
)
{
this
.
job
=
args
.
job
;
};
DummyStorageAllFail
.
prototype
=
{
checkNameAvailability
:
function
()
{
// Fails to check [job.userName].
DummyStorageAllFail
=
function
(
args
)
{
var
that
=
Jio
.
newBaseStorage
(
args
);
var
t
=
this
;
that
.
checkNameAvailability
=
function
()
{
// Fails to check [job.userName].
// wait a little in order to simulate asynchronous operation
setTimeout
(
function
()
{
t
.
fail
(
'
Cannot check name availability.
'
,
0
);
t
hat
.
fail
(
'
Cannot check name availability.
'
,
0
);
},
100
);
}
,
// end userNameAvailable
}
;
// end userNameAvailable
saveDocument
:
function
()
{
that
.
saveDocument
=
function
()
{
// Tells us that the document is not saved.
var
t
=
this
;
// wait a little in order to simulate asynchronous saving
setTimeout
(
function
()
{
t
.
fail
(
'
Unable to save document.
'
,
0
);
t
hat
.
fail
(
'
Unable to save document.
'
,
0
);
},
100
);
}
,
// end saveDocument
}
;
// end saveDocument
loadDocument
:
function
()
{
that
.
loadDocument
=
function
()
{
// Returns a document object containing nothing.
// document object is {'fileName':string,'fileContent':string,
// 'creationDate':date,'lastModified':date}
var
t
=
this
;
// wait a little in order to simulate asynchronous operation
setTimeout
(
function
()
{
t
.
fail
(
'
Unable to load document.
'
,
0
);
t
hat
.
fail
(
'
Unable to load document.
'
,
0
);
},
100
);
}
,
// end loadDocument
}
;
// end loadDocument
getDocumentList
:
function
()
{
that
.
getDocumentList
=
function
()
{
// It returns nothing.
// the list is [object,object] -> object = {'fileName':string,
// 'lastModified':date,'creationDate':date}
var
t
=
this
;
setTimeout
(
function
()
{
t
.
fail
(
'
Cannot get document list.
'
,
0
);
t
hat
.
fail
(
'
Cannot get document list.
'
,
0
);
},
100
);
}
,
// end getDocumentList
}
;
// end getDocumentList
removeDocument
:
function
(
job
,
jobendcallback
)
{
that
.
removeDocument
=
function
(
)
{
// Remove a document from the storage.
// returns {'status':string,'message':string,'isRemoved':boolean}
// in the jobendcallback arguments.
var
t
=
this
;
setTimeout
(
function
()
{
t
.
fail
(
'
Unable to remove anything.
'
,
0
);
t
hat
.
fail
(
'
Unable to remove anything.
'
,
0
);
},
100
);
}
};
// add key to storageObjectType of global jio
Jio
.
addStorageType
(
'
dummyallfail
'
,
function
(
options
)
{
return
new
DummyStorageAllFail
(
options
);
});
return
that
;
},
// end Dummy Storage All Fail
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Dummy Storage 3 : all not found
var
DummyStorageAllNotFound
=
function
(
args
)
{
this
.
job
=
args
.
job
;
};
DummyStorageAllNotFound
.
prototype
=
{
checkNameAvailability
:
function
()
{
// [job.userName] not found, so the name is available.
DummyStorageAllNotFound
=
function
(
args
)
{
var
that
=
Jio
.
newBaseStorage
(
args
);
var
t
=
this
;
that
.
checkNameAvailability
=
function
()
{
// [job.userName] not found, so the name is available.
// wait a little in order to simulate asynchronous operation
setTimeout
(
function
()
{
t
.
done
(
true
);
t
hat
.
done
(
true
);
},
100
);
}
,
// end userNameAvailable
}
;
// end userNameAvailable
saveDocument
:
function
()
{
that
.
saveDocument
=
function
()
{
// Document does not exists yet, create it.
var
t
=
this
;
// wait a little in order to simulate asynchronous saving
setTimeout
(
function
()
{
t
.
done
();
t
hat
.
done
();
},
100
);
}
,
// end saveDocument
}
;
// end saveDocument
loadDocument
:
function
(
job
,
jobendcallback
)
{
that
.
loadDocument
=
function
(
)
{
// Returns a document object containing nothing.
// document object is {'fileName':string,'fileContent':string,
// 'creationDate':date,'lastModified':date}
var
t
=
this
;
// wait a little in order to simulate asynchronous operation
setTimeout
(
function
()
{
t
.
fail
(
'
Document not found.
'
,
404
);
t
hat
.
fail
(
'
Document not found.
'
,
404
);
},
100
);
}
,
// end loadDocument
}
;
// end loadDocument
getDocumentList
:
function
(
job
,
jobendcallback
)
{
that
.
getDocumentList
=
function
(
)
{
// It returns nothing.
// the list is [object,object] -> object = {'fileName':string,
// 'lastModified':date,'creationDate':date}
var
t
=
this
;
setTimeout
(
function
()
{
t
.
fail
(
'
User collection not found.
'
,
404
);
t
hat
.
fail
(
'
User collection not found.
'
,
404
);
},
100
);
}
,
// end getDocumentList
}
;
// end getDocumentList
removeDocument
:
function
(
job
,
jobendcallback
)
{
that
.
removeDocument
=
function
(
)
{
// Remove a document from the storage.
// returns {'status':string,'message':string,'isRemoved':boolean}
// in the jobendcallback arguments.
var
t
=
this
;
setTimeout
(
function
()
{
t
.
done
();
t
hat
.
done
();
},
100
);
}
};
// add key to storageObjectType of global jio
Jio
.
addStorageType
(
'
dummyallnotfound
'
,
function
(
options
)
{
return
new
DummyStorageAllNotFound
(
options
);
});
return
that
;
},
// end Dummy Storage All Not Found
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Dummy Storage 4 : all 3 tries
var
DummyStorageAll3Tries
=
function
(
args
)
{
this
.
job
=
args
.
job
;
};
DummyStorageAllNotFound
.
prototype
=
{
checkNameAvailability
:
function
()
{
// [job.userName] not found, so the name is available.
var
t
=
this
;
// wait a little in order to simulate asynchronous operation
setTimeout
(
function
()
{
t
.
done
(
true
);
},
100
);
},
// end userNameAvailable
saveDocument
:
function
()
{
// Document does not exists yet, create it.
var
t
=
this
;
// wait a little in order to simulate asynchronous saving
setTimeout
(
function
()
{
t
.
done
();
},
100
);
},
// end saveDocument
loadDocument
:
function
()
{
// Returns a document object containing nothing.
// document object is {'fileName':string,'fileContent':string,
// 'creationDate':date,'lastModified':date}
var
t
=
this
;
DummyStorageAll3Tries
=
function
(
args
)
{
var
that
=
Jio
.
newBaseStorage
(
args
),
priv
=
{};
priv
.
doJob
=
function
(
ifokreturn
)
{
// wait a little in order to simulate asynchronous operation
setTimeout
(
function
()
{
t
.
fail
(
'
Document not found.
'
,
404
);
priv
.
Try3OKElseFail
(
that
.
cloneJob
().
tries
,
ifokreturn
);
},
100
);
},
// end loadDocument
getDocumentList
:
function
()
{
// It returns nothing.
// the list is [object,object] -> object = {'fileName':string,
// 'lastModified':date,'creationDate':date}
};
priv
.
Try3OKElseFail
=
function
(
tries
,
ifokreturn
)
{
if
(
tries
===
3
)
{
return
that
.
done
(
ifokreturn
);
}
if
(
tries
<
3
)
{
return
that
.
fail
(
''
+
(
3
-
tries
)
+
'
tries left.
'
);
}
if
(
tries
>
3
)
{
return
that
.
fail
(
'
Too much tries.
'
);
}
};
var
t
=
this
;
that
.
checkNameAvailability
=
function
()
{
priv
.
doJob
(
true
);
};
// end userNameAvailable
setTimeout
(
function
()
{
t
.
fail
(
'
User collection not found.
'
,
404
);
},
100
);
},
// end getDocumentList
that
.
saveDocument
=
function
()
{
priv
.
doJob
();
};
// end saveDocument
removeDocument
:
function
()
{
// Remove a document from the storage.
that
.
loadDocument
=
function
()
{
priv
.
doJob
({
'
fileContent
'
:
'
content2
'
,
'
fileName
'
:
'
file
'
,
'
creationDate
'
:
11000
,
'
lastModified
'
:
17000
});
};
// end loadDocument
// returns {'status':string,'message':string,'isRemoved':boolean}
// in the jobendcallback arguments.
that
.
getDocumentList
=
function
()
{
priv
.
doJob
([{
'
fileName
'
:
'
file
'
,
'
creationDate
'
:
10000
,
'
lastModified
'
:
15000
},
{
'
fileName
'
:
'
memo
'
,
'
creationDate
'
:
20000
,
'
lastModified
'
:
25000
}
]);
};
// end getDocumentList
var
t
=
this
;
that
.
removeDocument
=
function
()
{
priv
.
doJob
();
};
// end removeDocument
setTimeout
(
function
()
{
t
.
done
();
},
100
);
}
return
that
;
};
// end Dummy Storage All 3 Tries
////////////////////////////////////////////////////////////////////////////
// add key to storageObjectType of global jio
Jio
.
addStorageType
(
'
dummyallok
'
,
function
(
options
)
{
return
DummyStorageAllOk
(
options
);
});
Jio
.
addStorageType
(
'
dummyallfail
'
,
function
(
options
)
{
return
DummyStorageAllFail
(
options
);
});
Jio
.
addStorageType
(
'
dummyallnotfound
'
,
function
(
options
)
{
return
DummyStorageAllNotFound
(
options
);
});
Jio
.
addStorageType
(
'
dummyall3tries
'
,
function
(
options
)
{
return
new
DummyStorageAll3Tries
(
options
);
return
DummyStorageAll3Tries
(
options
);
});
// end Dummy Storage All 3 Tries
////////////////////////////////////////////////////////////////////////////
})(
JIO
);
jiotest/jiotests.js
View file @
a4b6f558
...
...
@@ -142,12 +142,16 @@ test ('Simple Job Elimination', function () {
id
=
o
.
jio
.
id
;
o
.
jio
.
saveDocument
({
'
fileName
'
:
'
file
'
,
'
fileContent
'
:
'
content
'
,
'
callback
'
:
o
.
f1
,
'
maxtries
'
:
1
});
console
.
log
(
localStorage
.
getItem
(
'
jio/jobObject/
'
+
id
));
ok
(
LocalOrCookieStorage
.
getItem
(
'
jio/jobObject/
'
+
id
)[
'
1
'
],
'
job creation
'
);
console
.
log
(
localStorage
.
getItem
(
'
jio/jobObject/
'
+
id
));
clock
.
tick
(
10
);
console
.
log
(
localStorage
.
getItem
(
'
jio/jobObject/
'
+
id
));
o
.
jio
.
removeDocument
({
'
fileName
'
:
'
file
'
,
'
fileContent
'
:
'
content
'
,
'
callback
'
:
o
.
f2
,
'
maxtries
'
:
1
});
o
.
tmp
=
LocalOrCookieStorage
.
getItem
(
'
jio/jobObject/
'
+
id
)[
'
1
'
];
console
.
log
(
localStorage
.
getItem
(
'
jio/jobObject/
'
+
id
));
ok
(
!
o
.
tmp
||
o
.
tmp
.
status
===
'
fail
'
,
'
job elimination
'
);
});
...
...
@@ -214,22 +218,20 @@ test ('Simple Job Waiting', function () {
test
(
'
Simple Time Waiting
'
,
function
()
{
// Test if the job that have fail wait until a certain moment to restart.
// It will use the dummyall3tries, which will work after the 3rd try.
var
o
=
{},
clock
=
this
.
sandbox
.
useFakeTimers
(),
id
=
0
;
o
.
f1
=
this
.
spy
();
o
.
jio
=
JIO
.
createNew
({
'
type
'
:
'
dummyallfail
'
,
'
userName
'
:
'
dummy
'
},
o
.
f
=
function
(
result
)
{
o
.
res
=
(
result
.
status
===
'
done
'
);
};
this
.
spy
(
o
,
'
f
'
);
o
.
jio
=
JIO
.
createNew
({
'
type
'
:
'
dummyall3tries
'
,
'
userName
'
:
'
dummy
'
},
{
'
ID
'
:
'
jiotests
'
});
id
=
o
.
jio
.
id
;
o
.
jio
.
saveDocument
({
'
fileName
'
:
'
file
'
,
'
fileContent
'
:
'
content
'
,
'
callback
'
:
o
.
f1
,
'
maxtries
'
:
2
});
clock
.
tick
(
400
);
o
.
tmp
=
LocalOrCookieStorage
.
getItem
(
'
jio/jobObject/
'
+
id
)[
'
1
'
];
ok
(
o
.
tmp
.
status
===
'
wait
'
&&
o
.
tmp
.
waitingFor
&&
o
.
tmp
.
waitingFor
.
time
,
'
is waiting for time
'
);
clock
.
tick
(
1500
);
o
.
tmp
=
LocalOrCookieStorage
.
getItem
(
'
jio/jobObject/
'
+
id
)[
'
1
'
];
ok
(
o
.
tmp
.
status
===
'
fail
'
,
'
it restores itself after time
'
);
console
.
log
(
localStorage
);
'
callback
'
:
o
.
f
,
'
maxtries
'
:
3
});
clock
.
tick
(
100000
);
ok
(
o
.
f
.
calledOnce
,
'
callback called once.
'
);
ok
(
o
.
res
,
'
job done.
'
);
o
.
jio
.
stop
();
});
...
...
@@ -631,11 +633,12 @@ test ('Check name availability', function () {
deepEqual
(
result
.
isAvailable
,
value
,
message
)};
t
.
spy
(
o
,
'
f
'
);
o
.
jio
.
checkNameAvailability
({
'
userName
'
:
'
Dummy
'
,
'
callback
'
:
o
.
f
,
'
maxtries
'
:
1
});
clock
.
tick
(
1
000
);
'
maxtries
'
:
o
.
maxtries
});
clock
.
tick
(
300
000
);
if
(
!
o
.
f
.
calledOnce
)
ok
(
false
,
'
no respose / too much results
'
);
};
o
.
maxtries
=
1
;
// DummyStorageAllOK,OK
o
.
jio
=
JIO
.
createNew
({
'
type
'
:
'
replicate
'
,
'
storageArray
'
:[
{
'
type
'
:
'
dummyallok
'
,
'
userName
'
:
'
1
'
},
...
...
@@ -664,24 +667,112 @@ test ('Check name availability', function () {
{
'
ID
'
:
'
jiotests
'
});
mytest
(
'
DummyStoragesAllFail,Fail : name not available
'
,
false
);
o
.
jio
.
stop
();
// DummyStorageAllOK,3Tries
o
.
maxtries
=
3
;
o
.
jio
=
JIO
.
createNew
({
'
type
'
:
'
replicate
'
,
'
storageArray
'
:[
{
'
type
'
:
'
dummyallok
'
,
'
userName
'
:
'
1
'
},
{
'
type
'
:
'
dummyall3tries
'
,
'
userName
'
:
'
2
'
}]},
{
'
ID
'
:
'
jiotests
'
});
mytest
(
'
DummyStoragesAllOK,3Tries : name available
'
,
true
);
o
.
jio
.
stop
();
// DummyStorageAll{3tries,{3tries,3tries},3tries}
o
.
maxtries
=
3
;
o
.
jio
=
JIO
.
createNew
({
'
type
'
:
'
replicate
'
,
'
storageArray
'
:[
{
'
type
'
:
'
dummyall3tries
'
,
'
userName
'
:
'
1
'
},
{
'
type
'
:
'
replicate
'
,
'
storageArray
'
:[
{
'
type
'
:
'
dummyall3tries
'
,
'
userName
'
:
'
2
'
},
{
'
type
'
:
'
dummyall3tries
'
,
'
userName
'
:
'
3
'
}]},
{
'
type
'
:
'
dummyall3tries
'
,
'
userName
'
:
'
4
'
}]},
{
'
ID
'
:
'
jiotests
'
});
mytest
(
'
DummyStorageAll{3tries,{3tries,3tries},3tries} : name available
'
,
true
);
o
.
jio
.
stop
();
});
test
(
'
Document load
'
,
function
()
{
// Test if ReplicateStorage can load several documents.
var
o
=
{};
var
clock
=
this
.
sandbox
.
useFakeTimers
();
var
t
=
this
;
var
mytest
=
function
(
message
,
doc
)
{
o
.
f
=
function
(
result
)
{
deepEqual
(
result
.
document
,
doc
,
message
);};
t
.
spy
(
o
,
'
f
'
);
o
.
jio
.
loadDocument
({
'
fileName
'
:
'
file
'
,
'
callback
'
:
o
.
f
});
clock
.
tick
(
100000
);
if
(
!
o
.
f
.
calledOnce
)
ok
(
false
,
'
no response / too much results
'
);
};
o
.
jio
=
JIO
.
createNew
({
'
type
'
:
'
replicate
'
,
'
userName
'
:
'
Dummy
'
,
'
storageArray
'
:[
{
'
type
'
:
'
dummyallok
'
,
'
userName
'
:
'
1
'
},
{
'
type
'
:
'
dummyallok
'
,
'
userName
'
:
'
2
'
}]},
{
'
ID
'
:
'
jiotests
'
});
mytest
(
'
DummyStorageAllOK,OK: load same file
'
,{
'
fileName
'
:
'
file
'
,
'
fileContent
'
:
'
content
'
,
'
lastModified
'
:
15000
,
'
creationDate
'
:
10000
});
o
.
jio
.
stop
();
o
.
jio
=
JIO
.
createNew
({
'
type
'
:
'
replicate
'
,
'
userName
'
:
'
Dummy
'
,
'
storageArray
'
:[
{
'
type
'
:
'
dummyallok
'
,
'
userName
'
:
'
1
'
},
{
'
type
'
:
'
dummyall3tries
'
,
'
userName
'
:
'
2
'
}]},
{
'
ID
'
:
'
jiotests
'
});
mytest
(
'
DummyStorageAllOK,3tries: load 2 different files
'
,{
'
fileName
'
:
'
file
'
,
'
fileContent
'
:
'
content2
'
,
'
lastModified
'
:
17000
,
'
creationDate
'
:
10000
});
o
.
jio
.
stop
();
});
// test ('Document load', function () {
// // Test if DavStorage can load documents.
// var o = {}; var clock = this.sandbox.useFakeTimers(); var t = this;
// var mytest = function (message,doc) {
// o.f = function (result) {
// deepEqual (result.document,doc,message);};
// t.spy(o,'f');
// o.jio.loadDocument({'fileName':'file','callback':o.f});
// clock.tick(1000);
// server.respond();
// if (!o.f.calledOnce)
// ok(false, 'no response / too much results');
// };
// o.jio = JIO.createNew({'type':'replicate','userName':'Dummy'},
// {'ID':'jiotests'});
// o.jio.stop();
// });
test
(
'
Document save
'
,
function
()
{
// Test if ReplicateStorage can save several documents.
var
o
=
{},
clock
=
this
.
sandbox
.
useFakeTimers
(),
t
=
this
,
mytest
=
function
(
message
,
value
)
{
o
.
f
=
function
(
result
)
{
deepEqual
(
result
.
isSaved
,
value
,
message
);};
t
.
spy
(
o
,
'
f
'
);
o
.
jio
.
saveDocument
({
'
fileName
'
:
'
file
'
,
'
fileContent
'
:
'
content
'
,
'
callback
'
:
o
.
f
,
'
maxtries
'
:
3
});
clock
.
tick
(
500
);
if
(
!
o
.
f
.
calledOnce
)
ok
(
false
,
'
no response / too much results
'
);
};
o
.
jio
=
JIO
.
createNew
({
'
type
'
:
'
replicate
'
,
'
userName
'
:
'
Dummy
'
,
'
storageArray
'
:[
{
'
type
'
:
'
dummyallok
'
,
'
userName
'
:
'
1
'
},
{
'
type
'
:
'
dummyallok
'
,
'
userName
'
:
'
2
'
}]},
{
'
ID
'
:
'
jiotests
'
});
mytest
(
'
DummyStorageAllOK,OK: save a file.
'
,
true
);
o
.
jio
.
stop
();
});
test
(
'
Get Document List
'
,
function
()
{
// Test if ReplicateStorage can save several documents.
var
o
=
{},
clock
=
this
.
sandbox
.
useFakeTimers
(),
t
=
this
,
mytest
=
function
(
message
,
value
)
{
o
.
f
=
function
(
result
)
{
var
objectifyDocumentArray
=
function
(
array
)
{
var
obj
=
{},
k
;
for
(
k
in
array
)
{
obj
[
array
[
k
].
fileName
]
=
array
[
k
];}
return
obj
;
};
deepEqual
(
objectifyDocumentArray
(
result
.
list
),
objectifyDocumentArray
(
value
),
'
getting list
'
);
};
t
.
spy
(
o
,
'
f
'
);
o
.
jio
.
saveDocument
({
'
fileName
'
:
'
file
'
,
'
fileContent
'
:
'
content
'
,
'
callback
'
:
o
.
f
,
'
maxtries
'
:
3
});
clock
.
tick
(
100000
);
if
(
!
o
.
f
.
calledOnce
)
ok
(
false
,
'
no response / too much results
'
);
};
o
.
jio
=
JIO
.
createNew
({
'
type
'
:
'
replicate
'
,
'
userName
'
:
'
Dummy
'
,
'
storageArray
'
:[
{
'
type
'
:
'
dummyallok
'
,
'
userName
'
:
'
1
'
},
{
'
type
'
:
'
dummyall3tries
'
,
'
userName
'
:
'
2
'
}]},
{
'
ID
'
:
'
jiotests
'
});
o
.
doc1
=
{
'
fileName
'
:
'
memo
'
,
'
fileContent
'
:
'
test
'
,
'
lastModified
'
:
15000
,
'
creationDate
'
:
10000
};
o
.
doc1
=
{
'
fileName
'
:
'
memo
'
,
'
fileContent
'
:
'
test
'
,
'
lastModified
'
:
25000
,
'
creationDate
'
:
20000
};
mytest
(
'
DummyStorageAllOK,3tries: get document list .
'
,[
o
.
doc1
,
o
.
doc2
]);
o
.
jio
.
stop
();
});
jiotest/unhosted/jio.js
View file @
a4b6f558
...
...
@@ -121,9 +121,10 @@
},
'
canEliminate
'
:
function
(
job1
,
job2
)
{
if
(
job1
.
status
!==
'
ongoing
'
&&
job2
.
method
===
'
removeDocument
'
&&
(
job1
.
method
===
'
removeDocument
'
||
job1
.
method
===
'
saveDocument
'
))
{
(
job1
.
method
===
'
removeDocument
'
&&
job2
.
method
===
'
saveDocument
'
||
job1
.
method
===
'
saveDocument
'
&&
job2
.
method
===
'
removeDocument
'
))
{
return
true
;
}
return
false
;
...
...
@@ -364,15 +365,11 @@
}
if
(
jioGlobalObj
.
jobManagingMethod
.
canEliminate
(
this
.
jobObject
[
id
],
job
))
{
console
.
log
(
'
Elimitated:
'
+
JSON
.
stringify
(
this
.
jobObject
[
id
]));
res
.
elimArray
.
push
(
id
);
continue
;
}
if
(
jioGlobalObj
.
jobManagingMethod
.
canReplace
(
this
.
jobObject
[
id
],
job
))
{
console
.
log
(
'
Replaced:
'
+
JSON
.
stringify
(
this
.
jobObject
[
id
]));
basestorage
=
new
BaseStorage
(
{
'
queue
'
:
this
,
'
job
'
:
this
.
jobObject
[
id
]});
basestorage
.
replace
(
job
);
...
...
@@ -381,16 +378,11 @@
}
if
(
jioGlobalObj
.
jobManagingMethod
.
cannotAccept
(
this
.
jobObject
[
id
],
job
))
{
console
.
log
(
'
Not accepted:
'
+
JSON
.
stringify
(
job
)
+
'
\n
by:
'
+
JSON
.
stringify
(
this
.
jobObject
[
id
]));
// Job not accepted
return
false
;
}
if
(
jioGlobalObj
.
jobManagingMethod
.
mustWait
(
this
.
jobObject
[
id
],
job
))
{
console
.
log
(
'
Waited:
'
+
JSON
.
stringify
(
this
.
jobObject
[
id
])
+
'
\n
by :
'
+
JSON
.
stringify
(
job
));
res
.
waitArray
.
push
(
id
);
continue
;
}
...
...
@@ -422,10 +414,10 @@
}
// set job id
job
.
id
=
this
.
jobid
;
job
.
tries
=
0
;
this
.
jobid
++
;
// save the new job into the queue
this
.
jobObject
[
job
.
id
]
=
job
;
console
.
log
(
'
new one:
'
+
JSON
.
stringify
(
job
));
}
// save into localStorage
this
.
copyJobQueueToLocalStorage
();
...
...
@@ -692,126 +684,187 @@
// The base storage, will call the good method from the good storage,
// and will check and return the associated value.
this
.
job
=
options
.
job
;
this
.
callback
=
options
.
job
.
callback
;
this
.
queue
=
options
.
queue
;
this
.
res
=
{
'
status
'
:
'
done
'
,
'
message
'
:
''
};
};
BaseStorage
.
prototype
=
{
createStorageObject
:
function
(
options
)
{
// Create a storage thanks to storages types set
// with 'addStorageType'.
if
(
!
jioGlobalObj
.
storageTypeObject
[
options
.
job
.
storage
.
type
])
return
null
;
// error!
return
jioGlobalObj
.
storageTypeObject
[
options
.
job
.
storage
.
type
](
options
);
var
that
=
{},
priv
=
{};
//// Private attributes
priv
.
job
=
options
.
job
;
priv
.
callback
=
options
.
job
.
callback
;
priv
.
queue
=
options
.
queue
;
priv
.
res
=
{
'
status
'
:
'
done
'
,
'
message
'
:
''
};
//// end Private attributes
//// Private Methods
priv
.
fail_checkNameAvailability
=
function
()
{
priv
.
res
.
isAvailable
=
false
;
},
priv
.
done_checkNameAvailability
=
function
(
isavailable
)
{
priv
.
res
.
message
=
priv
.
job
.
userName
+
'
is
'
+
(
isavailable
?
''
:
'
not
'
)
+
'
available.
'
;
priv
.
res
.
isAvailable
=
isavailable
;
},
priv
.
fail_loadDocument
=
function
()
{
priv
.
res
.
document
=
{};
},
priv
.
done_loadDocument
=
function
(
returneddocument
)
{
priv
.
res
.
message
=
'
Document loaded.
'
;
priv
.
res
.
document
=
returneddocument
;
},
retryLater
:
function
()
{
priv
.
fail_saveDocument
=
function
()
{
priv
.
res
.
isSaved
=
false
;
},
priv
.
done_saveDocument
=
function
()
{
priv
.
res
.
message
=
'
Document saved.
'
;
priv
.
res
.
isSaved
=
true
;
},
priv
.
fail_getDocumentList
=
function
()
{
priv
.
res
.
list
=
[];
},
priv
.
done_getDocumentList
=
function
(
documentlist
)
{
priv
.
res
.
message
=
'
Document list received.
'
;
priv
.
res
.
list
=
documentlist
;
},
priv
.
fail_removeDocument
=
function
()
{
priv
.
res
.
isRemoved
=
false
;
},
priv
.
done_removeDocument
=
function
()
{
priv
.
res
.
message
=
'
Document removed.
'
;
priv
.
res
.
isRemoved
=
true
;
};
priv
.
retryLater
=
function
()
{
// Change the job status to wait for time.
// The listener will invoke this job later.
this
.
job
.
status
=
'
wait
'
;
this
.
job
.
waitingFor
=
{
'
time
'
:
Date
.
now
()
+
(
this
.
job
.
tries
*
this
.
job
.
tries
*
1000
)};
},
eliminate
:
function
()
{
this
.
job
.
maxtries
=
0
;
this
.
fail
(
'
Job Stopped!
'
,
0
);
},
replace
:
function
(
newjob
)
{
priv
.
job
.
status
=
'
wait
'
;
priv
.
job
.
waitingFor
=
{
'
time
'
:
Date
.
now
()
+
(
priv
.
job
.
tries
*
priv
.
job
.
tries
*
1000
)};
};
//// end Private Methods
//// Getters Setters
that
.
cloneJob
=
function
()
{
return
$
.
extend
({},
priv
.
job
);
};
that
.
getUserName
=
function
()
{
return
priv
.
job
.
userName
||
''
;
};
that
.
getApplicantID
=
function
()
{
return
priv
.
job
.
applicant
.
ID
||
''
;
};
that
.
getStorageUserName
=
function
()
{
return
priv
.
job
.
storage
.
userName
||
''
;
};
that
.
getStoragePassword
=
function
()
{
return
priv
.
job
.
storage
.
password
||
''
;
};
that
.
getStorageLocation
=
function
()
{
return
priv
.
job
.
storage
.
location
||
''
;
};
that
.
getStorageArray
=
function
()
{
return
priv
.
job
.
storage
.
storageArray
||
[];
};
that
.
getFileName
=
function
()
{
return
priv
.
job
.
fileName
||
''
;
};
that
.
getFileContent
=
function
()
{
return
priv
.
job
.
fileContent
||
''
;
};
that
.
cloneOptionObject
=
function
()
{
return
$
.
extend
({},
priv
.
job
.
options
);
};
that
.
getMaxTries
=
function
()
{
return
priv
.
job
.
maxtries
;
};
that
.
getTries
=
function
()
{
return
priv
.
job
.
tries
||
0
;
};
that
.
setMaxTries
=
function
(
maxtries
)
{
priv
.
job
.
maxtries
=
maxtries
;
};
//// end Getters Setters
//// Public Methods
that
.
addJob
=
function
(
newjob
)
{
return
priv
.
queue
.
createJob
(
newjob
);
};
that
.
eliminate
=
function
()
{
// Stop and remove a job !
priv
.
job
.
maxtries
=
1
;
priv
.
job
.
tries
=
1
;
that
.
fail
(
'
Job Stopped!
'
,
0
);
};
that
.
replace
=
function
(
newjob
)
{
// It replace the current job by the new one.
// Replacing only the date
this
.
job
.
tries
=
0
;
this
.
job
.
date
=
newjob
.
date
;
this
.
job
.
callback
=
newjob
.
callback
;
priv
.
job
.
tries
=
0
;
priv
.
job
.
date
=
newjob
.
date
;
priv
.
job
.
callback
=
newjob
.
callback
;
this
.
res
.
status
=
'
fail
'
;
this
.
res
.
message
=
'
Job Stopped!
'
;
this
.
res
.
errno
=
0
;
this
[
'
fail_
'
+
this
.
job
.
method
]();
this
.
callback
(
this
.
res
);
}
,
fail
:
function
(
message
,
errno
)
{
priv
.
res
.
status
=
'
fail
'
;
priv
.
res
.
message
=
'
Job Stopped!
'
;
priv
.
res
.
errno
=
0
;
priv
[
'
fail_
'
+
priv
.
job
.
method
]();
priv
.
callback
(
priv
.
res
);
}
;
that
.
fail
=
function
(
message
,
errno
)
{
// Called when a job has failed.
// It will retry the job from a certain moment or it will return
// a failure.
this
.
res
.
status
=
'
fail
'
;
this
.
res
.
message
=
message
;
this
.
res
.
errno
=
errno
;
if
(
this
.
job
.
maxtries
&&
this
.
job
.
tries
<
this
.
job
.
maxtries
)
{
this
.
retryLater
();
priv
.
res
.
status
=
'
fail
'
;
priv
.
res
.
message
=
message
;
priv
.
res
.
errno
=
errno
;
if
(
!
priv
.
job
.
maxtries
||
priv
.
job
.
tries
<
priv
.
job
.
maxtries
)
{
priv
.
retryLater
();
}
else
{
this
.
job
.
status
=
'
fail
'
;
this
[
'
fail_
'
+
this
.
job
.
method
]();
this
.
queue
.
ended
(
this
.
job
);
this
.
callback
(
this
.
res
);
priv
.
job
.
status
=
'
fail
'
;
priv
[
'
fail_
'
+
priv
.
job
.
method
]();
priv
.
queue
.
ended
(
priv
.
job
);
priv
.
callback
(
priv
.
res
);
}
}
,
done
:
function
(
retvalue
)
{
}
;
that
.
done
=
function
(
retvalue
)
{
// Called when a job has terminated successfully.
// It will return the return value by the calling the callback
// function.
this
.
job
.
status
=
'
done
'
;
this
[
'
done_
'
+
this
.
job
.
method
](
retvalue
);
this
.
queue
.
ended
(
this
.
job
);
this
.
callback
(
this
.
res
);
}
,
execute
:
function
()
{
priv
.
job
.
status
=
'
done
'
;
priv
[
'
done_
'
+
priv
.
job
.
method
](
retvalue
);
priv
.
queue
.
ended
(
priv
.
job
);
priv
.
callback
(
priv
.
res
);
}
;
that
.
execute
=
function
()
{
// Execute the good function from the good storage.
var
t
=
this
,
stor
=
null
;
priv
.
job
.
tries
=
that
.
getTries
()
+
1
;
if
(
!
this
.
job
.
tries
)
{
this
.
job
.
tries
=
0
;
}
this
.
job
.
tries
++
;
stor
=
this
.
createStorageObject
(
{
'
job
'
:
this
.
job
,
'
queue
'
:
this
.
queue
}
);
stor
.
done
=
function
(
retval
)
{
t
.
done
(
retval
);
};
stor
.
fail
=
function
(
mess
,
errno
)
{
t
.
fail
(
mess
,
errno
);
};
stor
[
this
.
job
.
method
]();
},
fail_checkNameAvailability
:
function
()
{
this
.
res
.
isAvailable
=
false
;
},
done_checkNameAvailability
:
function
(
isavailable
)
{
this
.
res
.
message
=
this
.
job
.
userName
+
'
is
'
+
(
isavailable
?
''
:
'
not
'
)
+
'
available.
'
;
this
.
res
.
isAvailable
=
isavailable
;
},
fail_saveDocument
:
function
()
{
this
.
res
.
isSaved
=
false
;
},
done_saveDocument
:
function
()
{
this
.
res
.
message
=
'
Document saved.
'
;
this
.
res
.
isSaved
=
true
;
},
fail_loadDocument
:
function
()
{
this
.
res
.
document
=
{};
},
done_loadDocument
:
function
(
returneddocument
)
{
this
.
res
.
message
=
'
Document loaded.
'
;
this
.
res
.
document
=
returneddocument
;
},
fail_getDocumentList
:
function
()
{
this
.
res
.
list
=
[];
},
done_getDocumentList
:
function
(
documentlist
)
{
this
.
res
.
message
=
'
Document list received.
'
;
this
.
res
.
list
=
documentlist
;
},
fail_removeDocument
:
function
()
{
this
.
res
.
isRemoved
=
false
;
},
done_removeDocument
:
function
()
{
this
.
res
.
message
=
'
Document removed.
'
;
this
.
res
.
isRemoved
=
true
;
if
(
!
jioGlobalObj
.
storageTypeObject
[
priv
.
job
.
storage
.
type
]
)
{
return
null
;
}
return
jioGlobalObj
.
storageTypeObject
[
priv
.
job
.
storage
.
type
]({
'
job
'
:
priv
.
job
,
'
queue
'
:
priv
.
queue
})[
priv
.
job
.
method
]();
};
// These methods must be redefined!
that
.
checkNameAvailability
=
function
()
{
that
.
fail
(
'
This method must be redefined!
'
,
0
);
};
that
.
loadDocument
=
function
()
{
that
.
fail
(
'
This method must be redefined!
'
,
0
);
};
that
.
saveDocument
=
function
()
{
that
.
fail
(
'
This method must be redefined!
'
,
0
);
};
that
.
getDocumentList
=
function
()
{
that
.
fail
(
'
This method must be redefined!
'
,
0
);
};
that
.
removeDocument
=
function
()
{
that
.
fail
(
'
This method must be redefined!
'
,
0
);
};
//// end Public Methods
return
that
;
};
// end BaseStorage
////////////////////////////////////////////////////////////////////////////
...
...
@@ -848,9 +901,9 @@
}
// check storage type
if
(
this
.
storage
)
if
(
!
jioGlobalObj
.
storageTypeObject
[
this
.
storage
.
type
])
if
(
this
.
storage
&&
!
jioGlobalObj
.
storageTypeObject
[
this
.
storage
.
type
]){
$
.
error
(
'
Unknown storage type "
'
+
this
.
storage
.
type
+
'
"
'
);
}
// start jio process
this
.
start
();
...
...
@@ -1091,6 +1144,7 @@
// storage: the storage object or json string
// applicant: the applicant object or json string
// options.useLocalStorage: if true, save job queue on localStorage.
var
settings
=
$
.
extend
({
'
useLocalStorage
'
:
true
},
options
);
if
(
jioGlobalObj
.
localStorage
===
null
)
{
...
...
@@ -1099,8 +1153,10 @@
return
new
JioCons
(
storage
,
applicant
,
settings
);
},
getStoragePrototype
:
function
()
{
return
new
BaseStorage
();
newBaseStorage
:
function
(
options
)
{
// Create a Jio Storage which can be used to design new storage.
return
BaseStorage
(
options
);
},
addStorageType
:
function
(
type
,
constructor
)
{
// Add a storage type to jio. Jio must have keys/types which are
...
...
jiotest/unhosted/jio.storage.js
View file @
a4b6f558
...
...
@@ -35,39 +35,36 @@
// check dependencies
if
(
!
checkJioDependencies
())
{
return
;
}
////////////////////////////////////////////////////////////////////////////
// Local Storage
LocalStorage
=
function
(
args
)
{
// LocalStorage constructor
this
.
job
=
args
.
job
;
};
LocalStorage
.
prototype
=
{
checkNameAvailability
:
function
()
{
var
that
=
Jio
.
newBaseStorage
(
args
),
priv
=
{};
that
.
checkNameAvailability
=
function
()
{
// checks the availability of the [job.userName].
// if the name already exists, it is not available.
// this.job.userName: the name we want to check.
var
t
=
this
,
localStor
=
null
,
k
=
'
key
'
,
splitk
=
[
'
splitedkey
'
];
// wait a little in order to simulate asynchronous operation
setTimeout
(
function
()
{
localStor
=
jioGlobalObj
.
localStorage
.
getAll
();
for
(
k
in
localStor
)
{
var
localStorageObject
=
null
,
k
=
'
key
'
,
splitk
=
[
'
splitedkey
'
];
localStorageObject
=
jioGlobalObj
.
localStorage
.
getAll
();
for
(
k
in
localStorageObject
)
{
splitk
=
k
.
split
(
'
/
'
);
if
(
splitk
[
0
]
===
'
jio
'
&&
splitk
[
1
]
===
'
local
'
&&
splitk
[
2
]
===
t
.
job
.
userName
)
{
return
t
.
done
(
false
);
splitk
[
2
]
===
t
hat
.
getUserName
()
)
{
return
t
hat
.
done
(
false
);
}
}
return
t
.
done
(
true
);
return
t
hat
.
done
(
true
);
},
100
);
}
,
// end userNameAvailable
}
;
// end userNameAvailable
saveDocument
:
function
()
{
that
.
saveDocument
=
function
()
{
// Save a document in the local storage
// this.job.fileName: the document name.
// this.job.fileContent: the document content.
...
...
@@ -75,36 +72,37 @@
// this.job.storage.userName: the user name
// this.job.applicant.ID: the applicant id.
var
t
=
this
,
doc
=
null
;
// wait a little in order to simulate asynchronous saving
setTimeout
(
function
()
{
var
doc
=
null
;
// reading
doc
=
jioGlobalObj
.
localStorage
.
getItem
(
'
jio/local/
'
+
t
.
job
.
storage
.
userName
+
'
/
'
+
t
.
job
.
applicant
.
ID
+
'
/
'
+
t
.
job
.
fileName
);
'
jio/local/
'
+
t
hat
.
getStorageUserName
()
+
'
/
'
+
t
hat
.
getApplicantID
()
+
'
/
'
+
t
hat
.
getFileName
()
);
if
(
!
doc
)
{
// create document
doc
=
{
'
fileName
'
:
t
.
job
.
fileName
,
'
fileContent
'
:
t
.
job
.
fileContent
,
'
fileName
'
:
t
hat
.
getFileName
()
,
'
fileContent
'
:
t
hat
.
getFileContent
()
,
'
creationDate
'
:
Date
.
now
(),
'
lastModified
'
:
Date
.
now
()
}
}
else
{
// overwriting
doc
.
lastModified
=
Date
.
now
();
doc
.
fileContent
=
t
.
job
.
fileContent
;
doc
.
fileContent
=
t
hat
.
getFileContent
()
;
}
jioGlobalObj
.
localStorage
.
setItem
(
'
jio/local/
'
+
t
.
job
.
storage
.
userName
+
'
/
'
+
t
.
job
.
applicant
.
ID
+
'
/
'
+
t
.
job
.
fileName
,
doc
);
return
t
.
done
();
'
jio/local/
'
+
t
hat
.
getStorageUserName
()
+
'
/
'
+
t
hat
.
getApplicantID
()
+
'
/
'
+
t
hat
.
getFileName
()
,
doc
);
return
t
hat
.
done
();
},
100
);
}
,
// end saveDocument
}
;
// end saveDocument
loadDocument
:
function
()
{
that
.
loadDocument
=
function
()
{
// Load a document from the storage. It returns a document object
// containing all information of the document and its content.
// this.job.fileName : the document name we want to load.
...
...
@@ -113,29 +111,28 @@
// document object is {'fileName':string,'fileContent':string,
// 'creationDate':date,'lastModified':date}
var
t
=
this
,
doc
=
null
,
settings
=
$
.
extend
(
{
'
getContent
'
:
true
},
this
.
job
.
options
);
// wait a little in order to simulate asynchronous operation
setTimeout
(
function
()
{
var
doc
=
null
,
settings
=
$
.
extend
(
{
'
getContent
'
:
true
},
that
.
cloneOptionObject
());
doc
=
jioGlobalObj
.
localStorage
.
getItem
(
'
jio/local/
'
+
t
.
job
.
storage
.
userName
+
'
/
'
+
t
.
job
.
applicant
.
ID
+
'
/
'
+
t
.
job
.
fileName
);
'
jio/local/
'
+
t
hat
.
getStorageUserName
()
+
'
/
'
+
t
hat
.
getApplicantID
()
+
'
/
'
+
that
.
getFileName
()
);
if
(
!
doc
)
{
t
.
fail
(
'
Document not found.
'
,
404
);
t
hat
.
fail
(
'
Document not found.
'
,
404
);
}
else
{
if
(
!
settings
.
getContent
)
{
delete
doc
.
fileContent
;
}
t
.
done
(
doc
);
t
hat
.
done
(
doc
);
}
},
100
);
}
,
// end loadDocument
}
;
// end loadDocument
getDocumentList
:
function
()
{
that
.
getDocumentList
=
function
()
{
// Get a document list from the storage. It returns a document
// array containing all the user documents informations, but not
// their content.
// array containing all the user documents informations.
// this.job.storage: the storage informations.
// this.job.storage.userName: the userName.
// this.job.storage.applicant.ID: the applicant ID.
...
...
@@ -143,45 +140,45 @@
// the list is [object,object] -> object = {'fileName':string,
// 'lastModified':date,'creationDate':date}
var
t
=
this
,
list
=
[],
localStor
=
null
,
k
=
'
key
'
,
setTimeout
(
function
()
{
var
list
=
[],
localStor
=
null
,
k
=
'
key
'
,
splitk
=
[
'
splitedkey
'
];
setTimeout
(
function
()
{
localStor
=
jioGlobalObj
.
localStorage
.
getAll
();
for
(
k
in
localStor
)
{
localStorageObject
=
jioGlobalObj
.
localStorage
.
getAll
();
for
(
k
in
localStorageObject
)
{
splitk
=
k
.
split
(
'
/
'
);
if
(
splitk
[
0
]
===
'
jio
'
&&
splitk
[
1
]
===
'
local
'
&&
splitk
[
2
]
===
t
.
job
.
storage
.
userName
&&
splitk
[
3
]
===
t
.
job
.
applicant
.
ID
)
{
fileObject
=
JSON
.
parse
(
localStor
[
k
]);
splitk
[
2
]
===
t
hat
.
getStorageUserName
()
&&
splitk
[
3
]
===
t
hat
.
getApplicantID
()
)
{
fileObject
=
JSON
.
parse
(
localStor
ageObject
[
k
]);
list
.
push
({
'
fileName
'
:
fileObject
.
fileName
,
'
creationDate
'
:
fileObject
.
creationDate
,
'
lastModified
'
:
fileObject
.
lastModified
});
}
}
t
.
done
(
list
);
t
hat
.
done
(
list
);
},
100
);
}
,
// end getDocumentList
}
;
// end getDocumentList
removeDocument
:
function
()
{
that
.
removeDocument
=
function
()
{
// Remove a document from the storage.
// this.job.storage.userName: the userName.
// this.job.applicant.ID: the applicant ID.
// this.job.fileName: the document name.
var
t
=
this
;
setTimeout
(
function
()
{
// deleting
jioGlobalObj
.
localStorage
.
deleteItem
(
'
jio/local/
'
+
t
.
job
.
storage
.
userName
+
'
/
'
+
t
.
job
.
applicant
.
ID
+
'
/
'
+
t
.
job
.
fileName
);
return
t
.
done
();
that
.
getStorageUserName
()
+
'
/
'
+
that
.
getApplicantID
()
+
'
/
'
+
that
.
getFileName
());
return
that
.
done
();
},
100
);
}
};
return
that
;
},
// end Local Storage
...
...
@@ -190,10 +187,9 @@
////////////////////////////////////////////////////////////////////////////
// DAVStorage
DAVStorage
=
function
(
args
)
{
this
.
job
=
args
.
job
;
};
DAVStorage
.
prototype
=
{
mkcol
:
function
(
options
)
{
var
that
=
Jio
.
newBaseStorage
(
args
);
that
.
mkcol
=
function
(
options
)
{
// create folders in dav storage, synchronously
// options : contains mkcol list
// options.location : the davstorage locations
...
...
@@ -206,13 +202,13 @@
var
settings
=
$
.
extend
({
'
success
'
:
function
(){},
'
error
'
:
function
(){}},
options
),
splitpath
=
[
'
splitedpath
'
],
tmppath
=
'
temp/path
'
,
t
=
this
;
splitpath
=
[
'
splitedpath
'
],
tmppath
=
'
temp/path
'
;
// if pathstep is not defined, then split the settings.path
// and do mkcol recursively
if
(
!
settings
.
pathsteps
)
{
settings
.
pathsteps
=
1
;
th
is
.
mkcol
(
settings
);
th
at
.
mkcol
(
settings
);
}
else
{
splitpath
=
settings
.
path
.
split
(
'
/
'
);
// // check if the path is terminated by '/'
...
...
@@ -237,7 +233,7 @@
// xhrFields: {withCredentials: 'true'}, // cross domain
success
:
function
()
{
// done
t
.
mkcol
(
settings
);
t
hat
.
mkcol
(
settings
);
},
error
:
function
(
type
)
{
alert
(
JSON
.
stringify
(
type
));
...
...
@@ -253,8 +249,9 @@
}
}
);
}
},
checkNameAvailability
:
function
()
{
};
that
.
checkNameAvailability
=
function
()
{
// checks the availability of the [job.userName].
// if the name already exists, it is not available.
// this.job.storage: the storage informations.
...
...
@@ -263,33 +260,33 @@
// this.job.storage.userName: the user name.
// this.job.storage.password: the user password.
var
t
=
this
;
$
.
ajax
(
{
url
:
t
.
job
.
storage
.
location
+
'
/dav/
'
+
t
.
job
.
userName
+
'
/
'
,
url
:
that
.
getStorageLocation
()
+
'
/dav/
'
+
that
.
getStorageUserName
()
+
'
/
'
,
async
:
true
,
type
:
'
PROPFIND
'
,
dataType
:
'
xml
'
,
headers
:
{
'
Authorization
'
:
'
Basic
'
+
Base64
.
encode
(
t
.
job
.
storage
.
userName
+
'
:
'
+
t
.
job
.
storage
.
password
),
Depth
:
'
1
'
},
t
hat
.
getStorageUserName
()
+
'
:
'
+
t
hat
.
getStoragePassword
()
),
Depth
:
'
1
'
},
success
:
function
(
xmlData
)
{
t
.
done
(
false
);
t
hat
.
done
(
false
);
},
error
:
function
(
type
)
{
switch
(
type
.
status
){
case
404
:
t
.
done
(
true
);
t
hat
.
done
(
true
);
break
;
default
:
t
.
fail
(
'
Cannot check if
'
+
t
.
job
.
userName
+
t
hat
.
fail
(
'
Cannot check if
'
+
that
.
getUserName
()
+
'
is available.
'
,
type
.
status
);
break
;
}
}
}
);
},
saveDocument
:
function
()
{
};
that
.
saveDocument
=
function
()
{
// Save a document in a DAVStorage
// this.job.storage: the storage informations.
// this.job.storage.userName: the user name.
...
...
@@ -298,32 +295,31 @@
// this.job.fileName: the document name.
// this.job.fileContent: the document content.
var
t
=
this
;
// TODO if path of /dav/user/applic does not exists, it won't work!
//// save on dav
$
.
ajax
(
{
url
:
t
.
job
.
storage
.
location
+
'
/dav/
'
+
t
.
job
.
storage
.
userName
+
'
/
'
+
t
.
job
.
applicant
.
ID
+
'
/
'
+
t
.
job
.
fileName
,
url
:
t
hat
.
getStorageLocation
()
+
'
/dav/
'
+
t
hat
.
getStorageUserName
()
+
'
/
'
+
t
hat
.
getApplicantID
()
+
'
/
'
+
t
hat
.
getFileName
()
,
type
:
'
PUT
'
,
data
:
t
.
job
.
fileContent
,
data
:
t
hat
.
getFileContent
()
,
async
:
true
,
dataType
:
'
text
'
,
// TODO is it necessary ?
headers
:
{
'
Authorization
'
:
'
Basic
'
+
Base64
.
encode
(
t
.
job
.
storage
.
userName
+
'
:
'
+
t
.
job
.
storage
.
password
)},
t
hat
.
getStorageUserName
()
+
'
:
'
+
that
.
getStoragePassword
()
)},
// xhrFields: {withCredentials: 'true'}, // cross domain
success
:
function
()
{
t
.
done
();
t
hat
.
done
();
},
error
:
function
(
type
)
{
t
.
fail
(
'
Cannot save document.
'
,
type
.
status
);
t
hat
.
fail
(
'
Cannot save document.
'
,
type
.
status
);
}
}
);
//// end saving on dav
},
loadDocument
:
function
()
{
};
that
.
loadDocument
=
function
()
{
// Load a document from a DAVStorage. It returns a document object
// containing all information of the document and its content.
// this.job.fileName: the document name we want to load.
...
...
@@ -336,26 +332,26 @@
// document object is {'fileName':string,'fileContent':string,
// 'creationDate':date,'lastModified':date}
var
t
=
this
,
doc
=
{},
settings
=
$
.
extend
({
'
getContent
'
:
true
},
th
is
.
job
.
options
),
var
doc
=
{},
settings
=
$
.
extend
({
'
getContent
'
:
true
},
th
at
.
cloneOptionObject
()
),
// TODO check if job's features are good
getContent
=
function
()
{
$
.
ajax
(
{
url
:
t
.
job
.
storage
.
location
+
'
/dav/
'
+
t
.
job
.
storage
.
userName
+
'
/
'
+
t
.
job
.
applicant
.
ID
+
'
/
'
+
t
.
job
.
fileName
,
url
:
t
hat
.
getStorageLocation
()
+
'
/dav/
'
+
t
hat
.
getStorageUserName
()
+
'
/
'
+
t
hat
.
getApplicantID
()
+
'
/
'
+
t
hat
.
getFileName
()
,
type
:
"
GET
"
,
async
:
true
,
dataType
:
'
text
'
,
// TODO is it necessary ?
headers
:
{
'
Authorization
'
:
'
Basic
'
+
Base64
.
encode
(
t
.
job
.
storage
.
userName
+
'
:
'
+
t
.
job
.
storage
.
password
)},
t
hat
.
getStorageUserName
()
+
'
:
'
+
t
hat
.
getStoragePassword
()
)},
// xhrFields: {withCredentials: 'true'}, // cross domain
success
:
function
(
content
)
{
doc
.
fileContent
=
content
;
t
.
done
(
doc
);
t
hat
.
done
(
doc
);
},
error
:
function
(
type
)
{
var
message
;
...
...
@@ -363,25 +359,25 @@
case
404
:
message
=
'
Document not found.
'
;
break
;
default
:
message
=
'
Cannot load "
'
+
job
.
fileName
+
'
".
'
;
message
=
'
Cannot load "
'
+
that
.
getFileName
()
+
'
".
'
;
break
;
}
t
.
fail
(
message
,
type
.
status
);
t
hat
.
fail
(
message
,
type
.
status
);
}
}
);
}
}
;
// Get properties
$
.
ajax
(
{
url
:
t
.
job
.
storage
.
location
+
'
/dav/
'
+
t
.
job
.
storage
.
userName
+
'
/
'
+
t
.
job
.
applicant
.
ID
+
'
/
'
+
t
.
job
.
fileName
,
url
:
t
hat
.
getStorageLocation
()
+
'
/dav/
'
+
t
hat
.
getStorageUserName
()
+
'
/
'
+
t
hat
.
getApplicantID
()
+
'
/
'
+
t
hat
.
getFileName
()
,
type
:
"
PROPFIND
"
,
async
:
true
,
dataType
:
'
xml
'
,
headers
:
{
'
Authorization
'
:
'
Basic
'
+
Base64
.
encode
(
t
.
job
.
storage
.
userName
+
'
:
'
+
t
.
job
.
storage
.
password
)},
t
hat
.
getStorageUserName
()
+
'
:
'
+
t
hat
.
getStoragePassword
()
)},
success
:
function
(
xmlData
)
{
doc
.
lastModified
=
(
new
Date
(
$
(
$
(
"
lp1
\\
:getlastmodified
"
,
...
...
@@ -389,22 +385,22 @@
doc
.
creationDate
=
(
new
Date
(
$
(
$
(
"
lp1
\\
:creationdate
"
,
xmlData
).
get
(
0
)).
text
())).
getTime
();
doc
.
fileName
=
t
.
job
.
fileName
;
doc
.
fileName
=
t
hat
.
getFileName
()
;
if
(
settings
.
getContent
)
{
getContent
();
}
else
{
t
.
done
(
doc
);
t
hat
.
done
(
doc
);
}
},
error
:
function
(
type
)
{
t
.
fail
(
'
Cannot get document informations.
'
,
type
.
status
);
t
hat
.
fail
(
'
Cannot get document informations.
'
,
type
.
status
);
}
}
);
},
getDocumentList
:
function
()
{
};
that
.
getDocumentList
=
function
()
{
// Get a document list from a DAVStorage. It returns a document
// array containing all the user documents informations, but their
// content.
// array containing all the user documents informations.
// this.job.storage: the storage informations.
// this.job.storage.location: the dav storage location.
// this.job.storage.userName: the user name.
...
...
@@ -414,17 +410,18 @@
// the list is [object,object] -> object = {'fileName':string,
// 'lastModified':date,'creationDate':date}
var
t
=
this
,
documentArrayList
=
[],
file
=
{},
pathArray
=
[];
var
documentArrayList
=
[],
file
=
{},
pathArray
=
[];
$
.
ajax
(
{
url
:
t
.
job
.
storage
.
location
+
'
/dav/
'
+
t
.
job
.
storage
.
userName
+
'
/
'
+
t
.
job
.
applicant
.
ID
+
'
/
'
,
url
:
that
.
getStorageLocation
()
+
'
/dav/
'
+
that
.
getStorageUserName
()
+
'
/
'
+
that
.
getApplicantID
()
+
'
/
'
,
async
:
true
,
type
:
'
PROPFIND
'
,
dataType
:
'
xml
'
,
headers
:
{
'
Authorization
'
:
'
Basic
'
+
Base64
.
encode
(
t
.
job
.
storage
.
userName
+
'
:
'
+
t
.
job
.
storage
.
password
),
Depth
:
'
1
'
},
t
hat
.
getStorageUserName
()
+
'
:
'
+
t
hat
.
getStoragePassword
()
),
Depth
:
'
1
'
},
success
:
function
(
xmlData
)
{
$
(
"
D
\\
:response
"
,
xmlData
).
each
(
function
(
i
,
data
)
{
if
(
i
>
0
)
{
// exclude parent folder
...
...
@@ -445,14 +442,15 @@
documentArrayList
.
push
(
file
);
}
});
t
.
done
(
documentArrayList
);
t
hat
.
done
(
documentArrayList
);
},
error
:
function
(
type
)
{
t
.
fail
(
'
Cannot get list.
'
,
type
.
status
);
t
hat
.
fail
(
'
Cannot get list.
'
,
type
.
status
);
}
}
);
},
removeDocument
:
function
(
job
,
jobendcallback
)
{
};
that
.
removeDocument
=
function
()
{
// Remove a document from a DAVStorage.
// this.job.fileName: the document name we want to remove.
// this.job.storage: the storage informations.
...
...
@@ -461,34 +459,34 @@
// this.job.storage.password: the user password.
// this.job.applicant.ID: the applicant id.
var
t
=
this
;
$
.
ajax
(
{
url
:
t
.
job
.
storage
.
location
+
'
/dav/
'
+
t
.
job
.
storage
.
userName
+
'
/
'
+
t
.
job
.
applicant
.
ID
+
'
/
'
+
t
.
job
.
fileName
,
url
:
t
hat
.
getStorageLocation
()
+
'
/dav/
'
+
t
hat
.
getStorageUserName
()
+
'
/
'
+
t
hat
.
getApplicantID
()
+
'
/
'
+
t
hat
.
getFileName
()
,
type
:
"
DELETE
"
,
async
:
true
,
headers
:
{
'
Authorization
'
:
'
Basic
'
+
Base64
.
encode
(
t
.
job
.
storage
.
userName
+
'
:
'
+
t
.
job
.
storage
.
password
)},
t
hat
.
getStorageUserName
()
+
'
:
'
+
t
hat
.
getStoragePassword
()
)},
// xhrFields: {withCredentials: 'true'}, // cross domain
success
:
function
()
{
t
.
done
();
t
hat
.
done
();
},
error
:
function
(
type
)
{
switch
(
type
.
status
)
{
case
404
:
t
.
done
();
t
hat
.
done
();
break
;
default
:
t
.
fail
(
'
Cannot remove "
'
+
t
.
job
.
fileName
+
'
".
'
,
type
.
status
);
that
.
fail
(
'
Cannot remove "
'
+
that
.
getFileName
()
+
'
".
'
,
type
.
status
);
break
;
}
}
}
);
}
};
return
that
;
},
// end DAVStorage
////////////////////////////////////////////////////////////////////////////
...
...
@@ -496,67 +494,227 @@
////////////////////////////////////////////////////////////////////////////
// ReplicateStorage
ReplicateStorage
=
function
(
args
)
{
var
that
=
Jio
.
newBaseStorage
(
args
),
priv
=
{};
priv
.
storageArray
=
that
.
getStorageArray
();
// TODO Add a tests that check if there is no duplicate storages.
this
.
queue
=
args
.
queue
;
this
.
job
=
args
.
job
;
this
.
id
=
null
;
this
.
length
=
args
.
job
.
storage
.
storageArray
.
length
;
this
.
returnsValuesArray
=
[];
};
ReplicateStorage
.
prototype
=
{
checkNameAvailability
:
function
()
{
priv
.
length
=
priv
.
storageArray
.
length
;
priv
.
returnsValuesArray
=
[];
priv
.
maxtries
=
that
.
getMaxTries
();
that
.
setMaxTries
(
1
);
that
.
checkNameAvailability
=
function
()
{
// Checks the availability of the [job.userName].
// if the name already exists in a storage, it is not available.
// this.job.userName: the name we want to check.
// this.job.storage.storageArray: An Array of storages.
// TODO
var
t
=
this
,
newjob
=
{},
isavailable
=
true
,
var
newjob
=
{},
isavailable
=
true
,
res
=
{
'
status
'
:
'
done
'
},
i
=
'
ind
'
;
for
(
i
in
t
.
job
.
storage
.
storageArray
)
{
newjob
=
$
.
extend
({},
t
.
job
);
newjob
.
storage
=
t
.
job
.
storage
.
storageArray
[
i
];
for
(
i
in
priv
.
storageArray
)
{
newjob
=
that
.
cloneJob
();
newjob
.
maxtries
=
priv
.
maxtries
;
newjob
.
storage
=
priv
.
storageArray
[
i
];
newjob
.
callback
=
function
(
result
)
{
t
.
returnsValuesArray
.
push
(
result
);
priv
.
returnsValuesArray
.
push
(
result
);
if
(
result
.
status
===
'
fail
'
)
{
res
.
status
=
'
fail
'
;
}
if
(
!
result
.
isAvailable
)
{
isavailable
=
false
;
}
if
(
t
.
returnsValuesArray
.
length
===
t
.
length
)
{
if
(
priv
.
returnsValuesArray
.
length
===
priv
.
length
)
{
// if this is the last callback
t
.
done
(
isavailable
);
t
hat
.
done
(
isavailable
);
}
};
th
is
.
queue
.
create
Job
(
newjob
)
;
th
at
.
add
Job
(
newjob
)
;
}
}
,
saveDocument
:
function
()
{
}
;
that
.
saveDocument
=
function
()
{
// Save a single document in several storages.
// If a storage failed to save the document, it modify the job in
// in order to invoke it sometime later.
// job: the job object
// job.options: the save options object
// job.options.overwrite: true -> overwrite
// job.options.force: true -> save even if jobdate < existingdate
// or overwrite: false
// jobendcallback: the function called at the end of the job.
// returns {'status':string,'message':string,'isSaved':boolean,
// 'resultArray':Array} in the jobendcallback arguments.
// TODO
},
loadDocument
:
function
()
{
// If a storage failed to save the document.
// this.job.storage: the storage informations.
// this.job.storage.userName: the user name.
// this.job.storage.password: the user password.
// this.job.applicant.ID: the applicant ID.
// this.job.fileName: the document name.
// this.job.fileContent: the document content.
// TODO
},
getDocumentList
:
function
()
{
var
newjob
=
{},
res
=
{
'
status
'
:
'
done
'
},
i
=
'
ind
'
;
for
(
i
in
priv
.
storageArray
)
{
newjob
=
that
.
cloneJob
();
newjob
.
maxtries
=
priv
.
maxtries
;
newjob
.
storage
=
priv
.
storageArray
[
i
];
newjob
.
callback
=
function
(
result
)
{
priv
.
returnsValuesArray
.
push
(
result
);
if
(
result
.
status
===
'
fail
'
)
{
res
.
status
=
'
fail
'
;
}
if
(
priv
.
returnsValuesArray
.
length
===
priv
.
length
)
{
// if this is the last callback
if
(
res
.
status
===
'
fail
'
)
{
that
.
fail
(
'
Unable to save all files.
'
,
0
);
}
else
{
that
.
done
();
}
}
};
that
.
addJob
(
newjob
)
;
}
};
that
.
loadDocument
=
function
()
{
// Load a document from several storages. It returns a document
// object containing all information of the document and its
// content. TODO will popup a window which will help us to choose
// the good file if the files are different.
// this.job.fileName: the document name we want to load.
// this.job.storage: the storage informations.
// this.job.storage.location: the dav storage location.
// this.job.storage.userName: the user name.
// this.job.storage.password: the user password.
// this.job.options.getContent: if true, also get the file content.
// document object is {'fileName':string,'fileContent':string,
// 'creationDate':date,'lastModified':date}
// TODO
},
removeDocument
:
function
()
{
var
newjob
=
{},
aredifferent
=
false
,
doc
=
{},
res
=
{
'
status
'
:
'
done
'
},
i
=
'
ind
'
;
doc
.
fileName
=
that
.
getFileName
();
for
(
i
in
priv
.
storageArray
)
{
newjob
=
that
.
cloneJob
();
newjob
.
maxtries
=
priv
.
maxtries
;
newjob
.
storage
=
priv
.
storageArray
[
i
];
newjob
.
callback
=
function
(
result
)
{
priv
.
returnsValuesArray
.
push
(
result
);
if
(
result
.
status
===
'
fail
'
)
{
res
.
status
=
'
fail
'
;
}
else
{
// check if the file are different
if
(
!
doc
.
fileContent
&&
!
doc
.
creationDate
&&
!
doc
.
lastModified
)
{
// if it is the first document loaded
doc
=
$
.
extend
({},
result
.
document
);
}
else
{
if
(
doc
.
fileContent
!==
result
.
document
.
fileContent
)
{
// if the document is different from the
// previous one
aredifferent
=
true
;
}
if
(
doc
.
creationDate
>
result
.
document
.
creationDate
)
{
// get older creation date
doc
.
creationDate
=
result
.
document
.
creationDate
;
}
if
(
doc
.
lastModified
<
result
.
document
.
lastModified
)
{
// get newer last modified
doc
.
fileContent
=
result
.
document
.
fileContent
;
doc
.
lastModified
=
result
.
document
.
lastModified
;
}
}
}
if
(
priv
.
returnsValuesArray
.
length
===
priv
.
length
)
{
// if this is the last callback
if
(
res
.
status
===
'
fail
'
)
{
that
.
fail
(
'
Unable to load all files.
'
,
0
);
}
else
{
if
(
!
aredifferent
)
{
that
.
done
(
doc
);
}
else
{
// TODO the files are different! Give options
// to know what do we do now!
// console.warn ('The files are different.');
that
.
done
(
doc
);
}
}
}
};
that
.
addJob
(
newjob
)
;
}
};
that
.
getDocumentList
=
function
()
{
// Get a document list from several storages. It returns a document
// array containing all the user documents informations.
// this.job.storage: the storage informations.
// this.job.storage.location: the dav storage location.
// this.job.storage.userName: the user name.
// this.job.storage.password: the user password.
// this.job.applicant.ID: the applicant id.
// the list is [object,object] -> object = {'fileName':string,
// 'lastModified':date,'creationDate':date}
// TODO
var
newjob
=
{},
res
=
{
'
status
'
:
'
done
'
},
i
=
'
ind
'
;
for
(
i
in
priv
.
storageArray
)
{
newjob
=
that
.
cloneJob
();
newjob
.
maxtries
=
priv
.
maxtries
;
newjob
.
storage
=
priv
.
storageArray
[
i
];
newjob
.
callback
=
function
(
result
)
{
priv
.
returnsValuesArray
.
push
(
result
);
if
(
result
.
status
===
'
fail
'
)
{
res
.
status
=
'
fail
'
;
}
if
(
priv
.
returnsValuesArray
.
length
===
priv
.
length
)
{
// if this is the last callback
if
(
res
.
status
===
'
fail
'
)
{
that
.
fail
(
'
Unable retrieve all lists.
'
,
0
);
}
else
{
that
.
done
(
result
.
list
);
}
}
};
that
.
addJob
(
newjob
)
;
}
};
that
.
removeDocument
=
function
()
{
// Remove a document from several storages.
// this.job.fileName: the document name we want to remove.
// this.job.storage: the storage informations.
// this.job.storage.location: the dav storage location.
// this.job.storage.userName: the user name.
// this.job.storage.password: the user password.
// this.job.applicant.ID: the applicant id.
// TODO
var
newjob
=
{},
res
=
{
'
status
'
:
'
done
'
},
i
=
'
ind
'
;
for
(
i
in
priv
.
storageArray
)
{
newjob
=
that
.
cloneJob
();
newjob
.
maxtries
=
priv
.
maxtries
;
newjob
.
storage
=
priv
.
storageArray
[
i
];
newjob
.
callback
=
function
(
result
)
{
priv
.
returnsValuesArray
.
push
(
result
);
if
(
result
.
status
===
'
fail
'
)
{
res
.
status
=
'
fail
'
;
}
if
(
priv
.
returnsValuesArray
.
length
===
priv
.
length
)
{
// if this is the last callback
if
(
res
.
status
===
'
fail
'
)
{
that
.
fail
(
'
Unable remove all files.
'
,
0
);
}
else
{
that
.
done
();
}
}
};
that
.
addJob
(
newjob
)
;
}
};
return
that
;
};
// end ReplicateStorage
////////////////////////////////////////////////////////////////////////////
...
...
@@ -564,15 +722,11 @@
Jio
.
addStorageType
(
'
local
'
,
function
(
options
)
{
return
new
LocalStorage
(
options
);
});
// add key to storageObject
Jio
.
addStorageType
(
'
dav
'
,
function
(
options
)
{
return
new
DAVStorage
(
options
);
});
// add key to storageObject
Jio
.
addStorageType
(
'
replicate
'
,
function
(
options
)
{
return
new
ReplicateStorage
(
options
);
});
})(
JIO
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment