- Clone Spacebrew
npm install
depencencies- Run
node node node_server_forever.js
- Download this test using the download button on the left and extract it
- Open
index.html
in the browser
Created
February 12, 2015 23:05
-
-
Save snorpey/05ba1b0e932fb0ef0d10 to your computer and use it in GitHub Desktop.
Spacebrew Disconnect Test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title>Admin</title> | |
<style>body{font-family:sans-serif;padding:50px;}.is-connected{background-color:green}</style> | |
</head> | |
<body> | |
<h1>ADMIN</h1> | |
<script src="spacebrew-1.4.1.js"></script> | |
<script src="sb-admin-0.1.4.js"></script> | |
<script> | |
/*global Spacebrew*/ | |
var sb = new Spacebrew.Client( { server: 'localhost', name: 'Admin', reconnect: false } ); | |
sb.extend(Spacebrew.Admin); | |
sb.onNewClient = function ( client ) { | |
if ( client.name === 'User' ) { | |
document.body.classList.add( 'is-connected' ); | |
} | |
}; | |
sb.onRemoveClient = function ( name ) { | |
if ( name === 'User' ) { | |
document.body.classList.remove( 'is-connected' ); | |
} | |
}; | |
sb.connect(); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<style>*{margin:0;padding:0;box-sizing:border-box;}iframe{width:50vw;height:100vh;border:none;display:block;float:left;}</style> | |
</head> | |
<body> | |
<iframe src="user.html"></iframe> | |
<iframe src="admin.html"></iframe> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Spacebrew Admin Mixin for Javascript | |
* -------------------------------- | |
* | |
* This is an admin client mixim for the Spacebrew library. By passing the Spacebrew.Admin | |
* object into the Spacebrew.Client library's extend() method your client application will also | |
* connect to Spacebrew as an admin. | |
* | |
* Spacebrew is an open, dynamically re-routable software toolkit for choreographing interactive | |
* spaces. Or, in other words, a simple way to connect interactive things to one another. Learn | |
* more about Spacebrew here: http://docs.spacebrew.cc/ | |
* | |
* Please note that this library only works will the Spacebrew.js library sb-1.0.4.min.js and | |
* above. | |
* | |
* Latest Updates: | |
* - enable client apps to be extended to include admin privileges. | |
* - added methods to handle admin messages and to update routes. | |
* | |
* @author Julio Terra and Brett Renfer | |
* @filename sb-admin-0.1.4.js | |
* @version 0.1.4 | |
* @date April 8, 2013 | |
* | |
*/ | |
Spacebrew.Admin = { | |
admin: { | |
config: { | |
admin: true | |
, no_msgs: true | |
} | |
, active: true | |
, remoteAddress: undefined | |
, clients: [] | |
, routes: [] | |
} | |
} | |
/********************************* | |
** ADMIN CONFIGURATION METHODS | |
*********************************/ | |
/** | |
* Sends the admin config message to the Spacebrew server. This message resgisters | |
* a client app as an admin app. This method is automatically called by the | |
* Spacebrew library if the library is extended with the admin mix-in before the | |
* client is configured. | |
* | |
* @memberOf Spacebrew.Admin | |
* @private | |
*/ | |
Spacebrew.Admin.connectAdmin = function () { | |
this.socket.send(JSON.stringify(this.admin.config)); | |
} | |
/** | |
* Enables turning on or off configuration option for admin app to receive all | |
* messages sent between clients. This option must be set before the | |
* Spacebrew server connection is made | |
* | |
* @param {Boolean} get_msgs Flag that sets admin message request on or off | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.getMsgs = function ( get_msgs ) { | |
if (get_msgs == false) { | |
config.no_msgs = false; | |
} | |
else { | |
config.no_msgs = true; | |
} | |
} | |
/********************************* | |
** EVENT CALLBACK METHODS STUBS | |
*********************************/ | |
/** | |
* Override in your app to receive new client information, e.g. sb.onNewClient = yourFunction | |
* Admin-related method. | |
* | |
* @param {Object} client Object with client config details described below: | |
* - {String} name Name of client | |
* - {String} address IP address of client | |
* - {String} description Description of client | |
* - {Array} publish Array with all publish data feeds for client | |
* - {Array} subscribe Array with subscribe data feeds for client | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.onNewClient = function( client ){} | |
/** | |
* Override in your app to receive updated information about existing client, e.g. sb.onNewClient = yourFunction | |
* Admin-related method. | |
* | |
* @param {Object} client Object with client config details described below | |
* - {String} name Name of client | |
* - {String} address IP address of client | |
* - {String} description Description of client | |
* - {Array} publish Array with all publish data feeds for client | |
* - {Array} subscribe Array with subscribe data feeds for client | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
// Spacebrew.Admin.onUpdateClient = function( name, address, description, pubs, subs ){} | |
Spacebrew.Admin.onUpdateClient = function( client ){} | |
/** | |
* Override in your app to receive information about new routes, e.g. sb.onNewRoute = yourStringFunction | |
* Admin-related method. | |
* | |
* @param {String} action Type of action route message, either add or remove | |
* @param {Object} pub Object with name of client name and address, publish name and type | |
* @param {Object} sub Object with name of client name and address, subscribe name and type | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.onUpdateRoute = function( action, pub, sub ){} | |
/** | |
* Override in your app to receive client removal messages, e.g. sb.onCustomMessage = yourStringFunction | |
* Admin-related method. | |
* | |
* @param {String} name Name of client being removed | |
* @param {String} address Address of client being removed | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.onRemoveClient = function( name, address ){} | |
/********************************** | |
** PRIVATE EVENT HANDLER METHODS | |
**********************************/ | |
/** | |
* Handles admin messages received from the Spacbrew server. Routes the messages based on the | |
* message type | |
* @param {Object} data Message object that contains an admin, config, remove, or route message | |
* | |
* @memberOf Spacebrew.Admin | |
* @private | |
*/ | |
Spacebrew.Admin._handleAdminMessages = function( data ){ | |
if (this.debug) console.log("[_handleAdminMessages] new message received "); | |
if (data["admin"]) { | |
// nothing to be done | |
} | |
else if (data["remove"]) { | |
if (this.debug) console.log("[_handleAdminMessages] remove client message ", data["remove"]); | |
for (var i = 0; i < data.remove.length; i ++) { | |
this._onRemoveClient( data.remove[i] ); | |
} | |
} | |
else if (data["route"]) { | |
if (this.debug) console.log("[_handleAdminMessages] route update message ", data["route"]); | |
var new_route = { | |
route: { | |
type: data.route.type, | |
publisher: data.route.publisher, | |
subscriber: data.route.subscriber | |
} | |
} | |
if ( data.route.type !== "remove" ){ | |
var bFound = false; | |
for (var i = 0; i < this.admin.routes.length; i++) { | |
// if route does not exists then create it, otherwise abort | |
if (this._compareRoutes(new_route.route, this.admin.routes[i].route)){ | |
bFound = true; | |
break; | |
} | |
} | |
if ( !bFound ){ | |
this.admin.routes.push(new_route); | |
} | |
} else { | |
for (var i = this.admin.routes.length - 1; i >= 0; i--) { | |
// if route exists then remove it, otherwise abort | |
if (this._compareRoutes(data.route, this.admin.routes[i].route)){ | |
this.admin.routes.splice(i,i); | |
bFound = true; | |
break; | |
} | |
} | |
} | |
this.onUpdateRoute( data.route.type, data.route.publisher, data.route.subscriber ); | |
} | |
else if (data instanceof Array || data["config"]) { | |
if (this.debug) console.log("[_handleAdminMessages] handle client config message(s) ", data); | |
if (data["config"]) data = [data]; | |
for (var i = 0; i < data.length; i ++) { | |
if (data[i]["config"]) { | |
this._onNewClient(data[i].config); | |
} | |
} | |
} | |
} | |
/** | |
* Called when a new client message is received. It adds the client to this.admin.clients | |
* array and then forwards the message to front-end callback methods. | |
* | |
* @param {Object} client Object with client config details described below: | |
* - {String} name Name of client | |
* - {String} address IP address of client | |
* - {String} description Description of client | |
* - {Array} publish Array with all publish data feeds for client | |
* - {Array} subscribe Array with subscribe data feeds for client | |
* | |
* @memberOf Spacebrew.Admin | |
* @private | |
*/ | |
Spacebrew.Admin._onNewClient = function( client ){ | |
var existing_client = false; | |
this._setLocalIPAddress( client ); | |
for( var j = 0; j < this.admin.clients.length; j++ ){ | |
if ( this.admin.clients[j].name === client.name | |
&& this.admin.clients[j].remoteAddress === client.remoteAddress ) { | |
if (this.debug) console.log("existing client logged on " + client.name + " address " + client.remoteAddress); | |
existing_client = true; | |
this.admin.clients[j].publish = client.publish; | |
this.admin.clients[j].subscribe = client.subscribe; | |
this.admin.clients[j].description = client.description; | |
this.onUpdateClient( client ); | |
} | |
} | |
//if we did not find a matching client, then add this one | |
if ( !existing_client ) { | |
if (this.debug) console.log("new client logged on " + client.name + " address " + client.remoteAddress); | |
this.admin.clients.push( client ); | |
this.onNewClient( client ); | |
} | |
} | |
Spacebrew.Admin._onRemoveClient = function( client ){ | |
var existing_client = false; | |
var clientIndex = -1; | |
for( var j = 0; j < this.admin.clients.length; j++ ){ | |
if ( this.admin.clients[j].name === client.name | |
&& this.admin.clients[j].remoteAddress === client.remoteAddress ) { | |
if (this.debug) console.log("existing client logged on " + client.name + " address " + client.remoteAddress); | |
existing_client = true; | |
clientIndex = j; | |
} | |
} | |
//if we found a matching client, remove it | |
if ( existing_client ) { | |
if (this.debug) console.log("removed client " + client.name + " address " + client.remoteAddress); | |
this.admin.clients.splice( clientIndex, 1 ); | |
} | |
// broadcast event regardless | |
this.onRemoveClient( client.name, client.remoteAddress ); | |
} | |
/** | |
* Checks new client config messages to capture the IP address of the local application. | |
* It first check if the current apps IP address has already been identified, if not | |
* then it confirms that app name, and all publishers and subscribers match. | |
* | |
* @param {Object} client Object with client config details described below: | |
* - {String} name Name of client | |
* - {String} address IP address of client | |
* - {String} description Description of client | |
* - {Array} publish Array with all publish data feeds for client | |
* - {Array} subscribe Array with subscribe data feeds for client | |
* | |
* @memberOf Spacebrew.Admin | |
* @private | |
*/ | |
Spacebrew.Admin._setLocalIPAddress = function ( client ) { | |
var match_confirmed = true | |
, cur_pub_sub = ["subscribe", "publish"] | |
, client_config = [] | |
, local_config = [] | |
; | |
// check if client already exists | |
if (client.name === this._name && !this.admin.remoteAddress) { | |
if ((client.publish.messages.length == this.client_config.publish.messages.length) && | |
(client.subscribe.messages.length == this.client_config.subscribe.messages.length)) { | |
for (var j = 0; j < cur_pub_sub.length; j ++ ) { | |
client_config = client[cur_pub_sub[j]].messages; | |
local_config = this.client_config[cur_pub_sub[j]].messages; | |
for (var i = 0; i < client_config.length; i ++ ) { | |
if (!(client_config[i].name === local_config[i].name) || | |
!(client_config[i].type === local_config[i].type)) { | |
match_confirmed = false; | |
break; | |
} | |
} | |
} | |
if (match_confirmed){ | |
this.admin.remoteAddress = client.remoteAddress; | |
if (this.debug) console.log("[_setLocalIPAddress] local IP address set to ", this.admin.remoteAddress); | |
} | |
} | |
} | |
} | |
/********************************** | |
** ROUTE HANDLER METHODS | |
**********************************/ | |
/** | |
* Method that is used to add a route to the Spacebrew server | |
* @param {String or Object} pub_client Publish client app name OR | |
* object with all publish information. | |
* @param {String or Object} pub_address Publish app remote IP address OR | |
* object with all subscribe information. | |
* @param {String} pub_name Publish name | |
* @param {String} sub_client Subscribe client app name | |
* @param {String} sub_address Subscribe app remote IP address | |
* @param {String} sub_name Subscribe name | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.addRoute = function ( pub_client, pub_address, pub_name, sub_client, sub_address, sub_name ){ | |
this._updateRoute("add", pub_client, pub_address, pub_name, sub_client, sub_address, sub_name); | |
} | |
/** | |
* Method that is used to add a sub route to the Spacebrew server | |
* @param {String} pub_name Publish name | |
* @param {String} sub_client Subscribe client app name | |
* @param {String} sub_address Subscribe app remote IP address | |
* @param {String} sub_name Subscribe name | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.addSubRoute = function ( pub_name, sub_client, sub_address, sub_name ){ | |
if (!this.admin.remoteAddress) return; | |
this._updateRoute("add", this._name, this.admin.remoteAddress, pub_name, sub_client, sub_address, sub_name); | |
} | |
/** | |
* Method that is used to add a pub route to the Spacebrew server | |
* @param {String} sub_name Publish name | |
* @param {String} pub_client Subscribe client app name | |
* @param {String} pub_address Subscribe app remote IP address | |
* @param {String} pub_name Subscribe name | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.addPubRoute = function ( sub_name, pub_client, pub_address, pub_name){ | |
if (!this.admin.remoteAddress) return; | |
this._updateRoute("add", pub_client, pub_address, pub_name, this._name, this.admin.remoteAddress, sub_name); | |
} | |
/** | |
* Method that is used to remove a route from the Spacebrew server | |
* @param {String or Object} pub_client Publish client app name OR | |
* object with all publish information. | |
* @param {String or Object} pub_address Publish app remote IP address OR | |
* object with all subscribe information. | |
* @param {String} pub_name Publish name | |
* @param {String} sub_client Subscribe client app name | |
* @param {String} sub_address Subscribe app remote IP address | |
* @param {String} sub_name Subscribe name | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.removeRoute = function ( pub_client, pub_address, pub_name, sub_client, sub_address, sub_name ){ | |
this._updateRoute("remove", pub_client, pub_address, pub_name, sub_client, sub_address, sub_name); | |
} | |
/** | |
* Method that is used to remove a sub route from the Spacebrew server | |
* @param {String} pub_name Publish name | |
* @param {String} sub_client Subscribe client app name | |
* @param {String} sub_address Subscribe app remote IP address | |
* @param {String} sub_name Subscribe name | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.removeSubRoute = function ( pub_name, sub_client, sub_address, sub_name ){ | |
if (!this.admin.remoteAddress) return; | |
this._updateRoute("remove", this._name, this.admin.remoteAddress, pub_name, sub_client, sub_address, sub_name); | |
} | |
/** | |
* Method that is used to remove a pub route from the Spacebrew server | |
* @param {String} sub_name Publish name | |
* @param {String} pub_client Subscribe client app name | |
* @param {String} pub_address Subscribe app remote IP address | |
* @param {String} pub_name Subscribe name | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.removePubRoute = function ( sub_name, pub_client, pub_address, pub_name){ | |
if (!this.admin.remoteAddress) return; | |
this._updateRoute("remove", pub_client, pub_address, pub_name, this._name, this.admin.remoteAddress, sub_name); | |
} | |
/** | |
* Method that handles both add and remove route requests. Responsible for parsing requests | |
* and communicating with Spacebrew server | |
* | |
* @param {String} type Type of route request, either "add" or "remove" | |
* @param {String or Object} pub_client Publish client app name OR | |
* @param {String or Object} pub_client Publish client app name OR | |
* object with all publish information. | |
* @param {String or Object} pub_address Publish app remote IP address OR | |
* object with all subscribe information. | |
* @param {String} pub_name Publish name | |
* @param {String} sub_client Subscribe client app name | |
* @param {String} sub_address Subscribe app remote IP address | |
* @param {String} sub_name Subscribe name | |
* | |
* @memberOf Spacebrew.Admin | |
* @private | |
*/ | |
Spacebrew.Admin._updateRoute = function ( type, pub_client, pub_address, pub_name, sub_client, sub_address, sub_name ){ | |
var new_route | |
, route_type | |
, subscribe | |
, publish | |
; | |
// if request type is not supported then abort | |
if (type !== "add" && type !== "remove") return; | |
// check if pub and sub information was in first two arguments. If so then | |
if ((pub_client.clientName && pub_client.remoteAddress && pub_client.name && pub_client.type != undefined) && | |
(pub_address.clientName && pub_address.remoteAddress && pub_address.name && pub_address.type != undefined)) { | |
new_route = { | |
route: { | |
type: type, | |
publisher: pub_client, | |
subscriber: pub_address | |
} | |
} | |
if (type === "add") { | |
var bFound = false; | |
for (var i = 0; i < this.admin.routes.length; i++) { | |
// if route does not exists then create it, otherwise abort | |
if (this._compareRoutes(new_route.route, this.admin.routes[i].route)){ | |
bFound = true; | |
break; | |
} | |
} | |
if ( bFound ){ | |
return; | |
} else { | |
this.admin.routes.push(new_route); | |
} | |
} | |
else if (type === "remove") { | |
var bFound = false; | |
for (var i = this.admin.routes.length - 1; i >= 0; i--) { | |
// if route exists then remove it, otherwise abort | |
if (this._compareRoutes(new_route.route, this.admin.routes[i].route)){ | |
this.admin.routes.splice(i,i); | |
bFound = true; | |
break; | |
} | |
} | |
if ( !bFound ){ | |
console.log("[_updateRoute] trying to remove route that does not exist"); | |
//return; | |
} | |
} | |
// send new route information to spacebrew server | |
if (this.debug) console.log("[_updateRoute] sending route to admin ", JSON.stringify(new_route)); | |
this.socket.send(JSON.stringify(new_route)); | |
return; | |
} | |
pub_type = this.getPublishType(pub_client, pub_address, pub_name); | |
sub_type = this.getSubscribeType(sub_client, sub_address, sub_name); | |
if (pub_type != sub_type || pub_type == false || pub_type == undefined) { | |
if (this.debug) console.log("[_updateRoute] not routed :: types don't match - pub:" + pub_type + " sub: " + sub_type); | |
return; | |
} | |
publish = { | |
clientName: pub_client, | |
remoteAddress: pub_address, | |
name: pub_name, | |
type: pub_type | |
} | |
if (this.debug) console.log("[_updateRoute] created pub object ", publish); | |
subscribe = { | |
clientName: sub_client, | |
remoteAddress: sub_address, | |
name: sub_name, | |
type: sub_type | |
} | |
if (this.debug) console.log("[_updateRoute] created sub object ", subscribe); | |
// call itself with publish and subscribe objects properly formatted | |
this._updateRoute(type, publish, subscribe); | |
} | |
/** | |
* Compares two different routes. | |
* | |
* @param {Object} route_a Route description that contains client name and remote address, | |
* route name and type | |
* @param {Object} route_b Route description that contains client name and remote address, | |
* route name and type | |
* @return {Boolean} Returns true of match is valid, false otherwise | |
* | |
* @memberOf Spacebrew.Admin | |
* @private | |
*/ | |
Spacebrew.Admin._compareRoutes = function (route_a, route_b){ | |
// publisher | |
var bPublisherMatch = false; | |
if ((route_a.publisher.clientName === route_b.publisher.clientName) && | |
(route_a.publisher.name === route_b.publisher.name) && | |
(route_a.publisher.type === route_b.publisher.type) && | |
(route_a.publisher.remoteAddress === route_b.publisher.remoteAddress)) { | |
bPublisherMatch = true; | |
} | |
// subscriber | |
var bSubscriberMatch = false; | |
if ((route_a.subscriber.clientName === route_b.subscriber.clientName) && | |
(route_a.subscriber.name === route_b.subscriber.name) && | |
(route_a.subscriber.type === route_b.subscriber.type) && | |
(route_a.subscriber.remoteAddress === route_b.subscriber.remoteAddress)) { | |
bSubscriberMatch = true; | |
} | |
return bSubscriberMatch && bPublisherMatch; | |
} | |
/********************************** | |
** INSPECT CLIENT METHODS | |
**********************************/ | |
/** | |
* Returns the type of a publisher | |
* | |
* @param {String} client_name Name of the client app | |
* @param {String} remote_address Remote address of client app | |
* @param {String} pub_name Publisher name | |
* @return {String} Data type name | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.getPublishType = function (client_name, remote_address, pub_name){ | |
return this._getPubSubType("publish", client_name, remote_address, pub_name); | |
} | |
/** | |
* Returns the type of a subscriber | |
* | |
* @param {String} client_name Name of the client app | |
* @param {String} remote_address Remote address of client app | |
* @param {String} pub_name Subscriber name | |
* @return {String} Data type name | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.getSubscribeType = function (client_name, remote_address, sub_name){ | |
return this._getPubSubType("subscribe", client_name, remote_address, sub_name); | |
} | |
/** | |
* Returns the type of a subscriber or publisher | |
* | |
* @param {String} pub_or_sub Flag that identifies if matching subscriber or publisher | |
* @param {String} client_name Name of the client app | |
* @param {String} remote_address Remote address of client app | |
* @param {String} pub_name Subscriber name | |
* @return {String} Data type name | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin._getPubSubType = function (pub_or_sub, client_name, remote_address, pub_sub_name){ | |
var clients; | |
for( var j = 0; j < this.admin.clients.length; j++ ){ | |
client = this.admin.clients[j]; | |
if ( client.name === client_name && client.remoteAddress === remote_address ) { | |
for( var i = 0; i < client[pub_or_sub].messages.length; i++ ){ | |
//if (this.debug) console.log("Compare Types " + client[pub_or_sub].messages[i].name + " with " + pub_sub_name) | |
if (client[pub_or_sub].messages[i].name === pub_sub_name) { | |
return client[pub_or_sub].messages[i].type; | |
} | |
} | |
} | |
} | |
return false; | |
} | |
/** | |
* Checks if the a client name and ip address refer to current app | |
* | |
* @param {String} client_name Name of the client | |
* @param {String} remote_address IP address of the client | |
* @return {Boolean} True if name and ip address refers to current app | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.isThisApp = function (client_name, remote_address){ | |
if (this._name === client_name && this.admin.remoteAddress === remote_address) return true; | |
else return false; | |
} | |
/** | |
* Returns the client that matches the name and remoteAddress parameters queried. | |
* | |
* @param {String} name Name of the client application | |
* @param {String} remoteAddress IP address of the client apps | |
* @return {Object} Object featuring all client config information | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.getClient = function (name, remoteAddress){ | |
var client; | |
for( var j = 0; j < this.admin.clients.length; j++ ){ | |
client = this.admin.clients[j]; | |
if ( client.name === name && client.remoteAddress === remoteAddress ) { | |
return client; | |
} | |
} | |
} | |
/********************************** | |
** UTILITY METHODS | |
**********************************/ | |
/** | |
* returns a list of all subscribers that match a specific type. | |
* @param {String} type Data type of subscribers that should be returned | |
* @return {Array} Array with subscribers | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.subscribeListByType = function (type){ | |
return this._pubSubByType("subscribe", type); | |
} | |
/** | |
* returns a list of all publishers that match a specific type. | |
* @param {String} type Data type of publishers that should be returned | |
* @return {Array} Array with publishers | |
* | |
* @memberOf Spacebrew.Admin | |
* @public | |
*/ | |
Spacebrew.Admin.publishListByType = function (type){ | |
return this._pubSubByType("publish", type); | |
} | |
/** | |
* returns a list of all publishers or subscribers that match a specific type. | |
* @param {String} pub_or_sub Flag that identifies if method should return publishers or subscribers | |
* @param {String} type Data type of publishers or subscribers that should be returned | |
* @return {Array} Array with publishers | |
* | |
* @memberOf Spacebrew.Admin | |
* @private | |
*/ | |
Spacebrew.Admin._pubSubByType = function (pub_or_sub, type){ | |
var client = {} | |
, filtered_clients = [] | |
, pub_sub_item = {} | |
, new_item = {} | |
; | |
for( var j = 0; j < this.admin.clients.length; j++ ){ | |
client = this.admin.clients[j]; | |
for (var i = 0; i < client[pub_or_sub].messages.length; i++) { | |
pub_sub_item = client[pub_or_sub].messages[i]; | |
if ( pub_sub_item.type === type ) { | |
new_item = { clientName: client.name | |
, remoteAddress: client.remoteAddress | |
, name: pub_sub_item.name | |
, type: pub_sub_item.type | |
}; | |
filtered_clients.push( new_item ); | |
} | |
} | |
} | |
return filtered_clients; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* Spacebrew Library for Javascript | |
* -------------------------------- | |
* | |
* This library was designed to work on front-end (browser) envrionments, and back-end (server) | |
* environments. Please refer to the readme file, the documentation and examples to learn how to | |
* use this library. | |
* | |
* Spacebrew is an open, dynamically re-routable software toolkit for choreographing interactive | |
* spaces. Or, in other words, a simple way to connect interactive things to one another. Learn | |
* more about Spacebrew here: http://docs.spacebrew.cc/ | |
* | |
* To import into your web apps, we recommend using the minimized version of this library. | |
* | |
* Latest Updates: | |
* - added binary message support | |
* - added blank "options" attribute to config message - for future use | |
* - caps number of messages sent to 60 per second | |
* - reconnect to spacebrew if connection lost | |
* - enable client apps to extend libs with admin functionality. | |
* - added close method to close Spacebrew connection. | |
* | |
* @author LAB at Rockwell Group, Brett Renfer, Eric Eckhard-Ishii, Julio Terra, Quin Kennedy | |
* @filename sb-1.4.1.js | |
* @version 1.4.1 | |
* @date April 8, 2014 | |
* | |
*/ | |
/** | |
* Check if Bind method exists in current enviroment. If not, it creates an implementation of | |
* this useful method. | |
*/ | |
if (!Function.prototype.bind) { | |
Function.prototype.bind = function (oThis) { | |
if (typeof this !== "function") { | |
// closest thing possible to the ECMAScript 5 internal IsCallable function | |
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); | |
} | |
var aArgs = Array.prototype.slice.call(arguments, 1), | |
fToBind = this, | |
fNOP = function () {}, | |
fBound = function () { | |
return fToBind.apply(this instanceof fNOP | |
? this | |
: oThis || window, | |
aArgs.concat(Array.prototype.slice.call(arguments))); | |
}; | |
fNOP.prototype = this.prototype; | |
fBound.prototype = new fNOP(); | |
return fBound; | |
}; | |
} | |
/** | |
* @namespace for Spacebrew library | |
*/ | |
var Spacebrew = Spacebrew || {}; | |
/** | |
* create placeholder var for WebSocket object, if it does not already exist | |
*/ | |
var WebSocket = WebSocket || {}; | |
/** | |
* Check if Running in Browser or Server (Node) Environment * | |
*/ | |
// check if window object already exists to determine if running browswer | |
var window = window || undefined; | |
// check if module object already exists to determine if this is a node application | |
var module = module || undefined; | |
// if app is running in a browser, then define the getQueryString method | |
if (window) { | |
if (!window["getQueryString"]){ | |
/** | |
* Get parameters from a query string | |
* @param {String} name Name of query string to parse (w/o '?' or '&') | |
* @return {String} value of parameter (or empty string if not found) | |
*/ | |
window.getQueryString = function( name ) { | |
if (!window.location){ | |
return; | |
} | |
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]"); | |
var regexS = "[\\?&]"+name+"=([^&#]*)"; | |
var regex = new RegExp( regexS ); | |
var results = regex.exec( window.location.href ); | |
if( results == null ){ | |
return ""; | |
} else { | |
return results[1]; | |
} | |
}; | |
} | |
} | |
// if app is running in a node server environment then package Spacebrew library as a module. | |
// WebSocket module (ws) needs to be saved in a node_modules so that it can be imported. | |
if (!window && module) { | |
WebSocket = require("ws"); | |
module.exports = { | |
Spacebrew: Spacebrew | |
}; | |
} | |
/** | |
* Define the Spacebrew Library * | |
*/ | |
/** | |
* Spacebrew client! | |
* @constructor | |
* @param {String} server (Optional) Base address of Spacebrew server. This server address is overwritten if server defined in query string; defaults to localhost. | |
* @param {String} name (Optional) Base name of app. Base name is overwritten if "name" is defined in query string; defaults to window.location.href. | |
* @param {String} description (Optional) Base description of app. Description name is overwritten if "description" is defined in query string; | |
* @param {Object} options (Optional) An object that holds the optional parameters described below | |
* port (Optional) Port number for the Spacebrew server | |
* admin (Optional) Flag that identifies when app should register for admin privileges with server | |
* debug (Optional) Debug flag that turns on info and debug messaging (limited use) | |
*/ | |
Spacebrew.Client = function( server, name, description, options ){ | |
options = options || {}; | |
// check if the server variable is an object that holds all config values | |
if (server !== undefined) { | |
if (toString.call(server) !== "[object String]") { | |
options.port = server.port || undefined; | |
options.debug = server.debug || false; | |
options.reconnect = server.reconnect || false; | |
description = server.description || undefined; | |
name = server.name || undefined; | |
server = server.server || undefined; | |
} | |
} | |
this.debug = false; | |
if ( window ){ | |
this.debug = (window.getQueryString("debug") === "true" ? true : (options.debug || false)); | |
} | |
this.reconnect = typeof options.reconnect === "boolean" ? options.reconnect : true; | |
this.reconnect_timer = undefined; | |
this.sendRateCapped = options.capSendRate === undefined ? false : options.capSendRate; | |
this.send_interval = options.sendRate || 16; | |
this.send_blocked = false; | |
this.msg = {}; | |
/** | |
* Name of app | |
* @type {String} | |
*/ | |
this._name = name || "javascript client #"; | |
if (window) { | |
this._name = (window.getQueryString("name") !== "" ? unescape(window.getQueryString("name")) : this._name); | |
} | |
/** | |
* Description of your app | |
* @type {String} | |
*/ | |
this._description = description || "spacebrew javascript client"; | |
if (window) { | |
this._description = (window.getQueryString("description") !== "" ? unescape(window.getQueryString("description")) : this._description); | |
} | |
/** | |
* Spacebrew server to which the app will connect | |
* @type {String} | |
*/ | |
this.server = server || "sandbox.spacebrew.cc"; | |
if (window) { | |
this.server = (window.getQueryString("server") !== "" ? unescape(window.getQueryString("server")) : this.server); | |
} | |
/** | |
* Port number on which Spacebrew server is running | |
* @type {Integer} | |
*/ | |
this.port = options.port || 9000; | |
if (window) { | |
var port = window.getQueryString("port"); | |
if (port !== "" && !isNaN(port)) { | |
this.port = port; | |
} | |
} | |
/** | |
* Reference to WebSocket | |
* @type {WebSocket} | |
*/ | |
this.socket = null; | |
/** | |
* Configuration file for Spacebrew | |
* @type {Object} | |
*/ | |
this.client_config = { | |
name: this._name, | |
description: this._description, | |
publish:{ | |
messages:[] | |
}, | |
subscribe:{ | |
messages:[] | |
}, | |
options:{} | |
}; | |
this.admin = {}; | |
/** | |
* Are we connected to a Spacebrew server? | |
* @type {Boolean} | |
*/ | |
this._isConnected = false; | |
}; | |
/** | |
* Connect to Spacebrew | |
* @memberOf Spacebrew.Client | |
*/ | |
Spacebrew.Client.prototype.connect = function(){ | |
try { | |
this.socket = new WebSocket("ws://" + this.server + ":" + this.port); | |
this.socket.binaryType = "arraybuffer"; | |
this.socket.onopen = this._onOpen.bind(this); | |
this.socket.onmessage = this._onMessage.bind(this); | |
this.socket.onclose = this._onClose.bind(this); | |
} catch(e){ | |
this._isConnected = false; | |
console.log("[connect:Spacebrew] connection attempt failed"); | |
} | |
}; | |
/** | |
* Close Spacebrew connection | |
* @memberOf Spacebrew.Client | |
*/ | |
Spacebrew.Client.prototype.close = function(){ | |
try { | |
if (this._isConnected) { | |
this.socket.close(); | |
this._isConnected = false; | |
console.log("[close:Spacebrew] closing websocket connection"); | |
} | |
} catch (e) { | |
this._isConnected = false; | |
} | |
}; | |
/** | |
* Override in your app to receive on open event for connection | |
* @memberOf Spacebrew.Client | |
* @public | |
*/ | |
Spacebrew.Client.prototype.onOpen = function( name, value ){}; | |
/** | |
* Override in your app to receive on close event for connection | |
* @memberOf Spacebrew.Client | |
* @public | |
*/ | |
Spacebrew.Client.prototype.onClose = function( name, value ){}; | |
/** | |
* Override in your app to receive "range" messages, e.g. sb.onRangeMessage = yourRangeFunction | |
* @param {String} name Name of incoming route | |
* @param {Number} value The data being provided | |
* @memberOf Spacebrew.Client | |
* @public | |
*/ | |
Spacebrew.Client.prototype.onRangeMessage = function( name, value ){}; | |
/** | |
* Override in your app to receive "boolean" messages, e.g. sb.onBooleanMessage = yourBoolFunction | |
* @param {String} name Name of incoming route | |
* @param {Boolean} value The data being provided | |
* @memberOf Spacebrew.Client | |
* @public | |
*/ | |
Spacebrew.Client.prototype.onBooleanMessage = function( name, value ){}; | |
/** | |
* Override in your app to receive "string" messages, e.g. sb.onStringMessage = yourStringFunction | |
* @param {String} name Name of incoming route | |
* @param {String} value The data being provided | |
* @memberOf Spacebrew.Client | |
* @public | |
*/ | |
Spacebrew.Client.prototype.onStringMessage = function( name, value ){}; | |
/** | |
* Override in your app to receive "custom" messages, e.g. sb.onCustomMessage = yourStringFunction | |
* @param {String} name Name of incoming route | |
* @param {String} value The data being provided | |
* @param {String} type The type name of this route | |
* @memberOf Spacebrew.Client | |
* @public | |
*/ | |
Spacebrew.Client.prototype.onCustomMessage = function( name, value, type ){}; | |
/** | |
* Override in your app to receive any binary messages, e.g. sb.onBinaryMessage = yourBinaryFunction | |
* @param {String} name Name of incoming route | |
* @param {Object} value The data being provided on the named route {buffer:[received ArrayBuffer], startIndex:[start index for binary data]} | |
* @param {String} type The type name of this route | |
*/ | |
Spacebrew.Client.prototype.onBinaryMessage = function( name, value, type ){}; | |
/** | |
* Add a route you are publishing on | |
* @param {String} name Name of incoming route | |
* @param {String} type "boolean", "range", or "string" | |
* @param {String} def default value | |
* @memberOf Spacebrew.Client | |
* @public | |
*/ | |
Spacebrew.Client.prototype.addPublish = function( name, type, def ){ | |
this.client_config.publish.messages.push({"name":name, "type":type, "default":def}); | |
this.updatePubSub(); | |
}; | |
/** | |
* [addSubscriber description] | |
* @param {String} name Name of outgoing route | |
* @param {String} type "boolean", "range", or "string" | |
* @memberOf Spacebrew.Client | |
* @public | |
*/ | |
Spacebrew.Client.prototype.addSubscribe = function( name, type ){ | |
this.client_config.subscribe.messages.push({"name":name, "type":type }); | |
this.updatePubSub(); | |
}; | |
/** | |
* Update publishers and subscribers | |
* @memberOf Spacebrew.Client | |
* @private | |
*/ | |
Spacebrew.Client.prototype.updatePubSub = function(){ | |
if (this._isConnected) { | |
this.socket.send(JSON.stringify({"config": this.client_config})); | |
} | |
}; | |
/** | |
* Send a route to Spacebrew | |
* @param {String} name Name of outgoing route (must match something in addPublish) | |
* @param {String} type "boolean", "range", or "string" | |
* @param {String} value Value to send | |
* @memberOf Spacebrew.Client | |
* @public | |
*/ | |
Spacebrew.Client.prototype.send = function( name, type, value ){ | |
var self = this; | |
this.msg = { | |
"message": { | |
"clientName":this._name, | |
"name": name, | |
"type": type | |
} | |
}; | |
if (typeof(value) == "string"){ | |
this.msg.message.value = value; | |
} else { | |
if (("buffer" in value) && (value.buffer instanceof ArrayBuffer)){ | |
value = value.buffer; | |
} | |
if (value instanceof ArrayBuffer){ | |
this.msg.message.value = value.byteLength; | |
} else { | |
//unexpected value type | |
return; | |
} | |
} | |
// are we capping the rate at which we send messages? | |
if ( this.sendRateCapped ){ | |
// if send block is not active then send message | |
if (!this.send_blocked) { | |
this.socket.send(JSON.stringify(this.msg)); | |
this.send_blocked = true; | |
this.msg = undefined; | |
// set the timer to unblock message sending | |
setTimeout(function() { | |
self.send_blocked = false; // remove send block | |
if (self.msg !== undefined) { // if message exists then sent it | |
self.send(self.msg.message.name, self.msg.message.type, self.msg.message.value); | |
} | |
}, self.send_interval); | |
} | |
} else { | |
if (value instanceof ArrayBuffer){ | |
var jsonString = JSON.stringify(this.msg); | |
var jsonByteLength = Spacebrew.numBytesToEncodeString(jsonString); | |
var numBytesForJsonLength = (jsonByteLength < 254 ? 1 : (jsonByteLength <= 0xFFFF ? 3 : 5)); | |
var bufferSize = value.byteLength + jsonByteLength + numBytesForJsonLength; | |
var newBuffer = new ArrayBuffer( bufferSize ); | |
var numView = new Uint8Array(newBuffer, 0, numBytesForJsonLength); | |
if (numBytesForJsonLength == 5){ | |
numView[0] = 255; | |
numView[1] = ((jsonByteLength >>> 24) & 0xFF); | |
numView[2] = ((jsonByteLength >>> 16) & 0xFF); | |
numView[3] = ((jsonByteLength >>> 8) & 0xFF); | |
numView[4] = (jsonByteLength & 0xFF); | |
} else if (numBytesForJsonLength == 3){ | |
numView[0] = 254; | |
numView[1] = ((jsonByteLength >>> 8) & 0xFF); | |
numView[2] = (jsonByteLength & 0xFF); | |
} else { | |
numView[0] = jsonByteLength; | |
} | |
Spacebrew.encodeToBuffer(jsonString, new Uint8Array(newBuffer, numBytesForJsonLength, jsonByteLength)); | |
var dataView = new Uint8Array(newBuffer, numBytesForJsonLength + jsonByteLength); | |
dataView.set(new Uint8Array(value)); | |
this.socket.send(newBuffer); | |
} else { | |
this.socket.send(JSON.stringify(this.msg)); | |
} | |
} | |
}; | |
/** | |
* Called on WebSocket open | |
* @private | |
* @memberOf Spacebrew.Client | |
*/ | |
Spacebrew.Client.prototype._onOpen = function() { | |
console.log("[_onOpen:Spacebrew] Spacebrew connection opened, client name is: " + this._name); | |
this._isConnected = true; | |
if (this.admin.active) { | |
this.connectAdmin(); | |
} | |
// if reconnect functionality is activated then clear interval timer when connection succeeds | |
if (this.reconnect_timer) { | |
console.log("[_onOpen:Spacebrew] tearing down reconnect timer"); | |
this.reconnect_timer = clearInterval(this.reconnect_timer); | |
this.reconnect_timer = undefined; | |
} | |
// send my config | |
this.updatePubSub(); | |
this.onOpen(); | |
}; | |
/** | |
* Called on WebSocket message | |
* @private | |
* @param {Object} e | |
* @memberOf Spacebrew.Client | |
*/ | |
Spacebrew.Client.prototype._onMessage = function( e ){ | |
var data, binaryData; | |
if (e.data instanceof ArrayBuffer){ | |
var binaryPacket = new Uint8Array(e.data); | |
var jsonLength = binaryPacket[0]; | |
var jsonStartIndex = 1; | |
if (jsonLength == 254){ | |
jsonLength = ((binaryPacket[1] << 8) + binaryPacket[2]); | |
jsonStartIndex = 3; | |
} else if (jsonLength == 255){ | |
jsonLength = ((binaryPacket[1] << 24) + (binaryPacket[2] << 16) + (binaryPacket[3] << 8) + binaryPacket[4]); | |
jsonStartIndex = 5; | |
} | |
if (jsonLength > 0){ | |
try { | |
var jsonString = Spacebrew.decodeFromBuffer(new Uint8Array(e.data, jsonStartIndex, jsonLength)); | |
data = JSON.parse(jsonString); | |
binaryData = {"buffer":e.data, "startIndex":jsonStartIndex+jsonLength}; | |
} catch (err){ | |
console.error(err); | |
return; | |
} | |
} else { | |
//empty message? | |
return; | |
} | |
} else { | |
data = JSON.parse(e.data); | |
} | |
var name | |
, type | |
, value | |
, clientName // not used yet, needs to be added to callbacks! | |
; | |
// handle client messages | |
if ((!("targetType" in data) && !(data instanceof Array)) || data["targetType"] == "client"){ | |
//expecting only messages | |
if ("message" in data) { | |
name = data.message.name; | |
type = data.message.type; | |
value = data.message.value; | |
// for now only adding this if we have it, for backwards compatibility | |
if ( data.message.clientName ) { | |
clientName = data.message.clientName; | |
} | |
if (binaryData !== undefined){ | |
this.onBinaryMessage( name, binaryData, type ); | |
} else { | |
switch( type ){ | |
case "boolean": | |
this.onBooleanMessage( name, value == "true" ); | |
break; | |
case "string": | |
this.onStringMessage( name, value ); | |
break; | |
case "range": | |
this.onRangeMessage( name, Number(value) ); | |
break; | |
default: | |
this.onCustomMessage( name, value, type ); | |
} | |
} | |
} else { | |
//illegal message | |
return; | |
} | |
} | |
// handle admin messages | |
else { | |
if (this.admin) { | |
this._handleAdminMessages( data ); | |
} | |
} | |
}; | |
/** | |
* Called on WebSocket close | |
* @private | |
* @memberOf Spacebrew.Client | |
*/ | |
Spacebrew.Client.prototype._onClose = function() { | |
var self = this; | |
console.log("[_onClose:Spacebrew] Spacebrew connection closed"); | |
this._isConnected = false; | |
if (this.admin.active) { | |
this.admin.remoteAddress = undefined; | |
} | |
// if reconnect functionality is activated set interval timer if connection dies | |
if (this.reconnect && !this.reconnect_timer) { | |
console.log("[_onClose:Spacebrew] setting up reconnect timer"); | |
this.reconnect_timer = setInterval(function () { | |
if (self.isConnected !== false) { | |
self.connect(); | |
console.log("[reconnect:Spacebrew] attempting to reconnect to spacebrew"); | |
} | |
}, 5000); | |
} | |
this.onClose(); | |
}; | |
/** | |
* name Method that sets or gets the spacebrew app name. If parameter is provided then it sets the name, otherwise | |
* it just returns the current app name. | |
* @param {String} newName New name of the spacebrew app | |
* @return {String} Returns the name of the spacebrew app if called as a getter function. If called as a | |
* setter function it will return false if the method is called after connecting to spacebrew, | |
* because the name must be configured before connection is made. | |
*/ | |
Spacebrew.Client.prototype.name = function (newName){ | |
if (newName) { // if a name has been passed in then update it | |
if (this._isConnected) { | |
return false; // if already connected we can't update name | |
} | |
this._name = newName; | |
if (window) { | |
this._name = (window.getQueryString("name") !== "" ? unescape(window.getQueryString("name")) : this._name); | |
} | |
this.client_config.name = this._name; // update spacebrew config file | |
} | |
return this._name; | |
}; | |
/** | |
* name Method that sets or gets the spacebrew app description. If parameter is provided then it sets the description, | |
* otherwise it just returns the current app description. | |
* @param {String} newDesc New description of the spacebrew app | |
* @return {String} Returns the description of the spacebrew app if called as a getter function. If called as a | |
* setter function it will return false if the method is called after connecting to spacebrew, | |
* because the description must be configured before connection is made. | |
*/ | |
Spacebrew.Client.prototype.description = function (newDesc){ | |
if (newDesc) { // if a description has been passed in then update it | |
if (this._isConnected) { | |
return false; // if already connected we can't update description | |
} | |
this._description = newDesc || "spacebrew javascript client"; | |
if (window) { | |
this._description = (window.getQueryString("description") !== "" ? unescape(window.getQueryString("description")) : this._description); | |
} | |
this.client_config.description = this._description; // update spacebrew config file | |
} | |
return this._description; | |
}; | |
/** | |
* isConnected Method that returns current connection state of the spacebrew client. | |
* @return {Boolean} Returns true if currently connected to Spacebrew | |
*/ | |
Spacebrew.Client.prototype.isConnected = function (){ | |
return this._isConnected; | |
}; | |
Spacebrew.Client.prototype.extend = function ( mixin ) { | |
for (var prop in mixin) { | |
if (mixin.hasOwnProperty(prop)) { | |
this[prop] = mixin[prop]; | |
} | |
} | |
}; | |
/** | |
* Returns the number of bytes necessary to encode the provided string in UTF-8 | |
* @param {String} string The text to encode | |
* @return {Number} The number of bytes necessary to encode the provided text in UTF-8 | |
*/ | |
Spacebrew.numBytesToEncodeString = function( text ){ | |
var totalBytes = 0; | |
for (var i = text.length - 1; i >= 0; i--) { | |
totalBytes += Spacebrew.numBytesToEncodeCode(text.charCodeAt(i)); | |
} | |
return totalBytes; | |
}; | |
/** | |
* Returns the number of bytes required to encode the given character code in UTF-8 | |
* @param {Number} charCode The character code to encode | |
* @return {Number} The number of bytes required to encode in UTF-8 | |
*/ | |
Spacebrew.numBytesToEncodeCode = function( charCode ){ | |
if ((charCode >> 16) > 0){ | |
return 4; | |
} | |
if ((charCode >> 11) > 0){ | |
return 3; | |
} | |
if ((charCode >> 7) > 0){ | |
return 2; | |
} | |
return 1; | |
}; | |
/** | |
* Encodes the provided string into an ArrayBuffer using UTF-8 | |
* @param {String} string The string to encode into the ArrayBuffer | |
* @param {Uint8Array} buffer Optionally, pass in a buffer to use for storing the encoded bytes in. | |
* @return {Uint8Array} A Uint8Array containing the UTF-8 encoded bytes for the supplied string | |
*/ | |
Spacebrew.encodeToBuffer = function( text, view ){ | |
if (!(view instanceof Uint8Array)){ | |
var buffer = new ArrayBuffer(Spacebrew.numBytesToEncodeString(text)); | |
view = new Uint8Array(buffer); | |
} | |
var numBytes, charCode, currIndex = 0; | |
for (var i = 0; i < text.length; i++) { | |
charCode = text.charCodeAt(i); | |
numBytes = Spacebrew.numBytesToEncodeCode(charCode); | |
if (numBytes == 1){ | |
view[currIndex] = charCode; | |
currIndex++; | |
} else { | |
var temp = 0x80; | |
for(var j = 1; j < numBytes; j++){ | |
temp = ((temp >> 1) | 0x80); | |
view[currIndex + numBytes - j] = (0x80 | (charCode & 0x3F)); | |
charCode >>>= 6; | |
} | |
view[currIndex] = (temp | charCode); | |
currIndex += numBytes; | |
} | |
} | |
return view; | |
}; | |
/** | |
* Decodes the provided Uint8Array into a string, assuming UTF-8 encoding | |
* @param {Uint8Array} buffer The encoded string | |
* @return {String} The decoded string | |
*/ | |
Spacebrew.decodeFromBuffer = function( buffer ){ | |
if (!(buffer instanceof Uint8Array)){ | |
return undefined; | |
} | |
var charCode, numBytes, rawByte, text = ""; | |
for(var i = 0; i < buffer.length;){ | |
rawByte = buffer[i]; | |
i++; | |
numBytes = 0; | |
while((rawByte & 0x80) > 0){ | |
numBytes++; | |
rawByte = ((rawByte << 1) & 0xFF); | |
} | |
charCode = (rawByte >>> numBytes); | |
if (numBytes === 0){ | |
numBytes = 1; | |
} | |
for(var j = 1; j < numBytes; j++){ | |
charCode = ((charCode << 6) | (buffer[i] & 0x3F)); | |
i++; | |
} | |
text += String.fromCharCode(charCode); | |
} | |
return text; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title>User</title> | |
<style>body{font-family:sans-serif;padding:50px;}.is-connected{background-color:green}</style> | |
</head> | |
<body> | |
<h1>USER</h1> | |
<label for="toggle"><input id="toggle" type="checkbox">toggle spacebrew connection</label> | |
<script src="spacebrew-1.4.1.js"></script> | |
<script> | |
/*global Spacebrew*/ | |
var checkboxEl = document.getElementById( 'toggle' ); | |
var sb = new Spacebrew.Client( { server: 'localhost', name: 'User', reconnect: false } ); | |
sb.onOpen = function() { | |
checkboxEl.setAttribute( 'checked', 'checked' ); | |
document.body.classList.add( 'is-connected' ); | |
}; | |
sb.onClose = function() { | |
checkboxEl.removeAttribute( 'checked' ); | |
document.body.classList.remove( 'is-connected' ); | |
}; | |
checkboxEl.addEventListener( 'change', connectionToggled ); | |
function connectionToggled () { | |
if ( checkboxEl.getAttribute( 'checked' ) ) { | |
sb.close(); | |
} else { | |
sb.connect(); | |
} | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment