text: !function(global,factory){"object"==typeof exports&&"undefined"!=typeof module?module.exports=factory():"function"==typeof define&&define.amd?define(factory):(global=global||self).WSRPC=factory()}(this,function(){"use strict";function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError("Cannot call a class as a function")}function Deferred(){_classCallCheck(this,Deferred);var self=this;function wrapper(func){return function(){if(!self.done)return self.done=!0,func.apply(this,arguments);console.error(new Error("Promise already done"))}}return self.resolve=null,self.reject=null,self.done=!1,self.promise=new Promise(function(resolve,reject){self.resolve=wrapper(resolve),self.reject=wrapper(reject)}),self.promise.isPending=function(){return!self.done},self}function logGroup(group,level,args){console.group(group),console[level].apply(this,args),console.groupEnd()}function log(){WSRPC.DEBUG&&logGroup("WSRPC.DEBUG","trace",arguments)}function trace(msg){var payload;WSRPC.TRACE&&("data"in(payload=msg)&&(payload=JSON.parse(msg.data)),logGroup("WSRPC.TRACE","trace",[payload]))}var readyState=Object.freeze({0:"CONNECTING",1:"OPEN",2:"CLOSING",3:"CLOSED"}),WSRPC=function WSRPC(URL){var reconnectTimeout=1<arguments.length&&void 0!==arguments[1]?arguments[1]:1e3;_classCallCheck(this,WSRPC);var self=this;function createSocket(){var ws=new WebSocket(URL),rejectQueue=function(){for(self.connectionNumber++;0<self.callQueue.length;){var callObj=self.callQueue.shift(),deferred=self.store[callObj.id];delete self.store[callObj.id],deferred&&deferred.promise.isPending()&&deferred.reject("WebSocket error occurred")}for(var key in self.store)self.store.hasOwnProperty(key)&&(deferred=self.store[key])&&deferred.promise.isPending()&&deferred.reject("WebSocket error occurred")};function callEvents(evName,event){for(;0<self.oneTimeEventStore[evName].length;){var deferred=self.oneTimeEventStore[evName].shift();deferred.hasOwnProperty("resolve")&&deferred.promise.isPending()&&deferred.resolve()}for(var i in self.eventStore[evName]){self.eventStore[evName].hasOwnProperty(i)&&function(func,event){try{func(event)}catch(e){e.hasOwnProperty("stack")?log(e.stack):log("Event function",func,"raised unknown error:",e),console.error(e)}}(self.eventStore[evName][i],event)}}return ws.onclose=function(err){for(var serial in log("ONCLOSE CALLED","STATE",self.public.state()),trace(err),self.store)self.store.hasOwnProperty(serial)&&self.store[serial].hasOwnProperty("reject")&&self.store[serial].reject("Connection closed");rejectQueue(),callEvents("onclose",err),callEvents("onchange",err),function(callEvents){setTimeout(function(){try{self.socket=createSocket(),self.id=1}catch(exc){callEvents("onerror",exc),delete self.socket,console.error(exc)}},reconnectTimeout)}(callEvents)},ws.onerror=function(err){log("ONERROR CALLED","STATE",self.public.state()),trace(err),rejectQueue(),callEvents("onerror",err),callEvents("onchange",err),log("WebSocket has been closed by error: ",err)},ws.onopen=function(ev){for(log("ONOPEN CALLED","STATE",self.public.state()),trace(ev);0<self.callQueue.length;)self.socket.send(JSON.stringify(self.callQueue.shift(),0,1));callEvents("onconnect",ev),callEvents("onchange",ev)},ws.onmessage=function(message){var data;if(log("ONMESSAGE CALLED","STATE",self.public.state()),trace(message),"message"===message.type)try{if(log(data=JSON.parse(message.data)),data.hasOwnProperty("id"))return(data.hasOwnProperty("method")?function(self,data){if(!self.routes.hasOwnProperty(data.method))throw new Error("Route not found");var connectionNumber=self.connectionNumber,deferred=new Deferred;deferred.promise.then(function(result){connectionNumber===self.connectionNumber&&self.socket.send(JSON.stringify({id:data.id,result:result}))},function(error){connectionNumber===self.connectionNumber&&self.socket.send(JSON.stringify({id:data.id,error:error}))});var func=self.routes[data.method];if(self.asyncRoutes[data.method])return func.apply(deferred,[data]);function badPromise(){throw new Error("You should register route with async flag.")}var promiseMock={resolve:badPromise,reject:badPromise};try{deferred.resolve(func.apply(promiseMock,[data]))}catch(e){deferred.reject(e),console.error(e)}}:data.hasOwnProperty("error")&&null===data.error?function(self,data){if(!self.store.hasOwnProperty(data.id))return log("Unknown callback");var deferred=self.store[data.id];if(void 0===deferred)return log("Confirmation without handler");delete self.store[data.id],log("REJECTING",data.error),deferred.reject(data.error)}:function(self,data){var deferred=self.store[data.id];return void 0===deferred?log("Confirmation without handler"):(delete self.store[data.id],data.hasOwnProperty("result")?deferred.resolve(data.result):deferred.reject(data.error))})(self,data);console.group("Event received");for(var i=0;i<self.socketEventsListeners.length;i++)try{self.socketEventsListeners[i].apply(self.public,[data])}catch(e){console.error(e)}console.groupEnd()}catch(exception){var err={error:exception.message,result:null,id:data?data.id:null};self.socket.send(JSON.stringify(err)),console.error(exception)}},ws}return URL=function(url){if(/^\w+:\/\//.test(url))return url;if("undefined"==typeof window&&window.location.host.length<1)throw new Error("Can not construct absolute URL from ".concat(window.location));var scheme="https:"===window.location.protocol?"wss:":"ws:",port=""===window.location.port?":".concat(window.location.port):"",host=window.location.host,path=url.replace(/^\/+/gm,"");return"".concat(scheme,"//").concat(host).concat(port,"/").concat(path)}(URL),self.id=1,self.eventId=0,self.socketStarted=!1,self.eventStore={onconnect:{},onerror:{},onclose:{},onchange:{}},self.connectionNumber=0,self.oneTimeEventStore={onconnect:[],onerror:[],onclose:[],onchange:[]},self.callQueue=[],self.socketEventsListeners=[function(event){console.log(event)}],self.asyncRoutes={},self.routes={},self.store={},self.public=Object.freeze({defer:function(){return new Deferred},call:function(func,args,params){self.id+=2;var deferred=new Deferred,callObj=Object.freeze({id:self.id,method:func,params:args}),state=self.public.state();return"OPEN"===state?(self.store[self.id]=deferred,self.socket.send(JSON.stringify(callObj))):"CONNECTING"===state?(log("SOCKET IS",state),self.store[self.id]=deferred,self.callQueue.push(callObj)):(log("SOCKET IS",state),params&&params.noWait?deferred.reject("Socket is: ".concat(state)):(self.store[self.id]=deferred,self.callQueue.push(callObj))),deferred.promise},addRoute:function(route,callback,isAsync){self.asyncRoutes[route]=isAsync||!1,self.routes[route]=callback},deleteRoute:function(route){return delete self.asyncRoutes[route],delete self.routes[route]},addEventListener:function(event,func){var eventId=self.eventId++;return self.eventStore[event][eventId]=func,eventId},removeEventListener:function(event,index){return!!self.eventStore[event].hasOwnProperty(index)&&(delete self.eventStore[event][index],!0)},onEvent:function(event){var deferred=new Deferred;return self.oneTimeEventStore[event].push(deferred),deferred.promise},destroy:function(){return self.socket.close()},state:function(){return readyState[this.stateCode()]},stateCode:function(){return self.socketStarted&&self.socket?self.socket.readyState:3},connect:function(){self.socketStarted=!0,self.socket=createSocket()},addServerEventListener:function(callable){return self.socketEventsListeners.push(callable)-1},removeServerEventListener:function(index){return self.socketEventsListeners.splice(index,1).length},sendRaw:function(data){return self.socket.send(data)}}),self.public.addRoute("log",function(argsObj){console.info("Websocket sent: ".concat(argsObj))}),self.public.addRoute("ping",function(data){return data}),self.public};return WSRPC.DEBUG=!1,WSRPC.TRACE=!1,WSRPC});