Files
ShoppingList/IndexedDBShim.js

1672 lines
69 KiB
JavaScript
Raw Permalink Normal View History

2017-02-07 20:09:46 +00:00
/*jshint globalstrict: true*/
'use strict';
/**
* An initialization file that checks for conditions, removes console.log and warn, etc
*/
var idbModules = {};
/*jshint globalstrict: true*/
'use strict';
(function(idbModules) {
/**
* A utility method to callback onsuccess, onerror, etc as soon as the calling function's context is over
* @param {Object} fn
* @param {Object} context
* @param {Object} argArray
*/
function callback(fn, context, event, func) {
//window.setTimeout(function(){
event.target = context;
(typeof context[fn] === "function") && context[fn].apply(context, [event]);
(typeof func === "function") && func();
//}, 1);
}
/**
* Throws a new DOM Exception,
* @param {Object} name
* @param {Object} message
* @param {Object} error
*/
function throwDOMException(name, message, error) {
var e = new DOMException.constructor(0, message);
e.name = name;
e.message = message;
if (idbModules.DEBUG) {
console.log(name, message, error, e);
console.trace && console.trace();
}
throw e;
}
/**
* Shim the DOMStringList object.
*
*/
var StringList = function() {
this.length = 0;
this._items = [];
//Internal functions on the prototype have been made non-enumerable below.
if (Object.defineProperty) {
Object.defineProperty(this, '_items', {
enumerable: false
});
}
};
StringList.prototype = {
// Interface.
contains: function(str) {
return -1 !== this._items.indexOf(str);
},
item: function(key) {
return this._items[key];
},
// Helpers. Should only be used internally.
indexOf: function(str) {
return this._items.indexOf(str);
},
push: function(item) {
this._items.push(item);
this.length += 1;
for (var i = 0; i < this._items.length; i++) {
this[i] = this._items[i];
}
},
splice: function( /*index, howmany, item1, ..., itemX*/ ) {
this._items.splice.apply(this._items, arguments);
this.length = this._items.length;
for (var i in this) {
if (i === String(parseInt(i, 10))) {
delete this[i];
}
}
for (i = 0; i < this._items.length; i++) {
this[i] = this._items[i];
}
}
};
if (Object.defineProperty) {
for (var i in {
'indexOf': false,
'push': false,
'splice': false
}) {
Object.defineProperty(StringList.prototype, i, {
enumerable: false
});
}
}
idbModules.util = {
"throwDOMException": throwDOMException,
"callback": callback,
"quote": function(arg) {
return "'" + arg + "'";
},
"StringList": StringList
};
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules){
/**
* Implementation of the Structured Cloning Algorithm. Supports the
* following object types:
* - Blob
* - Boolean
* - Date object
* - File object (deserialized as Blob object).
* - Number object
* - RegExp object
* - String object
* This is accomplished by doing the following:
* 1) Using the cycle/decycle functions from:
* https://github.com/douglascrockford/JSON-js/blob/master/cycle.js
* 2) Serializing/deserializing objects to/from string that don't work with
* JSON.stringify and JSON.parse by using object specific logic (eg use
* the FileReader API to convert a Blob or File object to a data URL.
* 3) JSON.stringify and JSON.parse do the final conversion to/from string.
*/
var Sca = (function(){
return {
decycle: function(object, callback) {
//From: https://github.com/douglascrockford/JSON-js/blob/master/cycle.js
// Contains additional logic to convert the following object types to string
// so that they can properly be encoded using JSON.stringify:
// *Boolean
// *Date
// *File
// *Blob
// *Number
// *Regex
// Make a deep copy of an object or array, assuring that there is at most
// one instance of each object or array in the resulting structure. The
// duplicate references (which might be forming cycles) are replaced with
// an object of the form
// {$ref: PATH}
// where the PATH is a JSONPath string that locates the first occurance.
// So,
// var a = [];
// a[0] = a;
// return JSON.stringify(JSON.decycle(a));
// produces the string '[{"$ref":"$"}]'.
// JSONPath is used to locate the unique object. $ indicates the top level of
// the object or array. [NUMBER] or [STRING] indicates a child member or
// property.
var objects = [], // Keep a reference to each unique object or array
paths = [], // Keep the path to each unique object or array
queuedObjects = [],
returnCallback = callback;
/**
* Check the queue to see if all objects have been processed.
* if they have, call the callback with the converted object.
*/
function checkForCompletion() {
if (queuedObjects.length === 0) {
returnCallback(derezObj);
}
}
/**
* Convert a blob to a data URL.
* @param {Blob} blob to convert.
* @param {String} path of blob in object being encoded.
*/
function readBlobAsDataURL(blob, path) {
var reader = new FileReader();
reader.onloadend = function(loadedEvent) {
var dataURL = loadedEvent.target.result;
var blobtype = 'blob';
if (blob instanceof File) {
//blobtype = 'file';
}
updateEncodedBlob(dataURL, path, blobtype);
};
reader.readAsDataURL(blob);
}
/**
* Async handler to update a blob object to a data URL for encoding.
* @param {String} dataURL
* @param {String} path
* @param {String} blobtype - file if the blob is a file; blob otherwise
*/
function updateEncodedBlob(dataURL, path, blobtype) {
var encoded = queuedObjects.indexOf(path);
path = path.replace('$','derezObj');
eval(path+'.$enc="'+dataURL+'"');
eval(path+'.$type="'+blobtype+'"');
queuedObjects.splice(encoded, 1);
checkForCompletion();
}
function derez(value, path) {
// The derez recurses through the object, producing the deep copy.
var i, // The loop counter
name, // Property name
nu; // The new object or array
// typeof null === 'object', so go on if this value is really an object but not
// one of the weird builtin objects.
if (typeof value === 'object' && value !== null &&
!(value instanceof Boolean) &&
!(value instanceof Date) &&
!(value instanceof Number) &&
!(value instanceof RegExp) &&
!(value instanceof Blob) &&
!(value instanceof String)) {
// If the value is an object or array, look to see if we have already
// encountered it. If so, return a $ref/path object. This is a hard way,
// linear search that will get slower as the number of unique objects grows.
for (i = 0; i < objects.length; i += 1) {
if (objects[i] === value) {
return {$ref: paths[i]};
}
}
// Otherwise, accumulate the unique value and its path.
objects.push(value);
paths.push(path);
// If it is an array, replicate the array.
if (Object.prototype.toString.apply(value) === '[object Array]') {
nu = [];
for (i = 0; i < value.length; i += 1) {
nu[i] = derez(value[i], path + '[' + i + ']');
}
} else {
// If it is an object, replicate the object.
nu = {};
for (name in value) {
if (Object.prototype.hasOwnProperty.call(value, name)) {
nu[name] = derez(value[name],
path + '[' + JSON.stringify(name) + ']');
}
}
}
return nu;
} else if (value instanceof Blob) {
//Queue blob for conversion
queuedObjects.push(path);
readBlobAsDataURL(value, path);
} else if (value instanceof Boolean) {
value = {
'$type': 'bool',
'$enc': value.toString()
};
} else if (value instanceof Date) {
value = {
'$type': 'date',
'$enc': value.getTime()
};
} else if (value instanceof Number) {
value = {
'$type': 'num',
'$enc': value.toString()
};
} else if (value instanceof RegExp) {
value = {
'$type': 'regex',
'$enc': value.toString()
};
}
return value;
}
var derezObj = derez(object, '$');
checkForCompletion();
},
retrocycle: function retrocycle($) {
//From: https://github.com/douglascrockford/JSON-js/blob/master/cycle.js
// Contains additional logic to convert strings to the following object types
// so that they can properly be decoded:
// *Boolean
// *Date
// *File
// *Blob
// *Number
// *Regex
// Restore an object that was reduced by decycle. Members whose values are
// objects of the form
// {$ref: PATH}
// are replaced with references to the value found by the PATH. This will
// restore cycles. The object will be mutated.
// The eval function is used to locate the values described by a PATH. The
// root object is kept in a $ variable. A regular expression is used to
// assure that the PATH is extremely well formed. The regexp contains nested
// * quantifiers. That has been known to have extremely bad performance
// problems on some browsers for very long strings. A PATH is expected to be
// reasonably short. A PATH is allowed to belong to a very restricted subset of
// Goessner's JSONPath.
// So,
// var s = '[{"$ref":"$"}]';
// return JSON.retrocycle(JSON.parse(s));
// produces an array containing a single element which is the array itself.
var px = /^\$(?:\[(?:\d+|\"(?:[^\\\"\u0000-\u001f]|\\([\\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*\")\])*$/;
/**
* Converts the specified data URL to a Blob object
* @param {String} dataURL to convert to a Blob
* @returns {Blob} the converted Blob object
*/
function dataURLToBlob(dataURL) {
var BASE64_MARKER = ';base64,',
contentType,
parts,
raw;
if (dataURL.indexOf(BASE64_MARKER) === -1) {
parts = dataURL.split(',');
contentType = parts[0].split(':')[1];
raw = parts[1];
return new Blob([raw], {type: contentType});
}
parts = dataURL.split(BASE64_MARKER);
contentType = parts[0].split(':')[1];
raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array.buffer], {type: contentType});
}
function rez(value) {
// The rez function walks recursively through the object looking for $ref
// properties. When it finds one that has a value that is a path, then it
// replaces the $ref object with a reference to the value that is found by
// the path.
var i, item, name, path;
if (value && typeof value === 'object') {
if (Object.prototype.toString.apply(value) === '[object Array]') {
for (i = 0; i < value.length; i += 1) {
item = value[i];
if (item && typeof item === 'object') {
path = item.$ref;
if (typeof path === 'string' && px.test(path)) {
value[i] = eval(path);
} else {
value[i] = rez(item);
}
}
}
} else {
if (value.$type !== undefined) {
switch(value.$type) {
case 'blob':
case 'file':
value = dataURLToBlob(value.$enc);
break;
case 'bool':
value = Boolean(value.$enc === 'true');
break;
case 'date':
value = new Date(value.$enc);
break;
case 'num':
value = Number(value.$enc);
break;
case 'regex':
value = eval(value.$enc);
break;
}
} else {
for (name in value) {
if (typeof value[name] === 'object') {
item = value[name];
if (item) {
path = item.$ref;
if (typeof path === 'string' && px.test(path)) {
value[name] = eval(path);
} else {
value[name] = rez(item);
}
}
}
}
}
}
}
return value;
}
rez($);
return $;
},
/**
* Encode the specified object as a string. Because of the asynchronus
* conversion of Blob/File to string, the encode function requires
* a callback
* @param {Object} val the value to convert.
* @param {function} callback the function to call once conversion is
* complete. The callback gets called with the converted value.
*/
"encode": function(val, callback){
function finishEncode(val) {
callback(JSON.stringify(val));
}
this.decycle(val, finishEncode);
},
/**
* Deserialize the specified string to an object
* @param {String} val the serialized string
* @returns {Object} the deserialized object
*/
"decode": function(val){
return this.retrocycle(JSON.parse(val));
}
};
}());
idbModules.Sca = Sca;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules){
/**
* Encodes the keys and values based on their types. This is required to maintain collations
*/
var collations = ["", "number", "string", "boolean", "object", "undefined"];
var getGenericEncoder = function(){
return {
"encode": function(key){
return collations.indexOf(typeof key) + "-" + JSON.stringify(key);
},
"decode": function(key){
if (typeof key === "undefined") {
return undefined;
}
else {
return JSON.parse(key.substring(2));
}
}
};
};
var types = {
"number": getGenericEncoder("number"), // decoder will fail for NaN
"boolean": getGenericEncoder(),
"object": getGenericEncoder(),
"string": {
"encode": function(key){
return collations.indexOf("string") + "-" + key;
},
"decode": function(key){
return "" + key.substring(2);
}
},
"undefined": {
"encode": function(key){
return collations.indexOf("undefined") + "-undefined";
},
"decode": function(key){
return undefined;
}
}
};
var Key = (function(){
return {
encode: function(key){
return types[typeof key].encode(key);
},
decode: function(key){
return types[collations[key.substring(0, 1)]].decode(key);
}
};
}());
idbModules.Key = Key;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules, undefined){
// The event interface used for IndexedBD Actions.
var Event = function(type, debug){
// Returning an object instead of an even as the event's target cannot be set to IndexedDB Objects
// We still need to have event.target.result as the result of the IDB request
return {
"type": type,
debug: debug,
bubbles: false,
cancelable: false,
eventPhase: 0,
timeStamp: new Date()
};
};
idbModules.Event = Event;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules){
/**
* The IDBRequest Object that is returns for all async calls
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#request-api
*/
var IDBRequest = function(){
this.onsuccess = this.onerror = this.result = this.error = this.source = this.transaction = null;
this.readyState = "pending";
};
/**
* The IDBOpen Request called when a database is opened
*/
var IDBOpenRequest = function(){
this.onblocked = this.onupgradeneeded = null;
};
IDBOpenRequest.prototype = IDBRequest;
idbModules.IDBRequest = IDBRequest;
idbModules.IDBOpenRequest = IDBOpenRequest;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules, undefined){
/**
* The IndexedDB KeyRange object
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#dfn-key-range
* @param {Object} lower
* @param {Object} upper
* @param {Object} lowerOpen
* @param {Object} upperOpen
*/
var IDBKeyRange = function(lower, upper, lowerOpen, upperOpen){
this.lower = lower;
this.upper = upper;
this.lowerOpen = lowerOpen;
this.upperOpen = upperOpen;
};
IDBKeyRange.only = function(value){
return new IDBKeyRange(value, value, false, false);
};
IDBKeyRange.lowerBound = function(value, open){
return new IDBKeyRange(value, undefined, open, undefined);
};
IDBKeyRange.upperBound = function(value){
return new IDBKeyRange(undefined, value, undefined, open);
};
IDBKeyRange.bound = function(lower, upper, lowerOpen, upperOpen){
return new IDBKeyRange(lower, upper, lowerOpen, upperOpen);
};
idbModules.IDBKeyRange = IDBKeyRange;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules, undefined){
/**
* The IndexedDB Cursor Object
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBCursor
* @param {Object} range
* @param {Object} direction
* @param {Object} idbObjectStore
* @param {Object} cursorRequest
*/
function IDBCursor(range, direction, idbObjectStore, cursorRequest, keyColumnName, valueColumnName){
this.__range = range;
this.source = this.__idbObjectStore = idbObjectStore;
this.__req = cursorRequest;
this.key = undefined;
this.direction = direction;
this.__keyColumnName = keyColumnName;
this.__valueColumnName = valueColumnName;
if (!this.source.transaction.__active) {
idbModules.util.throwDOMException("TransactionInactiveError - The transaction this IDBObjectStore belongs to is not active.");
}
// Setting this to -1 as continue will set it to 0 anyway
this.__offset = -1;
this.__lastKeyContinued = undefined; // Used when continuing with a key
this["continue"]();
}
IDBCursor.prototype.__find = function(key, tx, success, error){
var me = this;
var sql = ["SELECT * FROM ", idbModules.util.quote(me.__idbObjectStore.name)];
var sqlValues = [];
sql.push("WHERE ", me.__keyColumnName, " NOT NULL");
if (me.__range && (me.__range.lower || me.__range.upper)) {
sql.push("AND");
if (me.__range.lower) {
sql.push(me.__keyColumnName + (me.__range.lowerOpen ? " >" : " >= ") + " ?");
sqlValues.push(idbModules.Key.encode(me.__range.lower));
}
(me.__range.lower && me.__range.upper) && sql.push("AND");
if (me.__range.upper) {
sql.push(me.__keyColumnName + (me.__range.upperOpen ? " < " : " <= ") + " ?");
sqlValues.push(idbModules.Key.encode(me.__range.upper));
}
}
if (typeof key !== "undefined") {
me.__lastKeyContinued = key;
me.__offset = 0;
}
if (me.__lastKeyContinued !== undefined) {
sql.push("AND " + me.__keyColumnName + " >= ?");
sqlValues.push(idbModules.Key.encode(me.__lastKeyContinued));
}
sql.push("ORDER BY ", me.__keyColumnName);
sql.push("LIMIT 1 OFFSET " + me.__offset);
idbModules.DEBUG && console.log(sql.join(" "), sqlValues);
tx.executeSql(sql.join(" "), sqlValues, function(tx, data){
if (data.rows.length === 1) {
var key = idbModules.Key.decode(data.rows.item(0)[me.__keyColumnName]);
var val = me.__valueColumnName === "value" ? idbModules.Sca.decode(data.rows.item(0)[me.__valueColumnName]) : idbModules.Key.decode(data.rows.item(0)[me.__valueColumnName]);
success(key, val);
}
else {
idbModules.DEBUG && console.log("Reached end of cursors");
success(undefined, undefined);
}
}, function(tx, data){
idbModules.DEBUG && console.log("Could not execute Cursor.continue");
error(data);
});
};
IDBCursor.prototype["continue"] = function(key){
var me = this;
this.__idbObjectStore.transaction.__addToTransactionQueue(function(tx, args, success, error){
me.__offset++;
me.__find(key, tx, function(key, val){
me.key = key;
me.value = val;
success(typeof me.key !== "undefined" ? me : undefined, me.__req);
}, function(data){
error(data);
});
});
};
IDBCursor.prototype.advance = function(count){
if (count <= 0) {
idbModules.util.throwDOMException("Type Error - Count is invalid - 0 or negative", count);
}
var me = this;
this.__idbObjectStore.transaction.__addToTransactionQueue(function(tx, args, success, error){
me.__offset += count;
me.__find(undefined, tx, function(key, value){
me.key = key;
me.value = value;
success(typeof me.key !== "undefined" ? me : undefined, me.__req);
}, function(data){
error(data);
});
});
};
IDBCursor.prototype.update = function(valueToUpdate){
var me = this,
request = this.__idbObjectStore.transaction.__createRequest(function(){}); //Stub request
idbModules.Sca.encode(valueToUpdate, function(encoded) {
this.__idbObjectStore.__pushToQueue(request, function(tx, args, success, error){
me.__find(undefined, tx, function(key, value){
var sql = "UPDATE " + idbModules.util.quote(me.__idbObjectStore.name) + " SET value = ? WHERE key = ?";
idbModules.DEBUG && console.log(sql, encoded, key);
tx.executeSql(sql, [idbModules.Sca.encode(encoded), idbModules.Key.encode(key)], function(tx, data){
if (data.rowsAffected === 1) {
success(key);
}
else {
error("No rowns with key found" + key);
}
}, function(tx, data){
error(data);
});
}, function(data){
error(data);
});
});
});
return request;
};
IDBCursor.prototype["delete"] = function(){
var me = this;
return this.__idbObjectStore.transaction.__addToTransactionQueue(function(tx, args, success, error){
me.__find(undefined, tx, function(key, value){
var sql = "DELETE FROM " + idbModules.util.quote(me.__idbObjectStore.name) + " WHERE key = ?";
idbModules.DEBUG && console.log(sql, key);
tx.executeSql(sql, [idbModules.Key.encode(key)], function(tx, data){
if (data.rowsAffected === 1) {
success(undefined);
}
else {
error("No rowns with key found" + key);
}
}, function(tx, data){
error(data);
});
}, function(data){
error(data);
});
});
};
idbModules.IDBCursor = IDBCursor;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules, undefined){
/**
* IDB Index
* http://www.w3.org/TR/IndexedDB/#idl-def-IDBIndex
* @param {Object} name;
* @param {Object} objectStore;
*/
function IDBIndex(indexName, idbObjectStore){
this.indexName = this.name = indexName;
this.__idbObjectStore = this.objectStore = this.source = idbObjectStore;
var indexList = idbObjectStore.__storeProps && idbObjectStore.__storeProps.indexList;
indexList && (indexList = JSON.parse(indexList));
this.keyPath = ((indexList && indexList[indexName] && indexList[indexName].keyPath) || indexName);
['multiEntry','unique'].forEach(function(prop){
this[prop] = !!indexList && !!indexList[indexName] && !!indexList[indexName].optionalParams && !!indexList[indexName].optionalParams[prop];
}, this);
}
IDBIndex.prototype.__createIndex = function(indexName, keyPath, optionalParameters){
var me = this;
var transaction = me.__idbObjectStore.transaction;
transaction.__addToTransactionQueue(function(tx, args, success, failure){
me.__idbObjectStore.__getStoreProps(tx, function(){
function error(){
idbModules.util.throwDOMException(0, "Could not create new index", arguments);
}
if (transaction.mode !== 2) {
idbModules.util.throwDOMException(0, "Invalid State error, not a version transaction", me.transaction);
}
var idxList = JSON.parse(me.__idbObjectStore.__storeProps.indexList);
if (typeof idxList[indexName] !== "undefined") {
idbModules.util.throwDOMException(0, "Index already exists on store", idxList);
}
var columnName = indexName;
idxList[indexName] = {
"columnName": columnName,
"keyPath": keyPath,
"optionalParams": optionalParameters
};
// For this index, first create a column
me.__idbObjectStore.__storeProps.indexList = JSON.stringify(idxList);
var sql = ["ALTER TABLE", idbModules.util.quote(me.__idbObjectStore.name), "ADD", columnName, "BLOB"].join(" ");
idbModules.DEBUG && console.log(sql);
tx.executeSql(sql, [], function(tx, data){
// Once a column is created, put existing records into the index
tx.executeSql("SELECT * FROM " + idbModules.util.quote(me.__idbObjectStore.name), [], function(tx, data){
(function initIndexForRow(i){
if (i < data.rows.length) {
try {
var value = idbModules.Sca.decode(data.rows.item(i).value);
var indexKey = eval("value['" + keyPath + "']");
tx.executeSql("UPDATE " + idbModules.util.quote(me.__idbObjectStore.name) + " set " + columnName + " = ? where key = ?", [idbModules.Key.encode(indexKey), data.rows.item(i).key], function(tx, data){
initIndexForRow(i + 1);
}, error);
}
catch (e) {
// Not a valid value to insert into index, so just continue
initIndexForRow(i + 1);
}
}
else {
idbModules.DEBUG && console.log("Updating the indexes in table", me.__idbObjectStore.__storeProps);
tx.executeSql("UPDATE __sys__ set indexList = ? where name = ?", [me.__idbObjectStore.__storeProps.indexList, me.__idbObjectStore.name], function(){
me.__idbObjectStore.__setReadyState("createIndex", true);
success(me);
}, error);
}
}(0));
}, error);
}, error);
}, "createObjectStore");
});
};
IDBIndex.prototype.openCursor = function(range, direction){
var cursorRequest = new idbModules.IDBRequest();
var cursor = new idbModules.IDBCursor(range, direction, this.source, cursorRequest, this.indexName, "value");
return cursorRequest;
};
IDBIndex.prototype.openKeyCursor = function(range, direction){
var cursorRequest = new idbModules.IDBRequest();
var cursor = new idbModules.IDBCursor(range, direction, this.source, cursorRequest, this.indexName, "key");
return cursorRequest;
};
IDBIndex.prototype.__fetchIndexData = function(key, opType){
var me = this;
return me.__idbObjectStore.transaction.__addToTransactionQueue(function(tx, args, success, error){
var sql = ["SELECT * FROM ", idbModules.util.quote(me.__idbObjectStore.name), " WHERE", me.indexName, "NOT NULL"];
var sqlValues = [];
if (typeof key !== "undefined") {
sql.push("AND", me.indexName, " = ?");
sqlValues.push(idbModules.Key.encode(key));
}
idbModules.DEBUG && console.log("Trying to fetch data for Index", sql.join(" "), sqlValues);
tx.executeSql(sql.join(" "), sqlValues, function(tx, data){
var d;
if (typeof opType === "count") {
d = data.rows.length;
}
else
if (data.rows.length === 0) {
d = undefined;
}
else
if (opType === "key") {
d = idbModules.Key.decode(data.rows.item(0).key);
}
else { // when opType is value
d = idbModules.Sca.decode(data.rows.item(0).value);
}
success(d);
}, error);
});
};
IDBIndex.prototype.get = function(key){
return this.__fetchIndexData(key, "value");
};
IDBIndex.prototype.getKey = function(key){
return this.__fetchIndexData(key, "key");
};
IDBIndex.prototype.count = function(key){
return this.__fetchIndexData(key, "count");
};
idbModules.IDBIndex = IDBIndex;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules){
/**
* IndexedDB Object Store
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBObjectStore
* @param {Object} name
* @param {Object} transaction
*/
var IDBObjectStore = function(name, idbTransaction, ready){
this.name = name;
this.transaction = idbTransaction;
this.__ready = {};
this.__setReadyState("createObjectStore", typeof ready === "undefined" ? true : ready);
this.indexNames = new idbModules.util.StringList();
};
/**
* Need this flag as createObjectStore is synchronous. So, we simply return when create ObjectStore is called
* but do the processing in the background. All other operations should wait till ready is set
* @param {Object} val
*/
IDBObjectStore.prototype.__setReadyState = function(key, val){
this.__ready[key] = val;
};
/**
* Called by all operations on the object store, waits till the store is ready, and then performs the operation
* @param {Object} callback
*/
IDBObjectStore.prototype.__waitForReady = function(callback, key){
var ready = true;
if (typeof key !== "undefined") {
ready = (typeof this.__ready[key] === "undefined") ? true : this.__ready[key];
}
else {
for (var x in this.__ready) {
if (!this.__ready[x]) {
ready = false;
}
}
}
if (ready) {
callback();
}
else {
idbModules.DEBUG && console.log("Waiting for to be ready", key);
var me = this;
window.setTimeout(function(){
me.__waitForReady(callback, key);
}, 100);
}
};
/**
* Gets (and optionally caches) the properties like keyPath, autoincrement, etc for this objectStore
* @param {Object} callback
*/
IDBObjectStore.prototype.__getStoreProps = function(tx, callback, waitOnProperty){
var me = this;
this.__waitForReady(function(){
if (me.__storeProps) {
idbModules.DEBUG && console.log("Store properties - cached", me.__storeProps);
callback(me.__storeProps);
}
else {
tx.executeSql("SELECT * FROM __sys__ where name = ?", [me.name], function(tx, data){
if (data.rows.length !== 1) {
callback();
}
else {
me.__storeProps = {
"name": data.rows.item(0).name,
"indexList": data.rows.item(0).indexList,
"autoInc": data.rows.item(0).autoInc,
"keyPath": data.rows.item(0).keyPath
};
idbModules.DEBUG && console.log("Store properties", me.__storeProps);
callback(me.__storeProps);
}
}, function(){
callback();
});
}
}, waitOnProperty);
};
/**
* From the store properties and object, extracts the value for the key in hte object Store
* If the table has auto increment, get the next in sequence
* @param {Object} props
* @param {Object} value
* @param {Object} key
*/
IDBObjectStore.prototype.__deriveKey = function(tx, value, key, callback){
function getNextAutoIncKey(){
tx.executeSql("SELECT * FROM sqlite_sequence where name like ?", [me.name], function(tx, data){
if (data.rows.length !== 1) {
callback(0);
}
else {
callback(data.rows.item(0).seq);
}
}, function(tx, error){
idbModules.util.throwDOMException(0, "Data Error - Could not get the auto increment value for key", error);
});
}
var me = this;
me.__getStoreProps(tx, function(props){
if (!props) {
idbModules.util.throwDOMException(0, "Data Error - Could not locate defination for this table", props);
}
if (props.keyPath) {
if (typeof key !== "undefined") {
idbModules.util.throwDOMException(0, "Data Error - The object store uses in-line keys and the key parameter was provided", props);
}
if (value) {
try {
var primaryKey = eval("value['" + props.keyPath + "']");
if (!primaryKey) {
if (props.autoInc === "true") {
getNextAutoIncKey();
}
else {
idbModules.util.throwDOMException(0, "Data Error - Could not eval key from keyPath");
}
}
else {
callback(primaryKey);
}
}
catch (e) {
idbModules.util.throwDOMException(0, "Data Error - Could not eval key from keyPath", e);
}
}
else {
idbModules.util.throwDOMException(0, "Data Error - KeyPath was specified, but value was not");
}
}
else {
if (typeof key !== "undefined") {
callback(key);
}
else {
if (props.autoInc === "false") {
idbModules.util.throwDOMException(0, "Data Error - The object store uses out-of-line keys and has no key generator and the key parameter was not provided. ", props);
}
else {
// Looks like this has autoInc, so lets get the next in sequence and return that.
getNextAutoIncKey();
}
}
}
});
};
IDBObjectStore.prototype.__insertData = function(tx, value, primaryKey, success, error){
var paramMap = {};
if (typeof primaryKey !== "undefined") {
paramMap.key = idbModules.Key.encode(primaryKey);
}
var indexes = JSON.parse(this.__storeProps.indexList);
for (var key in indexes) {
try {
paramMap[indexes[key].columnName] = idbModules.Key.encode(eval("JSON.parse(value)['" + indexes[key].keyPath + "']"));
}
catch (e) {
error(e);
}
}
var sqlStart = ["INSERT INTO ", idbModules.util.quote(this.name), "("];
var sqlEnd = [" VALUES ("];
var sqlValues = [];
for (key in paramMap) {
sqlStart.push(key + ",");
sqlEnd.push("?,");
sqlValues.push(paramMap[key]);
}
// removing the trailing comma
sqlStart.push("value )");
sqlEnd.push("?)");
sqlValues.push(value);
var sql = sqlStart.join(" ") + sqlEnd.join(" ");
idbModules.DEBUG && console.log("SQL for adding", sql, sqlValues);
tx.executeSql(sql, sqlValues, function(tx, data){
success(primaryKey);
}, function(tx, err){
error(err);
});
};
IDBObjectStore.prototype.add = function(value, key){
var me = this,
request = me.transaction.__createRequest(function(){}); //Stub request
idbModules.Sca.encode(value, function(encoded) {
me.transaction.__pushToQueue(request, function(tx, args, success, error){
me.__deriveKey(tx, value, key, function(primaryKey){
me.__insertData(tx, encoded, primaryKey, success, error);
});
});
});
return request;
};
IDBObjectStore.prototype.put = function(value, key){
var me = this,
request = me.transaction.__createRequest(function(){}); //Stub request
idbModules.Sca.encode(value, function(encoded) {
me.transaction.__pushToQueue(request, function(tx, args, success, error){
me.__deriveKey(tx, value, key, function(primaryKey){
// First try to delete if the record exists
var sql = "DELETE FROM " + idbModules.util.quote(me.name) + " where key = ?";
tx.executeSql(sql, [idbModules.Key.encode(primaryKey)], function(tx, data){
idbModules.DEBUG && console.log("Did the row with the", primaryKey, "exist? ", data.rowsAffected);
me.__insertData(tx, encoded, primaryKey, success, error);
}, function(tx, err){
error(err);
});
});
});
});
return request;
};
IDBObjectStore.prototype.get = function(key){
// TODO Key should also be a key range
var me = this;
return me.transaction.__addToTransactionQueue(function(tx, args, success, error){
me.__waitForReady(function(){
var primaryKey = idbModules.Key.encode(key);
idbModules.DEBUG && console.log("Fetching", me.name, primaryKey);
tx.executeSql("SELECT * FROM " + idbModules.util.quote(me.name) + " where key = ?", [primaryKey], function(tx, data){
idbModules.DEBUG && console.log("Fetched data", data);
try {
// Opera can't deal with the try-catch here.
if (0 === data.rows.length) {
return success();
}
success(idbModules.Sca.decode(data.rows.item(0).value));
}
catch (e) {
idbModules.DEBUG && console.log(e);
// If no result is returned, or error occurs when parsing JSON
success(undefined);
}
}, function(tx, err){
error(err);
});
});
});
};
IDBObjectStore.prototype["delete"] = function(key){
// TODO key should also support key ranges
var me = this;
return me.transaction.__addToTransactionQueue(function(tx, args, success, error){
me.__waitForReady(function(){
var primaryKey = idbModules.Key.encode(key);
idbModules.DEBUG && console.log("Fetching", me.name, primaryKey);
tx.executeSql("DELETE FROM " + idbModules.util.quote(me.name) + " where key = ?", [primaryKey], function(tx, data){
idbModules.DEBUG && console.log("Deleted from database", data.rowsAffected);
success();
}, function(tx, err){
error(err);
});
});
});
};
IDBObjectStore.prototype.clear = function(){
var me = this;
return me.transaction.__addToTransactionQueue(function(tx, args, success, error){
me.__waitForReady(function(){
tx.executeSql("DELETE FROM " + idbModules.util.quote(me.name), [], function(tx, data){
idbModules.DEBUG && console.log("Cleared all records from database", data.rowsAffected);
success();
}, function(tx, err){
error(err);
});
});
});
};
IDBObjectStore.prototype.count = function(key){
var me = this;
return me.transaction.__addToTransactionQueue(function(tx, args, success, error){
me.__waitForReady(function(){
var sql = "SELECT * FROM " + idbModules.util.quote(me.name) + ((typeof key !== "undefined") ? " WHERE key = ?" : "");
var sqlValues = [];
(typeof key !== "undefined") && sqlValues.push(idbModules.Key.encode(key));
tx.executeSql(sql, sqlValues, function(tx, data){
success(data.rows.length);
}, function(tx, err){
error(err);
});
});
});
};
IDBObjectStore.prototype.openCursor = function(range, direction){
var cursorRequest = new idbModules.IDBRequest();
var cursor = new idbModules.IDBCursor(range, direction, this, cursorRequest, "key", "value");
return cursorRequest;
};
IDBObjectStore.prototype.index = function(indexName){
var index = new idbModules.IDBIndex(indexName, this);
return index;
};
IDBObjectStore.prototype.createIndex = function(indexName, keyPath, optionalParameters){
var me = this;
optionalParameters = optionalParameters || {};
me.__setReadyState("createIndex", false);
var result = new idbModules.IDBIndex(indexName, me);
me.__waitForReady(function(){
result.__createIndex(indexName, keyPath, optionalParameters);
}, "createObjectStore");
me.indexNames.push(indexName);
return result;
};
IDBObjectStore.prototype.deleteIndex = function(indexName){
var result = new idbModules.IDBIndex(indexName, this, false);
result.__deleteIndex(indexName);
return result;
};
idbModules.IDBObjectStore = IDBObjectStore;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules){
/**
* The IndexedDB Transaction
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBTransaction
* @param {Object} storeNames
* @param {Object} mode
* @param {Object} db
*/
var READ = 0;
var READ_WRITE = 1;
var VERSION_TRANSACTION = 2;
var IDBTransaction = function(storeNames, mode, db){
if (typeof mode === "number") {
this.mode = mode;
(mode !== 2) && idbModules.DEBUG && console.log("Mode should be a string, but was specified as ", mode);
}
else
if (typeof mode === "string") {
switch (mode) {
case "readwrite":
this.mode = READ_WRITE;
break;
case "readonly":
this.mode = READ;
break;
default:
this.mode = READ;
break;
}
}
this.storeNames = typeof storeNames === "string" ? [storeNames] : storeNames;
for (var i = 0; i < this.storeNames.length; i++) {
if (!db.objectStoreNames.contains(this.storeNames[i])) {
idbModules.util.throwDOMException(0, "The operation failed because the requested database object could not be found. For example, an object store did not exist but was being opened.", this.storeNames[i]);
}
}
this.__active = true;
this.__running = false;
this.__requests = [];
this.__aborted = false;
this.db = db;
this.error = null;
this.onabort = this.onerror = this.oncomplete = null;
var me = this;
};
IDBTransaction.prototype.__executeRequests = function(){
if (this.__running && this.mode !== VERSION_TRANSACTION) {
idbModules.DEBUG && console.log("Looks like the request set is already running", this.mode);
return;
}
this.__running = true;
var me = this;
window.setTimeout(function(){
if (me.mode !== 2 && !me.__active) {
idbModules.util.throwDOMException(0, "A request was placed against a transaction which is currently not active, or which is finished", me.__active);
}
// Start using the version transaction
me.db.__db.transaction(function(tx){
me.__tx = tx;
var q = null, i = 0;
function success(result, req){
if (req) {
q.req = req;// Need to do this in case of cursors
}
q.req.readyState = "done";
q.req.result = result;
delete q.req.error;
var e = idbModules.Event("success");
idbModules.util.callback("onsuccess", q.req, e);
i++;
executeRequest();
}
function error(errorVal){
q.req.readyState = "done";
q.req.error = "DOMError";
var e = idbModules.Event("error", arguments);
idbModules.util.callback("onerror", q.req, e);
i++;
executeRequest();
}
function executeRequest(){
if (i >= me.__requests.length) {
me.__active = false; // All requests in the transaction is done
me.__requests = [];
return;
}
q = me.__requests[i];
q.op(tx, q.args, success, error);
}
try {
executeRequest();
}
catch (e) {
idbModules.DEBUG && console.log("An exception occured in transaction", arguments);
typeof me.onerror === "function" && me.onerror();
}
}, function(){
idbModules.DEBUG && console.log("An error in transaction", arguments);
typeof me.onerror === "function" && me.onerror();
}, function(){
idbModules.DEBUG && console.log("Transaction completed", arguments);
typeof me.oncomplete === "function" && me.oncomplete();
});
}, 1);
};
IDBTransaction.prototype.__addToTransactionQueue = function(callback, args){
if (!this.__active && this.mode !== VERSION_TRANSACTION) {
idbModules.util.throwDOMException(0, "A request was placed against a transaction which is currently not active, or which is finished.", this.__mode);
}
var request = this.__createRequest();
this.__pushToQueue(request, callback, args);
return request;
};
IDBTransaction.prototype.__createRequest = function(){
var request = new idbModules.IDBRequest();
request.source = this.db;
return request;
};
IDBTransaction.prototype.__pushToQueue = function(request, callback, args) {
this.__requests.push({
"op": callback,
"args": args,
"req": request
});
// Start the queue for executing the requests
this.__executeRequests();
};
IDBTransaction.prototype.objectStore = function(objectStoreName){
return new idbModules.IDBObjectStore(objectStoreName, this);
};
IDBTransaction.prototype.abort = function(){
!this.__active && idbModules.util.throwDOMException(0, "A request was placed against a transaction which is currently not active, or which is finished", this.__active);
};
IDBTransaction.prototype.READ_ONLY = 0;
IDBTransaction.prototype.READ_WRITE = 1;
IDBTransaction.prototype.VERSION_CHANGE = 2;
idbModules.IDBTransaction = IDBTransaction;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules){
/**
* IDB Database Object
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#database-interface
* @param {Object} db
*/
var IDBDatabase = function(db, name, version, storeProperties){
this.__db = db;
this.version = version;
this.__storeProperties = storeProperties;
this.objectStoreNames = new idbModules.util.StringList();
for (var i = 0; i < storeProperties.rows.length; i++) {
this.objectStoreNames.push(storeProperties.rows.item(i).name);
}
this.name = name;
this.onabort = this.onerror = this.onversionchange = null;
};
IDBDatabase.prototype.createObjectStore = function(storeName, createOptions){
var me = this;
createOptions = createOptions || {};
createOptions.keyPath = createOptions.keyPath || null;
var result = new idbModules.IDBObjectStore(storeName, me.__versionTransaction, false);
var transaction = me.__versionTransaction;
transaction.__addToTransactionQueue(function(tx, args, success, failure){
function error(){
idbModules.util.throwDOMException(0, "Could not create new object store", arguments);
}
if (!me.__versionTransaction) {
idbModules.util.throwDOMException(0, "Invalid State error", me.transaction);
}
//key INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE
var sql = ["CREATE TABLE", idbModules.util.quote(storeName), "(key BLOB", createOptions.autoIncrement ? ", inc INTEGER PRIMARY KEY AUTOINCREMENT" : "PRIMARY KEY", ", value BLOB)"].join(" ");
idbModules.DEBUG && console.log(sql);
tx.executeSql(sql, [], function(tx, data){
tx.executeSql("INSERT INTO __sys__ VALUES (?,?,?,?)", [storeName, createOptions.keyPath, createOptions.autoIncrement ? true : false, "{}"], function(){
result.__setReadyState("createObjectStore", true);
success(result);
}, error);
}, error);
});
// The IndexedDB Specification needs us to return an Object Store immediatly, but WebSQL does not create and return the store immediatly
// Hence, this can technically be unusable, and we hack around it, by setting the ready value to false
me.objectStoreNames.push(storeName);
return result;
};
IDBDatabase.prototype.deleteObjectStore = function(storeName){
var error = function(){
idbModules.util.throwDOMException(0, "Could not delete ObjectStore", arguments);
};
var me = this;
!me.objectStoreNames.contains(storeName) && error("Object Store does not exist");
me.objectStoreNames.splice(me.objectStoreNames.indexOf(storeName), 1);
var transaction = me.__versionTransaction;
transaction.__addToTransactionQueue(function(tx, args, success, failure){
if (!me.__versionTransaction) {
idbModules.util.throwDOMException(0, "Invalid State error", me.transaction);
}
me.__db.transaction(function(tx){
tx.executeSql("SELECT * FROM __sys__ where name = ?", [storeName], function(tx, data){
if (data.rows.length > 0) {
tx.executeSql("DROP TABLE " + idbModules.util.quote(storeName), [], function(){
tx.executeSql("DELETE FROM __sys__ WHERE name = ?", [storeName], function(){
}, error);
}, error);
}
});
});
});
};
IDBDatabase.prototype.close = function(){
// Don't do anything coz the database automatically closes
};
IDBDatabase.prototype.transaction = function(storeNames, mode){
var transaction = new idbModules.IDBTransaction(storeNames, mode || 1, this);
return transaction;
};
idbModules.IDBDatabase = IDBDatabase;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(idbModules){
var DEFAULT_DB_SIZE = 4 * 1024 * 1024;
if (!window.openDatabase) {
return;
}
// The sysDB to keep track of version numbers for databases
var sysdb = window.openDatabase("__sysdb__", 1, "System Database", DEFAULT_DB_SIZE);
sysdb.transaction(function(tx){
tx.executeSql("SELECT * FROM dbVersions", [], function(t, data){
// dbVersions already exists
}, function(){
// dbVersions does not exist, so creating it
sysdb.transaction(function(tx){
tx.executeSql("CREATE TABLE IF NOT EXISTS dbVersions (name VARCHAR(255), version INT);", [], function(){
}, function(){
idbModules.util.throwDOMException("Could not create table __sysdb__ to save DB versions");
});
});
});
}, function(){
// sysdb Transaction failed
idbModules.DEBUG && console.log("Error in sysdb transaction - when selecting from dbVersions", arguments);
});
var shimIndexedDB = {
/**
* The IndexedDB Method to create a new database and return the DB
* @param {Object} name
* @param {Object} version
*/
open: function(name, version){
var req = new idbModules.IDBOpenRequest();
var calledDbCreateError = false;
function dbCreateError(){
if (calledDbCreateError) {
return;
}
var e = idbModules.Event("error", arguments);
req.readyState = "done";
req.error = "DOMError";
idbModules.util.callback("onerror", req, e);
calledDbCreateError = true;
}
function openDB(oldVersion){
var db = window.openDatabase(name, 1, name, DEFAULT_DB_SIZE);
req.readyState = "done";
if (typeof version === "undefined") {
version = oldVersion || 1;
}
if (version <= 0 || oldVersion > version) {
idbModules.util.throwDOMException(0, "An attempt was made to open a database using a lower version than the existing version.", version);
}
db.transaction(function(tx){
tx.executeSql("CREATE TABLE IF NOT EXISTS __sys__ (name VARCHAR(255), keyPath VARCHAR(255), autoInc BOOLEAN, indexList BLOB)", [], function(){
tx.executeSql("SELECT * FROM __sys__", [], function(tx, data){
var e = idbModules.Event("success");
req.source = req.result = new idbModules.IDBDatabase(db, name, version, data);
if (oldVersion < version) {
// DB Upgrade in progress
sysdb.transaction(function(systx){
systx.executeSql("UPDATE dbVersions set version = ? where name = ?", [version, name], function(){
var e = idbModules.Event("upgradeneeded");
e.oldVersion = oldVersion;
e.newVersion = version;
req.transaction = req.result.__versionTransaction = new idbModules.IDBTransaction([], 2, req.source);
idbModules.util.callback("onupgradeneeded", req, e, function(){
var e = idbModules.Event("success");
idbModules.util.callback("onsuccess", req, e);
});
}, dbCreateError);
}, dbCreateError);
} else {
idbModules.util.callback("onsuccess", req, e);
}
}, dbCreateError);
}, dbCreateError);
}, dbCreateError);
}
sysdb.transaction(function(tx){
tx.executeSql("SELECT * FROM dbVersions where name = ?", [name], function(tx, data){
if (data.rows.length === 0) {
// Database with this name does not exist
tx.executeSql("INSERT INTO dbVersions VALUES (?,?)", [name, version || 1], function(){
openDB(0);
}, dbCreateError);
} else {
openDB(data.rows.item(0).version);
}
}, dbCreateError);
}, dbCreateError);
return req;
},
"deleteDatabase": function(name){
var req = new idbModules.IDBOpenRequest();
var calledDBError = false;
function dbError(msg){
if (calledDBError) {
return;
}
req.readyState = "done";
req.error = "DOMError";
var e = idbModules.Event("error");
e.message = msg;
e.debug = arguments;
idbModules.util.callback("onerror", req, e);
calledDBError = true;
}
var version = null;
function deleteFromDbVersions(){
sysdb.transaction(function(systx){
systx.executeSql("DELETE FROM dbVersions where name = ? ", [name], function(){
req.result = undefined;
var e = idbModules.Event("success");
e.newVersion = null;
e.oldVersion = version;
idbModules.util.callback("onsuccess", req, e);
}, dbError);
}, dbError);
}
sysdb.transaction(function(systx){
systx.executeSql("SELECT * FROM dbVersions where name = ?", [name], function(tx, data){
if (data.rows.length === 0) {
req.result = undefined;
var e = idbModules.Event("success");
e.newVersion = null;
e.oldVersion = version;
idbModules.util.callback("onsuccess", req, e);
return;
}
version = data.rows.item(0).version;
var db = window.openDatabase(name, 1, name, DEFAULT_DB_SIZE);
db.transaction(function(tx){
tx.executeSql("SELECT * FROM __sys__", [], function(tx, data){
var tables = data.rows;
(function deleteTables(i){
if (i >= tables.length) {
// If all tables are deleted, delete the housekeeping tables
tx.executeSql("DROP TABLE __sys__", [], function(){
// Finally, delete the record for this DB from sysdb
deleteFromDbVersions();
}, dbError);
} else {
// Delete all tables in this database, maintained in the sys table
tx.executeSql("DROP TABLE " + idbModules.util.quote(tables.item(i).name), [], function(){
deleteTables(i + 1);
}, function(){
deleteTables(i + 1);
});
}
}(0));
}, function(e){
// __sysdb table does not exist, but that does not mean delete did not happen
deleteFromDbVersions();
});
}, dbError);
});
}, dbError);
return req;
},
"cmp": function(key1, key2){
return idbModules.Key.encode(key1) > idbModules.Key.encode(key2) ? 1 : key1 === key2 ? 0 : -1;
}
};
idbModules.shimIndexedDB = shimIndexedDB;
}(idbModules));
/*jshint globalstrict: true*/
'use strict';
(function(window, idbModules){
if (typeof window.openDatabase !== "undefined") {
window.shimIndexedDB = idbModules.shimIndexedDB;
if (window.shimIndexedDB) {
window.shimIndexedDB.__useShim = function(){
window.indexedDB = idbModules.shimIndexedDB;
window.IDBDatabase = idbModules.IDBDatabase;
window.IDBTransaction = idbModules.IDBTransaction;
window.IDBCursor = idbModules.IDBCursor;
window.IDBKeyRange = idbModules.IDBKeyRange;
};
window.shimIndexedDB.__debug = function(val){
idbModules.DEBUG = val;
};
}
}
window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.oIndexedDB || window.msIndexedDB;
if (typeof window.indexedDB === "undefined" && typeof window.openDatabase !== "undefined") {
window.shimIndexedDB.__useShim();
}
else {
window.IDBDatabase = window.IDBDatabase || window.webkitIDBDatabase;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;
window.IDBCursor = window.IDBCursor || window.webkitIDBCursor;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
if(!window.IDBTransaction){
window.IDBTransaction = {};
}
window.IDBTransaction.READ_ONLY = window.IDBTransaction.READ_ONLY || "readonly";
window.IDBTransaction.READ_WRITE = window.IDBTransaction.READ_WRITE || "readwrite";
}
}(window, idbModules));