Line 3: |
Line 3: |
| console.log('Indb loading'); | | console.log('Indb loading'); |
| | | |
− | function IndexedDBContainer( ww ) { | + | function IndexedDBContainer(ww) { |
| | | |
− | // suppress console.log, switch to true for debug
| + | // suppress console.log, switch to true for debug |
− | var Debug_switch = false;
| + | var Debug_switch = false; |
− | // seemingly the Safari engine uses a somewhat different implementation of arguments for db.transaction and index.get
| + | // seemingly the Safari engine uses a somewhat different implementation of arguments for db.transaction and index.get |
− | // because no browser on any mac / ios is allowed to use a different engine, rather than user agent, platform can be tested
| + | // because no browser on any mac / ios is allowed to use a different engine, rather than user agent, platform can be tested |
− | var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
| + | var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); |
− | var Safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
| + | var Safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); |
− | debuglog("iOS:" + iOS + " & Saf:" + Safari);
| + | debuglog("iOS:" + iOS + " & Saf:" + Safari); |
− |
| |
− | // deterministic hash generator for creating user id#s from usernames, should be replaced by something more functional (guaranteed to be transitively different)
| |
− | hashCode = function(s) {
| |
− | if ( s == undefined) {
| |
− | s = '00'
| |
− | }
| |
− | var h = 0, l = s.length, i = 0;
| |
− | if ( l > 0 )
| |
− | while (i < l)
| |
− | h = (h << 5) - h + s.charCodeAt(i++) | 0;
| |
− | return h;
| |
− | };
| |
| | | |
− |
| + | // deterministic hash generator for creating user id#s from usernames, should be replaced by something more functional (guaranteed to be transitively different) |
− | if ( ww.user != undefined ) {
| + | hashCode = function(s) { |
− | // if user exists in object passed to constructor, get username
| + | if (s == undefined) { |
− | var self = ww.user.getName();
| + | s = '00' |
− | // create userid by hashing username
| + | } |
− | var idgen = hashCode(self);
| + | var h = 0, |
− | debuglog(idgen);
| + | l = s.length, |
| + | i = 0; |
| + | if (l > 0) |
| + | while (i < l) |
| + | h = (h << 5) - h + s.charCodeAt(i++) | 0; |
| + | return h; |
| + | }; |
| | | |
− | // Take username for standard databasename format:
| |
− | // Our current convention for personal databasename (separate dbname for each user)
| |
− | var PersonalDataDataBaseName = "PD.IO" + idgen + "Database";
| |
− | // Use username in user to get up to date version of self
| |
− | // Blank object for personal data Record with id and username included
| |
− | this.CurrentPerson = {id: idgen, name: {user: self}, age: 0};
| |
− | debuglog(PersonalDataDataBaseName);
| |
− | debuglog(this.CurrentPerson);
| |
− | }
| |
| | | |
− | // ADD MORE DOC | + | if (ww.user != undefined) { |
| + | // if user exists in object passed to constructor, get username |
| + | var self = ww.user.getName(); |
| + | // create userid by hashing username |
| + | var idgen = hashCode(self); |
| + | debuglog(idgen); |
| | | |
− | // EnqueuePDIO a PDIOLocalDatabase:User database access function with modes
| + | // Take username for standard databasename format: |
− | // Externally accessible wrapper for Enquire with current user / db preloaded
| + | // Our current convention for personal databasename (separate dbname for each user) |
| + | var PersonalDataDataBaseName = "PD.IO" + idgen + "Database"; |
| + | // Use username in user to get up to date version of self |
| + | // Blank object for personal data Record with id and username included |
| + | this.CurrentPerson = { |
| + | id: idgen, |
| + | name: { |
| + | user: self |
| + | }, |
| + | age: 0 |
| + | }; |
| + | debuglog(PersonalDataDataBaseName); |
| + | debuglog(this.CurrentPerson); |
| + | } |
| | | |
− | this.EnqueuePDIO = async function( sw, c1, c2, c3 ) {
| + | // ADD MORE DOC |
− | debuglog("enquing pdio db " + PersonalDataDataBaseName + " with user " + JSON.stringify(this.CurrentPerson));
| |
− | var k = await Enquire( PersonalDataDataBaseName, this.CurrentPerson, sw, c1, c2, c3 );
| |
− | return k;
| |
− | }
| |
| | | |
− | // Enquire, a general purpose indexeddb update function with modes
| + | // EnqueuePDIO a PDIOLocalDatabase:User database access function with modes |
| + | // Externally accessible wrapper for Enquire with current user / db preloaded |
| | | |
− | // Mode 'aggressive push':
| + | this.EnqueuePDIO = async function(sw, c1, c2, c3) { |
− | // parameter record is pushed to database, overwriting whatever is there
| + | debuglog("enquing pdio db " + PersonalDataDataBaseName + " with user " + JSON.stringify(this.CurrentPerson)); |
| + | var k = await Enquire(PersonalDataDataBaseName, this.CurrentPerson, sw, c1, c2, c3); |
| + | return k; |
| + | } |
| | | |
− | // Mode 'checkin':
| + | // Enquire, a general purpose indexeddb update function with modes |
− | // record is retrieved from database, parameter record is pushed if not found
| |
| | | |
− | // Mode 'get record'
| + | // Mode 'aggressive push': |
− | // updates record given as argument
| + | // parameter record is pushed to database, overwriting whatever is there |
| | | |
− | // Mode 'update record' field1 field2 field3
| + | // Mode 'checkin': |
− | // retrieve record, make record.field1.field2 = field3, push back
| + | // record is retrieved from database, parameter record is pushed if not found |
| | | |
− | // Mode 'update record' field1 field2
| + | // Mode 'get record' |
− | // retrieve record, make record.field1 = field2, push back
| + | // updates record given as argument |
| | | |
− | // Mode 'remove from record' field1
| + | // Mode 'update record' field1 field2 field3 |
− | // retrieve record, splice/delete record.field1, push back
| + | // retrieve record, make record.field1.field2 = field3, push back |
− |
| |
− | // Mode 'remove from record' field1 field2
| |
− | // retrieve record, splice/delete record.field1.field2, push back
| |
| | | |
− | // Mode 'import record' field1
| + | // Mode 'update record' field1 field2 |
− | // retrieve record, add all fields from field1, push back
| + | // retrieve record, make record.field1 = field2, push back |
| | | |
− | async function Enquire( pddbname, record, sqitch, control_1, control_2, control_3 ) {
| + | // Mode 'remove from record' field1 |
− | return new Promise(
| + | // retrieve record, splice/delete record.field1, push back |
− | function(resolve, reject) {
| |
| | | |
− | var pddb = window.indexedDB.open(pddbname, 3);
| + | // Mode 'remove from record' field1 field2 |
− | var inp_obj;
| + | // retrieve record, splice/delete record.field1.field2, push back |
| | | |
− | pddb.onerror = function() {
| + | // Mode 'import record' field1 |
− | console.log("Warning: Access to IndexedDB for application has been rejected.");
| + | // retrieve record, add all fields from field1, push back |
− | };
| |
| | | |
− | // indexeddb internal mechanism for version tracking of schema
| + | async function Enquire(pddbname, record, sqitch, control_1, control_2, control_3) { |
− | pddb.onupgradeneeded = function() {
| + | return new Promise( |
− | console.log('upgrading database');
| + | function(resolve, reject) { |
− | var db = pddb.result;
| |
− | var store = db.createObjectStore(pddbname, {keyPath: "id"});
| |
− | var index = store.createIndex("NameIndex", ["id"]);
| |
− |
| |
− | };
| |
− | // Database successfully opened function.
| |
− | pddb.onsuccess = function() {
| |
− | debuglog(pddb.result);
| |
− | var db = pddb.result;
| |
− | var tx;
| |
− |
| |
− | if ( iOS || Safari ) {
| |
− | tx = db.transaction([pddbname], "readwrite");
| |
− | } else {
| |
− | tx = db.transaction(pddbname, "readwrite");
| |
− | }
| |
− |
| |
− | var store = tx.objectStore(pddbname);
| |
− | var index = store.index("NameIndex");
| |
| | | |
− | // Get the original record from the db...
| + | var pddb = window.indexedDB.open(pddbname, 3); |
− | var getRecord;
| + | var inp_obj; |
− | if ( iOS || Safari ) {
| |
− | getRecord = index.get(record.id);
| |
− | } else {
| |
− | getRecord = index.get([record.id]);
| |
− | }
| |
| | | |
− | getRecord.onsuccess = function() {
| + | pddb.onerror = function() { |
− | debuglog( "Record in database: \n <<");
| + | console.log("Warning: Access to IndexedDB for application has been rejected."); |
− | debuglog( getRecord.result );
| + | }; |
− | debuglog( ">>");
| |
− | // Essential recordAdd
| |
− | if ( sqitch === "aggressive push" ) {
| |
− | // Mode 'aggressive push':
| |
− | // parameter record is pushed to database, overwriting whatever is there
| |
− | inp_obj = record;
| |
− | store.put( inp_obj );
| |
− | return inp_obj;
| |
− | } else if ( sqitch === "checkin" ) {
| |
− | // Mode 'checkin':
| |
− | // record is retrieved from database, parameter record is pushed if not found
| |
− | if ( getRecord.result != undefined ) {
| |
− | debuglog("getting from store");
| |
− | inp_obj = getRecord.result;
| |
− | } else {
| |
− | debuglog("overriding whatever in there");
| |
− | inp_obj = record;
| |
− | store.put(inp_obj);
| |
− | }
| |
| | | |
− | } else {
| + | // indexeddb internal mechanism for version tracking of schema |
− | // Methods manipulationg the actual record come here
| + | pddb.onupgradeneeded = function() { |
− | // Update & Remove from
| + | console.log('upgrading database'); |
− | // If we got the record from store, use that version to be manipulated
| + | var db = pddb.result; |
− | if ( getRecord.result !== undefined ) {
| + | var store = db.createObjectStore(pddbname, { |
− | inp_obj = getRecord.result;
| + | keyPath: "id" |
− |
| + | }); |
− | if ( sqitch === "get record" ) {
| + | var index = store.createIndex("NameIndex", ["id"]); |
− |
| |
− | debuglog("Getting record");
| |
− | // we will return later
| |
− |
| |
− | }
| |
| | | |
− | // endof onsuccess
| + | }; |
− | } else {
| + | // Database successfully opened function. |
| + | pddb.onsuccess = function() { |
| + | debuglog(pddb.result); |
| + | var db = pddb.result; |
| + | var tx; |
| | | |
− | debuglog("Record not found");
| + | if (iOS || Safari) { |
| + | tx = db.transaction([pddbname], "readwrite"); |
| + | } else { |
| + | tx = db.transaction(pddbname, "readwrite"); |
| + | } |
| | | |
− | if ( sqitch === "update record" ) {
| + | var store = tx.objectStore(pddbname); |
− | // Update record will use supplied copy to update it
| + | var index = store.index("NameIndex"); |
− | inp_obj = record;
| |
− | } else {
| |
− | if ( sqitch === "get record" ) {
| |
− | inp_obj = undefined;
| |
− | } else if ( sqitch === "remove from record" ) {
| |
− | inp_obj = record;
| |
− | } else {
| |
− | // any other method ...... would quit as record was not even foun
| |
− | return undefined;
| |
− | }
| |
− | }
| |
− | }
| |
− | }
| |
| | | |
− | if ( sqitch === "update record" ) { // Record Update
| + | // Get the original record from the db... |
− | if ( control_1 != undefined ) {
| + | var getRecord; |
− | if ( control_2 != undefined ) {
| + | if (iOS || Safari) { |
− | if ( control_3 != undefined ) {
| + | getRecord = index.get(record.id); |
| + | } else { |
| + | getRecord = index.get([record.id]); |
| + | } |
| | | |
− | // Mode 'update record' field1 field2 field3
| + | getRecord.onsuccess = function() { |
− | // retrieve record, make record.field1.field2 = field3, push back
| + | debuglog("Record in database: \n <<"); |
| + | debuglog(getRecord.result); |
| + | debuglog(">>"); |
| + | // Essential recordAdd |
| + | if (sqitch === "aggressive push") { |
| + | // Mode 'aggressive push': |
| + | // parameter record is pushed to database, overwriting whatever is there |
| + | inp_obj = record; |
| + | store.put(inp_obj); |
| + | return inp_obj; |
| + | } else if (sqitch === "checkin") { |
| + | // Mode 'checkin': |
| + | // record is retrieved from database, parameter record is pushed if not found |
| + | if (getRecord.result != undefined) { |
| + | debuglog("getting from store"); |
| + | inp_obj = getRecord.result; |
| + | } else { |
| + | debuglog("overriding whatever in there"); |
| + | inp_obj = record; |
| + | store.put(inp_obj); |
| + | } |
| | | |
− | if ( inp_obj[ control_1 ] == undefined ) { inp_obj[ control_1 ] = (isNaN( control_2 )) ? {} : [] };
| + | } else { |
− | inp_obj[ control_1 ][ control_2 ] = control_3; // => "Bob"
| + | // Methods manipulationg the actual record come here |
− | store.put(inp_obj);
| + | // Update & Remove from |
− | } else {
| + | // If we got the record from store, use that version to be manipulated |
| + | if (getRecord.result !== undefined) { |
| + | inp_obj = getRecord.result; |
| | | |
− | // Mode 'update record' field1 field2
| + | if (sqitch === "get record") { |
− | // retrieve record, make record.field1 = field2, push back
| |
| | | |
− | inp_obj[ control_1 ] = inp_obj[ control_1 ] || {};
| + | debuglog("Getting record"); |
− | if ( Array.isArray( inp_obj[ control_1 ] ) ) {
| + | // we will return later |
− | inp_obj[ control_1 ].push( control_2 );
| |
− | } else {
| |
− | inp_obj[ control_1 ] = control_2;
| |
− | }
| |
− | store.put(inp_obj);
| |
| | | |
− | }
| + | } |
− | } else {
| |
− | // Only one control defined. We will return that part of the record, and change nothing
| |
− | inp_obj = inp_obj[ control_1 ];
| |
− | }
| |
− | } else {
| |
| | | |
− | // There's not even control_1, we will just return the record itself
| + | // endof onsuccess |
| + | } else { |
| | | |
− | }
| + | debuglog("Record not found"); |
− | debuglog("return 3")
| |
− | // return inp_obj;
| |
| | | |
− | };
| + | if (sqitch === "update record") { |
| + | // Update record will use supplied copy to update it |
| + | inp_obj = record; |
| + | } else { |
| + | if (sqitch === "get record") { |
| + | inp_obj = undefined; |
| + | } else if (sqitch === "remove from record") { |
| + | inp_obj = record; |
| + | } else { |
| + | // any other method ...... would quit as record was not even foun |
| + | return undefined; |
| + | } |
| + | } |
| + | } |
| + | } |
| | | |
− | if ( sqitch === "remove from record" ) { // Record Update
| + | if (sqitch === "update record") { // Record Update |
− | if ( control_1 != undefined ) {
| + | if (control_1 != undefined) { |
− | if ( control_2 != undefined ) {
| + | if (control_2 != undefined) { |
| + | if (control_3 != undefined) { |
| | | |
− | // Mode 'remove from record' field1 field2
| + | // Mode 'update record' field1 field2 field3 |
− | // retrieve record, splice/delete record.field1.field2, push back
| + | // retrieve record, make record.field1.field2 = field3, push back |
− |
| |
− | if ( Array.isArray( inp_obj[ control_1 ] ) && !isNaN( control_2 ) ) {
| |
− | inp_obj[ control_1 ].splice( control_2, 1 );
| |
− | } else {
| |
− | delete inp_obj[ control_1 ][ control_2 ];
| |
− | }
| |
| | | |
− | } else {
| + | if (inp_obj[control_1] == undefined) { |
| + | inp_obj[control_1] = (isNaN(control_2)) ? {} : [] |
| + | }; |
| + | inp_obj[control_1][control_2] = control_3; // => "Bob" |
| + | store.put(inp_obj); |
| + | } else { |
| | | |
− | // Mode 'remove from record' field1
| + | // Mode 'update record' field1 field2 |
− | // retrieve record, splice/delete record.field1, push back
| + | // retrieve record, make record.field1 = field2, push back |
| | | |
− | delete inp_obj[ control_1 ];
| + | inp_obj[control_1] = inp_obj[control_1] || {}; |
− | }
| + | if (Array.isArray(inp_obj[control_1])) { |
− | }
| + | inp_obj[control_1].push(control_2); |
− | debuglog("putting object to store:");
| + | } else { |
− | debuglog(inp_obj);
| + | inp_obj[control_1] = control_2; |
− | store.put(inp_obj);
| + | } |
− | }
| + | store.put(inp_obj); |
| | | |
− | if ( sqitch === "import record" ) {
| + | } |
− | debuglog("import command");
| + | } else { |
− | if ( control_1 != undefined ) {
| + | // Only one control defined. We will return that part of the record, and change nothing |
− | // Only import if control_1 is a non-empty object
| + | inp_obj = inp_obj[control_1]; |
− | if ( ( !Array.isArray(control_1) ) && ( Object.keys(control_1).length > 0 ) ) {
| + | } |
− | debuglog("command is obj");
| + | } else { |
− | Object.keys(control_1).forEach( function( importkey ){
| |
− | debuglog("importing " + importkey );
| |
− | if (control_1.hasOwnProperty(importkey)) { inp_obj[importkey] = control_1[importkey] };
| |
− | })
| |
− | store.put(inp_obj);
| |
− | }
| |
− | }
| |
− | }
| |
| | | |
− | // We are past all methodic code, Async return
| + | // There's not even control_1, we will just return the record itself |
− |
| |
− | resolve( inp_obj );
| |
| | | |
− | };
| + | } |
− |
| + | debuglog("return 3") |
− | // Indexeddb internal mechanism, close db
| + | // return inp_obj; |
− |
| |
− | tx.oncomplete = function() {
| |
− | debuglog("return 1")
| |
− | db.close();
| |
− | };
| |
− | };
| |
− | debuglog("return 0")
| |
| | | |
− | // End of promise
| + | }; |
− |
| |
− | }).then( function(result) {
| |
− | debuglog("Mark 3");
| |
− | debuglog(result);
| |
− | return result;
| |
− | });
| |
− | }
| |
| | | |
− | function debuglog( l0g ){
| + | if (sqitch === "remove from record") { // Record Update |
− | if ( Debug_switch ) {
| + | if (control_1 != undefined) { |
− | console.log( l0g );
| + | if (control_2 != undefined) { |
− | }
| + | |
− | }
| + | // Mode 'remove from record' field1 field2 |
| + | // retrieve record, splice/delete record.field1.field2, push back |
| + | |
| + | if (Array.isArray(inp_obj[control_1]) && !isNaN(control_2)) { |
| + | inp_obj[control_1].splice(control_2, 1); |
| + | } else { |
| + | delete inp_obj[control_1][control_2]; |
| + | } |
| + | |
| + | } else { |
| + | |
| + | // Mode 'remove from record' field1 |
| + | // retrieve record, splice/delete record.field1, push back |
| + | |
| + | delete inp_obj[control_1]; |
| + | } |
| + | } |
| + | debuglog("putting object to store:"); |
| + | debuglog(inp_obj); |
| + | store.put(inp_obj); |
| + | } |
| + | |
| + | if (sqitch === "import record") { |
| + | debuglog("import command"); |
| + | if (control_1 != undefined) { |
| + | // Only import if control_1 is a non-empty object |
| + | if ((!Array.isArray(control_1)) && (Object.keys(control_1).length > 0)) { |
| + | debuglog("command is obj"); |
| + | Object.keys(control_1).forEach(function(importkey) { |
| + | debuglog("importing " + importkey); |
| + | if (control_1.hasOwnProperty(importkey)) { |
| + | inp_obj[importkey] = control_1[importkey] |
| + | }; |
| + | }) |
| + | store.put(inp_obj); |
| + | } |
| + | } |
| + | } |
| + | |
| + | // We are past all methodic code, Async return |
| + | |
| + | resolve(inp_obj); |
| + | |
| + | }; |
| + | |
| + | // Indexeddb internal mechanism, close db |
| + | |
| + | tx.oncomplete = function() { |
| + | debuglog("return 1") |
| + | db.close(); |
| + | }; |
| + | }; |
| + | debuglog("return 0") |
| + | |
| + | // End of promise |
| + | |
| + | }).then(function(result) { |
| + | debuglog("Mark 3"); |
| + | debuglog(result); |
| + | return result; |
| + | }); |
| + | } |
| + | |
| + | function debuglog(l0g) { |
| + | if (Debug_switch) { |
| + | console.log(l0g); |
| + | } |
| + | } |
| }; | | }; |