new wco wwws compatible with new backeend
This commit is contained in:
@@ -484,7 +484,7 @@ apx.update = async () => {
|
||||
//try again in 30 seconds
|
||||
setTimeout(apx.update, 30000);
|
||||
}
|
||||
if (initset.data.msg == "datamodelupdate") {
|
||||
if (initset.data.msg == "data_model_update") {
|
||||
// mise à jour local
|
||||
/*if (initset.data.data.wco) {
|
||||
|
||||
|
334
wco/apx/apxgeminicli.js
Normal file
334
wco/apx/apxgeminicli.js
Normal file
@@ -0,0 +1,334 @@
|
||||
/* eslint-env browser */
|
||||
/* eslint-disable no-alert, no-console */
|
||||
|
||||
/**
|
||||
* @file apx.js (previously apxnew.js)
|
||||
* @description Modern, class-based implementation to manage data and interactions with an apxtri instance from a webpage.
|
||||
* @version 2.1
|
||||
* @author support@ndda.fr
|
||||
*/
|
||||
|
||||
// Establish the global namespace
|
||||
window.apx = window.apx || {};
|
||||
|
||||
/**
|
||||
* @class ApxManager
|
||||
* Manages the core application state and lifecycle.
|
||||
*/
|
||||
class ApxManager {
|
||||
constructor() {
|
||||
this.data = {};
|
||||
this.pageContext = { search: {}, hash: {} };
|
||||
this.afterUpdateCallbacks = [];
|
||||
this.wcoProxies = {};
|
||||
// Capture the observer flag immediately from the initial global config.
|
||||
this.useWcoObserver = window.apxtri?.wcoobserver || false;
|
||||
}
|
||||
|
||||
ready(callback) {
|
||||
if (typeof callback !== 'function') {
|
||||
alert("Apx.ready(callback) requires a valid function.");
|
||||
return;
|
||||
}
|
||||
if (document.readyState !== 'loading') {
|
||||
callback();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', callback);
|
||||
}
|
||||
}
|
||||
|
||||
registerUpdateCallback(callback) {
|
||||
this.afterUpdateCallbacks.push(callback);
|
||||
}
|
||||
|
||||
lazyLoad() {
|
||||
const { xalias, xuuid, xtrkversion, xlang } = this.data.headers;
|
||||
const consentCookie = localStorage.getItem('consentcookie');
|
||||
|
||||
document.querySelectorAll('img[data-lazysrc]').forEach(img => {
|
||||
let src = img.dataset.lazysrc;
|
||||
if (img.dataset.trksrckey) {
|
||||
src = `/trk/${src}?alias=${xalias}&uuid=${xuuid}&srckey=${img.dataset.trksrckey}&version=${xtrkversion}&consentcookie=${consentCookie}&lg=${xlang}`;
|
||||
}
|
||||
img.setAttribute('src', src);
|
||||
img.removeAttribute('data-lazysrc');
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-lazybgsrc]').forEach(el => {
|
||||
el.style.backgroundImage = `url(${el.dataset.lazybgsrc})`;
|
||||
el.removeAttribute('data-lazybgsrc');
|
||||
});
|
||||
|
||||
document.querySelectorAll('a[data-trksrckey]').forEach(a => {
|
||||
let urlDest = a.getAttribute('href');
|
||||
if (!urlDest.startsWith('http') && !urlDest.startsWith('/')) {
|
||||
urlDest = `/${urlDest}`;
|
||||
}
|
||||
const trackedHref = `/trk/redirect?alias=${xalias}&uuid=${xuuid}&srckey=${a.dataset.trksrckey}&version=${xtrkversion}&consentcookie=${consentCookie}&lg=${xlang}&url=${encodeURIComponent(urlDest)}`;
|
||||
a.setAttribute('href', trackedHref);
|
||||
a.removeAttribute('data-trksrckey');
|
||||
});
|
||||
}
|
||||
|
||||
notify(selector, responseData, clearBefore = false) {
|
||||
const el = document.querySelector(selector);
|
||||
if (!el) {
|
||||
console.warn(`Notification selector not found: ${selector}`);
|
||||
return;
|
||||
}
|
||||
if (clearBefore) {
|
||||
el.innerHTML = '';
|
||||
}
|
||||
|
||||
const messages = responseData.multimsg || [responseData];
|
||||
messages.forEach(info => {
|
||||
const template = this.data.ref?.[info.ref]?.[info.msg];
|
||||
if (!template) {
|
||||
console.warn(`Notification template not found for ref: ${info.ref}, msg: ${info.msg}`);
|
||||
return;
|
||||
}
|
||||
el.innerHTML += Mustache.render(template, info.data);
|
||||
});
|
||||
|
||||
if (responseData.status === 200) {
|
||||
el.classList.remove('text-red');
|
||||
el.classList.add('text-green');
|
||||
} else {
|
||||
el.classList.add('text-red');
|
||||
el.classList.remove('text-green');
|
||||
}
|
||||
}
|
||||
|
||||
listenWcoData() {
|
||||
if (!this.data.wco || Object.keys(this.data.wco).length === 0) return;
|
||||
|
||||
const updateElement = (element, value) => {
|
||||
if (value.innerHTML !== undefined) element.innerHTML = value.innerHTML;
|
||||
if (value.textContent !== undefined) element.textContent = value.textContent;
|
||||
['src', 'alt', 'placeholder', 'class', 'href'].forEach(attr => {
|
||||
if (value[attr] !== undefined) element.setAttribute(attr, value[attr]);
|
||||
});
|
||||
};
|
||||
|
||||
for (const prop in this.data.wco) {
|
||||
if (Object.hasOwnProperty.call(this.data.wco, prop) && !this.wcoProxies[prop]) {
|
||||
const elements = document.querySelectorAll(`[data-wco='${prop}']`);
|
||||
elements.forEach(el => updateElement(el, this.data.wco[prop]));
|
||||
|
||||
this.wcoProxies[prop] = new Proxy(this.data.wco[prop], {
|
||||
set: (target, key, value) => {
|
||||
target[key] = value;
|
||||
elements.forEach(el => updateElement(el, target));
|
||||
return true;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
this.wco = new Proxy(this.data.wco, {
|
||||
set: (target, prop, value) => {
|
||||
target[prop] = value;
|
||||
this.listenWcoData();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
startWcoObserver() {
|
||||
console.log('[APX] Starting WCO observer and triggering initial load...');
|
||||
|
||||
const processElement = (element) => {
|
||||
if (element.nodeType !== 1 || !element.hasAttribute('wco-name') || !element.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wcoName = element.getAttribute('wco-name');
|
||||
const wcoModule = window.apx?.[wcoName];
|
||||
|
||||
if (wcoModule && typeof wcoModule.loadwco === 'function') {
|
||||
console.log(`[APX] Observer is processing component: ${wcoName} on #${element.id}`);
|
||||
const ctx = {};
|
||||
for (const attr of element.attributes) {
|
||||
if (attr.name.startsWith('wco-')) {
|
||||
ctx[attr.name.slice(4)] = attr.value;
|
||||
}
|
||||
}
|
||||
wcoModule.loadwco(element.id, ctx);
|
||||
} else {
|
||||
console.warn(`[APX] WCO handler not found for component: apx.${wcoName}`);
|
||||
}
|
||||
};
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === 'childList') {
|
||||
mutation.addedNodes.forEach(processElement);
|
||||
}
|
||||
if (mutation.type === 'attributes' && mutation.attributeName.startsWith('wco-')) {
|
||||
processElement(mutation.target);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
});
|
||||
|
||||
// This is the key part from your original logic:
|
||||
// Manually trigger the observer for components already in the DOM.
|
||||
document.querySelectorAll('div[wco-name]').forEach((element) => {
|
||||
const wcoName = element.getAttribute('wco-name');
|
||||
console.log(`[APX] Manually triggering initial load for: ${wcoName}`);
|
||||
element.setAttribute('wco-name', wcoName);
|
||||
});
|
||||
}
|
||||
|
||||
saveState() {
|
||||
localStorage.setItem(this.data.headers.xapp, JSON.stringify(this.data));
|
||||
}
|
||||
|
||||
parseUrl() {
|
||||
const parse = (str) => str.slice(1).split('&').reduce((acc, kv) => {
|
||||
if (kv) {
|
||||
const [key, value] = kv.split('=');
|
||||
acc[key] = decodeURIComponent(value);
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
this.pageContext.search = parse(window.location.search);
|
||||
this.pageContext.hash = parse(window.location.hash);
|
||||
}
|
||||
|
||||
loadState() {
|
||||
if (typeof apxtri === 'undefined') {
|
||||
throw new Error('Global `apxtri` configuration object is missing.');
|
||||
}
|
||||
|
||||
const storedState = localStorage.getItem(apxtri.headers.xapp);
|
||||
if (storedState) {
|
||||
this.data = JSON.parse(storedState);
|
||||
// Invalidate cache if key configuration has changed
|
||||
if (
|
||||
this.data.headers.xtribe !== apxtri.headers.xtribe ||
|
||||
this.data.headers.xlang !== apxtri.headers.xlang ||
|
||||
this.data.headers.xtrkversion !== apxtri.headers.xtrkversion
|
||||
) {
|
||||
localStorage.removeItem(apxtri.headers.xapp);
|
||||
// Create a deep copy to prevent modifying the global apxtri object
|
||||
this.data = JSON.parse(JSON.stringify(apxtri));
|
||||
}
|
||||
} else {
|
||||
// Create a deep copy to prevent modifying the global apxtri object
|
||||
this.data = JSON.parse(JSON.stringify(apxtri));
|
||||
}
|
||||
// Always update with current page info from the original config
|
||||
this.data.pagename = apxtri.pagename;
|
||||
if (apxtri.pageauth) this.data.pageauth = apxtri.pageauth;
|
||||
}
|
||||
|
||||
handleAuthRedirect() {
|
||||
const { xhash, xdays, xprofils, xtribe, url } = this.pageContext.hash;
|
||||
if (xhash && xdays && xprofils && xtribe && dayjs(xdays).isValid() && dayjs(xdays).diff(dayjs(), 'hours') < 25) {
|
||||
const headerKeys = ['xalias', 'xhash', 'xdays', 'xprofils', 'xtribe', 'xlang'];
|
||||
const newHeaders = {};
|
||||
let isValid = true;
|
||||
|
||||
headerKeys.forEach(key => {
|
||||
if (this.pageContext.hash[key]) {
|
||||
newHeaders[key] = (key === 'xprofils') ? this.pageContext.hash[key].split(',') : this.pageContext.hash[key];
|
||||
} else {
|
||||
isValid = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (isValid) {
|
||||
this.data.headers = { ...this.data.headers, ...newHeaders };
|
||||
this.saveState();
|
||||
if (url) {
|
||||
window.location.href = url;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
checkAccess() {
|
||||
const { allowedprofils, pagename, pageauth, headers } = this.data;
|
||||
if (allowedprofils && !allowedprofils.includes('anonymous') && pagename !== pageauth) {
|
||||
const hasAccess = allowedprofils.some(p => headers.xprofils?.includes(p));
|
||||
|
||||
if (!hasAccess) {
|
||||
alert(this.data.ref?.Middlewares?.notallowtoaccess || 'Access denied.');
|
||||
return false;
|
||||
}
|
||||
if (dayjs().valueOf() - headers.xdays > 86400000) {
|
||||
const redirectUrl = `/${pageauth}_${headers.xlang}.html#url=${pagename}_${headers.xlang}.html`;
|
||||
document.location.href = redirectUrl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async fetchAndUpdateDataModel() {
|
||||
this.data.version = 0;
|
||||
|
||||
const { xalias, xtribe, xapp } = this.data.headers;
|
||||
const ano = (xalias === 'anonymous') ? 'anonymous' : '';
|
||||
const url = `/api/apxtri/wwws/updatelocaldb${ano}/${xtribe}/${xapp}/${this.data.pagename}/${this.data.version}`;
|
||||
|
||||
try {
|
||||
const response = await axios.get(url, { headers: this.data.headers, timeout: 2000 });
|
||||
|
||||
if (response.data.msg === 'datamodelupdate') {
|
||||
Object.keys(response.data.data).forEach(key => {
|
||||
if (key !== 'headers') {
|
||||
this.data[key] = response.data.data[key];
|
||||
}
|
||||
});
|
||||
this.saveState();
|
||||
console.log('Local data model updated.');
|
||||
} else if (response.data.msg === 'forbidenaccess') {
|
||||
alert(this.data.ref?.Middlewares?.notallowtoaccess || 'Access denied by API.');
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('API is unavailable. Retrying in 30 seconds.', error);
|
||||
setTimeout(() => this.init(), 30000);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.loadState();
|
||||
this.parseUrl();
|
||||
|
||||
if (this.handleAuthRedirect()) return;
|
||||
if (!this.checkAccess()) return;
|
||||
|
||||
if (await this.fetchAndUpdateDataModel()) {
|
||||
this.listenWcoData();
|
||||
|
||||
// This is the correct place to check and start the observer.
|
||||
if (window.apxtri?.wcoobserver) {
|
||||
this.startWcoObserver();
|
||||
} else {
|
||||
console.log('[APX] wcoobserver is not enabled.');
|
||||
}
|
||||
|
||||
this.afterUpdateCallbacks.forEach(cb => cb());
|
||||
this.lazyLoad();
|
||||
this.saveState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate the manager and attach it to the global namespace
|
||||
apx.main = new ApxManager();
|
||||
|
||||
// Start the application lifecycle
|
||||
apx.main.ready(() => apx.main.init());
|
Reference in New Issue
Block a user