Commit 5ab6b025 authored by Tristan Cavelier's avatar Tristan Cavelier Committed by Sebastien Robin

QUnit&Sinon.js localStorage complete (todo simplify tests).

Local storage save document bugs fixed.
parent f32b249f
......@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<title>JIO QUnit Test</title>
<title>JIO QUnit/Sinon Test</title>
<link rel="stylesheet" href="qunit/qunit-1.5.0.css" />
</head>
<body>
......@@ -12,6 +12,8 @@
<script type="text/javascript" src="unhosted/jio.js"></script>
<script type="text/javascript" src="unhosted/base64.js"></script>
<script type="text/javascript" src="unhosted/jio.storage.js"></script>
<script type="text/javascript" src="sinon/sinon-1.3.4.js"></script>
<script type="text/javascript" src="sinon/sinon-qunit-1.0.0.js"></script>
<script type="text/javascript" src="jiotests.js"></script>
</body>
</html>
// test ( "QUnit", function () {
// ok ( true , "QUnit works!" );
// });
var o = {};
var globalIndex = 1;
var globalTestIndex = 0;
var globalMax = 10;
var globalID = setInterval (function (){
if (globalIndex === globalTestIndex) return;
switch (globalIndex) {
default:
globalIndex++;
break;
case 1: globalTestIndex = globalIndex;
test ( "Jio simple methods", function () {
var ca = $.Callbacks();
console.log (ca);
o.jio = JIO.createNew();
ok ( o.jio, 'a new jio -> 1');
o.jio2 = JIO.createNew();
ok ( o.jio2, 'another new jio -> 2');
ok ( JIO.addStorageType('qunit', function(){}) ,
"adding storage type.");
deepEqual ( o.jio.isReady(), false, '1 must be not ready');
deepEqual ( o.jio.start(), true, '1 must be ready now');
o.jio2.start();
ok ( o.jio2.id !== o.jio.id, '1 and 2 must be different');
deepEqual ( o.jio.stop(), true, '1 must be stopped');
o.jio2.stop();
globalIndex++;
});
break;
case 2: globalTestIndex = globalIndex;
o.local = {};
o.local.jio = JIO.createNew();
o.local.jio.start();
test ( 'Jio Pub/Sub methods', function () {
stop();
var i = 1, ti = 0, m = 10;
var id = setInterval(function (){
switch(i){
default:
i++;
break;
case 1: // if(i===ti)return;ti=i; // force do only one time
o.local.pubsub_test = false;
o.local.pubsub_callback = o.local.jio.subscribe(
'pubsub_test',function () {
o.local.pubsub_test = true;
});
o.local.jio.publish('pubsub_test');
i++; break;
case 5: // wait a little
deepEqual (o.local.pubsub_test, true,
'subscribe & publish');
if (o.local.pubsub_test) {
i++;
} else {
i=m;
globalIndex = globalMax;
}
break;
case 6:
o.local.pubsub_test = false;
o.local.jio.unsubscribe('pubsub_test',
o.local.pubsub_callback);
o.local.jio.publish('pubsub_test');
i ++;
break;
case 9: // wait a little
deepEqual (o.local.pubsub_test, false,
'unsubscribe');
o.local.pubsub_test = !o.local.pubsub_test;
i++;
break;
case m:
o.local.jio.stop();
start();
globalIndex++;
clearInterval(id);
break;
}
},100);
});
break;
case 3: globalTestIndex = globalIndex;
test ( 'LocalStorage' , function () {
stop();
var i = 0, ti = -1, m = 20;
var id = setInterval(function (){
switch(i){
default:
i++;
break;
case 0:
o.local.jio = JIO.createNew({
'type':'local',
'userName':'myName'
},{'ID':'myApp'});
o.local.jio.start();
i++;
break;
case 1:i++;
//// test check name
o.local.check_test = null;
o.local.jio.checkNameAvailability(
{'userName':'myName','callback': function (result) {
o.local.check_test = result.isAvailable;
i = 3;
}});
break;
case 2: break;
case 3: i++;
deepEqual (o.local.check_test, true,
'name must be available');
//// test save document
o.local.jio.save_test = null;
o.local.jio.saveDocument(
{'fileName':'file','fileContent':'content','callback':
function (result) {
o.local.save_test = result.isSaved;
i = 5;
}});
break;
case 4: break;
case 5: i++;
deepEqual (o.local.save_test, true,
'document must be saved');
//// test check name
o.local.check_test = null;
o.local.jio.checkNameAvailability(
{'userName':'myName','callback': function (result) {
o.local.check_test = result.isAvailable;
i = 7;
}});
break;
case 6: break;
case 7: i++;
deepEqual (o.local.check_test, false,
'name must be unavailable');
//// test get list
o.local.getlist_test = null;
o.local.jio.getDocumentList(
{'callback': function (result) {
o.local.getlist_test = result.list;
i = 9;
}});
break;
case 8: break;
case 9: i++;
if (o.local.getlist_test) {
delete o.local.getlist_test[0].lastModified;
delete o.local.getlist_test[0].creationDate;
}
deepEqual (o.local.getlist_test,
[{'fileName':'file'}],
'list must contain one file');
//// test remove document
o.local.remove_test = null;
o.local.jio.removeDocument(
{'fileName':'file','callback': function (result) {
o.local.remove_test = result.isRemoved;
i = 11;
}});
break;
case 10: break;
case 11: i++;
deepEqual (o.local.remove_test, true,
'file must be removed');
break;
case m:
o.local.jio.stop();
start();
globalIndex++;
clearInterval(id);
break;
}
},100);
});
break;
case globalMax:
clearInterval(globalID);
break;
var cookieOrLocal = null;
var browserStorage = function () {
};
browserStorage.prototype = {
getItem: function (name) {
return JSON.parse(localStorage.getItem(name));
},
setItem: function (name,value) {
if (name)
return localStorage.setItem(name,JSON.stringify(value));
},
getAll: function() {
return localStorage;
},
deleteItem: function (name) {
if (name)
delete localStorage[name];
}
},100);
};
var cookieStorage = function () {
};
cookieStorage.prototype = {
getItem: function (name) {
var cookies = document.cookie.split(';');
for (var i in cookies) {
var x = cookies[i].substr(0, cookies[i].indexOf('='));
var y = cookies[i].substr(cookies[i].indexOf('=')+1);
x = x.replace(/^\s+|\s+$/g,"");
if( x == name ) return unescape(y);
}
return null;
},
setItem: function (name,value) {
// function to store into cookies
if (value != undefined) {
document.cookie = name+'='+JSON.stringify(value)+';domain='+
window.location.hostname+
';path='+window.location.pathname;
return true;
}
return false;
},
getAll: function() {
var retObject = {};
var cookies = document.cookie.split(':');
for (var i in cookies) {
var x = cookies[i].substr(0, cookies[i].indexOf('='));
var y = cookies[i].substr(cookies[i].indexOf('=')+1);
x = x.replace(/^\s+|\s+$/g,"");
retObject[x] = unescape(y);
}
return retObject;
},
deleteItem: function (name) {
document.cookie = name+'=null;domain='+window.location.hostname+
';path='+window.location.pathname+
';expires=Thu, 01-Jan-1970 00:00:01 GMT';
}
};
// set good localStorage
try {
if (localStorage.getItem) {
cookieOrLocal = new browserStorage();
} else {
cookieOrLocal = new cookieStorage();
}
}
catch (e) {
cookieOrLocal = new cookieStorage();
}
// //// QUnit Tests ////
module ('Jio Global tests');
test ( "Jio simple methods", function () {
// Test Jio simple methods
// It checks if we can create several instance of jio at the same
// time. Checks if they don't overlap informations, if they are
// started and stopped correctly and if they are ready when they
// have to be ready.
var o = {};
o.jio = JIO.createNew();
ok ( o.jio, 'a new jio -> 1');
o.jio2 = JIO.createNew();
ok ( o.jio2, 'another new jio -> 2');
ok ( JIO.addStorageType('qunit', function(){}) ,
"adding storage type.");
deepEqual ( o.jio.isReady(), false, '1 must be not ready');
deepEqual ( o.jio.start(), true, '1 must be ready now');
o.jio2.start();
ok ( o.jio2.id !== o.jio.id, '1 and 2 must be different');
deepEqual ( o.jio.stop(), true, '1 must be stopped');
o.jio2.stop();
});
test ( 'Jio Publish/Sububscribe/Unsubscribe methods', function () {
// Test the Publisher, Subscriber of a single jio.
// It is just testing if these function are working correctly.
// The test publishes an event, waits a little, and check if the
// event has been received by the callback of the previous
// subscribe. Then, the test unsubscribe the callback function from
// the event, and publish the same event. If it receives the event,
// the unsubscribe method is not working correctly.
var o = {};
o.jio = JIO.createNew();
o.jio.start();
var spy1 = this.spy();
// Subscribe the pubsub_test event.
o.callback = o.jio.subscribe('pubsub_test',spy1);
// And publish the event.
o.jio.publish('pubsub_test');
ok (spy1.calledOnce, 'subscribing & publishing, event called once');
o.jio.unsubscribe('pubsub_test',spy1);
o.jio.publish('pubsub_test');
ok (spy1.calledOnce, 'unsubscribing, same event not called twice');
o.jio.stop();
});
module ( 'Jio LocalStorage' );
test ('Check name availability', function () {
// Test if LocalStorage can check the availabality of a name.
// We remove MrCheckName from local storage, and checking must return true.
// We now add MrCheckName to local storage, and checking must return false.
var o = {}
var clock = this.sandbox.useFakeTimers();
// name must be available
o.f = function (result) {
deepEqual ( result.isAvailable, true,
'checking name availabality');
};
var spy = this.spy(o,'f');
cookieOrLocal.deleteItem ('jio/local/MrCheckName/jiotests/file');
o.jio = JIO.createNew({'type':'local','userName':'noname'},
{"ID":'noid'});
o.jio.start();
o.jio.checkNameAvailability(
{'userName':'MrCheckName','callback': o.f});
clock.tick(510);
ok (o.f.calledOnce, 'callback called once');
// name must not be available
o.f2 = function (result) {
deepEqual ( result.isAvailable, false,
'another checking name availabality');
};
var spy2 = this.spy(o,'f2');
cookieOrLocal.setItem ('jio/local/MrCheckName/jiotests/file',{});
o.jio.checkNameAvailability(
{'userName':'MrCheckName','callback': o.f2});
clock.tick(510);
ok (o.f2.calledOnce, 'another callback called once');
o.jio.stop();
});
test ('Document save', function () {
// Test if LocalStorage can save documents.
// We launch a saving to localstorage and we check if the file is
// realy saved. Then save again and check if
var o = {}
var clock = this.sandbox.useFakeTimers();
// save and check document existence
o.f = function (result) {
deepEqual ( result.isSaved, true,
'saving document');
};
var spy = this.spy(o,'f');
cookieOrLocal.deleteItem ('jio/local/MrSaveName/jiotests/file');
clock.tick(200);
o.jio = JIO.createNew({'type':'local','userName':'MrSaveName'},
{"ID":'jiotests'});
o.jio.start();
o.jio.saveDocument(
{'fileName':'file','fileContent':'content','callback': o.f});
clock.tick(510);
ok (o.f.calledOnce, 'callback called once');
// check content
var tmp = cookieOrLocal.getItem ('jio/local/MrSaveName/jiotests/file');
deepEqual (tmp,{'fileName':'file','fileContent':'content',
'lastModified':200,'creationDate':200}, 'check content');
// re-save and check modification date
o.f2 = function (result) {
deepEqual ( result.isSaved, true,
'saving document again');
};
var spy2 = this.spy(o,'f2');
o.jio.saveDocument(
{'fileName':'file','fileContent':'content','callback': o.f2});
clock.tick(510);
ok (o.f2.calledOnce, 'another callback called once');
// check content
tmp = cookieOrLocal.getItem ('jio/local/MrSaveName/jiotests/file');
deepEqual (tmp,{'fileName':'file','fileContent':'content',
'lastModified':710,'creationDate':200}, 'check content');
o.jio.stop();
});
test ('Document load', function () {
// Test if LocalStorage can load documents.
// We launch a loading from localstorage and we check if the file is
// realy loaded.
var o = {}
var clock = this.sandbox.useFakeTimers();
// load a non existing file
o.f = function (result) {
deepEqual ( result.status, 'fail',
'loading document');
};
var spy = this.spy(o,'f');
cookieOrLocal.deleteItem ('jio/local/MrLoadName/jiotests/file');
o.jio = JIO.createNew({'type':'local','userName':'MrLoadName'},
{"ID":'jiotests'});
o.jio.start();
o.jio.loadDocument(
{'fileName':'file','callback': o.f});
clock.tick(510);
ok (o.f.calledOnce, 'callback called once');
// re-load file after saving it manually
var tmp = cookieOrLocal.setItem ('jio/local/MrLoadName/jiotests/file',
{'fileName':'file','fileContent':'content',
'lastModified':1234,'creationDate':1000});
o.f2 = function (result) {
deepEqual ( result.document,
{'fileName':'file','fileContent':'content',
'lastModified':1234,'creationDate':1000},
'loading document again, check content');
};
var spy2 = this.spy(o,'f2');
o.jio.loadDocument(
{'fileName':'file','callback': o.f2});
clock.tick(510);
ok (o.f2.calledOnce, 'another callback called once');
o.jio.stop();
});
test ('Document remove', function () {
// Test if LocalStorage can remove documents.
// We launch a remove from localstorage and we check if the file is
// realy removed.
var o = {}
var clock = this.sandbox.useFakeTimers();
o.f = function (result) {
deepEqual ( result.isRemoved, true,
'removing document');
};
var spy = this.spy(o,'f');
cookieOrLocal.setItem ('jio/local/MrRemoveName/jiotests/file',{});
o.jio = JIO.createNew({'type':'local','userName':'MrRemoveName'},
{"ID":'jiotests'});
o.jio.start();
o.jio.removeDocument(
{'fileName':'file','callback': o.f});
clock.tick(510);
ok (o.f.calledOnce, 'callback called once');
var tmp = cookieOrLocal.getItem ('jio/local/MrRemoveName/jiotests/file');
ok (!tmp, 'check no content');
o.jio.stop();
});
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* sinon-qunit 1.0.0, 2010/12/09
*
* @author Christian Johansen (christian@cjohansen.no)
*
* (The BSD License)
*
* Copyright (c) 2010-2011, Christian Johansen, christian@cjohansen.no
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Christian Johansen nor the names of his contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*global sinon, QUnit, test*/
sinon.assert.fail = function (msg) {
QUnit.ok(false, msg);
};
sinon.assert.pass = function (assertion) {
QUnit.ok(true, assertion);
};
sinon.config = {
injectIntoThis: true,
injectInto: null,
properties: ["spy", "stub", "mock", "clock", "sandbox"],
useFakeTimers: true,
useFakeServer: false
};
(function (global) {
var qTest = QUnit.test;
QUnit.test = global.test = function (testName, expected, callback, async) {
if (arguments.length === 2) {
callback = expected;
expected = null;
}
return qTest(testName, expected, sinon.test(callback), async);
};
}(this));
/*
JIO: Javascript I/O. A library to load, edit, save documents to
multiple storage backends using a common interface
*/
(function() {
////////////////////////////////////////////////////////////////////////////
// Define constants
var C = {
'STATUS_INITIAL': 'initial',
'STATUS_DONE': 'done',
'STATUS_ONGOING': 'ongoing',
'STATUS_FAIL': 'fail',
'METHOD_AVAILABLE': 'userNameAvailable',
'METHOD_SAVE': 'save',
'METHOD_LOAD': 'load',
'METHOD_GETLIST': 'getList',
'METHOD_DELETE': 'delete',
'EVENT_PREFIX': 'jio:',
'EVENT_JOB_DONE': 'job_done',
'EVENT_JOB_FAIL': 'job_fail',
'EVENT_START_AVAILABLE': 'start_usernameavailable',
'EVENT_STOP_AVAILABLE': 'stop_usernameavailable',
'EVENT_START_SAVING': 'start_saving',
'EVENT_STOP_SAVING': 'stop_saving',
'EVENT_START_LOADING': 'start_loading',
'EVENT_STOP_LOADING': 'stop_loading',
'EVENT_START_GETTINGLIST': 'start_gettinglist',
'EVENT_STOP_GETTINGLIST': 'stop_gettinglist',
'EVENT_START_DELETING': 'start_deleting',
'EVENT_STOP_DELETING': 'stop_deleting',
'FUNC_AVAILABLE':'userNameAvailableFromJob',
'FUNC_SAVE':'saveDocumentFromJob',
'FUNC_LOAD':'loadDocumentFromJob',
'FUNC_GETLIST':'getListFromJob',
'FUNC_DELETE':'deleteDocumentFromJob',
'DEFAULT_INTERVAL_DELAY': 100
};
C['DEFAULT_CONST_OBJECT'] = {};
C['DEFAULT_CONST_OBJECT'][C.METHOD_AVAILABLE] = {
'STARTEVENT':C.EVENT_START_AVAILABLE,
'STOPEVENT':C.EVENT_STOP_AVAILABLE,
'FUNC':C.FUNC_AVAILABLE};
C['DEFAULT_CONST_OBJECT'][C.METHOD_SAVE] = {
'STARTEVENT':C.EVENT_START_SAVING,
'STOPEVENT':C.EVENT_STOP_SAVING,
'FUNC':C.FUNC_SAVE};
C['DEFAULT_CONST_OBJECT'][C.METHOD_LOAD] = {
'STARTEVENT':C.EVENT_START_LOADING,
'STOPEVENT':C.EVENT_STOP_LOADING,
'FUNC':C.FUNC_LOAD};
C['DEFAULT_CONST_OBJECT'][C.METHOD_GETLIST] = {
'STARTEVENT':C.EVENT_START_GETTINGLIST,
'STOPEVENT':C.EVENT_STOP_GETTINGLIST,
'FUNC':C.FUNC_GETLIST};
C['DEFAULT_CONST_OBJECT'][C.METHOD_DELETE] = {
'STARTEVENT':C.EVENT_START_DELETING,
'STOPEVENT':C.EVENT_STOP_DELETING,
'FUNC':C.FUNC_DELETE};
var storageTypeObject = {};
var queue = null; // the jobs manager
var jioStorage = null;
var jioApplicant = null;
// TODO stock statusobject for this tab ? faster ? better ?
////////////////////////////////////////////////////////////////////////////
// JIO main object. Contains all IO methods
var JIO = {
listener: null,
/**
* prepare and return the jio object
* @param jioData : text or json content of jio.json or method to get it
* @param applicant : (optional) information about the person/application needing this JIO object (used to limit access)
* @return JIO object
*/
initialize: function(jioData, applicant) {
// JIO initialize
// jioData: object containing storage informations
// {type:,userName:,etc}
// applicant: a string of the applicant name
jioApplicant = {"ID":applicant};
if (!applicant) return false; // TODO to know: return false or alert user ?
if (!dependenceLoaded()) {
setTimeout(function() {JIO.initialize(jioData, jioApplicant);},50);
} else {
switch(typeof jioData) {
case "string" :
jioStorage = JSON.parse(jioData);
break;
case "object" :
jioStorage = jioData;
break;
case "function" :
jioStorage = jioData();
break;
default:
alert("Error while getting jio.json content");
break;
}
this.addStorageType('local',function (stor,app) {
return new JIO.LocalStorage(stor,app);
});
queue = new JIO.JobQueue();
this.listener = new JIO.JobListener(C.DEFAULT_INTERVAL_DELAY);
this.listener.start();
}
},
/**
* return the state of JIO object
* @return true if ready, false otherwise
*/
isReady: function() {if(queue && jioStorage) return true; return false;},
//IO functions
getLocation: function() {return this.location},
userNameAvailable: function(name) {
return queue.addJob (jioStorage,
new JIO.JobUser(C.METHOD_AVAILABLE,name));
},
loadDocument: function(fileName) {
return queue.addJob (jioStorage,
new JIO.Job(C.METHOD_LOAD,fileName));
},
saveDocument: function(fileContent, fileName) {
return queue.addJob (jioStorage,
new JIO.Job(C.METHOD_SAVE,fileName,fileContent));
},
deleteDocument: function(fileName) {
return queue.addJob (jioStorage,
new JIO.Job(C.METHOD_DELETE,fileName));
},
getDocumentList: function() {
return queue.addJob (jioStorage,
new JIO.Job(C.METHOD_GETLIST));
},
publish: function(event,obj) {
pubSub.publish(event,obj);
},
subscribe: function(event,func) {
pubSub.subscribe(event,func);
},
unsubscribe: function(event) {
pubSub.unsubscribe(event);
},
getApplicant: function() {
return jioApplicant;
},
getConstantObject: function() {
// return a copy of the constants
return JSON.parse(JSON.stringify(C));
},
addStorageType: function(newType,storageCreator) {
storageTypeObject[newType] = storageCreator;
}
}
// End JIO object
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Publisher Subscriber
var pubSub = new function () {
var topics = {};
$.JIOPubSub = function (id) {
var callbacks;
var topic = id && topics[id];
if (!topic) {
callbacks = $.Callbacks();
topic = {
publish: callbacks.fire,
subscribe: callbacks.add,
unsubscribe: callbacks.remove
};
if (id) {
topics[id] = topic;
}
}
return topic;
}
this.publish = function (eventname,obj) {
$.JIOPubSub(C.EVENT_PREFIX + eventname).publish(obj);
},
this.subscribe = function (eventname,func) {
$.JIOPubSub(C.EVENT_PREFIX + eventname).subscribe(func);
},
this.unsubscribe = function (eventname) {
$.JIOPubSub(C.EVENT_PREFIX + eventname).unsubscribe();
}
};
// End Publisher Subscriber
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// JIO Job Listener
JIO.JobListener = function (interval) {
// A little daemon which will start jobs from the joblist
this.interval = (interval ? interval : 200);
this.id = null;
};
JIO.JobListener.prototype = {
/**
* Set the time between two joblist check in millisec
*/
setIntervalDelay: function (interval) {
this.interval = interval;
},
/**
* Wait for jobs in the joblist inside the localStorage
*/
start: function () {this.id = setInterval (function(){
// if there are jobs
if (localStorage.joblist &&
localStorage.joblist !== '[]') {
queue.invokeAll();
}
},this.interval);},
/**
* Stop the listener
*/
stop: function () {
console.log ('stop listening');
clearInterval (this.id);
this.id = null;
}
};
// End Job Listener
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Job & JobQueue
/**
* Function parameters
* Parameter storage: Constains {type:local/dav/...,userName:...,...}.
* Must be an 'object'.
* Parameter job: Contains {[id:..,]method:save/load/delete,
* fileName:name,fileContent:content[,status:"initial"]}.
* May be a 'string' or an 'object'
*/
JIO.Job = function (method,filename,filecontent) {
// Job Constructor & initializer
var tmp = {'id':0}
tmp['method'] = method;
tmp['fileName'] = filename;
tmp['fileContent'] = filecontent;
tmp['status'] = C.STATUS_INITIAL;
return tmp;
};
JIO.JobUser = function (method,username) {
// JobUser Constructor & initializer
var tmp = {'id':0}
tmp['method'] = method;
tmp['userName'] = username;
tmp['status'] = C.STATUS_INITIAL;
return tmp;
};
JIO.JobQueue = function () {
//// set first job ID
this.jobid = 1;
var localjoblist = getJobArrayFromLocalStorage();
if (localjoblist.length > 0) {
this.jobid = localjoblist[localjoblist.length - 1].id + 1;
};
localjoblist = null; // it is not necessary to fill memory with
// unused vars
// reset all jobs' status to initial
this.resetAll();
var returnedJobAnalyse = function (job) {
// analyse the [job]
//// analysing job method
if (!C.DEFAULT_CONST_OBJECT[job.method])
return false;
if (!ifRemainingJobs(job.method)) {
var statusobject = getStatusObjectFromLocalStorage();
statusobject[job.method] = C.STATUS_DONE;
setStatusObjectToLocalStorage(statusobject);
switch (job.method) {
case C.METHOD_GETLIST:
// TODO merge list if necessary (we can create
// this.getList, deleted after sending the event,
// filled with done event) -> merge here or show
// possible actions (manually, auto, ...)
JIO.publish (C.DEFAULT_CONST_OBJECT[job.method].STOPEVENT/*, merged list*/);
// deleting list
return;
default: // if it was not a specific method
JIO.publish (C.DEFAULT_CONST_OBJECT[job.method].STOPEVENT);
return;
}
}
//// end analyse
};
//// subscribe an event
var t = this;
JIO.subscribe (C.EVENT_JOB_DONE, function (job) {
console.log (C.EVENT_JOB_DONE+'_received');
t.done(job);
returnedJobAnalyse (job);
});
JIO.subscribe (C.EVENT_JOB_FAIL, function (job) {
console.log (C.EVENT_JOB_FAIL+'_received');
t.fail(job);
returnedJobAnalyse (job);
});
//// end subscribing
};
JIO.JobQueue.prototype = {
addJob: function (storage,job) {
// Add a job to the Job list
// storage : the storage object
// job : the job object (may be a 'string')
console.log ('addJob');
//// Adding job to the list in localStorage
// transforming job to an object
if ((typeof job) === 'string') job = JSON.parse(job);
// create joblist in local storage if unset
if (!localStorage.joblist) localStorage.joblist = "[]";
// set job id
job.id = this.jobid;
this.jobid ++;
// set job storage
job.storage = storage;
// save the new job
var localjoblist = getJobArrayFromLocalStorage();
localjoblist.push(job);
setJobArrayToLocalStorage (localjoblist);
//// job added
}, // end addJob
resetAll: function () {
// reset All job to 'initial'
console.log ('resetAll');
var jobarray = getJobArrayFromLocalStorage();
for (var i in jobarray) {
jobarray[i].status = C.STATUS_INITIAL;
}
setJobArrayToLocalStorage(jobarray);
},
invokeAll: function () {
// Do all jobs in the list
//// do All jobs
var jobarray = getJobArrayFromLocalStorage();
for (var i in jobarray) {
if (jobarray[i].status === C.STATUS_INITIAL) {
jobarray[i].status = C.STATUS_ONGOING;
this.invoke(jobarray[i]);
}
}
setJobArrayToLocalStorage(jobarray);
//// end, continue doing jobs asynchronously
},
invoke: function (job) {
// Do a job
console.log ('invoke');
//// analysing job method
// browsing methods
if (!C.DEFAULT_CONST_OBJECT[job.method])
// TODO do an appropriate error reaction ? unknown job method
return false;
var statusobject = getStatusObjectFromLocalStorage ();
objectDump(statusobject);
if (statusobject[job.method] === C.STATUS_DONE) {
statusobject[job.method] = C.STATUS_ONGOING;
setStatusObjectToLocalStorage(statusobject);
JIO.publish (C.DEFAULT_CONST_OBJECT[job.method].STARTEVENT);
}
// create an object and use it to save,load,...!
createStorageObject(job.storage,jioApplicant)[
C.DEFAULT_CONST_OBJECT[job.method].FUNC
](job,true); // overwrite
//// end method analyse
},
done: function (job) {
// This job is supposed done, we can remove it from localStorage.
console.log ('done');
var localjoblist = getJobArrayFromLocalStorage ();
// remove the job from the job list
localjoblist = arrayRemoveValues (localjoblist,function (o) {
return (o.id === job.id);
});
// save to local storage
setJobArrayToLocalStorage (localjoblist);
},
fail: function (job) {
// This job cannot be done, change its status into localStorage.
console.log ('fail');
var localjoblist = getJobArrayFromLocalStorage ();
//// find this unique job inside the list
for (var i in localjoblist) {
if (localjoblist[i].id === job.id) {
localjoblist[i] = job;
break;
}
}
// save to local storage
setJobArrayToLocalStorage (localjoblist);
},
clean: function () {
// Clean the job list, removing all jobs that have failed.
// It also change the localStorage Job list
setJobArrayToLocalStorage(
arrayRemoveValues(
getJobArrayFromLocalStorage(),
function(j){return (j.status === C.STATUS_FAIL);}
)
);
}
}; // end JIO.JobQueue
// End Job & JobQueue
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// LOCAL STORAGE
/**
* Class LocalStorage
* @class provides usual API to save/load/delete documents on the localStorage
* @param storagedata : object containing every element needed to build the storage
* @param applicant : object containing inforamtion about the person/application needing this JIO object
*/
JIO.LocalStorage = function(storagedata,applicant) {
this.applicant = applicant;
//// check user's localStorage part
this.userName = storagedata.userName;
if(!localStorage.getItem(this.userName)) {
localStorage[this.userName] = '{}'; // Create user
}
//// end check
}
JIO.LocalStorage.prototype = {
getDocumentsFromLocalStorage: function() {
return JSON.parse(localStorage[this.userName]);
},
writeToLocalStorage: function(documents) {
localStorage[this.userName] = JSON.stringify(documents);
}
}
/**
* check if an user already exist
* @param name : the name you want to check
* @return true if the name is free, false otherwise
*/
JIO.LocalStorage.prototype[C.FUNC_AVAILABLE] = function(job) {
setTimeout(function () {
if (localStorage[job.userName]) {
job.status = C.STATUS_DONE;
job.message = '' + job.userName + ' is not available.';
job.userIsAvailable = false;
JIO.publish (C.EVENT_JOB_DONE,job);
return false;
}
job.status = C.STATUS_DONE;
job.message = '' + job.userName + ' is available.';
job.userIsAvailable = true;
JIO.publish (C.EVENT_JOB_DONE,job);
return true;
}, C.DEFAULT_INTERVAL_DELAY);
};
/**
* save a document in the local storage from a job
* @param job : contains all the information to save the document
* @param overwrite : a boolean set to true if the document has to be overwritten
*/
JIO.LocalStorage.prototype[C.FUNC_SAVE] = function(job, overwrite) {
var t = this;
// wait a little in order to let some other instructions to continue
setTimeout(function () {
var documents = t.getDocumentsFromLocalStorage();
// TODO check modification date !!!
if(!documents[job.fileName]) { // create document
documents[job.fileName] = {
'fileName': job.fileName,
'content': job.fileContent,
'creationDate': Date.now(),
'lastModified': Date.now()
}
t.writeToLocalStorage();
job.status=C.STATUS_DONE;
job.message='success';
JIO.publish(C.EVENT_JOB_DONE,job);
return;
} else {
if(overwrite === true) { // overwrite
documents[job.fileName].lastModified = Date.now();
documents[job.fileName].content = job.fileContent;
t.writeToLocalStorage(documents);
job.status=C.STATUS_DONE;
job.message='success';
JIO.publish(C.EVENT_JOB_DONE,job);
return;
} else {
job.status=C.STATUS_FAIL;
job.message='Document already exists.';
job.errno=403;
JIO.publish(C.EVENT_JOB_FAIL,job);
return;
}
}
}, C.DEFAULT_INTERVAL_DELAY);
return;
} // end saveDocumentFromJob
/**
* load a document in the storage, copy the content into the job,
* then the function sends the job with an event.
* @param job : the job that contains all informations about
* loading operation.
*/
JIO.LocalStorage.prototype[C.FUNC_LOAD] = function(job) {
var t = this;
// wait a little in order to let some other instructions to continue
setTimeout(function () {
var documents = t.getDocumentsFromLocalStorage();
if(!documents[job.fileName]) {
job.status=C.STATUS_FAIL;
job.errno=404;
job.message='Document not found.';
JIO.publish (C.EVENT_JOB_FAIL,job);
} else {
job.status=C.STATUS_DONE;
job.file=documents[job.fileName];
job.message='success';
JIO.publish (C.EVENT_JOB_DONE,job);
}
}, C.DEFAULT_INTERVAL_DELAY)
}; // end loadDocumentFromJob
/**
* load the list of the documents in this storage
* @param job : the job that contains all informations about
* getting list operation.
*/
JIO.LocalStorage.prototype[C.FUNC_GETLIST] = function(job) {
// TODO update it
var t = this;
setTimeout(function () {
var documents = t.getDocumentsFromLocalStorage();
job.list = [];
for (var key in documents) {
job.list.push ({
'fileName':documents[key].fileName,
'creationDate':documents[key].creationDate,
'lastModified':documents[key].lastModified
});
}
JIO.publish (C.EVENT_JOB_DONE,job);
}, C.DEFAULT_INTERVAL_DELAY);
}; // end getListFromJob
/**
* Delete a document or a list of documents from the storage
* @param fileName : fileName to delete
* @param options : optional object containing
*/
JIO.LocalStorage.prototype[C.FUNC_DELETE] = function(job) {
// TODO update it
var t = this;
setTimeout (function () {
var documents = t.getDocumentsFromLocalStorage();
// TODO is it realy a problem if we try to delete a deleted file ?
if (!documents[job.fileName]) {
job.status = C.STATUS_FAIL;
job.errno = 404;
job.message = 'Document not found.';
JIO.publish (C.EVENT_JOB_FAIL,job);
return;
}
delete documents[job.fileName];
t.writeToLocalStorage(documents);
job.status = C.STATUS_DONE;
job.message = 'success';
JIO.publish (C.EVENT_JOB_DONE,job);
return;
}, C.DEFAULT_INTERVAL_DELAY);
}; // end deleteDocumentFromJob
// END LOCAL STORAGE
////////////////////////////////////////////////////////////////////////////
///////////
// Tools //
///////////
var dependenceLoaded = function () {
// tests if the dependencies are loaded
try {
// check jQuery
if ($)
return true;
else return false;
} catch (e) {
return false;
}
};
var ifRemainingJobs = function(method) {
// check if it remains [method] jobs (save,load,...)
// method : can me 'string' or 'undefined'
// undefined -> test if there is at least one job
// string -> test if there is at least one [method] job
if (!method) return (localStorage.joblist !== '[]');
joba = getJobArrayFromLocalStorage();
for (var k in joba) {
if (joba[k].method === method &&
joba[k].status !== C.STATUS_FAIL) return true;
}
return false;
};
/**
* Create a storage with [data] informations
* @param data : information found in jio.json and needed to create the storage
* @param applicant : information about the person/application needing this JIO object (allow limited access)
*/
function createStorageObject(data,applicant) {
if (!storageTypeObject[data.type])
return null; // error!
return storageTypeObject[data.type](data,applicant);
}
var checkOptionObject = function (current,defaultopts) {
if(!current) {
return defaultopts;
}
for (var k in defaultopts) {
if (current[k] === undefined)
current[k] = defaultopts[k];
}
return current;
};
var getJobArrayFromLocalStorage = function () {
var localjoblist = [];
if (localStorage.joblist)
localjoblist = JSON.parse(localStorage.joblist);
return localjoblist;
};
var setJobArrayToLocalStorage = function (jobarray) {
localStorage.joblist = JSON.stringify(jobarray);
};
var getStatusObjectFromLocalStorage = function () {
var tmp = {};
if (localStorage.status) {
tmp = JSON.parse (localStorage.status);
} else {
for (var Method in C.DEFAULT_CONST_OBJECT) {
tmp[Method] = C.STATUS_DONE;
}
}
return tmp;
};
var setStatusObjectToLocalStorage = function (statusobject) {
localStorage.status = JSON.stringify(statusobject);
};
var arrayRemoveValues = function (a,testfunc) {
// Removes the values from [a] where [testfunc(value)] returns true.
var isArray = function (a) {
return Object.prototype.toString.apply(a) === '[object Array]';
};
if (!isArray(a)) return a;
var na = [];
for (var k in a) {
if (!testfunc(a[k]))
na.push (a[k]);
}
return na;
};
// TODO DEBUG we can remove this function
var objectDump = function (o) {
console.log (JSON.stringify(o));
};
// the name to use for the framework. Ex : JIO.initialize(...), JIO.loadDocument...
window.JIO = JIO;
})();
......@@ -25,6 +25,10 @@
'start_event':'start_loading',
'stop_event':'stop_loading',
'retvalue':'fileContent' }, // returns the file content 'string'
'getDocument': {
'start_event':'start_gettingDocument',
'stop_event':'stop_gettingDocument',
'retvalue':'document' }, // returns the document object
'getDocumentList': {
'start_event':'start_gettingList',
'stop_event':'stop_gettingList',
......@@ -600,7 +604,7 @@
// or in the storage set at init. At the end of the job,
// 'job_done' will be sent with this job and its 'isAvailable'
// return value.
// options.storage : the storage where to remove (optional)
// options.storage : the storage where to check (optional)
// options.applicant : the applicant (optional)
// options.callback(result) : called to get the result.
......@@ -628,7 +632,7 @@
// Load a document in the storage set in [options]
// or in the storage set at init. At the end of the job,
// 'job_done' will be sent with this job.
// options.storage : the storage where to remove (optional)
// options.storage : the storage where to save (optional)
// options.applicant : the applicant (optional)
// options.callback(result) : called to get the result.
......@@ -658,13 +662,14 @@
// or in the storage set at init. At the end of the job,
// 'job_done' will be sent with this job and its 'fileContent'
// return value.
// options.storage : the storage where to remove (optional)
// options.storage : the storage where to load (optional)
// options.applicant : the applicant (optional)
// options.callback(result) : called to get the result.
// jio.loadDocument({'fileName':'file','callback':
// function (result) { alert('content: '+
// result.fileContent); }});
// result.doc.fileContent + ' creation date: ' +
// result.doc.creationDate); }});
console.log ('load');
if (!this.isReady()) return null;
......@@ -676,19 +681,15 @@
},options);
// check dependencies
if ( settings.fileName && settings.storage && settings.applicant) {
return jioGlobalObj.queue.addJob ( new Job ( settings ) );
return this.queue.addJob ( new Job ( settings ) );
}
return null;
},
getDocument: function ( options ) {
// TODO (don't forgot to add this method to constants)
},
getDocumentList: function ( options ) {
// Get a document list of the user in the storage set in [options]
// or in the storage set at init.
// options.storage : the storage where to remove (optional)
// options.storage : the storage where to get the list (optional)
// options.applicant : the applicant (optional)
// options.callback(result) : called to get the result.
......
......@@ -34,6 +34,10 @@
// if the name already exists, it is not available.
// job: the job object
// job.userName: the name we want to check.
// jobendcallback : the function called at the end of the job.
// returns {'status':string,'message':string,'isAvailable':boolean}
// in the jobendcallback arguments.
// wait a little in order to simulate asynchronous operation
setTimeout(function () {
......@@ -73,6 +77,10 @@
// 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}
// in the jobendcallback arguments.
var settings = $.extend({'overwrite':true,
'force':false},job.options);
......@@ -88,8 +96,8 @@
doc = {
'fileName': job.fileName,
'fileContent': job.fileContent,
'creationDate': Date.now (),
'lastModified': Date.now ()
'creationDate': job.lastModified,
'lastModified': job.lastModified
}
// writing
jioGlobalObj.localStorage.setItem(
......@@ -119,7 +127,7 @@
return;
}
// overwriting
doc.lastModified = Date.now();
doc.lastModified = job.lastModified;
doc.fileContent = job.fileContent;
// writing
jioGlobalObj.localStorage.setItem(
......@@ -145,9 +153,17 @@
}, // end saveDocument
loadDocument: function ( job, jobendcallback ) {
// load a document in the storage, copy the content into the job
// job : the job
// Load a document from the storage. It returns a document object
// containing all information of the document and its content.
// job : the job object
// job.fileName : the document name we want to load.
// jobendcallback : the function called at the end of the job.
// returns {'status':string,'message':string,'document':object}
// in the jobendcallback arguments.
// 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 () {
......@@ -164,7 +180,11 @@
} else {
res.status = job.status = 'done';
res.message = 'Document loaded.';
res.fileContent = doc.fileContent;
res.document = {
'fileContent': doc.fileContent,
'fileName': doc.fileName,
'creationDate': doc.creationDate,
'lastModified': doc.lastModified};
jobendcallback(job);
job.callback(res);
}
......@@ -172,6 +192,17 @@
}, // end loadDocument
getDocumentList: function ( job, jobendcallback) {
// Get a document list from the storage. It returns a document
// array containing all the user documents informations, but not
// their content.
// job : the job object
// jobendcallback : the function called at the end of the job.
// returns {'status':string,'message':string,'list':array}
// in the jobendcallback arguments.
// the list is [object,object] -> object = {'fileName':string,
// 'lastModified':date,'creationDate':date}
var t = this;
setTimeout(function () {
var res = {};
......@@ -198,6 +229,13 @@
}, // end getDocumentList
removeDocument: function ( job, jobendcallback ) {
// Remove a document from the storage.
// job : the job object
// jobendcallback : the function called at the end of the job.
// returns {'status':string,'message':string,'isRemoved':boolean}
// in the jobendcallback arguments.
var t = this;
setTimeout (function () {
var res = {};
......@@ -228,7 +266,7 @@
}
};
// add key to storageObject
// add key to storageObjectType of global jio
Jio.addStorageType('local', function (options) {
return new LocalStorage(options);
});
......
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