Difference between revisions of "User:Abel/PersonalData.js"
Jump to navigation
Jump to search
m |
|||
(5 intermediate revisions by one other user not shown) | |||
Line 1: | Line 1: | ||
// License: GPL | // License: GPL | ||
− | // | + | // Indexeddb JavaScript |
− | console.log(' | + | console.log('Indb loading'); |
− | function | + | function IndexedDBContainer(ww) { |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | // suppress console.log, switch to true for debug | |
− | + | var Debug_switch = false; | |
− | + | // 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 | ||
+ | var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); | ||
+ | var Safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); | ||
+ | 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; | ||
+ | }; | ||
− | |||
− | + | 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); | ||
− | + | // 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 | |
− | + | // EnqueuePDIO a PDIOLocalDatabase:User database access function with modes | |
+ | // Externally accessible wrapper for Enquire with current user / db preloaded | ||
− | + | this.EnqueuePDIO = async function(sw, c1, c2, c3) { | |
+ | 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 | |
− | + | // Mode 'aggressive push': | |
+ | // parameter record is pushed to database, overwriting whatever is there | ||
− | + | // Mode 'checkin': | |
+ | // record is retrieved from database, parameter record is pushed if not found | ||
− | + | // Mode 'get record' | |
− | + | // updates record given as argument | |
− | |||
− | + | // Mode 'update record' field1 field2 field3 | |
+ | // retrieve record, make record.field1.field2 = field3, push back | ||
− | + | // Mode 'update record' field1 field2 | |
− | + | // retrieve record, make record.field1 = field2, push back | |
− | |||
− | |||
− | |||
− | |||
− | + | // Mode 'remove from record' field1 | |
− | + | // retrieve record, splice/delete record.field1, push back | |
− | |||
− | |||
− | + | // Mode 'remove from record' field1 field2 | |
− | + | // retrieve record, splice/delete record.field1.field2, push back | |
− | + | // Mode 'import record' field1 | |
− | + | // retrieve record, add all fields from field1, push back | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | async function Enquire(pddbname, record, sqitch, control_1, control_2, control_3) { | |
+ | return new Promise( | ||
+ | function(resolve, reject) { | ||
− | + | var pddb = window.indexedDB.open(pddbname, 3); | |
− | + | var inp_obj; | |
− | + | pddb.onerror = function() { | |
− | + | console.log("Warning: Access to IndexedDB for application has been rejected."); | |
− | + | }; | |
− | |||
− | |||
− | |||
− | |||
− | + | // indexeddb internal mechanism for version tracking of schema | |
+ | pddb.onupgradeneeded = function() { | ||
+ | console.log('upgrading database'); | ||
+ | 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 getRecord; | ||
+ | if (iOS || Safari) { | ||
+ | getRecord = index.get(record.id); | ||
+ | } else { | ||
+ | getRecord = index.get([record.id]); | ||
+ | } | ||
− | + | getRecord.onsuccess = function() { | |
+ | 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); | ||
+ | } | ||
− | + | } else { | |
+ | // Methods manipulationg the actual record come here | ||
+ | // Update & Remove from | ||
+ | // If we got the record from store, use that version to be manipulated | ||
+ | if (getRecord.result !== undefined) { | ||
+ | inp_obj = getRecord.result; | ||
− | + | if (sqitch === "get record") { | |
− | |||
− | |||
− | |||
− | |||
− | + | debuglog("Getting record"); | |
+ | // we will return later | ||
− | + | } | |
− | + | // endof onsuccess | |
+ | } else { | ||
− | + | debuglog("Record not found"); | |
− | + | 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 === "update record") { // Record Update | |
− | + | if (control_1 != undefined) { | |
− | + | if (control_2 != undefined) { | |
− | + | if (control_3 != undefined) { | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | // Mode 'update record' field1 field2 field3 | |
+ | // retrieve record, make record.field1.field2 = field3, push back | ||
− | + | 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 'update record' field1 field2 | |
+ | // retrieve record, make record.field1 = field2, push back | ||
− | + | inp_obj[control_1] = inp_obj[control_1] || {}; | |
+ | if (Array.isArray(inp_obj[control_1])) { | ||
+ | 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 |
+ | |||
+ | } | ||
+ | debuglog("return 3") | ||
+ | // return inp_obj; | ||
+ | |||
+ | }; | ||
+ | |||
+ | if (sqitch === "remove from record") { // Record Update | ||
+ | if (control_1 != undefined) { | ||
+ | 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); | ||
+ | } | ||
+ | } | ||
+ | }; |
Latest revision as of 11:43, 4 June 2019
// License: GPL
// Indexeddb JavaScript
console.log('Indb loading');
function IndexedDBContainer(ww) {
// suppress console.log, switch to true for debug
var Debug_switch = false;
// 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
var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
var Safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
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;
};
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);
// 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
// EnqueuePDIO a PDIOLocalDatabase:User database access function with modes
// Externally accessible wrapper for Enquire with current user / db preloaded
this.EnqueuePDIO = async function(sw, c1, c2, c3) {
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
// Mode 'aggressive push':
// parameter record is pushed to database, overwriting whatever is there
// Mode 'checkin':
// record is retrieved from database, parameter record is pushed if not found
// Mode 'get record'
// updates record given as argument
// Mode 'update record' field1 field2 field3
// retrieve record, make record.field1.field2 = field3, push back
// Mode 'update record' field1 field2
// retrieve record, make record.field1 = field2, push back
// Mode 'remove from record' field1
// retrieve record, splice/delete record.field1, push back
// Mode 'remove from record' field1 field2
// retrieve record, splice/delete record.field1.field2, push back
// Mode 'import record' field1
// retrieve record, add all fields from field1, push back
async function Enquire(pddbname, record, sqitch, control_1, control_2, control_3) {
return new Promise(
function(resolve, reject) {
var pddb = window.indexedDB.open(pddbname, 3);
var inp_obj;
pddb.onerror = function() {
console.log("Warning: Access to IndexedDB for application has been rejected.");
};
// indexeddb internal mechanism for version tracking of schema
pddb.onupgradeneeded = function() {
console.log('upgrading database');
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 getRecord;
if (iOS || Safari) {
getRecord = index.get(record.id);
} else {
getRecord = index.get([record.id]);
}
getRecord.onsuccess = function() {
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);
}
} else {
// Methods manipulationg the actual record come here
// Update & Remove from
// If we got the record from store, use that version to be manipulated
if (getRecord.result !== undefined) {
inp_obj = getRecord.result;
if (sqitch === "get record") {
debuglog("Getting record");
// we will return later
}
// endof onsuccess
} else {
debuglog("Record not found");
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 === "update record") { // Record Update
if (control_1 != undefined) {
if (control_2 != undefined) {
if (control_3 != undefined) {
// Mode 'update record' field1 field2 field3
// retrieve record, make record.field1.field2 = field3, push back
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 'update record' field1 field2
// retrieve record, make record.field1 = field2, push back
inp_obj[control_1] = inp_obj[control_1] || {};
if (Array.isArray(inp_obj[control_1])) {
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
}
debuglog("return 3")
// return inp_obj;
};
if (sqitch === "remove from record") { // Record Update
if (control_1 != undefined) {
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);
}
}
};