cordova-plugin-sqlite-2
Version:
Cordova SQLite Plugin 2
675 lines (573 loc) • 17.3 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.sqlitePlugin = factory());
}(this, (function () { 'use strict';
var test = function () {
// Don't get fooled by e.g. browserify environments.
return (typeof process !== 'undefined') && !process.browser;
};
var install = function (func) {
return function () {
process.nextTick(func);
};
};
var nextTick = {
test: test,
install: install
};
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
//based off rsvp https://github.com/tildeio/rsvp.js
//license https://github.com/tildeio/rsvp.js/blob/master/LICENSE
//https://github.com/tildeio/rsvp.js/blob/master/lib/rsvp/asap.js
var Mutation = commonjsGlobal.MutationObserver || commonjsGlobal.WebKitMutationObserver;
var test$1 = function () {
return Mutation;
};
var install$1 = function (handle) {
var called = 0;
var observer = new Mutation(handle);
var element = commonjsGlobal.document.createTextNode('');
observer.observe(element, {
characterData: true
});
return function () {
element.data = (called = ++called % 2);
};
};
var mutation = {
test: test$1,
install: install$1
};
var test$2 = function () {
if (commonjsGlobal.setImmediate) {
// we can only get here in IE10
// which doesn't handel postMessage well
return false;
}
return typeof commonjsGlobal.MessageChannel !== 'undefined';
};
var install$2 = function (func) {
var channel = new commonjsGlobal.MessageChannel();
channel.port1.onmessage = func;
return function () {
channel.port2.postMessage(0);
};
};
var messageChannel = {
test: test$2,
install: install$2
};
var test$3 = function () {
return 'document' in commonjsGlobal && 'onreadystatechange' in commonjsGlobal.document.createElement('script');
};
var install$3 = function (handle) {
return function () {
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
var scriptEl = commonjsGlobal.document.createElement('script');
scriptEl.onreadystatechange = function () {
handle();
scriptEl.onreadystatechange = null;
scriptEl.parentNode.removeChild(scriptEl);
scriptEl = null;
};
commonjsGlobal.document.documentElement.appendChild(scriptEl);
return handle;
};
};
var stateChange = {
test: test$3,
install: install$3
};
var test$4 = function () {
return true;
};
var install$4 = function (t) {
return function () {
setTimeout(t, 0);
};
};
var timeout = {
test: test$4,
install: install$4
};
var types = [
nextTick,
mutation,
messageChannel,
stateChange,
timeout
];
var draining;
var currentQueue;
var queueIndex = -1;
var queue = [];
var scheduled = false;
function cleanUpNextTick() {
if (!draining || !currentQueue) {
return;
}
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (queue.length) {
nextTick$1();
}
}
//named nextTick for less confusing stack traces
function nextTick$1() {
if (draining) {
return;
}
scheduled = false;
draining = true;
var len = queue.length;
var timeout$$1 = setTimeout(cleanUpNextTick);
while (len) {
currentQueue = queue;
queue = [];
while (currentQueue && ++queueIndex < len) {
currentQueue[queueIndex].run();
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
queueIndex = -1;
draining = false;
clearTimeout(timeout$$1);
}
var scheduleDrain;
var i = -1;
var len = types.length;
while (++i < len) {
if (types[i] && types[i].test && types[i].test()) {
scheduleDrain = types[i].install(nextTick$1);
break;
}
}
// v8 likes predictible objects
function Item(fun, array) {
this.fun = fun;
this.array = array;
}
Item.prototype.run = function () {
var fun = this.fun;
var array = this.array;
switch (array.length) {
case 0:
return fun();
case 1:
return fun(array[0]);
case 2:
return fun(array[0], array[1]);
case 3:
return fun(array[0], array[1], array[2]);
default:
return fun.apply(null, array);
}
};
var lib = immediate;
function immediate(task) {
var args = new Array(arguments.length - 1);
if (arguments.length > 1) {
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
}
queue.push(new Item(task, args));
if (!scheduled && !draining) {
scheduled = true;
scheduleDrain();
}
}
var argsarray = argsArray;
function argsArray(fun) {
return function () {
var len = arguments.length;
if (len) {
var args = [];
var i = -1;
while (++i < len) {
args[i] = arguments[i];
}
return fun.call(this, args);
} else {
return fun.call(this, []);
}
};
}
var noopFn = function() {};
// Simple FIFO queue implementation to avoid having to do shift()
// on an array, which is slow.
function Queue() {
this.length = 0;
}
Queue.prototype.push = function (item) {
var node = {item: item};
if (this.last) {
this.last = this.last.next = node;
} else {
this.last = this.first = node;
}
this.length++;
};
Queue.prototype.shift = function () {
var node = this.first;
if (node) {
this.first = node.next;
if (!(--this.length)) {
this.last = undefined;
}
return node.item;
}
};
Queue.prototype.slice = function (start, end) {
start = typeof start === 'undefined' ? 0 : start;
end = typeof end === 'undefined' ? Infinity : end;
var output = [];
var i = 0;
for (var node = this.first; node; node = node.next) {
if (--end < 0) {
break;
} else if (++i > start) {
output.push(node.item);
}
}
return output;
};
var tinyQueue = Queue;
function WebSQLRows(array) {
this._array = array;
this.length = array.length;
}
WebSQLRows.prototype.item = function (i) {
return this._array[i];
};
function WebSQLResultSet(insertId, rowsAffected, rows) {
this.insertId = insertId;
this.rowsAffected = rowsAffected;
this.rows = new WebSQLRows(rows);
}
var WebSQLResultSet_1 = WebSQLResultSet;
function errorUnhandled() {
return true; // a non-truthy return indicates error was handled
}
// WebSQL has some bizarre behavior regarding insertId/rowsAffected. To try
// to match the observed behavior of Chrome/Safari as much as possible, we
// sniff the SQL message to try to massage the returned insertId/rowsAffected.
// This helps us pass the tests, although it's error-prone and should
// probably be revised.
function massageSQLResult(sql, insertId, rowsAffected, rows) {
if (/^\s*UPDATE\b/i.test(sql)) {
// insertId is always undefined for "UPDATE" statements
insertId = void 0;
} else if (/^\s*CREATE\s+TABLE\b/i.test(sql)) {
// WebSQL always returns an insertId of 0 for "CREATE TABLE" statements
insertId = 0;
rowsAffected = 0;
} else if (/^\s*DROP\s+TABLE\b/i.test(sql)) {
// WebSQL always returns insertId=undefined and rowsAffected=0
// for "DROP TABLE" statements. Go figure.
insertId = void 0;
rowsAffected = 0;
} else if (!/^\s*INSERT\b/i.test(sql)) {
// for all non-inserts (deletes, etc.) insertId is always undefined
// ¯\_(ツ)_/¯
insertId = void 0;
}
return new WebSQLResultSet_1(insertId, rowsAffected, rows);
}
function SQLTask(sql, args, sqlCallback, sqlErrorCallback) {
this.sql = sql;
this.args = args;
this.sqlCallback = sqlCallback;
this.sqlErrorCallback = sqlErrorCallback;
}
function runBatch(self, batch) {
function onDone() {
self._running = false;
runAllSql(self);
}
var readOnly = self._websqlDatabase._currentTask.readOnly;
self._websqlDatabase._db.exec(batch, readOnly, function (err, results) {
/* istanbul ignore next */
if (err) {
self._error = err;
return onDone();
}
for (var i = 0; i < results.length; i++) {
var res = results[i];
var batchTask = batch[i];
if (res.error) {
if (batchTask.sqlErrorCallback(self, res.error)) {
// user didn't handle the error
self._error = res.error;
return onDone();
}
} else {
batchTask.sqlCallback(self, massageSQLResult(
batch[i].sql, res.insertId, res.rowsAffected, res.rows));
}
}
onDone();
});
}
function runAllSql(self) {
if (self._running || self._complete) {
return;
}
if (self._error) {
self._complete = true;
return self._websqlDatabase._onTransactionComplete(self._error);
}
if (!self._sqlQueue.length) {
self._complete = true;
return self._websqlDatabase._onTransactionComplete();
}
self._running = true;
var batch = [];
var task;
while ((task = self._sqlQueue.shift())) {
batch.push(task);
}
runBatch(self, batch);
}
function executeSql(self, sql, args, sqlCallback, sqlErrorCallback) {
self._sqlQueue.push(new SQLTask(sql, args, sqlCallback, sqlErrorCallback));
if (self._runningTimeout) {
return;
}
self._runningTimeout = true;
lib(function () {
self._runningTimeout = false;
runAllSql(self);
});
}
function WebSQLTransaction(websqlDatabase) {
this._websqlDatabase = websqlDatabase;
this._error = null;
this._complete = false;
this._runningTimeout = false;
this._sqlQueue = new tinyQueue();
if (!websqlDatabase._currentTask.readOnly) {
// Since we serialize all access to the database, there is no need to
// run read-only tasks in a transaction. This is a perf boost.
this._sqlQueue.push(new SQLTask('BEGIN;', [], noopFn, noopFn));
}
}
WebSQLTransaction.prototype.executeSql = function (sql, args, sqlCallback, sqlErrorCallback) {
args = Array.isArray(args) ? args : [];
sqlCallback = typeof sqlCallback === 'function' ? sqlCallback : noopFn;
sqlErrorCallback = typeof sqlErrorCallback === 'function' ? sqlErrorCallback : errorUnhandled;
executeSql(this, sql, args, sqlCallback, sqlErrorCallback);
};
WebSQLTransaction.prototype._checkDone = function () {
runAllSql(this);
};
var WebSQLTransaction_1 = WebSQLTransaction;
var ROLLBACK = [
{sql: 'ROLLBACK;', args: []}
];
var COMMIT = [
{sql: 'END;', args: []}
];
// v8 likes predictable objects
function TransactionTask(readOnly, txnCallback, errorCallback, successCallback) {
this.readOnly = readOnly;
this.txnCallback = txnCallback;
this.errorCallback = errorCallback;
this.successCallback = successCallback;
}
function WebSQLDatabase(dbVersion, db) {
this.version = dbVersion;
this._db = db;
this._txnQueue = new tinyQueue();
this._running = false;
this._currentTask = null;
}
WebSQLDatabase.prototype._onTransactionComplete = function(err) {
var self = this;
function done() {
if (err) {
self._currentTask.errorCallback(err);
} else {
self._currentTask.successCallback();
}
self._running = false;
self._currentTask = null;
self._runNextTransaction();
}
if (self._currentTask.readOnly) {
done(); // read-only doesn't require a transaction
} else if (err) {
self._db.exec(ROLLBACK, false, done);
} else {
self._db.exec(COMMIT, false, done);
}
};
WebSQLDatabase.prototype._runTransaction = function () {
var self = this;
var txn = new WebSQLTransaction_1(self);
lib(function () {
self._currentTask.txnCallback(txn);
txn._checkDone();
});
};
WebSQLDatabase.prototype._runNextTransaction = function() {
if (this._running) {
return;
}
var task = this._txnQueue.shift();
if (!task) {
return;
}
this._currentTask = task;
this._running = true;
this._runTransaction();
};
WebSQLDatabase.prototype._createTransaction = function(
readOnly, txnCallback, errorCallback, successCallback) {
errorCallback = errorCallback || noopFn;
successCallback = successCallback || noopFn;
if (typeof txnCallback !== 'function') {
throw new Error('The callback provided as parameter 1 is not a function.');
}
this._txnQueue.push(new TransactionTask(readOnly, txnCallback, errorCallback, successCallback));
this._runNextTransaction();
};
WebSQLDatabase.prototype.transaction = function (txnCallback, errorCallback, successCallback) {
this._createTransaction(false, txnCallback, errorCallback, successCallback);
};
WebSQLDatabase.prototype.readTransaction = function (txnCallback, errorCallback, successCallback) {
this._createTransaction(true, txnCallback, errorCallback, successCallback);
};
var WebSQLDatabase_1 = WebSQLDatabase;
function customOpenDatabase(SQLiteDatabase) {
function createDb(dbName, dbVersion) {
var sqliteDatabase = new SQLiteDatabase(dbName);
return new WebSQLDatabase_1(dbVersion, sqliteDatabase);
}
function openDatabase(args) {
if (args.length < 4) {
throw new Error('Failed to execute \'openDatabase\': ' +
'4 arguments required, but only ' + args.length + ' present');
}
var dbName = args[0];
var dbVersion = args[1];
// db description and size are ignored
var callback = args[4] || noopFn;
var db = createDb(dbName, dbVersion);
lib(function () {
callback(db);
});
return db;
}
return argsarray(openDatabase);
}
var custom = customOpenDatabase;
var custom$1 = custom;
function map(arr, fun) {
var len = arr.length;
var res = Array(len);
for (var i = 0; i < len; i++) {
res[i] = fun(arr[i], i);
}
return res;
}
function zipObject(props, values) {
var res = {};
var len = Math.min(props.length, values.length);
for (var i = 0; i < len; i++) {
res[props[i]] = values[i];
}
return res;
}
function SQLiteResult(error, insertId, rowsAffected, rows) {
this.error = error;
this.insertId = insertId;
this.rowsAffected = rowsAffected;
this.rows = rows;
}
function massageError(err) {
return typeof err === 'string' ? new Error(err) : err;
}
function SQLiteDatabase(name) {
this._name = name;
}
function dearrayifyRow(res) {
// use a compressed array format to send minimal data between
// native and web layers
var rawError = res[0];
if (rawError) {
return new SQLiteResult(massageError(res[0]));
}
var insertId = res[1];
if (insertId === null) {
insertId = void 0; // per the spec, should be undefined
}
var rowsAffected = res[2];
var columns = res[3];
var rows = res[4];
var zippedRows = [];
for (var i = 0, len = rows.length; i < len; i++) {
zippedRows.push(zipObject(columns, rows[i]));
}
// v8 likes predictable objects
return new SQLiteResult(null, insertId, rowsAffected, zippedRows);
}
// send less data over the wire, use an array
function arrayifyQuery(query) {
return [query.sql, (query.args || [])];
}
SQLiteDatabase.prototype.exec = function exec(queries, readOnly, callback) {
function onSuccess(rawResults) {
if (typeof rawResults === 'string') {
rawResults = JSON.parse(rawResults);
}
var results = map(rawResults, dearrayifyRow);
callback(null, results);
}
function onError(err) {
callback(massageError(err));
}
cordova.exec(onSuccess, onError, 'SQLitePlugin', 'exec', [
this._name,
map(queries, arrayifyQuery),
readOnly
]);
};
var openDB = custom$1(SQLiteDatabase);
function SQLitePlugin() {
}
function openDatabase(name, version, description, size, callback) {
if (name && typeof name === 'object') {
// accept SQLite Plugin 1-style object here
callback = version;
size = name.size;
description = name.description;
version = name.version;
name = name.name;
}
if (!size) {
size = 1;
}
if (!description) {
description = name;
}
if (!version) {
version = '1.0';
}
if (typeof name === 'undefined') {
throw new Error('please be sure to call: openDatabase("myname.db")');
}
return openDB(name, version, description, size, callback);
}
SQLitePlugin.prototype.openDatabase = openDatabase;
var index = new SQLitePlugin();
return index;
})));