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

localstorage updated to new jio design

parent 8bf30d16
......@@ -5,7 +5,8 @@
*/
/*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'.
......@@ -52,33 +53,17 @@
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
module(jIO, complex_queries);
}(['jio', 'complex_queries'], function (jIO, 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();
if (typeof exports === 'object') {
return module(exports, require('jio'), require('complex_queries'));
}
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
......@@ -132,363 +117,379 @@
}
};
jIO.addStorageType('local', function (spec, my) {
spec = spec || {};
var that, priv;
that = my.basicStorage(spec, my);
priv = {};
// attributes
priv.username = spec.username || '';
priv.application_name = spec.application_name || 'untitled';
priv.localpath = 'jio/localstorage/' + priv.username + '/' +
priv.application_name;
function LocalStorage(spec) {
if (typeof spec.username !== 'string' && !spec.username) {
throw new TypeError("LocalStorage 'username' must be a string " +
"which contains more than one character.");
}
this._localpath = 'jio/localstorage/' + spec.username + '/' + (
spec.application_name === null || spec.application_name ===
undefined ? 'untitled' : spec.application_name.toString()
);
switch (spec.mode) {
case "memory":
priv.database = ram;
priv.storage = memorystorage;
priv.mode = "memory";
this._database = ram;
this._storage = memorystorage;
this._mode = "memory";
break;
default:
priv.database = localStorage;
priv.storage = localstorage;
priv.mode = "localStorage";
this._database = localStorage;
this._storage = localstorage;
this._mode = "localStorage";
break;
}
}
// ===================== overrides ======================
that.specToStore = function () {
return {
"application_name": priv.application_name,
"username": priv.username,
"mode": priv.mode
};
};
/**
* Create a document in local storage.
*
* @method post
* @param {Object} command The JIO command
* @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 !== '') {
return '';
}
return 'Need at least one parameter: "username".';
};
/**
* Create or update a document in local storage.
*
* @method put
* @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 ====================
/**
* Create a document in local storage.
* @method post
* @param {object} command The JIO command
*/
that.post = function (command) {
setTimeout(function () {
var doc, doc_id = command.getDocId();
if (!doc_id) {
doc_id = generateUuid();
}
doc = priv.storage.getItem(priv.localpath + "/" + doc_id);
if (doc === null) {
// the document does not exist
doc = command.cloneDoc();
doc._id = doc_id;
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"
});
}
});
};
/**
* Add an attachment to a document
*
* @method putAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.putAttachment = function (command, param) {
var that = this, doc;
doc = this._storage.getItem(this._localpath + "/" + param._id);
if (doc === null) {
// the document does not exist
return command.error(
"not_found",
"missing",
"Impossible to add attachment"
);
}
/**
* Create or update a document in local storage.
* @method put
* @param {object} command The JIO command
*/
that.put = function (command) {
setTimeout(function () {
var doc, tmp;
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()
});
});
};
// the document already exists
// download data
jIO.util.blobAsBinaryString(param._blob).then(function (data) {
doc._attachments = doc._attachments || {};
doc._attachments[param._attachment] = {
"content_type": param._blob.type,
"digest": jIO.util.makeBinaryStringDigest(data),
"length": param._blob.size
};
/**
* Add an attachment to a document
* @method putAttachment
* @param {object} command The JIO command
*/
that.putAttachment = function (command) {
setTimeout(function () {
var doc;
doc = priv.storage.getItem(priv.localpath + "/" + command.getDocId());
if (doc === null) {
// the document does not exist
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "Impossible to add attachment",
"reason": "Document not found"
});
return;
}
that._storage.setItem(that._localpath + "/" + param._id + "/" +
param._attachment, data);
that._storage.setItem(that._localpath + "/" + param._id, doc);
command.success({"hash": doc._attachments[param._attachment].digest});
}, function () {
command.error(
"request_timeout",
"blob error",
"Unable to download blob content"
);
}, function () {
command.notify(50); // XXX get percentage
});
};
// the document already exists
doc._attachments = doc._attachments || {};
doc._attachments[command.getAttachmentId()] = {
"content_type": command.getAttachmentMimeType(),
"digest": "md5-" + command.md5SumAttachmentData(),
"length": command.getAttachmentLength()
};
/**
* Get a document
*
* @method get
* @param {Object} command The JIO command
* @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() + "/" +
command.getAttachmentId(),
command.getAttachmentData());
// write document
priv.storage.setItem(priv.localpath + "/" + command.getDocId(), doc);
that.success({
"ok": true,
"id": command.getDocId(),
"attachment": command.getAttachmentId()
});
});
};
/**
* Get an attachment
*
* @method getAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.getAttachment = function (command, param) {
var doc;
doc = this._storage.getItem(this._localpath + "/" + param._id);
if (doc === null) {
return command.error(
"not_found",
"missing document",
"Cannot find document"
);
}
/**
* Get a document
* @method get
* @param {object} command The JIO command
*/
that.get = function (command) {
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"
});
}
});
};
if (typeof doc._attachments !== 'object' ||
typeof doc._attachments[param._attachment] !== 'object') {
return command.error(
"not_found",
"missing attachment",
"Cannot find attachment"
);
}
/**
* Get a attachment
* @method getAttachment
* @param {object} command The JIO command
*/
that.getAttachment = function (command) {
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"
});
}
});
};
command.success({
"data": this._storage.getItem(
this._localpath + "/" + param._id +
"/" + param._attachment
) || "",
"content_type": doc._attachments[param._attachment].content_type || ""
});
};
/**
* Remove a document
* @method remove
* @param {object} command The JIO command
*/
that.remove = function (command) {
setTimeout(function () {
var doc, i, attachment_list;
doc = priv.storage.getItem(priv.localpath + "/" + command.getDocId());
attachment_list = [];
if (doc !== null && typeof doc === "object") {
if (typeof doc._attachments === "object") {
// prepare list of attachments
for (i in doc._attachments) {
if (doc._attachments.hasOwnProperty(i)) {
attachment_list.push(i);
}
}
/**
* Remove a document
*
* @method remove
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.remove = function (command, param) {
var doc, i, attachment_list;
doc = this._storage.getItem(this._localpath + "/" + param._id);
attachment_list = [];
if (doc !== null && typeof doc === "object") {
if (typeof doc._attachments === "object") {
// prepare list of attachments
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
for (i = 0; i < attachment_list.length; i += 1) {
priv.storage.removeItem(priv.localpath + "/" + command.getDocId() +
"/" + attachment_list[i]);
}
that.success({
"ok": true,
"id": command.getDocId()
});
});
};
}
} else {
return command.error(
"not_found",
"missing",
"Document not found"
);
}
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
* @method removeAttachment
* @param {object} command The JIO command
*/
that.removeAttachment = function (command) {
setTimeout(function () {
var doc, error;
error = function (word) {
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": word + " not found",
"reason": "missing"
});
};
doc = priv.storage.getItem(priv.localpath + "/" + command.getDocId());
// remove attachment from document
if (doc !== null && typeof doc === "object" &&
typeof doc._attachments === "object") {
if (typeof doc._attachments[command.getAttachmentId()] ===
"object") {
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");
}
});
};
/**
* Remove an attachment
*
* @method removeAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
LocalStorage.prototype.removeAttachment = function (command, param) {
var doc = this._storage.getItem(this._localpath + "/" + param._id);
if (typeof doc !== 'object') {
return command.error(
"not_found",
"missing document",
"Document not found"
);
}
if (typeof doc._attachments !== "object" ||
typeof doc._attachments[param._attachment] !== "object") {
return command.error(
"not_found",
"missing attachment",
"Attachment not found"
);
}
/**
* Get all filenames belonging to a user from the document index
* @method allDocs
* @param {object} command The JIO command
*/
that.allDocs = function (command) {
var i, row, path_re, rows, document_list, option, document_object;
delete doc._attachments[param._attachment];
if (objectIsEmpty(doc._attachments)) {
delete doc._attachments;
}
this._storage.setItem(this._localpath + "/" + param._id, doc);
this._storage.removeItem(this._localpath + "/" + param._id +
"/" + 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 = [];
document_list = [];
path_re = new RegExp(
"^" + complex_queries.stringEscapeRegexpCharacters(priv.localpath) +
"/[^/]+$"
);
option = command.cloneOption();
if (typeof complex_queries !== "object" ||
(option.query === undefined && option.sort_on === undefined &&
option.select_list === undefined &&
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);
for (i in this._database) {
if (this._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 (options.include_docs) {
row.doc = JSON.parse(this._storage.getItem(i));
}
rows.push(row);
}
}
that.success({"rows": rows, "total_rows": rows.length});
} else {
// create complex query object from returned results
for (i in priv.database) {
if (priv.database.hasOwnProperty(i)) {
if (path_re.test(i)) {
document_list.push(priv.storage.getItem(i));
}
}
command.success({"data": {"rows": rows, "total_rows": rows.length}});
} else {
// create complex query object from returned results
for (i in this._database) {
if (this._database.hasOwnProperty(i)) {
if (path_re.test(i)) {
document_list.push(this._storage.getItem(i));
}
}
option.select_list = option.select_list || [];
option.select_list.push("_id");
if (option.include_docs === true) {
document_object = {};
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;
}
options.select_list = options.select_list || [];
options.select_list.push("_id");
if (options.include_docs === true) {
document_object = {};
document_list.forEach(function (meta) {
document_object[meta._id] = meta;
});
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