/* * v.js * Copyright (C) 2024 veypi * * 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(/]*>([\s\S]*)<\/body>/i); const bodyContent = bodyMatch ? bodyMatch[1] : ''; let scriptMatches = htmlString.match(/]*>([\s\S]*?)<\/script>/ig); let scriptContents = [] let srcReg = /]*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>/ig, '')) } } return { bodyContent, scriptContents }; } /* * @param {HTMLElement} node * @return {Promise} * */ 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) }); })();