forked from phcode-dev/staging.phcode.dev
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathNodeConnection.js
More file actions
1 lines (1 loc) · 7.25 KB
/
NodeConnection.js
File metadata and controls
1 lines (1 loc) · 7.25 KB
1
define(function(require,exports,module){var EventDispatcher=require("utils/EventDispatcher"),CONNECTION_ATTEMPTS=10,CONNECTION_TIMEOUT=1e4,RETRY_DELAY=500,MAX_COUNTER_VALUE=4294967295;function setDeferredTimeout(deferred,delay){var timer=setTimeout(function(){deferred.reject("timeout")},delay);deferred.always(function(){clearTimeout(timer)})}function attemptSingleConnect(){var deferred=$.Deferred(),port=null,ws=null;return setDeferredTimeout(deferred,CONNECTION_TIMEOUT),brackets.app.getNodeState(function(err,nodePort){!err&&nodePort&&"rejected"!==deferred.state()?(port=nodePort,(ws=new WebSocket("ws://localhost:"+port)).binaryType="arraybuffer",ws.onclose=function(){deferred.reject("WebSocket closed")},ws.onopen=function(){ws.onclose=null,deferred.resolveWith(null,[ws,port])}):deferred.reject("brackets.app.getNodeState error: "+err)}),deferred.promise()}function NodeConnection(){this.domains={},this._registeredModules=[],this._pendingInterfaceRefreshDeferreds=[],this._pendingCommandDeferreds=[]}EventDispatcher.makeEventDispatcher(NodeConnection.prototype),NodeConnection.prototype.domains=null,NodeConnection.prototype._registeredModules=null,NodeConnection.prototype._ws=null,NodeConnection.prototype._port=null,NodeConnection.prototype._commandCount=1,NodeConnection.prototype._autoReconnect=!1,NodeConnection.prototype._pendingInterfaceRefreshDeferreds=null,NodeConnection.prototype._pendingCommandDeferreds=null,NodeConnection.prototype._getNextCommandID=function(){var nextID;return nextID=this._commandCount>4294967295?this._commandCount=0:this._commandCount++},NodeConnection.prototype._cleanup=function(){if(this.domains={},this._ws&&this._ws.readyState!==WebSocket.CLOSED)try{this._ws.close()}catch(e){}var failedDeferreds;this._pendingInterfaceRefreshDeferreds.concat(this._pendingCommandDeferreds).forEach(function(d){d.reject("cleanup")}),this._pendingInterfaceRefreshDeferreds=[],this._pendingCommandDeferreds=[],this._ws=null,this._port=null},NodeConnection.prototype.connect=function(autoReconnect){var self=this;self._autoReconnect=autoReconnect;var deferred=$.Deferred(),attemptCount=0,attemptTimestamp=null;function registerHandlersAndDomains(ws,port){function success(){self._ws.onclose=function(){if(self._autoReconnect){var $promise=self.connect(!0);self.trigger("close",$promise)}else self._cleanup(),self.trigger("close")},deferred.resolve()}function fail(err){self._cleanup(),deferred.reject(err)}self._ws=ws,self._port=port,self._ws.onmessage=self._receive.bind(self),self._refreshInterface().then(function(){self._registeredModules.length>0?self.loadDomains(self._registeredModules,!1).then(success,fail):success()},fail)}function doConnect(){attemptCount++,attemptTimestamp=new Date,attemptSingleConnect().then(registerHandlersAndDomains,function(){if(attemptCount<CONNECTION_ATTEMPTS){var now=new Date,delay=Math.max(RETRY_DELAY-(now-attemptTimestamp),1);setTimeout(doConnect,delay)}else deferred.reject("Max connection attempts reached")})}return self._cleanup(),doConnect(),deferred.promise()},NodeConnection.prototype.connected=function(){return!(!this._ws||this._ws.readyState!==WebSocket.OPEN)},NodeConnection.prototype.disconnect=function(){this._autoReconnect=!1,this._cleanup()},NodeConnection.prototype.loadDomains=function(paths,autoReload){var deferred=$.Deferred();setDeferredTimeout(deferred,CONNECTION_TIMEOUT);var pathArray=paths;return Array.isArray(paths)||(pathArray=[paths]),autoReload&&Array.prototype.push.apply(this._registeredModules,pathArray),this.domains.base&&this.domains.base.loadDomainModulesFromPaths?(this.domains.base.loadDomainModulesFromPaths(pathArray).then(function(success){success||deferred.reject("loadDomainModulesFromPaths failed")},function(reason){deferred.reject("Unable to load one of the modules: "+pathArray+(reason?", reason: "+reason:""))}),this._pendingInterfaceRefreshDeferreds.push(deferred)):deferred.reject("this.domains.base is undefined"),deferred.promise()},NodeConnection.prototype._send=function(m){if(this.connected()){var messageString=null;if("string"==typeof m)messageString=m;else try{messageString=JSON.stringify(m)}catch(stringifyError){console.error("[NodeConnection] Unable to stringify message in order to send: "+stringifyError.message)}if(messageString)try{this._ws.send(messageString)}catch(sendError){console.error("[NodeConnection] Error sending message: "+sendError.message)}}else console.error("[NodeConnection] Not connected to node, unable to send.")},NodeConnection.prototype._receive=function(message){var responseDeferred=null,data=message.data,m;if(message.data instanceof ArrayBuffer){if(data.byteLength<4)return void console.error("[NodeConnection] received malformed binary message");var header=data.slice(0,4),body=data.slice(4),headerView,id;m={type:"commandResponse",message:{id:new Uint32Array(header)[0],response:body}}}else try{m=JSON.parse(data)}catch(e){return void console.error("[NodeConnection] received malformed message",message,e.message)}switch(m.type){case"event":"base"===m.message.domain&&"newDomains"===m.message.event&&this._refreshInterface(),EventDispatcher.triggerWithArray(this,m.message.domain+":"+m.message.event,m.message.parameters);break;case"commandResponse":(responseDeferred=this._pendingCommandDeferreds[m.message.id])&&(responseDeferred.resolveWith(this,[m.message.response]),delete this._pendingCommandDeferreds[m.message.id]);break;case"commandProgress":(responseDeferred=this._pendingCommandDeferreds[m.message.id])&&responseDeferred.notifyWith(this,[m.message.message]);break;case"commandError":(responseDeferred=this._pendingCommandDeferreds[m.message.id])&&(responseDeferred.rejectWith(this,[m.message.message,m.message.stack]),delete this._pendingCommandDeferreds[m.message.id]);break;case"error":console.error("[NodeConnection] received error: "+m.message.message);break;default:console.error("[NodeConnection] unknown event type: "+m.type)}},NodeConnection.prototype._refreshInterface=function(){var deferred=$.Deferred(),self=this,pendingDeferreds=this._pendingInterfaceRefreshDeferreds;function refreshInterfaceCallback(spec){function makeCommandFunction(domainName,commandSpec){return function(){var deferred=$.Deferred(),parameters=Array.prototype.slice.call(arguments,0),id=self._getNextCommandID();return self._pendingCommandDeferreds[id]=deferred,self._send({id:id,domain:domainName,command:commandSpec.name,parameters:parameters}),deferred}}self.domains={},self.domainEvents={},spec.forEach(function(domainSpec){self.domains[domainSpec.domain]={},domainSpec.commands.forEach(function(commandSpec){self.domains[domainSpec.domain][commandSpec.name]=makeCommandFunction(domainSpec.domain,commandSpec)}),self.domainEvents[domainSpec.domain]={},domainSpec.events.forEach(function(eventSpec){var parameters=eventSpec.parameters;self.domainEvents[domainSpec.domain][eventSpec.name]=parameters})}),deferred.resolve()}return this._pendingInterfaceRefreshDeferreds=[],deferred.then(function(){pendingDeferreds.forEach(function(d){d.resolve()})},function(err){pendingDeferreds.forEach(function(d){d.reject(err)})}),this.connected()?$.getJSON("http://localhost:"+this._port+"/api").done(refreshInterfaceCallback).fail(function(err){deferred.reject(err)}):deferred.reject("Attempted to call _refreshInterface when not connected."),deferred.promise()},NodeConnection._getConnectionTimeout=function(){return CONNECTION_TIMEOUT},module.exports=NodeConnection});