// defines: // variable ws (the websocket) // variable wsReady (boolean) // variable wsVerbose (boolean) // wsSend(msg) (send a message when websocket is ready) // wsOnMessage(function(event) {...}) (react to messages) // wsOnOpen(function() {}) (react to opening of websocket) sclass HInitWebSocket { S wsVar = "ws"; // TODO: not used everywhere S readyMsg = "WebSocket ready!"; bool handleEvals = true; // automatically handle {"eval":"jsCode();"} bool wsVerbose = true; // now defaults to true (it's just so useful) bool autoOpen = true; // open websocket immediately - otherwise call ws.open() later new MapSO params; // appended to URL it it's not the first connect S reconnectParams = "reconnect=1"; *() {} *(IWebRequest req) { if (req != null && eq(req.get("wsVerbose"), "1")) set wsVerbose; } public S headStuff aka get aka toString() { ret hreconnectingWebSockets() + hscript(js()); } LS dependencies() { ret ll(hjssnippet(hreconnectingWebSockets_snippetID(), "data-comment" := "ReconnectingWebSocket")); } S js() { ret jsDollarVars([[ var wsReady = false; var wsInitialMsgs = []; var wsMsgListeners = []; var wsOpenListeners = []; var wsWillReconnectListeners = []; var wsVerbose = $wsVerbose; var wsConnectionCount = 0; var wsAugmentURL = function(url) { return url; }; var wsURL = ((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + window.location.pathname + window.location.search; if ($params != "") wsURL += (wsURL.match(/\?/) ? "&" : "?") + $params; var $wsVar = new ReconnectingWebSocket(wsURL, null, $options); $wsVar.onopen = function(event) { wsReady = true; console.log($readyMsg + " " + ws.url); if (++wsConnectionCount == 1) if ($reconnectParams != "") { wsURL += (wsURL.match(/\?/) ? "&" : "?") + $reconnectParams; ws.url = wsAugmentURL(wsURL); } wsOpenListeners.forEach(function(f) { f(); }); ws.onmessage = function(event) { if (wsVerbose) console.log("Got msg: " + event.data.substring(0, 200)); wsMsgListeners.forEach(function(listener) { listener(event); }); }; ws.onclose = function(event) { if (wsVerbose) console.log("WebSocket closed"); wsReady = false; }; wsInitialMsgs.forEach(function(msg) { if (wsVerbose) console.log("Sending initial msg: " + msg); ws.send(msg); }); wsInitialMsgs = []; if (wsVerbose) console.log("Cleared initial msgs"); }; // send it now if ws is open already or when ws was opened function wsSend(msg) { if (wsReady) { if (wsVerbose) console.log("Sending msg: " + msg); ws.send(msg); // TODO: wsReady = false on failed send? return; } else { if (wsVerbose) console.log("Scheduling msg: " + msg); wsInitialMsgs.push(msg); } } function wsOnOpen(f) { wsOpenListeners.push(f); } function wsOnMessage(f) { wsMsgListeners.push(f); } function wsSetAugmentURL(f) { wsAugmentURL = f; if (ws) ws.url = wsAugmentURL(wsURL); } ]], wsVar := JS(wsVar), +wsVerbose, +readyMsg, params := makePostData(params), reconnectParams := unnull(reconnectParams), +options()) + (!handleEvals ? "" : "wsOnMessage(" + js_evalOnWebSocketMessage() + ");\n"); } Map options() { ret litorderedmap( automaticOpen := autoOpen ? null : false ); } }