text: (function(){
  	
  	var Socket, myoList = {};
  	
  	if(typeof window === 'undefined'){
  		Socket = require('ws');
  	}else {
  		if(!("WebSocket" in window)) console.error('Myo.js : Sockets not supported :(');
  		Socket = WebSocket;
  	}
  
  	Myo = {
  		defaults : {
  			api_version : 3,
  			socket_url  : "ws://127.0.0.1:10138/myo/",
  			app_id      : 'com.myojs.default'
  		},
  		lockingPolicy : 'standard',
  		events : [],
  		myos : [],
  
  		onError : function(){
  			throw 'Myo.js had an error with the socket. Myo Connect might not be running. If it is, double check the API version.';
  		},
  
  		setLockingPolicy: function(policy) {
  			Myo.socket.send(
  					JSON.stringify(['command',{
  										"command": "set_locking_policy",
  										"type": policy
  									}]
  						));
  			Myo.lockingPolicy = policy;
  			return Myo;
  		},
  		
  		trigger : function(eventName){
  			var args = Array.prototype.slice.apply(arguments).slice(1);
  			emitter.trigger.call(Myo, Myo.events, eventName, args);
  			return Myo;
  		},
  		
  		on : function(eventName, fn){
  			return emitter.on(Myo.events, eventName, fn);
  		},
  		
  		off : function(eventName){
  			Myo.events = emitter.off(Myo.events, eventName);
  			return Myo;
  		},
  
  		connect : function(appId){
  			if(appId){
  				Myo.defaults.app_id = appId;
  			}
  			Myo.socket = new Socket(Myo.defaults.socket_url + Myo.defaults.api_version + '?appid=' + Myo.defaults.app_id);
  			Myo.socket.onmessage = Myo.handleMessage;
  			Myo.socket.onopen = Myo.trigger.bind(Myo, 'ready');
  			Myo.socket.onclose = Myo.trigger.bind(Myo, 'socket_closed');
  			Myo.socket.onerror = Myo.onError;
  		},
  		
  		disconnect : function(){
  			Myo.socket.close();
  		},
  
  		handleMessage : function(msg){
  			var data = JSON.parse(msg.data)[1];
  			if(!data.type || typeof(data.myo) === 'undefined') return;
  			if(data.type == 'paired'){
  				Myo.myos.push(Myo.create({
  					macAddress      : data.mac_address,
  					name            : data.name,
  					connectIndex    : data.myo
  				}));
  			}
  
  			Myo.myos.map(function(myo){
  				if(myo.connectIndex === data.myo){
  					var isStatusEvent = true;
  					if(eventTable[data.type]){
  						isStatusEvent = eventTable[data.type](myo, data);
  					}
  					if(!eventTable[data.type] || isStatusEvent){
  						myo.trigger(data.type, data, data.timestamp);
  						myo.trigger('status', data, data.timestamp);
  					}
  				}
  			})
  		},
  
  		create : function(props){
  			var myoProps = utils.merge({
  				macAddress      : undefined,
  				name            : undefined,
  				connectIndex    : undefined,
  				locked          : true,
  				connected       : false,
  				synced          : false,
  				batteryLevel    : 0,
  				lastIMU         : undefined,
  				arm             : undefined,
  				direction       : undefined,
  				warmupState     : undefined,
  				orientationOffset : {x : 0,y : 0,z : 0,w : 1},
  				events : [],
  			}, props || {});
  			return utils.merge(Object.create(Myo.methods), myoProps);
  		},
  
  		methods : {
  			trigger : function(eventName){
  				var args = Array.prototype.slice.apply(arguments).slice(1);
  				emitter.trigger.call(this, Myo.events, eventName, args);
  				emitter.trigger.call(this, this.events, eventName, args);
  				return this;
  			},
  			_trigger : function(eventName){
  				var args = Array.prototype.slice.apply(arguments).slice(1);
  				emitter.trigger.call(this, this.events, eventName, args);
  				return this;
  			},
  			on : function(eventName, fn){
  				return emitter.on(this.events, eventName, fn);
  			},
  			off : function(eventName){
  				this.events = emitter.off(this.events, eventName);
  				return this;
  			},
  			lock : function(){
  				Myo.socket.send(JSON.stringify(["command", {
  					"command": "lock",
  					"myo": this.connectIndex
  				}]));
  				return this;
  			},
  			unlock : function(hold){
  				Myo.socket.send(JSON.stringify(["command", {
  					"command": "unlock",
  					"myo": this.connectIndex,
  					"type": (hold ? "hold" : "timed")
  				}]));
  				return this;
  			},
  			zeroOrientation : function(){
  				this.orientationOffset = utils.quatInverse(this.lastQuant);
  				this.trigger('zero_orientation');
  				return this;
  			},
  			vibrate : function(intensity){
  				intensity = intensity || 'medium';
  				Myo.socket.send(JSON.stringify(['command',{
  					"command": "vibrate",
  					"myo": this.connectIndex,
  					"type": intensity
  				}]));
  				return this;
  			},
  			requestBluetoothStrength : function(){
  				Myo.socket.send(JSON.stringify(['command',{
  					"command": "request_rssi",
  					"myo": this.connectIndex
  				}]));
  				return this;
  			},
  			requestBatteryLevel : function(){
  				Myo.socket.send(JSON.stringify(['command',{
  					"command": "request_battery_level",
  					"myo": this.connectIndex
  				}]));
  				return this;
  			},
  			streamEMG : function(enabled){
  
  				console.log("myo.js streamEMG connect index", this.connectIndex);
  
  				Myo.socket.send(JSON.stringify(['command',{
  					"command": "set_stream_emg",
  					"myo": this.connectIndex,
  					"type" : (enabled ? 'enabled' : 'disabled')
  				}]));
  				return this;
  			}
  		}
  	};
  
  	var eventTable = {
  		//Stream Events
  		'pose' : function(myo, data){
  			if(myo.lastPose){
  				myo.trigger(myo.lastPose + '_off');
  				myo.trigger('pose_off', myo.lastPose);
  			}
  			if(data.pose == 'rest'){
  				myo.trigger('rest');
  				myo.lastPose = null;
  				if(Myo.lockingPolicy === 'standard') myo.unlock();
  			}else{
  				myo.trigger(data.pose);
  				myo.trigger('pose', data.pose);
  				myo.lastPose = data.pose;
  				if(Myo.lockingPolicy === 'standard') myo.unlock(true);
  			}
  		},
  		'orientation' : function(myo, data){
  			myo.lastQuant = data.orientation;
  			var ori = utils.quatRotate(myo.orientationOffset, data.orientation);
  			var imu_data = {
  				orientation : ori,
  				accelerometer : {
  					x : data.accelerometer[0],
  					y : data.accelerometer[1],
  					z : data.accelerometer[2]
  				},
  				gyroscope : {
  					x : data.gyroscope[0],
  					y : data.gyroscope[1],
  					z : data.gyroscope[2]
  				}
  			};
  			if(!myo.lastIMU) myo.lastIMU = imu_data;
  			myo.trigger('orientation',   imu_data.orientation, data.timestamp);
  			myo.trigger('accelerometer', imu_data.accelerometer, data.timestamp);
  			myo.trigger('gyroscope',     imu_data.gyroscope, data.timestamp);
  			myo.trigger('imu',           imu_data, data.timestamp);
  			myo.lastIMU = imu_data;
  		},
  		'emg' : function(myo, data){
  			myo.trigger(data.type, data.emg, data.timestamp);
  		},
  
  
  		//Status Events
  		'arm_synced' : function(myo, data){
  			myo.arm = data.arm;
  			myo.direction = data.x_direction;
  			myo.warmupState = data.warmup_state;
  			myo.synced = true;
  			return true;
  		},
  		'arm_unsynced' : function(myo, data){
  			myo.arm = undefined;
  			myo.direction = undefined;
  			myo.warmupState = undefined;
  			myo.synced = false;
  			return true;
  		},
  		'connected' : function(myo, data){
  			myo.connectVersion = data.version.join('.');
  			myo.connected = true;
  			return true;
  		},
  		'disconnected' : function(myo, data){
  			myo.connected = false;
  			return true;
  		},
  		'locked' : function(myo, data){
  			myo.locked = true;
  			return true;
  		},
  		'unlocked' : function(myo, data){
  			myo.locked = false;
  			return true;
  		},
  		'warmup_completed' : function(myo, data){
  			myo.warmupState = 'warm';
  			return true;
  		},
  
  		'rssi' : function(myo, data){
  			data.bluetooth_strength =  utils.getStrengthFromRssi(data.rssi);
  			myo.trigger('bluetooth_strength', data.bluetooth_strength, data.timestamp);
  			myo.trigger('rssi', data.rssi, data.timestamp);
  			myo.trigger('status', data, data.timestamp);
  		},
  		'battery_level' : function(myo, data){
  			myo.batteryLevel = data.battery_level;
  			myo.trigger('battery_level', data.battery_level, data.timestamp);
  			myo.trigger('status', data, data.timestamp);
  		},
  	};
  
  
  	var emitter = {
  		eventCounter : 0,
  		trigger : function(events, eventName, args){
  			var self = this;
  			events.map(function(event){
  				if(event.name == eventName) event.fn.apply(self, args);
  				if(event.name == '*'){
  					var args_temp = args.slice(0);
  					args_temp.unshift(eventName);
  					event.fn.apply(self, args_temp);
  				}
  			});
  			return this;
  		},
  		on : function(events, name, fn){
  			var id = new Date().getTime() + "" + emitter.eventCounter++;
  			events.push({
  				id   : id,
  				name : name,
  				fn   : fn
  			});
  			return id;
  		},
  		off : function(events, name){
  			events = events.reduce(function(result, event){
  				if(event.name == name || event.id == name || !name) {
  					return result;
  				}
  				result.push(event);
  				return result;
  			}, []);
  			return events;
  		},
  	};
  
  	var utils = {
  		merge : function(obj1,obj2){
  			for(var attrname in obj2) { obj1[attrname] = obj2[attrname]; }
  			return obj1;
  		},
  		quatInverse : function(q) {
  			var len = Math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
  			return {
  				w: q.w/len,
  				x: -q.x/len,
  				y: -q.y/len,
  				z: -q.z/len
  			};
  		},
  		quatRotate : function(q, r) {
  			return {
  				w: q.w * r.w - q.x * r.x - q.y * r.y - q.z * r.z,
  				x: q.w * r.x + q.x * r.w + q.y * r.z - q.z * r.y,
  				y: q.w * r.y - q.x * r.z + q.y * r.w + q.z * r.x,
  				z: q.w * r.z + q.x * r.y - q.y * r.x + q.z * r.w
  			};
  		},
  		getStrengthFromRssi : function(rssi){
  			var min = -95;
  			var max = -40;
  			rssi = (rssi < min) ? min : rssi;
  			rssi = (rssi > max) ? max : rssi;
  			return Math.round(((rssi-min)*100)/(max-min) * 100)/100;
  		},
  	};
  
  	if(typeof module !== 'undefined') module.exports = Myo;
  })();
  
  
  
  
  