diff --git a/oa/front/audio.html b/oa/front/audio.html new file mode 100644 index 0000000..07b5f23 --- /dev/null +++ b/oa/front/audio.html @@ -0,0 +1,50 @@ + + + + + + + Music Player + + + + +
+ + + +
+
+
+ + + + + diff --git a/oa/front/card.html b/oa/front/card.html new file mode 100644 index 0000000..58a05b2 --- /dev/null +++ b/oa/front/card.html @@ -0,0 +1,26 @@ + + + + + + Card + + + + +
+ slot a +
+
+ +
+
+
+ + + + diff --git a/oa/front/favicon.ico b/oa/front/favicon.ico new file mode 100644 index 0000000..e26bc40 Binary files /dev/null and b/oa/front/favicon.ico differ diff --git a/oa/front/index.html b/oa/front/index.html new file mode 100644 index 0000000..6e89a6b --- /dev/null +++ b/oa/front/index.html @@ -0,0 +1,31 @@ + + + + + + Index + + + + + +
+
123
+
123
+
123
+
+
111
+
c1
+
333
+
+
+
c2
+
+
+ + + + + diff --git a/oa/front/layout/default.html b/oa/front/layout/default.html new file mode 100644 index 0000000..3b9a9b1 --- /dev/null +++ b/oa/front/layout/default.html @@ -0,0 +1,12 @@ + + + Default + + +
+
+ +
+ diff --git a/oa/front/router.html b/oa/front/router.html new file mode 100644 index 0000000..969d7a5 --- /dev/null +++ b/oa/front/router.html @@ -0,0 +1,6 @@ + + > + + + diff --git a/oa/front/tailwind.config.js b/oa/front/tailwind.config.js new file mode 100644 index 0000000..16b1323 --- /dev/null +++ b/oa/front/tailwind.config.js @@ -0,0 +1,28 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./components/**/*.{js,vue,ts}", + "./layouts/**/*.vue", + "./pages/**/*.vue", + "./plugins/**/*.{js,ts}", + "./app.vue", + "./error.vue", + "*.html", + ], + theme: { + extend: { + colors: { + vprimary: '#2196f3', + vsecondary: '#ecc94b', + vaccents: '#ff9800', + verror: '#f44336', + vwaring: '#ff5722', + vinfo: '#ffc107', + vsuccess: '#53de58', + vignore: '#d1d5db', + } + }, + }, + plugins: [], +} + diff --git a/oa/front/v.js b/oa/front/v.js new file mode 100644 index 0000000..c2f26c8 --- /dev/null +++ b/oa/front/v.js @@ -0,0 +1,158 @@ +/* + * 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) + }); + +})(); diff --git a/oa/front/v2dom/v.js b/oa/front/v2dom/v.js new file mode 100644 index 0000000..f153290 --- /dev/null +++ b/oa/front/v2dom/v.js @@ -0,0 +1,10 @@ +/* + * v.js + * Copyright (C) 2024 veypi + * + * Distributed under terms of the GPL license. + */ + +export default { + a: 123 +}