text: (function(e){if("function"==typeof bootstrap)bootstrap("webrtc",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeWebRTC=e}else"undefined"!=typeof window?window.WebRTC=e():global.WebRTC=e()})(function(){var define,ses,bootstrap,module,exports;
  return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
  var webrtc = require('webrtcsupport');
  var getUserMedia = require('getusermedia');
  var PeerConnection = require('rtcpeerconnection');
  var WildEmitter = require('wildemitter');
  var hark = require('hark');
  var GainController = require('mediastream-gain');
  var mockconsole = require('mockconsole');
  
  
  function WebRTC(opts) {
      var self = this;
      var options = opts || {};
      var config = this.config = {
              debug: false,
              localVideoEl: '',
              remoteVideosEl: '',
              autoRequestMedia: false,
              // makes the entire PC config overridable
              peerConnectionConfig: {
                  iceServers: [{"url": "stun:stun.l.google.com:19302"}]
              },
              peerConnectionContraints: {
                  optional: [
                      {DtlsSrtpKeyAgreement: true},
                      {RtpDataChannels: true}
                  ]
              },
              autoAdjustMic: false,
              media: {
                  audio: true,
                  video: true
              },
              detectSpeakingEvents: true,
              enableDataChannels: true
          };
      var item, connection;
  
      // expose screensharing check
      this.screenSharingSupport = webrtc.screenSharing;
  
      // We also allow a 'logger' option. It can be any object that implements
      // log, warn, and error methods.
      // We log nothing by default, following "the rule of silence":
      // http://www.linfo.org/rule_of_silence.html
      this.logger = function () {
          // we assume that if you're in debug mode and you didn't
          // pass in a logger, you actually want to log as much as
          // possible.
          if (opts.debug) {
              return opts.logger || console;
          } else {
          // or we'll use your logger which should have its own logic
          // for output. Or we'll return the no-op.
              return opts.logger || mockconsole;
          }
      }();
  
      // set options
      for (item in options) {
          this.config[item] = options[item];
      }
  
      // check for support
      if (!webrtc.support) {
          this.logger.error('Your browser doesn\'t seem to support WebRTC');
      }
  
      // where we'll store our peer connections
      this.peers = [];
  
      WildEmitter.call(this);
  
      // log events in debug mode
      if (this.config.debug) {
          this.on('*', function (event, val1, val2) {
              var logger;
              // if you didn't pass in a logger and you explicitly turning on debug
              // we're just going to assume you're wanting log output with console
              if (self.config.logger === mockconsole) {
                  logger = console;
              } else {
                  logger = self.logger;
              }
              logger.log('event:', event, val1, val2);
          });
      }
  }
  
  WebRTC.prototype = Object.create(WildEmitter.prototype, {
      constructor: {
          value: WebRTC
      }
  });
  
  WebRTC.prototype.createPeer = function (opts) {
      var peer;
      opts.parent = this;
      peer = new Peer(opts);
      this.peers.push(peer);
      return peer;
  };
  
  WebRTC.prototype.startLocalMedia = function (mediaConstraints, cb) {
      var self = this;
      var constraints = mediaConstraints || {video: true, audio: true};
  
      getUserMedia(constraints, function (err, stream) {
          if (!err) {
              if (constraints.audio && self.config.detectSpeakingEvents) {
                  self.setupAudioMonitor(stream);
              }
              self.localStream = stream;
  
              if (self.config.autoAdjustMic) {
                  self.gainController = new GainController(stream);
                  // start out somewhat muted if we can track audio
                  self.setMicIfEnabled(0.5);
              }
  
              self.emit('localStream', stream);
          }
          if (cb) cb(err, stream);
      });
  };
  
  WebRTC.prototype.stopLocalMedia = function () {
      if (this.localStream) {
          this.localStream.stop();
          this.emit('localStreamStopped');
      }
  };
  
  // Audio controls
  WebRTC.prototype.mute = function () {
      this._audioEnabled(false);
      this.hardMuted = true;
      this.emit('audioOff');
  };
  WebRTC.prototype.unmute = function () {
      this._audioEnabled(true);
      this.hardMuted = false;
      this.emit('audioOn');
  };
  
  // Audio monitor
  WebRTC.prototype.setupAudioMonitor = function (stream) {
      this.logger.log('Setup audio');
      var audio = hark(stream);
      var self = this;
      var timeout;
  
      audio.on('speaking', function() {
          if (self.hardMuted) return;
          self.setMicIfEnabled(1);
          self.sendToAll('speaking', {});
          self.emit('speaking');
      });
  
      audio.on('stopped_speaking', function() {
          if (self.hardMuted) return;
          if (timeout) clearTimeout(timeout);
  
          timeout = setTimeout(function () {
              self.setMicIfEnabled(0.5);
              self.sendToAll('stopped_speaking', {});
              self.emit('stoppedSpeaking');
          }, 1000);
      });
  };
  
  // We do this as a seperate method in order to
  // still leave the "setMicVolume" as a working
  // method.
  WebRTC.prototype.setMicIfEnabled = function (volume) {
      if (!this.config.autoAdjustMic) return;
      this.gainController.setGain(volume);
  };
  
  // Video controls
  WebRTC.prototype.pauseVideo = function () {
      this._videoEnabled(false);
      this.emit('videoOff');
  };
  WebRTC.prototype.resumeVideo = function () {
      this._videoEnabled(true);
      this.emit('videoOn');
  };
  
  // Combined controls
  WebRTC.prototype.pause = function () {
      this._audioEnabled(false);
      this.pauseVideo();
  };
  WebRTC.prototype.resume = function () {
      this._audioEnabled(true);
      this.resumeVideo();
  };
  
  // Internal methods for enabling/disabling audio/video
  WebRTC.prototype._audioEnabled = function (bool) {
      // work around for chrome 27 bug where disabling tracks
      // doesn't seem to work (works in canary, remove when working)
      this.setMicIfEnabled(bool ? 1 : 0);
      this.localStream.getAudioTracks().forEach(function (track) {
          track.enabled = !!bool;
      });
  };
  WebRTC.prototype._videoEnabled = function (bool) {
      this.localStream.getVideoTracks().forEach(function (track) {
          track.enabled = !!bool;
      });
  };
  
  // removes peers
  WebRTC.prototype.removePeers = function (id, type) {
      this.getPeers(id, type).forEach(function (peer) {
          peer.end();
      });
  };
  
  // fetches all Peer objects by session id and/or type
  WebRTC.prototype.getPeers = function (sessionId, type) {
      return this.peers.filter(function (peer) {
          return (!sessionId || peer.id === sessionId) && (!type || peer.type === type);
      });
  };
  
  // sends message to all
  WebRTC.prototype.sendToAll = function (message, payload) {
      this.peers.forEach(function (peer) {
          peer.send(message, payload);
      });
  };
  
  
  function Peer(options) {
      var self = this;
  
      this.id = options.id;
      this.parent = options.parent;
      this.type = options.type || 'video';
      this.oneway = options.oneway || false;
      this.sharemyscreen = options.sharemyscreen || false;
      this.browserPrefix = options.prefix;
      this.stream = options.stream;
      this.channels = {};
      // Create an RTCPeerConnection via the polyfill
      this.pc = new PeerConnection(this.parent.config.peerConnectionConfig, this.parent.config.peerConnectionContraints);
      this.pc.on('ice', this.onIceCandidate.bind(this));
      this.pc.on('addStream', this.handleRemoteStreamAdded.bind(this));
      this.pc.on('addChannel', this.handleDataChannelAdded.bind(this));
      this.pc.on('removeStream', this.handleStreamRemoved.bind(this));
      // Just fire negotiation needed events for now
      // When browser re-negotiation handling seems to work
      // we can use this as the trigger for starting the offer/answer process
      // automatically. We'll just leave it be for now while this stabalizes.
      this.pc.on('negotiationNeeded', this.emit.bind(this, 'negotiationNeeded'));
      this.logger = this.parent.logger;
  
      // handle screensharing/broadcast mode
      if (options.type === 'screen') {
          if (this.parent.localScreen && this.sharemyscreen) {
              this.logger.log('adding local screen stream to peer connection');
              this.pc.addStream(this.parent.localScreen);
              this.broadcaster = options.broadcaster;
          }
      } else {
          this.pc.addStream(this.parent.localStream);
      }
  
      if (this.parent.config.enableDataChannels && webrtc.dataChannel) {
          // we may not have reliable channels
          try {
              this.reliableChannel = this.getDataChannel('reliable', {reliable: true});
              if (!this.reliableChannel.reliable) throw Error('Failed to make reliable channel');
          } catch (e) {
              this.logger.warn('Failed to create reliable data channel.')
              this.reliableChannel = false;
              delete this.channels.reliable;
          }
          // in FF I can't seem to create unreliable channels now
          try {
              this.unreliableChannel = this.getDataChannel('unreliable', {reliable: false, preset: true});
              if (this.unreliableChannel.unreliable !== false) throw Error('Failed to make unreliable channel');
          } catch (e) {
              this.logger.warn('Failed to create unreliable data channel.')
              this.unreliableChannel = false;
              delete this.channels.unreliableChannel;
          }
      }
  
      // call emitter constructor
      WildEmitter.call(this);
  
      // proxy events to parent
      this.on('*', function () {
          self.parent.emit.apply(self.parent, arguments);
      });
  }
  
  Peer.prototype = Object.create(WildEmitter.prototype, {
      constructor: {
          value: Peer
      }
  });
  
  Peer.prototype.handleMessage = function (message) {
      var self = this;
  
      this.logger.log('getting', message.type, message);
  
      if (message.prefix) this.browserPrefix = message.prefix;
  
      if (message.type === 'offer') {
          this.pc.answer(message.payload, function (err, sessionDesc) {
              self.send('answer', sessionDesc);
          });
      } else if (message.type === 'answer') {
          this.pc.handleAnswer(message.payload);
      } else if (message.type === 'candidate') {
          this.pc.processIce(message.payload);
      } else if (message.type === 'speaking') {
          this.parent.emit('speaking', {id: message.from});
      } else if (message.type === 'stopped_speaking') {
          this.parent.emit('stopped_speaking', {id: message.from});
      }
  };
  
  Peer.prototype.send = function (messageType, payload) {
      var message = {
          to: this.id,
          broadcaster: this.broadcaster,
          roomType: this.type,
          type: messageType,
          payload: payload,
          prefix: webrtc.prefix
      };
      this.logger.log('sending', messageType, message);
      this.parent.emit('message', message);
  };
  
  // Internal method registering handlers for a data channel and emitting events on the peer
  Peer.prototype._observeDataChannel = function (channel) {
      var self = this;
      channel.onclose = this.emit.bind(this, 'channelClose', channel);
      channel.onerror = this.emit.bind(this, 'channelError', channel);
      channel.onmessage = function (event) {
          self.emit('message', channel.label, event.data, channel, event);
      };
      channel.onopen = this.emit.bind(this, 'channelOpen', channel);
  };
  
  // Fetch or create a data channel by the given name
  Peer.prototype.getDataChannel = function (name, opts) {
      if (!webrtc.dataChannel) return this.emit('error', new Error('createDataChannel not supported'));
      var channel = this.channels[name];
      opts || (opts = {});
      if (channel) return channel;
      // if we don't have one by this label, create it
      channel = this.channels[name] = this.pc.createDataChannel(name, opts);
      this._observeDataChannel(channel);
      return channel;
  };
  
  Peer.prototype.onIceCandidate = function (candidate) {
      if (this.closed) return;
      if (candidate) {
          this.send('candidate', candidate);
      } else {
          this.logger.log("End of candidates.");
      }
  };
  
  Peer.prototype.start = function () {
      var self = this;
      this.pc.offer(function (err, sessionDescription) {
          self.send('offer', sessionDescription);
      });
  };
  
  Peer.prototype.end = function () {
      this.pc.close();
      this.handleStreamRemoved();
  };
  
  Peer.prototype.handleRemoteStreamAdded = function (event) {
      if (this.stream) {
          this.logger.warn('Already have a remote stream');
      } else {
          this.stream = event.stream;
          this.parent.emit('peerStreamAdded', this);
      }
  };
  
  Peer.prototype.handleStreamRemoved = function () {
      this.parent.peers.splice(this.parent.peers.indexOf(this), 1);
      this.closed = true;
      this.parent.emit('peerStreamRemoved', this);
  };
  
  Peer.prototype.handleDataChannelAdded = function (channel) {
      this.channels[channel.name] = channel;
  };
  
  module.exports = WebRTC;
  
  },{"getusermedia":3,"hark":6,"mediastream-gain":7,"mockconsole":8,"rtcpeerconnection":4,"webrtcsupport":2,"wildemitter":5}],2:[function(require,module,exports){
  // created by @HenrikJoreteg
  var prefix;
  var isChrome = false;
  var isFirefox = false;
  var ua = navigator.userAgent.toLowerCase();
  
  // basic sniffing
  if (ua.indexOf('firefox') !== -1) {
      prefix = 'moz';
      isFirefox = true;
  } else if (ua.indexOf('chrome') !== -1) {
      prefix = 'webkit';
      isChrome = true;
  }
  
  var PC = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
  var IceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
  var SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
  var MediaStream = window.webkitMediaStream || window.MediaStream;
  var screenSharing = navigator.userAgent.match('Chrome') && parseInt(navigator.userAgent.match(/Chrome\/(.*) /)[1], 10) >= 26;
  var AudioContext = window.webkitAudioContext || window.AudioContext;
  
  
  // export support flags and constructors.prototype && PC
  module.exports = {
      support: !!PC,
      dataChannel: isChrome || isFirefox || (PC.prototype && PC.prototype.createDataChannel),
      prefix: prefix,
      webAudio: !!(AudioContext && AudioContext.prototype.createMediaStreamSource),
      mediaStream: !!(MediaStream && MediaStream.prototype.removeTrack),
      screenSharing: !!screenSharing,
      AudioContext: AudioContext,
      PeerConnection: PC,
      SessionDescription: SessionDescription,
      IceCandidate: IceCandidate
  };
  
  },{}],3:[function(require,module,exports){
  // getUserMedia helper by @HenrikJoreteg
  var func = (navigator.getUserMedia ||
              navigator.webkitGetUserMedia ||
              navigator.mozGetUserMedia ||
              navigator.msGetUserMedia);
  
  
  module.exports = function (constraints, cb) {
      var options;
      var haveOpts = arguments.length === 2;
      var defaultOpts = {video: true, audio: true};
      var error;
      var denied = 'PERMISSION_DENIED';
      var notSatified = 'CONSTRAINT_NOT_SATISFIED';
  
      // make constraints optional
      if (!haveOpts) {
          cb = constraints;
          constraints = defaultOpts;
      }
  
      // treat lack of browser support like an error
      if (!func) {
          // throw proper error per spec
          error = new Error('NavigatorUserMediaError');
          error.name = 'NOT_SUPPORTED_ERROR';
          return cb(error);
      }
  
      func.call(navigator, constraints, function (stream) {
          cb(null, stream);
      }, function (err) {
          var error;
          // coerce into an error object since FF gives us a string
          // there are only two valid names according to the spec
          // we coerce all non-denied to "constraint not satisfied".
          if (typeof err === 'string') {
              error = new Error('NavigatorUserMediaError');
              if (err === denied) {
                  error.name = denied;
              } else {
                  error.name = notSatified;
              }
          } else {
              // if we get an error object make sure '.name' property is set
              // according to spec: http://dev.w3.org/2011/webrtc/editor/getusermedia.html#navigatorusermediaerror-and-navigatorusermediaerrorcallback
              error = err;
              if (!error.name) {
                  // this is likely chrome which
                  // sets a property called "ERROR_DENIED" on the error object
                  // if so we make sure to set a name
                  if (error[denied]) {
                      err.name = denied;
                  } else {
                      err.name = notSatified;
                  }
              }
          }
  
          cb(error);
      });
  };
  
  },{}],5:[function(require,module,exports){
  /*
  WildEmitter.js is a slim little event emitter by @henrikjoreteg largely based 
  on @visionmedia's Emitter from UI Kit.
  
  Why? I wanted it standalone.
  
  I also wanted support for wildcard emitters like this:
  
  emitter.on('*', function (eventName, other, event, payloads) {
      
  });
  
  emitter.on('somenamespace*', function (eventName, payloads) {
      
  });
  
  Please note that callbacks triggered by wildcard registered events also get 
  the event name as the first argument.
  */
  module.exports = WildEmitter;
  
  function WildEmitter() {
      this.callbacks = {};
  }
  
  // Listen on the given `event` with `fn`. Store a group name if present.
  WildEmitter.prototype.on = function (event, groupName, fn) {
      var hasGroup = (arguments.length === 3),
          group = hasGroup ? arguments[1] : undefined, 
          func = hasGroup ? arguments[2] : arguments[1];
      func._groupName = group;
      (this.callbacks[event] = this.callbacks[event] || []).push(func);
      return this;
  };
  
  // Adds an `event` listener that will be invoked a single
  // time then automatically removed.
  WildEmitter.prototype.once = function (event, groupName, fn) {
      var self = this,
          hasGroup = (arguments.length === 3),
          group = hasGroup ? arguments[1] : undefined, 
          func = hasGroup ? arguments[2] : arguments[1];
      function on() {
          self.off(event, on);
          func.apply(this, arguments);
      }
      this.on(event, group, on);
      return this;
  };
  
  // Unbinds an entire group
  WildEmitter.prototype.releaseGroup = function (groupName) {
      var item, i, len, handlers;
      for (item in this.callbacks) {
          handlers = this.callbacks[item];
          for (i = 0, len = handlers.length; i < len; i++) {
              if (handlers[i]._groupName === groupName) {
                  //console.log('removing');
                  // remove it and shorten the array we're looping through
                  handlers.splice(i, 1);
                  i--;
                  len--;
              }
          }
      }
      return this;
  };
  
  // Remove the given callback for `event` or all
  // registered callbacks.
  WildEmitter.prototype.off = function (event, fn) {
      var callbacks = this.callbacks[event],
          i;
      
      if (!callbacks) return this;
  
      // remove all handlers
      if (arguments.length === 1) {
          delete this.callbacks[event];
          return this;
      }
  
      // remove specific handler
      i = callbacks.indexOf(fn);
      callbacks.splice(i, 1);
      return this;
  };
  
  // Emit `event` with the given args.
  // also calls any `*` handlers
  WildEmitter.prototype.emit = function (event) {
      var args = [].slice.call(arguments, 1),
          callbacks = this.callbacks[event],
          specialCallbacks = this.getWildcardCallbacks(event),
          i,
          len,
          item;
  
      if (callbacks) {
          for (i = 0, len = callbacks.length; i < len; ++i) {
              if (callbacks[i]) {
                  callbacks[i].apply(this, args);
              } else {
                  break;
              }
          }
      }
  
      if (specialCallbacks) {
          for (i = 0, len = specialCallbacks.length; i < len; ++i) {
              if (specialCallbacks[i]) {
                  specialCallbacks[i].apply(this, [event].concat(args));
              } else {
                  break;
              }
          }
      }
  
      return this;
  };
  
  // Helper for for finding special wildcard event handlers that match the event
  WildEmitter.prototype.getWildcardCallbacks = function (eventName) {
      var item,
          split,
          result = [];
  
      for (item in this.callbacks) {
          split = item.split('*');
          if (item === '*' || (split.length === 2 && eventName.slice(0, split[1].length) === split[1])) {
              result = result.concat(this.callbacks[item]);
          }
      }
      return result;
  };
  
  },{}],8:[function(require,module,exports){
  var methods = "assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(",");
  var l = methods.length;
  var fn = function () {};
  var mockconsole = {};
  
  while (l--) {
      mockconsole[methods[l]] = fn;
  }
  
  module.exports = mockconsole;
  
  },{}],4:[function(require,module,exports){
  var WildEmitter = require('wildemitter');
  var webrtc = require('webrtcsupport');
  
  
  function PeerConnection(config, constraints) {
      var item;
      this.pc = new webrtc.PeerConnection(config, constraints);
      WildEmitter.call(this);
  
      // proxy some events directly
      this.pc.onremovestream = this.emit.bind(this, 'removeStream');
      this.pc.onnegotiationneeded = this.emit.bind(this, 'negotiationNeeded');
      this.pc.oniceconnectionstatechange = this.emit.bind(this, 'iceConnectionStateChange');
      this.pc.onsignalingstatechange = this.emit.bind(this, 'signalingStateChange');
  
      // handle incoming ice and data channel events
      this.pc.onaddstream = this._onAddStream.bind(this);
      this.pc.onicecandidate = this._onIce.bind(this);
      this.pc.ondatachannel = this._onDataChannel.bind(this);
  
      // whether to use SDP hack for faster data transfer
      this.config = {
          debug: false,
          sdpHack: true
      };
  
      // apply our config
      for (item in config) {
          this.config[item] = config[item];
      }
  
      if (this.config.debug) {
          this.on('*', function (eventName, event) {
              var logger = config.logger || console;
              logger.log('PeerConnection event:', arguments);
          });
      }
  }
  
  PeerConnection.prototype = Object.create(WildEmitter.prototype, {
      constructor: {
          value: PeerConnection
      }
  });
  
  // Add a stream to the peer connection object
  PeerConnection.prototype.addStream = function (stream) {
      this.localStream = stream;
      this.pc.addStream(stream);
  };
  
  
  // Init and add ice candidate object with correct constructor
  PeerConnection.prototype.processIce = function (candidate) {
      this.pc.addIceCandidate(new webrtc.IceCandidate(candidate));
  };
  
  // Generate and emit an offer with the given constraints
  PeerConnection.prototype.offer = function (constraints, cb) {
      var self = this;
      var hasConstraints = arguments.length === 2;
      var mediaConstraints = hasConstraints ? constraints : {
              mandatory: {
                  OfferToReceiveAudio: true,
                  OfferToReceiveVideo: true
              }
          };
      var callback = hasConstraints ? cb : constraints;
  
      // Actually generate the offer
      this.pc.createOffer(
          function (offer) {
              offer.sdp = self._applySdpHack(offer.sdp);
              self.pc.setLocalDescription(offer);
              self.emit('offer', offer);
              if (callback) callback(null, offer);
          },
          function (err) {
              self.emit('error', err);
              if (callback) callback(err);
          },
          mediaConstraints
      );
  };
  
  // Answer an offer with audio only
  PeerConnection.prototype.answerAudioOnly = function (offer, cb) {
      var mediaConstraints = {
              mandatory: {
                  OfferToReceiveAudio: true,
                  OfferToReceiveVideo: false
              }
          };
      this._answer(offer, mediaConstraints, cb);
  };
  
  // Answer an offer without offering to recieve
  PeerConnection.prototype.answerBroadcastOnly = function (offer, cb) {
      var mediaConstraints = {
              mandatory: {
                  OfferToReceiveAudio: false,
                  OfferToReceiveVideo: false
              }
          };
      this._answer(offer, mediaConstraints, cb);
  };
  
  // Answer an offer with given constraints default is audio/video
  PeerConnection.prototype.answer = function (offer, constraints, cb) {
      var self = this;
      var hasConstraints = arguments.length === 3;
      var callback = hasConstraints ? cb : constraints;
      var mediaConstraints = hasConstraints ? constraints : {
              mandatory: {
                  OfferToReceiveAudio: true,
                  OfferToReceiveVideo: true
              }
          };
  
      this._answer(offer, mediaConstraints, callback);
  };
  
  // Process an answer
  PeerConnection.prototype.handleAnswer = function (answer) {
      this.pc.setRemoteDescription(new webrtc.SessionDescription(answer));
  };
  
  // Close the peer connection
  PeerConnection.prototype.close = function () {
      this.pc.close();
      this.emit('close');
  };
  
  // Internal code sharing for various types of answer methods
  PeerConnection.prototype._answer = function (offer, constraints, cb) {
      var self = this;
      this.pc.setRemoteDescription(new webrtc.SessionDescription(offer));
      this.pc.createAnswer(
          function (answer) {
              answer.sdp = self._applySdpHack(answer.sdp);
              self.pc.setLocalDescription(answer);
              self.emit('answer', answer);
              if (cb) cb(null, answer);
          }, function (err) {
              self.emit('error', err);
              if (cb) cb(err);
          },
          constraints
      );
  };
  
  // Internal method for emitting ice candidates on our peer object
  PeerConnection.prototype._onIce = function (event) {
      if (event.candidate) {
          this.emit('ice', event.candidate);
      } else {
          this.emit('endOfCandidates');
      }
  };
  
  // Internal method for processing a new data channel being added by the
  // other peer.
  PeerConnection.prototype._onDataChannel = function (event) {
      this.emit('addChannel', event.channel);
  };
  
  // Internal handling of adding stream
  PeerConnection.prototype._onAddStream = function (event) {
      this.remoteStream = event.stream;
      this.emit('addStream', event);
  };
  
  // SDP hack for increasing AS (application specific) data transfer speed allowed in chrome
  PeerConnection.prototype._applySdpHack = function (sdp) {
      if (!this.config.sdpHack) return sdp;
      var parts = sdp.split('b=AS:30');
      if (parts.length === 2) {
          // increase max data transfer bandwidth to 100 Mbps
          return parts[0] + 'b=AS:102400' + parts[1];
      } else {
          return sdp;
      }
  };
  
  // Create a data channel spec reference:
  // http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCDataChannelInit
  PeerConnection.prototype.createDataChannel = function (name, opts) {
      opts || (opts = {});
      var reliable = !!opts.reliable;
      var protocol = opts.protocol || 'text/plain';
      var negotiated = !!(opts.negotiated || opts.preset);
      var settings;
      var channel;
      // firefox is a bit more finnicky
      if (webrtc.prefix === 'moz') {
          if (reliable) {
              settings = {
                  protocol: protocol,
                  preset: negotiated,
                  stream: name
              };
          } else {
              settings = {};
          }
          channel = this.pc.createDataChannel(name, settings);
          channel.binaryType = 'blob';
      } else {
          if (reliable) {
              settings = {
                  reliable: true
              };
          } else {
              settings = {reliable: false};
          }
          channel = this.pc.createDataChannel(name, settings);
      }
      return channel;
  };
  
  module.exports = PeerConnection;
  
  },{"webrtcsupport":2,"wildemitter":5}],6:[function(require,module,exports){
  var WildEmitter = require('wildemitter');
  
  function getMaxVolume (analyser, fftBins) {
    var maxVolume = -Infinity;
    analyser.getFloatFrequencyData(fftBins);
  
    for(var i=0, ii=fftBins.length; i < ii; i++) {
      if (fftBins[i] > maxVolume && fftBins[i] < 0) {
        maxVolume = fftBins[i];
      }
    };
  
    return maxVolume;
  }
  
  
  module.exports = function(stream, options) {
    var harker = new WildEmitter();
  
    // make it not break in non-supported browsers
    if (!window.webkitAudioContext) return harker;
  
    //Config
    var options = options || {},
        smoothing = (options.smoothing || 0.5),
        interval = (options.interval || 100),
        threshold = options.threshold,
        play = options.play;
  
    //Setup Audio Context
    var audioContext = new webkitAudioContext();
    var sourceNode, fftBins, analyser;
  
    analyser = audioContext.createAnalyser();
    analyser.fftSize = 512;
    analyser.smoothingTimeConstant = smoothing;
    fftBins = new Float32Array(analyser.fftSize);
  
    if (stream.jquery) stream = stream[0];
    if (stream instanceof HTMLAudioElement) {
      //Audio Tag
      sourceNode = audioContext.createMediaElementSource(stream);
      if (typeof play === 'undefined') play = true;
      threshold = threshold || -65;
    } else {
      //WebRTC Stream
      sourceNode = audioContext.createMediaStreamSource(stream);
      threshold = threshold || -45;
    }
  
    sourceNode.connect(analyser);
    if (play) analyser.connect(audioContext.destination);
  
    harker.speaking = false;
  
    harker.setThreshold = function(t) {
      threshold = t;
    };
  
    harker.setInterval = function(i) {
      interval = i;
    };
  
    // Poll the analyser node to determine if speaking
    // and emit events if changed
    var looper = function() {
      setTimeout(function() {
        var currentVolume = getMaxVolume(analyser, fftBins);
  
        harker.emit('volume_change', currentVolume, threshold);
  
        if (currentVolume > threshold) {
          if (!harker.speaking) {
            harker.speaking = true;
            harker.emit('speaking');
          }
        } else {
          if (harker.speaking) {
            harker.speaking = false;
            harker.emit('stopped_speaking');
          }
        }
  
        looper();
      }, interval);
    };
    looper();
  
  
    return harker;
  }
  
  },{"wildemitter":5}],7:[function(require,module,exports){
  var support = require('webrtcsupport');
  
  
  function GainController(stream) {
      this.support = support.webAudio && support.mediaStream;
  
      // set our starting value
      this.gain = 1;
  
      if (this.support) {
          var context = this.context = new support.AudioContext();
          this.microphone = context.createMediaStreamSource(stream);
          this.gainFilter = context.createGain();
          this.destination = context.createMediaStreamDestination();
          this.outputStream = this.destination.stream;
          this.microphone.connect(this.gainFilter);
          this.gainFilter.connect(this.destination);
          stream.removeTrack(stream.getAudioTracks()[0]);
          stream.addTrack(this.outputStream.getAudioTracks()[0]);
      }
      this.stream = stream;
  }
  
  // setting
  GainController.prototype.setGain = function (val) {
      // check for support
      if (!this.support) return;
      this.gainFilter.gain.value = val;
      this.gain = val;
  };
  
  GainController.prototype.getGain = function () {
      return this.gain;
  };
  
  GainController.prototype.off = function () {
      return this.setGain(0);
  };
  
  GainController.prototype.on = function () {
      this.setGain(1);
  };
  
  
  module.exports = GainController;
  
  },{"webrtcsupport":2}]},{},[1])(1)
  });
  ;