623 lines
23 KiB
JavaScript
623 lines
23 KiB
JavaScript
/*eslint no-undef:0*/
|
|
/*eslint-env browser*/
|
|
|
|
"use strict";
|
|
var apx = apx || {};
|
|
|
|
/**************************************************************************
|
|
* apx.js manage data to interact with an apxtri instance from a webpage *
|
|
* component can be add with name-wco
|
|
*************************************************************************
|
|
* This code is not minify and target to be understood by a maximum of
|
|
* curious people to audit and give any feedback to support@ndda.fr
|
|
*
|
|
* To audit it in a browser like chrome:
|
|
* - open web developpement (F12)
|
|
* - Menu Sources: apx.js , see below for more information
|
|
* - : apxid.js, this is the authentification module that works as a microservice
|
|
*
|
|
*
|
|
* Main principle:
|
|
* - Usage of localStorage to store any usefull data and save it between session
|
|
* - All data and template can be download from an apxtri
|
|
* get /api/apxtri/wwws/updatelocaldb{ano}/{tribe}/{xapp}/{pagename}/{version}
|
|
* ano = empty if authenticated anonymous if not
|
|
* tribe = the tribe where tha xapp is strore
|
|
* xapp = the folder name in /tribe/xapp/
|
|
* pagename = the name of the page without _xx (language)
|
|
* version = 0 or the version currently store (version is to manage cach)
|
|
*
|
|
* ------------------
|
|
* ? State management
|
|
* ------------------
|
|
* html page must have apxtri with at least headers key
|
|
*
|
|
<script>
|
|
const apxtri={
|
|
version:0, // 0 mean it is a first visit if you force to 0 then it will reload all app data
|
|
pagename:"pagename", // without _lg.html ex:pageadmin
|
|
pageauth:"apxid" local page with authentification without _lg,
|
|
headers:{
|
|
xalias:"anonymous",
|
|
xhash:"anonymous",
|
|
xtribe:"tribeId",
|
|
xapp:"app name",
|
|
xlang:"en",
|
|
xdays:0,
|
|
xuuid:"0",
|
|
xtrkversion:1}
|
|
}
|
|
wco:{}
|
|
//specific init data for this webpage
|
|
} ;
|
|
</script>
|
|
* ++++++++
|
|
* ? apx.ready(callback) await DOM is ready equivalent of jquery Document.ready()
|
|
* +++++++++
|
|
* ? apx.readyafterupdate(callback) allow to add function callback that will be load after apx.ready to add a function to execute apx.readyafterupdate(functname) without () after functname
|
|
* +++++++++
|
|
* ? apx.listendatawco(newpropertie) allow to store data in apx.data.wco and to listen any change to update HTML DOM content <tag data-wco="propertie"></tag> if apx.data.wco.propertie value change then it change every where data-wco=propertie exist innerHTML, textContent, class, ...value
|
|
* to add a new propertie to listen just add it apx.data.wco.propertie={innerHTML:"<p>test</p>"} and run apx.listendatawco(propertie)
|
|
* +++++++++
|
|
* ? apx.notification(selector, data) insert in tag selector an apx return call into data{status:xxx,ref:"Ref",msg:"key",data:{}}, it use apx.data.ref[data.ref].msg as template and render it with data
|
|
* +++++++++
|
|
* ? apx.save() save apx.data as {xapp} value in localStorage to be able for the same domain to share data
|
|
* +++++++++
|
|
* ? apx.update() :
|
|
* Run at least once after loading a pagename, what it does:
|
|
* - url/page.html?k1=v1&k2=v2#h1=v4&h2=v5 => store in apx.pagecontext = { search: {k1:v1,k2:v2}, hash: {h1:v4,h2:v5} };
|
|
* - updatelocaldb from api to localstorage name xapp (options / ref / schema / tpl /tpldata) store in {tribe}/wwws/{xapp}.json with key {pagename}
|
|
* - run apx.listendatawco() to allow any apx.data.wco variable to update DOM
|
|
* - run any function that were store in apx.afterupdate.
|
|
* - run apx.lazyload() to load image in background (this is to decrease time loading for search engine)
|
|
* ++++++++++
|
|
* */
|
|
|
|
apx.ready = (callback) => {
|
|
if (!callback) {
|
|
alert(
|
|
"You have an unknown callback apx.ready(callback), you need to order your code callback = ()=>{} then apx.ready(callback), boring but js rules ;-)"
|
|
);
|
|
}
|
|
if (document.readyState != "loading") callback();
|
|
// modern browsers
|
|
else if (document.addEventListener)
|
|
document.addEventListener("DOMContentLoaded", callback);
|
|
// IE <= 8
|
|
else
|
|
document.attachEvent("onreadystatechange", function () {
|
|
if (document.readyState == "complete") callback();
|
|
});
|
|
};
|
|
|
|
apx.readyafterupdate = (callback) => {
|
|
if (!apx.afterupdate) apx.afterupdate = [];
|
|
apx.afterupdate.push(callback);
|
|
};
|
|
|
|
apx.lazyload = () => {
|
|
document.querySelectorAll("img[data-lazysrc]").forEach((e) => {
|
|
// in src/page.html: src contain original img and data-lazysrc='true'
|
|
// in dist/page.html src is removed img src is in data-lazysrc=newimage.webp or svg
|
|
let src = e.getAttribute("src")
|
|
? e.getAttribute("src")
|
|
: e.getAttribute("data-lazysrc");
|
|
if (e.getAttribute("data-trksrckey")) {
|
|
src = `/trk/${src}?alias=${apx.data.headers.xalias}&uuid=${
|
|
apx.data.headers.xuuid
|
|
}&srckey=${e.getAttribute("data-trksrckey")}&version=${
|
|
apx.data.headers.xtrkversion
|
|
}&consentcookie=${localStorage.getItem("consentcookie")}&lg=${
|
|
apx.data.headers.xlang
|
|
}`;
|
|
}
|
|
e.setAttribute("src", src);
|
|
console.log("lazyload track:", src);
|
|
e.removeAttribute("data-lazysrc");
|
|
});
|
|
document.querySelectorAll("[data-lazybgsrc]").forEach((e) => {
|
|
e.style.backgroundImage = `url(${e.getAttribute("src")})`;
|
|
e.removeAttribute("data-lazybgsrc");
|
|
});
|
|
document.querySelectorAll("a[data-trksrckey]").forEach((e) => {
|
|
let urldestin = e.getAttribute("href");
|
|
if (
|
|
urldestin.substring(0, 4) != "http" &&
|
|
urldestin.substring(0, 1) != "/"
|
|
) {
|
|
urldestin = "/" + urldestin;
|
|
}
|
|
const hreftrack = `/trk/redirect?alias=${apx.data.headers.xalias}&uuid=${
|
|
apx.data.headers.xuuid
|
|
}&srckey=${e.getAttribute("data-trksrckey")}&version=${
|
|
apx.data.headers.xtrkversion
|
|
}&consentcookie=${localStorage.getItem("consentcookie")}&lg=${
|
|
apx.data.headers.xlang
|
|
}&url=${urldestin}`;
|
|
console.log("href track:", hreftrack);
|
|
e.setAttribute("href", hreftrack);
|
|
e.removeAttribute("data-trksrckey");
|
|
});
|
|
};
|
|
apx.notification = (selector, data, clearbefore) => {
|
|
/**
|
|
* @selector a text to use querySelector() in document
|
|
* @data apxtri return from any request {status:200,ref:"",msg:"key", data }
|
|
* if {status,multimsg:[{ref,msg,data}]} then a multi feedback are in
|
|
* @clearbefore boolean if true then it remove the previous message
|
|
* @return update the dom selctor with the relevant render message in the relevant language
|
|
*/
|
|
console.log("notification of ", data);
|
|
const el = document.querySelector(selector);
|
|
if (!el) {
|
|
console.log(
|
|
`WARNING !!! check apx.notification selector:${selector} does not exist in this page`
|
|
);
|
|
return false;
|
|
}
|
|
if (clearbefore) el.innerHTML = "";
|
|
const multimsg = data.multimsg ? data.multimsg : [data];
|
|
multimsg.forEach((info) => {
|
|
if (!apx.data.ref[info.ref]) {
|
|
console.log(
|
|
`check apx.data.ref, ${info.ref} does not exist in this page, add it `
|
|
);
|
|
return false;
|
|
} else if (!apx.data.ref[info.ref][info.msg]) {
|
|
console.log(
|
|
`check apx.data.ref.${info.ref} does not contain ${info.msg} update /schema/lg or /model/lg`
|
|
);
|
|
return false;
|
|
}
|
|
el.innerHTML +=
|
|
" " + Mustache.render(apx.data.ref[info.ref][info.msg], info.data);
|
|
if (data.status == 200) {
|
|
el.classList.remove("text-red");
|
|
el.classList.add("text-green");
|
|
} else {
|
|
el.classList.add("text-red");
|
|
el.classList.remove("text-green");
|
|
}
|
|
});
|
|
};
|
|
apx.listendatawco = (newpropertie) => {
|
|
// listen any change in apx.wco.newpropertie and update interface with this new value
|
|
// < data-wco="propertie of apx.wco"> is updated with content text, html any attribute in this new value
|
|
// to init run apx.listendatawco() this is done by apx.update()
|
|
// to dynamicaly add a new propertie to listen just run apx.listendatawco(propertietoadd);
|
|
// Then manage your data by modifying apx.wco and it will be update anywhere it use in the webpage
|
|
//example:
|
|
// <img data-wco="logodetoto" src="urltoimage" "alt">
|
|
// apx.wco.logodetoto={src:"newurltoimage",alt:"newalt"} then it will change every where data-wco=logodetoto
|
|
//
|
|
// <p data-wco="claim">Blabla</p>
|
|
// apx.wco.claim={html:"newblabla"}
|
|
console.log("From apx.data.wco:", apx.data.wco);
|
|
if (!apx.wco) apx.wco = {};
|
|
console.log(
|
|
"wco dynamic into the webpage",
|
|
apx.wco,
|
|
"no propertie to add:",
|
|
!newpropertie
|
|
);
|
|
if (!apx.data.wco || Object.keys(apx.data.wco).length == 0) return false;
|
|
newpropertie = !newpropertie ? Object.keys(apx.data.wco) : [newpropertie];
|
|
console.log("listen apx.data.wco properties:", newpropertie);
|
|
newpropertie.forEach((p) => {
|
|
const actionprop = (val, elt) => {
|
|
if (val.innerHTML) elt.innerHTML = val.innerHTML;
|
|
if (val.textContent) elt.textContent = val.textContent;
|
|
for (const h in ["innerHTML", "textContent"]) {
|
|
if (val[h]) elt[h] = val[h];
|
|
}
|
|
for (const a in ["src", "alt", "placeholder", "class", "href"]) {
|
|
if (val[a]) elt.setAttribute(a, val[a]);
|
|
}
|
|
};
|
|
const elements = document.querySelectorAll(`[data-wco='${p}']`);
|
|
elements.forEach((e) => actionprop(apx.data.wco[p], e));
|
|
//console.log(p, Object.hasOwnProperty(apx.wco));
|
|
if (Object.hasOwnProperty(apx.wco)) {
|
|
Object.defineProperty(apx.wco, p, {
|
|
set: (newv) => {
|
|
this[p] = newv;
|
|
elements.forEach((e) => actionprop(newv, e));
|
|
},
|
|
});
|
|
}
|
|
});
|
|
};
|
|
apx.wcoobserver = () => {
|
|
/**
|
|
* wco web component observer if apxtri.wcoobserver==true,
|
|
* Observe existing or creation of any element in DOM with <div wco-name="wconame" id="uniqueid" wco-YYY="aa"></div>
|
|
* if create or if any wco-YYY value change it runs apx[wconame].loadwco(id,ctx) where ctx={YYY:aa}
|
|
* Example:
|
|
* <div id="monComponent" wco-name="monComposant" wco-screen="dashboard" wco-theme="dark" wco-ref="123"></div>
|
|
* if innerHTML this OR if any wco-YYYY change =>
|
|
* run apx.monComposant.loadwco('monComponent',
|
|
* {'wco-name': 'monComposant',
|
|
* 'wco-screen': 'dashboard',
|
|
* 'wco-theme': 'dark',
|
|
* 'wco-ref': '123'});
|
|
* Used with wco component manage in apx to communicate between autonomous component to reload content if contexte change.
|
|
* Typical example of a wco menu that will load another wco content if not exist and will change a wco-screen to change the content of the wco content
|
|
*/
|
|
console.log("listen wcoobserver");
|
|
const processElement = (element) => {
|
|
if (element.nodeType === 1 && element.tagName === "DIV") {
|
|
if (element.id && element.hasAttribute("wco-name")) {
|
|
const ctx = {};
|
|
for (const attr of element.attributes) {
|
|
if (attr.name.startsWith("wco-")) {
|
|
ctx[attr.name.slice(4)] = attr.value;
|
|
}
|
|
}
|
|
|
|
const wcoName = element.getAttribute("wco-name");
|
|
if (apx[wcoName] && typeof apx[wcoName].loadwco === "function") {
|
|
apx[wcoName].loadwco(element.id, ctx);
|
|
} else {
|
|
console.log(`ERROR: apx.${wcoName}.loadwco() not found`);
|
|
}
|
|
}
|
|
}
|
|
// Process children recursively
|
|
if (element.children) {
|
|
Array.from(element.children).forEach(processElement);
|
|
}
|
|
};
|
|
|
|
const observer = new MutationObserver((mutations) => {
|
|
mutations.forEach((mutation) => {
|
|
if (mutation.type === "childList") {
|
|
mutation.addedNodes.forEach((node) => {
|
|
processElement(node);
|
|
});
|
|
}
|
|
|
|
if (
|
|
mutation.type === "attributes" &&
|
|
mutation.attributeName.startsWith("wco-")
|
|
) {
|
|
const element = mutation.target;
|
|
if (element.id && element.hasAttribute("wco-name")) {
|
|
const ctx = {};
|
|
for (const attr of element.attributes) {
|
|
if (attr.name.startsWith("wco-")) {
|
|
ctx[attr.name.slice(4)] = attr.value;
|
|
}
|
|
}
|
|
|
|
const wcoName = element.getAttribute("wco-name");
|
|
if (apx[wcoName] && typeof apx[wcoName].loadwco === "function") {
|
|
apx[wcoName].loadwco(element.id, ctx);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
observer.observe(document.body, {
|
|
childList: true,
|
|
subtree: true,
|
|
attributes: true, // <-- active la détection de changements d'attributs
|
|
});
|
|
|
|
// Pour les éléments déjà présents au chargement
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
document.querySelectorAll("div[wco-name]").forEach((element) => {
|
|
if (element.id) {
|
|
const ctx = {};
|
|
for (const attr of element.attributes) {
|
|
if (attr.name.startsWith("wco-")) {
|
|
ctx[attr.name.slice(4)] = attr.value;
|
|
}
|
|
}
|
|
const wcoName = element.getAttribute("wco-name");
|
|
console.log(`load observer of wco for ${wcoName} in id=${element.id}`);
|
|
if (apx[wcoName] && typeof apx[wcoName].loadwco === "function") {
|
|
apx[wcoName].loadwco(element.id, ctx);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
//load existing wco-name in the html page to initiate the wco process
|
|
// it read and write the wco-name that will trig the observer as a change
|
|
document.querySelectorAll("div[wco-name]").forEach((e) => {
|
|
const wconame = e.getAttribute("wco-name");
|
|
console.log("load wco-name:", wconame);
|
|
e.setAttribute("wco-name", wconame);
|
|
});
|
|
};
|
|
|
|
// State management
|
|
apx.save = () => {
|
|
localStorage.setItem(apx.data.headers.xapp, JSON.stringify(apx.data));
|
|
};
|
|
apx.update = async () => {
|
|
if (!apxtri) {
|
|
console.log(
|
|
'Please add to the html page header, this line const apxtri = { headers: { xtrkversion: 1, xtribe: "smatchit", xapp: "pwa", xlang: "fr", xalias: "anonymous", xhash: "anonymous", xdays: 0} ,pagename:"apxid"} '
|
|
);
|
|
return;
|
|
}
|
|
//if (apxtri.forcereload){localStorage.setItem("forcereload",true)};
|
|
if (document.querySelector("html").getAttribute("lang")) {
|
|
apxtri.headers.xlang = document.querySelector("html").getAttribute("lang");
|
|
}
|
|
//alert(localStorage.getItem(apxtri.headers.xapp))
|
|
if (localStorage.getItem(apxtri.headers.xapp)) {
|
|
apx.data = JSON.parse(localStorage.getItem(apxtri.headers.xapp));
|
|
//update with current pagename and eventualy pageauth
|
|
apx.data.pagename = apxtri.pagename;
|
|
if (apxtri.pageauth) apx.data.pageauth = apxtri.pageauth;
|
|
// check localstorage in line with current webpage
|
|
if (
|
|
apx.data.headers.xtribe != apxtri.headers.xtribe ||
|
|
apx.data.headers.xlang != apxtri.headers.xlang ||
|
|
apx.data.headers.xtrkversion != apxtri.headers.xtrkversion
|
|
) {
|
|
// if an app change of tribe
|
|
localStorage.removeItem(apxtri.headers.xapp);
|
|
delete apx.data;
|
|
}
|
|
}
|
|
if (!apx.data) {
|
|
console.log("init or reinit apx.data");
|
|
apx.data = apxtri;
|
|
}
|
|
apx.pagecontext = { search: {}, hash: {} };
|
|
if (window.location.hash != "") {
|
|
window.location.hash
|
|
.slice(1)
|
|
.split("&")
|
|
.forEach((kv) => {
|
|
const keyval = kv.split("=");
|
|
apx.pagecontext.hash[keyval[0]] = keyval[1];
|
|
});
|
|
}
|
|
if (window.location.search != "") {
|
|
window.location.search
|
|
.slice(1)
|
|
.split("&")
|
|
.forEach((kv) => {
|
|
const keyval = kv.split("=");
|
|
apx.pagecontext.hash[keyval[0]] = keyval[1];
|
|
});
|
|
}
|
|
console.log("apx.pagecontext:", apx.pagecontext);
|
|
|
|
// Set authenticate parameter if in pagecontext and redirect to the requested url
|
|
console.log(
|
|
apx.pagecontext.hash.xdays,
|
|
apx.pagecontext.hash.xprofils,
|
|
apx.pagecontext.hash.xtribe,
|
|
dayjs(apx.pagecontext.hash.xdays),
|
|
dayjs(apx.pagecontext.hash.xdays).diff(dayjs(), "hours") < 25,
|
|
apx.pagecontext.hash.xhash
|
|
);
|
|
if (
|
|
apx.pagecontext.hash.xhash &&
|
|
apx.pagecontext.hash.xdays &&
|
|
apx.pagecontext.hash.xprofils &&
|
|
apx.pagecontext.hash.xtribe &&
|
|
dayjs(apx.pagecontext.hash.xdays) &&
|
|
dayjs(apx.pagecontext.hash.xdays).diff(dayjs(), "hours") < 25
|
|
) {
|
|
//Means this page is called from an external auth app
|
|
let headervalid = true;
|
|
const headerkey = [
|
|
"xalias",
|
|
"xhash",
|
|
"xdays",
|
|
"xprofils",
|
|
"xtribe",
|
|
"xlang",
|
|
];
|
|
headerkey.forEach((h) => {
|
|
if (apx.pagecontext.hash[h]) {
|
|
apx.data.headers[h] = (h==="xprofils")? apx.pagecontext.hash[h].split(","):apx.pagecontext.hash[h];
|
|
} else {
|
|
headervalid = false;
|
|
}
|
|
});
|
|
console.log(headervalid, apx.data.headers);
|
|
if (headervalid) {
|
|
apx.save();
|
|
if (apx.pagecontext.hash.url) {
|
|
window.location.href = apx.pagecontext.hash.url;
|
|
}
|
|
} else {
|
|
console.log("Your try to access a page failled with ", apx.pagecontext);
|
|
}
|
|
}
|
|
if (
|
|
apx.data.allowedprofils &&
|
|
!apx.data.allowedprofils.includes("anonymous") &&
|
|
apx.data.pagename !== apx.data.pageauth
|
|
) {
|
|
const profilintersect = apx.data.allowedprofils.filter((x) =>
|
|
apx.data.headers.xprofils.includes(x)
|
|
);
|
|
console.log("profils authorized:", profilintersect);
|
|
if (profilintersect.length == 0) {
|
|
alert(apx.data.ref.Middlewares.notallowtoaccess);
|
|
return false;
|
|
}
|
|
if (dayjs().valueOf() - apx.data.headers.xdays > 86400000) {
|
|
// need to refresh authentification if possible by opening the pageauth with url context
|
|
// the pageauth redirect to this current page after authentification, if not then wait credential
|
|
document.location.href = `/${apx.data.pageauth}_${apx.data.headers.xlang}.html#url=${apx.data.pagename}_${apx.data.headers.xlang}.html`;
|
|
}
|
|
}
|
|
console.log("authorized to access");
|
|
/* à voir si utile redirect to authentification page pageauth with a redirection if authentify to the pagename (check if /src/ then add it)
|
|
window.location.href = `${apxtri.pageauth}_${
|
|
apxtri.headers.xlang
|
|
}.html?url=${window.location.href.includes("/src/") ? "/src/" : ""}${
|
|
apxtri.pagename
|
|
}_${apxtri.headers.xlang}.html`;
|
|
*/
|
|
////////////////////////////////////////////
|
|
apx.data.version = 0; //this force an update to be removed in production
|
|
///////////////////////////////////////////
|
|
const ano = apx.data.headers.xalias == "anonymous" ? "anonymous" : "";
|
|
const initdb = `/api/apxtri/wwws/updatelocaldb${ano}/${apx.data.headers.xtribe}/${apx.data.headers.xapp}/${apx.data.pagename}/${apx.data.version}`;
|
|
let initset = {};
|
|
try {
|
|
initset = await axios.get(initdb, {
|
|
headers: apx.data.headers,
|
|
timeout: 2000,
|
|
});
|
|
} catch (err) {
|
|
console.log(err);
|
|
initset = { data: { msg: "unavailableAPI" } };
|
|
}
|
|
console.log("recupe inidb for ", initdb, initset);
|
|
if (initset.data.msg == "forbidenaccess") {
|
|
alert(apx.data.ref.Middlewares.notallowtoaccess);
|
|
return false;
|
|
}
|
|
if (initset.data.msg == "unavailableAPI") {
|
|
console.log("Your api endpoint is down check your hosted server");
|
|
//try again in 30 seconds
|
|
setTimeout(apx.update, 30000);
|
|
}
|
|
if (initset.data.msg == "datamodelupdate") {
|
|
// mise à jour local
|
|
/*if (initset.data.data.wco) {
|
|
|
|
console.log("WARNING!!, local apxtri.wco was erase by updatelocaldb.wco");
|
|
}*/
|
|
Object.keys(initset.data.data).forEach((k) => {
|
|
if (k != "headers") {
|
|
apx.data[k] = initset.data.data[k];
|
|
}
|
|
});
|
|
/* if (apx.data.confpage.wco && !apx.data.wco){
|
|
console.log("update apx.data.wco with localdb cause does not exist")
|
|
apx.data.wco=apx.data.confpage.wco;
|
|
}
|
|
*/
|
|
console.log("local update done");
|
|
apx.save();
|
|
}
|
|
|
|
apx.listendatawco(); // listen any data-wco tag and update it when apxdatawco propertie change
|
|
if (apxtri.wcoobserver) apx.wcoobserver();
|
|
if (apx.afterupdate) apx.afterupdate.forEach((cb) => cb()); //run all function store in apx.afterupdate in order
|
|
apx.lazyload(); //reload image or any media that takes time to load to improve engine search
|
|
apx.save(); //store in local the modification
|
|
};
|
|
apx.ready(apx.update); //2nd param optional=> true mean does not wait same if apx.lock is set
|
|
|
|
apx.indexedDB = apx.indexedDB || {};
|
|
|
|
apx.indexedDB.set = async (db, storeName, keyPath, key, value) => {
|
|
return new Promise((resolve, reject) => {
|
|
const request = indexedDB.open(db, 1);
|
|
|
|
request.onupgradeneeded = (event) => {
|
|
const db = event.target.result;
|
|
|
|
if (window.threadsObj) {
|
|
for (const threadId of Object.keys(window.threadsObj)) {
|
|
if (!db.objectStoreNames.contains(threadId)) {
|
|
db.createObjectStore(threadId, { keyPath: keyPath });
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
request.onsuccess = (event) => {
|
|
const db = event.target.result;
|
|
|
|
if (!db.objectStoreNames.contains(storeName)) {
|
|
return resolve();
|
|
};
|
|
|
|
const transaction = db.transaction(storeName, "readwrite");
|
|
const store = transaction.objectStore(storeName);
|
|
|
|
const putRequest = store.put({ timestamp: key, value: value });
|
|
putRequest.onsuccess = () => resolve();
|
|
putRequest.onerror = (error) => reject(error);
|
|
};
|
|
|
|
request.onerror = (error) => reject(error);
|
|
});
|
|
};
|
|
|
|
apx.indexedDB.get = async (db, storeName, keyPath, key) => {
|
|
return new Promise((resolve, reject) => {
|
|
const request = indexedDB.open(db, 1);
|
|
|
|
request.onupgradeneeded = (event) => {
|
|
const db = event.target.result;
|
|
|
|
if (window.threadsObj) {
|
|
for (const threadId of Object.keys(window.threadsObj)) {
|
|
if (!db.objectStoreNames.contains(threadId)) {
|
|
db.createObjectStore(threadId, { keyPath: keyPath });
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
request.onsuccess = (event) => {
|
|
const db = event.target.result;
|
|
|
|
if (!db.objectStoreNames.contains(storeName)) {
|
|
return resolve(null);
|
|
};
|
|
|
|
const transaction = db.transaction(storeName, "readonly");
|
|
const store = transaction.objectStore(storeName);
|
|
const getRequest = store.get(key);
|
|
|
|
getRequest.onsuccess = () => {
|
|
resolve(getRequest.result ? getRequest.result.value : null);
|
|
};
|
|
|
|
getRequest.onerror = () => resolve(null);
|
|
};
|
|
|
|
request.onerror = (error) => reject(error);
|
|
});
|
|
};
|
|
|
|
apx.indexedDB.del = async (db, storeName, keyPath, key) => {
|
|
return new Promise((resolve, reject) => {
|
|
const request = indexedDB.open(db, 1);
|
|
|
|
request.onupgradeneeded = (event) => {
|
|
const db = event.target.result;
|
|
|
|
if (window.threadsObj) {
|
|
for (const threadId of Object.keys(window.threadsObj)) {
|
|
if (!db.objectStoreNames.contains(threadId)) {
|
|
db.createObjectStore(threadId, { keyPath: keyPath });
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
request.onsuccess = (event) => {
|
|
const db = event.target.result;
|
|
|
|
if (!db.objectStoreNames.contains(storeName)) {
|
|
return resolve();
|
|
};
|
|
|
|
const transaction = db.transaction(storeName, "readwrite");
|
|
const store = transaction.objectStore(storeName);
|
|
|
|
const deleteRequest = store.delete(key);
|
|
deleteRequest.onsuccess = () => resolve();
|
|
deleteRequest.onerror = (error) => reject(error);
|
|
};
|
|
|
|
request.onerror = (error) => reject(error);
|
|
});
|
|
}; |