This commit is contained in:
sujit
2025-07-30 07:25:30 +05:45
parent 611bd1efdd
commit de3f714847
12 changed files with 4610 additions and 158 deletions

View File

@@ -1,32 +1,33 @@
if(typeof window === 'undefined'){
if (typeof window === 'undefined') {
var window = {};
}
if(typeof module === 'undefined'){
if (typeof module === 'undefined') {
var module = {};
}
(function(window, module){ 'use strict';
var SS = function(url, opts){
(function (window, module) {
'use strict';
var SS = function (url, opts) {
opts = opts || {};
var self = this,
events = {},
reconnectOpts = {enabled: true, replayOnConnect: true, intervalMS: 5000},
reconnecting = false,
connectedOnce = false,
var self = this,
events = {},
reconnectOpts = { enabled: true, replayOnConnect: true, intervalMS: 5000 },
reconnecting = false,
connectedOnce = false,
headerStartCharCode = 1,
headerStartChar = String.fromCharCode(headerStartCharCode),
dataStartCharCode = 2,
dataStartChar = String.fromCharCode(dataStartCharCode),
subProtocol = 'sac-sock',
ws = new WebSocket(url, subProtocol);
headerStartChar = String.fromCharCode(headerStartCharCode),
dataStartCharCode = 2,
dataStartChar = String.fromCharCode(dataStartCharCode),
subProtocol = 'sac-sock',
ws = new WebSocket(url, subProtocol);
//blomp blomp-a noop noop a-noop noop noop
self.noop = function(){ };
self.noop = function () { };
//we really only support reconnect options for now
if(typeof opts.reconnectOpts == 'object'){
for(var i in opts.reconnectOpts){
if(!opts.reconnectOpts.hasOwnProperty(i)) continue;
if (typeof opts.reconnectOpts == 'object') {
for (var i in opts.reconnectOpts) {
if (!opts.reconnectOpts.hasOwnProperty(i)) continue;
reconnectOpts[i] = opts.reconnectOpts[i];
}
}
@@ -36,7 +37,7 @@ if(typeof module === 'undefined'){
ws.binaryType = 'arraybuffer';
//Parses all incoming messages and dispatches their payload to the appropriate eventName if one has been registered. Messages received for unregistered events will be ignored.
ws.onmessage = function(e){
ws.onmessage = function (e) {
var msg = e.data,
headers = {},
eventName = '',
@@ -44,46 +45,46 @@ if(typeof module === 'undefined'){
chr = null,
i, msgLen;
if(typeof msg === 'string'){
if (typeof msg === 'string') {
var dataStarted = false,
headerStarted = false;
for(i = 0, msgLen = msg.length; i < msgLen; i++){
for (i = 0, msgLen = msg.length; i < msgLen; i++) {
chr = msg[i];
if(!dataStarted && !headerStarted && chr !== dataStartChar && chr !== headerStartChar){
if (!dataStarted && !headerStarted && chr !== dataStartChar && chr !== headerStartChar) {
eventName += chr;
}else if(!headerStarted && chr === headerStartChar){
} else if (!headerStarted && chr === headerStartChar) {
headerStarted = true;
}else if(headerStarted && !dataStarted && chr !== dataStartChar){
} else if (headerStarted && !dataStarted && chr !== dataStartChar) {
headers[chr] = true;
}else if(!dataStarted && chr === dataStartChar){
} else if (!dataStarted && chr === dataStartChar) {
dataStarted = true;
}else{
} else {
data += chr;
}
}
}else if(msg && msg instanceof ArrayBuffer && msg.byteLength !== undefined){
} else if (msg && msg instanceof ArrayBuffer && msg.byteLength !== undefined) {
var dv = new DataView(msg),
headersStarted = false;
for(i = 0, msgLen = dv.byteLength; i < msgLen; i++){
for (i = 0, msgLen = dv.byteLength; i < msgLen; i++) {
chr = dv.getUint8(i);
if(chr !== dataStartCharCode && chr !== headerStartCharCode && !headersStarted){
if (chr !== dataStartCharCode && chr !== headerStartCharCode && !headersStarted) {
eventName += String.fromCharCode(chr);
}else if(chr === headerStartCharCode && !headersStarted){
} else if (chr === headerStartCharCode && !headersStarted) {
headersStarted = true;
}else if(headersStarted && chr !== dataStartCharCode){
} else if (headersStarted && chr !== dataStartCharCode) {
headers[String.fromCharCode(chr)] = true;
}else if(chr === dataStartCharCode){
data = dv.buffer.slice(i+1);
} else if (chr === dataStartCharCode) {
data = dv.buffer.slice(i + 1);
break;
}
}
}
if(eventName.length === 0) return; //no event to dispatch
if(typeof events[eventName] === 'undefined') return;
if (eventName.length === 0) return; //no event to dispatch
if (typeof events[eventName] === 'undefined') return;
events[eventName].call(self, (headers.J) ? JSON.parse(data) : data);
};
@@ -93,8 +94,8 @@ if(typeof module === 'undefined'){
* @function startReconnect
*
*/
function startReconnect(){
setTimeout(function(){
function startReconnect() {
setTimeout(function () {
console.log('attempting reconnect');
var newWS = new WebSocket(url, subProtocol);
newWS.onmessage = ws.onmessage;
@@ -104,11 +105,11 @@ if(typeof module === 'undefined'){
//we need to run the initially set onConnect function on first successful connect,
//even if replayOnConnect is disabled. The server might not be available on first
//connection attempt.
if(reconnectOpts.replayOnConnect || !connectedOnce){
if (reconnectOpts.replayOnConnect || !connectedOnce) {
newWS.onopen = ws.onopen;
}
ws = newWS;
if(!reconnectOpts.replayOnConnect && connectedOnce){
if (!reconnectOpts.replayOnConnect && connectedOnce) {
self.onConnect(self.noop);
}
}, reconnectOpts.intervalMS);
@@ -121,12 +122,12 @@ if(typeof module === 'undefined'){
* @param {Function} callback(event) - The callback that will be executed when the websocket connection opens.
*
*/
self.onConnect = function(callback){
ws.onopen = function(){
self.onConnect = function (callback) {
ws.onopen = function () {
connectedOnce = true;
var args = arguments;
callback.apply(self, args);
if(reconnecting){
if (reconnecting) {
reconnecting = false;
}
};
@@ -139,13 +140,13 @@ if(typeof module === 'undefined'){
* @method onDisconnect
* @param {Function} callback(event) - The callback that will be executed when the websocket connection is closed.
*/
self.onDisconnect = function(callback){
ws.onclose = function(){
self.onDisconnect = function (callback) {
ws.onclose = function () {
var args = arguments;
if(!reconnecting && connectedOnce){
if (!reconnecting && connectedOnce) {
callback.apply(self, args);
}
if(reconnectOpts.enabled){
if (reconnectOpts.enabled) {
reconnecting = true;
startReconnect();
}
@@ -162,7 +163,7 @@ if(typeof module === 'undefined'){
* @param {Function} callback(payload) - The callback that will be ran whenever the client receives an emit from the server for the given eventName. The payload passed into callback may be of type String, Object, or ArrayBuffer
*
*/
self.on = function(eventName, callback){
self.on = function (eventName, callback) {
events[eventName] = callback;
};
@@ -172,8 +173,8 @@ if(typeof module === 'undefined'){
* @method off
* @param {String} eventName - The name of event being unregistered
*/
self.off = function(eventName){
if(events[eventName]){
self.off = function (eventName) {
if (events[eventName]) {
delete events[eventName];
}
};
@@ -185,34 +186,34 @@ if(typeof module === 'undefined'){
* @param {String} eventName - The event to dispatch
* @param {String|Object|ArrayBuffer} data - The data to be sent to the server. If data is a string then it will be sent as a normal string to the server. If data is an object it will be converted to JSON before being sent to the server. If data is an ArrayBuffer then it will be sent to the server as a uint8 binary payload.
*/
self.emit = function(eventName, data){
self.emit = function (eventName, data) {
var rs = ws.readyState;
if(rs === 0){
if (rs === 0) {
console.warn("websocket is not open yet");
return;
}else if(rs === 2 || rs === 3){
} else if (rs === 2 || rs === 3) {
console.error("websocket is closed");
return;
}
var msg = '';
if(data instanceof ArrayBuffer){
var ab = new ArrayBuffer(data.byteLength+eventName.length+1),
if (data instanceof ArrayBuffer) {
var ab = new ArrayBuffer(data.byteLength + eventName.length + 1),
newBuf = new DataView(ab),
oldBuf = new DataView(data),
i = 0;
for(var evtLen = eventName.length; i < evtLen; i++){
for (var evtLen = eventName.length; i < evtLen; i++) {
newBuf.setUint8(i, eventName.charCodeAt(i));
}
newBuf.setUint8(i, dataStartCharCode);
i++;
for(var x = 0, xLen = oldBuf.byteLength; x < xLen; x++, i++){
for (var x = 0, xLen = oldBuf.byteLength; x < xLen; x++, i++) {
newBuf.setUint8(i, oldBuf.getUint8(x));
}
msg = ab;
}else if(typeof data === 'object'){
msg = eventName+dataStartChar+JSON.stringify(data);
}else{
msg = eventName+dataStartChar+data;
} else if (typeof data === 'object') {
msg = eventName + dataStartChar + JSON.stringify(data);
} else {
msg = eventName + dataStartChar + data;
}
ws.send(msg);
};
@@ -222,11 +223,11 @@ if(typeof module === 'undefined'){
*
* @method close
*/
self.close = function(){
self.close = function () {
reconnectOpts.enabled = false; //don't reconnect if close is called
return ws.close(1000);
};
};
window.SS = SS;
module.exports = SS;
})(window, module);
})(window, module);