Adding float-no-zero branch hosted build

This commit is contained in:
ahocevar
2014-03-07 10:55:12 +01:00
parent 84cad42f6d
commit bd9092199b
1664 changed files with 731463 additions and 0 deletions
@@ -0,0 +1,214 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Wrapper for a IndexedDB cursor.
*
*/
goog.provide('goog.db.Cursor');
goog.require('goog.async.Deferred');
goog.require('goog.db.Error');
goog.require('goog.debug');
goog.require('goog.events.EventTarget');
/**
* Creates a new IDBCursor wrapper object. Should not be created directly,
* access cursor through object store.
* @see goog.db.ObjectStore#openCursor
*
* @constructor
* @extends {goog.events.EventTarget}
*/
goog.db.Cursor = function() {
goog.base(this);
};
goog.inherits(goog.db.Cursor, goog.events.EventTarget);
/**
* Underlying IndexedDB cursor object.
*
* @type {IDBCursor}
* @private
*/
goog.db.Cursor.prototype.cursor_ = null;
/**
* Advances the cursor to the next position along its direction. When new data
* is available, the NEW_DATA event will be fired. If the cursor has reached the
* end of the range it will fire the COMPLETE event. If opt_key is specified it
* will advance to the key it matches in its direction.
*
* This wraps the native #continue method on the underlying object.
*
* @param {IDBKeyType=} opt_key The optional key to advance to.
*/
goog.db.Cursor.prototype.next = function(opt_key) {
if (opt_key) {
this.cursor_['continue'](opt_key);
} else {
this.cursor_['continue']();
}
};
/**
* Updates the value at the current position of the cursor in the object store.
* If the cursor points to a value that has just been deleted, a new value is
* created.
*
* @param {*} value The value to be stored.
* @return {!goog.async.Deferred} The resulting deferred request.
*/
goog.db.Cursor.prototype.update = function(value) {
var msg = 'updating via cursor with value ';
var d = new goog.async.Deferred();
var request;
try {
request = this.cursor_.update(value);
} catch (err) {
msg += goog.debug.deepExpose(value);
d.errback(goog.db.Error.fromException(err, msg));
return d;
}
request.onsuccess = function(ev) {
d.callback();
};
request.onerror = function(ev) {
msg += goog.debug.deepExpose(value);
d.errback(goog.db.Error.fromRequest(ev.target, msg));
};
return d;
};
/**
* Deletes the value at the cursor's position, without changing the cursor's
* position. Once the value is deleted, the cursor's value is set to null.
*
* @return {!goog.async.Deferred} The resulting deferred request.
*/
goog.db.Cursor.prototype.remove = function() {
var msg = 'deleting via cursor';
var d = new goog.async.Deferred();
var request;
try {
request = this.cursor_['delete']();
} catch (err) {
d.errback(goog.db.Error.fromException(err, msg));
return d;
}
request.onsuccess = function(ev) {
d.callback();
};
request.onerror = function(ev) {
d.errback(goog.db.Error.fromRequest(ev.target, msg));
};
return d;
};
/**
* @return {*} The value for the value at the cursor's position. Undefined
* if no current value, or null if value has just been deleted.
*/
goog.db.Cursor.prototype.getValue = function() {
return this.cursor_['value'];
};
/**
* @return {IDBKeyType} The key for the value at the cursor's position. If
* the cursor is outside its range, this is undefined.
*/
goog.db.Cursor.prototype.getKey = function() {
return this.cursor_.key;
};
/**
* Opens a value cursor from IDBObjectStore or IDBIndex over the specified key
* range. Returns a cursor object which is able to iterate over the given range.
* @param {!(IDBObjectStore|IDBIndex)} source Data source to open cursor.
* @param {!goog.db.KeyRange=} opt_range The key range. If undefined iterates
* over the whole data source.
* @param {!goog.db.Cursor.Direction=} opt_direction The direction. If undefined
* moves in a forward direction with duplicates.
* @return {!goog.db.Cursor} The cursor.
* @throws {goog.db.Error} If there was a problem opening the cursor.
*/
goog.db.Cursor.openCursor = function(source, opt_range, opt_direction) {
var cursor = new goog.db.Cursor();
var request;
try {
var range = opt_range ? opt_range.range() : null;
if (opt_direction) {
request = source.openCursor(range, opt_direction);
} else {
request = source.openCursor(range);
}
} catch (ex) {
cursor.dispose();
throw goog.db.Error.fromException(ex, source.name);
}
request.onsuccess = function(e) {
cursor.cursor_ = e.target.result || null;
if (cursor.cursor_) {
cursor.dispatchEvent(goog.db.Cursor.EventType.NEW_DATA);
} else {
cursor.dispatchEvent(goog.db.Cursor.EventType.COMPLETE);
}
};
request.onerror = function(e) {
cursor.dispatchEvent(goog.db.Cursor.EventType.ERROR);
};
return cursor;
};
/**
* Possible cursor directions.
* @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBCursor
*
* @enum {string}
*/
goog.db.Cursor.Direction = {
NEXT: 'next',
NEXT_NO_DUPLICATE: 'nextunique',
PREV: 'prev',
PREV_NO_DUPLICATE: 'prevunique'
};
/**
* Event types that the cursor can dispatch. COMPLETE events are dispatched when
* a cursor is depleted of values, a NEW_DATA event if there is new data
* available, and ERROR if an error occurred.
*
* @enum {string}
*/
goog.db.Cursor.EventType = {
COMPLETE: 'c',
ERROR: 'e',
NEW_DATA: 'n'
};
@@ -0,0 +1,182 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Wrappers for the HTML5 IndexedDB. The wrappers export nearly
* the same interface as the standard API, but return goog.async.Deferred
* objects instead of request objects and use Closure events. The wrapper works
* and has been tested on Chrome version 22+. It may work on older Chrome
* versions, but they aren't explicitly supported.
*
* Example usage:
*
* <code>
* goog.db.openDatabase('mydb', 1, function(ev, db, tx) {
* db.createObjectStore('mystore');
* }).addCallback(function(db) {
* var putTx = db.createTransaction(
* [],
* goog.db.Transaction.TransactionMode.READ_WRITE);
* var store = putTx.objectStore('mystore');
* store.put('value', 'key');
* goog.listen(putTx, goog.db.Transaction.EventTypes.COMPLETE, function() {
* var getTx = db.createTransaction([]);
* var request = getTx.objectStore('mystore').get('key');
* request.addCallback(function(result) {
* ...
* });
* });
* </code>
*
*/
goog.provide('goog.db');
goog.require('goog.async.Deferred');
goog.require('goog.db.Error');
goog.require('goog.db.IndexedDb');
goog.require('goog.db.Transaction');
/**
* The IndexedDB factory object.
*
* @type {IDBFactory}
* @private
*/
goog.db.indexedDb_ = goog.global.indexedDB || goog.global.mozIndexedDB ||
goog.global.webkitIndexedDB || goog.global.moz_indexedDB;
/**
* A callback that's called if a blocked event is received. When a database is
* supposed to be deleted or upgraded (i.e. versionchange), and there are open
* connections to this database, a block event will be fired to prevent the
* operations from going through until all such open connections are closed.
* This callback can be used to notify users that they should close other tabs
* that have open connections, or to close the connections manually. Databases
* can also listen for the {@link goog.db.IndexedDb.EventType.VERSION_CHANGE}
* event to automatically close themselves when they're blocking such
* operations.
*
* This is passed a VersionChangeEvent that has the version of the database
* before it was deleted, and "null" as the new version.
*
* @typedef {function(!goog.db.IndexedDb.VersionChangeEvent)}
*/
goog.db.BlockedCallback;
/**
* A callback that's called when opening a database whose internal version is
* lower than the version passed to {@link goog.db.openDatabase}.
*
* This callback is passed three arguments: a VersionChangeEvent with both the
* old version and the new version of the database; the database that's being
* opened, for which you can create and delete object stores; and the version
* change transaction, with which you can abort the version change.
*
* Note that the transaction is not active, which means that it can't be used to
* make changes to the database. However, since there is a transaction running,
* you can't create another one via {@link goog.db.IndexedDb.createTransaction}.
* This means that it's not possible to manipulate the database other than
* creating or removing object stores in this callback.
*
* @typedef {function(!goog.db.IndexedDb.VersionChangeEvent,
* !goog.db.IndexedDb,
* !goog.db.Transaction)}
*/
goog.db.UpgradeNeededCallback;
/**
* Opens a database connection and wraps it.
*
* @param {string} name The name of the database to open.
* @param {number=} opt_version The expected version of the database. If this is
* larger than the actual version, opt_onUpgradeNeeded will be called
* (possibly after opt_onBlocked; see {@link goog.db.BlockedCallback}). If
* this is passed, opt_onUpgradeNeeded must be passed as well.
* @param {goog.db.UpgradeNeededCallback=} opt_onUpgradeNeeded Called if
* opt_version is greater than the old version of the database. If
* opt_version is passed, this must be passed as well.
* @param {goog.db.BlockedCallback=} opt_onBlocked Called if there are active
* connections to the database.
* @return {!goog.async.Deferred} The deferred database object.
*/
goog.db.openDatabase = function(name, opt_version, opt_onUpgradeNeeded,
opt_onBlocked) {
goog.asserts.assert(
goog.isDef(opt_version) == goog.isDef(opt_onUpgradeNeeded),
'opt_version must be passed to goog.db.openDatabase if and only if ' +
'opt_onUpgradeNeeded is also passed');
var d = new goog.async.Deferred();
var openRequest = opt_version ?
goog.db.indexedDb_.open(name, opt_version) :
goog.db.indexedDb_.open(name);
openRequest.onsuccess = function(ev) {
var db = new goog.db.IndexedDb(ev.target.result);
d.callback(db);
};
openRequest.onerror = function(ev) {
var msg = 'opening database ' + name;
d.errback(goog.db.Error.fromRequest(ev.target, msg));
};
openRequest.onupgradeneeded = function(ev) {
if (!opt_onUpgradeNeeded) return;
var db = new goog.db.IndexedDb(ev.target.result);
opt_onUpgradeNeeded(
new goog.db.IndexedDb.VersionChangeEvent(ev.oldVersion, ev.newVersion),
db,
new goog.db.Transaction(ev.target.transaction, db));
};
openRequest.onblocked = function(ev) {
if (opt_onBlocked) {
opt_onBlocked(new goog.db.IndexedDb.VersionChangeEvent(
ev.oldVersion, ev.newVersion));
}
};
return d;
};
/**
* Deletes a database once all open connections have been closed.
*
* @param {string} name The name of the database to delete.
* @param {goog.db.BlockedCallback=} opt_onBlocked Called if there are active
* connections to the database.
* @return {goog.async.Deferred} A deferred object that will fire once the
* database is deleted.
*/
goog.db.deleteDatabase = function(name, opt_onBlocked) {
var d = new goog.async.Deferred();
var deleteRequest = goog.db.indexedDb_.deleteDatabase(name);
deleteRequest.onsuccess = function(ev) {
d.callback();
};
deleteRequest.onerror = function(ev) {
var msg = 'deleting database ' + name;
d.errback(goog.db.Error.fromRequest(ev.target, msg));
};
deleteRequest.onblocked = function(ev) {
if (opt_onBlocked) {
opt_onBlocked(new goog.db.IndexedDb.VersionChangeEvent(
ev.oldVersion, ev.newVersion));
}
};
return d;
};
@@ -0,0 +1,359 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Error classes for the IndexedDB wrapper.
*
*/
goog.provide('goog.db.Error');
goog.provide('goog.db.Error.ErrorCode');
goog.provide('goog.db.Error.ErrorName');
goog.provide('goog.db.Error.VersionChangeBlockedError');
goog.require('goog.debug.Error');
/**
* A database error. Since the stack trace can be unhelpful in an asynchronous
* context, the error provides a message about where it was produced.
*
* @param {number|!DOMError} error The DOMError instance returned by the
* browser for Chrome22+, or an error code for previous versions.
* @param {string} context A description of where the error occured.
* @param {string=} opt_message Additional message.
* @constructor
* @extends {goog.debug.Error}
*/
goog.db.Error = function(error, context, opt_message) {
var errorCode = null;
var internalError = null;
if (goog.isNumber(error)) {
errorCode = error;
internalError = {name: goog.db.Error.getName(errorCode)};
} else {
internalError = error;
errorCode = goog.db.Error.getCode(error.name);
}
/**
* The code for this error.
*
* @type {number}
*/
this.code = errorCode;
/**
* The DOMException as returned by the browser.
*
* @type {!DOMError}
* @private
*/
this.error_ = /** @type {!DOMError} */ (internalError);
var msg = 'Error ' + context + ': ' + this.getName();
if (opt_message) {
msg += ', ' + opt_message;
}
goog.base(this, msg);
};
goog.inherits(goog.db.Error, goog.debug.Error);
/**
* @return {string} The name of the error.
*/
goog.db.Error.prototype.getName = function() {
return this.error_.name;
};
/**
* A specific kind of database error. If a Version Change is unable to proceed
* due to other open database connections, it will block and this error will be
* thrown.
*
* @constructor
* @extends {goog.debug.Error}
*/
goog.db.Error.VersionChangeBlockedError = function() {
goog.base(this, 'Version change blocked');
};
goog.inherits(goog.db.Error.VersionChangeBlockedError, goog.debug.Error);
/**
* Synthetic error codes for database errors, for use when IndexedDB
* support is not available. This numbering differs in practice
* from the browser implementations, but it is not meant to be reliable:
* this object merely ensures that goog.db.Error is loadable on platforms
* that do not support IndexedDB.
*
* @enum {number}
* @private
*/
goog.db.Error.DatabaseErrorCode_ = {
UNKNOWN_ERR: 1,
NON_TRANSIENT_ERR: 2,
NOT_FOUND_ERR: 3,
CONSTRAINT_ERR: 4,
DATA_ERR: 5,
NOT_ALLOWED_ERR: 6,
TRANSACTION_INACTIVE_ERR: 7,
ABORT_ERR: 8,
READ_ONLY_ERR: 9,
TRANSIENT_ERR: 11,
TIMEOUT_ERR: 10,
QUOTA_ERR: 11,
INVALID_ACCESS_ERR: 12,
INVALID_STATE_ERR: 13
};
/**
* Error codes for database errors.
* @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBDatabaseException
*
* @enum {number}
*/
goog.db.Error.ErrorCode = {
UNKNOWN_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).UNKNOWN_ERR,
NON_TRANSIENT_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).NON_TRANSIENT_ERR,
NOT_FOUND_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).NOT_FOUND_ERR,
CONSTRAINT_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).CONSTRAINT_ERR,
DATA_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).DATA_ERR,
NOT_ALLOWED_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).NOT_ALLOWED_ERR,
TRANSACTION_INACTIVE_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).TRANSACTION_INACTIVE_ERR,
ABORT_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).ABORT_ERR,
READ_ONLY_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).READ_ONLY_ERR,
TIMEOUT_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).TIMEOUT_ERR,
QUOTA_ERR: (goog.global.IDBDatabaseException ||
goog.global.webkitIDBDatabaseException ||
goog.db.Error.DatabaseErrorCode_).QUOTA_ERR,
INVALID_ACCESS_ERR: (goog.global.DOMException ||
goog.db.Error.DatabaseErrorCode_).INVALID_ACCESS_ERR,
INVALID_STATE_ERR: (goog.global.DOMException ||
goog.db.Error.DatabaseErrorCode_).INVALID_STATE_ERR
};
/**
* Translates an error code into a more useful message.
*
* @param {number} code Error code.
* @return {string} A debug message.
*/
goog.db.Error.getMessage = function(code) {
switch (code) {
case goog.db.Error.ErrorCode.UNKNOWN_ERR:
return 'Unknown error';
case goog.db.Error.ErrorCode.NON_TRANSIENT_ERR:
return 'Invalid operation';
case goog.db.Error.ErrorCode.NOT_FOUND_ERR:
return 'Required database object not found';
case goog.db.Error.ErrorCode.CONSTRAINT_ERR:
return 'Constraint unsatisfied';
case goog.db.Error.ErrorCode.DATA_ERR:
return 'Invalid data';
case goog.db.Error.ErrorCode.NOT_ALLOWED_ERR:
return 'Operation disallowed';
case goog.db.Error.ErrorCode.TRANSACTION_INACTIVE_ERR:
return 'Transaction not active';
case goog.db.Error.ErrorCode.ABORT_ERR:
return 'Request aborted';
case goog.db.Error.ErrorCode.READ_ONLY_ERR:
return 'Modifying operation not allowed in a read-only transaction';
case goog.db.Error.ErrorCode.TIMEOUT_ERR:
return 'Transaction timed out';
case goog.db.Error.ErrorCode.QUOTA_ERR:
return 'Database storage space quota exceeded';
case goog.db.Error.ErrorCode.INVALID_ACCESS_ERR:
return 'Invalid operation';
case goog.db.Error.ErrorCode.INVALID_STATE_ERR:
return 'Invalid state';
default:
return 'Unrecognized exception with code ' + code;
}
};
/**
* Names of all possible errors as returned from the browser.
* @see http://www.w3.org/TR/IndexedDB/#exceptions
* @enum {string}
*/
goog.db.Error.ErrorName = {
ABORT_ERR: 'AbortError',
CONSTRAINT_ERR: 'ConstraintError',
DATA_CLONE_ERR: 'DataCloneError',
DATA_ERR: 'DataError',
INVALID_ACCESS_ERR: 'InvalidAccessError',
INVALID_STATE_ERR: 'InvalidStateError',
NOT_FOUND_ERR: 'NotFoundError',
QUOTA_EXCEEDED_ERR: 'QuotaExceededError',
READ_ONLY_ERR: 'ReadOnlyError',
SYNTAX_ERROR: 'SyntaxError',
TIMEOUT_ERR: 'TimeoutError',
TRANSACTION_INACTIVE_ERR: 'TransactionInactiveError',
UNKNOWN_ERR: 'UnknownError',
VERSION_ERR: 'VersionError'
};
/**
* Translates an error name to an error code. This is purely kept for backwards
* compatibility with Chrome21.
*
* @param {string} name The name of the erorr.
* @return {number} The error code corresponding to the error.
*/
goog.db.Error.getCode = function(name) {
switch (name) {
case goog.db.Error.ErrorName.UNKNOWN_ERR:
return goog.db.Error.ErrorCode.UNKNOWN_ERR;
case goog.db.Error.ErrorName.NOT_FOUND_ERR:
return goog.db.Error.ErrorCode.NOT_FOUND_ERR;
case goog.db.Error.ErrorName.CONSTRAINT_ERR:
return goog.db.Error.ErrorCode.CONSTRAINT_ERR;
case goog.db.Error.ErrorName.DATA_ERR:
return goog.db.Error.ErrorCode.DATA_ERR;
case goog.db.Error.ErrorName.TRANSACTION_INACTIVE_ERR:
return goog.db.Error.ErrorCode.TRANSACTION_INACTIVE_ERR;
case goog.db.Error.ErrorName.ABORT_ERR:
return goog.db.Error.ErrorCode.ABORT_ERR;
case goog.db.Error.ErrorName.READ_ONLY_ERR:
return goog.db.Error.ErrorCode.READ_ONLY_ERR;
case goog.db.Error.ErrorName.TIMEOUT_ERR:
return goog.db.Error.ErrorCode.TIMEOUT_ERR;
case goog.db.Error.ErrorName.QUOTA_EXCEEDED_ERR:
return goog.db.Error.ErrorCode.QUOTA_ERR;
case goog.db.Error.ErrorName.INVALID_ACCESS_ERR:
return goog.db.Error.ErrorCode.INVALID_ACCESS_ERR;
case goog.db.Error.ErrorName.INVALID_STATE_ERR:
return goog.db.Error.ErrorCode.INVALID_STATE_ERR;
default:
return goog.db.Error.ErrorCode.UNKNOWN_ERR;
}
};
/**
* Converts an error code used by the old spec, to an error name used by the
* latest spec.
* @see http://www.w3.org/TR/IndexedDB/#exceptions
*
* @param {!goog.db.Error.ErrorCode|number} code The error code to convert.
* @return {!goog.db.Error.ErrorName} The corresponding name of the error.
*/
goog.db.Error.getName = function(code) {
switch (code) {
case goog.db.Error.ErrorCode.UNKNOWN_ERR:
return goog.db.Error.ErrorName.UNKNOWN_ERR;
case goog.db.Error.ErrorCode.NOT_FOUND_ERR:
return goog.db.Error.ErrorName.NOT_FOUND_ERR;
case goog.db.Error.ErrorCode.CONSTRAINT_ERR:
return goog.db.Error.ErrorName.CONSTRAINT_ERR;
case goog.db.Error.ErrorCode.DATA_ERR:
return goog.db.Error.ErrorName.DATA_ERR;
case goog.db.Error.ErrorCode.TRANSACTION_INACTIVE_ERR:
return goog.db.Error.ErrorName.TRANSACTION_INACTIVE_ERR;
case goog.db.Error.ErrorCode.ABORT_ERR:
return goog.db.Error.ErrorName.ABORT_ERR;
case goog.db.Error.ErrorCode.READ_ONLY_ERR:
return goog.db.Error.ErrorName.READ_ONLY_ERR;
case goog.db.Error.ErrorCode.TIMEOUT_ERR:
return goog.db.Error.ErrorName.TIMEOUT_ERR;
case goog.db.Error.ErrorCode.QUOTA_ERR:
return goog.db.Error.ErrorName.QUOTA_EXCEEDED_ERR;
case goog.db.Error.ErrorCode.INVALID_ACCESS_ERR:
return goog.db.Error.ErrorName.INVALID_ACCESS_ERR;
case goog.db.Error.ErrorCode.INVALID_STATE_ERR:
return goog.db.Error.ErrorName.INVALID_STATE_ERR;
default:
return goog.db.Error.ErrorName.UNKNOWN_ERR;
}
};
/**
* Constructs an goog.db.Error instance from an IDBRequest. This abstraction is
* necessary to provide backwards compatibility with Chrome21.
*
* @param {!IDBRequest} request The request that failed.
* @param {string} message The error message to add to err if it's wrapped.
* @return {!goog.db.Error} The error that caused the failure.
*/
goog.db.Error.fromRequest = function(request, message) {
if ('error' in request) {
// Chrome 21 and before.
return new goog.db.Error(request.error, message);
} else if ('name' in request) {
// Chrome 22+.
var errorName = goog.db.Error.getName(request.errorCode);
return new goog.db.Error(
/**@type {!DOMError} */ ({name: errorName}), message);
} else {
return new goog.db.Error(/** @type {!DOMError} */ (
{name: goog.db.Error.ErrorName.UNKNOWN_ERR}), message);
}
};
/**
* Constructs an goog.db.Error instance from an DOMException. This abstraction
* is necessary to provide backwards compatibility with Chrome21.
*
* @param {!IDBDatabaseException} ex The exception that was thrown.
* @param {string} message The error message to add to err if it's wrapped.
* @return {!goog.db.Error} The error that caused the failure.
* @suppress {invalidCasts} The cast from IDBDatabaseException to DOMError
* is invalid and will not compile.
*/
goog.db.Error.fromException = function(ex, message) {
if ('name' in ex) {
// Chrome 21 and before.
return new goog.db.Error(/** @type {!DOMError} */ (ex), message);
} else if ('code' in ex) {
// Chrome 22+.
var errorName = goog.db.Error.getName(ex.code);
return new goog.db.Error(
/** @type {!DOMError} */ ({name: errorName}), message);
} else {
return new goog.db.Error(/** @type {!DOMError} */ (
{name: goog.db.Error.ErrorName.UNKNOWN_ERR}), message);
}
};
@@ -0,0 +1,245 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Wrapper for an IndexedDB index.
*
*/
goog.provide('goog.db.Index');
goog.require('goog.async.Deferred');
goog.require('goog.db.Cursor');
goog.require('goog.db.Error');
goog.require('goog.debug');
/**
* Creates an IDBIndex wrapper object. Indexes are associated with object
* stores and provide methods for looking up objects based on their non-key
* properties. Should not be created directly, access through the object store
* it belongs to.
* @see goog.db.ObjectStore#getIndex
*
* @param {!IDBIndex} index Underlying IDBIndex object.
* @constructor
*/
goog.db.Index = function(index) {
/**
* Underlying IndexedDB index object.
*
* @type {!IDBIndex}
* @private
*/
this.index_ = index;
};
/**
* @return {string} Name of the index.
*/
goog.db.Index.prototype.getName = function() {
return this.index_.name;
};
/**
* @return {string} Key path of the index.
*/
goog.db.Index.prototype.getKeyPath = function() {
return this.index_.keyPath;
};
/**
* @return {boolean} True if the index enforces that there is only one object
* for each unique value it indexes on.
*/
goog.db.Index.prototype.isUnique = function() {
return this.index_.unique;
};
/**
* Helper function for get and getKey.
*
* @param {string} fn Function name to call on the index to get the request.
* @param {string} msg Message to give to the error.
* @param {IDBKeyType} key The key to look up in the index.
* @return {!goog.async.Deferred} The resulting deferred object.
* @private
*/
goog.db.Index.prototype.get_ = function(fn, msg, key) {
var d = new goog.async.Deferred();
var request;
try {
request = this.index_[fn](key);
} catch (err) {
msg += ' with key ' + goog.debug.deepExpose(key);
d.errback(goog.db.Error.fromException(err, msg));
return d;
}
request.onsuccess = function(ev) {
d.callback(ev.target.result);
};
request.onerror = function(ev) {
msg += ' with key ' + goog.debug.deepExpose(key);
d.errback(goog.db.Error.fromRequest(ev.target, msg));
};
return d;
};
/**
* Fetches a single object from the object store. Even if there are multiple
* objects that match the given key, this method will get only one of them.
*
* @param {IDBKeyType} key Key to look up in the index.
* @return {!goog.async.Deferred} The deferred object for the given record.
*/
goog.db.Index.prototype.get = function(key) {
return this.get_('get', 'getting from index ' + this.getName(), key);
};
/**
* Looks up a single object from the object store and gives back the key that
* it's listed under in the object store. Even if there are multiple records
* that match the given key, this method returns the first.
*
* @param {IDBKeyType} key Key to look up in the index.
* @return {!goog.async.Deferred} The deferred key for the record that matches
* the key.
*/
goog.db.Index.prototype.getKey = function(key) {
return this.get_('getKey', 'getting key from index ' + this.getName(), key);
};
/**
* Helper function for getAll and getAllKeys.
*
* @param {string} fn Function name to call on the index to get the request.
* @param {string} msg Message to give to the error.
* @param {IDBKeyType=} opt_key Key to look up in the index.
* @return {!goog.async.Deferred} The resulting deferred array of objects.
* @private
*/
goog.db.Index.prototype.getAll_ = function(fn, msg, opt_key) {
// This is the most common use of IDBKeyRange. If more specific uses of
// cursors are needed then a full wrapper should be created.
var IDBKeyRange = goog.global.IDBKeyRange || goog.global.webkitIDBKeyRange;
var d = new goog.async.Deferred();
var request;
try {
if (opt_key) {
request = this.index_[fn](IDBKeyRange.only(opt_key));
} else {
request = this.index_[fn]();
}
} catch (err) {
if (opt_key) {
msg += ' for key ' + goog.debug.deepExpose(opt_key);
}
d.errback(goog.db.Error.fromException(err, msg));
return d;
}
var result = [];
request.onsuccess = function(ev) {
var cursor = ev.target.result;
if (cursor) {
result.push(cursor.value);
cursor['continue']();
} else {
d.callback(result);
}
};
request.onerror = function(ev) {
if (opt_key) {
msg += ' for key ' + goog.debug.deepExpose(opt_key);
}
d.errback(goog.db.Error.fromRequest(ev.target, msg));
};
return d;
};
/**
* Gets all indexed objects. If the key is provided, gets all indexed objects
* that match the key instead.
*
* @param {IDBKeyType=} opt_key Key to look up in the index.
* @return {!goog.async.Deferred} A deferred array of objects that match the
* key.
*/
goog.db.Index.prototype.getAll = function(opt_key) {
return this.getAll_(
'openCursor',
'getting all from index ' + this.getName(),
opt_key);
};
/**
* Gets the keys to look up all the indexed objects. If the key is provided,
* gets all records for objects that match the key instead.
*
* @param {IDBKeyType=} opt_key Key to look up in the index.
* @return {!goog.async.Deferred} A deferred array of keys for objects that
* match the key.
*/
goog.db.Index.prototype.getAllKeys = function(opt_key) {
return this.getAll_(
'openKeyCursor',
'getting all keys from index ' + this.getName(),
opt_key);
};
/**
* Opens a cursor over the specified key range. Returns a cursor object which is
* able to iterate over the given range.
*
* Example usage:
*
* <code>
* var cursor = index.openCursor(goog.db.Range.bound('a', 'c'));
*
* var key = goog.events.listen(
* cursor, goog.db.Cursor.EventType.NEW_DATA,
* function() {
* // Do something with data.
* cursor.next();
* });
*
* goog.events.listenOnce(
* cursor, goog.db.Cursor.EventType.COMPLETE,
* function() {
* // Clean up listener, and perform a finishing operation on the data.
* goog.events.unlistenByKey(key);
* });
* </code>
*
* @param {!goog.db.KeyRange=} opt_range The key range. If undefined iterates
* over the whole object store.
* @param {!goog.db.Cursor.Direction=} opt_direction The direction. If undefined
* moves in a forward direction with duplicates.
* @return {!goog.db.Cursor} The cursor.
* @throws {goog.db.Error} If there was a problem opening the cursor.
*/
goog.db.Index.prototype.openCursor = function(opt_range, opt_direction) {
return goog.db.Cursor.openCursor(this.index_, opt_range, opt_direction);
};
@@ -0,0 +1,337 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Wrapper for an IndexedDB database.
*
*/
goog.provide('goog.db.IndexedDb');
goog.require('goog.async.Deferred');
goog.require('goog.db.Error');
goog.require('goog.db.Error.VersionChangeBlockedError');
goog.require('goog.db.ObjectStore');
goog.require('goog.db.Transaction');
goog.require('goog.db.Transaction.TransactionMode');
goog.require('goog.events.Event');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventTarget');
/**
* Creates an IDBDatabase wrapper object. The database object has methods for
* setting the version to change the structure of the database and for creating
* transactions to get or modify the stored records. Should not be created
* directly, call {@link goog.db.openDatabase} to set up the connection.
*
* @param {!IDBDatabase} db Underlying IndexedDB database object.
* @constructor
* @extends {goog.events.EventTarget}
*/
goog.db.IndexedDb = function(db) {
goog.base(this);
/**
* Underlying IndexedDB database object.
*
* @type {!IDBDatabase}
* @private
*/
this.db_ = db;
/**
* Internal event handler that listens to IDBDatabase events.
* @type {!goog.events.EventHandler}
* @private
*/
this.eventHandler_ = new goog.events.EventHandler(this);
this.eventHandler_.listen(
this.db_,
goog.db.IndexedDb.EventType.ABORT,
goog.bind(
this.dispatchEvent,
this,
goog.db.IndexedDb.EventType.ABORT));
this.eventHandler_.listen(
this.db_,
goog.db.IndexedDb.EventType.ERROR,
this.dispatchError_);
this.eventHandler_.listen(
this.db_,
goog.db.IndexedDb.EventType.VERSION_CHANGE,
this.dispatchVersionChange_);
};
goog.inherits(goog.db.IndexedDb, goog.events.EventTarget);
/**
* True iff the database connection is open.
*
* @type {boolean}
* @private
*/
goog.db.IndexedDb.prototype.open_ = true;
/**
* Dispatches a wrapped error event based on the given event.
*
* @param {Event} ev The error event given to the underlying IDBDatabase.
* @private
*/
goog.db.IndexedDb.prototype.dispatchError_ = function(ev) {
this.dispatchEvent({
type: goog.db.IndexedDb.EventType.ERROR,
errorCode: /** @type {IDBRequest} */ (ev.target).errorCode
});
};
/**
* Dispatches a wrapped version change event based on the given event.
*
* @param {Event} ev The version change event given to the underlying
* IDBDatabase.
* @private
*/
goog.db.IndexedDb.prototype.dispatchVersionChange_ = function(ev) {
this.dispatchEvent(new goog.db.IndexedDb.VersionChangeEvent(
ev.oldVersion, ev.newVersion));
};
/**
* Closes the database connection. Metadata queries can still be made after this
* method is called, but otherwise this wrapper should not be used further.
*/
goog.db.IndexedDb.prototype.close = function() {
if (this.open_) {
this.db_.close();
this.open_ = false;
}
};
/**
* @return {boolean} Whether a connection is open and the database can be used.
*/
goog.db.IndexedDb.prototype.isOpen = function() {
return this.open_;
};
/**
* @return {string} The name of this database.
*/
goog.db.IndexedDb.prototype.getName = function() {
return this.db_.name;
};
/**
* @return {string} The current database version.
*/
goog.db.IndexedDb.prototype.getVersion = function() {
return this.db_.version;
};
/**
* @return {DOMStringList} List of object stores in this database.
*/
goog.db.IndexedDb.prototype.getObjectStoreNames = function() {
return this.db_.objectStoreNames;
};
/**
* Creates an object store in this database. Can only be called inside a
* {@link goog.db.UpgradeNeededCallback} or the callback for the Deferred
* returned from #setVersion.
*
* @param {string} name Name for the new object store.
* @param {Object=} opt_params Options object. The available options are:
* keyPath, which is a string and determines what object attribute
* to use as the key when storing objects in this object store; and
* autoIncrement, which is a boolean, which defaults to false and determines
* whether the object store should automatically generate keys for stored
* objects. If keyPath is not provided and autoIncrement is false, then all
* insert operations must provide a key as a parameter.
* @return {goog.db.ObjectStore} The newly created object store.
* @throws {goog.db.Error} If there's a problem creating the object store.
*/
goog.db.IndexedDb.prototype.createObjectStore = function(name, opt_params) {
try {
return new goog.db.ObjectStore(this.db_.createObjectStore(
name, opt_params));
} catch (ex) {
throw goog.db.Error.fromException(ex, 'creating object store ' + name);
}
};
/**
* Deletes an object store. Can only be called inside a
* {@link goog.db.UpgradeNeededCallback} or the callback for the Deferred
* returned from #setVersion.
*
* @param {string} name Name of the object store to delete.
* @throws {goog.db.Error} If there's a problem deleting the object store.
*/
goog.db.IndexedDb.prototype.deleteObjectStore = function(name) {
try {
this.db_.deleteObjectStore(name);
} catch (ex) {
throw goog.db.Error.fromException(ex, 'deleting object store ' + name);
}
};
/**
* Updates the version of the database and returns a Deferred transaction.
* The database's structure can be changed inside this Deferred's callback, but
* nowhere else. This means adding or deleting object stores, and adding or
* deleting indexes. The version change will not succeed unless there are no
* other connections active for this database anywhere. A new database
* connection should be opened after the version change is finished to pick
* up changes.
*
* This is deprecated, and only supported on Chrome prior to version 25. New
* applications should use the version parameter to {@link goog.db.openDatabase}
* instead.
*
* @param {string} version The new version of the database.
* @return {!goog.async.Deferred} The deferred transaction for changing the
* version.
*/
goog.db.IndexedDb.prototype.setVersion = function(version) {
var self = this;
var d = new goog.async.Deferred();
var request = this.db_.setVersion(version);
request.onsuccess = function(ev) {
// the transaction is in the result field (the transaction field is null
// for version change requests)
d.callback(new goog.db.Transaction(ev.target.result, self));
};
request.onerror = function(ev) {
// If a version change is blocked, onerror and onblocked may both fire.
// Check d.hasFired() to avoid an AlreadyCalledError.
if (!d.hasFired()) {
d.errback(goog.db.Error.fromRequest(ev.target, 'setting version'));
}
};
request.onblocked = function(ev) {
// If a version change is blocked, onerror and onblocked may both fire.
// Check d.hasFired() to avoid an AlreadyCalledError.
if (!d.hasFired()) {
d.errback(new goog.db.Error.VersionChangeBlockedError());
}
};
return d;
};
/**
* Creates a new transaction.
*
* @param {!Array.<string>} storeNames A list of strings that contains the
* transaction's scope, the object stores that this transaction can operate
* on.
* @param {goog.db.Transaction.TransactionMode=} opt_mode The mode of the
* transaction. If not present, the default is READ_ONLY. For VERSION_CHANGE
* transactions call {@link goog.db.IndexedDB#setVersion} instead.
* @return {!goog.db.Transaction} The wrapper for the newly created transaction.
* @throws {goog.db.Error} If there's a problem creating the transaction.
*/
goog.db.IndexedDb.prototype.createTransaction = function(storeNames, opt_mode) {
try {
// IndexedDB on Chrome 22+ requires that opt_mode not be passed rather than
// be explicitly passed as undefined.
var transaction = opt_mode ?
this.db_.transaction(storeNames, opt_mode) :
this.db_.transaction(storeNames);
return new goog.db.Transaction(transaction, this);
} catch (ex) {
throw goog.db.Error.fromException(ex, 'creating transaction');
}
};
/** @override */
goog.db.IndexedDb.prototype.disposeInternal = function() {
goog.base(this, 'disposeInternal');
this.eventHandler_.dispose();
};
/**
* Event types fired by a database.
*
* @enum {string} The event types for the web socket.
*/
goog.db.IndexedDb.EventType = {
/**
* Fired when a transaction is aborted and the event bubbles to its database.
*/
ABORT: 'abort',
/**
* Fired when a transaction has an error.
*/
ERROR: 'error',
/**
* Fired when someone (possibly in another window) is attempting to modify the
* structure of the database. Since a change can only be made when there are
* no active database connections, this usually means that the database should
* be closed so that the other client can make its changes.
*/
VERSION_CHANGE: 'versionchange'
};
/**
* Event representing a (possibly attempted) change in the database structure.
*
* At time of writing, no Chrome versions support oldVersion or newVersion. See
* http://crbug.com/153122.
*
* @param {number} oldVersion The previous version of the database.
* @param {number} newVersion The version the database is being or has been
* updated to.
* @constructor
* @extends {goog.events.Event}
*/
goog.db.IndexedDb.VersionChangeEvent = function(oldVersion, newVersion) {
goog.base(this, goog.db.IndexedDb.EventType.VERSION_CHANGE);
/**
* The previous version of the database.
* @type {number}
*/
this.oldVersion = oldVersion;
/**
* The version the database is being or has been updated to.
* @type {number}
*/
this.newVersion = newVersion;
};
goog.inherits(goog.db.IndexedDb.VersionChangeEvent, goog.events.Event);
@@ -0,0 +1,117 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Wrapper for a IndexedDB key range.
*
*/
goog.provide('goog.db.KeyRange');
/**
* Creates a new IDBKeyRange wrapper object. Should not be created directly,
* instead use one of the static factory methods. For example:
* @see goog.db.KeyRange.bound
* @see goog.db.KeyRange.lowerBound
*
* @param {!IDBKeyRange} range Underlying IDBKeyRange object.
* @constructor
*/
goog.db.KeyRange = function(range) {
/**
* Underlying IDBKeyRange object.
*
* @type {!IDBKeyRange}
* @private
*/
this.range_ = range;
};
/**
* The IDBKeyRange.
* @type {!Object}
* @private
*/
goog.db.KeyRange.IDB_KEY_RANGE_ = goog.global.IDBKeyRange ||
goog.global.webkitIDBKeyRange;
/**
* Creates a new key range for a single value.
*
* @param {IDBKeyType} key The single value in the range.
* @return {!goog.db.KeyRange} The key range.
*/
goog.db.KeyRange.only = function(key) {
return new goog.db.KeyRange(goog.db.KeyRange.IDB_KEY_RANGE_.only(key));
};
/**
* Creates a key range with upper and lower bounds.
*
* @param {IDBKeyType} lower The value of the lower bound.
* @param {IDBKeyType} upper The value of the upper bound.
* @param {boolean=} opt_lowerOpen If true, the range excludes the lower bound
* value.
* @param {boolean=} opt_upperOpen If true, the range excludes the upper bound
* value.
* @return {!goog.db.KeyRange} The key range.
*/
goog.db.KeyRange.bound = function(lower, upper, opt_lowerOpen, opt_upperOpen) {
return new goog.db.KeyRange(goog.db.KeyRange.IDB_KEY_RANGE_.bound(
lower, upper, opt_lowerOpen, opt_upperOpen));
};
/**
* Creates a key range with a lower bound only, finishes at the last record.
*
* @param {IDBKeyType} lower The value of the lower bound.
* @param {boolean=} opt_lowerOpen If true, the range excludes the lower bound
* value.
* @return {!goog.db.KeyRange} The key range.
*/
goog.db.KeyRange.lowerBound = function(lower, opt_lowerOpen) {
return new goog.db.KeyRange(goog.db.KeyRange.IDB_KEY_RANGE_.lowerBound(
lower, opt_lowerOpen));
};
/**
* Creates a key range with a upper bound only, starts at the first record.
*
* @param {IDBKeyType} upper The value of the upper bound.
* @param {boolean=} opt_upperOpen If true, the range excludes the upper bound
* value.
* @return {!goog.db.KeyRange} The key range.
*/
goog.db.KeyRange.upperBound = function(upper, opt_upperOpen) {
return new goog.db.KeyRange(goog.db.KeyRange.IDB_KEY_RANGE_.upperBound(
upper, opt_upperOpen));
};
/**
* Returns underlying key range object. This is used in ObjectStore's openCursor
* and count methods.
* @return {!IDBKeyRange}
*/
goog.db.KeyRange.prototype.range = function() {
return this.range_;
};
@@ -0,0 +1,399 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Wrapper for an IndexedDB object store.
*
*/
goog.provide('goog.db.ObjectStore');
goog.require('goog.async.Deferred');
goog.require('goog.db.Cursor');
goog.require('goog.db.Error');
goog.require('goog.db.Index');
goog.require('goog.debug');
goog.require('goog.events');
/**
* Creates an IDBObjectStore wrapper object. Object stores have methods for
* storing and retrieving records, and are accessed through a transaction
* object. They also have methods for creating indexes associated with the
* object store. They can only be created when setting the version of the
* database. Should not be created directly, access object stores through
* transactions.
* @see goog.db.IndexedDb#setVersion
* @see goog.db.Transaction#objectStore
*
* @param {!IDBObjectStore} store The backing IndexedDb object.
* @constructor
*
* TODO(user): revisit msg in exception and errors in this class. In newer
* Chrome (v22+) the error/request come with a DOM error string that is
* already very descriptive.
*/
goog.db.ObjectStore = function(store) {
/**
* Underlying IndexedDB object store object.
*
* @type {!IDBObjectStore}
* @private
*/
this.store_ = store;
};
/**
* @return {string} The name of the object store.
*/
goog.db.ObjectStore.prototype.getName = function() {
return this.store_.name;
};
/**
* Helper function for put and add.
*
* @param {string} fn Function name to call on the object store.
* @param {string} msg Message to give to the error.
* @param {*} value Value to insert into the object store.
* @param {IDBKeyType=} opt_key The key to use.
* @return {!goog.async.Deferred} The resulting deferred request.
* @private
*/
goog.db.ObjectStore.prototype.insert_ = function(fn, msg, value, opt_key) {
// TODO(user): refactor wrapping an IndexedDB request in a Deferred by
// creating a higher-level abstraction for it (mostly affects here and
// goog.db.Index)
var d = new goog.async.Deferred();
var request;
try {
// put or add with (value, undefined) throws an error, so we need to check
// for undefined ourselves
if (opt_key) {
request = this.store_[fn](value, opt_key);
} else {
request = this.store_[fn](value);
}
} catch (ex) {
msg += goog.debug.deepExpose(value);
if (opt_key) {
msg += ', with key ' + goog.debug.deepExpose(opt_key);
}
d.errback(goog.db.Error.fromException(ex, msg));
return d;
}
request.onsuccess = function(ev) {
d.callback();
};
var self = this;
request.onerror = function(ev) {
msg += goog.debug.deepExpose(value);
if (opt_key) {
msg += ', with key ' + goog.debug.deepExpose(opt_key);
}
d.errback(goog.db.Error.fromRequest(ev.target, msg));
};
return d;
};
/**
* Adds an object to the object store. Replaces existing objects with the
* same key.
*
* @param {*} value The value to put.
* @param {IDBKeyType=} opt_key The key to use. Cannot be used if the
* keyPath was specified for the object store. If the keyPath was not
* specified but autoIncrement was not enabled, it must be used.
* @return {!goog.async.Deferred} The deferred put request.
*/
goog.db.ObjectStore.prototype.put = function(value, opt_key) {
return this.insert_(
'put',
'putting into ' + this.getName() + ' with value',
value,
opt_key);
};
/**
* Adds an object to the object store. Requires that there is no object with
* the same key already present.
*
* @param {*} value The value to add.
* @param {IDBKeyType=} opt_key The key to use. Cannot be used if the
* keyPath was specified for the object store. If the keyPath was not
* specified but autoIncrement was not enabled, it must be used.
* @return {!goog.async.Deferred} The deferred add request.
*/
goog.db.ObjectStore.prototype.add = function(value, opt_key) {
return this.insert_(
'add',
'adding into ' + this.getName() + ' with value ',
value,
opt_key);
};
/**
* Removes an object from the store. No-op if there is no object present with
* the given key.
*
* @param {IDBKeyType} key The key to remove objects under.
* @return {!goog.async.Deferred} The deferred remove request.
*/
goog.db.ObjectStore.prototype.remove = function(key) {
var d = new goog.async.Deferred();
var request;
try {
request = this.store_['delete'](key);
} catch (err) {
var msg = 'removing from ' + this.getName() + ' with key ' +
goog.debug.deepExpose(key);
d.errback(goog.db.Error.fromException(err, msg));
return d;
}
request.onsuccess = function(ev) {
d.callback();
};
var self = this;
request.onerror = function(ev) {
var msg = 'removing from ' + self.getName() + ' with key ' +
goog.debug.deepExpose(key);
d.errback(goog.db.Error.fromRequest(ev.target, msg));
};
return d;
};
/**
* Gets an object from the store. If no object is present with that key
* the result is {@code undefined}.
*
* @param {IDBKeyType} key The key to look up.
* @return {!goog.async.Deferred} The deferred get request.
*/
goog.db.ObjectStore.prototype.get = function(key) {
var d = new goog.async.Deferred();
var request;
try {
request = this.store_.get(key);
} catch (err) {
var msg = 'getting from ' + this.getName() + ' with key ' +
goog.debug.deepExpose(key);
d.errback(goog.db.Error.fromException(err, msg));
return d;
}
request.onsuccess = function(ev) {
d.callback(ev.target.result);
};
var self = this;
request.onerror = function(ev) {
var msg = 'getting from ' + self.getName() + ' with key ' +
goog.debug.deepExpose(key);
d.errback(goog.db.Error.fromRequest(ev.target, msg));
};
return d;
};
/**
* Gets all objects from the store and returns them as an array.
*
* @param {!goog.db.KeyRange=} opt_range The key range. If undefined iterates
* over the whole object store.
* @param {!goog.db.Cursor.Direction=} opt_direction The direction. If undefined
* moves in a forward direction with duplicates.
* @return {!goog.async.Deferred} The deferred getAll request.
*/
goog.db.ObjectStore.prototype.getAll = function(opt_range, opt_direction) {
var d = new goog.async.Deferred();
var cursor;
try {
cursor = this.openCursor(opt_range, opt_direction);
} catch (err) {
d.errback(err);
return d;
}
var result = [];
var key = goog.events.listen(
cursor, goog.db.Cursor.EventType.NEW_DATA, function() {
result.push(cursor.getValue());
cursor.next();
});
goog.events.listenOnce(cursor, [
goog.db.Cursor.EventType.ERROR,
goog.db.Cursor.EventType.COMPLETE
], function(evt) {
cursor.dispose();
if (evt.type == goog.db.Cursor.EventType.COMPLETE) {
d.callback(result);
} else {
d.errback();
}
});
return d;
};
/**
* Opens a cursor over the specified key range. Returns a cursor object which is
* able to iterate over the given range.
*
* Example usage:
*
* <code>
* var cursor = objectStore.openCursor(goog.db.Range.bound('a', 'c'));
*
* var key = goog.events.listen(
* cursor, goog.db.Cursor.EventType.NEW_DATA, function() {
* // Do something with data.
* cursor.next();
* });
*
* goog.events.listenOnce(
* cursor, goog.db.Cursor.EventType.COMPLETE, function() {
* // Clean up listener, and perform a finishing operation on the data.
* goog.events.unlistenByKey(key);
* });
* </code>
*
* @param {!goog.db.KeyRange=} opt_range The key range. If undefined iterates
* over the whole object store.
* @param {!goog.db.Cursor.Direction=} opt_direction The direction. If undefined
* moves in a forward direction with duplicates.
* @return {!goog.db.Cursor} The cursor.
* @throws {goog.db.Error} If there was a problem opening the cursor.
*/
goog.db.ObjectStore.prototype.openCursor = function(opt_range, opt_direction) {
return goog.db.Cursor.openCursor(this.store_, opt_range, opt_direction);
};
/**
* Deletes all objects from the store.
*
* @return {!goog.async.Deferred} The deferred clear request.
*/
goog.db.ObjectStore.prototype.clear = function() {
var msg = 'clearing store ' + this.getName();
var d = new goog.async.Deferred();
var request;
try {
request = this.store_.clear();
} catch (err) {
d.errback(goog.db.Error.fromException(err, msg));
return d;
}
request.onsuccess = function(ev) {
d.callback();
};
request.onerror = function(ev) {
d.errback(goog.db.Error.fromRequest(ev.target, msg));
};
return d;
};
/**
* Creates an index in this object store. Can only be called inside the callback
* for the Deferred returned from goog.db.IndexedDb#setVersion.
*
* @param {string} name Name of the index to create.
* @param {string} keyPath Attribute to index on.
* @param {!Object=} opt_parameters Optional parameters object. The only
* available option is unique, which defaults to false. If unique is true,
* the index will enforce that there is only ever one object in the object
* store for each unique value it indexes on.
* @return {goog.db.Index} The newly created, wrapped index.
* @throws {goog.db.Error} In case of an error creating the index.
*/
goog.db.ObjectStore.prototype.createIndex = function(
name, keyPath, opt_parameters) {
try {
return new goog.db.Index(this.store_.createIndex(
name, keyPath, opt_parameters));
} catch (ex) {
var msg = 'creating new index ' + name + ' with key path ' + keyPath;
throw goog.db.Error.fromException(ex, msg);
}
};
/**
* Gets an index.
*
* @param {string} name Name of the index to fetch.
* @return {goog.db.Index} The requested wrapped index.
* @throws {goog.db.Error} In case of an error getting the index.
*/
goog.db.ObjectStore.prototype.getIndex = function(name) {
try {
return new goog.db.Index(this.store_.index(name));
} catch (ex) {
var msg = 'getting index ' + name;
throw goog.db.Error.fromException(ex, msg);
}
};
/**
* Deletes an index from the object store. Can only be called inside the
* callback for the Deferred returned from goog.db.IndexedDb#setVersion.
*
* @param {string} name Name of the index to delete.
* @throws {goog.db.Error} In case of an error deleting the index.
*/
goog.db.ObjectStore.prototype.deleteIndex = function(name) {
try {
this.store_.deleteIndex(name);
} catch (ex) {
var msg = 'deleting index ' + name;
throw goog.db.Error.fromException(ex, msg);
}
};
/**
* Gets number of records within a key range.
*
* @param {!goog.db.KeyRange=} opt_range The key range. If undefined, this will
* count all records in the object store.
* @return {!goog.async.Deferred} The deferred number of records.
*/
goog.db.ObjectStore.prototype.count = function(opt_range) {
var request;
var d = new goog.async.Deferred();
try {
var range = opt_range ? opt_range.range() : null;
request = this.store_.count(range);
} catch (ex) {
d.errback(goog.db.Error.fromException(ex, this.getName()));
}
request.onsuccess = function(ev) {
d.callback(ev.target.result);
};
request.onerror = function(ev) {
d.errback(goog.db.Error.fromRequest(ev.target, this.getName()));
};
return d;
};
@@ -0,0 +1,218 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Wrapper for an IndexedDB transaction.
*
*/
goog.provide('goog.db.Transaction');
goog.provide('goog.db.Transaction.TransactionMode');
goog.require('goog.async.Deferred');
goog.require('goog.db.Error');
goog.require('goog.db.ObjectStore');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventTarget');
/**
* Creates a new transaction. Transactions contain methods for accessing object
* stores and are created from the database object. Should not be created
* directly, open a database and call createTransaction on it.
* @see goog.db.IndexedDb#createTransaction
*
* @param {!IDBTransaction} tx IndexedDB transaction to back this wrapper.
* @param {!goog.db.IndexedDb} db The database that this transaction modifies.
* @constructor
* @extends {goog.events.EventTarget}
*/
goog.db.Transaction = function(tx, db) {
goog.base(this);
/**
* Underlying IndexedDB transaction object.
*
* @type {!IDBTransaction}
* @private
*/
this.tx_ = tx;
/**
* The database that this transaction modifies.
*
* @type {!goog.db.IndexedDb}
* @private
*/
this.db_ = db;
/**
* Event handler for this transaction.
*
* @type {!goog.events.EventHandler}
* @private
*/
this.eventHandler_ = new goog.events.EventHandler(this);
// TODO(user): remove these casts once the externs file is updated to
// correctly reflect that IDBTransaction extends EventTarget
this.eventHandler_.listen(
/** @type {EventTarget} */ (this.tx_),
'complete',
goog.bind(
this.dispatchEvent,
this,
goog.db.Transaction.EventTypes.COMPLETE));
this.eventHandler_.listen(
/** @type {EventTarget} */ (this.tx_),
'abort',
goog.bind(
this.dispatchEvent,
this,
goog.db.Transaction.EventTypes.ABORT));
this.eventHandler_.listen(
/** @type {EventTarget} */ (this.tx_),
'error',
this.dispatchError_);
};
goog.inherits(goog.db.Transaction, goog.events.EventTarget);
/**
* Dispatches an error event based on the given event, wrapping the error
* if necessary.
*
* @param {Event} ev The error event given to the underlying IDBTransaction.
* @private
*/
goog.db.Transaction.prototype.dispatchError_ = function(ev) {
if (ev.target instanceof goog.db.Error) {
this.dispatchEvent({
type: goog.db.Transaction.EventTypes.ERROR,
target: ev.target
});
} else {
this.dispatchEvent({
type: goog.db.Transaction.EventTypes.ERROR,
target: goog.db.Error.fromRequest(
/** @type {!IDBRequest} */ (ev.target), 'in transaction')
});
}
};
/**
* Event types the Transaction can dispatch. COMPLETE events are dispatched
* when the transaction is committed. If a transaction is aborted it dispatches
* both an ABORT event and an ERROR event with the ABORT_ERR code. Error events
* are dispatched on any error.
*
* @enum {string}
*/
goog.db.Transaction.EventTypes = {
COMPLETE: 'complete',
ABORT: 'abort',
ERROR: 'error'
};
/**
* @return {goog.db.Transaction.TransactionMode} The transaction's mode.
*/
goog.db.Transaction.prototype.getMode = function() {
return /** @type {goog.db.Transaction.TransactionMode} */ (this.tx_.mode);
};
/**
* @return {!goog.db.IndexedDb} The database that this transaction modifies.
*/
goog.db.Transaction.prototype.getDatabase = function() {
return this.db_;
};
/**
* Opens an object store to do operations on in this transaction. The requested
* object store must be one that is in this transaction's scope.
* @see goog.db.IndexedDb#createTransaction
*
* @param {string} name The name of the requested object store.
* @return {!goog.db.ObjectStore} The wrapped object store.
* @throws {goog.db.Error} In case of error getting the object store.
*/
goog.db.Transaction.prototype.objectStore = function(name) {
try {
return new goog.db.ObjectStore(this.tx_.objectStore(name));
} catch (ex) {
throw goog.db.Error.fromException(ex, 'getting object store ' + name);
}
};
/**
* @return {!goog.async.Deferred} A deferred that will fire once the
* transaction is complete. It fires the errback chain if an error occurs
* in the transaction, or if it is aborted.
*/
goog.db.Transaction.prototype.wait = function() {
var d = new goog.async.Deferred();
goog.events.listenOnce(
this, goog.db.Transaction.EventTypes.COMPLETE, goog.bind(d.callback, d));
goog.events.listenOnce(
this, goog.db.Transaction.EventTypes.ABORT, function() {
d.errback(new goog.db.Error(goog.db.Error.ErrorCode.ABORT_ERR,
'waiting for transaction to complete'));
});
goog.events.listenOnce(
this, goog.db.Transaction.EventTypes.ERROR, function(e) {
d.errback(e.target);
});
var db = this.getDatabase();
return d.addCallback(function() {
return db;
});
};
/**
* Aborts this transaction. No pending operations will be applied to the
* database. Dispatches an ABORT event.
*/
goog.db.Transaction.prototype.abort = function() {
this.tx_.abort();
};
/** @override */
goog.db.Transaction.prototype.disposeInternal = function() {
goog.base(this, 'disposeInternal');
this.eventHandler_.dispose();
};
/**
* The three possible transaction modes.
* @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBTransaction
*
* @enum {string}
*/
goog.db.Transaction.TransactionMode = {
READ_ONLY: 'readonly',
READ_WRITE: 'readwrite',
VERSION_CHANGE: 'versionchange'
};