Commit 9e6a4d29 authored by Lingnan Wu's avatar Lingnan Wu

add the required js for using the jio and also change the main page for jio storage

parent 2eb0b865
......@@ -5,6 +5,16 @@
<link rel="stylesheet" href="css/themes/default/jquery.mobile-1.1.0.css" />
<script src="js/jquery.js"></script>
<script src="js/jquery.mobile-1.1.0.js"></script>
<script type="text/javascript" src="lib/jstorage/jstorage.js"></script>
<script type="text/javascript" src="src/localorcookiestorage.js"></script>
<script type="text/javascript" src="src/jio.js"></script>
<script type="text/javascript" src="lib/base64/base64.js"></script>
<script type="text/javascript" src="lib/sjcl/sjcl.min.js"></script>
<script type="text/javascript" src="src/jio.storage.js"></script>
<script type="text/javascript" src="js/officejs.js"></script>
</head>
<body>
<!-- Home -->
......@@ -17,23 +27,33 @@
<div data-role="content" style="padding: 15px">
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<label for="textinput1">
<label class="control-label" for="input_json_storage">
JSON Storage
</label>
<input id="textinput1" placeholder="" value="{&quot;type&quot;:&quot;local&quot;,&quot;userName&quot;:&quot;lingnan&quot;}"
type="text">
<input class="input-xlarge"
type="text" name="JSONstorage" id="input_json_storage" placeholder="storage" value="{&quot;type&quot;:&quot;local&quot;,&quot;userName&quot;:&quot;lingnan&quot;}" >
</fieldset>
</div>
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<label for="textinput2">
<label class="control-label" for="JSONapplicantID">
JSON Application
</label>
<input id="textinput2" placeholder="" value="{&quot;ID&quot;:&quot;jiotests&quot;}"
type="text">
<input class="input-xlarge"
type="text" name="JSONapplicant" id="input_json_applicant"
value="{&quot;ID&quot;:&quot;jiotests&quot;}"
placeholder="applicant" />
</fieldset>
</div>
<a href="#tools" data-role="button" data-transition="slideup" data-theme="b">Go Docs </a>
<a href="#tools" data-role="button" data-transition="slideup" data-theme="b" onclick="console.log('asdf');OfficeJS.setJio(
$('#input_json_storage').attr('value'),
$('#input_json_applicant').attr('value'));">Create New JIO</a>
</div>
</div>
......
define ('Base64',['Base64API'], function () {
return Base64;
});
\ No newline at end of file
(function () {
// Tools
var baseName = function (file_name) {
var split = file_name.split('.');
if (split.length > 1) {
split.length -= 1;
return split.join('.');
} else {
return file_name;
}
};
/**
* OfficeJS Object
*/
window.OfficeJS = (function () {
var that = {}, priv = {};
// Attributes //
priv.preference_object = {
document_lister:'slickgrid',
edit_preferences:'simplepreferenceeditor',
text_editor:'elrte',
img_editor:'svg-edit',
spreadsheet:'jquery-sheet',
power_point:'power-point'
};
priv.app_object = {
topnavbar: {
type:'nav',
path:'component/top_nav_bar.html',
gadget_id:'page-top_nav_bar'
},
leftnavbar: {
type:'nav',
path:'component/left_nav_bar.html',
gadget_id:'page-left_nav_bar',
bar_tools: false,
update: function () {
var elmt;
if (priv.isJioSet() && !this.bar_tools) {
// add tools to nav bar
elmt = document.querySelector ('script#left-nav-tools');
document.querySelector ('#left-nav-bar').innerHTML +=
elmt.innerHTML;
this.bar_tools = true;
}
}
},
login: {
type:'loader',
path:'component/login.html',
gadget_id:'page-content',
getContent: function () {
var tmp = {
user_name: 'NoName',
password: 'NoPwd'
};
// NOTE : stringify or not ?
return JSON.stringify (tmp);
}
},
about: {
type:'viewer',
path:'component/about.html',
gadget_id:'page-content'
},
contact: {
type:'viewer',
path:'component/contact.html',
gadget_id:'page-content'
},
simplepreferenceeditor: {
// NOTE
type:'editor',
path:'',
// ...
},
elrte: {
type:'editor', // means it can edit a content
path:'component/elrte.html',
gadget_id:'page-content',
ext:'html',
element:'#elrte_editor',
getContent: function () {
$(this.element).elrte('updateSource');
return $(this.element).elrte('val');
},
setContent: function (content) {
$(this.element).elrte('val', content);
},
onload: function (param) {
// FIXME : wait for initialization end
setTimeout(function () {
if (typeof param.file_name !== 'undefined') {
$('#input_file_name').attr('value',
baseName(param.file_name));
that.load(baseName(param.file_name));
} else {
$('#input_file_name').attr(
'value','untitled');
}
},1000);
}
// TODO : onunload, are you sure? leave without saving?
},
'jquery-sheet': {
type:'editor',
path:'component/jquery-sheet.html',
gadget_id:'page-content',
ext:'jqs',
getContent: function () {
return JSON.stringify (
$.sheet.instance[0].exportSheet.json()
);
},
setContent: function (content) {
$('#jQuerySheet').sheet({
title: '',
inlineMenu: inlineMenu($.sheet.instance),
buildSheet: $.sheet.makeTable.json(
JSON.parse(content)
),
autoFiller: true
});
},
onload: function (param) {
// FIXME : wait for initialization end
setTimeout(function () {
if (typeof param.file_name !== 'undefined') {
$('#input_file_name').attr('value',
baseName(param.file_name));
that.load(baseName(param.file_name));
} else {
$('#input_file_name').attr(
'value','untitled');
}
},1000);
}
},
'svg-edit': {
type:'editor',
path:'component/svg-edit.html',
gadget_id:'page-content',
ext:'svg',
frameid:'svg_edit_frame',
getContent: function () {
return document.getElementById (this.frameid).
contentWindow.svgCanvas.getSvgString();
},
setContent: function (content) {
document.getElementById (this.frameid).
contentWindow.svgCanvas.setSvgString(content);
},
onload: function (param) {
var waitForInit = function (fun) {
// FIXME : wait for init end
setTimeout(fun,1000);
}
waitForInit(function () {
if (typeof param.file_name !== 'undefined') {
$('#input_file_name').attr('value',
baseName(param.file_name));
that.load(baseName(param.file_name));
} else {
$('#input_file_name').attr(
'value','untitled');
}
});
}
},
'power-point': {
type:'editor',
path:'component/slideshow.html',
gadget_id:'page-content',
ext:'ppt',
frameid:'svg_edit_frame',
getContent: function () {
return document.getElementById("list").innerHTML;
},
setContent: function (content) {
document.getElementById ("list").innerHTML=content;
//re initial the edit frame for the new page
$dialogEdit.append(editSlideIframe);
$('.edit_slide_button').click(function() {
editClick(this);
});
$('.remove_slide_button').click(function() {
removeClick(this);
});
$('section').hover(function() {
slideHover(this);
}, function(){
slideOut(this);
});
$('section').mousedown(function() {
slideOut(this);
});
},
onload: function (param) {
var waitForInit = function (fun) {
// FIXME : wait for init end
setTimeout(fun,1000);
}
waitForInit(function () {
if (typeof param.file_name !== 'undefined') {
$('#input_file_name').attr('value',
baseName(param.file_name));
that.load(baseName(param.file_name));
} else {
$('#input_file_name').attr(
'value','untitled');
}
});
}
},
slickgrid: {
type:'editor',
path:'component/slickgrid_document_lister.html',
gadget_id:'page-content',
update: function () {
OfficeJS.open({app:'document_lister',force:true});
}
}
};
priv.mime_object = {
// <pref> if the name of the app set in preferences.
// If pref does not exist it means that the extension is very
// specific, so <app> is called instead of the default editor.
// NOTE : the icon may be set in the app in app_object.
html:{pref:'text_editor',app:'elrte',
icon:'<i class="icon-font"></i>'},
svg:{pref:'img_editor',app:'svg-edit',
icon:'<i class="icon-pencil"></i>'},
ppt:{pref:'power_point',app:'power-point',
icon:'<i class="icon-facetime-video"></i>'},
jqs:{app:'jquery-sheet',
icon:'<i class="icon-signal"></i>'}
};
priv.data_object = {
documentList:[],
gadget_object:{}, // contains current gadgets id with their location
currentFile:null,
currentEditor:null
};
priv.loading_object = {
spinstate: 0,
savestate: 0,
loadstate: 0,
getliststate: 0,
removestate: 0,
main: function (string){
if (this[string+'state'] === 0){
document.querySelector ('#loading_'+string).
style.display = 'block';
}
this[string+'state'] ++;
},
end_main: function (string){
if (this[string+'state']>0) {
this[string+'state']--;
}
if (this[string+'state']===0){
document.querySelector ('#loading_'+string).
style.display = 'none';
}
},
spin:function(){this.main('spin');},
save:function(){this.main('save');this.spin();},
load:function(){this.main('load');this.spin();},
getlist:function(){this.main('getlist');this.spin();},
remove:function(){this.main('remove');this.spin();},
end_spin:function(){this.end_main('spin');},
end_save:function(){this.end_main('save');this.end_spin();},
end_load:function(){this.end_main('load');this.end_spin();},
end_getlist:function(){this.end_main('getlist');this.end_spin();},
end_remove:function(){this.end_main('remove');this.end_spin();}
};
// Initializer //
priv.init = function() {
};
// Methods //
/**
* Shows a list of document inside the left nav bar
* @method showDocumentListInsideLeftNavBar
*/
priv.showDocumentListInsideLeftNavBar = function () {
$('#nav_document_list_header').show();
};
/**
* @method getRealApplication
* @param {string} appname The app name set in preference.
* @return {object} The real application object.
*/
priv.getRealApplication = function (appname) {
var realappname = that.getPreference (appname);
if (!realappname) {
return priv.app_object[appname];
}
return priv.app_object[realappname];
};
/**
* @method isJioSet
* @return {boolean} true if jio is set else false.
*/
priv.isJioSet = function () {
return (typeof priv.jio === 'object');
};
/**
* Opens an application
* @method open
* @param {object} option Contains some settings:
* - app {string} The app name we want to open, set in preferences
* - force {boolean} To reload applications even if it is the same.
* (optional)
* - ... and some other parameters
*/
that.open = function (option) {
var realapp, realgadgetid, realpath, acientapp;
realapp = priv.getRealApplication (option.app);
if (!realapp) {
// cannot get real app
console.error ('Unknown application: ' + option.app);
return null;
}
realgadgetid = realapp.gadget_id;
realpath = realapp.path;
if (option.force || priv.data_object.currentEditor !== realapp) {
ancientapp = priv.data_object.gadget_object[realgadgetid];
if (ancientapp) {
// if there is already a gadget there, unload it
if (typeof ancientapp.onunload !== 'undefined' &&
!ancientapp.onunload()) {
// if onunload return false, it means that we must not
// load a new gadget because this one is not ready to
// exit.
return null;
}
}
priv.data_object.gadget_object[realgadgetid] = realapp;
//NOW THERE'S NO GADGET IN OFFICE MOBILE
// TabbularGadget.addNewTabGadget(realpath,realgadgetid);
// set current editor
switch (realapp.type) {
case 'editor':
priv.data_object.currentEditor = realapp;
break;
default:
priv.data_object.currentEditor = null;
break;
}
}
// onload call
if (typeof realapp.onload !== 'undefined') {
return realapp.onload(option);
}
};
/**
* @method getPreference
* @param {string} key The preference
* @return {string} The content of the preference.
*/
that.getPreference = function (key) {
return priv.preference_object[key];
};
/**
* @method getContentOf
* @param {string} app The application name
* @return {string} The content of the application, or null.
*/
that.getContentOf = function (app) {
var realapp = priv.getRealApplication (app);
if (!realapp) {
console.error ('Unknown application: ' + app);
return null;
}
if (typeof realapp.getContent !== 'undefined') {
return realapp.getContent();
}
return null;
};
/**
* @method getPathOf
* @param {string} app The application name
* @return {string} The path of the application component, or null.
*/
that.getPathOf = function (app) {
var realapp = priv.getRealApplication (app);
if (!realapp) {
console.error ('Unknown application: ' + app);
return null;
}
return realapp.path;
};
/**
* Returns the current editor file extension.
* @return {string} The current editor file extension.
*/
that.getExt = function () {
return priv.data_object.currentEditor.ext;
};
/**
* Returns a clone of the mime object having this file [extension].
* @method getMimeOfExt
* @param {string} extension The extension without '.'.
* @return {object} A clone of the mime object
*/
that.getMimeOfExt = function (extension) {
if (typeof priv.mime_object[extension] === 'undefined') {
return null;
}
return $.extend (true,{},priv.mime_object[extension]);
};
/**
* @method setJio
* @param {object} storage The storage informations
* @param {object} applicant The applicant informations
*/
that.setJio = function (storage,applicant) {
var leftnavbar;
if (priv.isJioSet()) {
alert ('Jio already set.');
return;
}
// if there is not any jio created
priv.jio = JIO.newJio (storage,applicant);
// update left nav bar
/* leftnavbar = priv.getRealApplication ('leftnavbar');
if (typeof leftnavbar.update !== 'undefined') {
leftnavbar.update();
}*/
that.getList();
};
/**
* Returns the array list in priv.data_object
* @method getList
* @param {function} callback Another callback called after retrieving
* the list. (optional)
*/
that.getList = function (callback) {
if (!priv.isJioSet()) {
console.error ('No Jio set yet.');
return;
}
priv.loading_object.getlist();
priv.jio.getDocumentList({
'sort':{'last_modified':'descending',
'name':'ascending'},
'limit':{begin:0,end:50},
// 'search':{name:'a'},
'maxtries':3,
'onResponse':function (result) {
if (result.status === 'done') {
priv.data_object.documentList = result.return_value;
priv.showDocumentListInsideLeftNavBar();
} else {
console.error (result.message);
}
priv.loading_object.end_getlist();
if (typeof callback !== 'undefined') {
callback();
}
}
});
};
that.cloneCurrentDocumentList = function () {
// clone document list
return $.extend(true,[],priv.data_object.documentList);
};
/**
* Saves the document.
* @method save
* @param {string} basename The document name without ext.
*/
that.save = function (basename) {
var current_editor = priv.data_object.currentEditor;
if (!priv.isJioSet()) {
console.error ('No Jio set yet.');
return;
}
priv.loading_object.save();
priv.jio.saveDocument({
'name':basename+'.'+current_editor.ext,
'content':current_editor.getContent(),
'onResponse':function (result) {
if (result.status === 'fail') {
console.error (result.message);
}
priv.loading_object.end_save();
that.getList();
}
});
};
/**
* Loads a document.
* @method load
* @param {string} basename The document name without ext.
*/
that.load = function (basename) {
var current_editor = priv.data_object.currentEditor;
if (!priv.isJioSet()) {
console.error ('No Jio set yet.');
return;
}
priv.loading_object.load();
priv.jio.loadDocument({
'name':basename+'.'+current_editor.ext,
'maxtries':3,
'onResponse':function (result) {
if (result.status === 'fail') {
console.error (result.message);
} else {
current_editor.setContent(
result.return_value.content);
}
priv.loading_object.end_load();
}
});
};
/**
* Removes a document.
* @method remove
* @param {string} name The document name.
*/
that.remove = function (name) {
if (!priv.isJioSet()) {
console.error ('No Jio set yet.');
return;
}
priv.loading_object.remove();
priv.jio.removeDocument({
'name':name,
'onResponse':function (result) {
if (result.status === 'fail') {
console.error (result.message);
}
priv.loading_object.end_remove();
that.getList();
}
});
};
/**
* Removes several files.
* @method removeSeveralFromArray
* @param {array} documentarray Contains all file names ({string}).
*/
that.removeSeveralFromArray = function (documentarray) {
var i, l, cpt = 0, current_editor = priv.data_object.currentEditor;
if (!priv.isJioSet()) {
console.error ('No Jio set yet.');
return;
}
for (i = 0, l = documentarray.length; i < l; i+= 1) {
priv.loading_object.remove();
priv.jio.removeDocument({
name:documentarray[i],
onResponse:function (result) {
cpt += 1;
if (cpt === l) {
if (typeof current_editor.update !== 'undefined') {
that.getList(current_editor.update);
}
}
priv.loading_object.end_remove();
}});
}
};
// End of class //
priv.init();
return that;
}()); // end OfficeJS
// show gadgets
OfficeJS.open({app:'topnavbar'});
OfficeJS.open({app:'leftnavbar'});
OfficeJS.open({app:'login'});
}());
/**
*
* Base64 encode / decode
* http://www.webtoolkit.info/
*
**/
var Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// public method for encoding
encode : function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = Base64._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
},
// public method for decoding
decode : function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64._utf8_decode(output);
return output;
},
// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}
/*
* ----------------------------- JSTORAGE -------------------------------------
* Simple local storage wrapper to save data on the browser side, supporting
* all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
*
* Copyright (c) 2010 Andris Reinman, andris.reinman@gmail.com
* Project homepage: www.jstorage.info
*
* Licensed under MIT-style license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* $.jStorage
*
* USAGE:
*
* jStorage requires Prototype, MooTools or jQuery! If jQuery is used, then
* jQuery-JSON (http://code.google.com/p/jquery-json/) is also needed.
* (jQuery-JSON needs to be loaded BEFORE jStorage!)
*
* Methods:
*
* -set(key, value)
* $.jStorage.set(key, value) -> saves a value
*
* -get(key[, default])
* value = $.jStorage.get(key [, default]) ->
* retrieves value if key exists, or default if it doesn't
*
* -deleteKey(key)
* $.jStorage.deleteKey(key) -> removes a key from the storage
*
* -flush()
* $.jStorage.flush() -> clears the cache
*
* -storageObj()
* $.jStorage.storageObj() -> returns a read-ony copy of the actual storage
*
* -storageSize()
* $.jStorage.storageSize() -> returns the size of the storage in bytes
*
* -index()
* $.jStorage.index() -> returns the used keys as an array
*
* -storageAvailable()
* $.jStorage.storageAvailable() -> returns true if storage is available
*
* -reInit()
* $.jStorage.reInit() -> reloads the data from browser storage
*
* <value> can be any JSON-able value, including objects and arrays.
*
**/
(function($){
if(!$ || !($.toJSON || Object.toJSON || window.JSON)){
throw new Error("jQuery, MooTools or Prototype needs to be loaded before jStorage!");
}
var
/* This is the object, that holds the cached values */
_storage = {},
/* Actual browser storage (localStorage or globalStorage['domain']) */
_storage_service = {jStorage:"{}"},
/* DOM element for older IE versions, holds userData behavior */
_storage_elm = null,
/* How much space does the storage take */
_storage_size = 0,
/* function to encode objects to JSON strings */
json_encode = $.toJSON || Object.toJSON || (window.JSON && (JSON.encode || JSON.stringify)),
/* function to decode objects from JSON strings */
json_decode = $.evalJSON || (window.JSON && (JSON.decode || JSON.parse)) || function(str){
return String(str).evalJSON();
},
/* which backend is currently used */
_backend = false,
/* Next check for TTL */
_ttl_timeout,
/**
* XML encoding and decoding as XML nodes can't be JSON'ized
* XML nodes are encoded and decoded if the node is the value to be saved
* but not if it's as a property of another object
* Eg. -
* $.jStorage.set("key", xmlNode); // IS OK
* $.jStorage.set("key", {xml: xmlNode}); // NOT OK
*/
_XMLService = {
/**
* Validates a XML node to be XML
* based on jQuery.isXML function
*/
isXML: function(elm){
var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
},
/**
* Encodes a XML node to string
* based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
*/
encode: function(xmlNode) {
if(!this.isXML(xmlNode)){
return false;
}
try{ // Mozilla, Webkit, Opera
return new XMLSerializer().serializeToString(xmlNode);
}catch(E1) {
try { // IE
return xmlNode.xml;
}catch(E2){}
}
return false;
},
/**
* Decodes a XML node from string
* loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
*/
decode: function(xmlString){
var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) ||
(window.ActiveXObject && function(_xmlString) {
var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
xml_doc.async = 'false';
xml_doc.loadXML(_xmlString);
return xml_doc;
}),
resultXML;
if(!dom_parser){
return false;
}
resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml');
return this.isXML(resultXML)?resultXML:false;
}
};
////////////////////////// PRIVATE METHODS ////////////////////////
/**
* Initialization function. Detects if the browser supports DOM Storage
* or userData behavior and behaves accordingly.
* @returns undefined
*/
function _init(){
/* Check if browser supports localStorage */
var localStorageReallyWorks = false;
if("localStorage" in window){
try {
window.localStorage.setItem('_tmptest', 'tmpval');
localStorageReallyWorks = true;
window.localStorage.removeItem('_tmptest');
} catch(BogusQuotaExceededErrorOnIos5) {
// Thanks be to iOS5 Private Browsing mode which throws
// QUOTA_EXCEEDED_ERRROR DOM Exception 22.
}
}
if(localStorageReallyWorks){
try {
if(window.localStorage) {
_storage_service = window.localStorage;
_backend = "localStorage";
}
} catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */}
}
/* Check if browser supports globalStorage */
else if("globalStorage" in window){
try {
if(window.globalStorage) {
_storage_service = window.globalStorage[window.location.hostname];
_backend = "globalStorage";
}
} catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */}
}
/* Check if browser supports userData behavior */
else {
_storage_elm = document.createElement('link');
if(_storage_elm.addBehavior){
/* Use a DOM element to act as userData storage */
_storage_elm.style.behavior = 'url(#default#userData)';
/* userData element needs to be inserted into the DOM! */
document.getElementsByTagName('head')[0].appendChild(_storage_elm);
_storage_elm.load("jStorage");
var data = "{}";
try{
data = _storage_elm.getAttribute("jStorage");
}catch(E5){}
_storage_service.jStorage = data;
_backend = "userDataBehavior";
}else{
_storage_elm = null;
return;
}
}
_load_storage();
// remove dead keys
_handleTTL();
}
/**
* Loads the data from the storage based on the supported mechanism
* @returns undefined
*/
function _load_storage(){
/* if jStorage string is retrieved, then decode it */
if(_storage_service.jStorage){
try{
_storage = json_decode(String(_storage_service.jStorage));
}catch(E6){_storage_service.jStorage = "{}";}
}else{
_storage_service.jStorage = "{}";
}
_storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
}
/**
* This functions provides the "save" mechanism to store the jStorage object
* @returns undefined
*/
function _save(){
try{
_storage_service.jStorage = json_encode(_storage);
// If userData is used as the storage engine, additional
if(_storage_elm) {
_storage_elm.setAttribute("jStorage",_storage_service.jStorage);
_storage_elm.save("jStorage");
}
_storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
}catch(E7){/* probably cache is full, nothing is saved this way*/}
}
/**
* Function checks if a key is set and is string or numberic
*/
function _checkKey(key){
if(!key || (typeof key != "string" && typeof key != "number")){
throw new TypeError('Key name must be string or numeric');
}
if(key == "__jstorage_meta"){
throw new TypeError('Reserved key name');
}
return true;
}
/**
* Removes expired keys
*/
function _handleTTL(){
var curtime, i, TTL, nextExpire = Infinity, changed = false;
clearTimeout(_ttl_timeout);
if(!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != "object"){
// nothing to do here
return;
}
curtime = +new Date();
TTL = _storage.__jstorage_meta.TTL;
for(i in TTL){
if(TTL.hasOwnProperty(i)){
if(TTL[i] <= curtime){
delete TTL[i];
delete _storage[i];
changed = true;
}else if(TTL[i] < nextExpire){
nextExpire = TTL[i];
}
}
}
// set next check
if(nextExpire != Infinity){
_ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime);
}
// save changes
if(changed){
_save();
}
}
////////////////////////// PUBLIC INTERFACE /////////////////////////
$.jStorage = {
/* Version number */
version: "0.1.6.1",
/**
* Sets a key's value.
*
* @param {String} key - Key to set. If this value is not set or not
* a string an exception is raised.
* @param value - Value to set. This can be any value that is JSON
* compatible (Numbers, Strings, Objects etc.).
* @returns the used value
*/
set: function(key, value){
_checkKey(key);
if(_XMLService.isXML(value)){
value = {_is_xml:true,xml:_XMLService.encode(value)};
}else if(typeof value == "function"){
value = null; // functions can't be saved!
}else if(value && typeof value == "object"){
// clone the object before saving to _storage tree
value = json_decode(json_encode(value));
}
_storage[key] = value;
_save();
return value;
},
/**
* Looks up a key in cache
*
* @param {String} key - Key to look up.
* @param {mixed} def - Default value to return, if key didn't exist.
* @returns the key value, default value or <null>
*/
get: function(key, def){
_checkKey(key);
if(key in _storage){
if(_storage[key] && typeof _storage[key] == "object" &&
_storage[key]._is_xml &&
_storage[key]._is_xml){
return _XMLService.decode(_storage[key].xml);
}else{
return _storage[key];
}
}
return typeof(def) == 'undefined' ? null : def;
},
/**
* Deletes a key from cache.
*
* @param {String} key - Key to delete.
* @returns true if key existed or false if it didn't
*/
deleteKey: function(key){
_checkKey(key);
if(key in _storage){
delete _storage[key];
// remove from TTL list
if(_storage.__jstorage_meta &&
typeof _storage.__jstorage_meta.TTL == "object" &&
key in _storage.__jstorage_meta.TTL){
delete _storage.__jstorage_meta.TTL[key];
}
_save();
return true;
}
return false;
},
/**
* Sets a TTL for a key, or remove it if ttl value is 0 or below
*
* @param {String} key - key to set the TTL for
* @param {Number} ttl - TTL timeout in milliseconds
* @returns true if key existed or false if it didn't
*/
setTTL: function(key, ttl){
var curtime = +new Date();
_checkKey(key);
ttl = Number(ttl) || 0;
if(key in _storage){
if(!_storage.__jstorage_meta){
_storage.__jstorage_meta = {};
}
if(!_storage.__jstorage_meta.TTL){
_storage.__jstorage_meta.TTL = {};
}
// Set TTL value for the key
if(ttl>0){
_storage.__jstorage_meta.TTL[key] = curtime + ttl;
}else{
delete _storage.__jstorage_meta.TTL[key];
}
_save();
_handleTTL();
return true;
}
return false;
},
/**
* Deletes everything in cache.
*
* @return true
*/
flush: function(){
_storage = {};
_save();
return true;
},
/**
* Returns a read-only copy of _storage
*
* @returns Object
*/
storageObj: function(){
function F() {}
F.prototype = _storage;
return new F();
},
/**
* Returns an index of all used keys as an array
* ['key1', 'key2',..'keyN']
*
* @returns Array
*/
index: function(){
var index = [], i;
for(i in _storage){
if(_storage.hasOwnProperty(i) && i != "__jstorage_meta"){
index.push(i);
}
}
return index;
},
/**
* How much space in bytes does the storage take?
*
* @returns Number
*/
storageSize: function(){
return _storage_size;
},
/**
* Which backend is currently in use?
*
* @returns String
*/
currentBackend: function(){
return _backend;
},
/**
* Test if storage is available
*
* @returns Boolean
*/
storageAvailable: function(){
return !!_backend;
},
/**
* Reloads the data from browser storage
*
* @returns undefined
*/
reInit: function(){
var new_storage_elm, data;
if(_storage_elm && _storage_elm.addBehavior){
new_storage_elm = document.createElement('link');
_storage_elm.parentNode.replaceChild(new_storage_elm, _storage_elm);
_storage_elm = new_storage_elm;
/* Use a DOM element to act as userData storage */
_storage_elm.style.behavior = 'url(#default#userData)';
/* userData element needs to be inserted into the DOM! */
document.getElementsByTagName('head')[0].appendChild(_storage_elm);
_storage_elm.load("jStorage");
data = "{}";
try{
data = _storage_elm.getAttribute("jStorage");
}catch(E5){}
_storage_service.jStorage = data;
_backend = "userDataBehavior";
}
_load_storage();
}
};
// Initialize jStorage
_init();
})(window.jQuery || window.$);
\ No newline at end of file
"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
if(typeof module!="undefined"&&module.exports)module.exports=sjcl;
sjcl.cipher.aes=function(a){this.h[0][0][0]||this.w();var b,c,d,e,f=this.h[0][4],g=this.h[1];b=a.length;var h=1;if(b!==4&&b!==6&&b!==8)throw new sjcl.exception.invalid("invalid aes key size");this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(a%b===0||b===8&&a%b===4){c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255];if(a%b===0){c=c<<8^c>>>24^h<<24;h=h<<1^(h>>7)*283}}d[a]=d[a-b]^c}for(b=0;a;b++,a--){c=d[b&3?a:a-4];e[b]=a<=4||b<4?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^
g[3][f[c&255]]}};
sjcl.cipher.aes.prototype={encrypt:function(a){return this.H(a,0)},decrypt:function(a){return this.H(a,1)},h:[[[],[],[],[],[]],[[],[],[],[],[]]],w:function(){var a=this.h[0],b=this.h[1],c=a[4],d=b[4],e,f,g,h=[],i=[],k,j,l,m;for(e=0;e<0x100;e++)i[(h[e]=e<<1^(e>>7)*283)^e]=e;for(f=g=0;!c[f];f^=k||1,g=i[g]||1){l=g^g<<1^g<<2^g<<3^g<<4;l=l>>8^l&255^99;c[f]=l;d[l]=f;j=h[e=h[k=h[f]]];m=j*0x1010101^e*0x10001^k*0x101^f*0x1010100;j=h[l]*0x101^l*0x1010100;for(e=0;e<4;e++){a[e][f]=j=j<<24^j>>>8;b[e][l]=m=m<<24^m>>>8}}for(e=
0;e<5;e++){a[e]=a[e].slice(0);b[e]=b[e].slice(0)}},H:function(a,b){if(a.length!==4)throw new sjcl.exception.invalid("invalid aes block size");var c=this.a[b],d=a[0]^c[0],e=a[b?3:1]^c[1],f=a[2]^c[2];a=a[b?1:3]^c[3];var g,h,i,k=c.length/4-2,j,l=4,m=[0,0,0,0];g=this.h[b];var n=g[0],o=g[1],p=g[2],q=g[3],r=g[4];for(j=0;j<k;j++){g=n[d>>>24]^o[e>>16&255]^p[f>>8&255]^q[a&255]^c[l];h=n[e>>>24]^o[f>>16&255]^p[a>>8&255]^q[d&255]^c[l+1];i=n[f>>>24]^o[a>>16&255]^p[d>>8&255]^q[e&255]^c[l+2];a=n[a>>>24]^o[d>>16&
255]^p[e>>8&255]^q[f&255]^c[l+3];l+=4;d=g;e=h;f=i}for(j=0;j<4;j++){m[b?3&-j:j]=r[d>>>24]<<24^r[e>>16&255]<<16^r[f>>8&255]<<8^r[a&255]^c[l++];g=d;d=e;e=f;f=a;a=g}return m}};
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===undefined?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(a.length===0||b.length===0)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return d===32?a.concat(b):sjcl.bitArray.P(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;
if(b===0)return 0;return(b-1)*32+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(a.length*32<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;if(c>0&&b)a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1);return a},partial:function(a,b,c){if(a===32)return b;return(c?b|0:b<<32-a)+a*0x10000000000},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return false;var c=0,d;for(d=0;d<a.length;d++)c|=
a[d]^b[d];return c===0},P:function(a,b,c,d){var e;e=0;if(d===undefined)d=[];for(;b>=32;b-=32){d.push(c);c=0}if(b===0)return d.concat(a);for(e=0;e<a.length;e++){d.push(c|a[e]>>>b);c=a[e]<<32-b}e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,b+a>32?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++){if((d&3)===0)e=a[d/4];b+=String.fromCharCode(e>>>24);e<<=8}return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++){d=d<<8|a.charCodeAt(c);if((c&3)===3){b.push(d);d=0}}c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,d*4)}};
sjcl.codec.base64={D:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.D,g=0,h=sjcl.bitArray.bitLength(a);if(c)f=f.substr(0,62)+"-_";for(c=0;d.length*6<h;){d+=f.charAt((g^a[c]>>>e)>>>26);if(e<6){g=a[c]<<6-e;e+=26;c++}else{g<<=6;e-=6}}for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d=0,e=sjcl.codec.base64.D,f=0,g;if(b)e=e.substr(0,62)+"-_";for(b=0;b<a.length;b++){g=e.indexOf(a.charAt(b));
if(g<0)throw new sjcl.exception.invalid("this isn't base64!");if(d>26){d-=26;c.push(f^g>>>d);f=g<<32-d}else{d+=6;f^=g<<32-d}}d&56&&c.push(sjcl.bitArray.partial(d&56,f,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.a[0]||this.w();if(a){this.n=a.n.slice(0);this.i=a.i.slice(0);this.e=a.e}else this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.n=this.N.slice(0);this.i=[];this.e=0;return this},update:function(a){if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);var b,c=this.i=sjcl.bitArray.concat(this.i,a);b=this.e;a=this.e=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)this.C(c.splice(0,16));return this},finalize:function(){var a,b=this.i,c=this.n;b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.e/
4294967296));for(b.push(this.e|0);b.length;)this.C(b.splice(0,16));this.reset();return c},N:[],a:[],w:function(){function a(e){return(e-Math.floor(e))*0x100000000|0}var b=0,c=2,d;a:for(;b<64;c++){for(d=2;d*d<=c;d++)if(c%d===0)continue a;if(b<8)this.N[b]=a(Math.pow(c,0.5));this.a[b]=a(Math.pow(c,1/3));b++}},C:function(a){var b,c,d=a.slice(0),e=this.n,f=this.a,g=e[0],h=e[1],i=e[2],k=e[3],j=e[4],l=e[5],m=e[6],n=e[7];for(a=0;a<64;a++){if(a<16)b=d[a];else{b=d[a+1&15];c=d[a+14&15];b=d[a&15]=(b>>>7^b>>>18^
b>>>3^b<<25^b<<14)+(c>>>17^c>>>19^c>>>10^c<<15^c<<13)+d[a&15]+d[a+9&15]|0}b=b+n+(j>>>6^j>>>11^j>>>25^j<<26^j<<21^j<<7)+(m^j&(l^m))+f[a];n=m;m=l;l=j;j=k+b|0;k=i;i=h;h=g;g=b+(h&i^k&(h^i))+(h>>>2^h>>>13^h>>>22^h<<30^h<<19^h<<10)|0}e[0]=e[0]+g|0;e[1]=e[1]+h|0;e[2]=e[2]+i|0;e[3]=e[3]+k|0;e[4]=e[4]+j|0;e[5]=e[5]+l|0;e[6]=e[6]+m|0;e[7]=e[7]+n|0}};
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,i=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];if(i<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;f<4&&k>>>8*f;f++);if(f<15-i)f=15-i;c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.G(a,b,c,d,e,f);g=sjcl.mode.ccm.I(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),i=f.clamp(b,h-e),k=f.bitSlice(b,
h-e);h=(h-e)/8;if(g<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;b<4&&h>>>8*b;b++);if(b<15-g)b=15-g;c=f.clamp(c,8*(15-b));i=sjcl.mode.ccm.I(a,i,c,k,e,b);a=sjcl.mode.ccm.G(a,i.data,c,d,e,b);if(!f.equal(i.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match");return i.data},G:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,i=h.k;e/=8;if(e%2||e<4||e>16)throw new sjcl.exception.invalid("ccm: invalid tag length");if(d.length>0xffffffff||b.length>0xffffffff)throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;if(c<=65279)g=[h.partial(16,c)];else if(c<=0xffffffff)g=h.concat([h.partial(16,65534)],[c]);g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(i(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(i(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,e*8)},I:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.k;var i=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!i)return{tag:d,data:[]};for(g=0;g<i;g+=4){c[3]++;e=a.encrypt(c);b[g]^=e[0];b[g+1]^=e[1];b[g+2]^=e[2];b[g+3]^=e[3]}return{tag:d,data:h.clamp(b,k)}}};
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");var g,h=sjcl.mode.ocb2.A,i=sjcl.bitArray,k=i.k,j=[0,0,0,0];c=h(a.encrypt(c));var l,m=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4){l=b.slice(g,g+4);j=k(j,l);m=m.concat(k(c,a.encrypt(k(c,l))));c=h(c)}l=b.slice(g);b=i.bitLength(l);g=a.encrypt(k(c,[0,0,0,b]));l=i.clamp(k(l.concat([0,0,0]),g),b);j=k(j,k(l.concat([0,0,0]),g));j=a.encrypt(k(j,k(c,h(c))));
if(d.length)j=k(j,f?d:sjcl.mode.ocb2.pmac(a,d));return m.concat(i.concat(l,i.clamp(j,e)))},decrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");e=e||64;var g=sjcl.mode.ocb2.A,h=sjcl.bitArray,i=h.k,k=[0,0,0,0],j=g(a.encrypt(c)),l,m,n=sjcl.bitArray.bitLength(b)-e,o=[];d=d||[];for(c=0;c+4<n/32;c+=4){l=i(j,a.decrypt(i(j,b.slice(c,c+4))));k=i(k,l);o=o.concat(l);j=g(j)}m=n-c*32;l=a.encrypt(i(j,[0,0,0,m]));l=i(l,h.clamp(b.slice(c),
m).concat([0,0,0]));k=i(k,l);k=a.encrypt(i(k,i(j,g(j))));if(d.length)k=i(k,f?d:sjcl.mode.ocb2.pmac(a,d));if(!h.equal(h.clamp(k,e),h.bitSlice(b,n)))throw new sjcl.exception.corrupt("ocb: tag doesn't match");return o.concat(h.clamp(l,m))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.A,e=sjcl.bitArray,f=e.k,g=[0,0,0,0],h=a.encrypt([0,0,0,0]);h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4){h=d(h);g=f(g,a.encrypt(f(h,b.slice(c,c+4))))}b=b.slice(c);if(e.bitLength(b)<128){h=f(h,d(h));b=e.concat(b,[2147483648|0,0,
0,0])}g=f(g,b);return a.encrypt(f(d(f(h,d(h))),g))},A:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^(a[0]>>>31)*135]}};sjcl.misc.hmac=function(a,b){this.M=b=b||sjcl.hash.sha256;var c=[[],[]],d=b.prototype.blockSize/32;this.l=[new b,new b];if(a.length>d)a=b.hash(a);for(b=0;b<d;b++){c[0][b]=a[b]^909522486;c[1][b]=a[b]^1549556828}this.l[0].update(c[0]);this.l[1].update(c[1])};
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a,b){a=(new this.M(this.l[0])).update(a,b).finalize();return(new this.M(this.l[1])).update(a).finalize()};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;if(d<0||c<0)throw sjcl.exception.invalid("invalid params to pbkdf2");if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,i,k=[],j=sjcl.bitArray;for(i=1;32*k.length<(d||1);i++){e=f=a.encrypt(j.concat(b,[i]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}if(d)k=j.clamp(k,d);return k};
sjcl.random={randomWords:function(a,b){var c=[];b=this.isReady(b);var d;if(b===0)throw new sjcl.exception.notReady("generator isn't seeded");else b&2&&this.U(!(b&1));for(b=0;b<a;b+=4){(b+1)%0x10000===0&&this.L();d=this.u();c.push(d[0],d[1],d[2],d[3])}this.L();return c.slice(0,a)},setDefaultParanoia:function(a){this.t=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.q[c],h=this.isReady();d=this.F[c];if(d===undefined)d=this.F[c]=this.R++;if(g===undefined)g=this.q[c]=0;this.q[c]=
(this.q[c]+1)%this.b.length;switch(typeof a){case "number":break;case "object":if(b===undefined)for(c=b=0;c<a.length;c++)for(e=a[c];e>0;){b++;e>>>=1}this.b[g].update([d,this.J++,2,b,f,a.length].concat(a));break;case "string":if(b===undefined)b=a.length;this.b[g].update([d,this.J++,3,b,f,a.length]);this.b[g].update(a);break;default:throw new sjcl.exception.bug("random: addEntropy only supports number, array or string");}this.j[g]+=b;this.f+=b;if(h===0){this.isReady()!==0&&this.K("seeded",Math.max(this.g,
this.f));this.K("progress",this.getProgress())}},isReady:function(a){a=this.B[a!==undefined?a:this.t];return this.g&&this.g>=a?this.j[0]>80&&(new Date).valueOf()>this.O?3:1:this.f>=a?2:0},getProgress:function(a){a=this.B[a?a:this.t];return this.g>=a?1["0"]:this.f>a?1["0"]:this.f/a},startCollectors:function(){if(!this.m){if(window.addEventListener){window.addEventListener("load",this.o,false);window.addEventListener("mousemove",this.p,false)}else if(document.attachEvent){document.attachEvent("onload",
this.o);document.attachEvent("onmousemove",this.p)}else throw new sjcl.exception.bug("can't attach event");this.m=true}},stopCollectors:function(){if(this.m){if(window.removeEventListener){window.removeEventListener("load",this.o,false);window.removeEventListener("mousemove",this.p,false)}else if(window.detachEvent){window.detachEvent("onload",this.o);window.detachEvent("onmousemove",this.p)}this.m=false}},addEventListener:function(a,b){this.r[a][this.Q++]=b},removeEventListener:function(a,b){var c;
a=this.r[a];var d=[];for(c in a)a.hasOwnProperty(c)&&a[c]===b&&d.push(c);for(b=0;b<d.length;b++){c=d[b];delete a[c]}},b:[new sjcl.hash.sha256],j:[0],z:0,q:{},J:0,F:{},R:0,g:0,f:0,O:0,a:[0,0,0,0,0,0,0,0],d:[0,0,0,0],s:undefined,t:6,m:false,r:{progress:{},seeded:{}},Q:0,B:[0,48,64,96,128,192,0x100,384,512,768,1024],u:function(){for(var a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}return this.s.encrypt(this.d)},L:function(){this.a=this.u().concat(this.u());this.s=new sjcl.cipher.aes(this.a)},
T:function(a){this.a=sjcl.hash.sha256.hash(this.a.concat(a));this.s=new sjcl.cipher.aes(this.a);for(a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}},U:function(a){var b=[],c=0,d;this.O=b[0]=(new Date).valueOf()+3E4;for(d=0;d<16;d++)b.push(Math.random()*0x100000000|0);for(d=0;d<this.b.length;d++){b=b.concat(this.b[d].finalize());c+=this.j[d];this.j[d]=0;if(!a&&this.z&1<<d)break}if(this.z>=1<<this.b.length){this.b.push(new sjcl.hash.sha256);this.j.push(0)}this.f-=c;if(c>this.g)this.g=c;this.z++;
this.T(b)},p:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX,a.y||a.clientY||a.offsetY],2,"mouse")},o:function(){sjcl.random.addEntropy(new Date,2,"loadtime")},K:function(a,b){var c;a=sjcl.random.r[a];var d=[];for(c in a)a.hasOwnProperty(c)&&d.push(a[c]);for(c=0;c<d.length;c++)d[c](b)}};try{var s=new Uint32Array(32);crypto.getRandomValues(s);sjcl.random.addEntropy(s,1024,"crypto['getRandomValues']")}catch(t){}
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.c({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.c(f,c);c=f.adata;if(typeof f.salt==="string")f.salt=sjcl.codec.base64.toBits(f.salt);if(typeof f.iv==="string")f.iv=sjcl.codec.base64.toBits(f.iv);if(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||typeof a==="string"&&f.iter<=100||f.ts!==64&&f.ts!==96&&f.ts!==128||f.ks!==128&&f.ks!==192&&f.ks!==0x100||f.iv.length<
2||f.iv.length>4)throw new sjcl.exception.invalid("json encrypt: invalid parameters");if(typeof a==="string"){g=sjcl.misc.cachedPbkdf2(a,f);a=g.key.slice(0,f.ks/32);f.salt=g.salt}if(typeof b==="string")b=sjcl.codec.utf8String.toBits(b);if(typeof c==="string")c=sjcl.codec.utf8String.toBits(c);g=new sjcl.cipher[f.cipher](a);e.c(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return e.encode(e.V(f,e.defaults))},decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.c(e.c(e.c({},e.defaults),
e.decode(b)),c,true);var f;c=b.adata;if(typeof b.salt==="string")b.salt=sjcl.codec.base64.toBits(b.salt);if(typeof b.iv==="string")b.iv=sjcl.codec.base64.toBits(b.iv);if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||typeof a==="string"&&b.iter<=100||b.ts!==64&&b.ts!==96&&b.ts!==128||b.ks!==128&&b.ks!==192&&b.ks!==0x100||!b.iv||b.iv.length<2||b.iv.length>4)throw new sjcl.exception.invalid("json decrypt: invalid parameters");if(typeof a==="string"){f=sjcl.misc.cachedPbkdf2(a,b);a=f.key.slice(0,b.ks/32);
b.salt=f.salt}if(typeof c==="string")c=sjcl.codec.utf8String.toBits(c);f=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,c,b.ts);e.c(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+'"'+b+'":';d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';
break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],1)+'"';break;default:throw new sjcl.exception.bug("json encode: unsupported type");}}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");if(!a.match(/^\{.*\}$/))throw new sjcl.exception.invalid("json decode: this isn't json!");a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++){if(!(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i)))throw new sjcl.exception.invalid("json decode: this isn't json!");
b[d[2]]=d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4])}return b},c:function(a,b,c){if(a===undefined)a={};if(b===undefined)return a;var d;for(d in b)if(b.hasOwnProperty(d)){if(c&&a[d]!==undefined&&a[d]!==b[d])throw new sjcl.exception.invalid("required parameter overridden");a[d]=b[d]}return a},V:function(a,b){var c={},d;for(d in a)if(a.hasOwnProperty(d)&&a[d]!==b[d])c[d]=a[d];return c},W:function(a,b){var c={},d;for(d=0;d<b.length;d++)if(a[b[d]]!==
undefined)c[b[d]]=a[b[d]];return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.S={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.S,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===undefined?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
// Adds 3 dummy storages to JIO
// type:
// - dummyallok
// - dummyallfail
// - dummyallnotfound
// - dummyall3tries
(function () { var jioDummyStorageLoader = function ( Jio ) {
////////////////////////////////////////////////////////////////////////////
// Dummy Storage 1 : all ok
var newDummyStorageAllOk = function ( spec, my ) {
var that = Jio.newBaseStorage( spec, my );
that.checkNameAvailability = function () {
// The [job.userName] IS available.
// wait a little in order to simulate asynchronous operation
setTimeout(function () {
that.done(true);
}, 100);
}; // end userNameAvailable
that.saveDocument = function () {
// Tells us that the document is saved.
// wait a little in order to simulate asynchronous saving
setTimeout (function () {
that.done();
}, 100);
}; // end saveDocument
that.loadDocument = function () {
// Returns a document object containing all information of the
// document and its content.
// document object is {'name':string,'content':string,
// 'creation_date':date,'last_modified':date}
// wait a little in order to simulate asynchronous operation
setTimeout(function () {
var doc = {
'content': 'content',
'name': 'file',
'creation_date': 10000,
'last_modified': 15000};
that.done(doc);
}, 100);
}; // end loadDocument
that.getDocumentList = function () {
// It returns a document array containing all the user documents
// informations, but not their content.
// the list is [object,object] -> object = {'name':string,
// 'last_modified':date,'creation_date':date}
setTimeout(function () {
var list = [
{'name':'file',
'creation_date':10000,
'last_modified':15000},
{'name':'memo',
'creation_date':20000,
'last_modified':25000
}];
that.done(list);
}, 100);
}; // end getDocumentList
that.removeDocument = function () {
// Remove a document from the storage.
setTimeout (function () {
that.done();
}, 100);
};
return that;
},
// end Dummy Storage All Ok
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Dummy Storage 2 : all fail
newDummyStorageAllFail = function ( spec, my ) {
var that = Jio.newBaseStorage( spec, my );
that.checkNameAvailability = function () {
// Fails to check [job.userName].
// wait a little in order to simulate asynchronous operation
setTimeout(function () {
that.fail({status:0,statusText:'Unknown Error',
message:'Unknown error.'});
}, 100);
}; // end userNameAvailable
that.saveDocument = function () {
// Tells us that the document is not saved.
// wait a little in order to simulate asynchronous saving
setTimeout (function () {
that.fail({status:0,statusText:'Unknown Error',
message:'Unknown error.'});
}, 100);
}; // end saveDocument
that.loadDocument = function () {
// Returns a document object containing nothing.
// document object is {'name':string,'content':string,
// 'creation_date':date,'last_modified':date}
// wait a little in order to simulate asynchronous operation
setTimeout(function () {
that.fail({status:0,statusText:'Unknown Error',
message:'Unknown error.'});
}, 100);
}; // end loadDocument
that.getDocumentList = function () {
// It returns nothing.
// the list is [object,object] -> object = {'name':string,
// 'last_modified':date,'creation_date':date}
setTimeout(function () {
that.fail({status:0,statusText:'Unknown Error',
message:'Unknown error.'});
}, 100);
}; // end getDocumentList
that.removeDocument = function () {
// Remove a document from the storage.
// returns {'status':string,'message':string,'isRemoved':boolean}
// in the jobendcallback arguments.
setTimeout (function () {
that.fail({status:0,statusText:'Unknown Error',
message:'Unknown error.'});
}, 100);
};
return that;
},
// end Dummy Storage All Fail
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Dummy Storage 3 : all not found
newDummyStorageAllNotFound = function ( spec, my ) {
var that = Jio.newBaseStorage( spec, my );
that.checkNameAvailability = function () {
// [job.userName] not found, so the name is available.
// wait a little in order to simulate asynchronous operation
setTimeout(function () {
that.done(true);
}, 100);
}; // end userNameAvailable
that.saveDocument = function () {
// Document does not exists yet, create it.
// wait a little in order to simulate asynchronous saving
setTimeout (function () {
that.done();
}, 100);
}; // end saveDocument
that.loadDocument = function () {
// Returns a document object containing nothing.
// document object is {'name':string,'content':string,
// 'creation_date':date,'last_modified':date}
// wait a little in order to simulate asynchronous operation
setTimeout(function () {
that.fail({status:404,statusText:'Not Found',
message:'Document "'+ that.getFileName() +
'" not found.'});
}, 100);
}; // end loadDocument
that.getDocumentList = function () {
// It returns nothing.
// the list is [object,object] -> object = {'name':string,
// 'last_modified':date,'creation_date':date}
setTimeout(function () {
that.fail({status:404,statusText:'Not Found',
message:'User list not found.'});
}, 100);
}; // end getDocumentList
that.removeDocument = function () {
// Remove a document from the storage.
// returns {'status':string,'message':string,'isRemoved':boolean}
// in the jobendcallback arguments.
setTimeout (function () {
that.done();
}, 100);
};
return that;
},
// end Dummy Storage All Not Found
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Dummy Storage 4 : all 3 tries
newDummyStorageAll3Tries = function ( spec, my ) {
var that = Jio.newBaseStorage( spec, my ), priv = {};
priv.doJob = function (if_ok_return) {
// wait a little in order to simulate asynchronous operation
setTimeout(function () {
priv.Try3OKElseFail (that.cloneJob().tries,if_ok_return);
}, 100);
};
priv.Try3OKElseFail = function (tries,if_ok_return) {
if ( tries === 3 ) {
return that.done(if_ok_return);
}
if ( tries < 3 ) {
return that.fail({message:'' + (3 - tries) + ' tries left.'});
}
if ( tries > 3 ) {
return that.fail({message:'Too much tries.'});
}
};
that.checkNameAvailability = function () {
priv.doJob (true);
}; // end userNameAvailable
that.saveDocument = function () {
priv.doJob ();
}; // end saveDocument
that.loadDocument = function () {
priv.doJob ({
'content': 'content2',
'name': 'file',
'creation_date': 11000,
'last_modified': 17000
});
}; // end loadDocument
that.getDocumentList = function () {
priv.doJob([{'name':'file',
'creation_date':10000,
'last_modified':15000},
{'name':'memo',
'creation_date':20000,
'last_modified':25000}
]);
}; // end getDocumentList
that.removeDocument = function () {
priv.doJob();
}; // end removeDocument
return that;
};
// end Dummy Storage All 3 Tries
////////////////////////////////////////////////////////////////////////////
// add key to storageObjectType of global jio
Jio.addStorageType('dummyallok', newDummyStorageAllOk);
Jio.addStorageType('dummyallfail', newDummyStorageAllFail);
Jio.addStorageType('dummyallnotfound', newDummyStorageAllNotFound);
Jio.addStorageType('dummyall3tries', newDummyStorageAll3Tries);
};
if (window.requirejs) {
define ('JIODummyStorages',['JIO'], jioDummyStorageLoader);
} else {
jioDummyStorageLoader ( JIO );
}
}());
/**
* JavaScript Input/Output
* @namespace JIO
* @module JIO
*/
var JIO =
(function () { var jioLoaderFunction = function ( localOrCookieStorage, $ ) {
////////////////////////////////////////////////////////////////////////////
// constants
var jio_const_obj = {
job_method_object: {
checkNameAvailability:{},
saveDocument:{},
loadDocument:{},
getDocumentList:{},
removeDocument:{}
}
},
// end constants
////////////////////////////////////////////////////////////////////////////
// jio globals
jio_global_obj = {
job_managing_method:{
/*
LEGEND:
- s: storage
- a: applicant
- m: method
- n: name
- c: content
- o: options
- =: are equal
- !: are not equal
select ALL s= a= n=
removefailordone fail|done
/ elim repl nacc wait
Remove !ongoing Save 1 x x x
Save !ongoing Remove 1 x x x
GetList !ongoing GetList 0 1 x x
Check !ongoing Check 0 1 x x
Remove !ongoing Remove 0 1 x x
Load !ongoing Load 0 1 x x
Save c= !ongoing Save 0 1 x x
Save c! !ongoing Save 0 1 x x
GetList ongoing GetList 0 0 1 x
Check ongoing Check 0 0 1 x
Remove ongoing Remove 0 0 1 x
Remove ongoing Load 0 0 1 x
Remove !ongoing Load 0 0 1 x
Load ongoing Load 0 0 1 x
Save c= ongoing Save 0 0 1 x
Remove ongoing Save 0 0 0 1
Load ongoing Remove 0 0 0 1
Load ongoing Save 0 0 0 1
Load !ongoing Remove 0 0 0 1
Load !ongoing Save 0 0 0 1
Save ongoing Remove 0 0 0 1
Save ongoing Load 0 0 0 1
Save c! ongoing Save 0 0 0 1
Save !ongoing Load 0 0 0 1
GetList ongoing Check 0 0 0 0
GetList ongoing Remove 0 0 0 0
GetList ongoing Load 0 0 0 0
GetList ongoing Save 0 0 0 0
GetList !ongoing Check 0 0 0 0
GetList !ongoing Remove 0 0 0 0
GetList !ongoing Load 0 0 0 0
GetList !ongoing Save 0 0 0 0
Check ongoing GetList 0 0 0 0
Check ongoing Remove 0 0 0 0
Check ongoing Load 0 0 0 0
Check ongoing Save 0 0 0 0
Check !ongoing GetList 0 0 0 0
Check !ongoing Remove 0 0 0 0
Check !ongoing Load 0 0 0 0
Check !ongoing Save 0 0 0 0
Remove ongoing GetList 0 0 0 0
Remove ongoing Check 0 0 0 0
Remove !ongoing GetList 0 0 0 0
Remove !ongoing Check 0 0 0 0
Load ongoing GetList 0 0 0 0
Load ongoing Check 0 0 0 0
Load !ongoing GetList 0 0 0 0
Load !ongoing Check 0 0 0 0
Save ongoing GetList 0 0 0 0
Save ongoing Check 0 0 0 0
Save !ongoing GetList 0 0 0 0
Save !ongoing Check 0 0 0 0
For more information, see documentation
*/
canSelect:function (job1,job2) {
if (JSON.stringify (job1.storage) ===
JSON.stringify (job2.storage) &&
JSON.stringify (job1.applicant) ===
JSON.stringify (job2.applicant) &&
job1.name === job2.name) {
return true;
}
return false;
},
canRemoveFailOrDone:function (job1,job2) {
if (job1.status === 'fail' ||
job1.status === 'done') {
return true;
}
return false;
},
canEliminate:function (job1,job2) {
if (job1.status !== 'on_going' &&
(job1.method === 'removeDocument' &&
job2.method === 'saveDocument' ||
job1.method === 'saveDocument' &&
job2.method === 'removeDocument')) {
return true;
}
return false;
},
canReplace:function (job1,job2) {
if (job1.status !== 'on_going' &&
job1.method === job2.method &&
job1.date < job2.date) {
return true;
}
return false;
},
cannotAccept:function (job1,job2) {
if (job1.status !== 'on_going' ) {
if (job1.method === 'removeDocument' &&
job2.method === 'loadDocument') {
return true;
}
} else { // ongoing
if (job1.method === job2.method === 'loadDocument') {
return true;
} else if (job1.method === 'removeDocument' &&
(job2.method === 'loadDocument' ||
job2.method === 'removeDocument')) {
return true;
} else if (job1.method === job2.method === 'saveDocument' &&
job1.content === job2.content) {
return true;
} else if (job1.method === job2.method ===
'getDocumentList' ||
job1.method === job2.method ===
'checkNameAvailability') {
return true;
}
}
return false;
},
mustWait:function (job1,job2) {
if (job1.method === 'getDocumentList' ||
job1.method === 'checkNameAvailability' ||
job2.method === 'getDocumentList' ||
job2.method === 'checkNameAvailability' ) {
return false;
}
return true;
}
},
queue_id: 1,
storage_type_object: {}, // ex: {'type':'local','creator': fun ...}
max_wait_time: 10000
},
// end jio globals
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Tools
// end Tools
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Classes
newPubSub,newJob,newJobQueue,newJobListener,newActivityUpdater,
newBaseStorage,newJioConstructor,newJioCreator;
// end Classes
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Publisher Subcriber
newPubSub = function (spec, my) {
var that = {}, priv = {},
topics = {}, callbacks, topic;
priv.eventAction = function (id) {
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;
};
that.publish = function (event_name,obj) {
// publish an event
priv.eventAction(event_name).publish(obj);
};
that.subscribe = function (event_name,callback) {
// subscribe and return the callback function
priv.eventAction(event_name).subscribe(callback);
return callback;
};
that.unsubscribe = function (event_name,callback) {
// unsubscribe the callback from eventname
priv.eventAction(event_name).unsubscribe(callback);
};
return that;
};
// end Publisher Subcriber
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Job & JobQueue
newJob = function ( spec, my ) {
// Job constructor
var job = $.extend(true,{},spec);
job['id']=0;
job['status']='initial';
job['date']=Date.now();
return job;
};
newJobQueue = function ( spec, my ) {
// JobQueue is a queue of jobs. It will regulary copy this queue
// into localStorage to resume undone tasks.
// spec.publisher: the publisher to use to send event
// spec.options.useLocalStorage: if true, save jobs into localStorage,
// else only save on memory.
var that = {}, priv = {}, jio_id_array_name = 'jio/id_array';
that.init = function ( options ) {
// initialize the JobQueue
// options.publisher : is the publisher to use to send events
// options.jio_id : the jio ID
var k, emptyFun = function (){}, jio_id_array;
if (priv.use_local_storage) {
jio_id_array = localOrCookieStorage.
getItem (jio_id_array_name)||[];
if (spec.publisher) {
priv.publisher = spec.publisher;
}
priv.jio_id = options.jio_id;
priv.job_object_name = 'jio/job_object/' + priv.jio_id;
jio_id_array.push (priv.jio_id);
localOrCookieStorage.setItem (jio_id_array_name,jio_id_array);
}
priv.job_object = {};
that.copyJobQueueToLocalStorage();
for (k in priv.recovered_job_object) {
priv.recovered_job_object[k].onResponse = emptyFun;
that.addJob (priv.recovered_job_object[k]);
}
};
that.close = function () {
// close the job queue.
// it also deletes from local storage only if the job list is
// empty.
if (JSON.stringify(priv.job_object) === '{}') {
localOrCookieStorage.deleteItem(priv.job_object_name);
}
};
that.getNewQueueID = function () {
// Returns a new queueID
var k = null, id = 0,
jio_id_array = localOrCookieStorage.getItem (jio_id_array_name)||[];
for (k = 0; k < jio_id_array.length; k += 1) {
if (jio_id_array[k] >= jio_global_obj.queue_id) {
jio_global_obj.queue_id = jio_id_array[k] + 1;
}
}
id = jio_global_obj.queue_id;
jio_global_obj.queue_id ++;
return id;
};
that.recoverOlderJobObject = function () {
// recover job object from older inactive jio
var k = null, new_jio_id_array = [], jio_id_array_changed = false,
jio_id_array;
if (priv.use_local_storage) {
jio_id_array = localOrCookieStorage.
getItem (jio_id_array_name)||[];
for (k = 0; k < jio_id_array.length; k += 1) {
if (localOrCookieStorage.getItem (
'jio/id/'+jio_id_array[k]) < Date.now () - 10000) {
// remove id from jio_id_array
// 10000 sec ? delete item
localOrCookieStorage.deleteItem('jio/id/'+
jio_id_array[k]);
// job recovery
priv.recovered_job_object = localOrCookieStorage.
getItem('jio/job_object/'+ jio_id_array[k]);
// remove ex job object
localOrCookieStorage.deleteItem(
'jio/job_object/'+ jio_id_array[k]);
jio_id_array_changed = true;
} else {
new_jio_id_array.push (jio_id_array[k]);
}
}
if (jio_id_array_changed) {
localOrCookieStorage.setItem(jio_id_array_name,
new_jio_id_array);
}
}
};
that.isThereJobsWhere = function( func ) {
// Check if there is jobs, in the queue,
// where [func](job) == true.
var id = 'id';
if (!func) { return true; }
for (id in priv.job_object) {
if (func(priv.job_object[id])) {
return true;
}
}
return false;
};
that.copyJobQueueToLocalStorage = function () {
// Copy job queue into localStorage.
if (priv.use_local_storage) {
return localOrCookieStorage.setItem(
priv.job_object_name,priv.job_object);
} else {
return false;
}
};
that.createJob = function ( options ) {
return that.addJob ( newJob ( options ) );
};
that.addJob = function ( job ) {
// Add a job to the queue, browsing all jobs
// and check if the new job can eliminate older ones,
// can replace older one, can be accepted, or must wait
// for older ones.
// It also clean fail or done jobs.
// job = the job object
var new_one = true, elim_array = [], wait_array = [],
remove_array = [], base_storage = null, id = 'id';
//// browsing current jobs
for (id in priv.job_object) {
if (jio_global_obj.job_managing_method.canRemoveFailOrDone(
priv.job_object[id],job)) {
remove_array.push(id);
continue;
}
if (jio_global_obj.job_managing_method.canSelect(
priv.job_object[id],job)) {
if (jio_global_obj.job_managing_method.canEliminate(
priv.job_object[id],job)) {
elim_array.push(id);
continue;
}
if (jio_global_obj.job_managing_method.canReplace(
priv.job_object[id],job)) {
base_storage = newBaseStorage(
{'queue':that,'job':priv.job_object[id]});
base_storage.replace(job);
new_one = false;
break;
}
if (jio_global_obj.job_managing_method.cannotAccept(
priv.job_object[id],job)) {
// Job not accepted
return false;
}
if (jio_global_obj.job_managing_method.mustWait(
priv.job_object[id],job)) {
wait_array.push(id);
continue;
}
// one of the previous tests must be ok.
// the program must not reach this part of the 'for'.
}
}
//// end browsing current jobs
if (new_one) {
// if it is a new job, we can eliminate deprecated jobs and
// set this job dependencies.
for (id = 0; id < elim_array.length; id += 1) {
base_storage = newBaseStorage(
{'queue':that,
'job':priv.job_object[elim_array[id]]});
base_storage.eliminate();
}
if (wait_array.length > 0) {
job.status = 'wait';
job.waiting_for = {'job_id_array':wait_array};
for (id = 0; id < wait_array.length; id += 1) {
if (priv.job_object[wait_array[id]]) {
priv.job_object[wait_array[id]].max_tries = 1;
}
}
}
for (id = 0; id < remove_array.length; id += 1) {
that.removeJob(priv.job_object[remove_array[id]]);
}
// set job id
job.id = priv.job_id;
job.tries = 0;
priv.job_id ++;
// save the new job into the queue
priv.job_object[job.id] = job;
}
// save into localStorage
that.copyJobQueueToLocalStorage();
return true;
}; // end addJob
that.removeJob = function ( options ) {
// Remove job(s) from queue where [options.where](job) === true.
// If there is no job in [options], then it will treat all job.
// If there is no [where] function, then it will remove all selected
// job. It means that if no option was given, it'll remove all jobs.
// options.job = the job object containing at least {id:..}.
// options.where = remove values where options.where(job) === true
var settings = $.extend ({where:function (job) {return true;}},
options),andwhere,found=false,k='key';
//// modify the job list
if (settings.job) {
if (priv.job_object[settings.job.id] && settings.where(
priv.job_object[settings.job.id]) ) {
delete priv.job_object[settings.job.id];
found = true;
}
}else {
for (k in priv.job_object) {
if (settings.where(priv.job_object[k])) {
delete priv.job_object[k];
found = true;
}
}
}
if (!found) {
console.error('No jobs was found, when trying to remove some.');
}
//// end modifying
that.copyJobQueueToLocalStorage();
};
that.resetAll = function () {
// Reset all job to 'initial'.
// TODO manage jobs ! All jobs are not 'initial'.
var id = 'id';
for (id in priv.job_object) {
priv.job_object[id].status = 'initial';
}
that.copyJobQueueToLocalStorage();
};
that.invokeAll = function () {
// Do all jobs in the queue.
var i = 'id', j, ok;
//// do All jobs
for (i in priv.job_object) {
ok = false;
if (priv.job_object[i].status === 'initial') {
// if status initial
// invoke new job
that.invoke(priv.job_object[i]);
} else if (priv.job_object[i].status === 'wait') {
ok = true;
// if status wait
if (priv.job_object[i].waiting_for.job_id_array) {
// wait job
// browsing job id array
for (j = 0;
j < priv.job_object[i].
waiting_for.job_id_array.length;
j += 1) {
if (priv.job_object[priv.job_object[i].
waiting_for.job_id_array[j]]) {
// if a job is still exist, don't invoke
ok = false;
break;
}
}
}
if (priv.job_object[i].waiting_for.time) {
// wait time
if (priv.job_object[i].waiting_for.time > Date.now()) {
// it is not time to restore the job!
ok = false;
}
}
// else wait nothing
if (ok) {
// invoke waiting job
that.invoke(priv.job_object[i]);
}
}
}
this.copyJobQueueToLocalStorage();
//// end, continue doing jobs asynchronously
};
that.invoke = function (job) {
// Do a job invoking the good method in the good storage.
var base_storage;
//// analysing job method
// if the method does not exist, do nothing
if (!jio_const_obj.job_method_object[job.method]) {
return false; // suppose never happen
}
// test if a similar job is on going, in order to publish a start
// event if it is the first of his kind (method).
if (!that.isThereJobsWhere(function (testjob){
return (testjob.method === job.method &&
testjob.method === 'initial');
})) {
job.status = 'on_going';
priv.publisher.publish(jio_const_obj.job_method_object[
job.method]['start_'+job.method]);
} else {
job.status = 'on_going';
}
// Create a storage object and use it to save,load,...!
base_storage = newBaseStorage({'queue':this,'job':job});
base_storage.execute();
//// end method analyse
};
that.ended = function (endedjob) {
// It is a callback function called just before user onResponse.
// It is called to manage job_object according to the ended job.
var job = $.extend(true,{},endedjob); // copy
// This job is supposed terminated, we can remove it from queue.
that.removeJob ({'job':job});
//// ended job analyse
// if the job method does not exists, return false
if (!jio_const_obj.job_method_object[job.method]) {
return false;
}
// if there isn't some job to do, then send stop event
if (!that.isThereJobsWhere(function(testjob){
return (testjob.method === job.method &&
// testjob.status === 'wait' || // TODO ?
testjob.status === 'on_going' ||
testjob.status === 'initial');
})) {
priv.publisher.publish(
jio_const_obj.job_method_object[
job.method]['stop_'+job.method]);
return;
}
//// end returnedJobAnalyse
};
that.clean = function () {
// Clean the job list, removing all jobs that have failed.
// It also change the localStorage job queue
that.removeJob (
undefined,{
'where':function (job) {
return (job.status === 'fail');
} });
};
//// end Methods
//// Initialize
priv.use_local_storage = spec.options.use_local_storage;
priv.publisher = spec.publisher;
priv.job_id = 1;
priv.jio_id = 0;
priv.job_object_name = '';
priv.job_object = {};
priv.recovered_job_object = {};
//// end Initialize
return that;
};
// end Job & JobQueue
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// jio job listener
newJobListener = function ( spec, my ) {
// A little daemon which will start jobs from the joblist
var that = {}, priv = {};
priv.interval = 200;
priv.id = null;
priv.queue = spec.queue;
that.setIntervalDelay = function (interval) {
// Set the time between two joblist check in millisec
priv.interval = interval;
};
that.start = function () {
// Start the listener. It will always check if there are jobs in the
// queue
if (!priv.id) {
priv.id = setInterval (function () {
// recover older jobs
priv.queue.recoverOlderJobObject();
priv.queue.invokeAll();
},priv.interval);
return true;
} else {
return false;
}
};
that.stop = function () {
if (priv.id) {
clearInterval (priv.id);
priv.id = null;
return true;
}
return false;
};
return that;
};
// end jio job listener
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// ActivityUpdater
newActivityUpdater = function () {
// The activity updater is a little thread that proves activity of this
// jio instance.
var that = {}, priv = {};
//// private vars
priv.interval = 400;
priv.id = null;
//// end private vars
//// methods
that.start = function (id) {
// start the updater
if (!priv.id) {
that.touch(id);
priv.id = setInterval (function () {
that.touch(id);
},priv.interval);
return true;
} else {
return false;
}
};
that.stop = function () {
// stop the updater
if (priv.id) {
clearInterval (priv.id);
priv.id = null;
return true;
}
return false;
};
that.touch = function (id) {
localOrCookieStorage.setItem ('jio/id/' + id,Date.now() );
};
//// end methods
return that;
};
// end ActivityUpdater
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// BaseStorage
newBaseStorage = function ( options ) {
// The base storage, will call the good method from the good storage,
// and will check and return the associated value.
var that = {}, priv = {};
//// Private attributes
priv.job = options.job;
priv.onResponse = options.job.onResponse;
priv.queue = options.queue;
priv.res = {'status':'done','message':''};
priv.sorted = false;
priv.limited = false;
priv.research_done = false;
//// end Private attributes
//// Private Methods
priv.fail_checkNameAvailability = function () {
priv.res.message = 'Unable to check name availability.';
};
priv.done_checkNameAvailability = function ( isavailable ) {
priv.res.message = priv.job.user_name + ' is ' +
(isavailable?'':'not ') + 'available.';
priv.res.return_value = isavailable;
};
priv.fail_loadDocument = function () {
priv.res.message = 'Unable to load document.';
};
priv.done_loadDocument = function ( returneddocument ) {
priv.res.message = 'Document loaded.';
priv.res.return_value = returneddocument;
// transform date into ms
priv.res.return_value.last_modified =
new Date(priv.res.return_value.last_modified).getTime();
priv.res.return_value.creation_date =
new Date(priv.res.return_value.creation_date).getTime();
};
priv.fail_saveDocument = function () {
priv.res.message = 'Unable to save document.';
};
priv.done_saveDocument = function () {
priv.res.message = 'Document saved.';
};
priv.fail_getDocumentList = function () {
priv.res.message = 'Unable to retrieve document list.';
};
priv.done_getDocumentList = function ( documentlist ) {
var i;
priv.res.message = 'Document list received.';
priv.res.return_value = documentlist;
for (i = 0; i < priv.res.return_value.length; i += 1) {
// transform current date format into ms since 1/1/1970
// useful for easy comparison
if (typeof priv.res.return_value[i].last_modified !== 'number') {
priv.res.return_value[i].last_modified =
new Date(priv.res.return_value[i].last_modified).
getTime();
}
if (typeof priv.res.return_value[i].creation_date !== 'number') {
priv.res.return_value[i].creation_date =
new Date(priv.res.return_value[i].creation_date).
getTime();
}
}
// check for sorting
if (!priv.sorted && typeof priv.job.sort !== 'undefined') {
that.sortDocumentArray(priv.res.return_value);
}
// check for limiting
if (!priv.limited &&
typeof priv.job.limit !== 'undefined' &&
typeof priv.job.limit.begin !== 'undefined' &&
typeof priv.job.limit.end !== 'undefined') {
priv.res.return_value =
that.limitDocumentArray(priv.res.return_value);
}
// check for research
if (!priv.research_done && typeof priv.job.search !== 'undefined') {
priv.res.return_value =
that.searchDocumentArray(priv.res.return_value);
}
};
priv.fail_removeDocument = function () {
priv.res.message = 'Unable to removed document.';
};
priv.done_removeDocument = function () {
priv.res.message = 'Document removed.';
};
priv.retryLater = function () {
// Change the job status to wait for time.
// The listener will invoke this job later.
var time = (priv.job.tries*priv.job.tries*1000);
if (time > jio_global_obj.max_wait_time) {
time = jio_global_obj.max_wait_time;
}
priv.job.status = 'wait';
priv.job.waiting_for = {'time':Date.now() + time};
};
//// end Private Methods
//// Getters Setters
that.cloneJob = function () {
return $.extend(true,{},priv.job);
};
that.getUserName = function () {
return priv.job.user_name || '';
};
that.getApplicantID = function () {
return priv.job.applicant.ID || '';
};
that.getStorageUserName = function () {
return priv.job.storage.user_name || '';
};
that.getStoragePassword = function () {
return priv.job.storage.password || '';
};
that.getStorageURL = function () {
return priv.job.storage.url || '';
};
that.getSecondStorage = function () {
return priv.job.storage.storage || {};
};
that.getStorageArray = function () {
return priv.job.storage.storage_array || [];
};
that.getFileName = function () {
return priv.job.name || '';
};
that.getFileContent = function () {
return priv.job.content || '';
};
that.cloneOptionObject = function () {
return $.extend(true,{},priv.job.options);
};
that.getMaxTries = function () {
return priv.job.max_tries;
};
that.getTries = function () {
return priv.job.tries || 0;
};
that.setMaxTries = function (max_tries) {
priv.job.max_tries = max_tries;
};
//// end Getters Setters
//// Public Methods
that.addJob = function ( newjob ) {
return priv.queue.createJob ( newjob );
};
that.eliminate = function () {
// Stop and remove a job !
priv.job.max_tries = 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
priv.job.tries = 0;
priv.job.date = newjob.date;
priv.job.onResponse = newjob.onResponse;
priv.res.status = 'fail';
priv.res.message = 'Job Stopped!';
priv.res.error = {};
priv.res.error.status = 0;
priv.res.error.statusText = 'Replaced';
priv.res.error.message = 'The job was replaced by a newer one.';
priv['fail_'+priv.job.method]();
priv.onResponse(priv.res);
};
that.fail = function ( errorobject ) {
// Called when a job has failed.
// It will retry the job from a certain moment or it will return
// a failure.
priv.res.status = 'fail';
priv.res.error = errorobject;
// init error object with default values
priv.res.error.status = priv.res.error.status || 0;
priv.res.error.statusText =
priv.res.error.statusText || 'Unknown Error';
priv.res.error.array = priv.res.error.array || [];
priv.res.error.message = priv.res.error.message || '';
// retry ?
if (!priv.job.max_tries ||
priv.job.tries < priv.job.max_tries) {
priv.retryLater();
} else {
priv.job.status = 'fail';
priv['fail_'+priv.job.method]();
priv.queue.ended(priv.job);
priv.onResponse(priv.res);
}
};
that.done = function ( retvalue ) {
// Called when a job has terminated successfully.
// It will return the return value by the calling of onResponse
// function.
priv.job.status = 'done';
priv['done_'+priv.job.method]( retvalue );
priv.queue.ended(priv.job);
priv.onResponse(priv.res);
};
that.execute = function () {
// Execute the good function from the good storage.
priv.job.tries = that.getTries() + 1;
if ( !jio_global_obj.storage_type_object[priv.job.storage.type] ) {
return null;
}
return jio_global_obj.storage_type_object[ priv.job.storage.type ]({
'job':priv.job,'queue':priv.queue})[priv.job.method]();
};
// These methods must be redefined!
that.checkNameAvailability = function () {
that.fail({status:0,
statusText:'Undefined Method',
message:'This method must be redefined!'});
};
that.loadDocument = function () {
that.fail({status:0,
statusText:'Undefined Method',
message:'This method must be redefined!'});
};
that.saveDocument = function () {
that.fail({status:0,
statusText:'Undefined Method',
message:'This method must be redefined!'});
};
that.getDocumentList = function () {
that.fail({status:0,
statusText:'Undefined Method',
message:'This method must be redefined!'});
};
that.removeDocument = function () {
that.fail({status:0,
statusText:'Undefined Method',
message:'This method must be redefined!'});
};
/**
* Sorts a document list using sort parameters set in the job.
* @method sortDocumentArray
* @param {array} documentarray The array we want to sort.
*/
that.sortDocumentArray = function (documentarray) {
documentarray.sort(function (row1,row2) {
var k, res;
for (k in priv.job.sort) {
var sign = (priv.job.sort[k] === 'descending' ? -1 : 1);
if (row1[k] === row2[k]) { continue; }
return (row1[k] > row2[k] ? sign : -sign);
}
return 0;
});
that.sortDone();
};
/**
* Tells to this storage that the sorting process is already done.
* @method sortDone
*/
that.sortDone = function () {
priv.sorted = true;
};
/**
* Limits the document list. Clones only the document list between
* begin and end set in limit object in the job.
* @method limitDocumentArray
* @param {array} documentarray The array we want to limit
* @return {array} The new document list
*/
that.limitDocumentArray = function (documentarray) {
that.limitDone();
return documentarray.slice(priv.job.limit.begin,
priv.job.limit.end);
};
/**
* Tells to this storage that the limiting process is already done.
* @method limitDone
*/
that.limitDone = function () {
priv.limited = true;
};
/**
* Search the strings inside the document list. Clones the document list
* containing only the matched strings.
* @method searchDocumentArray
* @param {array} documentarray The array we want to search into.
* @return {array} The new document list.
*/
that.searchDocumentArray = function (documentarray) {
var i, k, newdocumentarray = [];
for (i = 0; i < documentarray.length; i += 1) {
for (k in priv.job.search) {
if (typeof documentarray[i][k] === 'undefined') {
continue;
}
if (documentarray[i][k].search(priv.job.search[k]) > -1) {
newdocumentarray.push(documentarray[i]);
break;
}
}
}
that.researchDone();
return newdocumentarray;
};
/**
* Tells to this storage that the research is already done.
* @method researchDone
*/
that.researchDone = function () {
priv.research_done = true;
};
//// end Public Methods
return that;
};
// end BaseStorage
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// JIO Constructor
newJioConstructor = function ( spec, my ) {
// JIO Constructor, create a new JIO object.
// It just initializes values.
// storage : the storage that contains {type:..,[storageinfos]}
// applicant : the applicant that contains {ID:...}
// these parameters are optional and may be 'string' or 'object'
var that = {}, priv = {};
priv.wrongParametersError = function (settings) {
var m = 'Method: '+ settings.method +
', One or some parameters are undefined.';
console.error (m);
settings.onResponse({status:'fail',
error: {status:0,
statusText:'Undefined Parameter',
message: m}});
return null;
};
//// Getters Setters
that.getID = function () {
return priv.id;
};
//// end Getters Setters
//// Methods
that.start = function () {
// Start JIO: start listening to jobs and make it ready
if (priv.id !== 0) { return false; }
// set a new jio id
priv.id = priv.queue.getNewQueueID();
// initializing objects
priv.queue.init({'jio_id':priv.id});
// start activity updater
if (priv.updater){
priv.updater.start(priv.id);
}
// start listening
priv.listener.start();
// is now ready
priv.ready = true;
return that.isReady();
};
that.stop = function () {
// Finish some job if possible and stop listening.
// It can be restarted later
priv.queue.close();
priv.listener.stop();
if (priv.updater) {
priv.updater.stop();
}
priv.ready = false;
priv.id = 0;
return true;
};
that.kill = function () {
// kill this JIO, job listening and job operation (event if they
// are on going!)
priv.queue.close();
priv.listener.stop();
if (priv.updater) {
priv.updater.stop();
}
// TODO
priv.ready = false;
return true;
};
that.isReady = function () {
// Check if Jio is ready to use.
return priv.ready;
};
that.publish = function (eventname, obj) {
// publish an event on this jio
// eventname : the event name
// obj : is an object containing some parameters for example
if (!that.isReady()) { return ; }
return priv.pubsub.publish(eventname,obj);
};
that.subscribe = function (eventname, callback) {
// subscribe to an event on this jio. We can subscribe to jio event
// even if jio is not started. Returns the callback function in
// order to unsubscribe it.
// eventname : the event name.
// callback : called after receiving event.
return priv.pubsub.subscribe(eventname,callback);
};
that.unsubscribe = function (eventname,callback) {
// unsubscribe callback from an event
return priv.pubsub.unsubscribe(eventname,callback);
};
that.checkNameAvailability = function ( options ) {
// Check the user availability 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 and its 'isAvailable'
// return value.
// options.storage : the storage where to check (optional)
// options.applicant : the applicant (optional)
// options.onResponse(result) : called to get the result.
// returns: - null if dependencies are missing
// - false if the job was not added
// - true if the job was added or replaced
// example :
// jio.checkNameAvailability({'user_name':'myName','onResponse':
// function (result) {
// if (result.status === 'done') {
// if (result.return_value === true) { // available
// } else { } // not available
// } else { } // Error
// }});
var settings = $.extend (true,{
'user_name': priv.storage.user_name,
'storage': priv.storage,
'applicant': priv.applicant,
'method': 'checkNameAvailability',
'onResponse': function () {}
},options);
// check dependencies
if (that.isReady() && settings.user_name &&
settings.storage && settings.applicant) {
return priv.queue.createJob ( settings );
}
return priv.wrongParametersError(settings);
};
that.saveDocument = function ( options ) {
// 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 save (optional)
// options.applicant : the applicant (optional)
// options.onResponse(result) : called to get the result.
// returns: - null if dependencies are missing
// - false if the job was not added
// - true if the job was added or replaced
// jio.saveDocument({'name':'file','content':'content',
// 'onResponse': function (result) {
// if (result.status === 'done') { // Saved
// } else { } // Error
// }});
var settings = $.extend(true,{
'storage': priv.storage,
'applicant': priv.applicant,
'content': '',
'method':'saveDocument',
'onResponse': function () {}
},options);
// check dependencies
if (that.isReady() && settings.name &&
settings.storage && settings.applicant) {
return priv.queue.createJob ( settings );
}
return priv.wrongParametersError(settings);
};
that.loadDocument = function ( options ) {
// 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 and its 'content'
// return value.
// options.storage : the storage where to load (optional)
// options.applicant : the applicant (optional)
// options.onResponse(result) : called to get the result.
// returns: - null if dependencies are missing
// - false if the job was not added
// - true if the job was added or replaced
// jio.loadDocument({'name':'file','onResponse':
// function (result) {
// if (result.status === 'done') { // Loaded
// } else { } // Error
// }});
// result.return_value is a document object that looks like {
// name:'string',content:'string',
// creation_date:123,last_modified:456 }
var settings = $.extend (true,{
'storage': priv.storage,
'applicant': priv.applicant,
'method':'loadDocument',
'onResponse': function(){}
},options);
// check dependencies
if (that.isReady() && settings.name &&
settings.storage && settings.applicant) {
return priv.queue.createJob ( settings );
}
return priv.wrongParametersError(settings);
};
that.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 get the list (optional)
// options.applicant : the applicant (optional)
// options.onResponse(result) : called to get the result.
// returns: - null if dependencies are missing
// - false if the job was not added
// - true if the job was added or replaced
// jio.getDocumentList({'onResponse':
// function (result) {
// if (result.status === 'done') { // OK
// console.log(result.return_value);
// } else { } // Error
// }});
// result.return_value is an Array that contains documents objects.
var settings = $.extend (true,{
'storage': priv.storage,
'applicant': priv.applicant,
'method':'getDocumentList',
'onResponse':function(){}
},options);
// check dependencies
if (that.isReady() && settings.storage && settings.applicant ) {
return priv.queue.createJob( settings );
}
return priv.wrongParametersError(settings);
};
that.removeDocument = function ( options ) {
// Remove a document in the storage set in [options]
// or in the storage set at init.
// options.storage : the storage where to remove (optional)
// options.applicant : the applicant (optional)
// options.onResponse(result) : called to get the result.
// returns: - null if dependencies are missing
// - false if the job was not added
// - true if the job was added or replaced
// jio.removeDocument({'name':'file','onResponse':
// function (result) {
// if(result.status === 'done') { // Removed
// } else { } // Not Removed
// }});
var settings = $.extend (true,{
'storage': priv.storage,
'applicant': priv.applicant,
'method':'removeDocument',
'onResponse':function (){}
},options);
if (that.isReady() && settings.name &&
settings.storage && settings.applicant ) {
return priv.queue.createJob ( settings );
}
return priv.wrongParametersError(settings);
};
//// end Methods
//// Initialize
var settings = $.extend(true,{'use_local_storage':true},spec.options);
// objectify storage and applicant
if(typeof spec.storage === 'string') {
spec.storage = JSON.parse(spec.storage);
}
if(typeof spec.applicant === 'string') {
spec.applicant = JSON.parse(spec.applicant);
}
// set init values
priv['storage'] = spec.storage;
priv['applicant'] = spec.applicant;
priv['id'] = 0;
priv['pubsub'] = newPubSub({options:settings});
priv['queue'] = newJobQueue({publisher:priv.pubsub,
options:settings});
priv['listener'] = newJobListener({queue:priv.queue,
options:settings});
priv['ready'] = false;
if (settings.use_local_storage) {
priv['updater'] = newActivityUpdater({options:settings});
} else {
priv['updater'] = null;
}
// check storage type
if (priv.storage &&
!jio_global_obj.storage_type_object[priv.storage.type]){
console.error('Unknown storage type "' + priv.storage.type +'"');
}
// start jio process
that.start();
//// end Initialize
return that;
};
// end JIO
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Jio creator
newJioCreator = function ( spec, my ) {
var that = {};
// Jio creator object
// this object permit to create jio object
that.newJio = function ( storage, applicant, options ) {
// Return a new instance of JIO
// 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(true,{'use_local_storage':true},options);
return newJioConstructor({storage:storage,
applicant:applicant,
options:settings});
};
that.newBaseStorage = function ( spec, my ) {
// Create a Jio Storage which can be used to design new storage.
return newBaseStorage( spec, my );
};
that.addStorageType = function ( type, constructor ) {
// Add a storage type to jio. Jio must have keys/types which are
// bound to a storage creation function. ex: 'local', will
// create a LocalStorage (in jio.storage.js).
// It can replace a older type with a newer creation function.
// type : the type of the storage.
// constructor : the function which returns a new storage object.
if (type && constructor) {
jio_global_obj.storage_type_object[type] = constructor;
return true;
}
return false;
};
that.getGlobalObject = function () {
// Returns the global jio values
return jio_global_obj;
};
that.getConstObject = function () {
// Returns a copy of the constants
return $.extend(true,{},jio_const_obj);
};
return that;
};
return newJioCreator();
// end Jio Creator
////////////////////////////////////////////////////////////////////////////
};
if (window.requirejs) {
define ('JIO',['LocalOrCookieStorage','jQuery'],jioLoaderFunction);
return undefined;
} else {
return jioLoaderFunction ( LocalOrCookieStorage, jQuery );
}
}());
/**
* Adds 5 storages to JIO.
* - LocalStorage ('local')
* - DAVStorage ('dav')
* - ReplicateStorage ('replicate')
* - IndexedStorage ('indexed')
* - CryptedStorage ('crypted')
*
* @module JIOStorages
*/
(function () {
var jioStorageLoader =
function ( LocalOrCookieStorage, $, Base64, sjcl, Jio) {
////////////////////////////////////////////////////////////////////////////
// Tools
// end Tools
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Classes
var newLocalStorage,newDAVStorage,newReplicateStorage,
newIndexedStorage,newCryptedStorage;
// end Classes
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Local Storage
/**
* JIO Local Storage. Type = 'local'.
* It is a database located in the browser local storage.
*/
newLocalStorage = function ( spec, my ) {
// LocalStorage constructor
var that = Jio.newBaseStorage( spec, my ), priv = {};
priv.storage_user_array_name = 'jio/local_user_array';
priv.storage_file_array_name = 'jio/local_file_name_array/' +
that.getStorageUserName() + '/' + that.getApplicantID();
/**
* Returns a list of users.
* @method getUserArray
* @return {array} The list of users.
*/
priv.getUserArray = function () {
return LocalOrCookieStorage.getItem(
priv.storage_user_array_name) || [];
};
/**
* Adds a user to the user list.
* @method addUser
* @param {string} user_name The user name.
*/
priv.addUser = function (user_name) {
var user_array = priv.getUserArray();
user_array.push(user_name);
LocalOrCookieStorage.setItem(priv.storage_user_array_name,
user_array);
};
/**
* checks if a user exists in the user array.
* @method userExists
* @param {string} user_name The user name
* @return {boolean} true if exist, else false
*/
priv.userExists = function (user_name) {
var user_array = priv.getUserArray(), i, l;
for (i = 0, l = user_array.length; i < l; i += 1) {
if (user_array[i] === user_name) {
return true;
}
}
return false;
};
/**
* Returns the file names of all existing files owned by the user.
* @method getFileNameArray
* @return {array} All the existing file paths.
*/
priv.getFileNameArray = function () {
return LocalOrCookieStorage.getItem(
priv.storage_file_array_name) || [];
};
/**
* Adds a file name to the local file name array.
* @method addFileName
* @param {string} file_name The new file name.
*/
priv.addFileName = function (file_name) {
var file_name_array = priv.getFileNameArray();
file_name_array.push(file_name);
LocalOrCookieStorage.setItem(priv.storage_file_array_name,
file_name_array);
};
/**
* Removes a file name from the local file name array.
* @method removeFileName
* @param {string} file_name The file name to remove.
*/
priv.removeFileName = function (file_name) {
var i, l, array = priv.getFileNameArray(), new_array = [];
for (i = 0, l = array.length; i < l; i+= 1) {
if (array[i] !== file_name) {
new_array.push(array[i]);
}
}
LocalOrCookieStorage.setItem(priv.storage_file_array_name,
new_array);
};
/**
* Checks the availability of a user name set in the job.
* It will check if the user is set in the local user object
* @method checkNameAvailability
*/
that.checkNameAvailability = function () {
setTimeout(function () {
that.done(!priv.userExists(that.getUserName()));
}, 100);
}; // end checkNameAvailability
/**
* Saves a document in the local storage.
* It will store the file in 'jio/local/USR/APP/FILE_NAME'.
* @method saveDocument
*/
that.saveDocument = function () {
// wait a little in order to simulate asynchronous saving
setTimeout (function () {
var doc = null, path =
'jio/local/'+that.getStorageUserName()+'/'+
that.getApplicantID()+'/'+
that.getFileName();
// reading
doc = LocalOrCookieStorage.getItem(path);
if (!doc) {
// create document
doc = {
'name': that.getFileName(),
'content': that.getFileContent(),
'creation_date': Date.now(),
'last_modified': Date.now()
};
if (!priv.userExists(that.getStorageUserName())) {
priv.addUser (that.getStorageUserName());
}
priv.addFileName(that.getFileName());
} else {
// overwriting
doc.last_modified = Date.now();
doc.content = that.getFileContent();
}
LocalOrCookieStorage.setItem(path, doc);
return that.done();
}, 100);
}; // end saveDocument
/**
* Loads a document from the local storage.
* It will load file in 'jio/local/USR/APP/FILE_NAME'.
* You can add an 'options' object to the job, it can contain:
* - metadata_only {boolean} default false, retrieve the file metadata
* only if true.
* - content_only {boolean} default false, retrieve the file content
* only if true.
* @method loadDocument
*/
that.loadDocument = function () {
// document object is {'name':string,'content':string,
// 'creation_date':date,'last_modified':date}
setTimeout(function () {
var doc = null, settings = that.cloneOptionObject();
doc = LocalOrCookieStorage.getItem(
'jio/local/'+that.getStorageUserName()+'/'+
that.getApplicantID()+'/'+that.getFileName());
if (!doc) {
that.fail({status:404,statusText:'Not Found.',
message:'Document "'+ that.getFileName() +
'" not found in localStorage.'});
} else {
if (settings.metadata_only) {
delete doc.content;
} else if (settings.content_only) {
delete doc.last_modified;
delete doc.creation_date;
}
that.done(doc);
}
}, 100);
}; // end loadDocument
/**
* Gets a document list from the local storage.
* It will retreive an array containing files meta data owned by
* the user.
* @method getDocumentList
*/
that.getDocumentList = function () {
// the list is [object,object] -> object = {'name':string,
// 'last_modified':date,'creation_date':date}
setTimeout(function () {
var new_array = [], array = [], i, l, k = 'key',
path = 'jio/local/'+that.getStorageUserName()+'/'+
that.getApplicantID(), file_object = {};
array = priv.getFileNameArray();
for (i = 0, l = array.length; i < l; i += 1) {
file_object =
LocalOrCookieStorage.getItem(path+'/'+array[i]);
if (file_object) {
new_array.push ({
'name':file_object.name,
'creation_date':file_object.creation_date,
'last_modified':file_object.last_modified});
}
}
that.done(new_array);
}, 100);
}; // end getDocumentList
/**
* Removes a document from the local storage.
* It will also remove the path from the local file array.
* @method removeDocument
*/
that.removeDocument = function () {
setTimeout (function () {
var path = 'jio/local/'+
that.getStorageUserName()+'/'+
that.getApplicantID()+'/'+
that.getFileName();
// deleting
LocalOrCookieStorage.deleteItem(path);
priv.removeFileName(that.getFileName());
return that.done();
}, 100);
};
return that;
};
// end Local Storage
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// DAVStorage
newDAVStorage = function ( spec, my ) {
var that = Jio.newBaseStorage( spec, my );
that.mkcol = function ( options ) {
// create folders in dav storage, synchronously
// options : contains mkcol list
// options.url : the davstorage url
// options.path: if path=/foo/bar then creates url/dav/foo/bar
// options.success: the function called if success
// options.user_name: the user name
// options.password: the password
// TODO this method is not working !!!
var settings = $.extend ({
'success':function(){},'error':function(){}},options),
split_path = ['split_path'], tmp_path = 'temp/path';
// if pathstep is not defined, then split the settings.path
// and do mkcol recursively
if (!settings.pathsteps) {
settings.pathsteps = 1;
that.mkcol(settings);
} else {
split_path = settings.path.split('/');
// // check if the path is terminated by '/'
// if (split_path[split_path.length-1] == '') {
// split_path.length --;
// }
// check if the pathstep is lower than the longer
if (settings.pathsteps >= split_path.length-1) {
return settings.success();
}
split_path.length = settings.pathsteps + 1;
settings.pathsteps++;
tmp_path = split_path.join('/');
// alert(settings.url + tmp_path);
$.ajax ( {
url: settings.url + tmp_path,
type: 'MKCOL',
async: true,
headers: {'Authorization': 'Basic '+Base64.encode(
settings.user_name + ':' +
settings.password ), Depth: '1'},
// xhrFields: {withCredentials: 'true'}, // cross domain
success: function () {
// done
that.mkcol(settings);
},
error: function (type) {
// alert(JSON.stringify(type));
// switch (type.status) {
// case 405: // Method Not Allowed
// // already exists
// t.mkcol(settings);
// break;
// default:
settings.error();
// break;
// }
}
} );
}
};
that.checkNameAvailability = function () {
// checks the availability of the [job.user_name].
// if the name already exists, it is not available.
// this.job.storage: the storage informations.
// this.job.storage.url: the dav storage url.
// this.job.user_name: the name we want to check.
// this.job.storage.user_name: the user name.
// this.job.storage.password: the user password.
$.ajax ( {
url: that.getStorageURL() + '/dav/' +
that.getStorageUserName() + '/',
async: true,
type: 'PROPFIND',
dataType: 'xml',
headers: {'Authorization': 'Basic '+Base64.encode(
that.getStorageUserName() + ':' +
that.getStoragePassword() ), Depth: '1'},
success: function (xmlData) {
that.done(false);
},
error: function (type) {
if (type.status === 404) {
that.done(true);
} else {
type.message = 'Cannot check availability of "' +
that.getUserName() + '" into DAVStorage.';
that.fail(type);
}
}
} );
};
that.saveDocument = function () {
// Save a document in a DAVStorage
// this.job.storage: the storage informations.
// this.job.storage.user_name: the user name.
// this.job.storage.password: the user password.
// this.job.applicant.ID: the applicant ID.
// this.job.name: the document name.
// this.job.content: the document content.
// TODO if path of /dav/user/applic does not exists, it won't work!
//// save on dav
$.ajax ( {
url: that.getStorageURL() + '/dav/' +
that.getStorageUserName() + '/' +
that.getApplicantID() + '/' +
that.getFileName(),
type: 'PUT',
data: that.getFileContent(),
async: true,
dataType: 'text', // TODO is it necessary ?
headers: {'Authorization':'Basic '+Base64.encode(
that.getStorageUserName()+':'+that.getStoragePassword())},
// xhrFields: {withCredentials: 'true'}, // cross domain
success: function () {
that.done();
},
error: function (type) {
type.message = 'Cannot save "' + that.getFileName() +
'" into DAVStorage.';
that.fail(type);
}
} );
//// end saving on dav
};
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.name: the document name we want to load.
// this.job.storage: the storage informations.
// this.job.storage.url: the dav storage url.
// 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 {'name':string,'content':string,
// 'creation_date':date,'last_modified':date}
var doc = {},
settings = that.cloneOptionObject(),
getContent = function () {
$.ajax ( {
url: that.getStorageURL() + '/dav/' +
that.getStorageUserName() + '/' +
that.getApplicantID() + '/' +
that.getFileName(),
type: "GET",
async: true,
dataType: 'text', // TODO is it necessary ?
headers: {'Authorization':'Basic '+Base64.encode(
that.getStorageUserName() + ':' +
that.getStoragePassword() )},
// xhrFields: {withCredentials: 'true'}, // cross domain
success: function (content) {
doc.content = content;
that.done(doc);
},
error: function (type) {
if (type.status === 404) {
type.message = 'Document "' +
that.getFileName() +
'" not found in localStorage.';
} else {
type.message =
'Cannot load "' + that.getFileName() +
'" from DAVStorage.';
}
that.fail(type);
}
} );
};
doc.name = that.getFileName();
if (settings.content_only) {
getContent();
return;
}
// Get properties
$.ajax ( {
url: that.getStorageURL() + '/dav/' +
that.getStorageUserName() + '/' +
that.getApplicantID() + '/' +
that.getFileName(),
type: "PROPFIND",
async: true,
dataType: 'xml',
headers: {'Authorization':'Basic '+Base64.encode(
that.getStorageUserName() + ':' +
that.getStoragePassword() )},
success: function (xmlData) {
// doc.last_modified =
$(xmlData).find(
'lp1\\:getlastmodified, getlastmodified'
).each( function () {
doc.last_modified = $(this).text();
});
$(xmlData).find(
'lp1\\:creationdate, creationdate'
).each( function () {
doc.creation_date = $(this).text();
});
if (!settings.metadata_only) {
getContent();
} else {
that.done(doc);
}
},
error: function (type) {
type.message = 'Cannot load "' + that.getFileName() +
'" informations from DAVStorage.';
that.fail(type);
}
} );
};
that.getDocumentList = function () {
// Get a document list from a DAVStorage. It returns a document
// array containing all the user documents informations.
// this.job.storage: the storage informations.
// this.job.storage.url: the dav storage url.
// this.job.storage.user_name: the user name.
// this.job.storage.password: the user password.
// this.job.applicant.ID: the applicant id.
// the list is [object,object] -> object = {'name':string,
// 'last_modified':date,'creation_date':date}
var document_array = [], file = {}, path_array = [];
$.ajax ( {
url: that.getStorageURL() + '/dav/' +
that.getStorageUserName() + '/' +
that.getApplicantID() + '/',
async: true,
type: 'PROPFIND',
dataType: 'xml',
headers: {'Authorization': 'Basic '+Base64.encode(
that.getStorageUserName() + ':' +
that.getStoragePassword() ), Depth: '1'},
success: function (xmlData) {
$(xmlData).find(
'D\\:response, response'
).each( function(i,data){
if(i>0) { // exclude parent folder
file = {};
$(data).find('D\\:href, href').each(function(){
path_array = $(this).text().split('/');
file.name =
(path_array[path_array.length-1] ?
path_array[path_array.length-1] :
path_array[path_array.length-2]+'/');
});
if (file.name === '.htaccess' ||
file.name === '.htpasswd') { return; }
$(data).find(
'lp1\\:getlastmodified, getlastmodified'
).each(function () {
file.last_modified = $(this).text();
});
$(data).find(
'lp1\\:creationdate, creationdate'
).each(function () {
file.creation_date = $(this).text();
});
document_array.push (file);
}
});
that.done(document_array);
},
error: function (type) {
type.message =
'Cannot get a document list from DAVStorage.';
that.fail(type);
}
} );
};
that.removeDocument = function () {
// Remove a document from a DAVStorage.
// this.job.name: the document name we want to remove.
// this.job.storage: the storage informations.
// this.job.storage.url: the dav storage url.
// this.job.storage.user_name: the user name.
// this.job.storage.password: the user password.
// this.job.applicant.ID: the applicant id.
$.ajax ( {
url: that.getStorageURL() + '/dav/' +
that.getStorageUserName() + '/' +
that.getApplicantID() + '/' +
that.getFileName(),
type: "DELETE",
async: true,
headers: {'Authorization':'Basic '+Base64.encode(
that.getStorageUserName() + ':' +
that.getStoragePassword() )},
// xhrFields: {withCredentials: 'true'}, // cross domain
success: function () {
that.done();
},
error: function (type) {
if (type.status === 404) {
that.done();
} else {
type.message = 'Cannot remove "' + that.getFileName() +
'" from DAVStorage.';
that.fail(type);
}
}
} );
};
return that;
};
// end DAVStorage
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// ReplicateStorage
newReplicateStorage = function ( spec, my ) {
var that = Jio.newBaseStorage( spec, my ), priv = {};
priv.storageArray = that.getStorageArray();
// TODO Add a tests that check if there is no duplicate storages.
priv.length = priv.storageArray.length;
priv.return_value_array = [];
priv.max_tries = that.getMaxTries();
that.setMaxTries (1);
priv.execJobsFromStorageArray = function (onResponse) {
var newjob = {}, i;
for (i = 0; i < priv.storageArray.length; i += 1) {
newjob = that.cloneJob();
newjob.max_tries = priv.max_tries;
newjob.storage = priv.storageArray[i];
newjob.onResponse = onResponse;
that.addJob ( newjob ) ;
}
};
that.checkNameAvailability = function () {
// Checks the availability of the [job.user_name].
// if the name already exists in a storage, it is not available.
// this.job.user_name: the name we want to check.
// this.job.storage.storageArray: An Array of storages.
var i = 'id', done = false, error_array = [],
res = {'status':'done'}, onResponse = function (result) {
priv.return_value_array.push(result);
if (!done) {
if (result.status === 'fail') {
res.status = 'fail';
error_array.push(result.error);
} else {
if (result.return_value === false) {
that.done (false);
done = true;
return;
}
}
if (priv.return_value_array.length ===
priv.length) {
if (res.status === 'fail') {
that.fail (
{status:207,
statusText:'Multi-Status',
message:'Some check availability of "' +
that.getUserName() + '" requests have failed.',
array:error_array});
} else {
that.done (true);
}
done = true;
return;
}
}
};
priv.execJobsFromStorageArray(onResponse);
};
that.saveDocument = function () {
// Save a single document in several storages.
// If a storage failed to save the document.
// this.job.storage: the storage informations.
// this.job.storage.user_name: the user name.
// this.job.storage.password: the user password.
// this.job.applicant.ID: the applicant ID.
// this.job.name: the document name.
// this.job.content: the document content.
var res = {'status':'done'}, i = 'id',
done = false, error_array = [],
onResponse = function (result) {
priv.return_value_array.push(result);
if (!done) {
if (result.status !== 'fail') {
that.done ();
done = true;
} else {
error_array.push(result.error);
if (priv.return_value_array.length ===
priv.length) {
that.fail (
{status:207,
statusText:'Multi-Status',
message:'All save "' + that.getFileName() +
'" requests have failed.',
array:error_array});
}
}
}
};
priv.execJobsFromStorageArray(onResponse);
};
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.name: the document name we want to load.
// this.job.storage: the storage informations.
// this.job.storage.user_name: the user name.
// this.job.storage.password: the user password.
// this.job.options.getContent: if true, also get the file content.
var doc = {}, i = 'id',
done = false, error_array = [],
res = {'status':'done'}, onResponse = function (result) {
priv.return_value_array.push(result);
if (!done) {
if (result.status !== 'fail') {
that.done (result.return_value);
done = true;
} else {
error_array.push(result.error);
if (priv.return_value_array.length ===
priv.length) {
that.fail (
{status:207,
statusText:'Multi-Status',
message:'All load "' + that.getFileName() +
'" requests have failed.',
array:error_array});
}
}
}
};
priv.execJobsFromStorageArray(onResponse);
};
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.user_name: the user name.
// this.job.storage.password: the user password.
// this.job.applicant.ID: the applicant id.
var res = {'status':'done'}, i = 'id',
done = false, error_array = [],
onResponse = function (result) {
priv.return_value_array.push(result);
if (!done) {
if (result.status !== 'fail') {
that.done (result.return_value);
done = true;
} else {
error_array.push(result.error);
if (priv.return_value_array.length ===
priv.length) {
that.fail (
{status:207,
statusText:'Multi-Status',
message:'All get document list requests'+
' have failed',
array:error_array});
}
}
}
};
priv.execJobsFromStorageArray(onResponse);
};
that.removeDocument = function () {
// Remove a document from several storages.
// this.job.name: the document name we want to remove.
// this.job.storage: the storage informations.
// this.job.storage.user_name: the user name.
// this.job.storage.password: the user password.
// this.job.applicant.ID: the applicant id.
var res = {'status':'done'}, i = 'key',
done = false, error_array = [],
onResponse = function (result) {
priv.return_value_array.push(result);
if (!done) {
if (result.status !== 'fail') {
that.done ();
done = true;
} else {
error_array.push(result.error);
if (priv.return_value_array.length ===
priv.length) {
that.fail (
{status:207,
statusText:'Multi-Status',
message:'All remove "' + that.getFileName() +
'" requests have failed.',
array:error_array});
}
}
}
};
priv.execJobsFromStorageArray(onResponse);
};
return that;
};
// end ReplicateStorage
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Indexed Storage
/**
* JIO Indexed Storage. Type = 'indexed'.
* It retreives files metadata from another storage and keep them
* in a cache so that we can work faster.
*/
newIndexedStorage = function ( spec, my ) {
// IndexedStorage constructor
var that = Jio.newBaseStorage( spec, my ), priv = {};
priv.storage_array_name = 'jio/indexed_storage_array';
priv.storage_file_array_name = 'jio/indexed_file_array/'+
JSON.stringify (that.getSecondStorage()) + '/' +
that.getApplicantID();
/**
* Check if the indexed storage array exists.
* @method indexedStorageArrayExists
* @return {boolean} true if exists, else false
*/
priv.indexedStorageArrayExists = function () {
return (LocalOrCookieStorage.getItem(
priv.storage_array_name) ? true : false);
};
/**
* Returns a list of indexed storages.
* @method getIndexedStorageArray
* @return {array} The list of indexed storages.
*/
priv.getIndexedStorageArray = function () {
return LocalOrCookieStorage.getItem(
priv.storage_array_name) || [];
};
/**
* Adds a storage to the indexed storage list.
* @method addIndexedStorage
* @param {object} storage The new indexed storage.
*/
priv.addIndexedStorage = function (storage) {
var indexed_storage_array = priv.getIndexedStorageArray();
indexed_storage_array.push(JSON.stringify (storage));
LocalOrCookieStorage.setItem(priv.storage_array_name,
indexed_storage_array);
};
/**
* Checks if a storage exists in the indexed storage list.
* @method isAnIndexedStorage
* @param {object} storage The storage to find.
* @return {boolean} true if found, else false
*/
priv.isAnIndexedStorage = function (storage) {
var json_storae = JSON.stringify (storage),i,l,
array = priv.getIndexedStorageArray();
for (i = 0, l = array.length; i < l; i+= 1) {
if (JSON.stringify(array[i]) === json_storae) {
return true;
}
}
return false;
};
/**
* Checks if the file array exists.
* @method fileArrayExists
* @return {boolean} true if exists, else false
*/
priv.fileArrayExists = function () {
return (LocalOrCookieStorage.getItem (
priv.storage_file_array_name) ? true : false);
};
/**
* Returns the file from the indexed storage but not there content.
* @method getFileArray
* @return {array} All the existing file.
*/
priv.getFileArray = function () {
return LocalOrCookieStorage.getItem(
priv.storage_file_array_name) || [];
};
/**
* Sets the file array list.
* @method setFileArray
* @param {array} file_array The array containing files.
*/
priv.setFileArray = function (file_array) {
return LocalOrCookieStorage.setItem(
priv.storage_file_array_name,
file_array);
};
/**
* Checks if the file already exists in the array.
* @method isFileIndexed
* @param {string} file_name The file we want to find.
* @return {boolean} true if found, else false
*/
priv.isFileIndexed = function (file_name) {
var i, l, array = priv.getFileArray();
for (i = 0, l = array.length; i < l; i+= 1) {
if (array[i].name === file_name){
return true;
}
}
return false;
};
/**
* Adds a file to the local file array.
* @method addFile
* @param {object} file The new file.
*/
priv.addFile = function (file) {
var file_array = priv.getFileArray();
file_array.push(file);
LocalOrCookieStorage.setItem(priv.storage_file_array_name,
file_array);
};
/**
* Removes a file from the local file array.
* @method removeFile
* @param {string} file_name The file to remove.
*/
priv.removeFile = function (file_name) {
var i, l, array = priv.getFileArray(), new_array = [];
for (i = 0, l = array.length; i < l; i+= 1) {
if (array[i].name !== file_name) {
new_array.push(array[i]);
}
}
LocalOrCookieStorage.setItem(priv.storage_file_array_name,
new_array);
};
/**
* Updates the storage.
* It will retreive all files from a storage. It is an asynchronous task
* so the update can be on going even if IndexedStorage has already
* returned the result.
* @method update
*/
priv.update = function (onResponse) {
// retreive list before, and then retreive all files
var getlist_onResponse = function (result) {
if (result.status === 'done') {
if (!priv.isAnIndexedStorage(that.getSecondStorage())) {
priv.addIndexedStorage(that.getSecondStorage());
}
priv.setFileArray(result.return_value);
}
},
newjob = {
storage: that.getSecondStorage(),
applicant: {ID:that.getApplicantID()},
method: 'getDocumentList',
max_tries: 3,
onResponse: getlist_onResponse
};
that.addJob ( newjob );
};
/**
* Checks the availability of a user name set in the job.
* @method checkNameAvailability
*/
that.checkNameAvailability = function () {
var new_job = that.cloneJob();
priv.update();
new_job.storage = that.getSecondStorage();
new_job.onResponse = function (result) {
if (result.status === 'done') {
that.done(result.return_value);
} else {
that.fail(result.error);
}
};
that.addJob( new_job );
}; // end checkNameAvailability
/**
* Saves a document.
* @method saveDocument
*/
that.saveDocument = function () {
var new_job = that.cloneJob();
new_job.storage = that.getSecondStorage();
new_job.onResponse = function (result) {
if (result.status === 'done') {
if (!priv.isFileIndexed(that.getFileName())) {
priv.addFile({name:that.getFileName(),
last_modified:0,
creation_date:0});
}
priv.update();
that.done();
} else {
that.fail(result.error);
}
};
that.addJob ( new_job );
}; // end saveDocument
/**
* Loads a document.
* job.options.metadata_only {boolean}
* job.options.content_only {boolean}
* @method loadDocument
*/
that.loadDocument = function () {
var file_array, i, l, new_job,
loadOnResponse = function (result) {
if (result.status === 'done') {
// if (file_array[i].last_modified !==
// result.return_value.last_modified ||
// file_array[i].creation_date !==
// result.return_value.creation_date) {
// // the file in the index storage is different than
// // the one in the second storage. priv.update will
// // take care of refresh the indexed storage
// }
that.done(result.return_value);
} else {
that.fail(result.error);
}
},
secondLoadDocument = function () {
new_job = that.cloneJob();
new_job.storage = that.getSecondStorage();
new_job.onResponse = loadOnResponse;
that.addJob ( new_job );
},
settings = that.cloneOptionObject();
priv.update();
if (settings.metadata_only) {
setTimeout(function () {
if (priv.fileArrayExists()) {
file_array = priv.getFileArray();
for (i = 0, l = file_array.length; i < l; i+= 1) {
if (file_array[i].name === that.getFileName()) {
return that.done(file_array[i]);
}
}
} else {
secondLoadDocument();
}
},100);
} else {
secondLoadDocument();
}
}; // end loadDocument
/**
* Gets a document list.
* @method getDocumentList
*/
that.getDocumentList = function () {
var id;
priv.update();
id = setInterval(function () {
if (priv.fileArrayExists()) {
that.done(priv.getFileArray());
clearInterval(id);
}
},100);
}; // end getDocumentList
/**
* Removes a document.
* @method removeDocument
*/
that.removeDocument = function () {
var new_job = that.cloneJob();
new_job.storage = that.getSecondStorage();
new_job.onResponse = function (result) {
if (result.status === 'done') {
priv.removeFile(that.getFileName());
priv.update();
that.done();
} else {
that.fail(result.error);
}
};
that.addJob(new_job);
};
return that;
};
// end Indexed Storage
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Crypted Storage
/**
* JIO Crypted Storage. Type = 'crypted'.
* It will encrypt the file and its metadata stringified by JSON.
*/
newCryptedStorage = function ( spec, my ) {
// CryptedStorage constructor
var that = Jio.newBaseStorage( spec, my ), priv = {};
// TODO : IT IS NOT SECURE AT ALL!
// WE MUST REWORK CRYPTED STORAGE!
priv.encrypt_param_object = {
"iv":"kaprWwY/Ucr7pumXoTHbpA",
"v":1,
"iter":1000,
"ks":256,
"ts":128,
"mode":"ccm",
"adata":"",
"cipher":"aes",
"salt":"K4bmZG9d704"
};
priv.decrypt_param_object = {
"iv":"kaprWwY/Ucr7pumXoTHbpA",
"ks":256,
"ts":128,
"salt":"K4bmZG9d704"
};
priv.encrypt = function (data,callback,index) {
// end with a callback in order to improve encrypt to an
// asynchronous encryption.
var tmp = sjcl.encrypt (that.getStorageUserName()+':'+
that.getStoragePassword(), data,
priv.encrypt_param_object);
callback(JSON.parse(tmp).ct,index);
};
priv.decrypt = function (data,callback,index,key) {
var tmp, param = $.extend(true,{},priv.decrypt_param_object);
param.ct = data || '';
param = JSON.stringify (param);
try {
tmp = sjcl.decrypt (that.getStorageUserName()+':'+
that.getStoragePassword(),
param);
} catch (e) {
callback({status:0,statusText:'Decrypt Fail',
message:'Unable to decrypt.'},index,key);
return;
}
callback(tmp,index,key);
};
/**
* Checks the availability of a user name set in the job.
* @method checkNameAvailability
*/
that.checkNameAvailability = function () {
var new_job = that.cloneJob();
new_job.storage = that.getSecondStorage();
new_job.onResponse = function (result) {
if (result.status === 'done') {
that.done(result.return_value);
} else {
that.fail(result.error);
}
};
that.addJob( new_job );
}; // end checkNameAvailability
/**
* Saves a document.
* @method saveDocument
*/
that.saveDocument = function () {
var new_job, new_file_name, newfilecontent,
_1 = function () {
priv.encrypt(that.getFileName(),function(res) {
new_file_name = res;
_2();
});
},
_2 = function () {
priv.encrypt(that.getFileContent(),function(res) {
newfilecontent = res;
_3();
});
},
_3 = function () {
new_job = that.cloneJob();
new_job.name = new_file_name;
new_job.content = newfilecontent;
new_job.storage = that.getSecondStorage();
new_job.onResponse = function (result) {
if (result.status === 'done') {
that.done();
} else {
that.fail(result.error);
}
};
that.addJob ( new_job );
};
_1();
}; // end saveDocument
/**
* Loads a document.
* job.options.metadata_only {boolean}
* job.options.content_only {boolean}
* @method loadDocument
*/
that.loadDocument = function () {
var new_job, new_file_name, option = that.cloneOptionObject(),
_1 = function () {
priv.encrypt(that.getFileName(),function(res) {
new_file_name = res;
_2();
});
},
_2 = function () {
new_job = that.cloneJob();
new_job.name = new_file_name;
new_job.storage = that.getSecondStorage();
new_job.onResponse = loadOnResponse;
that.addJob ( new_job );
},
loadOnResponse = function (result) {
if (result.status === 'done') {
result.return_value.name = that.getFileName();
if (option.metadata_only) {
that.done(result.return_value);
} else {
priv.decrypt (result.return_value.content,function(res){
if (typeof res === 'object') {
that.fail({status:0,statusText:'Decrypt Fail',
message:'Unable to decrypt'});
} else {
result.return_value.content = res;
// content only: the second storage should
// manage content_only option, so it is not
// necessary to manage it.
that.done(result.return_value);
}
});
}
} else {
// NOTE : we can re create an error object instead of
// keep the old ex:status=404,message="document 1y59gyl8g
// not found in localStorage"...
that.fail(result.error);
}
};
_1();
}; // end loadDocument
/**
* Gets a document list.
* @method getDocumentList
*/
that.getDocumentList = function () {
var new_job, i, l, cpt = 0, array, ok = true,
_1 = function () {
new_job = that.cloneJob();
new_job.storage = that.getSecondStorage();
new_job.onResponse = getListOnResponse;
that.addJob ( new_job );
},
getListOnResponse = function (result) {
if (result.status === 'done') {
array = result.return_value;
for (i = 0, l = array.length; i < l; i+= 1) {
// cpt--;
priv.decrypt (array[i].name,
lastOnResponse,i,'name');
// priv.decrypt (array[i].content,
// lastOnResponse,i,'content');
}
} else {
that.fail(result.error);
}
},
lastOnResponse = function (res,index,key) {
var tmp;
cpt++;
if (typeof res === 'object') {
if (ok) {
that.fail({status:0,statusText:'Decrypt Fail',
message:'Unable to decrypt.'});
}
ok = false;
return;
}
array[index][key] = res;
if (cpt === l && ok) {
// this is the last callback
that.done(array);
}
};
_1();
}; // end getDocumentList
/**
* Removes a document.
* @method removeDocument
*/
that.removeDocument = function () {
var new_job, new_file_name,
_1 = function () {
priv.encrypt(that.getFileName(),function(res) {
new_file_name = res;
_2();
});
},
_2 = function () {
new_job = that.cloneJob();
new_job.name = new_file_name;
new_job.storage = that.getSecondStorage();
new_job.onResponse = removeOnResponse;
that.addJob(new_job);
},
removeOnResponse = function (result) {
if (result.status === 'done') {
that.done();
} else {
that.fail(result.error);
}
};
_1();
};
return that;
};
// end Crypted Storage
////////////////////////////////////////////////////////////////////////////
// add key to storageObjectType of global jio
Jio.addStorageType('local', newLocalStorage);
Jio.addStorageType('dav', newDAVStorage);
Jio.addStorageType('replicate', newReplicateStorage);
Jio.addStorageType('indexed', newIndexedStorage);
Jio.addStorageType('crypted', newCryptedStorage);
};
if (window.requirejs) {
define ('JIOStorages',
['LocalOrCookieStorage','jQuery','Base64','SJCL','JIO'],
jioStorageLoader);
} else {
jioStorageLoader ( LocalOrCookieStorage, jQuery, Base64, sjcl, JIO);
}
}());
var LocalOrCookieStorage =
(function () { var local_cookie_loader_function = function () {
// localorcookiestorage.js
// Creates an object that can store persistent information in localStorage.
// If it is not supported by the browser, it will store in cookies.
// Methods :
// - LocalOrCookieStorage.setItem('name',value);
// Sets an item with its value.
// - LocalOrCookieStorage.getItem('name');
// Returns a copy of the item.
// - LocalOrCookieStorage.deleteItem('name');
// Deletes an item forever.
// - LocalOrCookieStorage.getAll();
// Returns a new object containing all items and their values.
////////////////////////////////////////////////////////////////////////////
// cookies & localStorage
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];
}
}
};
var CookieStorage = function () {
};
CookieStorage.prototype = {
getItem: function (name) {
var cookies = document.cookie.split(';'), i;
for (i = 0; i < cookies.length; i += 1) {
var x = cookies[i].substr(0, cookies[i].indexOf('=')),
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 = {}, i,
cookies = document.cookie.split(':');
for (i = 0; i < cookies.length; i += 1) {
var x = cookies[i].substr(0, cookies[i].indexOf('=')),
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) {
return new BrowserStorage();
} else {
return new CookieStorage();
}
}
catch (e) {
return new CookieStorage();
}
// end cookies & localStorages
////////////////////////////////////////////////////////////////////////////
};
if (window.requirejs) {
define ('LocalOrCookieStorage',[], local_cookie_loader_function);
return undefined;
} else {
return local_cookie_loader_function ();
}
}());
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