//*********************************************************************************************************
//ixDbEz -     IndexedDB EZ is a js wrapper for IndexedDB providing rapid client-side development
//             of IndexedDB databases.
//
// Copyright (C) 2012 - Jake Drew
// Dual licensed under the MIT and GPL licenses.
//  - http://www.opensource.org/licenses/mit-license.php
//  - http://www.gnu.org/copyleft/gpl.html
//
//Created By - Jake Drew
//Version -    1.0, 07/16/2012
//Version -    2.0, 08/11/2012
//                  1.  Added Support for ixDbSync.
//                  2.  Made all error messages consistent, fixed error event capture.
//                  3.  Updated all data change functions to detect the IndexedDB API versionchange
//                      transaction and re-schedule themselves for the oncomplete event in the event that
//                      they are called during the version change transaction.
//                  4.  Added clear() function for the IndexedDB API clear method.
//                  5.  Added keyRange variable to getCursor() function to support IDBKeyRanges.
//                  6.  Added objectContainsProperty method to identify, if an object contains a property
//                      prior to adding the property to the object (used in ixDbEz and ixDbEzSync)
//                  7.  indexName was added to getCursor() function to provide support for opening index
//                      based cursors.
//*********************************************************************************************************
var ixDbEz = (function () {
    //Populate the window.indexedDB variables with the appropriate browser specific instance.
    window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
    window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.mozIDBTransaction || window.msIDBTransaction;
    window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.mozIDBKeyRange || window.msIDBKeyRange;

    var ixDb; //The current ixdb database instance being accessed in all functions below.
    var ixDbRequest; //The current ixdb request instance being accessed in all functions below.
    var ixDbVersionTansaction; //Holds a reference to a versionchange transaction object anytime a version change is in process.
    var ixDbVersionRequest;
    var ixDbSyncFlag;
    //*********************************************************************************************************
    //Function StartDB - Open or create the requested database and populate the variable ixDb with the new IndexedDB instance.
    //          dbName - Name of the IndexedDB database to open or create
    //       dbVersion - MUST be a valid integer. If not, the database is given a version number = 1.
    //         ixdbDDL - javascript var that contains a function with all the IndexedDB's valid ixDbEz DDL calls (see usage example)
    //       onSuccess - (optional) callback function to execute if function successful.
    //         onError - (optional) callback function to execute if function fails.
    //        ixDbSync - (optional) if true, fires ixDbSync events on all datachanges.
    //                   !!!only use the ixDbSync option for ixDbSync.js data server-side synchronization!!!
    //*********************************************************************************************************
    function StartDB_(dbName, dbVersion, ixdbDDL, onSuccess, onError, useIxDbSync) {
        //Check to see if we have a browser that supports IndexedDB
        if (window.indexedDB) {

            //Trigger data synchronization hooks, if ixDbSync is being used.
            ixDbSyncFlag = useIxDbSync;
            //Open or create the requested IndexedDB Database
            ixDbRequest = window.indexedDB.open(dbName, dbVersion);

            var newVersion = parseInt(dbVersion || 1);
            newVersion = isNaN(newVersion) || newVersion == null ? 1 : newVersion;

            ixDbRequest.onsuccess = function (e) {

                ixDb = ixDbRequest.result || e.result;  // FF4 requires e.result.

                //Check to see if a database upgrade is required.
                //This logic should work with Chrome until they catch up with Firefox and support onupgradeneeded event.
                //Also works on older browsers builds that still support setVersion
                if (typeof ixDb.setVersion === "function") {

                    var oldVersion = parseInt(ixDb.version || -1001);
                    oldVersion = isNaN(oldVersion) || oldVersion == null ? -1001 : oldVersion;

                    if (oldVersion < newVersion) {
                        ixDbVersionRequest = ixDb.setVersion(newVersion);
                        //Get a reference to the version request from the old setVersion method.
                        //ixDbVersionRequest = verRequest; //.result || e.currentTarget.result;

                        ixDbVersionRequest.onerror = ixDbEz.onerror;

                        ixDbVersionRequest.onsuccess = function (e) {
                            ixDbVersionTansaction = e.result || e.currentTarget.result;
                            //log successful database creation
                            console.log('ixDbEz: Created Database: ' + dbName + ',  Version: ' + newVersion + '.');
                            //Create database structure using function provided by the user.
                            ixdbDDL();
                            //Create any required ixDbSync database structures
                            if(ixDbSyncFlag) {
                                ixDbSync.syncDDL();
                            }

                            //must clear version request so getCursor callback works right after db creation
                            ixDbVersionRequest = undefined;
                            //destroy the version trasaction variable (since version change transactions lock the database)
                            ixDbVersionTansaction = undefined;
                        }
                    }
                    else {
                        //log successful database open
                        console.log('ixDbEz: Opened Database: ' + dbName + ',  Version: ' + newVersion + '.')
                    }
                }

                //execute onsuccess function, if one was provided
                if(typeof onSuccess === 'function') {
                    onSuccess();
                }

            };

            ixDbRequest.onerror = function (e) {
                logError(e, onError, ixDbVersionTansaction);
                console.log('ixDbEz Error: Opened Database: ' + dbName + ',  Version: ' + newVersion + ' failed.')
            };

            //The onupgradeneeded event is not yet supported by Chrome and requires a hook in the onsuccess event above.
            ixDbRequest.onupgradeneeded = function (e) {
                //FF uses this event to fire DDL function for upgrades.  All browsers will eventually use this method. Per - W3C Working Draft 24 May 2012
                ixDb = ixDbRequest.result || e.currentTarget.result;
                //Get a reference to the version transaction via the onupgradeneeded event (e)
                ixDbVersionTansaction = e.transaction || e.currentTarget.transaction;

                //Clear out upgrade flags as soon as the upgrade is completed.
                ixDbVersionTansaction.oncomplete = function (e) {
                    //must clear version request so getCursor callback works right after db creation
                    ixDbVersionRequest = undefined;
                    //destroy the version trasaction variable (since version change transactions lock the database)
                    ixDbVersionTansaction = undefined;
                };

                //log successful database creation
                console.log('ixDbEz: Created Database: ' + dbName + ',  Version: ' + newVersion + '.');
                //Create database using function provided by the user.
                ixdbDDL();
                //Create any required ixDbSync database structures
                if(ixDbSyncFlag) {
                    ixDbSync.syncDDL();
                }
            };
        }
    }

    //*********************************************************************************************************
    //Function CreateObjStore - Create IndexedDB object store (similar to a table)
    //       objectStoreName - Name of the Object Store / Table "MyOsName"
    //                 pkName - Keypath name (Similar to Primary Key)
    //          autoIncrement - true or false (assigns an autonumber to the primary key / Keypath value)
    //                          Default value = false.
    //               skipSync - (optional) If true, skips all data synchronization in ixDbSync for a single transaction.
    //*********************************************************************************************************
    function CreateObjStore_(objectStoreName, pkName, autoIncrement, skipSync) {
        //Create a default value for the autoIncrement variable
        autoIncrement = typeof autoIncrement === 'undefined' ? false : autoIncrement;
        var objectStore;

        try {
            objectStore = ixDb.createObjectStore(objectStoreName, { keyPath: pkName, autoIncrement: autoIncrement });

            //ixDbSync - data sync hook
            if(ixDbSyncFlag && objectStoreName != "ixDbSync" && skipSync != true) {
                ixDbSync.createObjStoreSync(objectStoreName, pkName, autoIncrement);
            }

            //Log os creation. onsuccess does not fire for objectStore!
            console.log('ixDbEz: Created ObjectStore ' + objectStoreName + '.');
        } catch (e) {
            logError(e);
            console.log('ixDbEz Error: Create ObjectStore ' + objectStoreName + ' failed.');
        }
        return objectStore;
    }

    //*********************************************************************************************************
    //Function CreateIndex - Create IndexedDB object store index (similar to a table index on a field)
    //     objectStoreName - Name of the Object Store / Table "MyOsName"
    //              ixName - Name of the Index to create
    //           fieldName - Keypath name to add the index too.  (Can the name of any property / field in the object store)
    //              unique - true or false, if True - all values in the index must be unique.
    //                       Default value = false.
    //          multiEntry - true or false, see w3 documentation: http://www.w3.org/TR/IndexedDB/#dfn-multientry
    //                       Default value = false.
    //            skipSync - (optional) If true, skips all data synchronization in ixDbSync for a single transaction.
    //*********************************************************************************************************
    function CreateIndex_(objectStoreName, ixName, fieldName, unique, multiEntry, skipSync) {
        //Create a default value for the autoIncrement variable
        unique = typeof unique === 'undefined' ? false : unique;
        multiEntry = typeof multiEntry === 'undefined' ? false : multiEntry;

        try {
            var ObjectStore = ixDbVersionTansaction.objectStore(objectStoreName);
            var index = ObjectStore.createIndex(ixName, fieldName, { unique: unique, multiEntry: multiEntry });

            //ixDbSync - data sync hook
            if(ixDbSyncFlag && objectStoreName != "ixDbSync" && skipSync != true) {
                ixDbSync.createIndexSync(objectStoreName, ixName, fieldName, unique, multiEntry);
            }

            //Log index creation. onsuccess does not fire for index!
            console.log('ixDbEz: Created index: ' + ixName + ' against keypath: ' + fieldName + '.');
        } catch (e) {
            logError(e);
            console.log('ixDbEz Error: Created index - ' + ixName + ' failed.');
        }
    }

    //*********************************************************************************************************
    //Function Add      - Insert a record into an object store.
    //  objectStoreName - Name of the Object Store / Table "MyOsName"
    //            value - Record object or value to insert.
    //              key - (optional) Key to access record.
    //        onSuccess - (optional) callback function to execute if function successful.
    //          onError - (optional) callback function to execute if function fails.
    //         skipSync - (optional) If true, skips all data synchronization in ixDbSync for a single transaction.
    //*********************************************************************************************************
    function Add_(objectStoreName, value, key, onSuccess, onError, skipSync) {
        if (ixDb) {

            //The database is being created or upgraded, re-run when completed.
            if(ixDbVersionTansaction) {
                ixDbVersionTansaction.addEventListener ("complete", function() { Add_(objectStoreName, value, key, onSuccess, onError, skipSync); }, false);
                return;
            }

            var transaction = ixDb.transaction(objectStoreName, "readwrite" ); //IDBTransaction.READ_WRITE);
            var objectStore = transaction.objectStore(objectStoreName);

            request = typeof key === 'undefined' ? objectStore.add(value) : objectStore.add(value, key);

            request.onsuccess = function (e) {
                if(typeof onSuccess === 'function') {
                    onSuccess();
                }

                //ixDbSync - data sync hook
                if(ixDbSyncFlag && objectStoreName != "ixDbSync" && skipSync != true) {
                    ixDbSync.addPutSync(objectStoreName, value, key, "Add");
                }

                console.log('ixDbEz: Created record in ObjectStore: ' + objectStoreName + ".");
            };


            request.onerror = function (e) {
                logError(e, onError);
                console.log('ixDbEz Error: Create record in ObjectStore: ' + objectStoreName + " failed.");
            }
        }
        else {
            //The database is in the middle of opening
            if (ixDbRequest) {
                ixDbRequest.addEventListener ("success", function() { Add_(objectStoreName, value, key, onSuccess, onError, skipSync); }, false);
            }
        }
    }

    //*********************************************************************************************************
    //Function Clear - Delete all records from an object store.
    //     onSuccess - (optional) callback function to execute if function successful.
    //       onError - (optional) callback function to execute if function fails.
    //      skipSync - (optional) If true, skips all data synchronization in ixDbSync for a single transaction.
    //*********************************************************************************************************
    function Clear_(objectStoreName, onSuccess, onError, skipSync) {
        if (ixDb) {
            //The database is being created or upgraded, re-run when completed.
            if(ixDbVersionTansaction) {
                ixDbVersionTansaction.addEventListener ("complete", function() { Clear_(objectStoreName, onSuccess, onError, skipSync) }, false);
                return;
            }

            var transaction = ixDb.transaction(objectStoreName, "readwrite"); // IDBTransaction.READ_WRITE);
            var objectStore = transaction.objectStore(objectStoreName);

            var request = objectStore.clear();

            request.onsuccess = function (e) {
                if(typeof onSuccess === 'function') {
                    onSuccess();
                }

                //ixDbSync - data sync hook
                if(ixDbSyncFlag && objectStoreName != "ixDbSync" && skipSync != true) {
                    ixDbSync.clearSync(objectStoreName);
                }

                console.log('ixDbEz: Deleted all records from ObjectStore ' + objectStoreName + ".");
            };

            request.onerror = function (e) {
                logError(e, onError);
                console.log('ixDbEz Error: Clear ObjectStore: ' + objectStoreName + " failed.");
            }
        }
        else {
            //The database is in the middle of opening
            if (ixDbRequest) {
                ixDbRequest.addEventListener ("success", function() { Clear_(objectStoreName, onSuccess, onError, skipSync) }, false);
            }
        }
    }

    //*********************************************************************************************************
    //Function Put      - Replace or insert a record in an object store.
    //  objectStoreName - Name of the Object Store / Table "MyOsName"
    //            value - Record object or value to insert.
    //              key - (optional) Key to access record.
    //        onSuccess - (optional) callback function to execute if function successful.
    //          onError - (optional) callback function to execute if function fails.
    //         skipSync - (optional) If true, skips all data synchronization in ixDbSync for a single transaction.
    //*********************************************************************************************************
    function Put_(objectStoreName, value, key, onSuccess, onError, skipSync) {
        if (ixDb) {
            //The database is being created or upgraded, re-run when completed.
            if(ixDbVersionTansaction) {
                ixDbVersionTansaction.addEventListener ("complete", function() { Put_(objectStoreName, value, key, onSuccess, onError, skipSync); }, false);
                return;
            }

            var transaction = ixDb.transaction(objectStoreName, "readwrite"); //IDBTransaction.READ_WRITE);
            var objectStore = transaction.objectStore(objectStoreName);

            try {
                var request = typeof key === 'undefined' ? objectStore.put(value) : objectStore.put(value, key);
            } catch (e) {
                // This is a workaround for the fact that chrome doesn't support blobs.
                // from: https://code.google.com/p/chromium/issues/detail?id=108012#c42
                //* when reading something that should be a blob, check if typeof value === "string"; if so, return new Blob([value], {type: 'application/octet-stream'}); otherwise return the value, which should be a Blob
                console.log('Couldn\'t save the Blob, trying a workaround');
                angular.forEach(value, function(data, index) {
                    if (Object.prototype.toString.call(data) === '[object Blob]') {
                        var reader = new FileReader();
                        reader.readAsBinaryString(data);
                        reader.onloadend = function(e) {
                            value[index] = reader.result;

                            Put_(objectStoreName, value, key, onSuccess, onError, skipSync);
                        };
                    }
                });

                return;
            }

            request.onsuccess = function (e) {
                if(typeof onSuccess === 'function') {
                    onSuccess(e.target.result);
                }

                //ixDbSync - data sync hook
                if(ixDbSyncFlag && objectStoreName != "ixDbSync" && skipSync != true) {
                    //Pass the primary key value to ixDbSync for the server lastUpdateLog
                    var logKey = typeof key === 'undefined' ? value[objectStore.keyPath] : key;
                    ixDbSync.addPutSync(objectStoreName, value, key, "Put", logKey);
                }

                console.log('ixDbEz: Put record into ObjectStore ' + objectStoreName + ".");
            };

            request.onerror = function (e) {

                logError(e, onError);
                console.log('ixDbEz Error: Put record into ObjectStore ' + objectStoreName + " failed.");
            }
        }
        else {
            if (ixDbRequest) {
                ixDbRequest.addEventListener ("success", function() { Put_(objectStoreName, value, key, onSuccess, onError, skipSync); }, false);
            }
        }
    }

    //*********************************************************************************************************
    //Function updateKey - Replace or insert a record in an object store.
    //   objectStoreName - Name of the Object Store / Table "MyOsName"
    //            oldKey - The Key value that needs to be updated.
    //            newKey - New value for the oldKey.
    //         onSuccess - (optional) callback function to execute if function successful.
    //           onError - (optional) callback function to execute if function fails.
    //          skipSync - (optional) If true, skips all data synchronization in ixDbSync for a single transaction.
    //
    //   newKey Warning! - If newKey exists in the ObjectStore, it's value will be replaced by oldKey.value
    //*********************************************************************************************************
    function UpdateKey_(objectStoreName, oldKey, newKey, onSuccess, onError, skipSync) {
        if (ixDb) {

            //The database is being created or upgraded, re-run when completed.
            if(ixDbVersionTansaction) {
                ixDbVersionTansaction.addEventListener ("complete", function() { UpdateKey_(objectStoreName, oldKey, newKey, onSuccess, onError, skipSync); }, false);
                return;
            }

            var keyInObject = false;
            var transaction = ixDb.transaction(objectStoreName, "readwrite"); // IDBTransaction.READ_WRITE);
            var objectStore = transaction.objectStore(objectStoreName);

            //Check oldKey exists request
            var request = objectStore.get(oldKey);

            request.onsuccess = function (e) {
                //Get the value from the oldKey record
                var oldKeyResult = e.result||this.result;

                //oldKey provided does not exist in database.
                if(typeof oldKeyResult === 'undefined'){
                    console.log('ixDbEz Error: updateKey failed. Key: ' + oldKey + ' does not exist in ObjectStore '  + objectStoreName + ".");
                }
                //oldKey provided does exist in the database
                else {
                    //if the value in the oldKey record is an object, and that object contains a
                    //property that matches the current ObjectStore's KeyPath name, update that property
                    //with the newKey value.
                    if(typeof oldKeyResult === 'object' && objectContainsProperty_(oldKeyResult, objectStore.keyPath)){
                        oldKeyResult[objectStore.keyPath] = newKey;
                        //since the newKey was updated in the object, newKey variable must = undefined
                        //or add_ and put_ will fail. keyInObject is checked later to set newKey = undefined
                        keyInObject = true;
                    }

                    //delete oldKey request
                    var request = objectStore.delete(oldKey);

                    //ixDbSync - data sync hook for above delete operation
                    if(ixDbSyncFlag && objectStoreName != "ixDbSync" && skipSync != true) {
                        ixDbSync.deleteSync(objectStoreName, oldKey);
                    }

                    request.onsuccess = function (e) {

                        //check newKey exists request
                        var request = objectStore.get(newKey);

                        request.onsuccess = function (e) {
                            var newKeyResult = e.result || this.result;

                            //newKey provided does not exist in database, so a new record is added
                            if(typeof newKeyResult === 'undefined'){
                                if(keyInObject){
                                    Add_(objectStoreName, oldKeyResult, undefined , onSuccess, onError, skipSync);
                                }
                                else{
                                    Add_(objectStoreName, oldKeyResult, newKey, onSuccess, onError, skipSync);
                                }
                            }
                            //newKey does exist in database, and it's value is replaced.
                            else {
                                if(keyInObject){
                                    Put_(objectStoreName, oldKeyResult, undefined , onSuccess, onError, skipSync);
                                }
                                else{
                                    Put_(objectStoreName, oldKeyResult, newKey, onSuccess, onError, skipSync);
                                }
                            } //else - newKey exists
                        } //check newKey.onsuccess

                        //check newKey failed
                        request.onerror = function (e) {
                            var errEvent = e.result||this.result;
                            logError(errEvent, onError);
                            console.log('ixDbEz Error: updateKey failed. Key: ' + newKey + ' is not valid in ObjectStore: '  + objectStoreName + ".");
                        }

                    } //delete oldKey.onsuccess

                    //delete oldKey failed
                    request.onerror = function (e) {
                        var errEvent = e.result||this.result;
                        logError(errEvent, onError);
                        console.log('ixDbEz Error: updateKey failed. Could not delete Key: ' + oldKey + ' from ObjectStore: '  + objectStoreName + ".");
                    }

                }  //else - oldKey exists
            } //check oldKey.onsuccess

            //check oldKey failed
            request.onerror = function (e) {
                var errEvent = e.result||this.result;
                logError(errEvent, onError);
                console.log('ixDbEz Error: updateKey failed. Key: ' + oldKey + ' is not valid in ObjectStore: '  + objectStoreName + ".");

            }

        } // ixDb exists
        else{
            if (ixDbRequest) {
                ixDbRequest.addEventListener ("success", function() { UpdateKey_(objectStoreName, oldKey, newKey, onSuccess, onError, skipSync); }, false);
            }
        }
    } // function

    //*********************************************************************************************************
    //Function Delete   - Delete a record in an object store.
    //  objectStoreName - Name of the Object Store / Table "MyOsName"
    //              key - Key of the record to be deleted.
    //        onSuccess - (optional) callback function to execute if function successful.
    //          onError - (optional) callback function to execute if function fails.
    //         skipSync - (optional) If true, skips all data synchronization in ixDbSync for a single transaction.
    //*********************************************************************************************************
    function Delete_(objectStoreName, key, onSuccess, onError, skipSync) {
        if (ixDb) {

            //The database is being created or upgraded, re-run when completed.
            if(ixDbVersionTansaction) {
                ixDbVersionTansaction.addEventListener ("complete", function() { Delete_(objectStoreName, key, onSuccess, onError, skipSync); }, false);
                return;
            }

            var transaction = ixDb.transaction(objectStoreName, "readwrite"); // IDBTransaction.READ_WRITE);
            var objectStore = transaction.objectStore(objectStoreName);

            request = objectStore.delete(key);

            request.onsuccess = function (e) {
                if(typeof onSuccess === 'function') {
                    onSuccess();
                }

                //ixDbSync - data sync hook
                if(ixDbSyncFlag && objectStoreName != "ixDbSync" && skipSync != true) {
                    ixDbSync.deleteSync(objectStoreName, key);
                }

                console.log('ixDbEz: Deleted record key: ' + key + ' from ObjectStore ' + objectStoreName + ".");
            };

            request.onerror = function (e) {
                var errEvent = e.result||this.result;
                logError(errEvent, onError);
                console.log('ixDbEz Error: Deleted record key: ' + key + ' from ObjectStore ' + objectStoreName + " failed.");
            }
        }
        else{
            if (ixDbRequest) {
                ixDbRequest.addEventListener ("success", function() { Delete_(objectStoreName, key, onSuccess, onError, skipSync); }, false);
            }
        }
    }

    //*********************************************************************************************************
    //Function getCursor - Returns a cursor for the requested ObjectStore
    //  objectStoreName  - Name of the Object Store / Table "MyOsName"
    //         onSuccess - Name of the function to call and pass the cursor request back to upon
    //                      successful completion.
    //           onError - (optional) callback function to execute if function fails.
    //          keyRange - (optional) IDBKeyRange to use when getting the cursor.
    //         readWrite - (optional) If set to true, returns a readwrite cursor.
    //         indexName - (optional) Name of a valid objectStore index.  If provided, the cursor is opened
    //                                using the requested indexName.
    //     onSuccess Ex. - getCursor_("ObjectStore_Name", MyCallBackFunction)
    //                      !! onSuccess function definition must have input variable for the request object !!
    //
    //                      Function MyCallBackFunction(CursorRequestObj) {
    //                                   CursorRequestObj.onsuccess = function() {//do stuff here};
    //                      }
    //
    //*********************************************************************************************************
    function getCursor_(objectStoreName, onSuccess, onError, keyRange, readWrite, indexName) {
        //The the openCursor call is asynchronous, so we must check to ensure a database
        //connection has been established and then provide the cursor via callback.
        if (ixDb) {
            //If the database is in the middle of an upgrade, return an undefined cursor.
            if(ixDbVersionTansaction || ixDbVersionRequest) {
                onSuccess();
            }
            else
            {
                try{
                    var transaction;
                    if(readWrite == true) {
                        transaction = ixDb.transaction(objectStoreName, "readwrite"); // IDBTransaction.READ_ONLY);
                    }
                    else{
                        transaction = ixDb.transaction(objectStoreName, "readonly");
                    }

                    var objectStore = transaction.objectStore(objectStoreName);

                    //If an indexName is provided, the cursor is opened using the requested index
                    //Otherwise it is opened via the object store. In either case, cursor is opened using
                    //a keyRange, if one is provided.
                    var cursor;
                    if(typeof indexName === "undefined") {
                        cursor = typeof keyRange === "undefined" ? objectStore.openCursor() : objectStore.openCursor(keyRange);
                    }
                    else {
                        var index = objectStore.index(indexName);
                        cursor = typeof keyRange === "undefined" ? index.openCursor() : index.openCursor(keyRange);
                    }

                    //Return the requested cursor via the callback function provided by the user.
                    onSuccess(cursor);
                    console.log('ixDbEz: Getting cursor request for ' + objectStoreName + ".");
                }
                catch(e){
                    logError(e, onError);
                    console.log('ixDbEz Error: getCursor failed');
                }
            }
        }
        else {
            if (ixDbRequest) {
                ixDbRequest.addEventListener ("success", function() { getCursor_(objectStoreName, onSuccess, onError); }, false);
            }
        }
    }

    //*********************************************************************************************************
    //Function objectContainsProperty - Returns true if the object contains the requested property, otherwise false.
    //                         object - A valid javascript object.
    //                       property - A string with the requested property name.
    //*********************************************************************************************************
    function objectContainsProperty_(object, property){
        var prototype = object.__prototype__ || object.constructor.prototype;
        return (property in object) && (!(property in prototype)
            || prototype[property] !== object[property]);
    }

    return {
        startDB: StartDB_,
        createObjStore: CreateObjStore_,
        createIndex: CreateIndex_,
        add : Add_,
        clear: Clear_,
        put : Put_,
        delete : Delete_,
        getCursor: getCursor_,
        updateKey : UpdateKey_,
        objectContainsProperty: objectContainsProperty_
    }
})();

//default console error handler
ixDbEz.onerror = function () { logError(e) };

//*********************************************************************************************************
//Function logError - Writes all errors to console.log
//         errEvent - event objects or and other javascript object which contains a errEvent.code
//                      and errEvent.message property.
//          onError - (optional) callback function to execute if function fails.
//
//              Tip - Re-route any Console.log messages to whereever you want. (div file etc...)
//                      window.console.log = function (msg) { //your code here }
//*********************************************************************************************************
function logError(errEvent, onError, transaction) {
    //if a valid onError function was passed, execute it.
    if(typeof onError === 'function') {
        onError();
    }

    //if a transaction object was passed, attempt to abort it
    if(typeof transaction !== 'undefined' && transaction.constructor.name == "IDBTransaction") {
        transaction.abort();
    }

    console.log('ixDbEz Error' + '(' + errEvent.code + '): ' + errEvent.message + '.');
}