@magnusbag/livets-client
Version:
Client-side connector for LiveTS framework - real-time server-rendered web applications
2 lines (1 loc) • 3.07 kB
JavaScript
!function(){"use strict";class t{constructor(){this.ws=null,this.reconnectAttempts=0,this.maxReconnectAttempts=5,this.reconnectDelay=1e3,this.pingInterval=null,this.init()}init(){this.connect(),this.setupEventDelegation()}connect(){const t=this.getWebSocketUrl();try{this.ws=new WebSocket(t),this.ws.onopen=()=>this.onOpen(),this.ws.onmessage=t=>this.onMessage(t),this.ws.onclose=()=>this.onClose(),this.ws.onerror=t=>this.onError(t)}catch(t){console.error("Failed to create WebSocket connection:",t),this.scheduleReconnect()}}onOpen(){this.reconnectAttempts=0,this.startPing()}onMessage(t){try{const e=JSON.parse(t.data);"p"===e.t&&this.applyCompactPatches(e.d||[])}catch(t){console.error("Failed to parse message:",t)}}onClose(){this.stopPing(),this.scheduleReconnect()}onError(t){console.error("WebSocket error:",t)}scheduleReconnect(){this.reconnectAttempts<this.maxReconnectAttempts&&(this.reconnectAttempts++,setTimeout(()=>this.connect(),this.reconnectDelay*this.reconnectAttempts))}startPing(){this.pingInterval=window.setInterval(()=>{var t;(null===(t=this.ws)||void 0===t?void 0:t.readyState)===WebSocket.OPEN&&this.ws.send('"p"')},3e4)}stopPing(){this.pingInterval&&(clearInterval(this.pingInterval),this.pingInterval=null)}setupEventDelegation(){document.addEventListener("click",t=>this.handleEvent(t,"click")),document.addEventListener("input",t=>this.handleEvent(t,"input")),document.addEventListener("change",t=>this.handleEvent(t,"change")),document.addEventListener("submit",t=>this.handleEvent(t,"submit"))}handleEvent(t,e){const s=t.target.closest(`[ts-on\\:${e}]`);if(!s)return;const i=s.getAttribute(`ts-on:${e}`),n=s.closest("[data-livets-id]");if(!i||!n)return;const o=n.dataset.livetsId;o&&(t.preventDefault(),this.sendEvent(o,i,this.extractEventData(t,s)))}extractEventData(t,e){const s=e;return{type:t.type,target:{tagName:e.tagName.toLowerCase(),value:s.value||void 0,checked:s.checked||void 0,dataset:s.dataset||{}}}}sendEvent(t,e,s){var i,n,o,c;if((null===(i=this.ws)||void 0===i?void 0:i.readyState)===WebSocket.OPEN){const i=`"e|${t.substring(0,8)}|${e}|${(null===(n=null==s?void 0:s.target)||void 0===n?void 0:n.value)||""}|${(null===(o=null==s?void 0:s.target)||void 0===o?void 0:o.checked)?"1":"0"}|${(null===(c=null==s?void 0:s.target)||void 0===c?void 0:c.tagName)||""}"`;this.ws.send(i)}}applyCompactPatches(t){t.forEach(t=>{try{const e=t.split("|"),s=e[0],i=`[data-ts-sel="${e[1]}"]`,n=document.querySelector(i);if(!n)return;switch(s){case"t":n.textContent=e[2]||"";break;case"a":n.setAttribute(e[2],e[3]||"");break;case"r":n.removeAttribute(e[2]);break;case"h":n.innerHTML=e[2]||"";break;case"e":n.outerHTML=e[2]||""}}catch(e){console.error("Failed to apply patch:",t,e)}})}getWebSocketUrl(){const t=window.LIVETS_WS_URL;return t||`${"https:"===window.location.protocol?"wss:":"ws:"}//${window.location.host}/ws`}}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>new t):new t,"undefined"!=typeof module&&module.exports&&(module.exports=t),"undefined"!=typeof window&&(window.LiveTSConnector=t)}();