Commit 2ee114f1 authored by Tristan Cavelier's avatar Tristan Cavelier

localstorage updated to new jio design

parent 8bf30d16
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
*/ */
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global jIO, localStorage, setTimeout, complex_queries, define */ /*global jIO, localStorage, setTimeout, complex_queries, window, define,
exports, require */
/** /**
* JIO Local Storage. Type = 'local'. * JIO Local Storage. Type = 'local'.
...@@ -52,33 +53,17 @@ ...@@ -52,33 +53,17 @@
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
return define(dependencies, module); return define(dependencies, module);
} }
module(jIO, complex_queries); if (typeof exports === 'object') {
}(['jio', 'complex_queries'], function (jIO, complex_queries) { return module(exports, require('jio'), require('complex_queries'));
"use strict";
/**
* Returns 4 hexadecimal random characters.
*
* @return {String} The characters
*/
function S4() {
return ('0000' + Math.floor(
Math.random() * 0x10000 /* 65536 */
).toString(16)).slice(-4);
}
/**
* An Universal Unique ID generator
*
* @return {String} The new UUID.
*/
function generateUuid() {
return S4() + S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + S4() + S4();
} }
window.local_storage = {};
module(window.local_storage, jIO, complex_queries);
}([
'exports',
'jio',
'complex_queries'
], function (exports, jIO, complex_queries) {
"use strict";
/** /**
* Checks if an object has no enumerable keys * Checks if an object has no enumerable keys
...@@ -132,363 +117,379 @@ ...@@ -132,363 +117,379 @@
} }
}; };
jIO.addStorageType('local', function (spec, my) { function LocalStorage(spec) {
if (typeof spec.username !== 'string' && !spec.username) {
spec = spec || {}; throw new TypeError("LocalStorage 'username' must be a string " +
var that, priv; "which contains more than one character.");
that = my.basicStorage(spec, my); }
priv = {}; this._localpath = 'jio/localstorage/' + spec.username + '/' + (
spec.application_name === null || spec.application_name ===
// attributes undefined ? 'untitled' : spec.application_name.toString()
priv.username = spec.username || ''; );
priv.application_name = spec.application_name || 'untitled';
priv.localpath = 'jio/localstorage/' + priv.username + '/' +
priv.application_name;
switch (spec.mode) { switch (spec.mode) {
case "memory": case "memory":
priv.database = ram; this._database = ram;
priv.storage = memorystorage; this._storage = memorystorage;
priv.mode = "memory"; this._mode = "memory";
break; break;
default: default:
priv.database = localStorage; this._database = localStorage;
priv.storage = localstorage; this._storage = localstorage;
priv.mode = "localStorage"; this._mode = "localStorage";
break; break;
} }
}
// ===================== overrides ====================== /**
that.specToStore = function () { * Create a document in local storage.
return { *
"application_name": priv.application_name, * @method post
"username": priv.username, * @param {Object} command The JIO command
"mode": priv.mode * @param {Object} metadata The metadata to store
}; * @param {Object} options The command options
}; */
LocalStorage.prototype.post = function (command, metadata) {
var doc, doc_id = metadata._id;
if (!doc_id) {
doc_id = jIO.util.generateUuid();
}
doc = this._storage.getItem(this._localpath + "/" + doc_id);
if (doc === null) {
// the document does not exist
doc = jIO.util.deepClone(metadata);
doc._id = doc_id;
delete doc._attachments;
this._storage.setItem(this._localpath + "/" + doc_id, doc);
command.success({"id": doc_id});
} else {
// the document already exists
command.error(
"conflict",
"document exists",
"Cannot create a new document"
);
}
};
that.validateState = function () { /**
if (typeof priv.username === "string" && priv.username !== '') { * Create or update a document in local storage.
return ''; *
} * @method put
return 'Need at least one parameter: "username".'; * @param {Object} command The JIO command
}; * @param {Object} metadata The metadata to store
* @param {Object} options The command options
*/
LocalStorage.prototype.put = function (command, metadata) {
var doc, tmp;
doc = this._storage.getItem(this._localpath + "/" + metadata._id);
if (doc === null) {
// the document does not exist
doc = jIO.util.deepClone(metadata);
delete doc._attachments;
} else {
// the document already exists
tmp = jIO.util.deepClone(metadata);
tmp._attachments = doc._attachments;
doc = tmp;
}
// write
this._storage.setItem(this._localpath + "/" + metadata._id, doc);
command.success();
};
// ==================== commands ==================== /**
/** * Add an attachment to a document
* Create a document in local storage. *
* @method post * @method putAttachment
* @param {object} command The JIO command * @param {Object} command The JIO command
*/ * @param {Object} param The given parameters
that.post = function (command) { * @param {Object} options The command options
setTimeout(function () { */
var doc, doc_id = command.getDocId(); LocalStorage.prototype.putAttachment = function (command, param) {
if (!doc_id) { var that = this, doc;
doc_id = generateUuid(); doc = this._storage.getItem(this._localpath + "/" + param._id);
} if (doc === null) {
doc = priv.storage.getItem(priv.localpath + "/" + doc_id); // the document does not exist
if (doc === null) { return command.error(
// the document does not exist "not_found",
doc = command.cloneDoc(); "missing",
doc._id = doc_id; "Impossible to add attachment"
delete doc._attachments; );
priv.storage.setItem(priv.localpath + "/" + doc_id, doc); }
that.success({
"ok": true,
"id": doc_id
});
} else {
// the document already exists
that.error({
"status": 409,
"statusText": "Conflicts",
"error": "conflicts",
"message": "Cannot create a new document",
"reason": "Document already exists"
});
}
});
};
/** // the document already exists
* Create or update a document in local storage. // download data
* @method put jIO.util.blobAsBinaryString(param._blob).then(function (data) {
* @param {object} command The JIO command doc._attachments = doc._attachments || {};
*/ doc._attachments[param._attachment] = {
that.put = function (command) { "content_type": param._blob.type,
setTimeout(function () { "digest": jIO.util.makeBinaryStringDigest(data),
var doc, tmp; "length": param._blob.size
doc = priv.storage.getItem(priv.localpath + "/" + command.getDocId()); };
if (doc === null) {
// the document does not exist
doc = command.cloneDoc();
delete doc._attachments;
} else {
// the document already exists
tmp = command.cloneDoc();
tmp._attachments = doc._attachments;
doc = tmp;
}
// write
priv.storage.setItem(priv.localpath + "/" + command.getDocId(), doc);
that.success({
"ok": true,
"id": command.getDocId()
});
});
};
/** that._storage.setItem(that._localpath + "/" + param._id + "/" +
* Add an attachment to a document param._attachment, data);
* @method putAttachment that._storage.setItem(that._localpath + "/" + param._id, doc);
* @param {object} command The JIO command command.success({"hash": doc._attachments[param._attachment].digest});
*/ }, function () {
that.putAttachment = function (command) { command.error(
setTimeout(function () { "request_timeout",
var doc; "blob error",
doc = priv.storage.getItem(priv.localpath + "/" + command.getDocId()); "Unable to download blob content"
if (doc === null) { );
// the document does not exist }, function () {
that.error({ command.notify(50); // XXX get percentage
"status": 404, });
"statusText": "Not Found", };
"error": "not_found",
"message": "Impossible to add attachment",
"reason": "Document not found"
});
return;
}
// the document already exists /**
doc._attachments = doc._attachments || {}; * Get a document
doc._attachments[command.getAttachmentId()] = { *
"content_type": command.getAttachmentMimeType(), * @method get
"digest": "md5-" + command.md5SumAttachmentData(), * @param {Object} command The JIO command
"length": command.getAttachmentLength() * @param {Object} param The given parameters
}; * @param {Object} options The command options
*/
LocalStorage.prototype.get = function (command, param) {
var doc = this._storage.getItem(
this._localpath + "/" + param._id
);
if (doc !== null) {
command.success({"data": doc});
} else {
command.error(
"not_found",
"missing",
"Cannot find document"
);
}
};
// upload data /**
priv.storage.setItem(priv.localpath + "/" + command.getDocId() + "/" + * Get an attachment
command.getAttachmentId(), *
command.getAttachmentData()); * @method getAttachment
// write document * @param {Object} command The JIO command
priv.storage.setItem(priv.localpath + "/" + command.getDocId(), doc); * @param {Object} param The given parameters
that.success({ * @param {Object} options The command options
"ok": true, */
"id": command.getDocId(), LocalStorage.prototype.getAttachment = function (command, param) {
"attachment": command.getAttachmentId() var doc;
}); doc = this._storage.getItem(this._localpath + "/" + param._id);
}); if (doc === null) {
}; return command.error(
"not_found",
"missing document",
"Cannot find document"
);
}
/** if (typeof doc._attachments !== 'object' ||
* Get a document typeof doc._attachments[param._attachment] !== 'object') {
* @method get return command.error(
* @param {object} command The JIO command "not_found",
*/ "missing attachment",
that.get = function (command) { "Cannot find attachment"
setTimeout(function () { );
var doc = priv.storage.getItem( }
priv.localpath + "/" + command.getDocId()
);
if (doc !== null) {
that.success(doc);
} else {
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "Cannot find the document",
"reason": "Document does not exist"
});
}
});
};
/** command.success({
* Get a attachment "data": this._storage.getItem(
* @method getAttachment this._localpath + "/" + param._id +
* @param {object} command The JIO command "/" + param._attachment
*/ ) || "",
that.getAttachment = function (command) { "content_type": doc._attachments[param._attachment].content_type || ""
setTimeout(function () { });
var doc = priv.storage.getItem( };
priv.localpath + "/" + command.getDocId() +
"/" + command.getAttachmentId()
);
if (doc !== null) {
that.success(doc);
} else {
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "Cannot find the attachment",
"reason": "Attachment does not exist"
});
}
});
};
/** /**
* Remove a document * Remove a document
* @method remove *
* @param {object} command The JIO command * @method remove
*/ * @param {Object} command The JIO command
that.remove = function (command) { * @param {Object} param The given parameters
setTimeout(function () { * @param {Object} options The command options
var doc, i, attachment_list; */
doc = priv.storage.getItem(priv.localpath + "/" + command.getDocId()); LocalStorage.prototype.remove = function (command, param) {
attachment_list = []; var doc, i, attachment_list;
if (doc !== null && typeof doc === "object") { doc = this._storage.getItem(this._localpath + "/" + param._id);
if (typeof doc._attachments === "object") { attachment_list = [];
// prepare list of attachments if (doc !== null && typeof doc === "object") {
for (i in doc._attachments) { if (typeof doc._attachments === "object") {
if (doc._attachments.hasOwnProperty(i)) { // prepare list of attachments
attachment_list.push(i); for (i in doc._attachments) {
} if (doc._attachments.hasOwnProperty(i)) {
} attachment_list.push(i);
} }
} else {
return that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "Document not found",
"reason": "missing"
});
} }
priv.storage.removeItem(priv.localpath + "/" + command.getDocId()); }
// delete all attachments } else {
for (i = 0; i < attachment_list.length; i += 1) { return command.error(
priv.storage.removeItem(priv.localpath + "/" + command.getDocId() + "not_found",
"/" + attachment_list[i]); "missing",
} "Document not found"
that.success({ );
"ok": true, }
"id": command.getDocId() this._storage.removeItem(this._localpath + "/" + param._id);
}); // delete all attachments
}); for (i = 0; i < attachment_list.length; i += 1) {
}; this._storage.removeItem(this._localpath + "/" + param._id +
"/" + attachment_list[i]);
}
command.success();
};
/** /**
* Remove an attachment * Remove an attachment
* @method removeAttachment *
* @param {object} command The JIO command * @method removeAttachment
*/ * @param {Object} command The JIO command
that.removeAttachment = function (command) { * @param {Object} param The given parameters
setTimeout(function () { * @param {Object} options The command options
var doc, error; */
error = function (word) { LocalStorage.prototype.removeAttachment = function (command, param) {
that.error({ var doc = this._storage.getItem(this._localpath + "/" + param._id);
"status": 404, if (typeof doc !== 'object') {
"statusText": "Not Found", return command.error(
"error": "not_found", "not_found",
"message": word + " not found", "missing document",
"reason": "missing" "Document not found"
}); );
}; }
doc = priv.storage.getItem(priv.localpath + "/" + command.getDocId()); if (typeof doc._attachments !== "object" ||
// remove attachment from document typeof doc._attachments[param._attachment] !== "object") {
if (doc !== null && typeof doc === "object" && return command.error(
typeof doc._attachments === "object") { "not_found",
if (typeof doc._attachments[command.getAttachmentId()] === "missing attachment",
"object") { "Attachment not found"
delete doc._attachments[command.getAttachmentId()]; );
if (objectIsEmpty(doc._attachments)) { }
delete doc._attachments;
}
priv.storage.setItem(priv.localpath + "/" + command.getDocId(),
doc);
priv.storage.removeItem(priv.localpath + "/" + command.getDocId() +
"/" + command.getAttachmentId());
that.success({
"ok": true,
"id": command.getDocId(),
"attachment": command.getAttachmentId()
});
} else {
error("Attachment");
}
} else {
error("Document");
}
});
};
/** delete doc._attachments[param._attachment];
* Get all filenames belonging to a user from the document index if (objectIsEmpty(doc._attachments)) {
* @method allDocs delete doc._attachments;
* @param {object} command The JIO command }
*/ this._storage.setItem(this._localpath + "/" + param._id, doc);
that.allDocs = function (command) { this._storage.removeItem(this._localpath + "/" + param._id +
var i, row, path_re, rows, document_list, option, document_object; "/" + param._attachment);
command.success();
};
/**
* Get all filenames belonging to a user from the document index
*
* @method allDocs
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.allDocs = function (command, param, options) {
var i, row, path_re, rows, document_list, document_object;
rows = [];
document_list = [];
path_re = new RegExp(
"^" + complex_queries.stringEscapeRegexpCharacters(this._localpath) +
"/[^/]+$"
);
if (options.query === undefined && options.sort_on === undefined &&
options.select_list === undefined &&
options.include_docs === undefined) {
rows = []; rows = [];
document_list = []; for (i in this._database) {
path_re = new RegExp( if (this._database.hasOwnProperty(i)) {
"^" + complex_queries.stringEscapeRegexpCharacters(priv.localpath) + // filter non-documents
"/[^/]+$" if (path_re.test(i)) {
); row = { value: {} };
option = command.cloneOption(); row.id = i.split('/').slice(-1)[0];
if (typeof complex_queries !== "object" || row.key = row.id;
(option.query === undefined && option.sort_on === undefined && if (options.include_docs) {
option.select_list === undefined && row.doc = JSON.parse(this._storage.getItem(i));
option.include_docs === undefined)) {
rows = [];
for (i in priv.database) {
if (priv.database.hasOwnProperty(i)) {
// filter non-documents
if (path_re.test(i)) {
row = { value: {} };
row.id = i.split('/').slice(-1)[0];
row.key = row.id;
if (command.getOption('include_docs')) {
row.doc = JSON.parse(priv.storage.getItem(i));
}
rows.push(row);
} }
rows.push(row);
} }
} }
that.success({"rows": rows, "total_rows": rows.length}); }
} else { command.success({"data": {"rows": rows, "total_rows": rows.length}});
// create complex query object from returned results } else {
for (i in priv.database) { // create complex query object from returned results
if (priv.database.hasOwnProperty(i)) { for (i in this._database) {
if (path_re.test(i)) { if (this._database.hasOwnProperty(i)) {
document_list.push(priv.storage.getItem(i)); if (path_re.test(i)) {
} document_list.push(this._storage.getItem(i));
} }
} }
option.select_list = option.select_list || []; }
option.select_list.push("_id"); options.select_list = options.select_list || [];
if (option.include_docs === true) { options.select_list.push("_id");
document_object = {}; if (options.include_docs === true) {
document_list.forEach(function (meta) { document_object = {};
document_object[meta._id] = meta; document_list.forEach(function (meta) {
}); document_object[meta._id] = meta;
}
complex_queries.QueryFactory.create(option.query || "").
exec(document_list, option);
document_list = document_list.map(function (value) {
var o = {
"id": value._id,
"key": value._id
};
if (option.include_docs === true) {
o.doc = document_object[value._id];
delete document_object[value._id];
}
delete value._id;
o.value = value;
return o;
}); });
that.success({"total_rows": document_list.length,
"rows": document_list});
} }
complex_queries.QueryFactory.create(options.query || "").
exec(document_list, options);
document_list = document_list.map(function (value) {
var o = {
"id": value._id,
"key": value._id
};
if (options.include_docs === true) {
o.doc = document_object[value._id];
delete document_object[value._id];
}
delete value._id;
o.value = value;
return o;
});
command.success({"data": {
"total_rows": document_list.length,
"rows": document_list
}});
}
};
jIO.addStorage('local', LocalStorage);
//////////////////////////////////////////////////////////////////////
// Tools
/**
* Tool to help users to create local storage description for JIO
*
* @param {String} username The username
* @param {String} [application_name] The application_name
* @return {Object} The storage description
*/
function createDescription(username, application_name) {
var description = {
"type": "local",
"username": username.toString()
}; };
if (application_name !== undefined) {
description.application_name = application_name.toString();
}
return description;
}
exports.createDescription = createDescription;
function clear() {
var k;
for (k in localStorage) {
if (localStorage.hasOwnProperty(k)) {
if (/^jio\/localstorage\//.test(k)) {
localStorage.removeItem(k);
}
}
}
}
exports.clear = clear;
exports.clearLocalStorage = clear;
function clearMemoryStorage() {
jIO.util.dictClear(ram);
}
exports.clearMemoryStorage = clearMemoryStorage;
return that;
});
})); }));
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