class BasicEventHandler {
constructor () {
const eventPooler = {};
const getNamespace = (str="")=> {
const sl = str.split(".");
var major = sl.shift();
return {
"major": major,
"minor": sl.join(".") || "*"
}
}
var id = 0;
const makeId = ()=> {
id++;
return id;
}
this.on = (eventName, fn)=> {
if (typeof fn !== "function") throw new error("Second parameter must be a function");
const nameSpace = getNamespace(eventName);
eventPooler[nameSpace.major] ||= {};
eventPooler[nameSpace.major][nameSpace.minor] ||= {};
eventPooler[nameSpace.major][nameSpace.minor][makeId()] = {
fn:fn
};
}
this.one = (eventName, fn)=> {
if (typeof fn !== "function") throw new error("Second parameter must be a function")
const nameSpace = getNamespace(eventName);
eventPooler[nameSpace.major] ||= {};
eventPooler[nameSpace.major][nameSpace.minor] ||= {};
eventPooler[nameSpace.major][nameSpace.minor][makeId()] = {
fn:fn,
one:true
};
}
this.off = (eventName)=> {
if (!eventName.includes(".")) {
if (!eventPooler[eventName]) return;
delete eventPooler[eventName];
} else {
let nameSpace = getNamespace(eventName);
try{
delete eventPooler[nameSpace.major][nameSpace.minor];
} catch(e) {
}
}
}
this.emmit = (eventName, ...args)=> {
const nameSpace = getNamespace(eventName);
if (!eventPooler[nameSpace.major]) return;
if (!eventName.includes(".")) {
for (let minor in eventPooler[nameSpace.major]) {
for (let i in eventPooler[nameSpace.major][minor]) {
eventPooler[nameSpace.major][minor][i].fn.apply(this, args);
if (eventPooler[nameSpace.major][minor][i].one) {
delete eventPooler[nameSpace.major][minor][i];
if (Object.keys(eventPooler[nameSpace.major][minor]).length == 0 ) delete eventPooler[nameSpace.major][minor];
}
}
}
} else {
if (!eventPooler[nameSpace.major][nameSpace.minor]) return;
for (let i in eventPooler[nameSpace.major][nameSpace.minor]) {
eventPooler[nameSpace.major][nameSpace.minor][i].fn.apply(this, args);
if (eventPooler[nameSpace.major][nameSpace.minor][i].one) {
delete eventPooler[nameSpace.major][nameSpace.minor][i];
if (Object.keys(eventPooler[nameSpace.major][nameSpace.minor]).length == 0 ) delete eventPooler[nameSpace.major][nameSpace.minor];
}
}
}
}
this.hasEvent = (eventName) => {
try {
const nameSpace = getNamespace(eventName);
if (!eventPooler[nameSpace.major]) return false;
if (!eventName.includes(".")) {
if (Object.keys(eventPooler[nameSpace.major]).length > 0 ) return true;
} else {
if (Object.keys(eventPooler[nameSpace.major][nameSpace.minor]).length > 0) return true;
}
return false;
} catch (e) {
return false;
}
}
this.trigger = this.emmit;
}
}
/**
* A simple indexedDB handler to store [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) like key value pair... except, it is better.
* The benefits are:
* - Can store large amount of data.
* - Asynchronous and non-blocking.
* - Loaded on demand unlike LocalStorage.
* - Can store JavaScript object data.
* @class
* @param {String} [dbName=commonDB] - The DB name
* @param {String} [tableName=keyValuePairs] - Table name
* @memberof common
* @example <caption>Basic usage</caption>
* var myStorage = new DB();
*
* //store a value to "someKey"
* await myStorage.set("someKey", {"someObject" : "with the value"}):
*
* //get the value of "someKey"
* await myStorage.get("someKey");
* // will returns:
* {
* "someObject": "with the value"
* }
*/
const SimpleDB = function(dbName="commonDB", tableName="keyValuePairs") {
const evt = new BasicEventHandler();
this.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
this.dbName = dbName || "commonDB";
this.tableName = tableName || "keyValuePairs";
this.readyPromise = new Promise((resolve) => {
this.resolver = resolve;
});
const initTransaction = async () => {
this.tx = this.db.transaction(this.tableName, "readwrite");
this.store = this.tx.objectStore(this.tableName);
}
const init = async () => {
return new Promise((resolve, reject) => {
this.connection = indexedDB.open(this.dbName, 1);
this.connected = false;
this.connection.onupgradeneeded = () =>{
this.db = this.connection.result;
this.store = this.db.createObjectStore(this.tableName, {keyPath: "id"});
//this.index = this.store.createIndex("NameIndex", ["name.last", "name.first"]);
};
this.connection.onsuccess = () => {
// Start a new transaction
this.db = this.connection.result;
this.tx = this.db.transaction(this.tableName, "readwrite");
this.store = this.tx.objectStore(this.tableName);
//this.index = this.store.index("NameIndex");
this.isInitialized = true;
this.resolver(this);
resolve(this);
}
})
}
this.untilReady = async () => {
if (this.isInitialized) return this;
return init();
}
/**
* Set a value to the local DB
* @async
* @param {String} key - key of the key-value pair
* @param {*} value - Value of the key-value pair. Value can be anything. Unlike LocalStorage, if you can also store object in the DB.
* @returns {Promise<Event>} - Transaction event
*/
this.set = async (key, value) => {
await this.untilReady();
evt.trigger("beforeSet", key, value);
var isChanged = false;
var prevValue
if (evt.hasEvent("change")) {
prevValue = await this.get(key);
if (JSON.stringify(prevValue) !== JSON.stringify(value)) isChanged = true;
}
return new Promise((resolve, reject) => {
initTransaction();
var request = this.store.put({id: key, value: value});
request.onsuccess = (e)=> {
evt.trigger("set", key, value);
if (isChanged) {
evt.trigger("change", key, value, prevValue);
}
resolve(e)
}
request.onerror = (e)=> {
reject(e)
}
})
}
this.setItem = this.set;
/**
* Get a value from local DB.
* @async
* @param {String} key - key of the key-value pair
* @returns {Promise<*>} - The value
*/
this.get = async (key) => {
await this.untilReady();
return new Promise((resolve, reject) => {
initTransaction();
var request = this.store.get(key);
request.onsuccess = (e)=> {
if (!request.result) return resolve();
resolve(request.result.value)
}
request.onerror = (e)=> {
reject(e)
}
})
}
this.getItem = this.get;
/**
* Delete a record from local DB.
* @async
* @param {String} key - key of the key-value pair
* @returns {Promise<Event>} - Transaction event
*/
this.delete = async (key) => {
await this.untilReady();
evt.trigger("beforeDelete", key);
return new Promise((resolve, reject) => {
initTransaction();
var request = this.store.delete(key);
request.onsuccess = (e)=> {
evt.trigger("delete", key);
resolve(e)
}
request.onerror = (e)=> {
reject(e)
}
})
}
this.removeItem = this.delete;
/**
* Clear a database.
* @returns {Promise<Event>} - Transaction event
*/
this.clear = async () => {
await this.untilReady();
evt.trigger("beforeClear");
return new Promise((resolve, reject) => {
initTransaction();
var request = this.store.clear();
request.onsuccess = (e)=> {
evt.trigger("clear", key);
resolve(e)
}
request.onerror = (e)=> {
reject(e)
}
})
}
this.on = (eventName, fn)=> {
evt.on(eventName, fn);
}
this.one = (eventName, fn)=> {
evt.one(eventName, fn);
}
this.off = (eventName) => {
evt.off(eventName);
}
}
module.exports = SimpleDB;