You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/oa/front/v.js

159 lines
4.0 KiB
JavaScript

11 months ago
/*
* v.js
* Copyright (C) 2024 veypi <i@veypi.com>
*
* Distributed under terms of the GPL license.
*/
(function () {
'use strict';
const config = { childList: true, subtree: true };
const callback = function (mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
// console.log(mutation)
// console.log('A child node has been added or removed.');
}
}
};
const observer = new MutationObserver(callback);
var cacheUrl = {}
var pendingRequests = {};
async function fetchFileWithCache(url) {
if (cacheUrl[url]) {
return Promise.resolve(cacheUrl[url]);
}
if (pendingRequests[url]) {
return pendingRequests[url];
}
const promise = fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
})
.then(txt => {
cacheUrl[url] = txt;
return txt;
})
.catch(error => {
console.error('Error fetching the file:', error);
delete pendingRequests[url];
throw error;
})
.finally(() => {
delete pendingRequests[url];
});
pendingRequests[url] = promise;
return promise;
}
function extractBodyAndScript(htmlString) {
const bodyMatch = htmlString.match(/<body[^>]*>([\s\S]*)<\/body>/i);
const bodyContent = bodyMatch ? bodyMatch[1] : '';
let scriptMatches = htmlString.match(/<script[^>]*>([\s\S]*?)<\/script>/ig);
let scriptContents = []
let srcReg = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/
for (let s of scriptMatches) {
let match = srcReg.exec(s)
if (match && match.length) {
// handle src script
// console.log(match[1])
} else {
scriptContents.push(s.replace(/<script[^>]*>|<\/script>/ig, ''))
}
}
return {
bodyContent,
scriptContents
};
}
/*
* @param {HTMLElement} node
* @return {Promise<void>}
* */
async function setupRef(node, data, itercount = 0) {
console.log(itercount)
if (itercount > 100) {
log.warn('infinite loop')
return
}
const ref = node.getAttribute('ref')
node.setAttribute('tag', ref)
node.removeAttribute('ref')
const txt = await fetchFileWithCache(ref + ".html")
let ast = extractBodyAndScript(txt)
let div = document.createElement('div')
div.innerHTML = ast.bodyContent
handleSlots(node, div)
//
node.innerHTML = div.innerHTML
let resp = new Function("{vnode,data}", ast.scriptContents.join('\n'))({ vnode: node, data: data })
for (let n of node.querySelectorAll("div[ref]")) {
// 确保子元素先加载完
await setupRef(n, resp?.data || {}, itercount + 1)
}
}
function handleSlots(origin, template) {
// handle slots
let originSnap = { '': [] }
let i = 0
let child
while (child = origin.children.item(i)) {
i++
let slotname = child.getAttribute ? child.getAttribute('slot') || '' : ''
if (slotname !== '') {
child.removeAttribute('slot')
originSnap[slotname] = child
} else {
originSnap[''].push(child)
}
}
app.b = originSnap
let slots = template.querySelectorAll('slot')
slots.forEach((slot) => {
let slotname = slot.getAttribute('name') || ''
let snap = originSnap[slotname]
if (snap && snap.length !== 0) {
if (slotname === '') {
slot.replaceWith(...snap)
} else {
slot.replaceWith(snap)
}
} else {
slot.replaceWith(...slot.childNodes)
}
})
}
document.addEventListener('DOMContentLoaded', () => {
console.log('DOMContentLoaded');
const node = document.getElementById('app');
observer.observe(node, config);
const sync = () => {
let test = node.querySelectorAll("div[ref]")
test.forEach(n => setupRef(n, app))
}
sync()
// setInterval(sync, 10)
});
})();