Skip to content
(()=>{let e;function t(e){let t=htmx.config.metaCharacter||":",n=`[${CSS.escape("hx-ws"+t+e)}]`;return htmx.config.prefix&&(n+=`,[${CSS.escape(htmx.config.prefix+"ws"+t+e)}]`),n}function n(t){let n=e.HCON.parse(e.attributeValue(t,"hx-config")).ws||{};return{reconnect:!0,reconnectCodes:[1001,1005,1006,1011,1012,1013,1014],reconnectDelay:500,reconnectMaxDelay:6e4,reconnectMaxAttempts:1/0,reconnectJitter:.3,pauseOnBackground:!0,maxOutgoingMessagesQueueSize:100,...htmx.config.ws,...n}}function o(e){if(e.startsWith("ws://")||e.startsWith("wss://"))return e;if(e.startsWith("http://"))return"ws://"+e.slice(7);if(e.startsWith("https://"))return"wss://"+e.slice(8);let t="https:"===window.location.protocol?"wss:":"ws:",n=window.location.host;return e.startsWith("//")?t+e:e.startsWith("/")?t+"//"+n+e:t+"//"+n+window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/")+1)+e}const r=new Set;function c(e,t){if(t.timer&&clearTimeout(t.timer),t.visibilityHandler&&document.removeEventListener("visibilitychange",t.visibilityHandler),t.abortController&&t.abortController.abort(),t.queue.length=0,t.socket)try{t.socket.readyState!==WebSocket.OPEN&&t.socket.readyState!==WebSocket.CONNECTING||t.socket.close()}catch(e){}e._htmx?.ws?.connection===t&&delete e._htmx.ws.connection,r.delete(t)}function i(t,n){let o=n.url;if(n.abortController&&n.abortController.abort(),n.socket){let e=n.socket;n.socket=null;try{e.readyState!==WebSocket.OPEN&&e.readyState!==WebSocket.CONNECTING||e.close()}catch(e){}}try{n.socket=new WebSocket(o,n.config?.protocols);let a=new AbortController;n.abortController=a;let l={signal:a.signal};n.socket.addEventListener("open",()=>{t.isConnected?(e.triggerHtmxEvent(t,"htmx:ws:after:connection",{connection:n}),n.attempt=0,function(e){for(;e.queue.length&&e.socket?.readyState===WebSocket.OPEN;){let t=e.queue.shift();s(e,t.element,t.message)}}(n)):c(t,n)},l),n.socket.addEventListener("message",o=>{n.receiving=n.receiving.then(()=>async function(t,n,o){let r=[],i={data:o.data,text(){let e=i.data;return"string"==typeof e?Promise.resolve(e):e instanceof Blob?e.text():Promise.resolve((new TextDecoder).decode(e))},json:()=>i.text().then(JSON.parse)};if(!t.isConnected)return void c(t,n);let s={connection:n,message:i,cancelled:!1,waitUntil(e){r.push(Promise.resolve(e))}},a=e.triggerHtmxEvent(t,"htmx:ws:before:message:incoming",s);if(await Promise.all(r),!a||s.cancelled)return;let l,u=null;if("string"==typeof i.data)try{u=await i.json()}catch(e){l=await i.text()}u&&(void 0!==u.content?l=u.content:void 0!==u.payload&&(l=u.payload,n._payloadWarnFired||(console.warn("htmx: [hx-ws] json.payload is deprecated; use json.content instead"),n._payloadWarnFired=!0)));if(null!=l){let n=u?.target||e.attributeValue(t,"hx-target"),o=u?.swap||e.attributeValue(t,"hx-swap")||htmx.config.defaultSwap;/(?:^|\s)swapEmpty(?::(?:true|false))?(?=\s|$)/.test(o)||(o+=" swapEmpty:false"),await htmx.swap({sourceElement:t,target:n||t,swap:o,select:u?.select??e.attributeValue(t,"hx-select"),selectOOB:e.attributeValue(t,"hx-select-oob"),text:l,transition:!1})}e.triggerHtmxEvent(t,"htmx:ws:after:message:incoming",{connection:n,message:i})}(t,n,o)).catch(o=>{t.isConnected&&e.triggerHtmxEvent(t,"htmx:ws:error",{connection:n,error:o})})},l),n.socket.addEventListener("close",o=>{if(o.target!==n.socket)return;if(t.isConnected&&e.triggerHtmxEvent(t,"htmx:ws:close",{connection:n,reason:"closed",code:o.code}),!r.has(n))return;let s=n.config;s.pauseOnBackground&&document.hidden||(s.reconnect&&s.reconnectCodes.includes(o.code)&&t.isConnected?function(t,n){let o=n.config;n.attempt++;let r=n.attempt;if(!o.reconnect||r>o.reconnectMaxAttempts)return void c(t,n);let s=htmx.parseInterval(o.reconnectDelay)??o.reconnectDelay,a=htmx.parseInterval(o.reconnectMaxDelay)??o.reconnectMaxDelay,l=Math.min(s*Math.pow(2,r-1),a);if(o.reconnectJitter>0){let e=l*o.reconnectJitter;l=Math.max(0,l+(2*Math.random()-1)*e)}if(!t.isConnected)return void c(t,n);if(n.cancelled=!1,!e.triggerHtmxEvent(t,"htmx:ws:before:connection",{connection:n})||n.cancelled)return e.triggerHtmxEvent(t,"htmx:ws:close",{connection:n,reason:"cancelled",code:null}),void c(t,n);n.timer=setTimeout(()=>{t.isConnected?i(t,n):c(t,n)},l)}(t,n):c(t,n))},l),n.socket.addEventListener("error",o=>{t.isConnected&&e.triggerHtmxEvent(t,"htmx:ws:error",{connection:n,error:o})},l)}catch(o){t.isConnected&&e.triggerHtmxEvent(t,"htmx:ws:error",{connection:n,error:o})}}function s(t,n,o){try{t.socket.send(o.data),e.triggerHtmxEvent(n,"htmx:ws:after:message:outgoing",{connection:t,message:o})}catch(o){e.triggerHtmxEvent(n,"htmx:ws:error",{connection:t,error:o})}}function a(t){if(e.htmxProp(t).ws??={},t._htmx.ws.initialized)return;let c=e.attributeValue(t,"hx-ws:connect");if(!c)return;let s=e.attributeValue(t,"hx-trigger")||"load";e.onTrigger(t,s,()=>{!function(t,c){if(c._htmx.ws.connection)return c._htmx.ws.connection;let s={url:o(t),config:n(c),socket:null,attempt:0,timer:null,queue:[],receiving:Promise.resolve(),sending:Promise.resolve(),abortController:null,visibilityHandler:null,cancelled:!1};!e.triggerHtmxEvent(c,"htmx:ws:before:connection",{connection:s})||s.cancelled?e.triggerHtmxEvent(c,"htmx:ws:close",{connection:s,reason:"cancelled",code:null}):(c._htmx.ws.connection=s,r.add(s),i(c,s),s.config.pauseOnBackground&&(s.visibilityHandler=()=>{document.hidden?s.socket&&s.socket.readyState===WebSocket.OPEN&&s.socket.close():s.socket&&s.socket.readyState!==WebSocket.CLOSED||(s.attempt=0,i(c,s))},document.addEventListener("visibilitychange",s.visibilityHandler)))}(c,t)}),t._htmx.ws.initialized=!0}function l(n){if(e.htmxProp(n).ws??={},n._htmx.ws.sendInitialized)return;let c=e.attributeValue(n,"hx-trigger");c||(c=n.matches("form")?"submit":n.matches("input:not([type=button]):not([type=submit]),select,textarea")?"change":"click"),e.onTrigger(n,c,async c=>{n.matches("form")&&"submit"===c.type&&c.preventDefault(),await async function(n,c){let i=n.closest(t("connect")),a=i&&e.attributeValue(i,"hx-ws:connect");if(!a)return void e.triggerHtmxEvent(n,"htmx:ws:error",{url:null,error:"No WebSocket connection found for element"});let l=o(a),u=i._htmx?.ws?.connection;if(!u)return void e.triggerHtmxEvent(n,"htmx:ws:error",{url:l,error:"Connection not open"});let d={...e.createRequestContext(n,c).request.headers};delete d.Accept;let m=n.form||n.closest("form"),h=e.collectFormData(n,m,c.submitter),g={};for(let[e,t]of h)g[e]=e in g?[].concat(g[e],t):t;let x=e.getAttributeObject(n,"hx-vals",e=>Object.assign(g,e)),w=u.sending.then(async()=>{x&&await x,delete g.headers;let t=[],o={headers:d,values:g,data:void 0},c={connection:u,message:o,cancelled:!1,waitUntil(e){t.push(Promise.resolve(e))}},i=e.triggerHtmxEvent(n,"htmx:ws:before:message:outgoing",c);try{if(await Promise.all(t),!i||c.cancelled)return;if(o.data??=JSON.stringify({...o.values,headers:o.headers}),!r.has(u))return void e.triggerHtmxEvent(n,"htmx:ws:error",{connection:u,error:"Connection closed"});u.socket?.readyState===WebSocket.OPEN?s(u,n,o):u.queue.length>=u.config.maxOutgoingMessagesQueueSize?e.triggerHtmxEvent(n,"htmx:ws:error",{connection:u,error:"Outgoing messages queue is full"}):u.queue.push({element:n,message:o})}catch(t){e.triggerHtmxEvent(n,"htmx:ws:error",{connection:u,error:t})}});u.sending=w.catch(()=>{}),await w}(n,c)}),n._htmx.ws.sendInitialized=!0}htmx.registerExtension("ws",{init:t=>{e=t,htmx.config.ws||(htmx.config.ws={})},htmx_after_process:n=>{const o=t=>{!function(e){if(e.hasAttribute("ws-connect")||e.hasAttribute("ws-send")){if(console.warn("htmx: [hx-ws] legacy attributes ws-connect and ws-send are deprecated; use hx-ws:connect and hx-ws:send instead"),e.hasAttribute("ws-connect")){let t=e.getAttribute("ws-connect"),n=htmx.config.metaCharacter||":",o=(htmx.config.prefix||"hx-")+"ws"+n+"connect";e.hasAttribute(o)||e.setAttribute(o,t)}if(e.hasAttribute("ws-send")){let t=htmx.config.metaCharacter||":",n=(htmx.config.prefix||"hx-")+"ws"+t+"send";e.hasAttribute(n)||e.setAttribute(n,"")}}}(t);let n=e.attributeValue(t,"hx-ws:send");null!=e.attributeValue(t,"hx-ws:connect")&&a(t),null!=n&&l(t)};o(n);let r=t("connect")+","+t("send")+",[ws-connect],[ws-send]";n.querySelectorAll(r).forEach(o)},htmx_before_cleanup:t=>{!function(t){let n=t._htmx?.ws?.connection;n&&(e.triggerHtmxEvent(t,"htmx:ws:close",{connection:n,reason:"removed",code:null}),c(t,n))}(t)}}),"undefined"!=typeof window&&window.htmx&&window.addEventListener("pagehide",()=>{r.forEach(e=>{e.socket&&e.socket.close(1e3,"page navigating away")})})})();