mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
feature: add gelf, a gunified elf for managing pvp hypergraphs from elf world
This commit is contained in:
parent
7cc4cce1a3
commit
c92bdaf97b
213
examples/gelf/gelf.js
Normal file
213
examples/gelf/gelf.js
Normal file
@ -0,0 +1,213 @@
|
||||
import '../../../gun/gun.js'
|
||||
const gun = window.Gun(location.origin + '/gun');
|
||||
|
||||
const database = {}
|
||||
const logs = {}
|
||||
|
||||
export function insights() {
|
||||
return logs
|
||||
}
|
||||
|
||||
function insight(name, link) {
|
||||
if(!logs[`${name}:${link}`]) {
|
||||
logs[`${name}:${link}`] = 0
|
||||
}
|
||||
logs[`${name}:${link}`] += 1
|
||||
}
|
||||
|
||||
const CREATE_EVENT = 'create'
|
||||
|
||||
const observableEvents = [CREATE_EVENT]
|
||||
|
||||
function update(link, target, compositor, lifeCycle={}) {
|
||||
insight('module:update', link)
|
||||
if(lifeCycle.beforeUpdate) {
|
||||
lifeCycle.beforeUpdate(target)
|
||||
}
|
||||
|
||||
const html = compositor(target)
|
||||
if(html) target.innerHTML = html
|
||||
|
||||
if(lifeCycle.afterUpdate) {
|
||||
lifeCycle.afterUpdate(target)
|
||||
}
|
||||
}
|
||||
|
||||
function draw(link, compositor, lifeCycle={}) {
|
||||
insight('module:draw', link)
|
||||
listen(CREATE_EVENT, link, (event) => {
|
||||
gun.get(this.seed).get(link).on(cache => {
|
||||
database[link] = JSON.parse(cache) || {}
|
||||
update(link, event.target, compositor, lifeCycle)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function style(link, stylesheet) {
|
||||
insight('module:style', link)
|
||||
const styles = `
|
||||
<style type="text/css" data-link="${link}">
|
||||
${stylesheet.replaceAll('&', link)}
|
||||
</style>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML("beforeend", styles)
|
||||
}
|
||||
|
||||
export function learn(link) {
|
||||
insight('module:learn', link)
|
||||
return database[link] || {}
|
||||
}
|
||||
|
||||
export function teach(link, knowledge, nuance = (s, p) => ({...s,...p})) {
|
||||
insight('module:teach', link)
|
||||
gun.get(this.seed).get(link).once(cache => {
|
||||
const data = cache ? JSON.parse(cache) : {}
|
||||
const latest = nuance(data, knowledge);
|
||||
gun.get(this.seed).get(link).put(JSON.stringify(latest))
|
||||
})
|
||||
}
|
||||
|
||||
export function when(link1, type, link2, callback) {
|
||||
const link = `${link1} ${link2}`
|
||||
insight('module:when:'+type, link)
|
||||
listen(type, link, callback)
|
||||
}
|
||||
|
||||
export default function module(link, initialState = {}) {
|
||||
insight('module', link)
|
||||
teach.call(this, link, initialState)
|
||||
|
||||
return {
|
||||
link,
|
||||
learn: learn.bind(this, link),
|
||||
draw: draw.bind(this, link),
|
||||
style: style.bind(this, link),
|
||||
when: when.bind(this, link),
|
||||
teach: teach.bind(this, link),
|
||||
}
|
||||
}
|
||||
|
||||
export function subscribe(fun) {
|
||||
notifications[fun.toString] = fun
|
||||
}
|
||||
|
||||
export function unsubscribe(fun) {
|
||||
if(notifications[fun.toString]) {
|
||||
delete notifications[fun.toString]
|
||||
}
|
||||
}
|
||||
|
||||
export function listen(type, link, handler = () => null) {
|
||||
const callback = (event) => {
|
||||
if(
|
||||
event.target &&
|
||||
event.target.matches &&
|
||||
event.target.matches(link)
|
||||
) {
|
||||
|
||||
insight('module:listen:'+type, link)
|
||||
handler.call(null, event);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener(type, callback, true);
|
||||
|
||||
if(observableEvents.includes(type)) {
|
||||
observe(link);
|
||||
}
|
||||
|
||||
return function unlisten() {
|
||||
if(type === CREATE_EVENT) {
|
||||
disregard(link);
|
||||
}
|
||||
|
||||
document.removeEventListener(type, callback, true);
|
||||
}
|
||||
}
|
||||
|
||||
let links = []
|
||||
|
||||
function observe(link) {
|
||||
links = [...new Set([...links, link])];
|
||||
maybeCreateReactive([...document.querySelectorAll(link)])
|
||||
}
|
||||
|
||||
function disregard(link) {
|
||||
const index = links.indexOf(link);
|
||||
if(index >= 0) {
|
||||
links = [
|
||||
...links.slice(0, index),
|
||||
...links.slice(index + 1)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function maybeCreateReactive(targets) {
|
||||
targets
|
||||
.filter(x => !x.reactive)
|
||||
.forEach(dispatchCreate)
|
||||
}
|
||||
|
||||
function getSubscribers({ target }) {
|
||||
if(links.length > 0)
|
||||
return [...target.querySelectorAll(links.join(', '))];
|
||||
else
|
||||
return []
|
||||
}
|
||||
|
||||
function dispatchCreate(target) {
|
||||
insight('module:create', target.localName)
|
||||
if(!target.id) target.id = sufficientlyUniqueId()
|
||||
target.dispatchEvent(new Event(CREATE_EVENT))
|
||||
target.reactive = true
|
||||
}
|
||||
|
||||
new MutationObserver((mutationsList) => {
|
||||
const targets = [...mutationsList]
|
||||
.map(getSubscribers)
|
||||
.flatMap(x => x)
|
||||
maybeCreateReactive(targets)
|
||||
}).observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
function sufficientlyUniqueId() {
|
||||
// https://stackoverflow.com/a/2117523
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
tags({ registry: '/examples/gelf/tags' })
|
||||
new MutationObserver(() => {
|
||||
tags({ registry: '/examples/gelf/tags' })
|
||||
}).observe(document.body, { childList: true, subtree: true });
|
||||
function tags({ registry }) {
|
||||
const tags = new Set(
|
||||
[...document.querySelectorAll(':not(:defined)')]
|
||||
.map(({ tagName }) => tagName.toLowerCase())
|
||||
)
|
||||
|
||||
tags.forEach(async (tag) => {
|
||||
const url = `${registry || '.'}/${tag}.js`
|
||||
const exists = (await fetch(url, { method: 'HEAD' })).ok
|
||||
if(!exists) return
|
||||
let definable = true
|
||||
await import(url).catch((e) => {
|
||||
definable = false
|
||||
console.error(e)
|
||||
})
|
||||
try {
|
||||
definable = definable && document.querySelector(tag) && document.querySelector(tag).matches(':not(:defined)')
|
||||
if(definable) {
|
||||
customElements.define(tag, class WebComponent extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch(e) {
|
||||
console.log('Error defining module:', tag, e)
|
||||
}
|
||||
})
|
||||
}
|
2
examples/gelf/hello-world.html
Normal file
2
examples/gelf/hello-world.html
Normal file
@ -0,0 +1,2 @@
|
||||
<hello-world></hello-world>
|
||||
<script type="module" src='./gelf.js'></script>
|
27
examples/gelf/note.html
Normal file
27
examples/gelf/note.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<style>html, body, textarea { width: 100%; height: 100%; padding: 0; margin: 0; }</style>
|
||||
<gelf-note></gelf-note>
|
||||
<script type="module">
|
||||
import gelf from './gelf.js'
|
||||
|
||||
const context = {
|
||||
seed: location.hash.replace('#','') || 1
|
||||
}
|
||||
|
||||
const $ = gelf.call(context, 'gelf-note')
|
||||
|
||||
$.draw((target) => {
|
||||
const { value } = $.learn()
|
||||
|
||||
if(!target.innerHTML) {
|
||||
target.innerHTML = `<textarea id="view" placeholder="write here..."></textarea>`
|
||||
target.view = target.querySelector('#view')
|
||||
}
|
||||
|
||||
target.view.value = value
|
||||
})
|
||||
|
||||
$.when('input', 'textarea', ({target}) => $.teach({
|
||||
value: target.value
|
||||
}))
|
||||
</script>
|
3
examples/gelf/tags/hello-world.js
Normal file
3
examples/gelf/tags/hello-world.js
Normal file
@ -0,0 +1,3 @@
|
||||
import gelf from '/examples/gelf/gelf.js'
|
||||
|
||||
gelf('hello-world').draw(() => `Hello World`)
|
Loading…
x
Reference in New Issue
Block a user