diff --git a/examples/gelf/array-test.html b/examples/gelf/array-test.html new file mode 100644 index 000000000..e863eb92c --- /dev/null +++ b/examples/gelf/array-test.html @@ -0,0 +1,25 @@ + + diff --git a/examples/gelf/gelf.js b/examples/gelf/gelf.js new file mode 100644 index 000000000..02dc092f3 --- /dev/null +++ b/examples/gelf/gelf.js @@ -0,0 +1,218 @@ +import '../../../gun/gun.js' +import '../../lib/open.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) => { + this.get(link).open(cache => { + debugger + database[link] = cache || {} + update(link, event.target, compositor, lifeCycle) + }) + }) +} + +function style(link, stylesheet) { + insight('module:style', link) + const styles = ` + + `; + + 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) + const process = this.get(link).open(cache => { + debugger + const latest = nuance(cache, knowledge); + this.get(link).put(latest) + process.off() + }) +} + +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) + + const seed = this && this.seed ? this.seed : 'root' + const root = gun.get(seed) + teach.call(root, link, initialState) + return { + link, + learn: learn.bind(root, link), + draw: draw.bind(root, link), + style: style.bind(root, link), + when: when.bind(root, link), + teach: teach.bind(root, 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) + } + }) +} diff --git a/examples/gelf/hello-world.html b/examples/gelf/hello-world.html new file mode 100644 index 000000000..b56f03877 --- /dev/null +++ b/examples/gelf/hello-world.html @@ -0,0 +1,2 @@ + + diff --git a/examples/gelf/note.html b/examples/gelf/note.html new file mode 100644 index 000000000..9a88267be --- /dev/null +++ b/examples/gelf/note.html @@ -0,0 +1,23 @@ + + + + + + diff --git a/examples/gelf/tags/hello-world.js b/examples/gelf/tags/hello-world.js new file mode 100644 index 000000000..8fedf7157 --- /dev/null +++ b/examples/gelf/tags/hello-world.js @@ -0,0 +1,3 @@ +import gelf from '/examples/gelf/gelf.js' + +gelf('hello-world').draw(() => `Hello World`) diff --git a/examples/gelf/terminal.html b/examples/gelf/terminal.html new file mode 100644 index 000000000..a95cc71b5 --- /dev/null +++ b/examples/gelf/terminal.html @@ -0,0 +1,1005 @@ + + + + diff --git a/gun.js b/gun.js index fc34f07ba..904196fdb 100644 --- a/gun.js +++ b/gun.js @@ -1250,6 +1250,9 @@ } k && (to.path || (to.path = [])).push(k); if(!(v = valid(d)) && !(g = Gun.is(d))){ + if(as.array && d instanceof Array) { + tmp = {}; Object.keys(d).forEach(function(v,i){ tmp[i] = v }); d = tmp; + } if(!Object.plain(d)){ ran.err(as, "Invalid data: "+ check(d) +" at " + (as.via.back(function(at){at.get && tmp.push(at.get)}, tmp = []) || tmp.join('.'))+'.'+(to.path||[]).join('.')); return } var seen = as.seen || (as.seen = []), i = seen.length; while(i--){ if(d === (tmp = seen[i]).it){ v = d = tmp.link; break } } diff --git a/src/put.js b/src/put.js index 6caa43629..40c50157d 100644 --- a/src/put.js +++ b/src/put.js @@ -29,6 +29,9 @@ Gun.chain.put = function(data, cb, as){ // I rewrote it :) } k && (to.path || (to.path = [])).push(k); if(!(v = valid(d)) && !(g = Gun.is(d))){ + if(as.array && d instanceof Array) { + tmp = {}; Object.keys(d).forEach(function(v,i){ tmp[i] = v }); d = tmp; + } if(!Object.plain(d)){ ran.err(as, "Invalid data: "+ check(d) +" at " + (as.via.back(function(at){at.get && tmp.push(at.get)}, tmp = []) || tmp.join('.'))+'.'+(to.path||[]).join('.')); return } var seen = as.seen || (as.seen = []), i = seen.length; while(i--){ if(d === (tmp = seen[i]).it){ v = d = tmp.link; break } }