clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

main
atlas / internal / app / assets / hx-sse.min.js
hx-sse.min.js 5.4K
1(()=>{let e;async function*t(e){let t=new TextDecoder,n="",r=!1,a={data:"",event:"",id:"",retry:null},s=!0;try{for(;;){let{done:c,value:i}=await e.read();if(c)break;let l=t.decode(i,{stream:!0});s&&(65279===l.charCodeAt(0)&&(l=l.slice(1)),s=!1),n+=l;let o=n.split(/\r\n|\r|\n/);n=o.pop()||"";for(let e of o){if(!e){r&&(yield a,r=!1,a={data:"",event:"",id:"",retry:null});continue}let t,n,s=e.indexOf(":");if(0!==s)if(s<0?(t=e,n=""):(t=e.slice(0,s),n=e.slice(s+1)," "===n[0]&&(n=n.slice(1))),"data"===t)a.data+=(r?"\n":"")+n,r=!0;else if("event"===t)a.event=n;else if("id"===t)n.includes("\0")||(a.id=n);else if("retry"===t){let e=parseInt(n,10);isNaN(e)||(a.retry=e)}}}}finally{e.releaseLock()}}async function n(n){let r=n.sourceElement,s=function(t){let n=null!=e.attributeValue(t.sourceElement,"hx-sse:connect");return{reconnect:n,reconnectDelay:500,reconnectMaxDelay:6e4,reconnectMaxAttempts:1/0,reconnectJitter:.3,pauseOnBackground:n,...htmx.config.sse||{},...t.request.sse||{}}}(n),c=!1,i={url:n.request.action,config:s,abortController:null,reader:null,lastEventId:null,delayCanceller:null,visibilityHandler:null,attempt:0,cancelled:!1,status:null};e.htmxProp(r).sse=i;let l=!1,o=null;if(s.pauseOnBackground){let e=()=>{document.hidden?(l=!0,i.reader?.cancel()):l&&(l=!1,o&&o())};document.addEventListener("visibilitychange",e),i.visibilityHandler=e}if(i.cancelled=!1,!e.triggerHtmxEvent(r,"htmx:before:sse:connection",{connection:i})||i.cancelled)return void a(r,"cancelled");i.status=n.response.status,e.triggerHtmxEvent(r,"htmx:after:sse:connection",{connection:i});let d=n.response.raw;try{for(;r.isConnected;){if(i.attempt>0){if(l){if(await new Promise(e=>{o=e}),o=null,!r.isConnected)break;i.attempt=1,c=!0}if(!c&&(!s.reconnect||i.attempt>s.reconnectMaxAttempts))break;let t=htmx.parseInterval(s.reconnectDelay)??s.reconnectDelay,a=htmx.parseInterval(s.reconnectMaxDelay)??s.reconnectMaxDelay,m=Math.min(t*Math.pow(2,i.attempt-1),a);if(s.reconnectJitter>0){let e=m*s.reconnectJitter;m=Math.max(0,m+(2*Math.random()-1)*e)}if(i.cancelled=!1,!e.triggerHtmxEvent(r,"htmx:before:sse:connection",{connection:i})||i.cancelled)break;if(await new Promise(e=>{i.delayCanceller=e,setTimeout(e,m)}),i.delayCanceller=null,!r.isConnected)break;let u=new AbortController;i.abortController=u;try{i.lastEventId&&(n.request.headers["Last-Event-ID"]=i.lastEventId),d=await fetch(n.request.action,{...n.request,signal:u.signal})}catch(t){if(u.signal.aborted)break;e.triggerHtmxEvent(r,"htmx:sse:error",{error:t,url:n.request.action}),c=!1,i.attempt++;continue}if(!d.ok){e.triggerHtmxEvent(r,"htmx:sse:error",{error:new Error(`SSE reconnect failed with status ${d.status}`),status:d.status,url:n.request.action}),c=!1,i.attempt++;continue}i.status=d.status,e.triggerHtmxEvent(r,"htmx:after:sse:connection",{connection:i}),i.attempt=0}c=!1;try{i.reader=d.body.getReader();for await(let l of t(i.reader)){if(!r.isConnected||c)break;let t={message:{data:l.data,event:l.event,id:l.id,cancelled:!1}};if(e.triggerHtmxEvent(r,"htmx:before:sse:message",t)&&!t.message.cancelled){if(l.id&&(i.lastEventId=l.id),null!=l.retry&&(s.reconnectDelay=l.retry),t.message.event){htmx.trigger(r,t.message.event,{data:t.message.data,id:t.message.id}),delete t.message.cancelled,e.triggerHtmxEvent(r,"htmx:after:sse:message",t);let n=e.attributeValue(r,"hx-sse:close");if(n&&t.message.event===n)return void a(r,"message");continue}n.text=t.message.data,n.swap.includes("swapEmpty")||(n.swap+=" swapEmpty:false"),await htmx.swap(n),delete t.message.cancelled,e.triggerHtmxEvent(r,"htmx:after:sse:message",t)}}}catch(t){i.abortController?.signal?.aborted||e.triggerHtmxEvent(r,"htmx:sse:error",{error:t,url:n.request.action})}if(i.reader=null,!r.isConnected)break;i.attempt++}}finally{a(r,r.isConnected?"ended":"removed")}}function r(t){let n=e.attributeValue(t,"hx-sse:connect");if(!n)return;if(t._htmx?.sse)return;let r=e.attributeValue(t,"hx-trigger")||"load";e.onTrigger(t,r,()=>{t._htmx?.sse||htmx.ajax("GET",n,{source:t})})}function a(t,n){let r=t?._htmx?.sse;r&&(r.abortController?.abort(),r.reader?.cancel?.(),r.delayCanceller&&r.delayCanceller(),r.visibilityHandler&&document.removeEventListener("visibilitychange",r.visibilityHandler),e.triggerHtmxEvent(t,"htmx:sse:close",{connection:r,reason:n||"cleanup"}),delete t._htmx.sse)}function s(e){if(e.hasAttribute("sse-connect")){console.warn("htmx: [hx-sse] legacy attribute sse-connect is deprecated; use hx-sse:connect instead");let t=e.getAttribute("sse-connect"),n=(htmx.config.prefix||"hx-")+"sse"+(htmx.config.metaCharacter||":")+"connect";e.hasAttribute(n)||e.setAttribute(n,t)}e.hasAttribute("sse-swap")&&console.warn("htmx: [hx-sse] sse-swap is removed in htmx 4. Unnamed SSE messages are swapped automatically. Named events are dispatched as DOM events.")}htmx.registerExtension("sse",{init:t=>{e=t},htmx_config_request:(e,{ctx:{request:t}})=>{t.headers.Accept=`${t.headers.Accept??t.headers.accept??"text/html"}, text/event-stream`},htmx_before_response:(t,r)=>{let s=r.ctx,c=s.response.raw.headers.get("Content-Type");if(c?.includes("text/event-stream"))return n(s).catch(n=>{e.triggerHtmxEvent(t,"htmx:sse:error",{error:n,url:s.request.action}),a(t)}),!1},htmx_after_process:e=>{s(e),r(e);let t=htmx.config.metaCharacter||":",n=`[${CSS.escape("hx-sse"+t+"connect")}]`;htmx.config.prefix&&(n+=`,[${CSS.escape(htmx.config.prefix+"sse"+t+"connect")}]`),e.querySelectorAll(`${n},[sse-connect]`).forEach(e=>{s(e),r(e)})},htmx_before_cleanup:e=>{a(e)}})})();