UNPKG

@anywhichway/lazui

Version:

Single page apps and lazy loading sites with minimal JavaScript or client build processes.

83 lines (81 loc) 3.62 kB
const patch = (target,value) => { Object.entries(value).forEach(([key,value]) => { if(value && typeof value === "object") { if(!target[key] || typeof target[key]!=="object") target[key] = {}; patch(target[key],value); } else { target[key] = value; } }) }; const state = async({el,attribute,root,options,window,document,lazui,args}) => { const {getState,setState,JSON,router,prefix} = lazui, src = el.getAttribute('data-lz:src'); let id, state, created, loaded; if(attribute.value.startsWith("{")) { state = JSON.parse(attribute.value); } else { id = attribute.value; } if (!el.id) { el.setAttribute("id",id || `state${(Math.random()+"").substring(2)}`); } if(!state) state = getState(el.id,{root,options}); if(!state) { if(src) { const response = await router.fetch(src); if(response.status===200) { const text = await response.text(); try { state = JSON.parse(text); } catch(e) { throw new Error(`${prefix}:state ${src} is not valid JSON`); } loaded = src; } else if(response.status!==404) { throw new Error(`${prefix}:state ${src} returned ${response.status}`); } } if(!state) { try { state = JSON.parse(el.innerText.trim()||el.innerHTML.trim()); created = true; if(src) { const response = await router.fetch(new Request(src,{method:"PUT",body:JSON.stringify(state),headers:{"content-type":"application/json"}})); if(response.status!==200) { throw new Error(`${prefix}:state PUT ${src} returned ${response.status}`); } } } catch(e) { if(el.hasAttribute('data-lz:src')) { throw new Error(`${prefix}:state ${src} was not found and ${el.id} is not valid JSON ${e}`) } throw new Error(`${prefix}:state ${el.id} is not valid JSON ${e}`); } } } state = setState(el,state,{root,options}); if(loaded) state.dispatchEvent(new CustomEvent("state:loaded",{bubbles:true,detail:{state,src:loaded}})); if(created) state.dispatchEvent(new CustomEvent("state:created",{bubbles:true,detail:{state}})); if(options.put) { state.addEventListener("state:change", async ({detail}) => { const {property,path,ancestors,state} = detail; if(property==="mtime" && path[path.length-1]==="^") return; // prevent loops from changes on server to mtime const data = ancestors[0] || state, text = await router.fetch(new Request(src,{method:"PUT",body:JSON.stringify(data),headers:{"content-type":"application/json"}})).then((response) => { return response.text(); }), newstate = JSON.parse(text); patch(data,newstate); state.dispatchEvent(new CustomEvent("state:put",{bubbles:true,detail})); }) } if(options.delete) { state.addEventListener("state:deleted", () => { router.fetch(new Request(src,{method:"DELETE"})); }) } if(args[0]==="global") window.globalState = state; else if(args[0]==="document") document.documentState = state; }; export {state}