var FILE_SYSTEM_NAME = require('../constants.js').FILE_SYSTEM_NAME; var FILE_STORE_NAME = require('../constants.js').FILE_STORE_NAME; var WSQL_VERSION = require('../constants.js').WSQL_VERSION; var WSQL_SIZE = require('../constants.js').WSQL_SIZE; var WSQL_DESC = require('../constants.js').WSQL_DESC; var Errors = require('../errors.js'); var base64ArrayBuffer = require('base64-arraybuffer'); function WebSQLContext(db, isReadOnly) { var that = this; this.getTransaction = function(callback) { if(that.transaction) { callback(that.transaction); return; } // Either do readTransaction() (read-only) or transaction() (read/write) db[isReadOnly ? 'readTransaction' : 'transaction'](function(transaction) { that.transaction = transaction; callback(transaction); }); }; } WebSQLContext.prototype.clear = function(callback) { function onError(transaction, error) { callback(error); } function onSuccess() { callback(null); } this.getTransaction(function(transaction) { transaction.executeSql('DELETE FROM ' + FILE_STORE_NAME + ';', [], onSuccess, onError); }); }; function _get(getTransaction, key, callback) { function onSuccess(transaction, result) { // If the key isn't found, return null var value = result.rows.length === 0 ? null : result.rows.item(0).data; callback(null, value); } function onError(transaction, error) { callback(error); } getTransaction(function(transaction) { transaction.executeSql('SELECT data FROM ' + FILE_STORE_NAME + ' WHERE id = ? LIMIT 1;', [key], onSuccess, onError); }); } WebSQLContext.prototype.getObject = function(key, callback) { _get(this.getTransaction, key, function(err, result) { if(err) { return callback(err); } try { if(result) { result = JSON.parse(result); } } catch(e) { return callback(e); } callback(null, result); }); }; WebSQLContext.prototype.getBuffer = function(key, callback) { _get(this.getTransaction, key, function(err, result) { if(err) { return callback(err); } // Deal with zero-length ArrayBuffers, which will be encoded as '' if(result || result === '') { var arrayBuffer = base64ArrayBuffer.decode(result); result = Buffer.from(arrayBuffer); } callback(null, result); }); }; function _put(getTransaction, key, value, callback) { function onSuccess() { callback(null); } function onError(transaction, error) { callback(error); } getTransaction(function(transaction) { transaction.executeSql('INSERT OR REPLACE INTO ' + FILE_STORE_NAME + ' (id, data) VALUES (?, ?);', [key, value], onSuccess, onError); }); } WebSQLContext.prototype.putObject = function(key, value, callback) { var json = JSON.stringify(value); _put(this.getTransaction, key, json, callback); }; WebSQLContext.prototype.putBuffer = function(key, uint8BackedBuffer, callback) { var base64 = base64ArrayBuffer.encode(uint8BackedBuffer.buffer); _put(this.getTransaction, key, base64, callback); }; WebSQLContext.prototype.delete = function(key, callback) { function onSuccess() { callback(null); } function onError(transaction, error) { callback(error); } this.getTransaction(function(transaction) { transaction.executeSql('DELETE FROM ' + FILE_STORE_NAME + ' WHERE id = ?;', [key], onSuccess, onError); }); }; function WebSQL(name) { this.name = name || FILE_SYSTEM_NAME; this.db = null; } WebSQL.isSupported = function() { return !!global.openDatabase; }; WebSQL.prototype.open = function(callback) { var that = this; // Bail if we already have a db open if(that.db) { return callback(); } var db = global.openDatabase(that.name, WSQL_VERSION, WSQL_DESC, WSQL_SIZE); if(!db) { callback('[WebSQL] Unable to open database.'); return; } function onError(transaction, error) { if (error.code === 5) { callback(new Errors.EINVAL('WebSQL cannot be accessed. If private browsing is enabled, disable it.')); } callback(error); } function onSuccess() { that.db = db; callback(); } // Create the table and index we'll need to store the fs data. db.transaction(function(transaction) { function createIndex(transaction) { transaction.executeSql('CREATE INDEX IF NOT EXISTS idx_' + FILE_STORE_NAME + '_id' + ' on ' + FILE_STORE_NAME + ' (id);', [], onSuccess, onError); } transaction.executeSql('CREATE TABLE IF NOT EXISTS ' + FILE_STORE_NAME + ' (id unique, data TEXT);', [], createIndex, onError); }); }; WebSQL.prototype.getReadOnlyContext = function() { return new WebSQLContext(this.db, true); }; WebSQL.prototype.getReadWriteContext = function() { return new WebSQLContext(this.db, false); }; module.exports = WebSQL;