Commit cf895161 authored by Xiaowu Zhang's avatar Xiaowu Zhang

add method for code more readable

parent f528b71f
......@@ -28,7 +28,7 @@
*/
/*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) {
"use strict";
......@@ -47,7 +47,6 @@
var Promise = RSVP.Promise, generateUuid = jIO.util.generateUuid;
// XXX doc string
function metadataObjectToString(value) {
var i, l;
if (Array.isArray(value)) {
......@@ -80,305 +79,385 @@
this._database_name = "jio:" + description.database;
}
// XXX doc string
function openRequestOnUpgradeNeeded(shared, event) {
if (shared.aborted) { return; }
shared.db = event.target.result;
try {
shared.store = shared.db.createObjectStore("metadata", {
"keyPath": "_id"
//"autoIncrement": true
});
// `createObjectStore` can throw InvalidStateError - open_req.onerror
// and db.onerror won't be called.
shared.store.createIndex("_id", "_id");
// `store.createIndex` can throw an error
shared.db_created = true;
shared.store.transaction.oncomplete = function () {
delete shared.store;
/**
* creat 3 objectStores
* @param {string} the name of the database
*/
function openIndexedDB(db_name) {
var request;
function resolver(resolve, reject) {
// Open DB //
request = indexedDB.open(db_name);
request.onerror = reject;
// Create DB if necessary //
request.onupgradeneeded = function (evt) {
var db = evt.target.result,
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 //
open_req.onupgradeneeded =
openRequestOnUpgradeNeeded.bind(open_req, shared);
IndexedDBStorage.prototype.createDBIfNecessary = function () {
return openIndexedDB(this._database_name);
};
open_req.onsuccess = function (event) {
if (shared.aborted) { return; }
try {
shared.db = event.target.result;
// Open transaction //
shared.tx = shared.db.transaction("metadata", "readonly");
shared.tx.onerror = function () {
reject(shared.tx.error);
shared.db.close();
};
shared.onCancel = function () {
shared.tx.abort();
shared.db.close();
};
/**
*put a data into a store object
*@param {ObjectStore} store The objectstore
*@param {Object} metadata The data to put in
*@return a new promise
*/
function putIndexedDB(store, metadata) {
function resolver(resolve, reject) {
var request = store.put(metadata);
request.onerror = reject;
request.onsuccess = function () {
resolve(metadata);
};
}
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 () {
resolve(shared.final_result);
shared.db.close();
};
} catch (e1) {
reject(e1);
shared.db.close();
}
/**
* get a data from a store object
* @param {ObjectStore} store The objectstore
* @param {String} id The data id
* return a new promise
*/
function getIndexedDB(store, id) {
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) {
var shared = {"connector": this};
if (!metadata._id) {
metadata._id = generateUuid();
/**
* delete a data of a store object
* @param {ObjectStore} store The objectstore
* @param {String} id The data id
* @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) {
shared.reject = reject;
return new RSVP.Promise(resolver);
}
// Open DB //
var open_req = indexedDB.open(shared.connector._database_name);
open_req.onerror = function (event) {
reject(event.target.errorCode);
/**
* research an id in a store
* @param {ObjectStore} store The objectstore
* @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) {
if (shared.aborted) { return; }
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 //
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);
};
function promiseResearch(transaction, id, table, researchID) {
var store = transaction.objectStore(table);
return researchIndexedDB(store, id, researchID);
}
shared.tx.oncomplete = function () {
// Respond to jIO //
shared.db.close();
resolve({"id": metadata._id});
};
} catch (e1) {
reject(e1);
shared.db.close();
}
};
}).then(command.success, command.error, command.notify);
};
/**
* put or post a metadata into objectstore:metadata,attachment
* @param {function} open The function to open a basedata
* @param {function} research The function to reserach
* @param {function} ongoing The function to process
* @param {function} end The completed function
* @param {Object} command The JIO command
* @param {Object} metadata The data to put
*/
IndexedDBStorage.prototype._putOrPost =
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; }
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();
};
/**
* Retrieve data
*
*@param {Object} command The JIO command
*@param {Object} param The command parameters
*/
IndexedDBStorage.prototype.get = function (command, param) {
var jio_storage = this,
transaction,
global_db,
meta;
new RSVP.Queue()
.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 //
shared.db.close();
resolve({"status": shared.found ? 204 : 201});
};
} catch (e1) {
reject(e1);
shared.db.close();
/**
* Remove a document
*
* @param {Object} command The JIO command
* @param {Object} param The command parameters
*/
IndexedDBStorage.prototype.remove = function (command, param) {
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."});
}
};
}).then(command.success, command.error, command.notify);
//delete metadata
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 =
openRequestOnUpgradeNeeded.bind(open_req, shared);
/**
* Creates a new document if not already existes
* @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) {
if (shared.aborted) { return; }
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();
};
function promiseEndPost(metadata) {
return ({"id": metadata._id});
}
// Get index //
shared.store = shared.tx.objectStore("metadata");
shared.index = shared.store.index("_id");
// 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 //
};
that._putOrPost(openIndexedDB, promiseResearch,
promiseOngoingPost, promiseEndPost,
command, metadata);
shared.tx.oncomplete = function () {
// Respond to jIO //
shared.db.close();
resolve();
};
} catch (e1) {
reject(e1);
shared.db.close();
};
/**
* Creates or updates a document
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to post
*/
IndexedDBStorage.prototype.put = function (command, metadata) {
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);
return new Promise(function (resolve, reject, notify) {
open_req.onerror = function () {
......@@ -386,30 +465,25 @@
reject(open_req.error);
};
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 {
tx = db.transaction("metadata", "readonly");
tx = db.transaction(["metadata", "attachment"], "readonly");
onCancel = function () {
tx.abort();
db.close();
};
store = tx.objectStore("metadata");
index = store.index("_id");
index_req = index.openCursor();
index_req = tx.objectStore("metadata").index("_id").openCursor();
date = Date.now();
index_req.onsuccess = function (event) {
var cursor = event.target.result, now, value, i, key;
if (cursor) {
// Called for each matching record.
// Called for each matching record
// notification management
now = Date.now();
if (date <= now - 1000) {
notify({"loaded": rows.length});
date = now;
}
// option.limit management
if (Array.isArray(option.limit)) {
if (option.limit.length > 1) {
......@@ -433,7 +507,6 @@
option.limit[0] -= 1;
}
}
value = {};
// option.select_list management
if (option.select_list) {
......@@ -442,7 +515,6 @@
value[key] = cursor.value[key];
}
}
// option.include_docs management
if (option.include_docs) {
rows.push({
......@@ -456,14 +528,37 @@
"value": value
});
}
// continue to next iteration
cursor["continue"]();
} else {
notify({"loaded": rows.length});
// No more matching records.
resolve({"data": {"rows": rows, "total_rows": rows.length}});
db.close();
index_req = tx.objectStore("attachment").
index("_id").openCursor();
index_req.onsuccess = function (event) {
//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) {
......@@ -481,7 +576,7 @@
IndexedDBStorage.prototype.allDocs = function (command, param, option) {
/*jslint unparam: true */
this.createDBIfNecessary().
then(this.getList.bind(this, option)).
then(this.getListMetadata.bind(this, option)).
then(command.success, command.error, command.notify);
};
......@@ -494,5 +589,4 @@
};
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