diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 7514ec8..68f976f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,31 @@
-# socketcluster-client-android
Native Android client for SocketCluster
-This project is working in progress, please read ``MainActivity.java`` for existing usages,
-feedbacks are welcome.
+Please read MainActivity.java for existing usages, feedbacks are welcome.
+
+Project on basis ilani project. Updated to SocketCluster client version 4.3. As for now android is able to handle most of events and methods from scSocket client.
+
+Ive been trying to make it as simplest as i can to handle messages at activity. Refactoring existing code, which was very usefull and most of this client is based on forked code, is necessary to keep this client connected to server even if application is in background. To achieve this android client is working as service.
+
+Implementing this client in application is very simple. Somwhere in app, probably in main activity, implement BroadcastReceiver:
+
+ private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String event = intent.getStringExtra("event");
+ String data = intent.getStringExtra("data");
+ handleEvents(event,data); //this line is optional
+ }
+ };
+And thats it. From this moment you can handle each event sent from server. Of course its also possible to implement BroadcastReceiver as new class.
+
+Benchmark button. I wanted to check how efficient is android client, not very efficient but is dispatching all messages even if its many of them in short time. This is code i used in worker:
+
+ socket.on('benchmark', function(data){
+ for(n=0;n<500;n++){
+ socket.emit('rand', {
+ rand: n
+ });
+ console.log('emit' + n);
+ }
+ });
diff --git a/app/app.iml b/app/app.iml
index c465029..31c9524 100644
--- a/app/app.iml
+++ b/app/app.iml
@@ -1,5 +1,5 @@
-
+
@@ -65,34 +65,27 @@
-
-
-
-
-
+
+
-
-
-
-
-
-
+
+
-
+
-
-
-
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 55256f0..5f2880e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -18,14 +18,15 @@ allprojects {
dependencies {
- compile fileTree(include: ['*.jar'], dir: 'libs')
- compile 'com.android.support:appcompat-v7:22.1.1'
+ compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':socketclusterandroidclient')
+ compile 'com.android.support:appcompat-v7:23.1.1'
+ compile 'com.android.support:support-v4:23.1.1'
}
android {
- compileSdkVersion 21
- buildToolsVersion "22.0.1"
+ compileSdkVersion 23
+ buildToolsVersion '23.0.2'
defaultConfig {
applicationId "socketcluster.io.androiddemo"
minSdkVersion 19
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 60952eb..9f8f960 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,11 +1,15 @@
+
+
-
+
+
+
+
-
+
\ No newline at end of file
diff --git a/app/src/main/java/socketcluster/io/androiddemo/MainActivity.java b/app/src/main/java/socketcluster/io/androiddemo/MainActivity.java
index 558f8f7..e548652 100644
--- a/app/src/main/java/socketcluster/io/androiddemo/MainActivity.java
+++ b/app/src/main/java/socketcluster/io/androiddemo/MainActivity.java
@@ -1,78 +1,196 @@
package socketcluster.io.androiddemo;
+
+/**
+ * Dariusz Krempa
+ */
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
+import android.os.IBinder;
+import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
+import android.widget.TextView;
+
+import com.fangjian.WebViewJavascriptBridge;
+
+import org.json.simple.JSONValue;
-import socketcluster.io.socketclusterandroidclient.ISocketCluster;
-import socketcluster.io.socketclusterandroidclient.SocketCluster;
+import java.util.HashMap;
+import java.util.Map;
+import io.socketcluster.socketclusterandroidclient.SCSocketService;
-public class MainActivity extends AppCompatActivity implements ISocketCluster {
- private SocketCluster sc;
+public class MainActivity extends Activity {
+
private static String TAG = "SCDemo";
+ private SCSocketService scSocket;
+ private Boolean bound = false;
+ private String options;
+ private boolean logout;
+ private TextView textView;
+ private String authToken;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
- sc = new SocketCluster("192.168.199.103", "8000", false, this);
- sc.setDelegate(this);
- // Connect button
- final Button connectBtn = (Button) findViewById(R.id.btnConnect);
- connectBtn.setOnClickListener(new View.OnClickListener() {
+ // Check active subscriptions
+ final Button subsBtn = (Button) findViewById(R.id.activeSubsBtn);
+ textView = (TextView) findViewById(R.id.textView);
+ subsBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ scSocket.subscriptions(true, new WebViewJavascriptBridge.WVJBResponseCallback() {
+ @Override
+ public void callback(String data) {
+ handleEvents("subscriptions", data);
+ }
+ });
+ }
+ });
+ Map map = new HashMap();
+
+ String host = "example.com";
+ String port = "3000";
+ map.put("hostname", host);
+ map.put("port", port);
+ options = JSONValue.toJSONString(map);
+
+ // Get connection state button
+ final Button stateBtn = (Button) findViewById(R.id.stateBtn);
+ stateBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- sc.connect();
+ scSocket.getState(new WebViewJavascriptBridge.WVJBResponseCallback() {
+ @Override
+ public void callback(String data) {
+ handleEvents("getState", data);
+ }
+ });
+ }
+ });
+ // Benchmark
+ final Button benchmark = (Button) findViewById(R.id.benchmark);
+ benchmark.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ scSocket.emitEvent("benchmark", "start");
}
});
// Disconnect button
- final Button disconnectBtn = (Button) findViewById(R.id.btnDisconnect);
+ final Button disconnectBtn = (Button) findViewById(R.id.disconnectBtn);
disconnectBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- sc.disconnect();
+ scSocket.disconnect();
+ logout = true;
}
});
- // Listen to Rand event button handler
- final Button listenToRandBtn = (Button) findViewById(R.id.btnListenRand);
- listenToRandBtn.setOnClickListener(new View.OnClickListener() {
+ // Login
+ final Button loginBtn = (Button) findViewById(R.id.loginBtn);
+ loginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- sc.registerEvent("rand");
+ scSocket.emitEvent("login", "test");
}
});
-
- final Button subToWeatherBtn = (Button) findViewById(R.id.btnSubWeather);
+ // Subscribe to WEATHER channel
+ final Button subToWeatherBtn = (Button) findViewById(R.id.subBtn);
subToWeatherBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
- sc.subscribeToChannel("WEATHER");
+ String channel = "WEATHER";
+ scSocket.subscribe(channel);
}
});
- final Button unSubToWeatherBtn = (Button) findViewById(R.id.btnUnSubWeather);
+ //Unsubscribe WEATHER channel
+ final Button unSubToWeatherBtn = (Button) findViewById(R.id.unsubsBtn);
unSubToWeatherBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
- sc.unsubscribeFromChannel("WEATHER");
+ String channel = "WEATHER";
+ scSocket.unsubscribe(channel);
}
});
-
- final Button pubToWeatherBtn = (Button) findViewById(R.id.btnPubWeather);
+ // Publish to WEATHER channel
+ final Button pubToWeatherBtn = (Button) findViewById(R.id.pubBtn);
pubToWeatherBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
- sc.publishToChannel("WEATHER", "CLOUDY");
+ String msg = "publish to channel";
+ scSocket.publish("WEATHER", msg);
}
});
+ }
+
+ private ServiceConnection conn = new ServiceConnection(){
+
+ @Override
+ public void onServiceConnected(ComponentName component, IBinder binder){
+ SCSocketService.SCSocketBinder scSocketBinder = (SCSocketService.SCSocketBinder) binder;
+ scSocket = scSocketBinder.getBinder();
+ scSocket.setDelegate(MainActivity.this);
+ bound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName component){
+ bound = false;
+ }
+ };
+ /**
+ * Bind service is required to access methods in SCSocketService
+ * startService is required, even if service is bound, to keep SCSocketCluster alive when activity isn't foreground app
+ * this let to stay application connected to server and receive events and subscribed messages
+ */
+ @Override
+ protected void onStart(){
+ super.onStart();
+ Intent intent = new Intent(this, SCSocketService.class);
+ bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ startService(intent);
+ }
+ @Override
+ protected void onStop(){
+ super.onStop();
+ if(bound){
+ unbindService(conn);
+ bound = false;
+ }
}
+
+ @Override
+ protected void onResume(){
+ super.onResume();
+ LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
+ new IntentFilter("io.socketcluster.eventsreceiver"));
+ }
+
+ @Override
+ protected void onPause(){
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
+ super.onPause();
+ }
+
+ @Override
+ protected void onDestroy(){
+ super.onDestroy();
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
@@ -95,42 +213,116 @@ public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
- @Override
- public void socketClusterReceivedEvent(String name, String data) {
- Log.i(TAG, "ReceivedEvent " + name);
- Log.i(TAG, "ReceivedEvent " + data);
- }
+ /**
+ * BroadcastReceiver to receive messages from SCSocketClusterService to handle events
+ * Broadcast receiver can be changed or even implemented at new class but has to be to handle events from socketcluster client
+ */
+ private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
- @Override
- public void socketClusterChannelReceivedEvent(String name, String data) {
- Log.i(TAG, "socketClusterChannelReceivedEvent " + name + " data: " + data);
- }
- @Override
- public void socketClusterDidConnect() {
- Log.i(TAG, "SocketClusterDidConnect");
- }
- @Override
- public void socketClusterDidDisconnect() {
- Log.i(TAG, "socketClusterDidDisconnect");
- }
- @Override
- public void socketClusterOnError(String error) {
- Log.i(TAG, "socketClusterOnError");
- }
- @Override
- public void socketClusterOnKickOut() {
- Log.i(TAG, "socketClusterOnKickOut");
- }
- @Override
- public void socketClusterOnSubscribe() {
- Log.i(TAG, "socketClusterOnSubscribe");
- }
- @Override
- public void socketClusterOnSubscribeFail() {
- Log.i(TAG, "socketClusterOnSubscribeFail");
- }
- @Override
- public void socketClusterOnUnsubscribe() {
- Log.i(TAG, "socketClusterOnUnsubscribe");
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String event = intent.getStringExtra("event");
+ String data = intent.getStringExtra("data");
+ handleEvents(event,data);
+ }
+ };
+
+
+ public void handleEvents(String event, String data) {
+ switch(event){
+
+ default:
+ textView.append(event + ": " + data + "\n");
+ break;
+
+ case SCSocketService.EVENT_ON_READY:
+ scSocket.connect(options);
+ textView.append(event + ": " + data + "\n");
+ Log.d(TAG, "ready");
+ break;
+
+ case SCSocketService.EVENT_ON_CONNECT:
+ scSocket.emitEvent("login", "Test Driver", new WebViewJavascriptBridge.WVJBResponseCallback() {
+ @Override
+ public void callback(String data) {
+ textView.append("callback: "+data+"\n");
+ }
+ });
+
+ scSocket.registerEvent("rand");
+ textView.append(event+": "+data+"\n");
+ Log.d(TAG, "connected: "+data);
+ break;
+
+ case SCSocketService.EVENT_ON_DISCONNECT:
+ if(!logout)
+ scSocket.authenticate(authToken);
+
+ textView.append(event+": "+data+"\n");
+ Log.d(TAG, "disconnected");
+ break;
+
+ case SCSocketService.EVENT_ON_EVENT_MESSAGE:
+ textView.setText(event + ": " + data + "\n");
+
+ Log.d(TAG, "onEvent: "+data);
+ break;
+
+ case SCSocketService.EVENT_ON_SUBSCRIBED_MESSAGE:
+
+ textView.append(event + ": " + data + "\n");
+ Log.d(TAG, "subscribed message: "+data);
+ break;
+
+ case SCSocketService.EVENT_ON_AUTHENTICATE_STATE_CHANGE:
+
+ textView.append(event+": "+data+"\n");
+ Log.d(TAG, "authStateChanged: "+data);
+ break;
+
+ case SCSocketService.EVENT_ON_SUBSCRIBE_STATE_CHANGE:
+
+ textView.append(event+": "+data+"\n");
+ Log.d(TAG, "subscribeStateChanged: "+data);
+ break;
+
+ case SCSocketService.EVENT_ON_ERROR:
+
+ textView.append(event+": "+data+"\n");
+ Log.d(TAG, "error: "+data);
+ break;
+
+ case SCSocketService.EVENT_ON_SUBSCRIBE_FAIL:
+
+ textView.append(event+": "+data+"\n");
+ Log.d(TAG, "subscribeFailed: "+data);
+ break;
+
+ case SCSocketService.EVENT_ON_AUTHENTICATE:
+ authToken = data;
+ textView.append(event+": "+data+"\n");
+ Log.d(TAG, "authenticated: "+ authToken);
+ break;
+
+ case SCSocketService.EVENT_ON_DEAUTHENTICATE:
+
+ textView.append(event+": "+data+"\n");
+ Log.d(TAG, "error: "+data);
+ break;
+
+ case SCSocketService.EVENT_ON_SUBSCRIBE:
+
+ textView.append(event+": "+data+"\n");
+ Log.d(TAG, "error: "+data);
+ break;
+
+ case SCSocketService.EVENT_ON_UNSUBSCRIBE:
+
+ textView.append(event+": "+data+"\n");
+ Log.d(TAG, "error: "+data);
+ break;
+
+ }
}
+
}
diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml
new file mode 100644
index 0000000..2a6a65b
--- /dev/null
+++ b/app/src/main/res/layout-land/activity_main.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-port/activity_main.xml b/app/src/main/res/layout-port/activity_main.xml
new file mode 100644
index 0000000..24cc353
--- /dev/null
+++ b/app/src/main/res/layout-port/activity_main.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index cfcf6c8..0000000
--- a/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,139 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/socketclusterandroidclient/build.gradle b/socketclusterandroidclient/build.gradle
index 347b42d..849983e 100644
--- a/socketclusterandroidclient/build.gradle
+++ b/socketclusterandroidclient/build.gradle
@@ -11,8 +11,8 @@ buildscript {
android {
- compileSdkVersion 19
- buildToolsVersion "22.0.1"
+ compileSdkVersion 23
+ buildToolsVersion '23.0.2'
defaultConfig {
minSdkVersion 19
targetSdkVersion 19
@@ -30,5 +30,6 @@ android {
}
dependencies {
- compile fileTree(include: ['*.jar'], dir: 'libs')
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:support-v4:23.1.1'
}
diff --git a/socketclusterandroidclient/socketclusterandroidclient.iml b/socketclusterandroidclient/socketclusterandroidclient.iml
index 44c678b..7c14401 100644
--- a/socketclusterandroidclient/socketclusterandroidclient.iml
+++ b/socketclusterandroidclient/socketclusterandroidclient.iml
@@ -1,5 +1,5 @@
-
+
@@ -27,7 +27,7 @@
-
+
@@ -65,30 +65,25 @@
+
-
-
-
-
-
-
-
-
-
+
-
+
+
+
\ No newline at end of file
diff --git a/socketclusterandroidclient/src/main/AndroidManifest.xml b/socketclusterandroidclient/src/main/AndroidManifest.xml
index 95e88dd..91e43ac 100644
--- a/socketclusterandroidclient/src/main/AndroidManifest.xml
+++ b/socketclusterandroidclient/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
+ package="io.socketcluster.socketclusterandroidclient">
diff --git a/socketclusterandroidclient/src/main/assets/socketcluster.js b/socketclusterandroidclient/src/main/assets/socketcluster.js
new file mode 100644
index 0000000..693fbbb
--- /dev/null
+++ b/socketclusterandroidclient/src/main/assets/socketcluster.js
@@ -0,0 +1,4540 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.socketCluster = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o maxTimeout) {
+ throw new InvalidArgumentsError('The ' + propertyName +
+ ' value provided exceeded the maximum amount allowed');
+ }
+ };
+
+ verifyDuration('connectTimeout');
+ verifyDuration('ackTimeout');
+ verifyDuration('pingTimeout');
+
+ this._localEvents = {
+ 'connect': 1,
+ 'connectAbort': 1,
+ 'disconnect': 1,
+ 'message': 1,
+ 'error': 1,
+ 'raw': 1,
+ 'fail': 1,
+ 'kickOut': 1,
+ 'subscribe': 1,
+ 'unsubscribe': 1,
+ 'subscribeStateChange': 1,
+ 'authStateChange': 1,
+ 'authenticate': 1,
+ 'deauthenticate': 1,
+ 'removeAuthToken': 1,
+ 'subscribeRequest': 1
+ };
+
+ this._connectAttempts = 0;
+
+ this._emitBuffer = new LinkedList();
+ this._channels = {};
+
+ this.options = opts;
+
+ this._cid = 1;
+
+ this.options.callIdGenerator = function () {
+ return self._callIdGenerator();
+ };
+
+ if (this.options.autoReconnect) {
+ if (this.options.autoReconnectOptions == null) {
+ this.options.autoReconnectOptions = {};
+ }
+
+ // Add properties to the this.options.autoReconnectOptions object.
+ // We assign the reference to a reconnectOptions variable to avoid repetition.
+ var reconnectOptions = this.options.autoReconnectOptions;
+ if (reconnectOptions.initialDelay == null) {
+ reconnectOptions.initialDelay = 10000;
+ }
+ if (reconnectOptions.randomness == null) {
+ reconnectOptions.randomness = 10000;
+ }
+ if (reconnectOptions.multiplier == null) {
+ reconnectOptions.multiplier = 1.5;
+ }
+ if (reconnectOptions.maxDelay == null) {
+ reconnectOptions.maxDelay = 60000;
+ }
+ }
+
+ if (this.options.subscriptionRetryOptions == null) {
+ this.options.subscriptionRetryOptions = {};
+ }
+
+ if (this.options.authEngine) {
+ this.auth = this.options.authEngine;
+ } else {
+ this.auth = new AuthEngine();
+ }
+
+ this.options.path = this.options.path.replace(/\/$/, '') + '/';
+
+ this.options.query = opts.query || {};
+ if (typeof this.options.query == 'string') {
+ this.options.query = querystring.parse(this.options.query);
+ }
+
+ this.connect();
+
+ this._channelEmitter = new SCEmitter();
+
+ if (isBrowser) {
+ var unloadHandler = function () {
+ self.disconnect();
+ };
+
+ if (global.attachEvent) {
+ global.attachEvent('onunload', unloadHandler);
+ } else if (global.addEventListener) {
+ global.addEventListener('beforeunload', unloadHandler, false);
+ }
+ }
+};
+
+SCSocket.prototype = Object.create(SCEmitter.prototype);
+
+SCSocket.CONNECTING = SCSocket.prototype.CONNECTING = SCTransport.prototype.CONNECTING;
+SCSocket.OPEN = SCSocket.prototype.OPEN = SCTransport.prototype.OPEN;
+SCSocket.CLOSED = SCSocket.prototype.CLOSED = SCTransport.prototype.CLOSED;
+
+SCSocket.AUTHENTICATED = SCSocket.prototype.AUTHENTICATED = 'authenticated';
+SCSocket.UNAUTHENTICATED = SCSocket.prototype.UNAUTHENTICATED = 'unauthenticated';
+SCSocket.PENDING = SCSocket.prototype.PENDING = 'pending';
+
+SCSocket.ignoreStatuses = scErrors.socketProtocolIgnoreStatuses;
+SCSocket.errorStatuses = scErrors.socketProtocolErrorStatuses;
+
+SCSocket.prototype._privateEventHandlerMap = {
+ '#publish': function (data) {
+ var isSubscribed = this.isSubscribed(data.channel, true);
+
+ if (isSubscribed) {
+ this._channelEmitter.emit(data.channel, data.data);
+ }
+ },
+ '#kickOut': function (data) {
+ var channelName = data.channel;
+ var channel = this._channels[channelName];
+ if (channel) {
+ SCEmitter.prototype.emit.call(this, 'kickOut', data.message, channelName);
+ channel.emit('kickOut', data.message, channelName);
+ this._triggerChannelUnsubscribe(channel);
+ }
+ },
+ '#setAuthToken': function (data, response) {
+ var self = this;
+
+ if (data) {
+ var triggerAuthenticate = function (err) {
+ if (err) {
+ // This is a non-fatal error, we don't want to close the connection
+ // because of this but we do want to notify the server and throw an error
+ // on the client.
+ response.error(err);
+ self._onSCError(err);
+ } else {
+ self._changeToAuthenticatedState(data.token);
+ response.end();
+ }
+ };
+
+ this.auth.saveToken(this.options.authTokenName, data.token, {}, triggerAuthenticate);
+ } else {
+ response.error(new InvalidMessageError('No token data provided by #setAuthToken event'));
+ }
+ },
+ '#removeAuthToken': function (data, response) {
+ var self = this;
+
+ this.auth.removeToken(this.options.authTokenName, function (err, oldToken) {
+ if (err) {
+ // Non-fatal error - Do not close the connection
+ response.error(err);
+ self._onSCError(err);
+ } else {
+ SCEmitter.prototype.emit.call(self, 'removeAuthToken', oldToken);
+ self._changeToUnauthenticatedState();
+ response.end();
+ }
+ });
+ },
+ '#disconnect': function (data) {
+ this.transport.close(data.code, data.data);
+ }
+};
+
+SCSocket.prototype._callIdGenerator = function () {
+ return this._cid++;
+};
+
+SCSocket.prototype.getState = function () {
+ return this.state;
+};
+
+SCSocket.prototype.getBytesReceived = function () {
+ return this.transport.getBytesReceived();
+};
+
+SCSocket.prototype.deauthenticate = function (callback) {
+ var self = this;
+
+ this.auth.removeToken(this.options.authTokenName, function (err, oldToken) {
+ self.emit('#removeAuthToken');
+ callback && callback(err);
+ if (err) {
+ // Non-fatal error - Do not close the connection
+ self._onSCError(err);
+ } else {
+ SCEmitter.prototype.emit.call(self, 'removeAuthToken', oldToken);
+ self._changeToUnauthenticatedState();
+ }
+ });
+};
+
+SCSocket.prototype.connect = SCSocket.prototype.open = function () {
+ var self = this;
+
+ if (this.state == this.CLOSED) {
+ this.pendingReconnect = false;
+ clearTimeout(this._reconnectTimeoutRef);
+
+ this.state = this.CONNECTING;
+ this._changeToPendingAuthState();
+
+ if (this.transport) {
+ this.transport.off();
+ }
+
+ this.transport = new SCTransport(this.auth, this.options);
+
+ this.transport.on('open', function (status) {
+ self.state = self.OPEN;
+ self._onSCOpen(status);
+ });
+
+ this.transport.on('error', function (err) {
+ self._onSCError(err);
+ });
+
+ this.transport.on('close', function (code, data) {
+ self.state = self.CLOSED;
+ self._onSCClose(code, data);
+ });
+
+ this.transport.on('openAbort', function (code, data) {
+ self.state = self.CLOSED;
+ self._onSCClose(code, data, true);
+ });
+
+ this.transport.on('event', function (event, data, res) {
+ self._onSCEvent(event, data, res);
+ });
+ }
+};
+
+SCSocket.prototype.reconnect = function () {
+ this.disconnect();
+ this.connect();
+};
+
+SCSocket.prototype.disconnect = function (code, data) {
+ code = code || 1000;
+
+ if (this.state == this.OPEN) {
+ var packet = {
+ code: code,
+ data: data
+ };
+ this.transport.emit('#disconnect', packet);
+ this.transport.close(code);
+
+ } else if (this.state == this.CONNECTING) {
+ this.transport.close(code);
+ } else {
+ this.pendingReconnect = false;
+ clearTimeout(this._reconnectTimeoutRef);
+ }
+};
+
+SCSocket.prototype._changeToPendingAuthState = function () {
+ if (this.authState != this.PENDING) {
+ var oldState = this.authState;
+ this.authState = this.PENDING;
+ var stateChangeData = {
+ oldState: oldState,
+ newState: this.authState
+ };
+ SCEmitter.prototype.emit.call(this, 'authStateChange', stateChangeData);
+ }
+};
+
+SCSocket.prototype._changeToUnauthenticatedState = function () {
+ if (this.authState != this.UNAUTHENTICATED) {
+ var oldState = this.authState;
+ this.authState = this.UNAUTHENTICATED;
+ this.signedAuthToken = null;
+ this.authToken = null;
+
+ var stateChangeData = {
+ oldState: oldState,
+ newState: this.authState
+ };
+ SCEmitter.prototype.emit.call(this, 'authStateChange', stateChangeData);
+ if (oldState == this.AUTHENTICATED) {
+ SCEmitter.prototype.emit.call(this, 'deauthenticate');
+ }
+ }
+};
+
+SCSocket.prototype._changeToAuthenticatedState = function (signedAuthToken) {
+ if (this.authState != this.AUTHENTICATED) {
+ var oldState = this.authState;
+ this.authState = this.AUTHENTICATED;
+ this.signedAuthToken = signedAuthToken;
+ this.authToken = this._extractAuthTokenData(signedAuthToken);
+ var stateChangeData = {
+ oldState: oldState,
+ newState: this.authState,
+ signedAuthToken: signedAuthToken,
+ authToken: this.authToken
+ };
+ this.processPendingSubscriptions();
+
+ SCEmitter.prototype.emit.call(this, 'authStateChange', stateChangeData);
+ SCEmitter.prototype.emit.call(this, 'authenticate', signedAuthToken);
+ }
+};
+
+SCSocket.prototype.decodeBase64 = function (encodedString) {
+ var decodedString;
+ if (typeof Buffer == 'undefined') {
+ if (global.atob) {
+ decodedString = global.atob(encodedString);
+ } else {
+ decodedString = base64.decode(encodedString);
+ }
+ } else {
+ var buffer = new Buffer(encodedString, 'base64');
+ decodedString = buffer.toString('utf8');
+ }
+ return decodedString;
+};
+
+SCSocket.prototype.encodeBase64 = function (decodedString) {
+ var encodedString;
+ if (typeof Buffer == 'undefined') {
+ if (global.btoa) {
+ encodedString = global.btoa(decodedString);
+ } else {
+ encodedString = base64.encode(decodedString);
+ }
+ } else {
+ var buffer = new Buffer(decodedString, 'utf8');
+ encodedString = buffer.toString('base64');
+ }
+ return encodedString;
+};
+
+SCSocket.prototype._extractAuthTokenData = function (signedAuthToken) {
+ var tokenParts = (signedAuthToken || '').split('.');
+ var encodedTokenData = tokenParts[1];
+ if (encodedTokenData != null) {
+ var tokenData = encodedTokenData;
+ try {
+ tokenData = this.decodeBase64(tokenData);
+ return JSON.parse(tokenData);
+ } catch (e) {
+ return tokenData;
+ }
+ }
+ return null;
+};
+
+SCSocket.prototype.getAuthToken = function () {
+ return this.authToken;
+};
+
+SCSocket.prototype.getSignedAuthToken = function () {
+ return this.signedAuthToken;
+};
+
+// Perform client-initiated authentication by providing an encrypted token string
+SCSocket.prototype.authenticate = function (signedAuthToken, callback) {
+ var self = this;
+
+ this._changeToPendingAuthState();
+
+ this.emit('#authenticate', signedAuthToken, function (err, authStatus) {
+ if (authStatus && authStatus.authError) {
+ authStatus.authError = scErrors.hydrateError(authStatus.authError);
+ }
+ if (err) {
+ self._changeToUnauthenticatedState();
+ callback && callback(err, authStatus);
+ } else {
+ self.auth.saveToken(self.options.authTokenName, signedAuthToken, {}, function (err) {
+ callback && callback(err, authStatus);
+ if (err) {
+ self._changeToUnauthenticatedState();
+ self._onSCError(err);
+ } else {
+ if (authStatus.isAuthenticated) {
+ self._changeToAuthenticatedState(signedAuthToken);
+ } else {
+ self._changeToUnauthenticatedState();
+ }
+ }
+ });
+ }
+ });
+};
+
+SCSocket.prototype._tryReconnect = function (initialDelay) {
+ var self = this;
+
+ var exponent = this._connectAttempts++;
+ var reconnectOptions = this.options.autoReconnectOptions;
+ var timeout;
+
+ if (initialDelay == null || exponent > 0) {
+ var initialTimeout = Math.round(reconnectOptions.initialDelay + (reconnectOptions.randomness || 0) * Math.random());
+
+ timeout = Math.round(initialTimeout * Math.pow(reconnectOptions.multiplier, exponent));
+ } else {
+ timeout = initialDelay;
+ }
+
+ if (timeout > reconnectOptions.maxDelay) {
+ timeout = reconnectOptions.maxDelay;
+ }
+
+ clearTimeout(this._reconnectTimeoutRef);
+
+ this.pendingReconnect = true;
+ this._reconnectTimeoutRef = setTimeout(function () {
+ self.connect();
+ }, timeout);
+};
+
+SCSocket.prototype._onSCOpen = function (status) {
+ var self = this;
+
+ if (status) {
+ this.id = status.id;
+ this.pingTimeout = status.pingTimeout;
+ this.transport.pingTimeout = this.pingTimeout;
+ if (status.isAuthenticated) {
+ this._changeToAuthenticatedState(status.authToken);
+ } else {
+ this._changeToUnauthenticatedState();
+ }
+ } else {
+ this._changeToUnauthenticatedState();
+ }
+
+ this._connectAttempts = 0;
+ if (this.options.autoProcessSubscriptions) {
+ this.processPendingSubscriptions();
+ } else {
+ this.pendingConnectCallback = true;
+ }
+
+ // If the user invokes the callback while in autoProcessSubscriptions mode, it
+ // won't break anything - The processPendingSubscriptions() call will be a no-op.
+ SCEmitter.prototype.emit.call(this, 'connect', status, function () {
+ self.processPendingSubscriptions();
+ });
+
+ this._flushEmitBuffer();
+};
+
+SCSocket.prototype._onSCError = function (err) {
+ var self = this;
+
+ // Throw error in different stack frame so that error handling
+ // cannot interfere with a reconnect action.
+ setTimeout(function () {
+ if (self.listeners('error').length < 1) {
+ throw err;
+ } else {
+ SCEmitter.prototype.emit.call(self, 'error', err);
+ }
+ }, 0);
+};
+
+SCSocket.prototype._suspendSubscriptions = function () {
+ var channel, newState;
+ for (var channelName in this._channels) {
+ if (this._channels.hasOwnProperty(channelName)) {
+ channel = this._channels[channelName];
+ if (channel.state == channel.SUBSCRIBED ||
+ channel.state == channel.PENDING) {
+
+ newState = channel.PENDING;
+ } else {
+ newState = channel.UNSUBSCRIBED;
+ }
+
+ this._triggerChannelUnsubscribe(channel, newState);
+ }
+ }
+};
+
+SCSocket.prototype._onSCClose = function (code, data, openAbort) {
+ var self = this;
+
+ this.id = null;
+
+ if (this.transport) {
+ this.transport.off();
+ }
+ this.pendingReconnect = false;
+ clearTimeout(this._reconnectTimeoutRef);
+
+ this._changeToPendingAuthState();
+ this._suspendSubscriptions();
+
+ if (openAbort) {
+ SCEmitter.prototype.emit.call(self, 'connectAbort', code, data);
+ } else {
+ SCEmitter.prototype.emit.call(self, 'disconnect', code, data);
+ }
+
+ // Try to reconnect
+ // on server ping timeout (4000)
+ // or on client pong timeout (4001)
+ // or on close without status (1005)
+ // or on handshake failure (4003)
+ // or on socket hung up (1006)
+ if (this.options.autoReconnect) {
+ if (code == 4000 || code == 4001 || code == 1005) {
+ // If there is a ping or pong timeout or socket closes without
+ // status, don't wait before trying to reconnect - These could happen
+ // if the client wakes up after a period of inactivity and in this case we
+ // want to re-establish the connection as soon as possible.
+ this._tryReconnect(0);
+
+ } else if (code != 1000) {
+ this._tryReconnect();
+ }
+ }
+
+ if (!SCSocket.ignoreStatuses[code]) {
+ var err = new SocketProtocolError(SCSocket.errorStatuses[code] || 'Socket connection failed for unknown reasons', code);
+ this._onSCError(err);
+ }
+};
+
+SCSocket.prototype._onSCEvent = function (event, data, res) {
+ var handler = this._privateEventHandlerMap[event];
+ if (handler) {
+ handler.call(this, data, res);
+ } else {
+ SCEmitter.prototype.emit.call(this, event, data, function () {
+ res && res.callback.apply(res, arguments);
+ });
+ }
+};
+
+SCSocket.prototype.parse = function (message) {
+ return this.transport.parse(message);
+};
+
+SCSocket.prototype.stringify = function (object) {
+ return this.transport.stringify(object);
+};
+
+SCSocket.prototype._flushEmitBuffer = function () {
+ var currentNode = this._emitBuffer.head;
+ var nextNode;
+
+ while (currentNode) {
+ nextNode = currentNode.next;
+ var eventObject = currentNode.data;
+ currentNode.detach();
+ this.transport.emitRaw(eventObject);
+ currentNode = nextNode;
+ }
+};
+
+SCSocket.prototype._handleEventAckTimeout = function (eventObject, eventNode) {
+ if (eventNode) {
+ eventNode.detach();
+ }
+ var error = new TimeoutError("Event response for '" + eventObject.event + "' timed out");
+ var callback = eventObject.callback;
+ if (callback) {
+ delete eventObject.callback;
+ callback.call(eventObject, error, eventObject);
+ }
+};
+
+SCSocket.prototype._emit = function (event, data, callback) {
+ var self = this;
+
+ if (this.state == this.CLOSED) {
+ this.connect();
+ }
+ var eventObject = {
+ event: event,
+ data: data,
+ callback: callback
+ };
+
+ var eventNode = new LinkedList.Item();
+ eventNode.data = eventObject;
+
+ eventObject.timeout = setTimeout(function () {
+ self._handleEventAckTimeout(eventObject, eventNode);
+ }, this.ackTimeout);
+
+ this._emitBuffer.append(eventNode);
+
+ if (this.state == this.OPEN) {
+ this._flushEmitBuffer();
+ }
+};
+
+SCSocket.prototype.send = function (data) {
+ this.transport.send(data);
+};
+
+SCSocket.prototype.emit = function (event, data, callback) {
+ if (this._localEvents[event] == null) {
+ this._emit(event, data, callback);
+ } else {
+ SCEmitter.prototype.emit.call(this, event, data);
+ }
+};
+
+SCSocket.prototype.publish = function (channelName, data, callback) {
+ var pubData = {
+ channel: channelName,
+ data: data
+ };
+ this.emit('#publish', pubData, callback);
+};
+
+SCSocket.prototype._triggerChannelSubscribe = function (channel) {
+ var channelName = channel.name;
+
+ if (channel.state != channel.SUBSCRIBED) {
+ var oldState = channel.state;
+ channel.state = channel.SUBSCRIBED;
+
+ var stateChangeData = {
+ channel: channelName,
+ oldState: oldState,
+ newState: channel.state
+ };
+ channel.emit('subscribeStateChange', stateChangeData);
+ channel.emit('subscribe', channelName);
+ SCEmitter.prototype.emit.call(this, 'subscribeStateChange', stateChangeData);
+ SCEmitter.prototype.emit.call(this, 'subscribe', channelName);
+ }
+};
+
+SCSocket.prototype._triggerChannelSubscribeFail = function (err, channel) {
+ var channelName = channel.name;
+ var meetsAuthRequirements = !channel.waitForAuth || this.authState == this.AUTHENTICATED;
+
+ if (channel.state != channel.UNSUBSCRIBED && meetsAuthRequirements) {
+ channel.state = channel.UNSUBSCRIBED;
+
+ channel.emit('subscribeFail', err, channelName);
+ SCEmitter.prototype.emit.call(this, 'subscribeFail', err, channelName);
+ }
+};
+
+// Cancel any pending subscribe callback
+SCSocket.prototype._cancelPendingSubscribeCallback = function (channel) {
+ if (channel._pendingSubscriptionCid != null) {
+ this.transport.cancelPendingResponse(channel._pendingSubscriptionCid);
+ delete channel._pendingSubscriptionCid;
+ }
+};
+
+SCSocket.prototype._trySubscribe = function (channel) {
+ var self = this;
+
+ var meetsAuthRequirements = !channel.waitForAuth || this.authState == this.AUTHENTICATED;
+
+ // We can only ever have one pending subscribe action at any given time on a channel
+ if (this.state == this.OPEN && !this.pendingConnectCallback &&
+ channel._pendingSubscriptionCid == null && meetsAuthRequirements) {
+
+ var options = {
+ noTimeout: true
+ };
+
+ var subscriptionData = {
+ channel: channel.name
+ };
+ if (channel.waitForAuth) {
+ options.waitForAuth = true;
+ subscriptionData.waitForAuth = options.waitForAuth;
+ }
+
+ channel._pendingSubscriptionCid = this.transport.emit(
+ '#subscribe', subscriptionData, options,
+ function (err) {
+ delete channel._pendingSubscriptionCid;
+ if (err) {
+ self._triggerChannelSubscribeFail(err, channel);
+ } else {
+ self._triggerChannelSubscribe(channel);
+ }
+ }
+ );
+ SCEmitter.prototype.emit.call(this, 'subscribeRequest', channel.name);
+ }
+};
+
+SCSocket.prototype.subscribe = function (channelName, options) {
+ var channel = this._channels[channelName];
+
+ if (!channel) {
+ channel = new SCChannel(channelName, this, options);
+ this._channels[channelName] = channel;
+ }
+
+ if (channel.state == channel.UNSUBSCRIBED) {
+ channel.state = channel.PENDING;
+ this._trySubscribe(channel);
+ }
+
+ return channel;
+};
+
+SCSocket.prototype._triggerChannelUnsubscribe = function (channel, newState) {
+ var channelName = channel.name;
+ var oldState = channel.state;
+
+ if (newState) {
+ channel.state = newState;
+ } else {
+ channel.state = channel.UNSUBSCRIBED;
+ }
+ this._cancelPendingSubscribeCallback(channel);
+
+ if (oldState == channel.SUBSCRIBED) {
+ var stateChangeData = {
+ channel: channelName,
+ oldState: oldState,
+ newState: channel.state
+ };
+ channel.emit('subscribeStateChange', stateChangeData);
+ channel.emit('unsubscribe', channelName);
+ SCEmitter.prototype.emit.call(this, 'subscribeStateChange', stateChangeData);
+ SCEmitter.prototype.emit.call(this, 'unsubscribe', channelName);
+ }
+};
+
+SCSocket.prototype._tryUnsubscribe = function (channel) {
+ var self = this;
+
+ if (this.state == this.OPEN) {
+ var options = {
+ noTimeout: true
+ };
+ // If there is a pending subscribe action, cancel the callback
+ this._cancelPendingSubscribeCallback(channel);
+
+ // This operation cannot fail because the TCP protocol guarantees delivery
+ // so long as the connection remains open. If the connection closes,
+ // the server will automatically unsubscribe the socket and thus complete
+ // the operation on the server side.
+ this.transport.emit('#unsubscribe', channel.name, options);
+ }
+};
+
+SCSocket.prototype.unsubscribe = function (channelName) {
+
+ var channel = this._channels[channelName];
+
+ if (channel) {
+ if (channel.state != channel.UNSUBSCRIBED) {
+
+ this._triggerChannelUnsubscribe(channel);
+ this._tryUnsubscribe(channel);
+ }
+ }
+};
+
+SCSocket.prototype.channel = function (channelName, options) {
+ var currentChannel = this._channels[channelName];
+
+ if (!currentChannel) {
+ currentChannel = new SCChannel(channelName, this, options);
+ this._channels[channelName] = currentChannel;
+ }
+ return currentChannel;
+};
+
+SCSocket.prototype.destroyChannel = function (channelName) {
+ var channel = this._channels[channelName];
+ channel.unwatch();
+ channel.unsubscribe();
+ delete this._channels[channelName];
+};
+
+SCSocket.prototype.subscriptions = function (includePending) {
+ var subs = [];
+ var channel, includeChannel;
+ for (var channelName in this._channels) {
+ if (this._channels.hasOwnProperty(channelName)) {
+ channel = this._channels[channelName];
+
+ if (includePending) {
+ includeChannel = channel && (channel.state == channel.SUBSCRIBED ||
+ channel.state == channel.PENDING);
+ } else {
+ includeChannel = channel && channel.state == channel.SUBSCRIBED;
+ }
+
+ if (includeChannel) {
+ subs.push(channelName);
+ }
+ }
+ }
+ return subs;
+};
+
+SCSocket.prototype.isSubscribed = function (channel, includePending) {
+ var channel = this._channels[channel];
+ if (includePending) {
+ return !!channel && (channel.state == channel.SUBSCRIBED ||
+ channel.state == channel.PENDING);
+ }
+ return !!channel && channel.state == channel.SUBSCRIBED;
+};
+
+SCSocket.prototype.processPendingSubscriptions = function () {
+ var self = this;
+
+ this.pendingConnectCallback = false;
+
+ for (var i in this._channels) {
+ if (this._channels.hasOwnProperty(i)) {
+ (function (channel) {
+ if (channel.state == channel.PENDING) {
+ self._trySubscribe(channel);
+ }
+ })(this._channels[i]);
+ }
+ }
+};
+
+SCSocket.prototype.watch = function (channelName, handler) {
+ if (typeof handler != 'function') {
+ throw new InvalidArgumentsError('No handler function was provided');
+ }
+ this._channelEmitter.on(channelName, handler);
+};
+
+SCSocket.prototype.unwatch = function (channelName, handler) {
+ if (handler) {
+ this._channelEmitter.removeListener(channelName, handler);
+ } else {
+ this._channelEmitter.removeAllListeners(channelName);
+ }
+};
+
+SCSocket.prototype.watchers = function (channelName) {
+ return this._channelEmitter.listeners(channelName);
+};
+
+module.exports = SCSocket;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer)
+},{"./auth":2,"./response":3,"./sctransport":6,"base-64":7,"buffer":18,"linked-list":10,"querystring":23,"sc-channel":11,"sc-emitter":12,"sc-errors":14}],5:[function(require,module,exports){
+(function (global){
+var SCSocket = require('./scsocket');
+
+var _connections = {};
+
+function getMultiplexId(options) {
+ var protocolPrefix = options.secure ? 'https://' : 'http://';
+ return protocolPrefix + options.hostname + ':' + options.port + options.path;
+}
+
+function isUrlSecure() {
+ return global.location && location.protocol == 'https:';
+}
+
+function getPort(options, isSecureDefault) {
+ var isSecure = options.secure == null ? isSecureDefault : options.secure;
+ return options.port || (global.location && location.port ? location.port : isSecure ? 443 : 80);
+}
+
+function connect(options) {
+ var self = this;
+
+ options = options || {};
+ var isSecureDefault = isUrlSecure();
+
+ var opts = {
+ port: getPort(options, isSecureDefault),
+ hostname: global.location && location.hostname,
+ path: '/socketcluster/',
+ secure: isSecureDefault,
+ autoReconnect: true,
+ autoProcessSubscriptions: true,
+ connectTimeout: 20000,
+ ackTimeout: 10000,
+ timestampRequests: false,
+ timestampParam: 't',
+ authEngine: null,
+ authTokenName: 'socketCluster.authToken',
+ binaryType: 'arraybuffer',
+ multiplex: true
+ };
+ for (var i in options) {
+ if (options.hasOwnProperty(i)) {
+ opts[i] = options[i];
+ }
+ }
+ var multiplexId = getMultiplexId(opts);
+ if (opts.multiplex === false) {
+ return new SCSocket(opts);
+ }
+ if (_connections[multiplexId]) {
+ _connections[multiplexId].connect();
+ } else {
+ _connections[multiplexId] = new SCSocket(opts);
+ }
+ return _connections[multiplexId];
+}
+
+function destroy(options) {
+ var self = this;
+
+ options = options || {};
+ var isSecureDefault = isUrlSecure();
+
+ var opts = {
+ port: getPort(options, isSecureDefault),
+ hostname: global.location && location.hostname,
+ path: '/socketcluster/',
+ secure: isSecureDefault
+ };
+ for (var i in options) {
+ if (options.hasOwnProperty(i)) {
+ opts[i] = options[i];
+ }
+ }
+ var multiplexId = getMultiplexId(opts);
+ delete _connections[multiplexId];
+}
+
+module.exports = {
+ connect: connect,
+ destroy: destroy
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./scsocket":4}],6:[function(require,module,exports){
+var SCEmitter = require('sc-emitter').SCEmitter;
+var formatter = require('sc-formatter');
+var Response = require('./response').Response;
+var querystring = require('querystring');
+var WebSocket = require('sc-ws');
+
+var scErrors = require('sc-errors');
+var TimeoutError = scErrors.TimeoutError;
+
+
+var SCTransport = function (authEngine, options) {
+ this.state = this.CLOSED;
+ this.auth = authEngine;
+ this.options = options;
+ this.connectTimeout = options.connectTimeout;
+ this.pingTimeout = options.ackTimeout;
+ this.callIdGenerator = options.callIdGenerator;
+
+ this._pingTimeoutTicker = null;
+ this._callbackMap = {};
+
+ this.open();
+};
+
+SCTransport.prototype = Object.create(SCEmitter.prototype);
+
+SCTransport.CONNECTING = SCTransport.prototype.CONNECTING = 'connecting';
+SCTransport.OPEN = SCTransport.prototype.OPEN = 'open';
+SCTransport.CLOSED = SCTransport.prototype.CLOSED = 'closed';
+
+SCTransport.prototype.uri = function () {
+ var query = this.options.query || {};
+ var schema = this.options.secure ? 'wss' : 'ws';
+ var port = '';
+
+ if (this.options.port && (('wss' == schema && this.options.port != 443)
+ || ('ws' == schema && this.options.port != 80))) {
+ port = ':' + this.options.port;
+ }
+
+ if (this.options.timestampRequests) {
+ query[this.options.timestampParam] = (new Date()).getTime();
+ }
+
+ query = querystring.stringify(query);
+
+ if (query.length) {
+ query = '?' + query;
+ }
+
+ return schema + '://' + this.options.hostname + port + this.options.path + query;
+};
+
+SCTransport.prototype.open = function () {
+ var self = this;
+
+ this.state = this.CONNECTING;
+ var uri = this.uri();
+
+ var wsSocket = new WebSocket(uri, null, this.options);
+ wsSocket.binaryType = this.options.binaryType;
+ this.socket = wsSocket;
+
+ wsSocket.onopen = function () {
+ self._onOpen();
+ };
+
+ wsSocket.onclose = function (event) {
+ self._onClose(event.code, event.reason);
+ };
+
+ wsSocket.onmessage = function (message, flags) {
+ self._onMessage(message.data);
+ };
+
+ wsSocket.onerror = function (error) {
+ // The onclose event will be called automatically after the onerror event
+ // if the socket is connected - Otherwise, if it's in the middle of
+ // connecting, we want to close it manually with a 1006 - This is necessary
+ // to prevent inconsistent behavior when running the client in Node.js
+ // vs in a browser.
+
+ if (self.state === self.CONNECTING) {
+ self._onClose(1006);
+ }
+ };
+
+ this._connectTimeoutRef = setTimeout(function () {
+ self._onClose(4007);
+ self.socket.close(4007);
+ }, this.connectTimeout);
+};
+
+SCTransport.prototype._onOpen = function () {
+ var self = this;
+
+ clearTimeout(this._connectTimeoutRef);
+ this._resetPingTimeout();
+
+ this._handshake(function (err, status) {
+ if (err) {
+ self._onError(err);
+ self._onClose(4003);
+ self.socket.close(4003);
+ } else {
+ self.state = self.OPEN;
+ SCEmitter.prototype.emit.call(self, 'open', status);
+ self._resetPingTimeout();
+ }
+ });
+};
+
+SCTransport.prototype._handshake = function (callback) {
+ var self = this;
+ this.auth.loadToken(this.options.authTokenName, function (err, token) {
+ if (err) {
+ callback(err);
+ } else {
+ // Don't wait for this.state to be 'open'.
+ // The underlying WebSocket (this.socket) is already open.
+ var options = {
+ force: true
+ };
+ self.emit('#handshake', {
+ authToken: token
+ }, options, function (err, status) {
+ if (status) {
+ // Add the token which was used as part of authentication attempt
+ // to the status object.
+ status.authToken = token;
+ if (status.authError) {
+ status.authError = scErrors.hydrateError(status.authError);
+ }
+ }
+ callback(err, status);
+ });
+ }
+ });
+};
+
+SCTransport.prototype._onClose = function (code, data) {
+ delete this.socket.onopen;
+ delete this.socket.onclose;
+ delete this.socket.onmessage;
+ delete this.socket.onerror;
+
+ clearTimeout(this._connectTimeoutRef);
+
+ if (this.state == this.OPEN) {
+ this.state = this.CLOSED;
+ SCEmitter.prototype.emit.call(this, 'close', code, data);
+
+ } else if (this.state == this.CONNECTING) {
+ this.state = this.CLOSED;
+ SCEmitter.prototype.emit.call(this, 'openAbort', code, data);
+ }
+};
+
+SCTransport.prototype._onMessage = function (message) {
+ SCEmitter.prototype.emit.call(this, 'event', 'message', message);
+
+ // If ping
+ if (message == '#1') {
+ this._resetPingTimeout();
+ if (this.socket.readyState == this.socket.OPEN) {
+ this.socket.send('#2');
+ }
+ } else {
+ var obj;
+ try {
+ obj = this.parse(message);
+ } catch (err) {
+ obj = message;
+ }
+ var event = obj.event;
+
+ if (event) {
+ var response = new Response(this, obj.cid);
+ SCEmitter.prototype.emit.call(this, 'event', event, obj.data, response);
+ } else if (obj.rid != null) {
+
+ var eventObject = this._callbackMap[obj.rid];
+ if (eventObject) {
+ clearTimeout(eventObject.timeout);
+ delete this._callbackMap[obj.rid];
+
+ if (eventObject.callback) {
+ var rehydratedError = scErrors.hydrateError(obj.error);
+ eventObject.callback(rehydratedError, obj.data);
+ }
+ }
+ } else {
+ SCEmitter.prototype.emit.call(this, 'event', 'raw', obj);
+ }
+ }
+};
+
+SCTransport.prototype._onError = function (err) {
+ SCEmitter.prototype.emit.call(this, 'error', err);
+};
+
+SCTransport.prototype._resetPingTimeout = function () {
+ var self = this;
+
+ var now = (new Date()).getTime();
+ clearTimeout(this._pingTimeoutTicker);
+
+ this._pingTimeoutTicker = setTimeout(function () {
+ self._onClose(4000);
+ self.socket.close(4000);
+ }, this.pingTimeout);
+};
+
+SCTransport.prototype.getBytesReceived = function () {
+ return this.socket.bytesReceived;
+};
+
+SCTransport.prototype.close = function (code, data) {
+ code = code || 1000;
+
+ if (this.state == this.OPEN) {
+ var packet = {
+ code: code,
+ data: data
+ };
+ this.emit('#disconnect', packet);
+
+ this._onClose(code, data);
+ this.socket.close(code);
+
+ } else if (this.state == this.CONNECTING) {
+ this._onClose(code, data);
+ this.socket.close(code);
+ }
+};
+
+SCTransport.prototype.emitRaw = function (eventObject) {
+ eventObject.cid = this.callIdGenerator();
+
+ if (eventObject.callback) {
+ this._callbackMap[eventObject.cid] = eventObject;
+ }
+
+ var simpleEventObject = {
+ event: eventObject.event,
+ data: eventObject.data,
+ cid: eventObject.cid
+ };
+
+ this.sendObject(simpleEventObject);
+ return eventObject.cid;
+};
+
+
+SCTransport.prototype._handleEventAckTimeout = function (eventObject) {
+ var errorMessage = "Event response for '" + eventObject.event + "' timed out";
+ var error = new TimeoutError(errorMessage);
+
+ if (eventObject.cid) {
+ delete this._callbackMap[eventObject.cid];
+ }
+ var callback = eventObject.callback;
+ delete eventObject.callback;
+ callback.call(eventObject, error, eventObject);
+};
+
+// The last two optional arguments (a and b) can be options and/or callback
+SCTransport.prototype.emit = function (event, data, a, b) {
+ var self = this;
+
+ var callback, options;
+
+ if (b) {
+ options = a;
+ callback = b;
+ } else {
+ if (a instanceof Function) {
+ options = {};
+ callback = a;
+ } else {
+ options = a;
+ }
+ }
+
+ var eventObject = {
+ event: event,
+ data: data,
+ callback: callback
+ };
+
+ if (callback && !options.noTimeout) {
+ eventObject.timeout = setTimeout(function () {
+ self._handleEventAckTimeout(eventObject);
+ }, this.options.ackTimeout);
+ }
+
+ var cid = null;
+ if (this.state == this.OPEN || options.force) {
+ cid = this.emitRaw(eventObject);
+ }
+ return cid;
+};
+
+SCTransport.prototype.cancelPendingResponse = function (cid) {
+ delete this._callbackMap[cid];
+};
+
+SCTransport.prototype.parse = function (message) {
+ return formatter.parse(message);
+};
+
+SCTransport.prototype.stringify = function (object) {
+ return formatter.stringify(object);
+};
+
+SCTransport.prototype.send = function (data) {
+ if (this.socket.readyState != this.socket.OPEN) {
+ this._onClose(1005);
+ } else {
+ this.socket.send(data);
+ }
+};
+
+SCTransport.prototype.sendObject = function (object) {
+ var str, formatError;
+ try {
+ str = this.stringify(object);
+ } catch (err) {
+ formatError = err;
+ this._onError(formatError);
+ }
+ if (!formatError) {
+ this.send(str);
+ }
+};
+
+module.exports.SCTransport = SCTransport;
+
+},{"./response":3,"querystring":23,"sc-emitter":12,"sc-errors":14,"sc-formatter":15,"sc-ws":16}],7:[function(require,module,exports){
+(function (global){
+/*! http://mths.be/base64 v0.1.0 by @mathias | MIT license */
+;(function(root) {
+
+ // Detect free variables `exports`.
+ var freeExports = typeof exports == 'object' && exports;
+
+ // Detect free variable `module`.
+ var freeModule = typeof module == 'object' && module &&
+ module.exports == freeExports && module;
+
+ // Detect free variable `global`, from Node.js or Browserified code, and use
+ // it as `root`.
+ var freeGlobal = typeof global == 'object' && global;
+ if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
+ root = freeGlobal;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ var InvalidCharacterError = function(message) {
+ this.message = message;
+ };
+ InvalidCharacterError.prototype = new Error;
+ InvalidCharacterError.prototype.name = 'InvalidCharacterError';
+
+ var error = function(message) {
+ // Note: the error messages used throughout this file match those used by
+ // the native `atob`/`btoa` implementation in Chromium.
+ throw new InvalidCharacterError(message);
+ };
+
+ var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+ // http://whatwg.org/html/common-microsyntaxes.html#space-character
+ var REGEX_SPACE_CHARACTERS = /[\t\n\f\r ]/g;
+
+ // `decode` is designed to be fully compatible with `atob` as described in the
+ // HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob
+ // The optimized base64-decoding algorithm used is based on @atk’s excellent
+ // implementation. https://gist.github.com/atk/1020396
+ var decode = function(input) {
+ input = String(input)
+ .replace(REGEX_SPACE_CHARACTERS, '');
+ var length = input.length;
+ if (length % 4 == 0) {
+ input = input.replace(/==?$/, '');
+ length = input.length;
+ }
+ if (
+ length % 4 == 1 ||
+ // http://whatwg.org/C#alphanumeric-ascii-characters
+ /[^+a-zA-Z0-9/]/.test(input)
+ ) {
+ error(
+ 'Invalid character: the string to be decoded is not correctly encoded.'
+ );
+ }
+ var bitCounter = 0;
+ var bitStorage;
+ var buffer;
+ var output = '';
+ var position = -1;
+ while (++position < length) {
+ buffer = TABLE.indexOf(input.charAt(position));
+ bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;
+ // Unless this is the first of a group of 4 characters…
+ if (bitCounter++ % 4) {
+ // …convert the first 8 bits to a single ASCII character.
+ output += String.fromCharCode(
+ 0xFF & bitStorage >> (-2 * bitCounter & 6)
+ );
+ }
+ }
+ return output;
+ };
+
+ // `encode` is designed to be fully compatible with `btoa` as described in the
+ // HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoa
+ var encode = function(input) {
+ input = String(input);
+ if (/[^\0-\xFF]/.test(input)) {
+ // Note: no need to special-case astral symbols here, as surrogates are
+ // matched, and the input is supposed to only contain ASCII anyway.
+ error(
+ 'The string to be encoded contains characters outside of the ' +
+ 'Latin1 range.'
+ );
+ }
+ var padding = input.length % 3;
+ var output = '';
+ var position = -1;
+ var a;
+ var b;
+ var c;
+ var d;
+ var buffer;
+ // Make sure any padding is handled outside of the loop.
+ var length = input.length - padding;
+
+ while (++position < length) {
+ // Read three bytes, i.e. 24 bits.
+ a = input.charCodeAt(position) << 16;
+ b = input.charCodeAt(++position) << 8;
+ c = input.charCodeAt(++position);
+ buffer = a + b + c;
+ // Turn the 24 bits into four chunks of 6 bits each, and append the
+ // matching character for each of them to the output.
+ output += (
+ TABLE.charAt(buffer >> 18 & 0x3F) +
+ TABLE.charAt(buffer >> 12 & 0x3F) +
+ TABLE.charAt(buffer >> 6 & 0x3F) +
+ TABLE.charAt(buffer & 0x3F)
+ );
+ }
+
+ if (padding == 2) {
+ a = input.charCodeAt(position) << 8;
+ b = input.charCodeAt(++position);
+ buffer = a + b;
+ output += (
+ TABLE.charAt(buffer >> 10) +
+ TABLE.charAt((buffer >> 4) & 0x3F) +
+ TABLE.charAt((buffer << 2) & 0x3F) +
+ '='
+ );
+ } else if (padding == 1) {
+ buffer = input.charCodeAt(position);
+ output += (
+ TABLE.charAt(buffer >> 2) +
+ TABLE.charAt((buffer << 4) & 0x3F) +
+ '=='
+ );
+ }
+
+ return output;
+ };
+
+ var base64 = {
+ 'encode': encode,
+ 'decode': decode,
+ 'version': '0.1.0'
+ };
+
+ // Some AMD build optimizers, like r.js, check for specific condition patterns
+ // like the following:
+ if (
+ typeof define == 'function' &&
+ typeof define.amd == 'object' &&
+ define.amd
+ ) {
+ define(function() {
+ return base64;
+ });
+ } else if (freeExports && !freeExports.nodeType) {
+ if (freeModule) { // in Node.js or RingoJS v0.8.0+
+ freeModule.exports = base64;
+ } else { // in Narwhal or RingoJS v0.7.0-
+ for (var key in base64) {
+ base64.hasOwnProperty(key) && (freeExports[key] = base64[key]);
+ }
+ }
+ } else { // in Rhino or a web browser
+ root.base64 = base64;
+ }
+
+}(this));
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],8:[function(require,module,exports){
+
+/**
+ * Expose `Emitter`.
+ */
+
+module.exports = Emitter;
+
+/**
+ * Initialize a new `Emitter`.
+ *
+ * @api public
+ */
+
+function Emitter(obj) {
+ if (obj) return mixin(obj);
+};
+
+/**
+ * Mixin the emitter properties.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+function mixin(obj) {
+ for (var key in Emitter.prototype) {
+ obj[key] = Emitter.prototype[key];
+ }
+ return obj;
+}
+
+/**
+ * Listen on the given `event` with `fn`.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.on =
+Emitter.prototype.addEventListener = function(event, fn){
+ this._callbacks = this._callbacks || {};
+ (this._callbacks['$' + event] = this._callbacks['$' + event] || [])
+ .push(fn);
+ return this;
+};
+
+/**
+ * Adds an `event` listener that will be invoked a single
+ * time then automatically removed.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.once = function(event, fn){
+ function on() {
+ this.off(event, on);
+ fn.apply(this, arguments);
+ }
+
+ on.fn = fn;
+ this.on(event, on);
+ return this;
+};
+
+/**
+ * Remove the given callback for `event` or all
+ * registered callbacks.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.off =
+Emitter.prototype.removeListener =
+Emitter.prototype.removeAllListeners =
+Emitter.prototype.removeEventListener = function(event, fn){
+ this._callbacks = this._callbacks || {};
+
+ // all
+ if (0 == arguments.length) {
+ this._callbacks = {};
+ return this;
+ }
+
+ // specific event
+ var callbacks = this._callbacks['$' + event];
+ if (!callbacks) return this;
+
+ // remove all handlers
+ if (1 == arguments.length) {
+ delete this._callbacks['$' + event];
+ return this;
+ }
+
+ // remove specific handler
+ var cb;
+ for (var i = 0; i < callbacks.length; i++) {
+ cb = callbacks[i];
+ if (cb === fn || cb.fn === fn) {
+ callbacks.splice(i, 1);
+ break;
+ }
+ }
+ return this;
+};
+
+/**
+ * Emit `event` with the given args.
+ *
+ * @param {String} event
+ * @param {Mixed} ...
+ * @return {Emitter}
+ */
+
+Emitter.prototype.emit = function(event){
+ this._callbacks = this._callbacks || {};
+ var args = [].slice.call(arguments, 1)
+ , callbacks = this._callbacks['$' + event];
+
+ if (callbacks) {
+ callbacks = callbacks.slice(0);
+ for (var i = 0, len = callbacks.length; i < len; ++i) {
+ callbacks[i].apply(this, args);
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Return array of callbacks for `event`.
+ *
+ * @param {String} event
+ * @return {Array}
+ * @api public
+ */
+
+Emitter.prototype.listeners = function(event){
+ this._callbacks = this._callbacks || {};
+ return this._callbacks['$' + event] || [];
+};
+
+/**
+ * Check if this emitter has `event` handlers.
+ *
+ * @param {String} event
+ * @return {Boolean}
+ * @api public
+ */
+
+Emitter.prototype.hasListeners = function(event){
+ return !! this.listeners(event).length;
+};
+
+},{}],9:[function(require,module,exports){
+'use strict';
+
+/**
+ * Constants.
+ */
+
+var errorMessage;
+
+errorMessage = 'An argument without append, prepend, ' +
+ 'or detach methods was given to `List';
+
+/**
+ * Creates a new List: A linked list is a bit like an Array, but
+ * knows nothing about how many items are in it, and knows only about its
+ * first (`head`) and last (`tail`) items. Each item (e.g. `head`, `tail`,
+ * &c.) knows which item comes before or after it (its more like the
+ * implementation of the DOM in JavaScript).
+ * @global
+ * @private
+ * @constructor
+ * @class Represents an instance of List.
+ */
+
+function List(/*items...*/) {
+ if (arguments.length) {
+ return List.from(arguments);
+ }
+}
+
+var ListPrototype;
+
+ListPrototype = List.prototype;
+
+/**
+ * Creates a new list from the arguments (each a list item) passed in.
+ * @name List.of
+ * @param {...ListItem} [items] - Zero or more items to attach.
+ * @returns {list} - A new instance of List.
+ */
+
+List.of = function (/*items...*/) {
+ return List.from.call(this, arguments);
+};
+
+/**
+ * Creates a new list from the given array-like object (each a list item)
+ * passed in.
+ * @name List.from
+ * @param {ListItem[]} [items] - The items to append.
+ * @returns {list} - A new instance of List.
+ */
+List.from = function (items) {
+ var list = new this(), length, iterator, item;
+
+ if (items && (length = items.length)) {
+ iterator = -1;
+
+ while (++iterator < length) {
+ item = items[iterator];
+
+ if (item !== null && item !== undefined) {
+ list.append(item);
+ }
+ }
+ }
+
+ return list;
+};
+
+/**
+ * List#head
+ * Default to `null`.
+ */
+ListPrototype.head = null;
+
+/**
+ * List#tail
+ * Default to `null`.
+ */
+ListPrototype.tail = null;
+
+/**
+ * Returns the list's items as an array. This does *not* detach the items.
+ * @name List#toArray
+ * @returns {ListItem[]} - An array of (still attached) ListItems.
+ */
+ListPrototype.toArray = function () {
+ var item = this.head,
+ result = [];
+
+ while (item) {
+ result.push(item);
+ item = item.next;
+ }
+
+ return result;
+};
+
+/**
+ * Prepends the given item to the list: Item will be the new first item
+ * (`head`).
+ * @name List#prepend
+ * @param {ListItem} item - The item to prepend.
+ * @returns {ListItem} - An instance of ListItem (the given item).
+ */
+ListPrototype.prepend = function (item) {
+ if (!item) {
+ return false;
+ }
+
+ if (!item.append || !item.prepend || !item.detach) {
+ throw new Error(errorMessage + '#prepend`.');
+ }
+
+ var self, head;
+
+ // Cache self.
+ self = this;
+
+ // If self has a first item, defer prepend to the first items prepend
+ // method, and return the result.
+ head = self.head;
+
+ if (head) {
+ return head.prepend(item);
+ }
+
+ // ...otherwise, there is no `head` (or `tail`) item yet.
+
+ // Detach the prependee.
+ item.detach();
+
+ // Set the prependees parent list to reference self.
+ item.list = self;
+
+ // Set self's first item to the prependee, and return the item.
+ self.head = item;
+
+ return item;
+};
+
+/**
+ * Appends the given item to the list: Item will be the new last item (`tail`)
+ * if the list had a first item, and its first item (`head`) otherwise.
+ * @name List#append
+ * @param {ListItem} item - The item to append.
+ * @returns {ListItem} - An instance of ListItem (the given item).
+ */
+
+ListPrototype.append = function (item) {
+ if (!item) {
+ return false;
+ }
+
+ if (!item.append || !item.prepend || !item.detach) {
+ throw new Error(errorMessage + '#append`.');
+ }
+
+ var self, head, tail;
+
+ // Cache self.
+ self = this;
+
+ // If self has a last item, defer appending to the last items append
+ // method, and return the result.
+ tail = self.tail;
+
+ if (tail) {
+ return tail.append(item);
+ }
+
+ // If self has a first item, defer appending to the first items append
+ // method, and return the result.
+ head = self.head;
+
+ if (head) {
+ return head.append(item);
+ }
+
+ // ...otherwise, there is no `tail` or `head` item yet.
+
+ // Detach the appendee.
+ item.detach();
+
+ // Set the appendees parent list to reference self.
+ item.list = self;
+
+ // Set self's first item to the appendee, and return the item.
+ self.head = item;
+
+ return item;
+};
+
+/**
+ * Creates a new ListItem: A linked list item is a bit like DOM node:
+ * It knows only about its "parent" (`list`), the item before it (`prev`),
+ * and the item after it (`next`).
+ * @global
+ * @private
+ * @constructor
+ * @class Represents an instance of ListItem.
+ */
+
+function ListItem() {}
+
+List.Item = ListItem;
+
+var ListItemPrototype = ListItem.prototype;
+
+ListItemPrototype.next = null;
+
+ListItemPrototype.prev = null;
+
+ListItemPrototype.list = null;
+
+/**
+ * Detaches the item operated on from its parent list.
+ * @name ListItem#detach
+ * @returns {ListItem} - The item operated on.
+ */
+ListItemPrototype.detach = function () {
+ // Cache self, the parent list, and the previous and next items.
+ var self = this,
+ list = self.list,
+ prev = self.prev,
+ next = self.next;
+
+ // If the item is already detached, return self.
+ if (!list) {
+ return self;
+ }
+
+ // If self is the last item in the parent list, link the lists last item
+ // to the previous item.
+ if (list.tail === self) {
+ list.tail = prev;
+ }
+
+ // If self is the first item in the parent list, link the lists first item
+ // to the next item.
+ if (list.head === self) {
+ list.head = next;
+ }
+
+ // If both the last and first items in the parent list are the same,
+ // remove the link to the last item.
+ if (list.tail === list.head) {
+ list.tail = null;
+ }
+
+ // If a previous item exists, link its next item to selfs next item.
+ if (prev) {
+ prev.next = next;
+ }
+
+ // If a next item exists, link its previous item to selfs previous item.
+ if (next) {
+ next.prev = prev;
+ }
+
+ // Remove links from self to both the next and previous items, and to the
+ // parent list.
+ self.prev = self.next = self.list = null;
+
+ // Return self.
+ return self;
+};
+
+/**
+ * Prepends the given item *before* the item operated on.
+ * @name ListItem#prepend
+ * @param {ListItem} item - The item to prepend.
+ * @returns {ListItem} - The item operated on, or false when that item is not
+ * attached.
+ */
+ListItemPrototype.prepend = function (item) {
+ if (!item || !item.append || !item.prepend || !item.detach) {
+ throw new Error(errorMessage + 'Item#prepend`.');
+ }
+
+ // Cache self, the parent list, and the previous item.
+ var self = this,
+ list = self.list,
+ prev = self.prev;
+
+ // If self is detached, return false.
+ if (!list) {
+ return false;
+ }
+
+ // Detach the prependee.
+ item.detach();
+
+ // If self has a previous item...
+ if (prev) {
+ // ...link the prependees previous item, to selfs previous item.
+ item.prev = prev;
+
+ // ...link the previous items next item, to self.
+ prev.next = item;
+ }
+
+ // Set the prependees next item to self.
+ item.next = self;
+
+ // Set the prependees parent list to selfs parent list.
+ item.list = list;
+
+ // Set the previous item of self to the prependee.
+ self.prev = item;
+
+ // If self is the first item in the parent list, link the lists first item
+ // to the prependee.
+ if (self === list.head) {
+ list.head = item;
+ }
+
+ // If the the parent list has no last item, link the lists last item to
+ // self.
+ if (!list.tail) {
+ list.tail = self;
+ }
+
+ // Return the prependee.
+ return item;
+};
+
+/**
+ * Appends the given item *after* the item operated on.
+ * @name ListItem#append
+ * @param {ListItem} item - The item to append.
+ * @returns {ListItem} - The item operated on, or false when that item is not
+ * attached.
+ */
+ListItemPrototype.append = function (item) {
+ // If item is falsey, return false.
+ if (!item || !item.append || !item.prepend || !item.detach) {
+ throw new Error(errorMessage + 'Item#append`.');
+ }
+
+ // Cache self, the parent list, and the next item.
+ var self = this,
+ list = self.list,
+ next = self.next;
+
+ // If self is detached, return false.
+ if (!list) {
+ return false;
+ }
+
+ // Detach the appendee.
+ item.detach();
+
+ // If self has a next item...
+ if (next) {
+ // ...link the appendees next item, to selfs next item.
+ item.next = next;
+
+ // ...link the next items previous item, to the appendee.
+ next.prev = item;
+ }
+
+ // Set the appendees previous item to self.
+ item.prev = self;
+
+ // Set the appendees parent list to selfs parent list.
+ item.list = list;
+
+ // Set the next item of self to the appendee.
+ self.next = item;
+
+ // If the the parent list has no last item or if self is the parent lists
+ // last item, link the lists last item to the appendee.
+ if (self === list.tail || !list.tail) {
+ list.tail = item;
+ }
+
+ // Return the appendee.
+ return item;
+};
+
+/**
+ * Expose `List`.
+ */
+
+module.exports = List;
+
+},{}],10:[function(require,module,exports){
+'use strict';
+
+module.exports = require('./_source/linked-list.js');
+
+},{"./_source/linked-list.js":9}],11:[function(require,module,exports){
+var SCEmitter = require('sc-emitter').SCEmitter;
+
+var SCChannel = function (name, client, options) {
+ var self = this;
+
+ SCEmitter.call(this);
+
+ this.PENDING = 'pending';
+ this.SUBSCRIBED = 'subscribed';
+ this.UNSUBSCRIBED = 'unsubscribed';
+
+ this.name = name;
+ this.state = this.UNSUBSCRIBED;
+ this.client = client;
+
+ this.options = options || {};
+ this.waitForAuth = this.options.waitForAuth || false;
+};
+
+SCChannel.prototype = Object.create(SCEmitter.prototype);
+
+SCChannel.prototype.getState = function () {
+ return this.state;
+};
+
+SCChannel.prototype.subscribe = function () {
+ this.client.subscribe(this.name);
+};
+
+SCChannel.prototype.unsubscribe = function () {
+ this.client.unsubscribe(this.name);
+};
+
+SCChannel.prototype.isSubscribed = function (includePending) {
+ return this.client.isSubscribed(this.name, includePending);
+};
+
+SCChannel.prototype.publish = function (data, callback) {
+ this.client.publish(this.name, data, callback);
+};
+
+SCChannel.prototype.watch = function (handler) {
+ this.client.watch(this.name, handler);
+};
+
+SCChannel.prototype.unwatch = function (handler) {
+ this.client.unwatch(this.name, handler);
+};
+
+SCChannel.prototype.watchers = function () {
+ return this.client.watchers(this.name);
+};
+
+SCChannel.prototype.destroy = function () {
+ this.client.destroyChannel(this.name);
+};
+
+module.exports.SCChannel = SCChannel;
+
+},{"sc-emitter":12}],12:[function(require,module,exports){
+var Emitter = require('component-emitter');
+
+if (!Object.create) {
+ Object.create = require('./objectcreate');
+}
+
+var SCEmitter = function () {
+ Emitter.call(this);
+};
+
+SCEmitter.prototype = Object.create(Emitter.prototype);
+
+SCEmitter.prototype.emit = function (event) {
+ if (event == 'error' && this.domain) {
+ // Emit the error on the domain if it has one.
+ // See https://github.com/joyent/node/blob/ef4344311e19a4f73c031508252b21712b22fe8a/lib/events.js#L78-85
+
+ var err = arguments[1];
+
+ if (!err) {
+ err = new Error('Uncaught, unspecified "error" event.');
+ }
+ err.domainEmitter = this;
+ err.domain = this.domain;
+ err.domainThrown = false;
+ this.domain.emit('error', err);
+ }
+ Emitter.prototype.emit.apply(this, arguments);
+};
+
+module.exports.SCEmitter = SCEmitter;
+
+},{"./objectcreate":13,"component-emitter":8}],13:[function(require,module,exports){
+module.exports.create = (function () {
+ function F() {};
+
+ return function (o) {
+ if (arguments.length != 1) {
+ throw new Error('Object.create implementation only accepts one parameter.');
+ }
+ F.prototype = o;
+ return new F();
+ }
+})();
+},{}],14:[function(require,module,exports){
+
+function AuthTokenExpiredError(message, expiry) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'AuthTokenExpiredError';
+ this.message = message;
+ this.expiry = expiry;
+};
+AuthTokenExpiredError.prototype = Object.create(Error.prototype);
+
+
+function AuthTokenInvalidError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'AuthTokenInvalidError';
+ this.message = message;
+};
+AuthTokenInvalidError.prototype = Object.create(Error.prototype);
+
+
+function SilentMiddlewareBlockedError(message, type) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'SilentMiddlewareBlockedError';
+ this.message = message;
+ this.type = type;
+};
+SilentMiddlewareBlockedError.prototype = Object.create(Error.prototype);
+
+
+function InvalidActionError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'InvalidActionError';
+ this.message = message;
+};
+InvalidActionError.prototype = Object.create(Error.prototype);
+
+function InvalidArgumentsError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'InvalidArgumentsError';
+ this.message = message;
+};
+InvalidArgumentsError.prototype = Object.create(Error.prototype);
+
+function InvalidOptionsError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'InvalidOptionsError';
+ this.message = message;
+};
+InvalidOptionsError.prototype = Object.create(Error.prototype);
+
+
+function InvalidMessageError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'InvalidMessageError';
+ this.message = message;
+};
+InvalidMessageError.prototype = Object.create(Error.prototype);
+
+
+function SocketProtocolError(message, code) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'SocketProtocolError';
+ this.message = message;
+ this.code = code;
+};
+SocketProtocolError.prototype = Object.create(Error.prototype);
+
+
+function ServerProtocolError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'ServerProtocolError';
+ this.message = message;
+};
+ServerProtocolError.prototype = Object.create(Error.prototype);
+
+function HTTPServerError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'HTTPServerError';
+ this.message = message;
+};
+HTTPServerError.prototype = Object.create(Error.prototype);
+
+
+function ResourceLimitError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'ResourceLimitError';
+ this.message = message;
+};
+ResourceLimitError.prototype = Object.create(Error.prototype);
+
+
+function TimeoutError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'TimeoutError';
+ this.message = message;
+};
+TimeoutError.prototype = Object.create(Error.prototype);
+
+
+function BrokerError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'BrokerError';
+ this.message = message;
+};
+BrokerError.prototype = Object.create(Error.prototype);
+
+
+function ProcessExitError(message, code) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'ProcessExitError';
+ this.message = message;
+ this.code = code;
+};
+ProcessExitError.prototype = Object.create(Error.prototype);
+
+
+function UnknownError(message) {
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, arguments.callee);
+ } else {
+ this.stack = (new Error()).stack;
+ }
+ this.name = 'UnknownError';
+ this.message = message;
+};
+UnknownError.prototype = Object.create(Error.prototype);
+
+
+// Expose all error types
+
+module.exports = {
+ AuthTokenExpiredError: AuthTokenExpiredError,
+ AuthTokenInvalidError: AuthTokenInvalidError,
+ SilentMiddlewareBlockedError: SilentMiddlewareBlockedError,
+ InvalidActionError: InvalidActionError,
+ InvalidArgumentsError: InvalidArgumentsError,
+ InvalidOptionsError: InvalidOptionsError,
+ InvalidMessageError: InvalidMessageError,
+ SocketProtocolError: SocketProtocolError,
+ ServerProtocolError: ServerProtocolError,
+ HTTPServerError: HTTPServerError,
+ ResourceLimitError: ResourceLimitError,
+ TimeoutError: TimeoutError,
+ BrokerError: BrokerError,
+ ProcessExitError: ProcessExitError,
+ UnknownError: UnknownError
+};
+
+module.exports.socketProtocolErrorStatuses = {
+ 1001: 'Socket was disconnected',
+ 1002: 'A WebSocket protocol error was encountered',
+ 1003: 'Server terminated socket because it received invalid data',
+ 1005: 'Socket closed without status code',
+ 1006: 'Socket hung up',
+ 1007: 'Message format was incorrect',
+ 1008: 'Encountered a policy violation',
+ 1009: 'Message was too big to process',
+ 1010: 'Client ended the connection because the server did not comply with extension requirements',
+ 1011: 'Server encountered an unexpected fatal condition',
+ 4000: 'Server ping timed out',
+ 4001: 'Client pong timed out',
+ 4002: 'Server failed to sign auth token',
+ 4003: 'Failed to complete handshake',
+ 4004: 'Client failed to save auth token',
+ 4005: 'Did not receive #handshake from client before timeout',
+ 4006: 'Failed to bind socket to message broker',
+ 4007: 'Client connection establishment timed out'
+};
+
+module.exports.socketProtocolIgnoreStatuses = {
+ 1000: 'Socket closed normally',
+ 1001: 'Socket hung up'
+};
+
+// Properties related to error domains cannot be serialized.
+var unserializableErrorProperties = {
+ domain: 1,
+ domainEmitter: 1,
+ domainThrown: 1
+};
+
+module.exports.dehydrateError = function (error, includeStackTrace) {
+ var dehydratedError;
+ if (typeof error == 'string') {
+ dehydratedError = error;
+ } else {
+ dehydratedError = {
+ message: error.message
+ };
+ if (includeStackTrace) {
+ dehydratedError.stack = error.stack;
+ }
+ for (var i in error) {
+ if (!unserializableErrorProperties[i]) {
+ dehydratedError[i] = error[i];
+ }
+ }
+ }
+ return dehydratedError;
+};
+
+module.exports.hydrateError = function (error) {
+ var hydratedError = null;
+ if (error != null) {
+ if (typeof error == 'string') {
+ hydratedError = error;
+ } else {
+ hydratedError = new Error(error.message);
+ for (var i in error) {
+ if (error.hasOwnProperty(i)) {
+ hydratedError[i] = error[i];
+ }
+ }
+ }
+ }
+ return hydratedError;
+};
+
+},{}],15:[function(require,module,exports){
+(function (global){
+var base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+module.exports.parse = function (input) {
+ if (input == null) {
+ return null;
+ }
+ var message = input.toString();
+
+ try {
+ return JSON.parse(message);
+ } catch (err) {}
+ return message;
+};
+
+var arrayBufferToBase64 = function (arraybuffer) {
+ var bytes = new Uint8Array(arraybuffer);
+ var len = bytes.length;
+ var base64 = '';
+
+ for (var i = 0; i < len; i += 3) {
+ base64 += base64Chars[bytes[i] >> 2];
+ base64 += base64Chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
+ base64 += base64Chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
+ base64 += base64Chars[bytes[i + 2] & 63];
+ }
+
+ if ((len % 3) === 2) {
+ base64 = base64.substring(0, base64.length - 1) + '=';
+ } else if (len % 3 === 1) {
+ base64 = base64.substring(0, base64.length - 2) + '==';
+ }
+
+ return base64;
+};
+
+var isOwnDescendant = function (object, ancestors) {
+ for (var i in ancestors) {
+ if (ancestors.hasOwnProperty(i)) {
+ if (ancestors[i] === object) {
+ return true;
+ }
+ }
+ }
+ return false;
+};
+
+var convertBuffersToBase64 = function (object, ancestors) {
+ if (!ancestors) {
+ ancestors = [];
+ }
+ if (isOwnDescendant(object, ancestors)) {
+ throw new Error('Cannot traverse circular structure');
+ }
+ var newAncestors = ancestors.concat([object]);
+
+ if (global.ArrayBuffer && object instanceof global.ArrayBuffer) {
+ object = {
+ base64: true,
+ data: arrayBufferToBase64(object)
+ };
+ } else if (global.Buffer && object instanceof global.Buffer) {
+ object = {
+ base64: true,
+ data: object.toString('base64')
+ };
+ } else if (object instanceof Array) {
+ for (var i in object) {
+ if (object.hasOwnProperty(i)) {
+ object[i] = convertBuffersToBase64(object[i], newAncestors);
+ }
+ }
+ } else if (object instanceof Object) {
+ for (var j in object) {
+ if (object.hasOwnProperty(j)) {
+ object[j] = convertBuffersToBase64(object[j], newAncestors);
+ }
+ }
+ }
+ return object;
+};
+
+module.exports.stringify = function (object) {
+ var base64Object = convertBuffersToBase64(object);
+ return JSON.stringify(base64Object);
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],16:[function(require,module,exports){
+
+/**
+ * Module dependencies.
+ */
+
+var global = (function() { return this; })();
+
+/**
+ * WebSocket constructor.
+ */
+
+var WebSocket = global.WebSocket || global.MozWebSocket;
+
+/**
+ * Module exports.
+ */
+
+module.exports = WebSocket ? ws : null;
+
+/**
+ * WebSocket constructor.
+ *
+ * The third `opts` options object gets ignored in web browsers, since it's
+ * non-standard, and throws a TypeError if passed to the constructor.
+ * See: https://github.com/einaros/ws/issues/227
+ *
+ * @param {String} uri
+ * @param {Array} protocols (optional)
+ * @param {Object} opts (optional)
+ * @api public
+ */
+
+function ws(uri, protocols, opts) {
+ var instance;
+ if (protocols) {
+ instance = new WebSocket(uri, protocols);
+ } else {
+ instance = new WebSocket(uri);
+ }
+ return instance;
+}
+
+if (WebSocket) ws.prototype = WebSocket.prototype;
+
+},{}],17:[function(require,module,exports){
+;(function (exports) {
+ 'use strict'
+
+ var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+
+ var Arr = (typeof Uint8Array !== 'undefined')
+ ? Uint8Array
+ : Array
+
+ var PLUS = '+'.charCodeAt(0)
+ var SLASH = '/'.charCodeAt(0)
+ var NUMBER = '0'.charCodeAt(0)
+ var LOWER = 'a'.charCodeAt(0)
+ var UPPER = 'A'.charCodeAt(0)
+ var PLUS_URL_SAFE = '-'.charCodeAt(0)
+ var SLASH_URL_SAFE = '_'.charCodeAt(0)
+
+ function decode (elt) {
+ var code = elt.charCodeAt(0)
+ if (code === PLUS || code === PLUS_URL_SAFE) return 62 // '+'
+ if (code === SLASH || code === SLASH_URL_SAFE) return 63 // '/'
+ if (code < NUMBER) return -1 // no match
+ if (code < NUMBER + 10) return code - NUMBER + 26 + 26
+ if (code < UPPER + 26) return code - UPPER
+ if (code < LOWER + 26) return code - LOWER + 26
+ }
+
+ function b64ToByteArray (b64) {
+ var i, j, l, tmp, placeHolders, arr
+
+ if (b64.length % 4 > 0) {
+ throw new Error('Invalid string. Length must be a multiple of 4')
+ }
+
+ // the number of equal signs (place holders)
+ // if there are two placeholders, than the two characters before it
+ // represent one byte
+ // if there is only one, then the three characters before it represent 2 bytes
+ // this is just a cheap hack to not do indexOf twice
+ var len = b64.length
+ placeHolders = b64.charAt(len - 2) === '=' ? 2 : b64.charAt(len - 1) === '=' ? 1 : 0
+
+ // base64 is 4/3 + up to two characters of the original data
+ arr = new Arr(b64.length * 3 / 4 - placeHolders)
+
+ // if there are placeholders, only get up to the last complete 4 chars
+ l = placeHolders > 0 ? b64.length - 4 : b64.length
+
+ var L = 0
+
+ function push (v) {
+ arr[L++] = v
+ }
+
+ for (i = 0, j = 0; i < l; i += 4, j += 3) {
+ tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
+ push((tmp & 0xFF0000) >> 16)
+ push((tmp & 0xFF00) >> 8)
+ push(tmp & 0xFF)
+ }
+
+ if (placeHolders === 2) {
+ tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
+ push(tmp & 0xFF)
+ } else if (placeHolders === 1) {
+ tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
+ push((tmp >> 8) & 0xFF)
+ push(tmp & 0xFF)
+ }
+
+ return arr
+ }
+
+ function uint8ToBase64 (uint8) {
+ var i
+ var extraBytes = uint8.length % 3 // if we have 1 byte left, pad 2 bytes
+ var output = ''
+ var temp, length
+
+ function encode (num) {
+ return lookup.charAt(num)
+ }
+
+ function tripletToBase64 (num) {
+ return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
+ }
+
+ // go through the array every three bytes, we'll deal with trailing stuff later
+ for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
+ temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
+ output += tripletToBase64(temp)
+ }
+
+ // pad the end with zeros, but make sure to not forget the extra bytes
+ switch (extraBytes) {
+ case 1:
+ temp = uint8[uint8.length - 1]
+ output += encode(temp >> 2)
+ output += encode((temp << 4) & 0x3F)
+ output += '=='
+ break
+ case 2:
+ temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
+ output += encode(temp >> 10)
+ output += encode((temp >> 4) & 0x3F)
+ output += encode((temp << 2) & 0x3F)
+ output += '='
+ break
+ default:
+ break
+ }
+
+ return output
+ }
+
+ exports.toByteArray = b64ToByteArray
+ exports.fromByteArray = uint8ToBase64
+}(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
+
+},{}],18:[function(require,module,exports){
+(function (global){
+/*!
+ * The buffer module from node.js, for the browser.
+ *
+ * @author Feross Aboukhadijeh
+ * @license MIT
+ */
+/* eslint-disable no-proto */
+
+'use strict'
+
+var base64 = require('base64-js')
+var ieee754 = require('ieee754')
+var isArray = require('isarray')
+
+exports.Buffer = Buffer
+exports.SlowBuffer = SlowBuffer
+exports.INSPECT_MAX_BYTES = 50
+Buffer.poolSize = 8192 // not used by this implementation
+
+var rootParent = {}
+
+/**
+ * If `Buffer.TYPED_ARRAY_SUPPORT`:
+ * === true Use Uint8Array implementation (fastest)
+ * === false Use Object implementation (most compatible, even IE6)
+ *
+ * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
+ * Opera 11.6+, iOS 4.2+.
+ *
+ * Due to various browser bugs, sometimes the Object implementation will be used even
+ * when the browser supports typed arrays.
+ *
+ * Note:
+ *
+ * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
+ * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
+ *
+ * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
+ *
+ * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
+ * incorrect length in some situations.
+
+ * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
+ * get the Object implementation, which is slower but behaves correctly.
+ */
+Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
+ ? global.TYPED_ARRAY_SUPPORT
+ : typedArraySupport()
+
+function typedArraySupport () {
+ try {
+ var arr = new Uint8Array(1)
+ arr.foo = function () { return 42 }
+ return arr.foo() === 42 && // typed array instances can be augmented
+ typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
+ arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
+ } catch (e) {
+ return false
+ }
+}
+
+function kMaxLength () {
+ return Buffer.TYPED_ARRAY_SUPPORT
+ ? 0x7fffffff
+ : 0x3fffffff
+}
+
+/**
+ * Class: Buffer
+ * =============
+ *
+ * The Buffer constructor returns instances of `Uint8Array` that are augmented
+ * with function properties for all the node `Buffer` API functions. We use
+ * `Uint8Array` so that square bracket notation works as expected -- it returns
+ * a single octet.
+ *
+ * By augmenting the instances, we can avoid modifying the `Uint8Array`
+ * prototype.
+ */
+function Buffer (arg) {
+ if (!(this instanceof Buffer)) {
+ // Avoid going through an ArgumentsAdaptorTrampoline in the common case.
+ if (arguments.length > 1) return new Buffer(arg, arguments[1])
+ return new Buffer(arg)
+ }
+
+ if (!Buffer.TYPED_ARRAY_SUPPORT) {
+ this.length = 0
+ this.parent = undefined
+ }
+
+ // Common case.
+ if (typeof arg === 'number') {
+ return fromNumber(this, arg)
+ }
+
+ // Slightly less common case.
+ if (typeof arg === 'string') {
+ return fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8')
+ }
+
+ // Unusual.
+ return fromObject(this, arg)
+}
+
+function fromNumber (that, length) {
+ that = allocate(that, length < 0 ? 0 : checked(length) | 0)
+ if (!Buffer.TYPED_ARRAY_SUPPORT) {
+ for (var i = 0; i < length; i++) {
+ that[i] = 0
+ }
+ }
+ return that
+}
+
+function fromString (that, string, encoding) {
+ if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8'
+
+ // Assumption: byteLength() return value is always < kMaxLength.
+ var length = byteLength(string, encoding) | 0
+ that = allocate(that, length)
+
+ that.write(string, encoding)
+ return that
+}
+
+function fromObject (that, object) {
+ if (Buffer.isBuffer(object)) return fromBuffer(that, object)
+
+ if (isArray(object)) return fromArray(that, object)
+
+ if (object == null) {
+ throw new TypeError('must start with number, buffer, array or string')
+ }
+
+ if (typeof ArrayBuffer !== 'undefined') {
+ if (object.buffer instanceof ArrayBuffer) {
+ return fromTypedArray(that, object)
+ }
+ if (object instanceof ArrayBuffer) {
+ return fromArrayBuffer(that, object)
+ }
+ }
+
+ if (object.length) return fromArrayLike(that, object)
+
+ return fromJsonObject(that, object)
+}
+
+function fromBuffer (that, buffer) {
+ var length = checked(buffer.length) | 0
+ that = allocate(that, length)
+ buffer.copy(that, 0, 0, length)
+ return that
+}
+
+function fromArray (that, array) {
+ var length = checked(array.length) | 0
+ that = allocate(that, length)
+ for (var i = 0; i < length; i += 1) {
+ that[i] = array[i] & 255
+ }
+ return that
+}
+
+// Duplicate of fromArray() to keep fromArray() monomorphic.
+function fromTypedArray (that, array) {
+ var length = checked(array.length) | 0
+ that = allocate(that, length)
+ // Truncating the elements is probably not what people expect from typed
+ // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior
+ // of the old Buffer constructor.
+ for (var i = 0; i < length; i += 1) {
+ that[i] = array[i] & 255
+ }
+ return that
+}
+
+function fromArrayBuffer (that, array) {
+ array.byteLength // this throws if `array` is not a valid ArrayBuffer
+
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ // Return an augmented `Uint8Array` instance, for best performance
+ that = new Uint8Array(array)
+ that.__proto__ = Buffer.prototype
+ } else {
+ // Fallback: Return an object instance of the Buffer class
+ that = fromTypedArray(that, new Uint8Array(array))
+ }
+ return that
+}
+
+function fromArrayLike (that, array) {
+ var length = checked(array.length) | 0
+ that = allocate(that, length)
+ for (var i = 0; i < length; i += 1) {
+ that[i] = array[i] & 255
+ }
+ return that
+}
+
+// Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object.
+// Returns a zero-length buffer for inputs that don't conform to the spec.
+function fromJsonObject (that, object) {
+ var array
+ var length = 0
+
+ if (object.type === 'Buffer' && isArray(object.data)) {
+ array = object.data
+ length = checked(array.length) | 0
+ }
+ that = allocate(that, length)
+
+ for (var i = 0; i < length; i += 1) {
+ that[i] = array[i] & 255
+ }
+ return that
+}
+
+if (Buffer.TYPED_ARRAY_SUPPORT) {
+ Buffer.prototype.__proto__ = Uint8Array.prototype
+ Buffer.__proto__ = Uint8Array
+} else {
+ // pre-set for values that may exist in the future
+ Buffer.prototype.length = undefined
+ Buffer.prototype.parent = undefined
+}
+
+function allocate (that, length) {
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ // Return an augmented `Uint8Array` instance, for best performance
+ that = new Uint8Array(length)
+ that.__proto__ = Buffer.prototype
+ } else {
+ // Fallback: Return an object instance of the Buffer class
+ that.length = length
+ }
+
+ var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1
+ if (fromPool) that.parent = rootParent
+
+ return that
+}
+
+function checked (length) {
+ // Note: cannot use `length < kMaxLength` here because that fails when
+ // length is NaN (which is otherwise coerced to zero.)
+ if (length >= kMaxLength()) {
+ throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
+ 'size: 0x' + kMaxLength().toString(16) + ' bytes')
+ }
+ return length | 0
+}
+
+function SlowBuffer (subject, encoding) {
+ if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding)
+
+ var buf = new Buffer(subject, encoding)
+ delete buf.parent
+ return buf
+}
+
+Buffer.isBuffer = function isBuffer (b) {
+ return !!(b != null && b._isBuffer)
+}
+
+Buffer.compare = function compare (a, b) {
+ if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
+ throw new TypeError('Arguments must be Buffers')
+ }
+
+ if (a === b) return 0
+
+ var x = a.length
+ var y = b.length
+
+ var i = 0
+ var len = Math.min(x, y)
+ while (i < len) {
+ if (a[i] !== b[i]) break
+
+ ++i
+ }
+
+ if (i !== len) {
+ x = a[i]
+ y = b[i]
+ }
+
+ if (x < y) return -1
+ if (y < x) return 1
+ return 0
+}
+
+Buffer.isEncoding = function isEncoding (encoding) {
+ switch (String(encoding).toLowerCase()) {
+ case 'hex':
+ case 'utf8':
+ case 'utf-8':
+ case 'ascii':
+ case 'binary':
+ case 'base64':
+ case 'raw':
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return true
+ default:
+ return false
+ }
+}
+
+Buffer.concat = function concat (list, length) {
+ if (!isArray(list)) throw new TypeError('list argument must be an Array of Buffers.')
+
+ if (list.length === 0) {
+ return new Buffer(0)
+ }
+
+ var i
+ if (length === undefined) {
+ length = 0
+ for (i = 0; i < list.length; i++) {
+ length += list[i].length
+ }
+ }
+
+ var buf = new Buffer(length)
+ var pos = 0
+ for (i = 0; i < list.length; i++) {
+ var item = list[i]
+ item.copy(buf, pos)
+ pos += item.length
+ }
+ return buf
+}
+
+function byteLength (string, encoding) {
+ if (typeof string !== 'string') string = '' + string
+
+ var len = string.length
+ if (len === 0) return 0
+
+ // Use a for loop to avoid recursion
+ var loweredCase = false
+ for (;;) {
+ switch (encoding) {
+ case 'ascii':
+ case 'binary':
+ // Deprecated
+ case 'raw':
+ case 'raws':
+ return len
+ case 'utf8':
+ case 'utf-8':
+ return utf8ToBytes(string).length
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return len * 2
+ case 'hex':
+ return len >>> 1
+ case 'base64':
+ return base64ToBytes(string).length
+ default:
+ if (loweredCase) return utf8ToBytes(string).length // assume utf8
+ encoding = ('' + encoding).toLowerCase()
+ loweredCase = true
+ }
+ }
+}
+Buffer.byteLength = byteLength
+
+function slowToString (encoding, start, end) {
+ var loweredCase = false
+
+ start = start | 0
+ end = end === undefined || end === Infinity ? this.length : end | 0
+
+ if (!encoding) encoding = 'utf8'
+ if (start < 0) start = 0
+ if (end > this.length) end = this.length
+ if (end <= start) return ''
+
+ while (true) {
+ switch (encoding) {
+ case 'hex':
+ return hexSlice(this, start, end)
+
+ case 'utf8':
+ case 'utf-8':
+ return utf8Slice(this, start, end)
+
+ case 'ascii':
+ return asciiSlice(this, start, end)
+
+ case 'binary':
+ return binarySlice(this, start, end)
+
+ case 'base64':
+ return base64Slice(this, start, end)
+
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return utf16leSlice(this, start, end)
+
+ default:
+ if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
+ encoding = (encoding + '').toLowerCase()
+ loweredCase = true
+ }
+ }
+}
+
+// Even though this property is private, it shouldn't be removed because it is
+// used by `is-buffer` to detect buffer instances in Safari 5-7.
+Buffer.prototype._isBuffer = true
+
+Buffer.prototype.toString = function toString () {
+ var length = this.length | 0
+ if (length === 0) return ''
+ if (arguments.length === 0) return utf8Slice(this, 0, length)
+ return slowToString.apply(this, arguments)
+}
+
+Buffer.prototype.equals = function equals (b) {
+ if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
+ if (this === b) return true
+ return Buffer.compare(this, b) === 0
+}
+
+Buffer.prototype.inspect = function inspect () {
+ var str = ''
+ var max = exports.INSPECT_MAX_BYTES
+ if (this.length > 0) {
+ str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
+ if (this.length > max) str += ' ... '
+ }
+ return ''
+}
+
+Buffer.prototype.compare = function compare (b) {
+ if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
+ if (this === b) return 0
+ return Buffer.compare(this, b)
+}
+
+Buffer.prototype.indexOf = function indexOf (val, byteOffset) {
+ if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff
+ else if (byteOffset < -0x80000000) byteOffset = -0x80000000
+ byteOffset >>= 0
+
+ if (this.length === 0) return -1
+ if (byteOffset >= this.length) return -1
+
+ // Negative offsets start from the end of the buffer
+ if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0)
+
+ if (typeof val === 'string') {
+ if (val.length === 0) return -1 // special case: looking for empty string always fails
+ return String.prototype.indexOf.call(this, val, byteOffset)
+ }
+ if (Buffer.isBuffer(val)) {
+ return arrayIndexOf(this, val, byteOffset)
+ }
+ if (typeof val === 'number') {
+ if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') {
+ return Uint8Array.prototype.indexOf.call(this, val, byteOffset)
+ }
+ return arrayIndexOf(this, [ val ], byteOffset)
+ }
+
+ function arrayIndexOf (arr, val, byteOffset) {
+ var foundIndex = -1
+ for (var i = 0; byteOffset + i < arr.length; i++) {
+ if (arr[byteOffset + i] === val[foundIndex === -1 ? 0 : i - foundIndex]) {
+ if (foundIndex === -1) foundIndex = i
+ if (i - foundIndex + 1 === val.length) return byteOffset + foundIndex
+ } else {
+ foundIndex = -1
+ }
+ }
+ return -1
+ }
+
+ throw new TypeError('val must be string, number or Buffer')
+}
+
+function hexWrite (buf, string, offset, length) {
+ offset = Number(offset) || 0
+ var remaining = buf.length - offset
+ if (!length) {
+ length = remaining
+ } else {
+ length = Number(length)
+ if (length > remaining) {
+ length = remaining
+ }
+ }
+
+ // must be an even number of digits
+ var strLen = string.length
+ if (strLen % 2 !== 0) throw new Error('Invalid hex string')
+
+ if (length > strLen / 2) {
+ length = strLen / 2
+ }
+ for (var i = 0; i < length; i++) {
+ var parsed = parseInt(string.substr(i * 2, 2), 16)
+ if (isNaN(parsed)) throw new Error('Invalid hex string')
+ buf[offset + i] = parsed
+ }
+ return i
+}
+
+function utf8Write (buf, string, offset, length) {
+ return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
+}
+
+function asciiWrite (buf, string, offset, length) {
+ return blitBuffer(asciiToBytes(string), buf, offset, length)
+}
+
+function binaryWrite (buf, string, offset, length) {
+ return asciiWrite(buf, string, offset, length)
+}
+
+function base64Write (buf, string, offset, length) {
+ return blitBuffer(base64ToBytes(string), buf, offset, length)
+}
+
+function ucs2Write (buf, string, offset, length) {
+ return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
+}
+
+Buffer.prototype.write = function write (string, offset, length, encoding) {
+ // Buffer#write(string)
+ if (offset === undefined) {
+ encoding = 'utf8'
+ length = this.length
+ offset = 0
+ // Buffer#write(string, encoding)
+ } else if (length === undefined && typeof offset === 'string') {
+ encoding = offset
+ length = this.length
+ offset = 0
+ // Buffer#write(string, offset[, length][, encoding])
+ } else if (isFinite(offset)) {
+ offset = offset | 0
+ if (isFinite(length)) {
+ length = length | 0
+ if (encoding === undefined) encoding = 'utf8'
+ } else {
+ encoding = length
+ length = undefined
+ }
+ // legacy write(string, encoding, offset, length) - remove in v0.13
+ } else {
+ var swap = encoding
+ encoding = offset
+ offset = length | 0
+ length = swap
+ }
+
+ var remaining = this.length - offset
+ if (length === undefined || length > remaining) length = remaining
+
+ if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
+ throw new RangeError('attempt to write outside buffer bounds')
+ }
+
+ if (!encoding) encoding = 'utf8'
+
+ var loweredCase = false
+ for (;;) {
+ switch (encoding) {
+ case 'hex':
+ return hexWrite(this, string, offset, length)
+
+ case 'utf8':
+ case 'utf-8':
+ return utf8Write(this, string, offset, length)
+
+ case 'ascii':
+ return asciiWrite(this, string, offset, length)
+
+ case 'binary':
+ return binaryWrite(this, string, offset, length)
+
+ case 'base64':
+ // Warning: maxLength not taken into account in base64Write
+ return base64Write(this, string, offset, length)
+
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return ucs2Write(this, string, offset, length)
+
+ default:
+ if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
+ encoding = ('' + encoding).toLowerCase()
+ loweredCase = true
+ }
+ }
+}
+
+Buffer.prototype.toJSON = function toJSON () {
+ return {
+ type: 'Buffer',
+ data: Array.prototype.slice.call(this._arr || this, 0)
+ }
+}
+
+function base64Slice (buf, start, end) {
+ if (start === 0 && end === buf.length) {
+ return base64.fromByteArray(buf)
+ } else {
+ return base64.fromByteArray(buf.slice(start, end))
+ }
+}
+
+function utf8Slice (buf, start, end) {
+ end = Math.min(buf.length, end)
+ var res = []
+
+ var i = start
+ while (i < end) {
+ var firstByte = buf[i]
+ var codePoint = null
+ var bytesPerSequence = (firstByte > 0xEF) ? 4
+ : (firstByte > 0xDF) ? 3
+ : (firstByte > 0xBF) ? 2
+ : 1
+
+ if (i + bytesPerSequence <= end) {
+ var secondByte, thirdByte, fourthByte, tempCodePoint
+
+ switch (bytesPerSequence) {
+ case 1:
+ if (firstByte < 0x80) {
+ codePoint = firstByte
+ }
+ break
+ case 2:
+ secondByte = buf[i + 1]
+ if ((secondByte & 0xC0) === 0x80) {
+ tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
+ if (tempCodePoint > 0x7F) {
+ codePoint = tempCodePoint
+ }
+ }
+ break
+ case 3:
+ secondByte = buf[i + 1]
+ thirdByte = buf[i + 2]
+ if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
+ tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
+ if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
+ codePoint = tempCodePoint
+ }
+ }
+ break
+ case 4:
+ secondByte = buf[i + 1]
+ thirdByte = buf[i + 2]
+ fourthByte = buf[i + 3]
+ if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
+ tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
+ if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
+ codePoint = tempCodePoint
+ }
+ }
+ }
+ }
+
+ if (codePoint === null) {
+ // we did not generate a valid codePoint so insert a
+ // replacement char (U+FFFD) and advance only 1 byte
+ codePoint = 0xFFFD
+ bytesPerSequence = 1
+ } else if (codePoint > 0xFFFF) {
+ // encode to utf16 (surrogate pair dance)
+ codePoint -= 0x10000
+ res.push(codePoint >>> 10 & 0x3FF | 0xD800)
+ codePoint = 0xDC00 | codePoint & 0x3FF
+ }
+
+ res.push(codePoint)
+ i += bytesPerSequence
+ }
+
+ return decodeCodePointsArray(res)
+}
+
+// Based on http://stackoverflow.com/a/22747272/680742, the browser with
+// the lowest limit is Chrome, with 0x10000 args.
+// We go 1 magnitude less, for safety
+var MAX_ARGUMENTS_LENGTH = 0x1000
+
+function decodeCodePointsArray (codePoints) {
+ var len = codePoints.length
+ if (len <= MAX_ARGUMENTS_LENGTH) {
+ return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
+ }
+
+ // Decode in chunks to avoid "call stack size exceeded".
+ var res = ''
+ var i = 0
+ while (i < len) {
+ res += String.fromCharCode.apply(
+ String,
+ codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
+ )
+ }
+ return res
+}
+
+function asciiSlice (buf, start, end) {
+ var ret = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; i++) {
+ ret += String.fromCharCode(buf[i] & 0x7F)
+ }
+ return ret
+}
+
+function binarySlice (buf, start, end) {
+ var ret = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; i++) {
+ ret += String.fromCharCode(buf[i])
+ }
+ return ret
+}
+
+function hexSlice (buf, start, end) {
+ var len = buf.length
+
+ if (!start || start < 0) start = 0
+ if (!end || end < 0 || end > len) end = len
+
+ var out = ''
+ for (var i = start; i < end; i++) {
+ out += toHex(buf[i])
+ }
+ return out
+}
+
+function utf16leSlice (buf, start, end) {
+ var bytes = buf.slice(start, end)
+ var res = ''
+ for (var i = 0; i < bytes.length; i += 2) {
+ res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
+ }
+ return res
+}
+
+Buffer.prototype.slice = function slice (start, end) {
+ var len = this.length
+ start = ~~start
+ end = end === undefined ? len : ~~end
+
+ if (start < 0) {
+ start += len
+ if (start < 0) start = 0
+ } else if (start > len) {
+ start = len
+ }
+
+ if (end < 0) {
+ end += len
+ if (end < 0) end = 0
+ } else if (end > len) {
+ end = len
+ }
+
+ if (end < start) end = start
+
+ var newBuf
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ newBuf = this.subarray(start, end)
+ newBuf.__proto__ = Buffer.prototype
+ } else {
+ var sliceLen = end - start
+ newBuf = new Buffer(sliceLen, undefined)
+ for (var i = 0; i < sliceLen; i++) {
+ newBuf[i] = this[i + start]
+ }
+ }
+
+ if (newBuf.length) newBuf.parent = this.parent || this
+
+ return newBuf
+}
+
+/*
+ * Need to make sure that buffer isn't trying to write out of bounds.
+ */
+function checkOffset (offset, ext, length) {
+ if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
+ if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
+}
+
+Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
+ offset = offset | 0
+ byteLength = byteLength | 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var val = this[offset]
+ var mul = 1
+ var i = 0
+ while (++i < byteLength && (mul *= 0x100)) {
+ val += this[offset + i] * mul
+ }
+
+ return val
+}
+
+Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
+ offset = offset | 0
+ byteLength = byteLength | 0
+ if (!noAssert) {
+ checkOffset(offset, byteLength, this.length)
+ }
+
+ var val = this[offset + --byteLength]
+ var mul = 1
+ while (byteLength > 0 && (mul *= 0x100)) {
+ val += this[offset + --byteLength] * mul
+ }
+
+ return val
+}
+
+Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 1, this.length)
+ return this[offset]
+}
+
+Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ return this[offset] | (this[offset + 1] << 8)
+}
+
+Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ return (this[offset] << 8) | this[offset + 1]
+}
+
+Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return ((this[offset]) |
+ (this[offset + 1] << 8) |
+ (this[offset + 2] << 16)) +
+ (this[offset + 3] * 0x1000000)
+}
+
+Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset] * 0x1000000) +
+ ((this[offset + 1] << 16) |
+ (this[offset + 2] << 8) |
+ this[offset + 3])
+}
+
+Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
+ offset = offset | 0
+ byteLength = byteLength | 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var val = this[offset]
+ var mul = 1
+ var i = 0
+ while (++i < byteLength && (mul *= 0x100)) {
+ val += this[offset + i] * mul
+ }
+ mul *= 0x80
+
+ if (val >= mul) val -= Math.pow(2, 8 * byteLength)
+
+ return val
+}
+
+Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
+ offset = offset | 0
+ byteLength = byteLength | 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var i = byteLength
+ var mul = 1
+ var val = this[offset + --i]
+ while (i > 0 && (mul *= 0x100)) {
+ val += this[offset + --i] * mul
+ }
+ mul *= 0x80
+
+ if (val >= mul) val -= Math.pow(2, 8 * byteLength)
+
+ return val
+}
+
+Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 1, this.length)
+ if (!(this[offset] & 0x80)) return (this[offset])
+ return ((0xff - this[offset] + 1) * -1)
+}
+
+Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ var val = this[offset] | (this[offset + 1] << 8)
+ return (val & 0x8000) ? val | 0xFFFF0000 : val
+}
+
+Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ var val = this[offset + 1] | (this[offset] << 8)
+ return (val & 0x8000) ? val | 0xFFFF0000 : val
+}
+
+Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset]) |
+ (this[offset + 1] << 8) |
+ (this[offset + 2] << 16) |
+ (this[offset + 3] << 24)
+}
+
+Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset] << 24) |
+ (this[offset + 1] << 16) |
+ (this[offset + 2] << 8) |
+ (this[offset + 3])
+}
+
+Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+ return ieee754.read(this, offset, true, 23, 4)
+}
+
+Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+ return ieee754.read(this, offset, false, 23, 4)
+}
+
+Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 8, this.length)
+ return ieee754.read(this, offset, true, 52, 8)
+}
+
+Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 8, this.length)
+ return ieee754.read(this, offset, false, 52, 8)
+}
+
+function checkInt (buf, value, offset, ext, max, min) {
+ if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance')
+ if (value > max || value < min) throw new RangeError('value is out of bounds')
+ if (offset + ext > buf.length) throw new RangeError('index out of range')
+}
+
+Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset | 0
+ byteLength = byteLength | 0
+ if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0)
+
+ var mul = 1
+ var i = 0
+ this[offset] = value & 0xFF
+ while (++i < byteLength && (mul *= 0x100)) {
+ this[offset + i] = (value / mul) & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset | 0
+ byteLength = byteLength | 0
+ if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0)
+
+ var i = byteLength - 1
+ var mul = 1
+ this[offset + i] = value & 0xFF
+ while (--i >= 0 && (mul *= 0x100)) {
+ this[offset + i] = (value / mul) & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
+ if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
+ this[offset] = (value & 0xff)
+ return offset + 1
+}
+
+function objectWriteUInt16 (buf, value, offset, littleEndian) {
+ if (value < 0) value = 0xffff + value + 1
+ for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) {
+ buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
+ (littleEndian ? i : 1 - i) * 8
+ }
+}
+
+Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value & 0xff)
+ this[offset + 1] = (value >>> 8)
+ } else {
+ objectWriteUInt16(this, value, offset, true)
+ }
+ return offset + 2
+}
+
+Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value >>> 8)
+ this[offset + 1] = (value & 0xff)
+ } else {
+ objectWriteUInt16(this, value, offset, false)
+ }
+ return offset + 2
+}
+
+function objectWriteUInt32 (buf, value, offset, littleEndian) {
+ if (value < 0) value = 0xffffffff + value + 1
+ for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) {
+ buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
+ }
+}
+
+Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset + 3] = (value >>> 24)
+ this[offset + 2] = (value >>> 16)
+ this[offset + 1] = (value >>> 8)
+ this[offset] = (value & 0xff)
+ } else {
+ objectWriteUInt32(this, value, offset, true)
+ }
+ return offset + 4
+}
+
+Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value >>> 24)
+ this[offset + 1] = (value >>> 16)
+ this[offset + 2] = (value >>> 8)
+ this[offset + 3] = (value & 0xff)
+ } else {
+ objectWriteUInt32(this, value, offset, false)
+ }
+ return offset + 4
+}
+
+Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) {
+ var limit = Math.pow(2, 8 * byteLength - 1)
+
+ checkInt(this, value, offset, byteLength, limit - 1, -limit)
+ }
+
+ var i = 0
+ var mul = 1
+ var sub = value < 0 ? 1 : 0
+ this[offset] = value & 0xFF
+ while (++i < byteLength && (mul *= 0x100)) {
+ this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) {
+ var limit = Math.pow(2, 8 * byteLength - 1)
+
+ checkInt(this, value, offset, byteLength, limit - 1, -limit)
+ }
+
+ var i = byteLength - 1
+ var mul = 1
+ var sub = value < 0 ? 1 : 0
+ this[offset + i] = value & 0xFF
+ while (--i >= 0 && (mul *= 0x100)) {
+ this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
+ if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
+ if (value < 0) value = 0xff + value + 1
+ this[offset] = (value & 0xff)
+ return offset + 1
+}
+
+Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value & 0xff)
+ this[offset + 1] = (value >>> 8)
+ } else {
+ objectWriteUInt16(this, value, offset, true)
+ }
+ return offset + 2
+}
+
+Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value >>> 8)
+ this[offset + 1] = (value & 0xff)
+ } else {
+ objectWriteUInt16(this, value, offset, false)
+ }
+ return offset + 2
+}
+
+Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value & 0xff)
+ this[offset + 1] = (value >>> 8)
+ this[offset + 2] = (value >>> 16)
+ this[offset + 3] = (value >>> 24)
+ } else {
+ objectWriteUInt32(this, value, offset, true)
+ }
+ return offset + 4
+}
+
+Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
+ value = +value
+ offset = offset | 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
+ if (value < 0) value = 0xffffffff + value + 1
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value >>> 24)
+ this[offset + 1] = (value >>> 16)
+ this[offset + 2] = (value >>> 8)
+ this[offset + 3] = (value & 0xff)
+ } else {
+ objectWriteUInt32(this, value, offset, false)
+ }
+ return offset + 4
+}
+
+function checkIEEE754 (buf, value, offset, ext, max, min) {
+ if (value > max || value < min) throw new RangeError('value is out of bounds')
+ if (offset + ext > buf.length) throw new RangeError('index out of range')
+ if (offset < 0) throw new RangeError('index out of range')
+}
+
+function writeFloat (buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
+ }
+ ieee754.write(buf, value, offset, littleEndian, 23, 4)
+ return offset + 4
+}
+
+Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
+ return writeFloat(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
+ return writeFloat(this, value, offset, false, noAssert)
+}
+
+function writeDouble (buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
+ }
+ ieee754.write(buf, value, offset, littleEndian, 52, 8)
+ return offset + 8
+}
+
+Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
+ return writeDouble(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
+ return writeDouble(this, value, offset, false, noAssert)
+}
+
+// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
+Buffer.prototype.copy = function copy (target, targetStart, start, end) {
+ if (!start) start = 0
+ if (!end && end !== 0) end = this.length
+ if (targetStart >= target.length) targetStart = target.length
+ if (!targetStart) targetStart = 0
+ if (end > 0 && end < start) end = start
+
+ // Copy 0 bytes; we're done
+ if (end === start) return 0
+ if (target.length === 0 || this.length === 0) return 0
+
+ // Fatal error conditions
+ if (targetStart < 0) {
+ throw new RangeError('targetStart out of bounds')
+ }
+ if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
+ if (end < 0) throw new RangeError('sourceEnd out of bounds')
+
+ // Are we oob?
+ if (end > this.length) end = this.length
+ if (target.length - targetStart < end - start) {
+ end = target.length - targetStart + start
+ }
+
+ var len = end - start
+ var i
+
+ if (this === target && start < targetStart && targetStart < end) {
+ // descending copy from end
+ for (i = len - 1; i >= 0; i--) {
+ target[i + targetStart] = this[i + start]
+ }
+ } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
+ // ascending copy from start
+ for (i = 0; i < len; i++) {
+ target[i + targetStart] = this[i + start]
+ }
+ } else {
+ Uint8Array.prototype.set.call(
+ target,
+ this.subarray(start, start + len),
+ targetStart
+ )
+ }
+
+ return len
+}
+
+// fill(value, start=0, end=buffer.length)
+Buffer.prototype.fill = function fill (value, start, end) {
+ if (!value) value = 0
+ if (!start) start = 0
+ if (!end) end = this.length
+
+ if (end < start) throw new RangeError('end < start')
+
+ // Fill 0 bytes; we're done
+ if (end === start) return
+ if (this.length === 0) return
+
+ if (start < 0 || start >= this.length) throw new RangeError('start out of bounds')
+ if (end < 0 || end > this.length) throw new RangeError('end out of bounds')
+
+ var i
+ if (typeof value === 'number') {
+ for (i = start; i < end; i++) {
+ this[i] = value
+ }
+ } else {
+ var bytes = utf8ToBytes(value.toString())
+ var len = bytes.length
+ for (i = start; i < end; i++) {
+ this[i] = bytes[i % len]
+ }
+ }
+
+ return this
+}
+
+// HELPER FUNCTIONS
+// ================
+
+var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g
+
+function base64clean (str) {
+ // Node strips out invalid characters like \n and \t from the string, base64-js does not
+ str = stringtrim(str).replace(INVALID_BASE64_RE, '')
+ // Node converts strings with length < 2 to ''
+ if (str.length < 2) return ''
+ // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
+ while (str.length % 4 !== 0) {
+ str = str + '='
+ }
+ return str
+}
+
+function stringtrim (str) {
+ if (str.trim) return str.trim()
+ return str.replace(/^\s+|\s+$/g, '')
+}
+
+function toHex (n) {
+ if (n < 16) return '0' + n.toString(16)
+ return n.toString(16)
+}
+
+function utf8ToBytes (string, units) {
+ units = units || Infinity
+ var codePoint
+ var length = string.length
+ var leadSurrogate = null
+ var bytes = []
+
+ for (var i = 0; i < length; i++) {
+ codePoint = string.charCodeAt(i)
+
+ // is surrogate component
+ if (codePoint > 0xD7FF && codePoint < 0xE000) {
+ // last char was a lead
+ if (!leadSurrogate) {
+ // no lead yet
+ if (codePoint > 0xDBFF) {
+ // unexpected trail
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ continue
+ } else if (i + 1 === length) {
+ // unpaired lead
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ continue
+ }
+
+ // valid lead
+ leadSurrogate = codePoint
+
+ continue
+ }
+
+ // 2 leads in a row
+ if (codePoint < 0xDC00) {
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ leadSurrogate = codePoint
+ continue
+ }
+
+ // valid surrogate pair
+ codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
+ } else if (leadSurrogate) {
+ // valid bmp char, but last char was a lead
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ }
+
+ leadSurrogate = null
+
+ // encode utf8
+ if (codePoint < 0x80) {
+ if ((units -= 1) < 0) break
+ bytes.push(codePoint)
+ } else if (codePoint < 0x800) {
+ if ((units -= 2) < 0) break
+ bytes.push(
+ codePoint >> 0x6 | 0xC0,
+ codePoint & 0x3F | 0x80
+ )
+ } else if (codePoint < 0x10000) {
+ if ((units -= 3) < 0) break
+ bytes.push(
+ codePoint >> 0xC | 0xE0,
+ codePoint >> 0x6 & 0x3F | 0x80,
+ codePoint & 0x3F | 0x80
+ )
+ } else if (codePoint < 0x110000) {
+ if ((units -= 4) < 0) break
+ bytes.push(
+ codePoint >> 0x12 | 0xF0,
+ codePoint >> 0xC & 0x3F | 0x80,
+ codePoint >> 0x6 & 0x3F | 0x80,
+ codePoint & 0x3F | 0x80
+ )
+ } else {
+ throw new Error('Invalid code point')
+ }
+ }
+
+ return bytes
+}
+
+function asciiToBytes (str) {
+ var byteArray = []
+ for (var i = 0; i < str.length; i++) {
+ // Node's code seems to be doing this and not & 0x7F..
+ byteArray.push(str.charCodeAt(i) & 0xFF)
+ }
+ return byteArray
+}
+
+function utf16leToBytes (str, units) {
+ var c, hi, lo
+ var byteArray = []
+ for (var i = 0; i < str.length; i++) {
+ if ((units -= 2) < 0) break
+
+ c = str.charCodeAt(i)
+ hi = c >> 8
+ lo = c % 256
+ byteArray.push(lo)
+ byteArray.push(hi)
+ }
+
+ return byteArray
+}
+
+function base64ToBytes (str) {
+ return base64.toByteArray(base64clean(str))
+}
+
+function blitBuffer (src, dst, offset, length) {
+ for (var i = 0; i < length; i++) {
+ if ((i + offset >= dst.length) || (i >= src.length)) break
+ dst[i + offset] = src[i]
+ }
+ return i
+}
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"base64-js":17,"ieee754":20,"isarray":19}],19:[function(require,module,exports){
+var toString = {}.toString;
+
+module.exports = Array.isArray || function (arr) {
+ return toString.call(arr) == '[object Array]';
+};
+
+},{}],20:[function(require,module,exports){
+exports.read = function (buffer, offset, isLE, mLen, nBytes) {
+ var e, m
+ var eLen = nBytes * 8 - mLen - 1
+ var eMax = (1 << eLen) - 1
+ var eBias = eMax >> 1
+ var nBits = -7
+ var i = isLE ? (nBytes - 1) : 0
+ var d = isLE ? -1 : 1
+ var s = buffer[offset + i]
+
+ i += d
+
+ e = s & ((1 << (-nBits)) - 1)
+ s >>= (-nBits)
+ nBits += eLen
+ for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
+
+ m = e & ((1 << (-nBits)) - 1)
+ e >>= (-nBits)
+ nBits += mLen
+ for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
+
+ if (e === 0) {
+ e = 1 - eBias
+ } else if (e === eMax) {
+ return m ? NaN : ((s ? -1 : 1) * Infinity)
+ } else {
+ m = m + Math.pow(2, mLen)
+ e = e - eBias
+ }
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
+}
+
+exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
+ var e, m, c
+ var eLen = nBytes * 8 - mLen - 1
+ var eMax = (1 << eLen) - 1
+ var eBias = eMax >> 1
+ var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
+ var i = isLE ? 0 : (nBytes - 1)
+ var d = isLE ? 1 : -1
+ var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
+
+ value = Math.abs(value)
+
+ if (isNaN(value) || value === Infinity) {
+ m = isNaN(value) ? 1 : 0
+ e = eMax
+ } else {
+ e = Math.floor(Math.log(value) / Math.LN2)
+ if (value * (c = Math.pow(2, -e)) < 1) {
+ e--
+ c *= 2
+ }
+ if (e + eBias >= 1) {
+ value += rt / c
+ } else {
+ value += rt * Math.pow(2, 1 - eBias)
+ }
+ if (value * c >= 2) {
+ e++
+ c /= 2
+ }
+
+ if (e + eBias >= eMax) {
+ m = 0
+ e = eMax
+ } else if (e + eBias >= 1) {
+ m = (value * c - 1) * Math.pow(2, mLen)
+ e = e + eBias
+ } else {
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
+ e = 0
+ }
+ }
+
+ for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
+
+ e = (e << mLen) | m
+ eLen += mLen
+ for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
+
+ buffer[offset + i - d] |= s * 128
+}
+
+},{}],21:[function(require,module,exports){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+'use strict';
+
+// If obj.hasOwnProperty has been overridden, then calling
+// obj.hasOwnProperty(prop) will break.
+// See: https://github.com/joyent/node/issues/1707
+function hasOwnProperty(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+}
+
+module.exports = function(qs, sep, eq, options) {
+ sep = sep || '&';
+ eq = eq || '=';
+ var obj = {};
+
+ if (typeof qs !== 'string' || qs.length === 0) {
+ return obj;
+ }
+
+ var regexp = /\+/g;
+ qs = qs.split(sep);
+
+ var maxKeys = 1000;
+ if (options && typeof options.maxKeys === 'number') {
+ maxKeys = options.maxKeys;
+ }
+
+ var len = qs.length;
+ // maxKeys <= 0 means that we should not limit keys count
+ if (maxKeys > 0 && len > maxKeys) {
+ len = maxKeys;
+ }
+
+ for (var i = 0; i < len; ++i) {
+ var x = qs[i].replace(regexp, '%20'),
+ idx = x.indexOf(eq),
+ kstr, vstr, k, v;
+
+ if (idx >= 0) {
+ kstr = x.substr(0, idx);
+ vstr = x.substr(idx + 1);
+ } else {
+ kstr = x;
+ vstr = '';
+ }
+
+ k = decodeURIComponent(kstr);
+ v = decodeURIComponent(vstr);
+
+ if (!hasOwnProperty(obj, k)) {
+ obj[k] = v;
+ } else if (isArray(obj[k])) {
+ obj[k].push(v);
+ } else {
+ obj[k] = [obj[k], v];
+ }
+ }
+
+ return obj;
+};
+
+var isArray = Array.isArray || function (xs) {
+ return Object.prototype.toString.call(xs) === '[object Array]';
+};
+
+},{}],22:[function(require,module,exports){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+'use strict';
+
+var stringifyPrimitive = function(v) {
+ switch (typeof v) {
+ case 'string':
+ return v;
+
+ case 'boolean':
+ return v ? 'true' : 'false';
+
+ case 'number':
+ return isFinite(v) ? v : '';
+
+ default:
+ return '';
+ }
+};
+
+module.exports = function(obj, sep, eq, name) {
+ sep = sep || '&';
+ eq = eq || '=';
+ if (obj === null) {
+ obj = undefined;
+ }
+
+ if (typeof obj === 'object') {
+ return map(objectKeys(obj), function(k) {
+ var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
+ if (isArray(obj[k])) {
+ return map(obj[k], function(v) {
+ return ks + encodeURIComponent(stringifyPrimitive(v));
+ }).join(sep);
+ } else {
+ return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
+ }
+ }).join(sep);
+
+ }
+
+ if (!name) return '';
+ return encodeURIComponent(stringifyPrimitive(name)) + eq +
+ encodeURIComponent(stringifyPrimitive(obj));
+};
+
+var isArray = Array.isArray || function (xs) {
+ return Object.prototype.toString.call(xs) === '[object Array]';
+};
+
+function map (xs, f) {
+ if (xs.map) return xs.map(f);
+ var res = [];
+ for (var i = 0; i < xs.length; i++) {
+ res.push(f(xs[i], i));
+ }
+ return res;
+}
+
+var objectKeys = Object.keys || function (obj) {
+ var res = [];
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
+ }
+ return res;
+};
+
+},{}],23:[function(require,module,exports){
+'use strict';
+
+exports.decode = exports.parse = require('./decode');
+exports.encode = exports.stringify = require('./encode');
+
+},{"./decode":21,"./encode":22}]},{},[1])(1)
+});
\ No newline at end of file
diff --git a/socketclusterandroidclient/src/main/assets/socketcluster_bridge.js b/socketclusterandroidclient/src/main/assets/socketcluster_bridge.js
index 3fdc7c5..3bde51b 100644
--- a/socketclusterandroidclient/src/main/assets/socketcluster_bridge.js
+++ b/socketclusterandroidclient/src/main/assets/socketcluster_bridge.js
@@ -21,13 +21,12 @@ function getSocketId() {
connectWebViewJavascriptBridge(function(bridge) {
+ /**
+ * scSocket.connect handler
+ */
bridge.registerHandler("connectHandler", function(data) {
var data = JSON.parse(data);
- scProxy = socketCluster.connect({
- hostname: data.hostname,
- secure: data.secure === "true" ? true : false,
- port: data.port
- });
+ scProxy = socketCluster.connect(data);
scProxy.removeAllListeners('error');
scProxy.on('error', function(err) {
@@ -36,8 +35,8 @@ connectWebViewJavascriptBridge(function(bridge) {
});
});
scProxy.removeAllListeners('connect');
- scProxy.on('connect', function() {
- var jsonText = JSON.stringify({});
+ scProxy.on('connect', function(data) {
+ var jsonText = JSON.stringify({'data':data});
bridge.callHandler('onConnectHandler', jsonText, function(response) {
});
});
@@ -70,17 +69,47 @@ connectWebViewJavascriptBridge(function(bridge) {
bridge.callHandler('onUnsubscribeHandler', JSON.stringify({'data': data}), function(response) {
});
});
+
+ scProxy.removeAllListeners('authenticate');
+ scProxy.on('authenticate', function(data) {
+ bridge.callHandler('onAuthenticateHandler', JSON.stringify({'data':data}), function(response) {
+ });
+ });
+
+ scProxy.removeAllListeners('deauthenticate');
+ scProxy.on('deauthenticate', function(){
+ bridge.callHandler('onDeauthenticateHandler', JSON.stringify({}), function(response) {
+ });
+ });
+
+ scProxy.removeAllListeners('authStateChange');
+ scProxy.on('authStateChange', function(data) {
+ bridge.callHandler('onAuthStateChangeHandler', JSON.stringify ({'data':data}), function(response) {
+ });
+ });
+
+ scProxy.removeAllListeners('subscribeStateChange');
+ scProxy.on('subscribeStateChange', function(data) {
+ bridge.callHandler('onSubscribeStateChangeHandler', JSON.stringify ({'data':data}), function(response) {
+ });
+ });
+
});
+ /**
+ * scSocket.disconnect handler
+ */
bridge.registerHandler("disconnectHandler", function(data) {
scProxy.disconnect();
});
+ /**
+ * scSocket.on(event) handler
+ */
bridge.registerHandler("onEventHandler", function(data, responseCallback) {
data = JSON.parse(data);
var eventName = data.event;
- scProxy.on(eventName, function(data) {
- console.log("eventName", eventName, data);
+ scProxy.on(eventName, function(data, respCallback) {
bridge.callHandler('onEventReceivedFromSocketCluster', JSON.stringify({
'event': eventName,
'data': JSON.stringify(data)
@@ -88,25 +117,58 @@ connectWebViewJavascriptBridge(function(bridge) {
});
});
+ /**
+ * scSocket.off(event) handler
+ */
+ bridge.registerHandler("offEventHandler", function(data) {
+ data = JSON.parse(data);
+ scProxy.off(data.event);
+ });
+
+ /**
+ * scSocket.publish handler
+ */
bridge.registerHandler("publishHandler", function(packet) {
packet = JSON.parse(packet);
var channelName = packet.channel;
if (!channelName) {
return;
}
- scProxy.publish(channelName, JSON.stringify({
- 'data': packet.data
- }));
+ scProxy.publish(
+ channelName,
+ JSON.stringify({'data': packet.data})
+ );
});
- bridge.registerHandler("subscribeHandler", function(packet) {
+ /**
+ * scSocket.publish handler
+ */
+ bridge.registerHandler("publishCallbackHandler", function(packet, callback) {
packet = JSON.parse(packet);
var channelName = packet.channel;
if (!channelName) {
return;
}
+ scProxy.publish(
+ channelName,
+ JSON.stringify({'data': packet.data}),
+ function(err){
+ callback(err);
+ }
+ );
+ });
+
+ /**
+ * scSocket.subscribe handler
+ */
+ bridge.registerHandler("subscribeHandler", function(packet) {
+ packet = JSON.parse(packet);
+ var channelName = packet.channel;
+ if (!channelName || scProxy.state != scProxy.OPEN) {
+ return;
+ }
- if (!scProxy.isSubscribed(channelName)) {
+ if (!scProxy.isSubscribed(channelName, true)) {
scProxy.watch(channelName, function(publishedData) {
bridge.callHandler('onChannelReceivedEventFromSocketCluster', JSON.stringify({
'channel': channelName,
@@ -116,6 +178,10 @@ connectWebViewJavascriptBridge(function(bridge) {
}
scProxy.subscribe(channelName);
});
+
+ /**
+ * scSocket.unsubscribe handler
+ */
bridge.registerHandler("unsubscribeHandler", function(data) {
data = JSON.parse(data);
var channelName = data.channel;
@@ -125,15 +191,74 @@ connectWebViewJavascriptBridge(function(bridge) {
scProxy.unwatch(channelName);
scProxy.unsubscribe(channelName);
});
+
+ /**
+ * scSocket.emit handler
+ */
bridge.registerHandler("emitEventHandler", function(data) {
data = JSON.parse(data);
- var eventName = data.event;
- scProxy.emit(eventName, data.data);
+ scProxy.emit(data.event, data.data);
+ });
+
+ /**
+ * scSocket.emit handler
+ */
+ bridge.registerHandler("emitEventCallbackHandler", function(data, callback) {
+ data = JSON.parse(data);
+ scProxy.emit(data.event, data.data, function(err){
+ callback(err);
+ });
+ });
+
+ /**
+ * scSocket.deauthenticate handler
+ */
+ bridge.registerHandler("deauthenticateHandler", function() {
+ scProxy.deauthenticate();
+ });
+
+ /**
+ * scSocket.deauthenticate handler
+ */
+ bridge.registerHandler("deauthenticateCallbackHandler", function(data, callback) {
+ scProxy.deauthenticate(function(err){
+ callback(err);
+ });
+ });
+
+ /**
+ * scSocket.getState handler
+ */
+ bridge.registerHandler("getStateHandler", function(data, callback) {
+ var state = scProxy.getState();
+ callback(JSON.stringify(state));
});
- bridge.init(function(message, responseCallback) {
+ /**
+ * scSocket.subscriptions handler
+ */
+ bridge.registerHandler("subscriptionsHandler", function(includePending, callback) {
+ var state = scProxy.subscriptions(includePending);
+ callback(JSON.stringify(state));
+ });
+ /**
+ * scSocket.authenticate handler
+ */
+ bridge.registerHandler("authenticateHandler", function(data) {
+ scProxy.authenticate(data);
});
-});
+ /**
+ * scSocket.authenticate handler
+ */
+ bridge.registerHandler("authenticateCallbackHandler", function(data, callback) {
+ scProxy.authenticate(data, function(err){
+ callback(err);
+ });
+ });
+ bridge.init(function(message) {
+ });
+ bridge.callHandler('readyHandler');
+});
\ No newline at end of file
diff --git a/socketclusterandroidclient/src/main/assets/user_client.html b/socketclusterandroidclient/src/main/assets/user_client.html
new file mode 100644
index 0000000..947aade
--- /dev/null
+++ b/socketclusterandroidclient/src/main/assets/user_client.html
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/socketclusterandroidclient/src/main/assets/webviewjavascriptbridge.js b/socketclusterandroidclient/src/main/assets/webviewjavascriptbridge.js
index cda792c..52b8d51 100755
--- a/socketclusterandroidclient/src/main/assets/webviewjavascriptbridge.js
+++ b/socketclusterandroidclient/src/main/assets/webviewjavascriptbridge.js
@@ -1,92 +1,93 @@
if (window.WebViewJavascriptBridge3) {
- return;
-}
+}
+else {
-var messageHandlers = {};
-var responseCallbacks = {};
-var uniqueId = 1;
+ var messageHandlers = {};
+ var responseCallbacks = {};
+ var uniqueId = 1;
-function init(messageHandler) {
- if (WebViewJavascriptBridge3._messageHandler) {
- throw new Error('WebViewJavascriptBridge3.init called twice');
+ function init(messageHandler) {
+ if (WebViewJavascriptBridge3._messageHandler) {
+ throw new Error('WebViewJavascriptBridge3.init called twice');
+ }
+ WebViewJavascriptBridge3._messageHandler = messageHandler;
}
- WebViewJavascriptBridge3._messageHandler = messageHandler;
-}
-function _doSend(message, responseCallback) {
- console.log("responseCallback:" + responseCallback);
- if (responseCallback) {
- var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
- responseCallbacks[callbackId] = responseCallback;
- message['callbackId'] = callbackId;
- }
- console.log("sending:" + JSON.stringify(message));
- _WebViewJavascriptBridge._handleMessageFromJs(message.data || null, message.responseId || null,
- message.responseData || null, message.callbackId || null, message.handlerName || null);
+ function _doSend(message, responseCallback) {
+ console.log("responseCallback:" + responseCallback);
+ if (responseCallback) {
+ var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
+ responseCallbacks[callbackId] = responseCallback;
+ message['callbackId'] = callbackId;
+ }
+ console.log("sending:" + JSON.stringify(message));
+ _WebViewJavascriptBridge._handleMessageFromJs(message.data || null, message.responseId || null,
+ message.responseData || null, message.callbackId || null, message.handlerName || null);
-}
+ }
-function send(data, responseCallback) {
- _doSend({data: data}, responseCallback);
-}
+ function send(data, responseCallback) {
+ _doSend({data: data}, responseCallback);
+ }
-function registerHandler(handlerName, handler) {
- messageHandlers[handlerName] = handler;
-}
+ function registerHandler(handlerName, handler) {
+ messageHandlers[handlerName] = handler;
+ }
-function callHandler(handlerName, data, responseCallback) {
- _doSend({handlerName: handlerName, data: data}, responseCallback);
-}
+ function callHandler(handlerName, data, responseCallback) {
+ _doSend({handlerName: handlerName, data: data}, responseCallback);
+ }
-function _dispatchMessageFromJava(messageJSON) {
- var message = JSON.parse(messageJSON);
- var messageHandler;
+ function _dispatchMessageFromJava(messageJSON) {
+ var message = JSON.parse(messageJSON);
+ var messageHandler;
- if (message.responseId) {
- var responseCallback = responseCallbacks[message.responseId];
- if (!responseCallback) {
- return;
- }
- responseCallback(message.responseData);
- delete responseCallbacks[message.responseId];
- }
- else {
- var responseCallback;
- if (message.callbackId) {
- var callbackResponseId = message.callbackId;
- responseCallback = function(responseData) {
- _doSend({
- responseId: callbackResponseId,
- responseData: responseData
- });
- };
+ if (message.responseId) {
+ var responseCallback = responseCallbacks[message.responseId];
+ if (!responseCallback) {
+ return;
+ }
+ responseCallback(message.responseData);
+ delete responseCallbacks[message.responseId];
}
+ else {
+ var responseCallback;
+ if (message.callbackId) {
+ var callbackResponseId = message.callbackId;
+ responseCallback = function(responseData) {
+ _doSend({
+ responseId: callbackResponseId,
+ responseData: responseData
+ });
+ };
+ }
- var handler = WebViewJavascriptBridge3._messageHandler;
- if (message.handlerName) {
- handler = messageHandlers[message.handlerName];
- }
- try {
- handler(message.data, responseCallback);
- } catch(exception) {
- if (typeof console !='undefined') {
- console.log("WebViewJavascriptBridge3: WARNING: javascript handler threw.", message, exception);
+ var handler = WebViewJavascriptBridge3._messageHandler;
+ if (message.handlerName) {
+ handler = messageHandlers[message.handlerName];
+ }
+ try {
+ handler(message.data, responseCallback);
+ } catch(exception) {
+ if (typeof console !='undefined') {
+ console.log("WebViewJavascriptBridge3: WARNING: javascript handler threw.", message, exception);
+ }
}
}
}
-}
-function _handleMessageFromJava(messageJSON) {
- _dispatchMessageFromJava(messageJSON);
-}
-window.WebViewJavascriptBridge3 = {
- 'init': init,
- 'send': send,
- 'registerHandler': registerHandler,
- 'callHandler': callHandler,
- '_handleMessageFromJava': _handleMessageFromJava
-};
+ function _handleMessageFromJava(messageJSON) {
+ _dispatchMessageFromJava(messageJSON);
+ }
+ window.WebViewJavascriptBridge3 = {
+ 'init': init,
+ 'send': send,
+ 'registerHandler': registerHandler,
+ 'callHandler': callHandler,
+ '_handleMessageFromJava': _handleMessageFromJava
+ };
+}
\ No newline at end of file
diff --git a/socketclusterandroidclient/src/main/java/com/fangjian/WebViewJavascriptBridge.java b/socketclusterandroidclient/src/main/java/com/fangjian/WebViewJavascriptBridge.java
index d9cb3ea..e5af3e8 100755
--- a/socketclusterandroidclient/src/main/java/com/fangjian/WebViewJavascriptBridge.java
+++ b/socketclusterandroidclient/src/main/java/com/fangjian/WebViewJavascriptBridge.java
@@ -1,11 +1,15 @@
package com.fangjian;
import android.app.Activity;
+import android.os.Build;
import android.util.Log;
-import android.webkit.*;
-import android.widget.Toast;
-import org.json.JSONObject;
-
+import android.webkit.ConsoleMessage;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
@@ -39,6 +43,9 @@ public WebViewJavascriptBridge(Activity context,WebView webview,WVJBHandler hand
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(this, "_WebViewJavascriptBridge");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ mWebView.setWebContentsDebuggingEnabled(true);
+ }
mWebView.setWebViewClient(new MyWebViewClient());
mWebView.setWebChromeClient(new MyWebChromeClient()); //optional, for show console and alert
}
@@ -73,8 +80,8 @@ public void onPageFinished(WebView webView, String url) {
private class MyWebChromeClient extends WebChromeClient {
@Override
public boolean onConsoleMessage(ConsoleMessage cm) {
- Log.d("test", cm.message()
- +" line:"+ cm.lineNumber()
+ Log.d("console.log: ", cm.message()
+ +" line:"+ cm.lineNumber()
);
return true;
}
@@ -102,7 +109,7 @@ public CallbackJs(String callbackIdJs){
}
@Override
public void callback(String data) {
- _callbackJs(callbackIdJs,data);
+ _callbackJs(callbackIdJs,data);
}
}
@@ -141,12 +148,7 @@ public void _handleMessageFromJs(final String data,String responseId,
handler = _messageHandler;
}
try {
- mContext.runOnUiThread(new Runnable(){
- @Override
- public void run() {
- handler.handle(data, responseCallback);
- }
- });
+ handler.handle(data, responseCallback);
}catch (Exception exception) {
Log.e("test","WebViewJavascriptBridge: WARNING: java handler threw. "+exception.getMessage());
}
@@ -177,13 +179,13 @@ private void _sendData(String data,WVJBResponseCallback responseCallback,String
@JavascriptInterface
private void _dispatchMessage(Map message){
String messageJSON = new JSONObject(message).toString();
- Log.d("test","sending:"+messageJSON);
- final String javascriptCommand =
+ Log.d("_dispatchMessageWVJB","sending:"+messageJSON);
+ final String javascriptCommand =
String.format("javascript:WebViewJavascriptBridge3._handleMessageFromJava('%s');",doubleEscapeString(messageJSON));
mContext.runOnUiThread(new Runnable(){
@Override
public void run() {
- mWebView.loadUrl(javascriptCommand);
+ mWebView.loadUrl(javascriptCommand);
}
});
}
@@ -193,7 +195,7 @@ public void callHandler(String handlerName) {
}
@JavascriptInterface
public void callHandler(String handlerName,String data) {
- callHandler(handlerName, data,null);
+ callHandler(handlerName, data, null);
}
@JavascriptInterface
public void callHandler(String handlerName,String data,WVJBResponseCallback responseCallback){
@@ -209,14 +211,14 @@ public void callHandler(String handlerName,String data,WVJBResponseCallback resp
* http://www.json.org/
*/
private String doubleEscapeString(String javascript) {
- String result;
- result = javascript.replace("\\", "\\\\");
- result = result.replace("\"", "\\\"");
- result = result.replace("\'", "\\\'");
- result = result.replace("\n", "\\n");
- result = result.replace("\r", "\\r");
- result = result.replace("\f", "\\f");
- return result;
+ String result;
+ result = javascript.replace("\\", "\\\\");
+ result = result.replace("\"", "\\\"");
+ result = result.replace("\'", "\\\'");
+ result = result.replace("\n", "\\n");
+ result = result.replace("\r", "\\r");
+ result = result.replace("\f", "\\f");
+ return result;
}
-}
+}
\ No newline at end of file
diff --git a/socketclusterandroidclient/src/main/java/io/socketcluster/socketclusterandroidclient/SCSocketService.java b/socketclusterandroidclient/src/main/java/io/socketcluster/socketclusterandroidclient/SCSocketService.java
new file mode 100644
index 0000000..49bf0c5
--- /dev/null
+++ b/socketclusterandroidclient/src/main/java/io/socketcluster/socketclusterandroidclient/SCSocketService.java
@@ -0,0 +1,454 @@
+package io.socketcluster.socketclusterandroidclient;
+
+/**
+ * basis on ilani project
+ * Dariusz Krempa
+ */
+
+import android.app.Activity;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import com.fangjian.WebViewJavascriptBridge;
+
+import org.json.simple.JSONValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class SCSocketService extends Service {
+
+ public static final String EVENT_ON_READY = "ready";
+ public static final String EVENT_ON_CONNECT = "onConnect";
+ public static final String EVENT_ON_DISCONNECT = "onDisconnect";
+ public static final String EVENT_ON_EVENT_MESSAGE = "onEvent";
+ public static final String EVENT_ON_SUBSCRIBED_MESSAGE = "onMessage";
+ public static final String EVENT_ON_AUTHENTICATE_STATE_CHANGE = "onAuthStateChange";
+ public static final String EVENT_ON_SUBSCRIBE_STATE_CHANGE = "onSubscribeStateChange";
+ public static final String EVENT_ON_ERROR = "onError";
+ public static final String EVENT_ON_SUBSCRIBE_FAIL = "onSubscribeFail";
+ public static final String EVENT_ON_AUTHENTICATE = "onAuthenticate";
+ public static final String EVENT_ON_DEAUTHENTICATE = "onDeauthenticate";
+ public static final String EVENT_ON_SUBSCRIBE = "onSubscribe";
+ public static final String EVENT_ON_UNSUBSCRIBE = "onUnsubscribe";
+ public static final String EVENT_ON_KICKOUT = "onKickOut";
+
+ private WebView webView;
+ private WebViewJavascriptBridge bridge;
+ private final String TAG = "SCClient";
+ private Activity mContext;
+ private final IBinder binder = new SCSocketBinder();
+
+ public class SCSocketBinder extends Binder {
+ public SCSocketService getBinder(){
+ return SCSocketService.this;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent){
+ return binder;
+ }
+ @Override
+ public void onCreate(){
+ }
+
+ public void setDelegate(Context delegate) {
+ if(webView == null) {
+ this.mContext = (Activity) delegate;
+ this.setupSCWebClient(mContext);
+ this.registerHandles();
+ }
+ }
+
+ class UserServerHandler implements WebViewJavascriptBridge.WVJBHandler{
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ Log.d("UserServerHandler","Received message from javascript: "+ data);
+ if (null !=jsCallback) {
+ jsCallback.callback("Java said:Right back atcha");
+ }
+ }
+ }
+
+
+ private void setupSCWebClient(Activity context) {
+ webView = new WebView(context);
+ bridge = new WebViewJavascriptBridge(context, webView, new UserServerHandler());
+ webView.setWebViewClient(
+ new WebViewClient() {
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ view.loadUrl(url);
+ return false;
+ }
+ });
+ webView.loadUrl("file:///android_asset/user_client.html");
+ }
+
+ private void registerHandles(){
+
+ bridge.registerHandler("readyHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_READY, data);
+ }
+ });
+
+ /**
+ * 'connect' event handler
+ * @param data json object
+ */
+ bridge.registerHandler("onConnectHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_CONNECT, data);
+ }
+ });
+
+ /**
+ * 'disconnect' event handler
+ */
+ bridge.registerHandler("onDisconnectedHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_DISCONNECT, data);
+ }
+ });
+
+ /**
+ * 'error' event handler
+ */
+ bridge.registerHandler("onErrorHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_ERROR, data);
+ }
+ });
+
+ /**
+ * 'kickOut' event handler
+ * @param data - channel
+ */
+ bridge.registerHandler("onKickoutHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_KICKOUT, data);
+ }
+ });
+
+ /**
+ * 'subscribeFail' event handler
+ * @param data - error
+ */
+ bridge.registerHandler("onSubscribeFailHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_SUBSCRIBE_FAIL, data);
+ }
+ });
+
+ /**
+ * 'authenticate' event handler
+ * @param data - authToken
+ */
+ bridge.registerHandler("onAuthenticateHandler", new WebViewJavascriptBridge.WVJBHandler(){
+
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_AUTHENTICATE, data);
+ }
+ });
+
+ /**
+ * 'deauthenticate' event handler
+ */
+ bridge.registerHandler("onDeauthenticateHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_DEAUTHENTICATE, data);
+ }
+ });
+
+ /**
+ * 'unsubscribe' event handler
+ * @param data channel name
+ */
+ bridge.registerHandler("onUnsubscribeHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_UNSUBSCRIBE, data);
+ }
+ });
+
+ /**
+ * 'authChangeState' event handler
+ */
+ bridge.registerHandler("onAuthStateChangeHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_AUTHENTICATE_STATE_CHANGE, data);
+ }
+ });
+
+ /**
+ * 'subscribeStateChange' event handler
+ */
+ bridge.registerHandler("onSubscribeStateChangeHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_SUBSCRIBE_STATE_CHANGE, data);
+ }
+ });
+
+ /**
+ * 'subscribe' event handler
+ */
+ bridge.registerHandler("onSubscribeHandler", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_SUBSCRIBE, data);
+ }
+ });
+
+ /**
+ * 'on(event)' event handler
+ */
+ bridge.registerHandler("onEventReceivedFromSocketCluster", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_EVENT_MESSAGE, data);
+ }
+ });
+
+ /**
+ * 'channel' received data helper
+ */
+ bridge.registerHandler("onChannelReceivedEventFromSocketCluster", new WebViewJavascriptBridge.WVJBHandler() {
+ @Override
+ public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
+ sendMsg(EVENT_ON_SUBSCRIBED_MESSAGE, data);
+ }
+ });
+ }
+
+ private void callJavaScript(WebView view, String methodName, Object...params){
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("javascript:try{");
+ stringBuilder.append(methodName);
+ stringBuilder.append("(");
+ String separator = "";
+ for (Object param : params) {
+ stringBuilder.append(separator);
+ separator = ",";
+ if(param instanceof String){
+ stringBuilder.append("'");
+ }
+ stringBuilder.append(param);
+ if(param instanceof String) {
+ stringBuilder.append("'");
+ }
+
+ }
+ stringBuilder.append(")}catch(error){console.error(error.message);}");
+ final String call = stringBuilder.toString();
+ Log.i(TAG, "callJavaScript: call=" + call);
+
+ view.loadUrl(call);
+ }
+
+ /**
+ * Call scSocket.connect
+ * @param options
+ */
+ public void connect(String options) {
+ bridge.callHandler("connectHandler", options);
+ }
+
+ /**
+ * Call scSocket.disconnect
+ */
+ public void disconnect() {
+ bridge.callHandler("disconnectHandler");
+ }
+
+ /**
+ * Call scSocket.emit
+ * @param eventName
+ * @param eventData
+ */
+ public void emitEvent(String eventName, String eventData) {
+ if (null == eventData) {
+ eventData = "";
+ }
+ Map data = new HashMap();
+ data.put("event", eventName);
+ data.put("data", eventData);
+ String jsonText = JSONValue.toJSONString(data);
+ bridge.callHandler("emitEventHandler", jsonText);
+ }
+
+ public void emitEvent(String eventName, String eventData, WebViewJavascriptBridge.WVJBResponseCallback callback) {
+ if (null == eventData) {
+ eventData = "";
+ }
+ Map data = new HashMap();
+ data.put("event", eventName);
+ data.put("data", eventData);
+ String jsonText = JSONValue.toJSONString(data);
+ bridge.callHandler("emitEventCallbackHandler", jsonText, callback);
+ }
+
+ /**
+ * Call scSocket.on(event)
+ */
+ public void registerEvent(String eventName) {
+ Map data = new HashMap();
+ data.put("event", eventName);
+ String jsonText = JSONValue.toJSONString(data);
+ bridge.callHandler("onEventHandler", jsonText);
+ }
+
+ /**
+ * Call scSocket.off(event)
+ * @param eventName
+ */
+ public void unregisterEvent(String eventName) {
+ Map data = new HashMap();
+ data.put("event", eventName);
+ String jsonText = JSONValue.toJSONString(data);
+ bridge.callHandler("offEventHandler", jsonText);
+ }
+
+ /**
+ * Call scSocket.publish
+ * @param channelName
+ * @param eventData
+ */
+ public void publish(String channelName, String eventData) {
+ Map data = new HashMap();
+ data.put("channel", channelName);
+ data.put("data", eventData);
+ String jsonText = JSONValue.toJSONString(data);
+ bridge.callHandler("publishHandler", jsonText);
+ }
+
+ public void publish(String channelName, String eventData, WebViewJavascriptBridge.WVJBResponseCallback callback) {
+ Map data = new HashMap();
+ data.put("channel", channelName);
+ data.put("data", eventData);
+ String jsonText = JSONValue.toJSONString(data);
+ bridge.callHandler("publishCallbackHandler", jsonText, callback);
+ }
+
+ /**
+ * Call scSocket.subscribe
+ * @param channelName
+ */
+ public void subscribe(String channelName) {
+ Map data = new HashMap();
+ data.put("channel", channelName);
+ String jsonText = JSONValue.toJSONString(data);
+ bridge.callHandler("subscribeHandler", jsonText);
+ }
+
+ /**
+ * Call scSocket.unsubscribe
+ * @param channelName
+ */
+ public void unsubscribe(String channelName) {
+ Map data = new HashMap();
+ data.put("channel", channelName);
+ String jsonText = JSONValue.toJSONString(data);
+ bridge.callHandler("unsubscribeHandler", jsonText);
+ }
+
+ /**
+ * Call scSocket.authenticate
+ * @param authToken
+ */
+ public void authenticate(String authToken) {
+ bridge.callHandler("authenticateHandler", authToken);
+ }
+
+ public void authenticate(String authToken, WebViewJavascriptBridge.WVJBResponseCallback callback) {
+ bridge.callHandler("authenticateCallbackHandler", authToken, callback);
+ }
+
+ /**
+ * Call scSocket.deauthenticate
+ */
+ public void deauthenticate() {
+ bridge.callHandler("deauthenticateHandler");
+ }
+
+ public void deauthenticate(WebViewJavascriptBridge.WVJBResponseCallback callback) {
+ bridge.callHandler("deauthenticateCallbackHandler", "", callback);
+ }
+
+ /**
+ * Call scSocket.getState
+ */
+ public void getState(WebViewJavascriptBridge.WVJBResponseCallback callback){
+ bridge.callHandler("getStateHandler", "", callback);
+ }
+
+ /**
+ * Call scSocket.subscriptions
+ * @param pending
+ */
+ public void subscriptions(Boolean pending, WebViewJavascriptBridge.WVJBResponseCallback callback){
+ Map map = new HashMap();
+ map.put("pending", pending.toString());
+ String jsonText = JSONValue.toJSONString(map);
+ bridge.callHandler("subscriptionsHandler", jsonText, callback);
+ }
+
+ private void sendMsg(String event, String data){
+
+ /**
+ * Sending events to handle at activity or any other broadcast receiver and its required as is
+ */
+ Intent intent = new Intent("io.socketcluster.eventsreceiver");
+ intent.putExtra("event", event);
+ intent.putExtra("data", data);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
+
+
+ /** TODO
+ * This is only example how to handle events with notifications and can be removed
+
+ final int NOTIFICATION_ID = 1;
+ NotificationManager mNotificationManager;
+ NotificationCompat.Builder builder;
+
+ mNotificationManager = (NotificationManager)
+ this.getSystemService(Context.NOTIFICATION_SERVICE);
+ Intent myApp = new Intent(getApplicationContext(), mContext.getClass());
+ myApp.setAction(Intent.ACTION_MAIN);
+ myApp.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+
+ PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, myApp, 0);
+
+ NotificationCompat.Builder mBuilder =
+ new NotificationCompat.Builder(this)
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentTitle("SCSocketCluster")
+ .setStyle(new NotificationCompat.BigTextStyle()
+ .bigText(data))
+ .setContentText(event)
+ .setAutoCancel(true);
+
+ mBuilder.setContentIntent(contentIntent);
+ mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());*/
+ }
+
+}
\ No newline at end of file
diff --git a/socketclusterandroidclient/src/main/java/socketcluster/io/socketclusterandroidclient/ISocketCluster.java b/socketclusterandroidclient/src/main/java/socketcluster/io/socketclusterandroidclient/ISocketCluster.java
deleted file mode 100644
index 4a67a2a..0000000
--- a/socketclusterandroidclient/src/main/java/socketcluster/io/socketclusterandroidclient/ISocketCluster.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package socketcluster.io.socketclusterandroidclient;
-
-/**
- * Created by lihanli on 9/08/2015.
- */
-public interface ISocketCluster {
- void socketClusterReceivedEvent(String name, String data);
- void socketClusterChannelReceivedEvent(String name, String data);
-
- void socketClusterDidConnect();
- void socketClusterDidDisconnect();
- void socketClusterOnError(String error);
- void socketClusterOnKickOut();
- void socketClusterOnSubscribe();
- void socketClusterOnSubscribeFail();
- void socketClusterOnUnsubscribe();
-}
diff --git a/socketclusterandroidclient/src/main/java/socketcluster/io/socketclusterandroidclient/SocketCluster.java b/socketclusterandroidclient/src/main/java/socketcluster/io/socketclusterandroidclient/SocketCluster.java
deleted file mode 100644
index 5febd29..0000000
--- a/socketclusterandroidclient/src/main/java/socketcluster/io/socketclusterandroidclient/SocketCluster.java
+++ /dev/null
@@ -1,218 +0,0 @@
-package socketcluster.io.socketclusterandroidclient;
-import android.app.Activity;
-import android.util.Log;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import com.fangjian.WebViewJavascriptBridge;
-
-import org.json.simple.JSONValue;
-import java.io.InputStream;
-
-import java.util.HashMap;
-import java.util.Map;
-
-
-public class SocketCluster {
- private String host;
- private String port;
- private Boolean isHttps;
- private WebView webView;
- private WebViewJavascriptBridge bridge;
- private ISocketCluster socketClusterDelegate;
- private final String TAG = "SCClient";
-
- public SocketCluster(String host, String port, boolean isHttps, Activity context) {
- this.host = host;
- this.port = port;
- this.isHttps = isHttps;
- this.setupSCWebClient(context);
- this.registerHandles();
- }
-
- public void setDelegate(ISocketCluster delegate) {
- socketClusterDelegate = delegate;
- }
-
- class UserServerHandler implements WebViewJavascriptBridge.WVJBHandler{
- @Override
- public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
- Log.d("test","Received message from javascript: "+ data);
- if (null !=jsCallback) {
- jsCallback.callback("Java said:Right back atcha");
- }
- }
- }
-
-
- private void setupSCWebClient(Activity context) {
- webView = new WebView(context);
- bridge = new WebViewJavascriptBridge(context, webView, new UserServerHandler());
- webView.setWebViewClient(
- new WebViewClient() {
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- view.loadUrl(url);
- return false;
- }
- });
- InputStream is=context.getResources().openRawResource(R.raw.user_client);
- String user_client_html=WebViewJavascriptBridge.convertStreamToString(is);
- webView.loadDataWithBaseURL("file:///android_asset/", user_client_html, "text/html", "utf-8", "");
-
-// InputStream is2 = context.getResources().openRawResource(R.raw.webviewjavascriptbridge);
-// String script= WebViewJavascriptBridge.convertStreamToString(is2);
-// webView.loadUrl("javascript:" + script);
-
- }
- private void registerHandles(){
-
- bridge.registerHandler("onConnectHandler", new WebViewJavascriptBridge.WVJBHandler() {
- @Override
- public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
- socketClusterDelegate.socketClusterDidConnect();
- }
- });
-
-
- bridge.registerHandler("onDisconnectedHandler", new WebViewJavascriptBridge.WVJBHandler() {
- @Override
- public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
- socketClusterDelegate.socketClusterDidConnect();
- }
- });
-
-
- bridge.registerHandler("onErrorHandler", new WebViewJavascriptBridge.WVJBHandler() {
- @Override
- public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
- socketClusterDelegate.socketClusterOnError(data);
- }
- });
-
-
- bridge.registerHandler("onKickoutHandler", new WebViewJavascriptBridge.WVJBHandler() {
- @Override
- public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
- socketClusterDelegate.socketClusterOnKickOut();
- }
- });
-
-
- bridge.registerHandler("onSubscribeFailHandler", new WebViewJavascriptBridge.WVJBHandler() {
- @Override
- public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
- socketClusterDelegate.socketClusterOnSubscribeFail();
- }
- });
-
-
- bridge.registerHandler("onUnsubscribeHandler", new WebViewJavascriptBridge.WVJBHandler() {
- @Override
- public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
- socketClusterDelegate.socketClusterOnUnsubscribe();
- }
- });
-
-
- bridge.registerHandler("onSubscribeHandler", new WebViewJavascriptBridge.WVJBHandler() {
- @Override
- public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
- socketClusterDelegate.socketClusterOnSubscribe();
- }
- });
-
- bridge.registerHandler("onEventReceivedFromSocketCluster", new WebViewJavascriptBridge.WVJBHandler() {
- @Override
- public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
- HashMap receivedData = (HashMap)JSONValue.parse(data);
- socketClusterDelegate.socketClusterReceivedEvent(receivedData.get("event"), receivedData.get("data"));
- }
- });
- bridge.registerHandler("onChannelReceivedEventFromSocketCluster", new WebViewJavascriptBridge.WVJBHandler() {
- @Override
- public void handle(String data, WebViewJavascriptBridge.WVJBResponseCallback jsCallback) {
- HashMap receivedData = (HashMap) JSONValue.parse(data);
- socketClusterDelegate.socketClusterReceivedEvent(receivedData.get("channel"), receivedData.get("data"));
- }
- });
- }
-
- private void callJavaScript(WebView view, String methodName, Object...params){
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append("javascript:try{");
- stringBuilder.append(methodName);
- stringBuilder.append("(");
- String separator = "";
- for (Object param : params) {
- stringBuilder.append(separator);
- separator = ",";
- if(param instanceof String){
- stringBuilder.append("'");
- }
- stringBuilder.append(param);
- if(param instanceof String) {
- stringBuilder.append("'");
- }
-
- }
- stringBuilder.append(")}catch(error){console.error(error.message);}");
- final String call = stringBuilder.toString();
- Log.i(TAG, "callJavaScript: call=" + call);
-
- view.loadUrl(call);
- }
-
-
-
- public void connect() {
- Map map = new HashMap();
- map.put("hostname", host);
- map.put("secure", isHttps ? "true" : "false");
- map.put("port", port);
- String jsonText = JSONValue.toJSONString(map);
- bridge.callHandler("connectHandler", jsonText);
- }
- public void disconnect() {
- bridge.callHandler("disconnectHandler");
- }
-
- public void emitEvent(String eventName, String eventData) {
-
- if (null == eventData) {
- eventData = "";
- }
- Map data = new HashMap();
- data.put("event", eventName);
- data.put("data", eventData);
- String jsonText = JSONValue.toJSONString(data);
- bridge.callHandler("emitEventHandler", jsonText);
- }
-
- public void registerEvent(String eventName) {
- Map data = new HashMap();
- data.put("event", eventName);
- String jsonText = JSONValue.toJSONString(data);
- bridge.callHandler("onEventHandler", jsonText);
- }
-
- public void publishToChannel(String channelName, String eventData) {
- Map data = new HashMap();
- data.put("channel", channelName);
- data.put("data", eventData);
- String jsonText = JSONValue.toJSONString(data);
- bridge.callHandler("publishHandler", jsonText);
- }
-
- public void subscribeToChannel(String channelName) {
- Map data = new HashMap();
- data.put("channel", channelName);
- String jsonText = JSONValue.toJSONString(data);
- bridge.callHandler("subscribeHandler", jsonText);
- }
-
- public void unsubscribeFromChannel(String channelName) {
- Map data = new HashMap();
- data.put("channel", channelName);
- String jsonText = JSONValue.toJSONString(data);
- bridge.callHandler("unsubscribeHandler", jsonText);
- }
-}
diff --git a/socketclusterandroidclient/src/main/java/socketcluster/io/socketclusterandroidclient/SocketClusterJSInterface.java b/socketclusterandroidclient/src/main/java/socketcluster/io/socketclusterandroidclient/SocketClusterJSInterface.java
deleted file mode 100644
index b461146..0000000
--- a/socketclusterandroidclient/src/main/java/socketcluster/io/socketclusterandroidclient/SocketClusterJSInterface.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package socketcluster.io.socketclusterandroidclient;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.util.Log;
-
-/**
- * Created by lihanli on 8/06/2015.
- */
-public class SocketClusterJSInterface {
- private Context con;
-
- public SocketClusterJSInterface(Context con) {
- this.con = con;
- }
-
- @android.webkit.JavascriptInterface
- public void showToast() {
- Log.e("JSInterface", "test");
- }
-}
diff --git a/socketclusterandroidclient/src/main/res/mipmap/ic_launcher.png b/socketclusterandroidclient/src/main/res/mipmap/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/socketclusterandroidclient/src/main/res/mipmap/ic_launcher.png differ
diff --git a/socketclusterandroidclient/src/main/res/raw/user_client.html b/socketclusterandroidclient/src/main/res/raw/user_client.html
deleted file mode 100644
index f2c6f3a..0000000
--- a/socketclusterandroidclient/src/main/res/raw/user_client.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/socketclusterandroidclient/src/main/res/raw/webviewjavascriptbridge.js b/socketclusterandroidclient/src/main/res/raw/webviewjavascriptbridge.js
deleted file mode 100755
index cda792c..0000000
--- a/socketclusterandroidclient/src/main/res/raw/webviewjavascriptbridge.js
+++ /dev/null
@@ -1,92 +0,0 @@
-
-if (window.WebViewJavascriptBridge3) {
- return;
-}
-
-
-var messageHandlers = {};
-var responseCallbacks = {};
-var uniqueId = 1;
-
-function init(messageHandler) {
- if (WebViewJavascriptBridge3._messageHandler) {
- throw new Error('WebViewJavascriptBridge3.init called twice');
- }
- WebViewJavascriptBridge3._messageHandler = messageHandler;
-}
-
-function _doSend(message, responseCallback) {
- console.log("responseCallback:" + responseCallback);
- if (responseCallback) {
- var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
- responseCallbacks[callbackId] = responseCallback;
- message['callbackId'] = callbackId;
- }
- console.log("sending:" + JSON.stringify(message));
- _WebViewJavascriptBridge._handleMessageFromJs(message.data || null, message.responseId || null,
- message.responseData || null, message.callbackId || null, message.handlerName || null);
-
-}
-
-function send(data, responseCallback) {
- _doSend({data: data}, responseCallback);
-}
-
-function registerHandler(handlerName, handler) {
- messageHandlers[handlerName] = handler;
-}
-
-function callHandler(handlerName, data, responseCallback) {
- _doSend({handlerName: handlerName, data: data}, responseCallback);
-}
-
-
-
-function _dispatchMessageFromJava(messageJSON) {
- var message = JSON.parse(messageJSON);
- var messageHandler;
-
- if (message.responseId) {
- var responseCallback = responseCallbacks[message.responseId];
- if (!responseCallback) {
- return;
- }
- responseCallback(message.responseData);
- delete responseCallbacks[message.responseId];
- }
- else {
- var responseCallback;
- if (message.callbackId) {
- var callbackResponseId = message.callbackId;
- responseCallback = function(responseData) {
- _doSend({
- responseId: callbackResponseId,
- responseData: responseData
- });
- };
- }
-
- var handler = WebViewJavascriptBridge3._messageHandler;
- if (message.handlerName) {
- handler = messageHandlers[message.handlerName];
- }
- try {
- handler(message.data, responseCallback);
- } catch(exception) {
- if (typeof console !='undefined') {
- console.log("WebViewJavascriptBridge3: WARNING: javascript handler threw.", message, exception);
- }
- }
- }
-}
-
-function _handleMessageFromJava(messageJSON) {
- _dispatchMessageFromJava(messageJSON);
-}
-window.WebViewJavascriptBridge3 = {
- 'init': init,
- 'send': send,
- 'registerHandler': registerHandler,
- 'callHandler': callHandler,
- '_handleMessageFromJava': _handleMessageFromJava
-};