2010-10-19 04:31:16 +08:00
|
|
|
/*
|
|
|
|
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
|
|
|
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
|
|
|
* Copyright (c) 2010, IBM Corporation
|
|
|
|
*/
|
2010-09-07 02:13:09 +08:00
|
|
|
|
2009-12-09 06:08:48 +08:00
|
|
|
/*
|
|
|
|
* This is purely for the Android 1.5/1.6 HTML 5 Storage
|
2010-09-07 02:13:09 +08:00
|
|
|
* I was hoping that Android 2.0 would deprecate this, but given the fact that
|
2009-12-09 06:08:48 +08:00
|
|
|
* most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required
|
|
|
|
*/
|
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
/**
|
|
|
|
* Storage object that is called by native code when performing queries.
|
|
|
|
* PRIVATE METHOD
|
|
|
|
*/
|
2010-09-07 02:13:09 +08:00
|
|
|
var DroidDB = function() {
|
2010-10-14 02:43:52 +08:00
|
|
|
this.queryQueue = {};
|
2010-09-07 02:13:09 +08:00
|
|
|
};
|
2009-12-09 06:08:48 +08:00
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
/**
|
|
|
|
* Callback from native code when query is complete.
|
|
|
|
* PRIVATE METHOD
|
|
|
|
*
|
2010-10-14 02:43:52 +08:00
|
|
|
* @param id Query id
|
2010-10-13 04:53:57 +08:00
|
|
|
*/
|
2010-11-07 04:01:22 +08:00
|
|
|
DroidDB.prototype.completeQuery = function(id, data) {
|
2010-10-14 02:43:52 +08:00
|
|
|
var query = this.queryQueue[id];
|
|
|
|
if (query) {
|
|
|
|
try {
|
|
|
|
delete this.queryQueue[id];
|
|
|
|
|
|
|
|
// Get transaction
|
|
|
|
var tx = query.tx;
|
|
|
|
|
|
|
|
// If transaction hasn't failed
|
|
|
|
// Note: We ignore all query results if previous query
|
|
|
|
// in the same transaction failed.
|
|
|
|
if (tx && tx.queryList[id]) {
|
|
|
|
|
|
|
|
// Save query results
|
|
|
|
var r = new DroidDB_Result();
|
2010-11-07 04:01:22 +08:00
|
|
|
r.rows.resultSet = data;
|
|
|
|
r.rows.length = data.length;
|
2010-10-14 02:43:52 +08:00
|
|
|
try {
|
|
|
|
if (typeof query.successCallback == 'function') {
|
|
|
|
query.successCallback(query.tx, r);
|
|
|
|
}
|
|
|
|
} catch (ex) {
|
|
|
|
console.log("executeSql error calling user success callback: "+ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.queryComplete(id);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.log("executeSql error: "+e);
|
|
|
|
}
|
2010-10-13 04:53:57 +08:00
|
|
|
}
|
2010-09-07 02:13:09 +08:00
|
|
|
};
|
2009-12-09 06:08:48 +08:00
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
/**
|
|
|
|
* Callback from native code when query fails
|
|
|
|
* PRIVATE METHOD
|
|
|
|
*
|
2010-10-14 02:43:52 +08:00
|
|
|
* @param reason Error message
|
|
|
|
* @param id Query id
|
2010-10-13 04:53:57 +08:00
|
|
|
*/
|
2010-10-14 02:43:52 +08:00
|
|
|
DroidDB.prototype.fail = function(reason, id) {
|
|
|
|
var query = this.queryQueue[id];
|
|
|
|
if (query) {
|
|
|
|
try {
|
|
|
|
delete this.queryQueue[id];
|
|
|
|
|
|
|
|
// Get transaction
|
|
|
|
var tx = query.tx;
|
|
|
|
|
|
|
|
// If transaction hasn't failed
|
|
|
|
// Note: We ignore all query results if previous query
|
|
|
|
// in the same transaction failed.
|
|
|
|
if (tx && tx.queryList[id]) {
|
|
|
|
tx.queryList = {};
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (typeof query.errorCallback == 'function') {
|
|
|
|
query.errorCallback(query.tx, reason);
|
|
|
|
}
|
|
|
|
} catch (ex) {
|
|
|
|
console.log("executeSql error calling user error callback: "+ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.queryFailed(id, reason);
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
console.log("executeSql error: "+e);
|
|
|
|
}
|
2010-10-13 04:53:57 +08:00
|
|
|
}
|
2010-09-07 02:13:09 +08:00
|
|
|
};
|
2009-12-16 02:38:38 +08:00
|
|
|
|
2010-09-07 02:13:09 +08:00
|
|
|
var DatabaseShell = function() {
|
|
|
|
};
|
2009-12-09 06:08:48 +08:00
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
/**
|
|
|
|
* Start a transaction.
|
2010-10-14 02:43:52 +08:00
|
|
|
* Does not support rollback in event of failure.
|
2010-10-13 04:53:57 +08:00
|
|
|
*
|
2010-10-14 02:43:52 +08:00
|
|
|
* @param process {Function} The transaction function
|
|
|
|
* @param successCallback {Function}
|
|
|
|
* @param errorCallback {Function}
|
2010-10-13 04:53:57 +08:00
|
|
|
*/
|
2010-11-09 00:41:18 +08:00
|
|
|
DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
|
2010-10-13 04:53:57 +08:00
|
|
|
var tx = new DroidDB_Tx();
|
2010-10-14 02:43:52 +08:00
|
|
|
tx.successCallback = successCallback;
|
|
|
|
tx.errorCallback = errorCallback;
|
|
|
|
try {
|
|
|
|
process(tx);
|
|
|
|
} catch (e) {
|
|
|
|
console.log("Transaction error: "+e);
|
|
|
|
if (tx.errorCallback) {
|
|
|
|
try {
|
|
|
|
tx.errorCallback(e);
|
|
|
|
} catch (ex) {
|
|
|
|
console.log("Transaction error calling user error callback: "+e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-09-07 02:13:09 +08:00
|
|
|
};
|
2009-12-09 06:08:48 +08:00
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
/**
|
|
|
|
* Transaction object
|
|
|
|
* PRIVATE METHOD
|
|
|
|
*/
|
|
|
|
var DroidDB_Tx = function() {
|
|
|
|
|
|
|
|
// Set the id of the transaction
|
|
|
|
this.id = PhoneGap.createUUID();
|
|
|
|
|
2010-10-14 02:43:52 +08:00
|
|
|
// Callbacks
|
|
|
|
this.successCallback = null;
|
|
|
|
this.errorCallback = null;
|
|
|
|
|
|
|
|
// Query list
|
|
|
|
this.queryList = {};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark query in transaction as complete.
|
|
|
|
* If all queries are complete, call the user's transaction success callback.
|
|
|
|
*
|
|
|
|
* @param id Query id
|
|
|
|
*/
|
|
|
|
DroidDB_Tx.prototype.queryComplete = function(id) {
|
|
|
|
delete this.queryList[id];
|
|
|
|
|
|
|
|
// If no more outstanding queries, then fire transaction success
|
|
|
|
if (this.successCallback) {
|
|
|
|
var count = 0;
|
|
|
|
for (var i in this.queryList) {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
if (count == 0) {
|
|
|
|
try {
|
|
|
|
this.successCallback();
|
|
|
|
} catch(e) {
|
|
|
|
console.log("Transaction error calling user success callback: " + e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark query in transaction as failed.
|
|
|
|
*
|
|
|
|
* @param id Query id
|
|
|
|
* @param reason Error message
|
|
|
|
*/
|
|
|
|
DroidDB_Tx.prototype.queryFailed = function(id, reason) {
|
|
|
|
|
|
|
|
// The sql queries in this transaction have already been run, since
|
|
|
|
// we really don't have a real transaction implemented in native code.
|
|
|
|
// However, the user callbacks for the remaining sql queries in transaction
|
|
|
|
// will not be called.
|
|
|
|
this.queryList = {};
|
|
|
|
|
|
|
|
if (this.errorCallback) {
|
|
|
|
try {
|
|
|
|
this.errorCallback(reason);
|
|
|
|
} catch(e) {
|
|
|
|
console.log("Transaction error calling user error callback: " + e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SQL query object
|
|
|
|
* PRIVATE METHOD
|
|
|
|
*
|
|
|
|
* @param tx The transaction object that this query belongs to
|
|
|
|
*/
|
|
|
|
var DroidDB_Query = function(tx) {
|
|
|
|
|
|
|
|
// Set the id of the query
|
|
|
|
this.id = PhoneGap.createUUID();
|
|
|
|
|
|
|
|
// Add this query to the queue
|
|
|
|
droiddb.queryQueue[this.id] = this;
|
2010-10-13 04:53:57 +08:00
|
|
|
|
|
|
|
// Init result
|
2010-09-07 02:13:09 +08:00
|
|
|
this.resultSet = [];
|
2010-10-14 02:43:52 +08:00
|
|
|
|
|
|
|
// Set transaction that this query belongs to
|
|
|
|
this.tx = tx;
|
|
|
|
|
|
|
|
// Add this query to transaction list
|
|
|
|
this.tx.queryList[this.id] = this;
|
|
|
|
|
|
|
|
// Callbacks
|
|
|
|
this.successCallback = null;
|
|
|
|
this.errorCallback = null;
|
|
|
|
|
|
|
|
}
|
2009-12-09 06:08:48 +08:00
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
/**
|
|
|
|
* Execute SQL statement
|
|
|
|
*
|
2010-10-14 02:43:52 +08:00
|
|
|
* @param sql SQL statement to execute
|
|
|
|
* @param params Statement parameters
|
|
|
|
* @param successCallback Success callback
|
|
|
|
* @param errorCallback Error callback
|
2010-10-13 04:53:57 +08:00
|
|
|
*/
|
2010-10-14 02:43:52 +08:00
|
|
|
DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) {
|
2010-10-13 04:53:57 +08:00
|
|
|
|
|
|
|
// Init params array
|
|
|
|
if (typeof params == 'undefined') {
|
|
|
|
params = [];
|
|
|
|
}
|
|
|
|
|
2010-10-14 02:43:52 +08:00
|
|
|
// Create query and add to queue
|
|
|
|
var query = new DroidDB_Query(this);
|
|
|
|
droiddb.queryQueue[query.id] = query;
|
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
// Save callbacks
|
2010-10-14 02:43:52 +08:00
|
|
|
query.successCallback = successCallback;
|
|
|
|
query.errorCallback = errorCallback;
|
2010-10-13 04:53:57 +08:00
|
|
|
|
|
|
|
// Call native code
|
2010-10-21 12:53:33 +08:00
|
|
|
PhoneGap.exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
|
2010-09-07 02:13:09 +08:00
|
|
|
};
|
2009-12-09 06:08:48 +08:00
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
/**
|
|
|
|
* SQL result set that is returned to user.
|
|
|
|
* PRIVATE METHOD
|
|
|
|
*/
|
|
|
|
DroidDB_Result = function() {
|
|
|
|
this.rows = new DroidDB_Rows();
|
2010-09-07 02:13:09 +08:00
|
|
|
};
|
2009-12-09 06:08:48 +08:00
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
/**
|
|
|
|
* SQL result set object
|
|
|
|
* PRIVATE METHOD
|
|
|
|
*/
|
|
|
|
DroidDB_Rows = function() {
|
|
|
|
this.resultSet = []; // results array
|
|
|
|
this.length = 0; // number of rows
|
2010-09-07 02:13:09 +08:00
|
|
|
};
|
2009-12-09 06:08:48 +08:00
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
/**
|
|
|
|
* Get item from SQL result set
|
|
|
|
*
|
|
|
|
* @param row The row number to return
|
|
|
|
* @return The row object
|
|
|
|
*/
|
|
|
|
DroidDB_Rows.prototype.item = function(row) {
|
|
|
|
return this.resultSet[row];
|
2010-09-07 02:13:09 +08:00
|
|
|
};
|
2009-12-09 06:08:48 +08:00
|
|
|
|
2010-10-13 04:53:57 +08:00
|
|
|
/**
|
|
|
|
* Open database
|
|
|
|
*
|
|
|
|
* @param name Database name
|
|
|
|
* @param version Database version
|
|
|
|
* @param display_name Database display name
|
|
|
|
* @param size Database size in bytes
|
|
|
|
* @return Database object
|
|
|
|
*/
|
|
|
|
DroidDB_openDatabase = function(name, version, display_name, size) {
|
2010-10-21 12:53:33 +08:00
|
|
|
PhoneGap.exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
|
2010-10-13 04:53:57 +08:00
|
|
|
var db = new DatabaseShell();
|
|
|
|
return db;
|
2010-09-07 02:13:09 +08:00
|
|
|
};
|
2009-12-09 07:58:07 +08:00
|
|
|
|
2010-11-10 16:32:19 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
|
|
|
|
* TODO: Do similar for sessionStorage.
|
|
|
|
*/
|
|
|
|
|
|
|
|
var CupcakeLocalStorage = function() {
|
|
|
|
try {
|
|
|
|
|
|
|
|
this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
|
|
|
|
var storage = {};
|
|
|
|
this.db.transaction(
|
|
|
|
function (transaction) {
|
|
|
|
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
|
|
|
transaction.executeSql('SELECT * FROM storage', [], function(tx, result) {
|
|
|
|
for(var i = 0; i < result.rows.length; i++) {
|
|
|
|
storage[result.rows.item(i)['id']] = result.rows.item(i)['body'];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
},
|
|
|
|
function (err) {
|
|
|
|
alert(err.message);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
this.setItem = function(key, val) {
|
|
|
|
console.log('set');
|
|
|
|
storage[key] = val;
|
|
|
|
|
|
|
|
this.db.transaction(
|
|
|
|
function (transaction) {
|
|
|
|
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
|
|
|
|
|
|
|
transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
this.getItem = function(key) {
|
|
|
|
return storage[key];
|
|
|
|
}
|
|
|
|
this.removeItem = function(key) {
|
|
|
|
delete storage[key];
|
|
|
|
this.db.transaction(
|
|
|
|
function (transaction) {
|
|
|
|
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
|
|
|
|
|
|
|
transaction.executeSql('DELETE FROM storage where id=?', [key]);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch(e) {
|
|
|
|
alert("Database error "+e+".");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
2009-12-09 06:08:48 +08:00
|
|
|
PhoneGap.addConstructor(function() {
|
2010-11-10 16:32:19 +08:00
|
|
|
if (typeof window.openDatabase == "undefined") {
|
2010-10-13 04:53:57 +08:00
|
|
|
navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
|
2010-09-07 02:13:09 +08:00
|
|
|
window.droiddb = new DroidDB();
|
|
|
|
}
|
2010-11-10 16:32:19 +08:00
|
|
|
|
|
|
|
if (typeof window.localStorage == "undefined") {
|
|
|
|
navigator.localStorage = window.localStorage = new CupcakeLocalStorage();
|
|
|
|
}
|
2009-12-09 06:08:48 +08:00
|
|
|
});
|
2010-11-10 16:32:19 +08:00
|
|
|
|