Commit cf895161 authored by Xiaowu Zhang's avatar Xiaowu Zhang

add method for code more readable

parent f528b71f
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
*/ */
/*jslint indent: 2, maxlen: 80, nomen: true */ /*jslint indent: 2, maxlen: 80, nomen: true */
/*global define, module, require, indexedDB, jIO, RSVP */ /*global define, module, require, indexedDB, jIO, RSVP, Blob */
(function (dependencies, factory) { (function (dependencies, factory) {
"use strict"; "use strict";
...@@ -47,7 +47,6 @@ ...@@ -47,7 +47,6 @@
var Promise = RSVP.Promise, generateUuid = jIO.util.generateUuid; var Promise = RSVP.Promise, generateUuid = jIO.util.generateUuid;
// XXX doc string
function metadataObjectToString(value) { function metadataObjectToString(value) {
var i, l; var i, l;
if (Array.isArray(value)) { if (Array.isArray(value)) {
...@@ -80,305 +79,385 @@ ...@@ -80,305 +79,385 @@
this._database_name = "jio:" + description.database; this._database_name = "jio:" + description.database;
} }
// XXX doc string
function openRequestOnUpgradeNeeded(shared, event) { /**
if (shared.aborted) { return; } * creat 3 objectStores
shared.db = event.target.result; * @param {string} the name of the database
try { */
shared.store = shared.db.createObjectStore("metadata", { function openIndexedDB(db_name) {
"keyPath": "_id" var request;
//"autoIncrement": true function resolver(resolve, reject) {
}); // Open DB //
// `createObjectStore` can throw InvalidStateError - open_req.onerror request = indexedDB.open(db_name);
// and db.onerror won't be called. request.onerror = reject;
shared.store.createIndex("_id", "_id");
// `store.createIndex` can throw an error // Create DB if necessary //
shared.db_created = true; request.onupgradeneeded = function (evt) {
shared.store.transaction.oncomplete = function () { var db = evt.target.result,
delete shared.store; store;
store = db.createObjectStore("metadata", {
"keyPath": "_id"
//"autoIncrement": true
});
store.createIndex("_id", "_id");
store = db.createObjectStore("attachment", {
"keyPath": "_id"
//"autoIncrement": true
});
store.createIndex("_id", "_id");
store = db.createObjectStore("blob", {
"keyPath": ["_id", "_attachment"]
//"autoIncrement": true
});
store.createIndex("_id_attachment", ["_id", "_attachment"]);
};
request.onsuccess = function () {
resolve(request.result);
}; };
} catch (e) {
shared.reject(e);
shared.db.close();
shared.aborted = true;
} }
return new RSVP.Promise(resolver);
} }
IndexedDBStorage.prototype.createDBIfNecessary = function () {
var shared = {}, open_req = indexedDB.open(this._database_name);
// No request.abort() is provided so we cannot cancel database creation
return new Promise(function (resolve, reject) {
shared.reject = reject;
open_req.onupgradeneeded =
openRequestOnUpgradeNeeded.bind(open_req, shared);
open_req.onerror = function () {
if (open_req.result) { open_req.result.close(); }
reject(open_req.error);
};
open_req.onsuccess = function () {
// *Called at t + 3*
open_req.result.close();
resolve(shared.db_created ? "created" : "no_content");
};
});
};
// XXX doc string
IndexedDBStorage.prototype.get = function (command, param) {
var shared = {"connector": this};
new Promise(function (resolve, reject) {
shared.reject = reject;
// Open DB //
var open_req = indexedDB.open(shared.connector._database_name);
open_req.onerror = function (event) {
reject(event.target.errorCode);
};
// Create DB if necessary // IndexedDBStorage.prototype.createDBIfNecessary = function () {
open_req.onupgradeneeded = return openIndexedDB(this._database_name);
openRequestOnUpgradeNeeded.bind(open_req, shared); };
open_req.onsuccess = function (event) { /**
if (shared.aborted) { return; } *put a data into a store object
try { *@param {ObjectStore} store The objectstore
shared.db = event.target.result; *@param {Object} metadata The data to put in
// Open transaction // *@return a new promise
shared.tx = shared.db.transaction("metadata", "readonly"); */
shared.tx.onerror = function () { function putIndexedDB(store, metadata) {
reject(shared.tx.error); function resolver(resolve, reject) {
shared.db.close(); var request = store.put(metadata);
}; request.onerror = reject;
shared.onCancel = function () { request.onsuccess = function () {
shared.tx.abort(); resolve(metadata);
shared.db.close(); };
}; }
return new RSVP.Promise(resolver);
}
// Get index //
shared.store = shared.tx.objectStore("metadata");
shared.index_request = shared.store.index("_id");
// Get metadata //
shared.index_request.get(param._id).onsuccess = function (event) {
if (shared.aborted) { return; }
if (event.target.result === undefined) {
reject({"status": 404});
return;
}
shared.final_result = {"data": event.target.result};
};
// Respond to jIO // /**
shared.tx.oncomplete = function () { * get a data from a store object
resolve(shared.final_result); * @param {ObjectStore} store The objectstore
shared.db.close(); * @param {String} id The data id
}; * return a new promise
} catch (e1) { */
reject(e1); function getIndexedDB(store, id) {
shared.db.close(); function resolver(resolve, reject) {
} var request = store.get(id);
request.onerror = reject;
request.onsuccess = function () {
resolve(request.result);
}; };
}).then(command.success, command.error, command.notify); }
}; return new RSVP.Promise(resolver);
}
// XXX doc string /**
IndexedDBStorage.prototype.post = function (command, metadata) { * delete a data of a store object
var shared = {"connector": this}; * @param {ObjectStore} store The objectstore
if (!metadata._id) { * @param {String} id The data id
metadata._id = generateUuid(); * @return a new promise
*
*/
function removeIndexedDB(store, id) {
function resolver(resolve, reject) {
var request = store["delete"](id);
request.onerror = reject;
request.onsuccess = resolve;
} }
new Promise(function (resolve, reject) { return new RSVP.Promise(resolver);
shared.reject = reject; }
// Open DB // /**
var open_req = indexedDB.open(shared.connector._database_name); * research an id in a store
open_req.onerror = function (event) { * @param {ObjectStore} store The objectstore
reject(event.target.errorCode); * @param {String} id The index id
* @param {var} researchID The data id
* return a new promise
*/
function researchIndexedDB(store, id, researchID) {
function resolver(resolve) {
var index = store.index(researchID);
index.get(id).onsuccess = function (evt) {
resolve({"result" : evt.target.result, "store": store});
}; };
}
return new RSVP.Promise(resolver);
}
// Create DB if necessary //
open_req.onupgradeneeded =
openRequestOnUpgradeNeeded.bind(open_req, shared);
open_req.onsuccess = function (event) { function promiseResearch(transaction, id, table, researchID) {
if (shared.aborted) { return; } var store = transaction.objectStore(table);
try { return researchIndexedDB(store, id, researchID);
shared.db = event.target.result; }
// Open transaction //
shared.tx = shared.db.transaction("metadata", "readwrite");
shared.tx.onerror = function () {
reject(shared.tx.error);
shared.db.close();
};
shared.onCancel = function () {
shared.tx.abort();
shared.db.close();
};
// Get index //
shared.store = shared.tx.objectStore("metadata");
shared.index_request = shared.store.index("_id");
// Get metadata //
shared.index_request.get(metadata._id).onsuccess = function (event) {
if (shared.aborted) { return; }
if (event.target.result !== undefined) {
shared.db.close();
reject({"status": 409, "reason": "document already exist"});
return;
}
delete metadata._attachments;
// Push metadata //
shared.store.put(metadata);
};
shared.tx.oncomplete = function () { /**
// Respond to jIO // * put or post a metadata into objectstore:metadata,attachment
shared.db.close(); * @param {function} open The function to open a basedata
resolve({"id": metadata._id}); * @param {function} research The function to reserach
}; * @param {function} ongoing The function to process
} catch (e1) { * @param {function} end The completed function
reject(e1); * @param {Object} command The JIO command
shared.db.close(); * @param {Object} metadata The data to put
} */
}; IndexedDBStorage.prototype._putOrPost =
}).then(command.success, command.error, command.notify); function (open, research, ongoing, end, command, metadata) {
}; var jio_storage = this,
transaction,
global_db,
result;
new RSVP.Queue()
.push(function () {
//open a database
return open(jio_storage._database_name);
})
.push(function (db) {
global_db = db;
transaction = db.transaction(["metadata",
"attachment"], "readwrite");
//research in metadata
return research(transaction, metadata._id, "metadata", "_id");
})
.push(function (researchResult) {
return ongoing(researchResult);
})
.push(function (ongoingResult) {
//research in attachment
result = ongoingResult;
return research(transaction, metadata._id, "attachment", "_id");
})
.push(function (researchResult) {
//create an id in attachment si necessary
if (researchResult.result === undefined) {
putIndexedDB(researchResult.store, {"_id": metadata._id});
} else {
return end(result);
}
})
.push(function () {
return end(result);
})
.push(undefined, function (error) {
// Check if transaction is ongoing, if so, abort it
if (transaction !== undefined) {
transaction.abort();
}
if (global_db !== undefined) {
global_db.close();
}
throw error;
})
.push(command.success, command.error, command.notify);
};
// XXX doc string
IndexedDBStorage.prototype.put = function (command, metadata) {
var shared = {"connector": this};
new Promise(function (resolve, reject) {
shared.reject = reject;
// Open DB //
var open_req = indexedDB.open(shared.connector._database_name);
open_req.onerror = function (event) {
reject(event.target.errorCode);
};
// Create DB if necessary //
open_req.onupgradeneeded =
openRequestOnUpgradeNeeded.bind(open_req, shared);
open_req.onsuccess = function (event) { /**
if (shared.aborted) { return; } * Retrieve data
try { *
shared.db = event.target.result; *@param {Object} command The JIO command
// Open transaction // *@param {Object} param The command parameters
shared.tx = shared.db.transaction("metadata", "readwrite"); */
shared.tx.onerror = function () { IndexedDBStorage.prototype.get = function (command, param) {
reject(shared.tx.error); var jio_storage = this,
shared.db.close(); transaction,
}; global_db,
shared.onCancel = function () { meta;
shared.tx.abort(); new RSVP.Queue()
shared.db.close(); .push(function () {
}; return openIndexedDB(jio_storage._database_name);
})
.push(function (db) {
global_db = db;
transaction = db.transaction(["metadata", "attachment"], "readwrite");
var store = transaction.objectStore("metadata");
return getIndexedDB(store, param._id);
})
.push(function (result) {
if (result) {
//get a part data from metadata
meta = result;
var store = transaction.objectStore("attachment");
return getIndexedDB(store, param._id);
}
throw ({"status": 404, "reason": "Not Found",
"message": "IndexeddbStorage, unable to get document."});
})
.push(function (result) {
//get the reste data from attachment
if (result._attachment) {
meta._attachment = result._attachment;
}
return ({"data": meta});
})
.push(undefined, function (error) {
// Check if transaction is ongoing, if so, abort it
if (transaction !== undefined) {
transaction.abort();
}
if (global_db !== undefined) {
global_db.close();
}
throw error;
})
.push(command.success, command.error, command.notify);
};
// Get index //
shared.store = shared.tx.objectStore("metadata");
shared.index = shared.store.index("_id");
// Get metadata //
shared.index.get(metadata._id).onsuccess = function (event) {
if (shared.aborted) { return; }
var i, l, key, array, data;
if (event.target.result !== undefined) {
shared.found = true;
data = event.target.result;
// Update metadata //
array = Object.keys(metadata);
for (i = 0, l = array.length; i < l; i += 1) {
key = array[i];
metadata[key] = metadataObjectToString(metadata[key]);
}
if (data._attachments) {
metadata._attachments = data._attachments;
}
} else {
delete metadata._attachments;
}
// Push metadata //
shared.store.put(metadata);
};
shared.tx.oncomplete = function () { /**
// Respond to jIO // * Remove a document
shared.db.close(); *
resolve({"status": shared.found ? 204 : 201}); * @param {Object} command The JIO command
}; * @param {Object} param The command parameters
} catch (e1) { */
reject(e1); IndexedDBStorage.prototype.remove = function (command, param) {
shared.db.close(); var jio_storage = this,
transaction,
global_db,
queue = new RSVP.Queue();
queue.push(function () {
return openIndexedDB(jio_storage._database_name);
})
.push(function (db) {
global_db = db;
transaction = db.transaction(["metadata",
"attachment", "blob"], "readwrite");
return promiseResearch(transaction, param._id, "metadata", "_id");
})
.push(function (resultResearch) {
if (resultResearch.result === undefined) {
throw ({"status": 404, "reason": "Not Found",
"message": "IndexeddbStorage, unable to get metadata."});
} }
}; //delete metadata
}).then(command.success, command.error, command.notify); return removeIndexedDB(resultResearch.store, param._id);
})
.push(function () {
var store = transaction.objectStore("attachment");
return getIndexedDB(store, param._id);
})
.push(function (result) {
if (result._attachment) {
var i, l, key, array, func, store;
array = Object.keys(result._attachment);
store = transaction.objectStore("blob");
for (i = 0, l = array.length; i < l; i += 1) {
key = array[i];
//delete blob
func = removeIndexedDB(store, [param._id, key]);
queue.push(func);
}
}
})
.push(function () {
var store = transaction.objectStore("attachment");
//delete attachment
return removeIndexedDB(store, param._id);
})
.push(function () {
return ({"status": 204});
})
.push(undefined, function (error) {
// Check if transaction is ongoing, if so, abort it
if (transaction !== undefined) {
transaction.abort();
}
if (global_db !== undefined) {
global_db.close();
}
throw error;
})
.push(command.success, command.error, command.notify);
}; };
// XXX doc string
IndexedDBStorage.prototype.remove = function (command, param) {
var shared = {"connector": this};
new Promise(function (resolve, reject) {
shared.reject = reject;
// Open DB //
var open_req = indexedDB.open(shared.connector._database_name);
open_req.onerror = function (event) {
reject(event.target.errorCode);
};
// Create DB if necessary // /**
open_req.onupgradeneeded = * Creates a new document if not already existes
openRequestOnUpgradeNeeded.bind(open_req, shared); * @param {Object} command The JIO command
* @param {Object} metadata The metadata to put
*/
IndexedDBStorage.prototype.post = function (command, metadata) {
var that = this;
if (!metadata._id) {
metadata._id = generateUuid();
}
function promiseOngoingPost(researchResult) {
if (researchResult.result === undefined) {
delete metadata._attachment;
return putIndexedDB(researchResult.store, metadata);
}
throw ({"status": 409, "reason": "Document exists"});
}
open_req.onsuccess = function (event) { function promiseEndPost(metadata) {
if (shared.aborted) { return; } return ({"id": metadata._id});
try { }
shared.db = event.target.result;
// Open transaction //
shared.tx = shared.db.transaction("metadata", "readwrite");
shared.tx.onerror = function () {
reject(shared.tx.error);
shared.db.close();
};
shared.onCancel = function () {
shared.tx.abort();
shared.db.close();
};
// Get index // that._putOrPost(openIndexedDB, promiseResearch,
shared.store = shared.tx.objectStore("metadata"); promiseOngoingPost, promiseEndPost,
shared.index = shared.store.index("_id"); command, metadata);
// Get metadata //
shared.index.get(param._id).onsuccess = function (event) {
if (shared.aborted) { return; }
if (event.target.result === undefined) {
shared.db.close();
reject({"status": 404});
return;
}
// Delete metadata //
shared.store["delete"](param._id);
// XXX Delete attachments //
};
shared.tx.oncomplete = function () { };
// Respond to jIO // /**
shared.db.close(); * Creates or updates a document
resolve(); * @param {Object} command The JIO command
}; * @param {Object} metadata The metadata to post
} catch (e1) { */
reject(e1); IndexedDBStorage.prototype.put = function (command, metadata) {
shared.db.close(); var that = this,
found;
function promiseOngoingPut(researchResult) {
var key;
for (key in metadata) {
if (metadata.hasOwnProperty(key)) {
metadata[key] = metadataObjectToString(metadata[key]);
} }
}; }
}).then(command.success, command.error, command.notify); delete metadata._attachment;
if (researchResult.result !== undefined) {
found = true;
}
return putIndexedDB(researchResult.store, metadata);
}
function promiseEndPut() {
return {"status": (found ? 204 : 201) };
}
that._putOrPost(openIndexedDB, promiseResearch,
promiseOngoingPut, promiseEndPut,
command, metadata);
}; };
// XXX doc string
IndexedDBStorage.prototype.getList = function (option) {
/**
* Retrieve a list of present document
*
* @method allDocs
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
* @param {Boolean} [options.include_docs=false]
* Also retrieve the actual document content.
*/
IndexedDBStorage.prototype.getListMetadata = function (option) {
var rows = [], onCancel, open_req = indexedDB.open(this._database_name); var rows = [], onCancel, open_req = indexedDB.open(this._database_name);
return new Promise(function (resolve, reject, notify) { return new Promise(function (resolve, reject, notify) {
open_req.onerror = function () { open_req.onerror = function () {
...@@ -386,30 +465,25 @@ ...@@ -386,30 +465,25 @@
reject(open_req.error); reject(open_req.error);
}; };
open_req.onsuccess = function () { open_req.onsuccess = function () {
var tx, store, index, date, index_req, db = open_req.result; var tx, date, j = 0, index_req, db = open_req.result;
try { try {
tx = db.transaction("metadata", "readonly"); tx = db.transaction(["metadata", "attachment"], "readonly");
onCancel = function () { onCancel = function () {
tx.abort(); tx.abort();
db.close(); db.close();
}; };
store = tx.objectStore("metadata"); index_req = tx.objectStore("metadata").index("_id").openCursor();
index = store.index("_id");
index_req = index.openCursor();
date = Date.now(); date = Date.now();
index_req.onsuccess = function (event) { index_req.onsuccess = function (event) {
var cursor = event.target.result, now, value, i, key; var cursor = event.target.result, now, value, i, key;
if (cursor) { if (cursor) {
// Called for each matching record. // Called for each matching record
// notification management // notification management
now = Date.now(); now = Date.now();
if (date <= now - 1000) { if (date <= now - 1000) {
notify({"loaded": rows.length}); notify({"loaded": rows.length});
date = now; date = now;
} }
// option.limit management // option.limit management
if (Array.isArray(option.limit)) { if (Array.isArray(option.limit)) {
if (option.limit.length > 1) { if (option.limit.length > 1) {
...@@ -433,7 +507,6 @@ ...@@ -433,7 +507,6 @@
option.limit[0] -= 1; option.limit[0] -= 1;
} }
} }
value = {}; value = {};
// option.select_list management // option.select_list management
if (option.select_list) { if (option.select_list) {
...@@ -442,7 +515,6 @@ ...@@ -442,7 +515,6 @@
value[key] = cursor.value[key]; value[key] = cursor.value[key];
} }
} }
// option.include_docs management // option.include_docs management
if (option.include_docs) { if (option.include_docs) {
rows.push({ rows.push({
...@@ -456,14 +528,37 @@ ...@@ -456,14 +528,37 @@
"value": value "value": value
}); });
} }
// continue to next iteration // continue to next iteration
cursor["continue"](); cursor["continue"]();
} else { } else {
notify({"loaded": rows.length}); index_req = tx.objectStore("attachment").
// No more matching records. index("_id").openCursor();
resolve({"data": {"rows": rows, "total_rows": rows.length}}); index_req.onsuccess = function (event) {
db.close(); //second table
cursor = event.target.result;
if (cursor) {
value = {};
if (cursor.value._attachment) {
if (option.select_list) {
for (i = 0; i < option.select_list.length; i += 1) {
key = option.select_list[i];
value[key] = cursor.value._attachment[key];
}
}
//add info of attachment into metadata
rows[j].value._attachment = value;
if (option.include_docs) {
rows[j].doc._attachment = cursor.value._attachment;
}
}
j += 1;
cursor["continue"]();
} else {
notify({"loaded": rows.length});
resolve({"data": {"rows": rows, "total_rows": rows.length}});
db.close();
}
};
} }
}; };
} catch (e) { } catch (e) {
...@@ -481,7 +576,7 @@ ...@@ -481,7 +576,7 @@
IndexedDBStorage.prototype.allDocs = function (command, param, option) { IndexedDBStorage.prototype.allDocs = function (command, param, option) {
/*jslint unparam: true */ /*jslint unparam: true */
this.createDBIfNecessary(). this.createDBIfNecessary().
then(this.getList.bind(this, option)). then(this.getListMetadata.bind(this, option)).
then(command.success, command.error, command.notify); then(command.success, command.error, command.notify);
}; };
...@@ -494,5 +589,4 @@ ...@@ -494,5 +589,4 @@
}; };
jIO.addStorage("indexeddb", IndexedDBStorage); jIO.addStorage("indexeddb", IndexedDBStorage);
}));
})); \ No newline at end of file
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