From 5eae36ef1fefb8a7d29f283b443b37ec18053fe9 Mon Sep 17 00:00:00 2001 From: Daniel Berteaud Date: Tue, 19 Jul 2016 15:33:59 +0200 Subject: [PATCH] Revert "Revert "Update SimpleWebRTC to 1.19.1"" This reverts commit b7e79b7f96011176fa8048052fa30df7fe98d97c. --- public/js/simplewebrtc.bundle.js | 4813 +++++++++++++++++++++++--------------- 1 file changed, 2978 insertions(+), 1835 deletions(-) diff --git a/public/js/simplewebrtc.bundle.js b/public/js/simplewebrtc.bundle.js index 1141605..9e707b9 100644 --- a/public/js/simplewebrtc.bundle.js +++ b/public/js/simplewebrtc.bundle.js @@ -463,59 +463,7 @@ SimpleWebRTC.prototype.sendFile = function () { module.exports = SimpleWebRTC; -},{"./socketioconnection":3,"./webrtc":2,"attachmediastream":7,"mockconsole":6,"webrtcsupport":5,"wildemitter":4}],5:[function(require,module,exports){ -// created by @HenrikJoreteg -var prefix; -var version; - -if (window.mozRTCPeerConnection || navigator.mozGetUserMedia) { - prefix = 'moz'; - version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10); -} else if (window.webkitRTCPeerConnection || navigator.webkitGetUserMedia) { - prefix = 'webkit'; - version = navigator.userAgent.match(/Chrom(e|ium)/) && parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10); -} - -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 = window.location.protocol === 'https:' && - ((prefix === 'webkit' && version >= 26) || - (prefix === 'moz' && version >= 33)) -var AudioContext = window.AudioContext || window.webkitAudioContext; -var videoEl = document.createElement('video'); -var supportVp8 = videoEl && videoEl.canPlayType && videoEl.canPlayType('video/webm; codecs="vp8", vorbis') === "probably"; -var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia; - -// export support flags and constructors.prototype && PC -module.exports = { - prefix: prefix, - browserVersion: version, - support: !!PC && supportVp8 && !!getUserMedia, - // new support style - supportRTCPeerConnection: !!PC, - supportVp8: supportVp8, - supportGetUserMedia: !!getUserMedia, - supportDataChannel: !!(PC && PC.prototype && PC.prototype.createDataChannel), - supportWebAudio: !!(AudioContext && AudioContext.prototype.createMediaStreamSource), - supportMediaStream: !!(MediaStream && MediaStream.prototype.removeTrack), - supportScreenSharing: !!screenSharing, - // old deprecated style. Dont use this anymore - dataChannel: !!(PC && PC.prototype && PC.prototype.createDataChannel), - webAudio: !!(AudioContext && AudioContext.prototype.createMediaStreamSource), - mediaStream: !!(MediaStream && MediaStream.prototype.removeTrack), - screenSharing: !!screenSharing, - // constructors - AudioContext: AudioContext, - PeerConnection: PC, - SessionDescription: SessionDescription, - IceCandidate: IceCandidate, - MediaStream: MediaStream, - getUserMedia: getUserMedia -}; - -},{}],4:[function(require,module,exports){ +},{"./socketioconnection":3,"./webrtc":2,"attachmediastream":7,"mockconsole":6,"webrtcsupport":5,"wildemitter":4}],4:[function(require,module,exports){ /* WildEmitter.js is a slim little event emitter by @henrikjoreteg largely based on @visionmedia's Emitter from UI Kit. @@ -660,6 +608,58 @@ WildEmitter.prototype.getWildcardCallbacks = function (eventName) { return result; }; +},{}],5:[function(require,module,exports){ +// created by @HenrikJoreteg +var prefix; +var version; + +if (window.mozRTCPeerConnection || navigator.mozGetUserMedia) { + prefix = 'moz'; + version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10); +} else if (window.webkitRTCPeerConnection || navigator.webkitGetUserMedia) { + prefix = 'webkit'; + version = navigator.userAgent.match(/Chrom(e|ium)/) && parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10); +} + +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 = window.location.protocol === 'https:' && + ((prefix === 'webkit' && version >= 26) || + (prefix === 'moz' && version >= 33)) +var AudioContext = window.AudioContext || window.webkitAudioContext; +var videoEl = document.createElement('video'); +var supportVp8 = videoEl && videoEl.canPlayType && videoEl.canPlayType('video/webm; codecs="vp8", vorbis') === "probably"; +var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia; + +// export support flags and constructors.prototype && PC +module.exports = { + prefix: prefix, + browserVersion: version, + support: !!PC && supportVp8 && !!getUserMedia, + // new support style + supportRTCPeerConnection: !!PC, + supportVp8: supportVp8, + supportGetUserMedia: !!getUserMedia, + supportDataChannel: !!(PC && PC.prototype && PC.prototype.createDataChannel), + supportWebAudio: !!(AudioContext && AudioContext.prototype.createMediaStreamSource), + supportMediaStream: !!(MediaStream && MediaStream.prototype.removeTrack), + supportScreenSharing: !!screenSharing, + // old deprecated style. Dont use this anymore + dataChannel: !!(PC && PC.prototype && PC.prototype.createDataChannel), + webAudio: !!(AudioContext && AudioContext.prototype.createMediaStreamSource), + mediaStream: !!(MediaStream && MediaStream.prototype.removeTrack), + screenSharing: !!screenSharing, + // constructors + AudioContext: AudioContext, + PeerConnection: PC, + SessionDescription: SessionDescription, + IceCandidate: IceCandidate, + MediaStream: MediaStream, + getUserMedia: getUserMedia +}; + },{}],6:[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; @@ -5389,6 +5389,9 @@ var INBAND_FILETRANSFER_V1 = 'https://simplewebrtc.com/protocol/filetransfer#inb function Peer(options) { var self = this; + // call emitter constructor + WildEmitter.call(this); + this.id = options.id; this.parent = options.parent; this.type = options.type || 'video'; @@ -5448,9 +5451,6 @@ function Peer(options) { }); } - // call emitter constructor - WildEmitter.call(this); - this.on('channelOpen', function (channel) { if (channel.protocol === INBAND_FILETRANSFER_V1) { channel.onmessage = function (event) { @@ -5506,7 +5506,7 @@ Peer.prototype.handleMessage = function (message) { this.parent.emit('mute', {id: message.from, name: message.payload.name}); } else if (message.type === 'unmute') { this.parent.emit('unmute', {id: message.from, name: message.payload.name}); - } else { + } else { this.parent.emit(message.type, {id: message.from, payload: message.payload, roomType: message.roomType}); } }; @@ -5656,91 +5656,7 @@ Peer.prototype.sendFile = function (file) { module.exports = Peer; -},{"filetransfer":15,"rtcpeerconnection":14,"util":8,"webrtcsupport":5,"wildemitter":4}],16:[function(require,module,exports){ -// getUserMedia helper by @HenrikJoreteg -var func = (window.navigator.getUserMedia || - window.navigator.webkitGetUserMedia || - window.navigator.mozGetUserMedia || - window.navigator.msGetUserMedia); - - -module.exports = function (constraints, cb) { - var options, error; - var haveOpts = arguments.length === 2; - var defaultOpts = {video: true, audio: true}; - - var denied = 'PermissionDeniedError'; - var altDenied = 'PERMISSION_DENIED'; - var notSatisfied = 'ConstraintNotSatisfiedError'; - - // 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('MediaStreamError'); - error.name = 'NotSupportedError'; - - // keep all callbacks async - return window.setTimeout(function () { - cb(error); - }, 0); - } - - // normalize error handling when no media types are requested - if (!constraints.audio && !constraints.video) { - error = new Error('MediaStreamError'); - error.name = 'NoMediaRequestedError'; - - // keep all callbacks async - return window.setTimeout(function () { - cb(error); - }, 0); - } - - if (localStorage && localStorage.useFirefoxFakeDevice === "true") { - constraints.fake = true; - } - - func.call(window.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('MediaStreamError'); - if (err === denied || err === altDenied) { - error.name = denied; - } else { - error.name = notSatisfied; - } - } 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 = notSatisfied; - } - } - } - - cb(error); - }); -}; - -},{}],10:[function(require,module,exports){ +},{"filetransfer":15,"rtcpeerconnection":14,"util":8,"webrtcsupport":5,"wildemitter":4}],10:[function(require,module,exports){ var util = require('util'); var hark = require('hark'); var webrtc = require('webrtcsupport'); @@ -5757,6 +5673,7 @@ function LocalMedia(opts) { var config = this.config = { autoAdjustMic: false, detectSpeakingEvents: true, + audioFallback: false, media: { audio: true, video: true @@ -5791,6 +5708,7 @@ LocalMedia.prototype.start = function (mediaConstraints, cb) { var constraints = mediaConstraints || this.config.media; getUserMedia(constraints, function (err, stream) { + if (!err) { if (constraints.audio && self.config.detectSpeakingEvents) { self.setupAudioMonitor(stream, self.config.harkOptions); @@ -5816,6 +5734,13 @@ LocalMedia.prototype.start = function (mediaConstraints, cb) { }; self.emit('localStream', stream); + } else { + // Fallback for users without a camera + if (self.config.audioFallback && err.name === 'DevicesNotFoundError' && constraints.video !== false) { + constraints.video = false; + self.start(constraints, cb); + return; + } } if (cb) { return cb(err, stream); @@ -5827,23 +5752,35 @@ LocalMedia.prototype.stop = function (stream) { var self = this; // FIXME: duplicates cleanup code until fixed in FF if (stream) { - stream.stop(); - self.emit('localStreamStopped', stream); + stream.getTracks().forEach(function (track) { track.stop(); }); var idx = self.localStreams.indexOf(stream); if (idx > -1) { + self.emit('localStreamStopped', stream); self.localStreams = self.localStreams.splice(idx, 1); + } else { + idx = self.localScreens.indexOf(stream); + if (idx > -1) { + self.emit('localScreenStopped', stream); + self.localScreens = self.localScreens.splce(idx, 1); + } } } else { - if (this.audioMonitor) { - this.audioMonitor.stop(); - delete this.audioMonitor; - } - this.localStreams.forEach(function (stream) { - stream.stop(); - self.emit('localStreamStopped', stream); - }); - this.localStreams = []; + this.stopStreams(); + this.stopScreenShare(); + } +}; + +LocalMedia.prototype.stopStreams = function () { + var self = this; + if (this.audioMonitor) { + this.audioMonitor.stop(); + delete this.audioMonitor; } + this.localStreams.forEach(function (stream) { + stream.getTracks().forEach(function (track) { track.stop(); }); + self.emit('localStreamStopped', stream); + }); + this.localStreams = []; }; LocalMedia.prototype.startScreenShare = function (cb) { @@ -5873,11 +5810,14 @@ LocalMedia.prototype.startScreenShare = function (cb) { }; LocalMedia.prototype.stopScreenShare = function (stream) { + var self = this; if (stream) { - stream.stop(); + stream.getTracks().forEach(function (track) { track.stop(); }); + this.emit('localScreenStopped', stream); } else { this.localScreens.forEach(function (stream) { - stream.stop(); + stream.getTracks().forEach(function (track) { track.stop(); }); + self.emit('localScreenStopped', stream); }); this.localScreens = []; } @@ -6018,51 +5958,676 @@ Object.defineProperty(LocalMedia.prototype, 'localScreen', { module.exports = LocalMedia; -},{"getscreenmedia":18,"getusermedia":16,"hark":17,"mediastream-gain":19,"mockconsole":6,"util":8,"webrtcsupport":5,"wildemitter":4}],14:[function(require,module,exports){ -var util = require('util'); -var each = require('lodash.foreach'); -var pluck = require('lodash.pluck'); -var webrtc = require('webrtcsupport'); -var SJJ = require('sdp-jingle-json'); -var WildEmitter = require('wildemitter'); -var peerconn = require('traceablepeerconnection'); - - -function PeerConnection(config, constraints) { - var self = this; - var item; - WildEmitter.call(this); +},{"getscreenmedia":17,"getusermedia":16,"hark":18,"mediastream-gain":19,"mockconsole":6,"util":8,"webrtcsupport":5,"wildemitter":4}],20:[function(require,module,exports){ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + +/* More information about these options at jshint.com/docs/options */ +/* jshint browser: true, camelcase: true, curly: true, devel: true, + eqeqeq: true, forin: false, globalstrict: true, node: true, + quotmark: single, undef: true, unused: strict */ +/* global mozRTCIceCandidate, mozRTCPeerConnection, Promise, +mozRTCSessionDescription, webkitRTCPeerConnection, MediaStreamTrack */ +/* exported trace,requestUserMedia */ + +'use strict'; + +var getUserMedia = null; +var attachMediaStream = null; +var reattachMediaStream = null; +var webrtcDetectedBrowser = null; +var webrtcDetectedVersion = null; +var webrtcMinimumVersion = null; +var webrtcUtils = { + log: function() { + // suppress console.log output when being included as a module. + if (typeof module !== 'undefined' || + typeof require === 'function' && typeof define === 'function') { + return; + } + console.log.apply(console, arguments); + } +}; - config = config || {}; - config.iceServers = config.iceServers || []; +function trace(text) { + // This function is used for logging. + if (text[text.length - 1] === '\n') { + text = text.substring(0, text.length - 1); + } + if (window.performance) { + var now = (window.performance.now() / 1000).toFixed(3); + webrtcUtils.log(now + ': ' + text); + } else { + webrtcUtils.log(text); + } +} - // make sure this only gets enabled in Google Chrome - // EXPERIMENTAL FLAG, might get removed without notice - this.enableChromeNativeSimulcast = false; - if (constraints && constraints.optional && - webrtc.prefix === 'webkit' && - navigator.appVersion.match(/Chromium\//) === null) { - constraints.optional.forEach(function (constraint, idx) { - if (constraint.enableChromeNativeSimulcast) { - self.enableChromeNativeSimulcast = true; +if (typeof window === 'object') { + if (window.HTMLMediaElement && + !('srcObject' in window.HTMLMediaElement.prototype)) { + // Shim the srcObject property, once, when HTMLMediaElement is found. + Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', { + get: function() { + // If prefixed srcObject property exists, return it. + // Otherwise use the shimmed property, _srcObject + return 'mozSrcObject' in this ? this.mozSrcObject : this._srcObject; + }, + set: function(stream) { + if ('mozSrcObject' in this) { + this.mozSrcObject = stream; + } else { + // Use _srcObject as a private property for this shim + this._srcObject = stream; + // TODO: revokeObjectUrl(this.src) when !stream to release resources? + this.src = URL.createObjectURL(stream); + } + } + }); + } + // Proxy existing globals + getUserMedia = window.navigator && window.navigator.getUserMedia; +} + +// Attach a media stream to an element. +attachMediaStream = function(element, stream) { + element.srcObject = stream; +}; + +reattachMediaStream = function(to, from) { + to.srcObject = from.srcObject; +}; + +if (typeof window === 'undefined' || !window.navigator) { + webrtcUtils.log('This does not appear to be a browser'); + webrtcDetectedBrowser = 'not a browser'; +} else if (navigator.mozGetUserMedia && window.mozRTCPeerConnection) { + webrtcUtils.log('This appears to be Firefox'); + + webrtcDetectedBrowser = 'firefox'; + + // the detected firefox version. + webrtcDetectedVersion = + parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10); + + // the minimum firefox version still supported by adapter. + webrtcMinimumVersion = 31; + + // The RTCPeerConnection object. + window.RTCPeerConnection = function(pcConfig, pcConstraints) { + if (webrtcDetectedVersion < 38) { + // .urls is not supported in FF < 38. + // create RTCIceServers with a single url. + if (pcConfig && pcConfig.iceServers) { + var newIceServers = []; + for (var i = 0; i < pcConfig.iceServers.length; i++) { + var server = pcConfig.iceServers[i]; + if (server.hasOwnProperty('urls')) { + for (var j = 0; j < server.urls.length; j++) { + var newServer = { + url: server.urls[j] + }; + if (server.urls[j].indexOf('turn') === 0) { + newServer.username = server.username; + newServer.credential = server.credential; + } + newIceServers.push(newServer); } - }); + } else { + newIceServers.push(pcConfig.iceServers[i]); + } + } + pcConfig.iceServers = newIceServers; + } } + return new mozRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors + }; - // EXPERIMENTAL FLAG, might get removed without notice - this.enableMultiStreamHacks = false; - if (constraints && constraints.optional && - webrtc.prefix === 'webkit') { - constraints.optional.forEach(function (constraint, idx) { - if (constraint.enableMultiStreamHacks) { - self.enableMultiStreamHacks = true; + // The RTCSessionDescription object. + window.RTCSessionDescription = mozRTCSessionDescription; + + // The RTCIceCandidate object. + window.RTCIceCandidate = mozRTCIceCandidate; + + // getUserMedia constraints shim. + getUserMedia = function(constraints, onSuccess, onError) { + var constraintsToFF37 = function(c) { + if (typeof c !== 'object' || c.require) { + return c; + } + var require = []; + Object.keys(c).forEach(function(key) { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + var r = c[key] = (typeof c[key] === 'object') ? + c[key] : {ideal: c[key]}; + if (r.min !== undefined || + r.max !== undefined || r.exact !== undefined) { + require.push(key); + } + if (r.exact !== undefined) { + if (typeof r.exact === 'number') { + r.min = r.max = r.exact; + } else { + c[key] = r.exact; + } + delete r.exact; + } + if (r.ideal !== undefined) { + c.advanced = c.advanced || []; + var oc = {}; + if (typeof r.ideal === 'number') { + oc[key] = {min: r.ideal, max: r.ideal}; + } else { + oc[key] = r.ideal; + } + c.advanced.push(oc); + delete r.ideal; + if (!Object.keys(r).length) { + delete c[key]; + } + } + }); + if (require.length) { + c.require = require; + } + return c; + }; + if (webrtcDetectedVersion < 38) { + webrtcUtils.log('spec: ' + JSON.stringify(constraints)); + if (constraints.audio) { + constraints.audio = constraintsToFF37(constraints.audio); + } + if (constraints.video) { + constraints.video = constraintsToFF37(constraints.video); + } + webrtcUtils.log('ff37: ' + JSON.stringify(constraints)); + } + return navigator.mozGetUserMedia(constraints, onSuccess, onError); + }; + + navigator.getUserMedia = getUserMedia; + + // Shim for mediaDevices on older versions. + if (!navigator.mediaDevices) { + navigator.mediaDevices = {getUserMedia: requestUserMedia, + addEventListener: function() { }, + removeEventListener: function() { } + }; + } + navigator.mediaDevices.enumerateDevices = + navigator.mediaDevices.enumerateDevices || function() { + return new Promise(function(resolve) { + var infos = [ + {kind: 'audioinput', deviceId: 'default', label: '', groupId: ''}, + {kind: 'videoinput', deviceId: 'default', label: '', groupId: ''} + ]; + resolve(infos); + }); + }; + + if (webrtcDetectedVersion < 41) { + // Work around http://bugzil.la/1169665 + var orgEnumerateDevices = + navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices); + navigator.mediaDevices.enumerateDevices = function() { + return orgEnumerateDevices().catch(function(e) { + if (e.name === 'NotFoundError') { + return []; + } + throw e; + }); + }; + } +} else if (navigator.webkitGetUserMedia && !!window.chrome) { + webrtcUtils.log('This appears to be Chrome'); + + webrtcDetectedBrowser = 'chrome'; + + // the detected chrome version. + webrtcDetectedVersion = + parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10); + + // the minimum chrome version still supported by adapter. + webrtcMinimumVersion = 38; + + // The RTCPeerConnection object. + window.RTCPeerConnection = function(pcConfig, pcConstraints) { + // Translate iceTransportPolicy to iceTransports, + // see https://code.google.com/p/webrtc/issues/detail?id=4869 + if (pcConfig && pcConfig.iceTransportPolicy) { + pcConfig.iceTransports = pcConfig.iceTransportPolicy; + } + + var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors + var origGetStats = pc.getStats.bind(pc); + pc.getStats = function(selector, successCallback, errorCallback) { // jshint ignore: line + var self = this; + var args = arguments; + + // If selector is a function then we are in the old style stats so just + // pass back the original getStats format to avoid breaking old users. + if (arguments.length > 0 && typeof selector === 'function') { + return origGetStats(selector, successCallback); + } + + var fixChromeStats = function(response) { + var standardReport = {}; + var reports = response.result(); + reports.forEach(function(report) { + var standardStats = { + id: report.id, + timestamp: report.timestamp, + type: report.type + }; + report.names().forEach(function(name) { + standardStats[name] = report.stat(name); + }); + standardReport[standardStats.id] = standardStats; + }); + + return standardReport; + }; + + if (arguments.length >= 2) { + var successCallbackWrapper = function(response) { + args[1](fixChromeStats(response)); + }; + + return origGetStats.apply(this, [successCallbackWrapper, arguments[0]]); + } + + // promise-support + return new Promise(function(resolve, reject) { + if (args.length === 1 && selector === null) { + origGetStats.apply(self, [ + function(response) { + resolve.apply(null, [fixChromeStats(response)]); + }, reject]); + } else { + origGetStats.apply(self, [resolve, reject]); + } + }); + }; + + return pc; + }; + + // add promise support + ['createOffer', 'createAnswer'].forEach(function(method) { + var nativeMethod = webkitRTCPeerConnection.prototype[method]; + webkitRTCPeerConnection.prototype[method] = function() { + var self = this; + if (arguments.length < 1 || (arguments.length === 1 && + typeof(arguments[0]) === 'object')) { + var opts = arguments.length === 1 ? arguments[0] : undefined; + return new Promise(function(resolve, reject) { + nativeMethod.apply(self, [resolve, reject, opts]); + }); + } else { + return nativeMethod.apply(this, arguments); + } + }; + }); + + ['setLocalDescription', 'setRemoteDescription', + 'addIceCandidate'].forEach(function(method) { + var nativeMethod = webkitRTCPeerConnection.prototype[method]; + webkitRTCPeerConnection.prototype[method] = function() { + var args = arguments; + var self = this; + return new Promise(function(resolve, reject) { + nativeMethod.apply(self, [args[0], + function() { + resolve(); + if (args.length >= 2) { + args[1].apply(null, []); + } + }, + function(err) { + reject(err); + if (args.length >= 3) { + args[2].apply(null, [err]); + } + }] + ); + }); + }; + }); + + // getUserMedia constraints shim. + var constraintsToChrome = function(c) { + if (typeof c !== 'object' || c.mandatory || c.optional) { + return c; + } + var cc = {}; + Object.keys(c).forEach(function(key) { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]}; + if (r.exact !== undefined && typeof r.exact === 'number') { + r.min = r.max = r.exact; + } + var oldname = function(prefix, name) { + if (prefix) { + return prefix + name.charAt(0).toUpperCase() + name.slice(1); + } + return (name === 'deviceId') ? 'sourceId' : name; + }; + if (r.ideal !== undefined) { + cc.optional = cc.optional || []; + var oc = {}; + if (typeof r.ideal === 'number') { + oc[oldname('min', key)] = r.ideal; + cc.optional.push(oc); + oc = {}; + oc[oldname('max', key)] = r.ideal; + cc.optional.push(oc); + } else { + oc[oldname('', key)] = r.ideal; + cc.optional.push(oc); + } + } + if (r.exact !== undefined && typeof r.exact !== 'number') { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname('', key)] = r.exact; + } else { + ['min', 'max'].forEach(function(mix) { + if (r[mix] !== undefined) { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname(mix, key)] = r[mix]; + } + }); + } + }); + if (c.advanced) { + cc.optional = (cc.optional || []).concat(c.advanced); + } + return cc; + }; + + getUserMedia = function(constraints, onSuccess, onError) { + if (constraints.audio) { + constraints.audio = constraintsToChrome(constraints.audio); + } + if (constraints.video) { + constraints.video = constraintsToChrome(constraints.video); + } + webrtcUtils.log('chrome: ' + JSON.stringify(constraints)); + return navigator.webkitGetUserMedia(constraints, onSuccess, onError); + }; + navigator.getUserMedia = getUserMedia; + + if (!navigator.mediaDevices) { + navigator.mediaDevices = {getUserMedia: requestUserMedia, + enumerateDevices: function() { + return new Promise(function(resolve) { + var kinds = {audio: 'audioinput', video: 'videoinput'}; + return MediaStreamTrack.getSources(function(devices) { + resolve(devices.map(function(device) { + return {label: device.label, + kind: kinds[device.kind], + deviceId: device.id, + groupId: ''}; + })); + }); + }); + }}; + } + + // A shim for getUserMedia method on the mediaDevices object. + // TODO(KaptenJansson) remove once implemented in Chrome stable. + if (!navigator.mediaDevices.getUserMedia) { + navigator.mediaDevices.getUserMedia = function(constraints) { + return requestUserMedia(constraints); + }; + } else { + // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia + // function which returns a Promise, it does not accept spec-style + // constraints. + var origGetUserMedia = navigator.mediaDevices.getUserMedia. + bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function(c) { + webrtcUtils.log('spec: ' + JSON.stringify(c)); // whitespace for alignment + c.audio = constraintsToChrome(c.audio); + c.video = constraintsToChrome(c.video); + webrtcUtils.log('chrome: ' + JSON.stringify(c)); + return origGetUserMedia(c); + }; + } + + // Dummy devicechange event methods. + // TODO(KaptenJansson) remove once implemented in Chrome stable. + if (typeof navigator.mediaDevices.addEventListener === 'undefined') { + navigator.mediaDevices.addEventListener = function() { + webrtcUtils.log('Dummy mediaDevices.addEventListener called.'); + }; + } + if (typeof navigator.mediaDevices.removeEventListener === 'undefined') { + navigator.mediaDevices.removeEventListener = function() { + webrtcUtils.log('Dummy mediaDevices.removeEventListener called.'); + }; + } + + // Attach a media stream to an element. + attachMediaStream = function(element, stream) { + if (webrtcDetectedVersion >= 43) { + element.srcObject = stream; + } else if (typeof element.src !== 'undefined') { + element.src = URL.createObjectURL(stream); + } else { + webrtcUtils.log('Error attaching stream to element.'); + } + }; + reattachMediaStream = function(to, from) { + if (webrtcDetectedVersion >= 43) { + to.srcObject = from.srcObject; + } else { + to.src = from.src; + } + }; + +} else if (navigator.mediaDevices && navigator.userAgent.match( + /Edge\/(\d+).(\d+)$/)) { + webrtcUtils.log('This appears to be Edge'); + webrtcDetectedBrowser = 'edge'; + + webrtcDetectedVersion = + parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10); + + // the minimum version still supported by adapter. + webrtcMinimumVersion = 12; +} else { + webrtcUtils.log('Browser does not appear to be WebRTC-capable'); +} + +// Returns the result of getUserMedia as a Promise. +function requestUserMedia(constraints) { + return new Promise(function(resolve, reject) { + getUserMedia(constraints, resolve, reject); + }); +} + +var webrtcTesting = {}; +Object.defineProperty(webrtcTesting, 'version', { + set: function(version) { + webrtcDetectedVersion = version; + } +}); + +if (typeof module !== 'undefined') { + var RTCPeerConnection; + if (typeof window !== 'undefined') { + RTCPeerConnection = window.RTCPeerConnection; + } + module.exports = { + RTCPeerConnection: RTCPeerConnection, + getUserMedia: getUserMedia, + attachMediaStream: attachMediaStream, + reattachMediaStream: reattachMediaStream, + webrtcDetectedBrowser: webrtcDetectedBrowser, + webrtcDetectedVersion: webrtcDetectedVersion, + webrtcMinimumVersion: webrtcMinimumVersion, + webrtcTesting: webrtcTesting + //requestUserMedia: not exposed on purpose. + //trace: not exposed on purpose. + }; +} else if ((typeof require === 'function') && (typeof define === 'function')) { + // Expose objects and functions when RequireJS is doing the loading. + define([], function() { + return { + RTCPeerConnection: window.RTCPeerConnection, + getUserMedia: getUserMedia, + attachMediaStream: attachMediaStream, + reattachMediaStream: reattachMediaStream, + webrtcDetectedBrowser: webrtcDetectedBrowser, + webrtcDetectedVersion: webrtcDetectedVersion, + webrtcMinimumVersion: webrtcMinimumVersion, + webrtcTesting: webrtcTesting + //requestUserMedia: not exposed on purpose. + //trace: not exposed on purpose. + }; + }); +} + +},{}],15:[function(require,module,exports){ +var WildEmitter = require('wildemitter'); +var util = require('util'); + +function Sender(opts) { + WildEmitter.call(this); + var options = opts || {}; + this.config = { + chunksize: 16384, + pacing: 0 + }; + // set our config from options + var item; + for (item in options) { + this.config[item] = options[item]; + } + + this.file = null; + this.channel = null; +} +util.inherits(Sender, WildEmitter); + +Sender.prototype.send = function (file, channel) { + var self = this; + this.file = file; + this.channel = channel; + var sliceFile = function(offset) { + var reader = new window.FileReader(); + reader.onload = (function() { + return function(e) { + self.channel.send(e.target.result); + self.emit('progress', offset, file.size, e.target.result); + if (file.size > offset + e.target.result.byteLength) { + window.setTimeout(sliceFile, self.config.pacing, offset + self.config.chunksize); + } else { + self.emit('progress', file.size, file.size, null); + self.emit('sentFile'); + } + }; + })(file); + var slice = file.slice(offset, offset + self.config.chunksize); + reader.readAsArrayBuffer(slice); + }; + window.setTimeout(sliceFile, 0, 0); +}; + +function Receiver() { + WildEmitter.call(this); + + this.receiveBuffer = []; + this.received = 0; + this.metadata = {}; + this.channel = null; +} +util.inherits(Receiver, WildEmitter); + +Receiver.prototype.receive = function (metadata, channel) { + var self = this; + + if (metadata) { + this.metadata = metadata; + } + this.channel = channel; + // chrome only supports arraybuffers and those make it easier to calc the hash + channel.binaryType = 'arraybuffer'; + this.channel.onmessage = function (event) { + var len = event.data.byteLength; + self.received += len; + self.receiveBuffer.push(event.data); + + self.emit('progress', self.received, self.metadata.size, event.data); + if (self.received === self.metadata.size) { + self.emit('receivedFile', new window.Blob(self.receiveBuffer), self.metadata); + self.receiveBuffer = []; // discard receivebuffer + } else if (self.received > self.metadata.size) { + // FIXME + console.error('received more than expected, discarding...'); + self.receiveBuffer = []; // just discard... + + } + }; +}; + +module.exports = {}; +module.exports.support = typeof window !== 'undefined' && window && window.File && window.FileReader && window.Blob; +module.exports.Sender = Sender; +module.exports.Receiver = Receiver; + +},{"util":8,"wildemitter":4}],14:[function(require,module,exports){ +var util = require('util'); +var each = require('lodash.foreach'); +var pluck = require('lodash.pluck'); +var SJJ = require('sdp-jingle-json'); +var WildEmitter = require('wildemitter'); +var peerconn = require('traceablepeerconnection'); +var adapter = require('webrtc-adapter-test'); + +function PeerConnection(config, constraints) { + var self = this; + var item; + WildEmitter.call(this); + + config = config || {}; + config.iceServers = config.iceServers || []; + + // make sure this only gets enabled in Google Chrome + // EXPERIMENTAL FLAG, might get removed without notice + this.enableChromeNativeSimulcast = false; + if (constraints && constraints.optional && + adapter.webrtcDetectedBrowser === 'chrome' && + navigator.appVersion.match(/Chromium\//) === null) { + constraints.optional.forEach(function (constraint) { + if (constraint.enableChromeNativeSimulcast) { + self.enableChromeNativeSimulcast = true; + } + }); + } + + // EXPERIMENTAL FLAG, might get removed without notice + this.enableMultiStreamHacks = false; + if (constraints && constraints.optional && + adapter.webrtcDetectedBrowser === 'chrome') { + constraints.optional.forEach(function (constraint) { + if (constraint.enableMultiStreamHacks) { + self.enableMultiStreamHacks = true; } }); } // EXPERIMENTAL FLAG, might get removed without notice this.restrictBandwidth = 0; if (constraints && constraints.optional) { - constraints.optional.forEach(function (constraint, idx) { + constraints.optional.forEach(function (constraint) { if (constraint.andyetRestrictBandwidth) { self.restrictBandwidth = constraint.andyetRestrictBandwidth; } @@ -6075,7 +6640,7 @@ function PeerConnection(config, constraints) { // ~20ms seems good this.batchIceCandidates = 0; if (constraints && constraints.optional) { - constraints.optional.forEach(function (constraint, idx) { + constraints.optional.forEach(function (constraint) { if (constraint.andyetBatchIce) { self.batchIceCandidates = constraint.andyetBatchIce; } @@ -6087,8 +6652,8 @@ function PeerConnection(config, constraints) { // this attemps to strip out candidates with an already known foundation // and type -- i.e. those which are gathered via the same TURN server // but different transports (TURN udp, tcp and tls respectively) - if (constraints && constraints.optional && webrtc.prefix === 'webkit') { - constraints.optional.forEach(function (constraint, idx) { + if (constraints && constraints.optional && adapter.webrtcDetectedBrowser === 'chrome') { + constraints.optional.forEach(function (constraint) { if (constraint.andyetFasterICE) { self.eliminateDuplicateCandidates = constraint.andyetFasterICE; } @@ -6098,7 +6663,7 @@ function PeerConnection(config, constraints) { // when using a server such as the jitsi videobridge we don't need to signal // our candidates if (constraints && constraints.optional) { - constraints.optional.forEach(function (constraint, idx) { + constraints.optional.forEach(function (constraint) { if (constraint.andyetDontSignalCandidates) { self.dontSignalCandidates = constraint.andyetDontSignalCandidates; } @@ -6109,7 +6674,7 @@ function PeerConnection(config, constraints) { // EXPERIMENTAL FLAG, might get removed without notice this.assumeSetLocalSuccess = false; if (constraints && constraints.optional) { - constraints.optional.forEach(function (constraint, idx) { + constraints.optional.forEach(function (constraint) { if (constraint.andyetAssumeSetLocalSuccess) { self.assumeSetLocalSuccess = constraint.andyetAssumeSetLocalSuccess; } @@ -6119,10 +6684,10 @@ function PeerConnection(config, constraints) { // EXPERIMENTAL FLAG, might get removed without notice // working around https://bugzilla.mozilla.org/show_bug.cgi?id=1087551 // pass in a timeout for this - if (webrtc.prefix === 'moz') { + if (adapter.webrtcDetectedBrowser === 'firefox') { if (constraints && constraints.optional) { this.wtFirefox = 0; - constraints.optional.forEach(function (constraint, idx) { + constraints.optional.forEach(function (constraint) { if (constraint.andyetFirefoxMakesMeSad) { self.wtFirefox = constraint.andyetFirefoxMakesMeSad; if (self.wtFirefox > 0) { @@ -6179,7 +6744,7 @@ function PeerConnection(config, constraints) { } if (this.config.debug) { - this.on('*', function (eventName, event) { + this.on('*', function () { var logger = config.logger || console; logger.log('PeerConnection event:', arguments); }); @@ -6279,7 +6844,7 @@ PeerConnection.prototype.processIce = function (update, cb) { function (candidate) { var iceCandidate = SJJ.toCandidateSDP(candidate) + '\r\n'; self.pc.addIceCandidate( - new webrtc.IceCandidate({ + new RTCIceCandidate({ candidate: iceCandidate, sdpMLineIndex: mline, sdpMid: mid @@ -6308,7 +6873,7 @@ PeerConnection.prototype.processIce = function (update, cb) { } self.pc.addIceCandidate( - new webrtc.IceCandidate(update.candidate), + new RTCIceCandidate(update.candidate), function () { }, function (err) { self.emit('error', err); @@ -6462,7 +7027,7 @@ PeerConnection.prototype.handleOffer = function (offer, cb) { self._checkRemoteCandidate(line); } }); - self.pc.setRemoteDescription(new webrtc.SessionDescription(offer), + self.pc.setRemoteDescription(new RTCSessionDescription(offer), function () { cb(); }, @@ -6494,7 +7059,6 @@ PeerConnection.prototype.answerBroadcastOnly = function (cb) { // Answer an offer with given constraints default is audio/video PeerConnection.prototype.answer = function (constraints, cb) { - var self = this; var hasConstraints = arguments.length === 2; var callback = hasConstraints ? cb : constraints; var mediaConstraints = hasConstraints && constraints ? constraints : { @@ -6525,14 +7089,14 @@ PeerConnection.prototype.handleAnswer = function (answer, cb) { } }); self.pc.setRemoteDescription( - new webrtc.SessionDescription(answer), + new RTCSessionDescription(answer), function () { if (self.wtFirefox) { window.setTimeout(function () { self.firefoxcandidatebuffer.forEach(function (candidate) { // add candidates later self.pc.addIceCandidate( - new webrtc.IceCandidate(candidate), + new RTCIceCandidate(candidate), function () { }, function (err) { self.emit('error', err); @@ -6573,7 +7137,6 @@ PeerConnection.prototype._answer = function (constraints, cb) { self.pc.createAnswer( function (answer) { var sim = []; - var rtx = []; if (self.enableChromeNativeSimulcast) { // native simulcast part 1: add another SSRC answer.jingle = SJJ.toSessionJSON(answer.sdp, { @@ -6581,7 +7144,6 @@ PeerConnection.prototype._answer = function (constraints, cb) { direction: 'outgoing' }); if (answer.jingle.contents.length >= 2 && answer.jingle.contents[1].name === 'video') { - var hasSimgroup = false; var groups = answer.jingle.contents[1].description.sourceGroups || []; var hasSim = false; groups.forEach(function (group) { @@ -6649,7 +7211,6 @@ PeerConnection.prototype._answer = function (constraints, cb) { direction: 'outgoing' }); } - var groups = expandedAnswer.jingle.contents[1].description.sourceGroups || []; expandedAnswer.jingle.contents[1].description.sources.forEach(function (source, idx) { // the floor idx/2 is a hack that relies on a particular order // of groups, alternating between sim and rtx @@ -6840,8 +7401,9 @@ PeerConnection.prototype.createDataChannel = function (name, opts) { }; // a wrapper around getStats which hides the differences (where possible) +// TODO: remove in favor of adapter.js shim PeerConnection.prototype.getStats = function (cb) { - if (webrtc.prefix === 'moz') { + if (adapter.webrtcDetectedBrowser === 'firefox') { this.pc.getStats( function (res) { var items = []; @@ -6874,95 +7436,88 @@ PeerConnection.prototype.getStats = function (cb) { module.exports = PeerConnection; -},{"lodash.foreach":22,"lodash.pluck":23,"sdp-jingle-json":20,"traceablepeerconnection":21,"util":8,"webrtcsupport":5,"wildemitter":4}],15:[function(require,module,exports){ -var WildEmitter = require('wildemitter'); -var util = require('util'); +},{"lodash.foreach":23,"lodash.pluck":24,"sdp-jingle-json":21,"traceablepeerconnection":22,"util":8,"webrtc-adapter-test":20,"wildemitter":4}],16:[function(require,module,exports){ +// getUserMedia helper by @HenrikJoreteg +var adapter = require('webrtc-adapter-test'); -function Sender(opts) { - WildEmitter.call(this); - var options = opts || {}; - this.config = { - chunksize: 16384, - pacing: 0 - }; - // set our config from options - var item; - for (item in options) { - this.config[item] = options[item]; - } +module.exports = function (constraints, cb) { + var options, error; + var haveOpts = arguments.length === 2; + var defaultOpts = {video: true, audio: true}; - this.file = null; - this.channel = null; -} -util.inherits(Sender, WildEmitter); + var denied = 'PermissionDeniedError'; + var altDenied = 'PERMISSION_DENIED'; + var notSatisfied = 'ConstraintNotSatisfiedError'; -Sender.prototype.send = function (file, channel) { - var self = this; - this.file = file; - this.channel = channel; - var sliceFile = function(offset) { - var reader = new window.FileReader(); - reader.onload = (function() { - return function(e) { - self.channel.send(e.target.result); - self.emit('progress', offset, file.size, e.target.result); - if (file.size > offset + e.target.result.byteLength) { - window.setTimeout(sliceFile, self.config.pacing, offset + self.config.chunksize); - } else { - self.emit('progress', file.size, file.size, null); - self.emit('sentFile'); - } - }; - })(file); - var slice = file.slice(offset, offset + self.config.chunksize); - reader.readAsArrayBuffer(slice); - }; - window.setTimeout(sliceFile, 0, 0); -}; + // make constraints optional + if (!haveOpts) { + cb = constraints; + constraints = defaultOpts; + } -function Receiver() { - WildEmitter.call(this); + // treat lack of browser support like an error + if (!navigator.getUserMedia) { + // throw proper error per spec + error = new Error('MediaStreamError'); + error.name = 'NotSupportedError'; - this.receiveBuffer = []; - this.received = 0; - this.metadata = {}; - this.channel = null; -} -util.inherits(Receiver, WildEmitter); + // keep all callbacks async + return window.setTimeout(function () { + cb(error); + }, 0); + } -Receiver.prototype.receive = function (metadata, channel) { - var self = this; + // normalize error handling when no media types are requested + if (!constraints.audio && !constraints.video) { + error = new Error('MediaStreamError'); + error.name = 'NoMediaRequestedError'; - if (metadata) { - this.metadata = metadata; + // keep all callbacks async + return window.setTimeout(function () { + cb(error); + }, 0); } - this.channel = channel; - // chrome only supports arraybuffers and those make it easier to calc the hash - channel.binaryType = 'arraybuffer'; - this.channel.onmessage = function (event) { - var len = event.data.byteLength; - self.received += len; - self.receiveBuffer.push(event.data); - self.emit('progress', self.received, self.metadata.size, event.data); - if (self.received === self.metadata.size) { - self.emit('receivedFile', new window.Blob(self.receiveBuffer), self.metadata); - self.receiveBuffer = []; // discard receivebuffer - } else if (self.received > self.metadata.size) { - // FIXME - console.error('received more than expected, discarding...'); - self.receiveBuffer = []; // just discard... + // testing support + if (localStorage && localStorage.useFirefoxFakeDevice === "true") { + constraints.fake = true; + } + navigator.getUserMedia(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('MediaStreamError'); + if (err === denied || err === altDenied) { + error.name = denied; + } else { + error.name = notSatisfied; + } + } 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 = notSatisfied; + } + } } - }; -}; -module.exports = {}; -module.exports.support = window && window.File && window.FileReader && window.Blob; -module.exports.Sender = Sender; -module.exports.Receiver = Receiver; + cb(error); + }); +}; -},{"util":8,"wildemitter":4}],17:[function(require,module,exports){ +},{"webrtc-adapter-test":25}],18:[function(require,module,exports){ var WildEmitter = require('wildemitter'); function getMaxVolume (analyser, fftBins) { @@ -7092,129 +7647,545 @@ module.exports = function(stream, options) { return harker; } -},{"wildemitter":24}],20:[function(require,module,exports){ -var toSDP = require('./lib/tosdp'); -var toJSON = require('./lib/tojson'); - +},{"wildemitter":26}],25:[function(require,module,exports){ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + +/* More information about these options at jshint.com/docs/options */ +/* jshint browser: true, camelcase: true, curly: true, devel: true, + eqeqeq: true, forin: false, globalstrict: true, node: true, + quotmark: single, undef: true, unused: strict */ +/* global mozRTCIceCandidate, mozRTCPeerConnection, Promise, +mozRTCSessionDescription, webkitRTCPeerConnection, MediaStreamTrack */ +/* exported trace,requestUserMedia */ + +'use strict'; + +var getUserMedia = null; +var attachMediaStream = null; +var reattachMediaStream = null; +var webrtcDetectedBrowser = null; +var webrtcDetectedVersion = null; +var webrtcMinimumVersion = null; +var webrtcUtils = { + log: function() { + // suppress console.log output when being included as a module. + if (typeof module !== 'undefined' || + typeof require === 'function' && typeof define === 'function') { + return; + } + console.log.apply(console, arguments); + } +}; -// Converstion from JSON to SDP +function trace(text) { + // This function is used for logging. + if (text[text.length - 1] === '\n') { + text = text.substring(0, text.length - 1); + } + if (window.performance) { + var now = (window.performance.now() / 1000).toFixed(3); + webrtcUtils.log(now + ': ' + text); + } else { + webrtcUtils.log(text); + } +} -exports.toIncomingSDPOffer = function (session) { - return toSDP.toSessionSDP(session, { - role: 'responder', - direction: 'incoming' - }); -}; -exports.toOutgoingSDPOffer = function (session) { - return toSDP.toSessionSDP(session, { - role: 'initiator', - direction: 'outgoing' - }); -}; -exports.toIncomingSDPAnswer = function (session) { - return toSDP.toSessionSDP(session, { - role: 'initiator', - direction: 'incoming' - }); -}; -exports.toOutgoingSDPAnswer = function (session) { - return toSDP.toSessionSDP(session, { - role: 'responder', - direction: 'outgoing' - }); -}; -exports.toIncomingMediaSDPOffer = function (media) { - return toSDP.toMediaSDP(media, { - role: 'responder', - direction: 'incoming' - }); -}; -exports.toOutgoingMediaSDPOffer = function (media) { - return toSDP.toMediaSDP(media, { - role: 'initiator', - direction: 'outgoing' - }); -}; -exports.toIncomingMediaSDPAnswer = function (media) { - return toSDP.toMediaSDP(media, { - role: 'initiator', - direction: 'incoming' - }); -}; -exports.toOutgoingMediaSDPAnswer = function (media) { - return toSDP.toMediaSDP(media, { - role: 'responder', - direction: 'outgoing' +if (typeof window === 'object') { + if (window.HTMLMediaElement && + !('srcObject' in window.HTMLMediaElement.prototype)) { + // Shim the srcObject property, once, when HTMLMediaElement is found. + Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', { + get: function() { + // If prefixed srcObject property exists, return it. + // Otherwise use the shimmed property, _srcObject + return 'mozSrcObject' in this ? this.mozSrcObject : this._srcObject; + }, + set: function(stream) { + if ('mozSrcObject' in this) { + this.mozSrcObject = stream; + } else { + // Use _srcObject as a private property for this shim + this._srcObject = stream; + // TODO: revokeObjectUrl(this.src) when !stream to release resources? + this.src = URL.createObjectURL(stream); + } + } }); -}; -exports.toCandidateSDP = toSDP.toCandidateSDP; -exports.toMediaSDP = toSDP.toMediaSDP; -exports.toSessionSDP = toSDP.toSessionSDP; + } + // Proxy existing globals + getUserMedia = window.navigator && window.navigator.getUserMedia; +} + +// Attach a media stream to an element. +attachMediaStream = function(element, stream) { + element.srcObject = stream; +}; + +reattachMediaStream = function(to, from) { + to.srcObject = from.srcObject; +}; + +if (typeof window === 'undefined' || !window.navigator) { + webrtcUtils.log('This does not appear to be a browser'); + webrtcDetectedBrowser = 'not a browser'; +} else if (navigator.mozGetUserMedia && window.mozRTCPeerConnection) { + webrtcUtils.log('This appears to be Firefox'); + + webrtcDetectedBrowser = 'firefox'; + + // the detected firefox version. + webrtcDetectedVersion = + parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10); + + // the minimum firefox version still supported by adapter. + webrtcMinimumVersion = 31; + + // The RTCPeerConnection object. + window.RTCPeerConnection = function(pcConfig, pcConstraints) { + if (webrtcDetectedVersion < 38) { + // .urls is not supported in FF < 38. + // create RTCIceServers with a single url. + if (pcConfig && pcConfig.iceServers) { + var newIceServers = []; + for (var i = 0; i < pcConfig.iceServers.length; i++) { + var server = pcConfig.iceServers[i]; + if (server.hasOwnProperty('urls')) { + for (var j = 0; j < server.urls.length; j++) { + var newServer = { + url: server.urls[j] + }; + if (server.urls[j].indexOf('turn') === 0) { + newServer.username = server.username; + newServer.credential = server.credential; + } + newIceServers.push(newServer); + } + } else { + newIceServers.push(pcConfig.iceServers[i]); + } + } + pcConfig.iceServers = newIceServers; + } + } + return new mozRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors + }; + // The RTCSessionDescription object. + window.RTCSessionDescription = mozRTCSessionDescription; -// Conversion from SDP to JSON + // The RTCIceCandidate object. + window.RTCIceCandidate = mozRTCIceCandidate; -exports.toIncomingJSONOffer = function (sdp, creators) { - return toJSON.toSessionJSON(sdp, { - role: 'responder', - direction: 'incoming', - creators: creators - }); -}; -exports.toOutgoingJSONOffer = function (sdp, creators) { - return toJSON.toSessionJSON(sdp, { - role: 'initiator', - direction: 'outgoing', - creators: creators - }); -}; -exports.toIncomingJSONAnswer = function (sdp, creators) { - return toJSON.toSessionJSON(sdp, { - role: 'initiator', - direction: 'incoming', - creators: creators - }); -}; -exports.toOutgoingJSONAnswer = function (sdp, creators) { - return toJSON.toSessionJSON(sdp, { - role: 'responder', - direction: 'outgoing', - creators: creators + // getUserMedia constraints shim. + getUserMedia = function(constraints, onSuccess, onError) { + var constraintsToFF37 = function(c) { + if (typeof c !== 'object' || c.require) { + return c; + } + var require = []; + Object.keys(c).forEach(function(key) { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + var r = c[key] = (typeof c[key] === 'object') ? + c[key] : {ideal: c[key]}; + if (r.min !== undefined || + r.max !== undefined || r.exact !== undefined) { + require.push(key); + } + if (r.exact !== undefined) { + if (typeof r.exact === 'number') { + r.min = r.max = r.exact; + } else { + c[key] = r.exact; + } + delete r.exact; + } + if (r.ideal !== undefined) { + c.advanced = c.advanced || []; + var oc = {}; + if (typeof r.ideal === 'number') { + oc[key] = {min: r.ideal, max: r.ideal}; + } else { + oc[key] = r.ideal; + } + c.advanced.push(oc); + delete r.ideal; + if (!Object.keys(r).length) { + delete c[key]; + } + } + }); + if (require.length) { + c.require = require; + } + return c; + }; + if (webrtcDetectedVersion < 38) { + webrtcUtils.log('spec: ' + JSON.stringify(constraints)); + if (constraints.audio) { + constraints.audio = constraintsToFF37(constraints.audio); + } + if (constraints.video) { + constraints.video = constraintsToFF37(constraints.video); + } + webrtcUtils.log('ff37: ' + JSON.stringify(constraints)); + } + return navigator.mozGetUserMedia(constraints, onSuccess, onError); + }; + + navigator.getUserMedia = getUserMedia; + + // Shim for mediaDevices on older versions. + if (!navigator.mediaDevices) { + navigator.mediaDevices = {getUserMedia: requestUserMedia, + addEventListener: function() { }, + removeEventListener: function() { } + }; + } + navigator.mediaDevices.enumerateDevices = + navigator.mediaDevices.enumerateDevices || function() { + return new Promise(function(resolve) { + var infos = [ + {kind: 'audioinput', deviceId: 'default', label: '', groupId: ''}, + {kind: 'videoinput', deviceId: 'default', label: '', groupId: ''} + ]; + resolve(infos); }); -}; -exports.toIncomingMediaJSONOffer = function (sdp, creator) { - return toJSON.toMediaJSON(sdp, { - role: 'responder', - direction: 'incoming', - creator: creator - }); -}; -exports.toOutgoingMediaJSONOffer = function (sdp, creator) { - return toJSON.toMediaJSON(sdp, { - role: 'initiator', - direction: 'outgoing', - creator: creator - }); -}; -exports.toIncomingMediaJSONAnswer = function (sdp, creator) { - return toJSON.toMediaJSON(sdp, { - role: 'initiator', - direction: 'incoming', - creator: creator - }); -}; -exports.toOutgoingMediaJSONAnswer = function (sdp, creator) { - return toJSON.toMediaJSON(sdp, { - role: 'responder', - direction: 'outgoing', - creator: creator + }; + + if (webrtcDetectedVersion < 41) { + // Work around http://bugzil.la/1169665 + var orgEnumerateDevices = + navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices); + navigator.mediaDevices.enumerateDevices = function() { + return orgEnumerateDevices().catch(function(e) { + if (e.name === 'NotFoundError') { + return []; + } + throw e; + }); + }; + } +} else if (navigator.webkitGetUserMedia && !!window.chrome) { + webrtcUtils.log('This appears to be Chrome'); + + webrtcDetectedBrowser = 'chrome'; + + // the detected chrome version. + webrtcDetectedVersion = + parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10); + + // the minimum chrome version still supported by adapter. + webrtcMinimumVersion = 38; + + // The RTCPeerConnection object. + window.RTCPeerConnection = function(pcConfig, pcConstraints) { + // Translate iceTransportPolicy to iceTransports, + // see https://code.google.com/p/webrtc/issues/detail?id=4869 + if (pcConfig && pcConfig.iceTransportPolicy) { + pcConfig.iceTransports = pcConfig.iceTransportPolicy; + } + + var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors + var origGetStats = pc.getStats.bind(pc); + pc.getStats = function(selector, successCallback, errorCallback) { // jshint ignore: line + var self = this; + var args = arguments; + + // If selector is a function then we are in the old style stats so just + // pass back the original getStats format to avoid breaking old users. + if (arguments.length > 0 && typeof selector === 'function') { + return origGetStats(selector, successCallback); + } + + var fixChromeStats = function(response) { + var standardReport = {}; + var reports = response.result(); + reports.forEach(function(report) { + var standardStats = { + id: report.id, + timestamp: report.timestamp, + type: report.type + }; + report.names().forEach(function(name) { + standardStats[name] = report.stat(name); + }); + standardReport[standardStats.id] = standardStats; + }); + + return standardReport; + }; + + if (arguments.length >= 2) { + var successCallbackWrapper = function(response) { + args[1](fixChromeStats(response)); + }; + + return origGetStats.apply(this, [successCallbackWrapper, arguments[0]]); + } + + // promise-support + return new Promise(function(resolve, reject) { + if (args.length === 1 && selector === null) { + origGetStats.apply(self, [ + function(response) { + resolve.apply(null, [fixChromeStats(response)]); + }, reject]); + } else { + origGetStats.apply(self, [resolve, reject]); + } + }); + }; + + return pc; + }; + + // add promise support + ['createOffer', 'createAnswer'].forEach(function(method) { + var nativeMethod = webkitRTCPeerConnection.prototype[method]; + webkitRTCPeerConnection.prototype[method] = function() { + var self = this; + if (arguments.length < 1 || (arguments.length === 1 && + typeof(arguments[0]) === 'object')) { + var opts = arguments.length === 1 ? arguments[0] : undefined; + return new Promise(function(resolve, reject) { + nativeMethod.apply(self, [resolve, reject, opts]); + }); + } else { + return nativeMethod.apply(this, arguments); + } + }; + }); + + ['setLocalDescription', 'setRemoteDescription', + 'addIceCandidate'].forEach(function(method) { + var nativeMethod = webkitRTCPeerConnection.prototype[method]; + webkitRTCPeerConnection.prototype[method] = function() { + var args = arguments; + var self = this; + return new Promise(function(resolve, reject) { + nativeMethod.apply(self, [args[0], + function() { + resolve(); + if (args.length >= 2) { + args[1].apply(null, []); + } + }, + function(err) { + reject(err); + if (args.length >= 3) { + args[2].apply(null, [err]); + } + }] + ); + }); + }; + }); + + // getUserMedia constraints shim. + var constraintsToChrome = function(c) { + if (typeof c !== 'object' || c.mandatory || c.optional) { + return c; + } + var cc = {}; + Object.keys(c).forEach(function(key) { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]}; + if (r.exact !== undefined && typeof r.exact === 'number') { + r.min = r.max = r.exact; + } + var oldname = function(prefix, name) { + if (prefix) { + return prefix + name.charAt(0).toUpperCase() + name.slice(1); + } + return (name === 'deviceId') ? 'sourceId' : name; + }; + if (r.ideal !== undefined) { + cc.optional = cc.optional || []; + var oc = {}; + if (typeof r.ideal === 'number') { + oc[oldname('min', key)] = r.ideal; + cc.optional.push(oc); + oc = {}; + oc[oldname('max', key)] = r.ideal; + cc.optional.push(oc); + } else { + oc[oldname('', key)] = r.ideal; + cc.optional.push(oc); + } + } + if (r.exact !== undefined && typeof r.exact !== 'number') { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname('', key)] = r.exact; + } else { + ['min', 'max'].forEach(function(mix) { + if (r[mix] !== undefined) { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname(mix, key)] = r[mix]; + } + }); + } }); -}; -exports.toCandidateJSON = toJSON.toCandidateJSON; -exports.toMediaJSON = toJSON.toMediaJSON; -exports.toSessionJSON = toJSON.toSessionJSON; + if (c.advanced) { + cc.optional = (cc.optional || []).concat(c.advanced); + } + return cc; + }; + + getUserMedia = function(constraints, onSuccess, onError) { + if (constraints.audio) { + constraints.audio = constraintsToChrome(constraints.audio); + } + if (constraints.video) { + constraints.video = constraintsToChrome(constraints.video); + } + webrtcUtils.log('chrome: ' + JSON.stringify(constraints)); + return navigator.webkitGetUserMedia(constraints, onSuccess, onError); + }; + navigator.getUserMedia = getUserMedia; + + if (!navigator.mediaDevices) { + navigator.mediaDevices = {getUserMedia: requestUserMedia, + enumerateDevices: function() { + return new Promise(function(resolve) { + var kinds = {audio: 'audioinput', video: 'videoinput'}; + return MediaStreamTrack.getSources(function(devices) { + resolve(devices.map(function(device) { + return {label: device.label, + kind: kinds[device.kind], + deviceId: device.id, + groupId: ''}; + })); + }); + }); + }}; + } + + // A shim for getUserMedia method on the mediaDevices object. + // TODO(KaptenJansson) remove once implemented in Chrome stable. + if (!navigator.mediaDevices.getUserMedia) { + navigator.mediaDevices.getUserMedia = function(constraints) { + return requestUserMedia(constraints); + }; + } else { + // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia + // function which returns a Promise, it does not accept spec-style + // constraints. + var origGetUserMedia = navigator.mediaDevices.getUserMedia. + bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function(c) { + webrtcUtils.log('spec: ' + JSON.stringify(c)); // whitespace for alignment + c.audio = constraintsToChrome(c.audio); + c.video = constraintsToChrome(c.video); + webrtcUtils.log('chrome: ' + JSON.stringify(c)); + return origGetUserMedia(c); + }; + } + + // Dummy devicechange event methods. + // TODO(KaptenJansson) remove once implemented in Chrome stable. + if (typeof navigator.mediaDevices.addEventListener === 'undefined') { + navigator.mediaDevices.addEventListener = function() { + webrtcUtils.log('Dummy mediaDevices.addEventListener called.'); + }; + } + if (typeof navigator.mediaDevices.removeEventListener === 'undefined') { + navigator.mediaDevices.removeEventListener = function() { + webrtcUtils.log('Dummy mediaDevices.removeEventListener called.'); + }; + } + + // Attach a media stream to an element. + attachMediaStream = function(element, stream) { + if (webrtcDetectedVersion >= 43) { + element.srcObject = stream; + } else if (typeof element.src !== 'undefined') { + element.src = URL.createObjectURL(stream); + } else { + webrtcUtils.log('Error attaching stream to element.'); + } + }; + reattachMediaStream = function(to, from) { + if (webrtcDetectedVersion >= 43) { + to.srcObject = from.srcObject; + } else { + to.src = from.src; + } + }; + +} else if (navigator.mediaDevices && navigator.userAgent.match( + /Edge\/(\d+).(\d+)$/)) { + webrtcUtils.log('This appears to be Edge'); + webrtcDetectedBrowser = 'edge'; + + webrtcDetectedVersion = + parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10); + + // the minimum version still supported by adapter. + webrtcMinimumVersion = 12; +} else { + webrtcUtils.log('Browser does not appear to be WebRTC-capable'); +} + +// Returns the result of getUserMedia as a Promise. +function requestUserMedia(constraints) { + return new Promise(function(resolve, reject) { + getUserMedia(constraints, resolve, reject); + }); +} + +var webrtcTesting = {}; +Object.defineProperty(webrtcTesting, 'version', { + set: function(version) { + webrtcDetectedVersion = version; + } +}); + +if (typeof module !== 'undefined') { + var RTCPeerConnection; + if (typeof window !== 'undefined') { + RTCPeerConnection = window.RTCPeerConnection; + } + module.exports = { + RTCPeerConnection: RTCPeerConnection, + getUserMedia: getUserMedia, + attachMediaStream: attachMediaStream, + reattachMediaStream: reattachMediaStream, + webrtcDetectedBrowser: webrtcDetectedBrowser, + webrtcDetectedVersion: webrtcDetectedVersion, + webrtcMinimumVersion: webrtcMinimumVersion, + webrtcTesting: webrtcTesting + //requestUserMedia: not exposed on purpose. + //trace: not exposed on purpose. + }; +} else if ((typeof require === 'function') && (typeof define === 'function')) { + // Expose objects and functions when RequireJS is doing the loading. + define([], function() { + return { + RTCPeerConnection: window.RTCPeerConnection, + getUserMedia: getUserMedia, + attachMediaStream: attachMediaStream, + reattachMediaStream: reattachMediaStream, + webrtcDetectedBrowser: webrtcDetectedBrowser, + webrtcDetectedVersion: webrtcDetectedVersion, + webrtcMinimumVersion: webrtcMinimumVersion, + webrtcTesting: webrtcTesting + //requestUserMedia: not exposed on purpose. + //trace: not exposed on purpose. + }; + }); +} -},{"./lib/tojson":26,"./lib/tosdp":25}],24:[function(require,module,exports){ +},{}],26:[function(require,module,exports){ /* WildEmitter.js is a slim little event emitter by @henrikjoreteg largely based on @visionmedia's Emitter from UI Kit. @@ -7358,7 +8329,129 @@ WildEmitter.prototype.getWildcardCallbacks = function (eventName) { return result; }; -},{}],18:[function(require,module,exports){ +},{}],21:[function(require,module,exports){ +var toSDP = require('./lib/tosdp'); +var toJSON = require('./lib/tojson'); + + +// Converstion from JSON to SDP + +exports.toIncomingSDPOffer = function (session) { + return toSDP.toSessionSDP(session, { + role: 'responder', + direction: 'incoming' + }); +}; +exports.toOutgoingSDPOffer = function (session) { + return toSDP.toSessionSDP(session, { + role: 'initiator', + direction: 'outgoing' + }); +}; +exports.toIncomingSDPAnswer = function (session) { + return toSDP.toSessionSDP(session, { + role: 'initiator', + direction: 'incoming' + }); +}; +exports.toOutgoingSDPAnswer = function (session) { + return toSDP.toSessionSDP(session, { + role: 'responder', + direction: 'outgoing' + }); +}; +exports.toIncomingMediaSDPOffer = function (media) { + return toSDP.toMediaSDP(media, { + role: 'responder', + direction: 'incoming' + }); +}; +exports.toOutgoingMediaSDPOffer = function (media) { + return toSDP.toMediaSDP(media, { + role: 'initiator', + direction: 'outgoing' + }); +}; +exports.toIncomingMediaSDPAnswer = function (media) { + return toSDP.toMediaSDP(media, { + role: 'initiator', + direction: 'incoming' + }); +}; +exports.toOutgoingMediaSDPAnswer = function (media) { + return toSDP.toMediaSDP(media, { + role: 'responder', + direction: 'outgoing' + }); +}; +exports.toCandidateSDP = toSDP.toCandidateSDP; +exports.toMediaSDP = toSDP.toMediaSDP; +exports.toSessionSDP = toSDP.toSessionSDP; + + +// Conversion from SDP to JSON + +exports.toIncomingJSONOffer = function (sdp, creators) { + return toJSON.toSessionJSON(sdp, { + role: 'responder', + direction: 'incoming', + creators: creators + }); +}; +exports.toOutgoingJSONOffer = function (sdp, creators) { + return toJSON.toSessionJSON(sdp, { + role: 'initiator', + direction: 'outgoing', + creators: creators + }); +}; +exports.toIncomingJSONAnswer = function (sdp, creators) { + return toJSON.toSessionJSON(sdp, { + role: 'initiator', + direction: 'incoming', + creators: creators + }); +}; +exports.toOutgoingJSONAnswer = function (sdp, creators) { + return toJSON.toSessionJSON(sdp, { + role: 'responder', + direction: 'outgoing', + creators: creators + }); +}; +exports.toIncomingMediaJSONOffer = function (sdp, creator) { + return toJSON.toMediaJSON(sdp, { + role: 'responder', + direction: 'incoming', + creator: creator + }); +}; +exports.toOutgoingMediaJSONOffer = function (sdp, creator) { + return toJSON.toMediaJSON(sdp, { + role: 'initiator', + direction: 'outgoing', + creator: creator + }); +}; +exports.toIncomingMediaJSONAnswer = function (sdp, creator) { + return toJSON.toMediaJSON(sdp, { + role: 'initiator', + direction: 'incoming', + creator: creator + }); +}; +exports.toOutgoingMediaJSONAnswer = function (sdp, creator) { + return toJSON.toMediaJSON(sdp, { + role: 'responder', + direction: 'outgoing', + creator: creator + }); +}; +exports.toCandidateJSON = toJSON.toCandidateJSON; +exports.toMediaJSON = toJSON.toMediaJSON; +exports.toSessionJSON = toJSON.toSessionJSON; + +},{"./lib/tojson":28,"./lib/tosdp":27}],17:[function(require,module,exports){ // getScreenMedia helper by @HenrikJoreteg var getUserMedia = require('getusermedia'); @@ -7383,7 +8476,7 @@ module.exports = function (constraints, cb) { // "known" crash in chrome 34 and 35 on linux if (window.navigator.userAgent.match('Linux')) maxver = 35; - // check that the extension is installed by looking for a + // check that the extension is installed by looking for a // sessionStorage variable that contains the extension id // this has to be set after installation unless the contest // script does that @@ -7396,7 +8489,7 @@ module.exports = function (constraints, cb) { error.name = 'PERMISSION_DENIED'; callback(error); } else { - var constraints = constraints || {audio: false, video: { + constraints = (hasConstraints && constraints) || {audio: false, video: { mandatory: { chromeMediaSource: 'desktop', maxWidth: window.screen.width, @@ -7413,6 +8506,30 @@ module.exports = function (constraints, cb) { } } ); + } else if (window.cefGetScreenMedia) { + //window.cefGetScreenMedia is experimental - may be removed without notice + window.cefGetScreenMedia(function(sourceId) { + if (!sourceId) { + var error = new Error('cefGetScreenMediaError'); + error.name = 'CEF_GETSCREENMEDIA_CANCELED'; + callback(error); + } else { + constraints = (hasConstraints && constraints) || {audio: false, video: { + mandatory: { + chromeMediaSource: 'desktop', + maxWidth: window.screen.width, + maxHeight: window.screen.height, + maxFrameRate: 3 + }, + optional: [ + {googLeakyBucket: true}, + {googTemporalLayeredScreencast: true} + ] + }}; + constraints.video.mandatory.chromeMediaSourceId = sourceId; + getUserMedia(constraints, callback); + } + }); } else if (isCef || (chromever >= 26 && chromever <= maxver)) { // chrome 26 - chrome 33 way to do it -- requires bad chrome://flags // note: this is basically in maintenance mode and will go away soon @@ -7506,7 +8623,54 @@ window.addEventListener('message', function (event) { } }); -},{"getusermedia":16}],22:[function(require,module,exports){ +},{"getusermedia":16}],19:[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.addTrack(this.outputStream.getAudioTracks()[0]); + stream.removeTrack(stream.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":5}],23:[function(require,module,exports){ /** * lodash 3.0.3 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -7570,54 +8734,7 @@ var forEach = createForEach(arrayEach, baseEach); module.exports = forEach; -},{"lodash._arrayeach":30,"lodash._baseeach":28,"lodash._bindcallback":29,"lodash.isarray":27}],19:[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.addTrack(this.outputStream.getAudioTracks()[0]); - stream.removeTrack(stream.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":5}],23:[function(require,module,exports){ +},{"lodash._arrayeach":29,"lodash._baseeach":30,"lodash._bindcallback":31,"lodash.isarray":32}],24:[function(require,module,exports){ /** * lodash 3.1.2 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -7776,540 +8893,89 @@ function property(path) { module.exports = pluck; -},{"lodash._baseget":33,"lodash._topath":34,"lodash.isarray":31,"lodash.map":32}],31:[function(require,module,exports){ -/** - * lodash 3.0.3 (Custom Build) - * Build: `lodash modern modularize exports="npm" -o ./` - * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.8.3 - * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - * Available under MIT license - */ +},{"lodash._baseget":33,"lodash._topath":34,"lodash.isarray":35,"lodash.map":36}],27:[function(require,module,exports){ +var SENDERS = require('./senders'); -/** `Object#toString` result references. */ -var arrayTag = '[object Array]', - funcTag = '[object Function]'; -/** - * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special). - * In addition to special characters the forward slash is escaped to allow for - * easier `eval` use and `Function` compilation. - */ -var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g, - reHasRegExpChars = RegExp(reRegExpChars.source); +exports.toSessionSDP = function (session, opts) { + var role = opts.role || 'initiator'; + var direction = opts.direction || 'outgoing'; + var sid = opts.sid || session.sid || Date.now(); + var time = opts.time || Date.now(); -/** Used to detect host constructors (Safari > 5). */ -var reIsHostCtor = /^\[object .+?Constructor\]$/; + var sdp = [ + 'v=0', + 'o=- ' + sid + ' ' + time + ' IN IP4 0.0.0.0', + 's=-', + 't=0 0', + 'a=msid-semantic: WMS *' + ]; -/** - * Converts `value` to a string if it's not one. An empty string is returned - * for `null` or `undefined` values. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ -function baseToString(value) { - if (typeof value == 'string') { - return value; - } - return value == null ? '' : (value + ''); -} + var groups = session.groups || []; + groups.forEach(function (group) { + sdp.push('a=group:' + group.semantics + ' ' + group.contents.join(' ')); + }); -/** - * Checks if `value` is object-like. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - */ -function isObjectLike(value) { - return !!value && typeof value == 'object'; -} + var contents = session.contents || []; + contents.forEach(function (content) { + sdp.push(exports.toMediaSDP(content, opts)); + }); -/** Used for native method references. */ -var objectProto = Object.prototype; + return sdp.join('\r\n') + '\r\n'; +}; -/** Used to resolve the decompiled source of functions. */ -var fnToString = Function.prototype.toString; +exports.toMediaSDP = function (content, opts) { + var sdp = []; -/** Used to check objects for own properties. */ -var hasOwnProperty = objectProto.hasOwnProperty; + var role = opts.role || 'initiator'; + var direction = opts.direction || 'outgoing'; -/** - * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring) - * of values. - */ -var objToString = objectProto.toString; + var desc = content.description; + var transport = content.transport; + var payloads = desc.payloads || []; + var fingerprints = (transport && transport.fingerprints) || []; -/** Used to detect if a method is native. */ -var reIsNative = RegExp('^' + - escapeRegExp(fnToString.call(hasOwnProperty)) - .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' -); + var mline = []; + if (desc.descType == 'datachannel') { + mline.push('application'); + mline.push('1'); + mline.push('DTLS/SCTP'); + if (transport.sctp) { + transport.sctp.forEach(function (map) { + mline.push(map.number); + }); + } + } else { + mline.push(desc.media); + mline.push('1'); + if ((desc.encryption && desc.encryption.length > 0) || (fingerprints.length > 0)) { + mline.push('RTP/SAVPF'); + } else { + mline.push('RTP/AVPF'); + } + payloads.forEach(function (payload) { + mline.push(payload.id); + }); + } -/* Native method references for those with the same name as other `lodash` methods. */ -var nativeIsArray = getNative(Array, 'isArray'); -/** - * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer) - * of an array-like value. - */ -var MAX_SAFE_INTEGER = 9007199254740991; + sdp.push('m=' + mline.join(' ')); -/** - * Gets the native function at `key` of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {string} key The key of the method to get. - * @returns {*} Returns the function if it's native, else `undefined`. - */ -function getNative(object, key) { - var value = object == null ? undefined : object[key]; - return isNative(value) ? value : undefined; -} + sdp.push('c=IN IP4 0.0.0.0'); + if (desc.bandwidth && desc.bandwidth.type && desc.bandwidth.bandwidth) { + sdp.push('b=' + desc.bandwidth.type + ':' + desc.bandwidth.bandwidth); + } + if (desc.descType == 'rtp') { + sdp.push('a=rtcp:1 IN IP4 0.0.0.0'); + } -/** - * Checks if `value` is a valid array-like length. - * - * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength). - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. - */ -function isLength(value) { - return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; -} - -/** - * Checks if `value` is classified as an `Array` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isArray([1, 2, 3]); - * // => true - * - * _.isArray(function() { return arguments; }()); - * // => false - */ -var isArray = nativeIsArray || function(value) { - return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; -}; - -/** - * Checks if `value` is a native function. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, else `false`. - * @example - * - * _.isNative(Array.prototype.push); - * // => true - * - * _.isNative(_); - * // => false - */ -function isNative(value) { - if (value == null) { - return false; - } - if (objToString.call(value) == funcTag) { - return reIsNative.test(fnToString.call(value)); - } - return isObjectLike(value) && reIsHostCtor.test(value); -} - -/** - * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?", - * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escapeRegExp('[lodash](https://lodash.com/)'); - * // => '\[lodash\]\(https:\/\/lodash\.com\/\)' - */ -function escapeRegExp(string) { - string = baseToString(string); - return (string && reHasRegExpChars.test(string)) - ? string.replace(reRegExpChars, '\\$&') - : string; -} - -module.exports = isArray; - -},{}],27:[function(require,module,exports){ -/** - * lodash 3.0.3 (Custom Build) - * Build: `lodash modern modularize exports="npm" -o ./` - * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.8.3 - * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - * Available under MIT license - */ - -/** `Object#toString` result references. */ -var arrayTag = '[object Array]', - funcTag = '[object Function]'; - -/** - * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special). - * In addition to special characters the forward slash is escaped to allow for - * easier `eval` use and `Function` compilation. - */ -var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g, - reHasRegExpChars = RegExp(reRegExpChars.source); - -/** Used to detect host constructors (Safari > 5). */ -var reIsHostCtor = /^\[object .+?Constructor\]$/; - -/** - * Converts `value` to a string if it's not one. An empty string is returned - * for `null` or `undefined` values. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ -function baseToString(value) { - if (typeof value == 'string') { - return value; - } - return value == null ? '' : (value + ''); -} - -/** - * Checks if `value` is object-like. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - */ -function isObjectLike(value) { - return !!value && typeof value == 'object'; -} - -/** Used for native method references. */ -var objectProto = Object.prototype; - -/** Used to resolve the decompiled source of functions. */ -var fnToString = Function.prototype.toString; - -/** Used to check objects for own properties. */ -var hasOwnProperty = objectProto.hasOwnProperty; - -/** - * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring) - * of values. - */ -var objToString = objectProto.toString; - -/** Used to detect if a method is native. */ -var reIsNative = RegExp('^' + - escapeRegExp(fnToString.call(hasOwnProperty)) - .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' -); - -/* Native method references for those with the same name as other `lodash` methods. */ -var nativeIsArray = getNative(Array, 'isArray'); - -/** - * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer) - * of an array-like value. - */ -var MAX_SAFE_INTEGER = 9007199254740991; - -/** - * Gets the native function at `key` of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {string} key The key of the method to get. - * @returns {*} Returns the function if it's native, else `undefined`. - */ -function getNative(object, key) { - var value = object == null ? undefined : object[key]; - return isNative(value) ? value : undefined; -} - -/** - * Checks if `value` is a valid array-like length. - * - * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength). - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. - */ -function isLength(value) { - return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; -} - -/** - * Checks if `value` is classified as an `Array` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isArray([1, 2, 3]); - * // => true - * - * _.isArray(function() { return arguments; }()); - * // => false - */ -var isArray = nativeIsArray || function(value) { - return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; -}; - -/** - * Checks if `value` is a native function. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, else `false`. - * @example - * - * _.isNative(Array.prototype.push); - * // => true - * - * _.isNative(_); - * // => false - */ -function isNative(value) { - if (value == null) { - return false; - } - if (objToString.call(value) == funcTag) { - return reIsNative.test(fnToString.call(value)); - } - return isObjectLike(value) && reIsHostCtor.test(value); -} - -/** - * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?", - * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escapeRegExp('[lodash](https://lodash.com/)'); - * // => '\[lodash\]\(https:\/\/lodash\.com\/\)' - */ -function escapeRegExp(string) { - string = baseToString(string); - return (string && reHasRegExpChars.test(string)) - ? string.replace(reRegExpChars, '\\$&') - : string; -} - -module.exports = isArray; - -},{}],30:[function(require,module,exports){ -/** - * lodash 3.0.0 (Custom Build) - * Build: `lodash modern modularize exports="npm" -o ./` - * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.7.0 - * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - * Available under MIT license - */ - -/** - * A specialized version of `_.forEach` for arrays without support for callback - * shorthands or `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns `array`. - */ -function arrayEach(array, iteratee) { - var index = -1, - length = array.length; - - while (++index < length) { - if (iteratee(array[index], index, array) === false) { - break; - } - } - return array; -} - -module.exports = arrayEach; - -},{}],29:[function(require,module,exports){ -/** - * lodash 3.0.1 (Custom Build) - * Build: `lodash modern modularize exports="npm" -o ./` - * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.8.3 - * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - * Available under MIT license - */ - -/** - * A specialized version of `baseCallback` which only supports `this` binding - * and specifying the number of arguments to provide to `func`. - * - * @private - * @param {Function} func The function to bind. - * @param {*} thisArg The `this` binding of `func`. - * @param {number} [argCount] The number of arguments to provide to `func`. - * @returns {Function} Returns the callback. - */ -function bindCallback(func, thisArg, argCount) { - if (typeof func != 'function') { - return identity; - } - if (thisArg === undefined) { - return func; - } - switch (argCount) { - case 1: return function(value) { - return func.call(thisArg, value); - }; - case 3: return function(value, index, collection) { - return func.call(thisArg, value, index, collection); - }; - case 4: return function(accumulator, value, index, collection) { - return func.call(thisArg, accumulator, value, index, collection); - }; - case 5: return function(value, other, key, object, source) { - return func.call(thisArg, value, other, key, object, source); - }; - } - return function() { - return func.apply(thisArg, arguments); - }; -} - -/** - * This method returns the first argument provided to it. - * - * @static - * @memberOf _ - * @category Utility - * @param {*} value Any value. - * @returns {*} Returns `value`. - * @example - * - * var object = { 'user': 'fred' }; - * - * _.identity(object) === object; - * // => true - */ -function identity(value) { - return value; -} - -module.exports = bindCallback; - -},{}],25:[function(require,module,exports){ -var SENDERS = require('./senders'); - - -exports.toSessionSDP = function (session, opts) { - var role = opts.role || 'initiator'; - var direction = opts.direction || 'outgoing'; - var sid = opts.sid || session.sid || Date.now(); - var time = opts.time || Date.now(); - - var sdp = [ - 'v=0', - 'o=- ' + sid + ' ' + time + ' IN IP4 0.0.0.0', - 's=-', - 't=0 0' - ]; - - var groups = session.groups || []; - groups.forEach(function (group) { - sdp.push('a=group:' + group.semantics + ' ' + group.contents.join(' ')); - }); - - var contents = session.contents || []; - contents.forEach(function (content) { - sdp.push(exports.toMediaSDP(content, opts)); - }); - - return sdp.join('\r\n') + '\r\n'; -}; - -exports.toMediaSDP = function (content, opts) { - var sdp = []; - - var role = opts.role || 'initiator'; - var direction = opts.direction || 'outgoing'; - - var desc = content.description; - var transport = content.transport; - var payloads = desc.payloads || []; - var fingerprints = (transport && transport.fingerprints) || []; - - var mline = []; - if (desc.descType == 'datachannel') { - mline.push('application'); - mline.push('1'); - mline.push('DTLS/SCTP'); - if (transport.sctp) { - transport.sctp.forEach(function (map) { - mline.push(map.number); - }); - } - } else { - mline.push(desc.media); - mline.push('1'); - if ((desc.encryption && desc.encryption.length > 0) || (fingerprints.length > 0)) { - mline.push('RTP/SAVPF'); - } else { - mline.push('RTP/AVPF'); - } - payloads.forEach(function (payload) { - mline.push(payload.id); - }); - } - - - sdp.push('m=' + mline.join(' ')); - - sdp.push('c=IN IP4 0.0.0.0'); - if (desc.bandwidth && desc.bandwidth.type && desc.bandwidth.bandwidth) { - sdp.push('b=' + desc.bandwidth.type + ':' + desc.bandwidth.bandwidth); - } - if (desc.descType == 'rtp') { - sdp.push('a=rtcp:1 IN IP4 0.0.0.0'); - } - - if (transport) { - if (transport.ufrag) { - sdp.push('a=ice-ufrag:' + transport.ufrag); - } - if (transport.pwd) { - sdp.push('a=ice-pwd:' + transport.pwd); - } + if (transport) { + if (transport.ufrag) { + sdp.push('a=ice-ufrag:' + transport.ufrag); + } + if (transport.pwd) { + sdp.push('a=ice-pwd:' + transport.pwd); + } var pushedSetup = false; fingerprints.forEach(function (fingerprint) { @@ -8331,6 +8997,14 @@ exports.toMediaSDP = function (content, opts) { } sdp.push('a=mid:' + content.name); + if (desc.sources && desc.sources.length) { + (desc.sources[0].parameters || []).forEach(function (param) { + if (param.key === 'msid') { + sdp.push('a=msid:' + param.value); + } + }); + } + if (desc.mux) { sdp.push('a=rtcp-mux'); } @@ -8444,7 +9118,7 @@ exports.toCandidateSDP = function (candidate) { return 'a=candidate:' + sdp.join(' '); }; -},{"./senders":35}],26:[function(require,module,exports){ +},{"./senders":37}],28:[function(require,module,exports){ var SENDERS = require('./senders'); var parsers = require('./parsers'); var idCounter = Math.random(); @@ -8602,7 +9276,25 @@ exports.toMediaJSON = function (media, session, opts) { desc.sourceGroups = parsers.sourceGroups(ssrcGroupLines || []); var ssrcLines = parsers.findLines('a=ssrc:', lines); - desc.sources = parsers.sources(ssrcLines || []); + var sources = desc.sources = parsers.sources(ssrcLines || []); + + var msidLine = parsers.findLine('a=msid:', lines); + if (msidLine) { + var msid = parsers.msid(msidLine); + ['msid', 'mslabel', 'label'].forEach(function (key) { + for (var i = 0; i < sources.length; i++) { + var found = false; + for (var j = 0; j < sources[i].parameters.length; j++) { + if (sources[i].parameters[j].key === key) { + found = true; + } + } + if (!found) { + sources[i].parameters.push({ key: key, value: msid[key] }); + } + } + }); + } if (parsers.findLine('a=x-google-flag:conference', lines, sessionLines)) { desc.googConferenceFlag = true; @@ -8620,37 +9312,319 @@ exports.toMediaJSON = function (media, session, opts) { trans.fingerprints.push(fp); }); - var ufragLine = parsers.findLine('a=ice-ufrag:', lines, sessionLines); - var pwdLine = parsers.findLine('a=ice-pwd:', lines, sessionLines); - if (ufragLine && pwdLine) { - trans.ufrag = ufragLine.substr(12); - trans.pwd = pwdLine.substr(10); - trans.candidates = []; + var ufragLine = parsers.findLine('a=ice-ufrag:', lines, sessionLines); + var pwdLine = parsers.findLine('a=ice-pwd:', lines, sessionLines); + if (ufragLine && pwdLine) { + trans.ufrag = ufragLine.substr(12); + trans.pwd = pwdLine.substr(10); + trans.candidates = []; + + var candidateLines = parsers.findLines('a=candidate:', lines, sessionLines); + candidateLines.forEach(function (line) { + trans.candidates.push(exports.toCandidateJSON(line)); + }); + } + + if (desc.descType == 'datachannel') { + var sctpmapLines = parsers.findLines('a=sctpmap:', lines); + sctpmapLines.forEach(function (line) { + var sctp = parsers.sctpmap(line); + trans.sctp.push(sctp); + }); + } + + return content; +}; + +exports.toCandidateJSON = function (line) { + var candidate = parsers.candidate(line.split('\r\n')[0]); + candidate.id = (idCounter++).toString(36).substr(0, 12); + return candidate; +}; + +},{"./parsers":38,"./senders":37}],29:[function(require,module,exports){ +/** + * lodash 3.0.0 (Custom Build) + * Build: `lodash modern modularize exports="npm" -o ./` + * Copyright 2012-2015 The Dojo Foundation + * Based on Underscore.js 1.7.0 + * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ + +/** + * A specialized version of `_.forEach` for arrays without support for callback + * shorthands or `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ +function arrayEach(array, iteratee) { + var index = -1, + length = array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; +} + +module.exports = arrayEach; + +},{}],31:[function(require,module,exports){ +/** + * lodash 3.0.1 (Custom Build) + * Build: `lodash modern modularize exports="npm" -o ./` + * Copyright 2012-2015 The Dojo Foundation + * Based on Underscore.js 1.8.3 + * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ + +/** + * A specialized version of `baseCallback` which only supports `this` binding + * and specifying the number of arguments to provide to `func`. + * + * @private + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {number} [argCount] The number of arguments to provide to `func`. + * @returns {Function} Returns the callback. + */ +function bindCallback(func, thisArg, argCount) { + if (typeof func != 'function') { + return identity; + } + if (thisArg === undefined) { + return func; + } + switch (argCount) { + case 1: return function(value) { + return func.call(thisArg, value); + }; + case 3: return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + case 5: return function(value, other, key, object, source) { + return func.call(thisArg, value, other, key, object, source); + }; + } + return function() { + return func.apply(thisArg, arguments); + }; +} + +/** + * This method returns the first argument provided to it. + * + * @static + * @memberOf _ + * @category Utility + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'user': 'fred' }; + * + * _.identity(object) === object; + * // => true + */ +function identity(value) { + return value; +} + +module.exports = bindCallback; + +},{}],32:[function(require,module,exports){ +/** + * lodash 3.0.4 (Custom Build) + * Build: `lodash modern modularize exports="npm" -o ./` + * Copyright 2012-2015 The Dojo Foundation + * Based on Underscore.js 1.8.3 + * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ + +/** `Object#toString` result references. */ +var arrayTag = '[object Array]', + funcTag = '[object Function]'; + +/** Used to detect host constructors (Safari > 5). */ +var reIsHostCtor = /^\[object .+?Constructor\]$/; + +/** + * Checks if `value` is object-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** Used for native method references. */ +var objectProto = Object.prototype; + +/** Used to resolve the decompiled source of functions. */ +var fnToString = Function.prototype.toString; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ +var objToString = objectProto.toString; + +/** Used to detect if a method is native. */ +var reIsNative = RegExp('^' + + fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' +); + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeIsArray = getNative(Array, 'isArray'); - var candidateLines = parsers.findLines('a=candidate:', lines, sessionLines); - candidateLines.forEach(function (line) { - trans.candidates.push(exports.toCandidateJSON(line)); - }); - } +/** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */ +var MAX_SAFE_INTEGER = 9007199254740991; - if (desc.descType == 'datachannel') { - var sctpmapLines = parsers.findLines('a=sctpmap:', lines); - sctpmapLines.forEach(function (line) { - var sctp = parsers.sctpmap(line); - trans.sctp.push(sctp); - }); - } +/** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ +function getNative(object, key) { + var value = object == null ? undefined : object[key]; + return isNative(value) ? value : undefined; +} - return content; -}; +/** + * Checks if `value` is a valid array-like length. + * + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + */ +function isLength(value) { + return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; +} -exports.toCandidateJSON = function (line) { - var candidate = parsers.candidate(line.split('\r\n')[0]); - candidate.id = (idCounter++).toString(36).substr(0, 12); - return candidate; +/** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(function() { return arguments; }()); + * // => false + */ +var isArray = nativeIsArray || function(value) { + return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; }; -},{"./parsers":36,"./senders":35}],33:[function(require,module,exports){ +/** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ +function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in older versions of Chrome and Safari which return 'function' for regexes + // and Safari 8 equivalents which return 'object' for typed array constructors. + return isObject(value) && objToString.call(value) == funcTag; +} + +/** + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */ +function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); +} + +/** + * Checks if `value` is a native function. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ +function isNative(value) { + if (value == null) { + return false; + } + if (isFunction(value)) { + return reIsNative.test(fnToString.call(value)); + } + return isObjectLike(value) && reIsHostCtor.test(value); +} + +module.exports = isArray; + +},{}],33:[function(require,module,exports){ /** * lodash 3.7.2 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -8687,14 +9661,170 @@ function baseGet(object, path, pathKey) { } /** - * Converts `value` to an object if it's not one. + * Converts `value` to an object if it's not one. + * + * @private + * @param {*} value The value to process. + * @returns {Object} Returns the object. + */ +function toObject(value) { + return isObject(value) ? value : Object(value); +} + +/** + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */ +function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); +} + +module.exports = baseGet; + +},{}],35:[function(require,module,exports){ +/** + * lodash 3.0.4 (Custom Build) + * Build: `lodash modern modularize exports="npm" -o ./` + * Copyright 2012-2015 The Dojo Foundation + * Based on Underscore.js 1.8.3 + * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ + +/** `Object#toString` result references. */ +var arrayTag = '[object Array]', + funcTag = '[object Function]'; + +/** Used to detect host constructors (Safari > 5). */ +var reIsHostCtor = /^\[object .+?Constructor\]$/; + +/** + * Checks if `value` is object-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** Used for native method references. */ +var objectProto = Object.prototype; + +/** Used to resolve the decompiled source of functions. */ +var fnToString = Function.prototype.toString; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ +var objToString = objectProto.toString; + +/** Used to detect if a method is native. */ +var reIsNative = RegExp('^' + + fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' +); + +/* Native method references for those with the same name as other `lodash` methods. */ +var nativeIsArray = getNative(Array, 'isArray'); + +/** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */ +var MAX_SAFE_INTEGER = 9007199254740991; + +/** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ +function getNative(object, key) { + var value = object == null ? undefined : object[key]; + return isNative(value) ? value : undefined; +} + +/** + * Checks if `value` is a valid array-like length. + * + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + */ +function isLength(value) { + return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; +} + +/** + * Checks if `value` is classified as an `Array` object. * - * @private - * @param {*} value The value to process. - * @returns {Object} Returns the object. + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(function() { return arguments; }()); + * // => false */ -function toObject(value) { - return isObject(value) ? value : Object(value); +var isArray = nativeIsArray || function(value) { + return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; +}; + +/** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ +function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in older versions of Chrome and Safari which return 'function' for regexes + // and Safari 8 equivalents which return 'object' for typed array constructors. + return isObject(value) && objToString.call(value) == funcTag; } /** @@ -8724,246 +9854,306 @@ function isObject(value) { return !!value && (type == 'object' || type == 'function'); } -module.exports = baseGet; +/** + * Checks if `value` is a native function. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ +function isNative(value) { + if (value == null) { + return false; + } + if (isFunction(value)) { + return reIsNative.test(fnToString.call(value)); + } + return isObjectLike(value) && reIsHostCtor.test(value); +} -},{}],21:[function(require,module,exports){ -// based on https://github.com/ESTOS/strophe.jingle/ -// adds wildemitter support -var util = require('util'); -var webrtc = require('webrtcsupport'); -var WildEmitter = require('wildemitter'); +module.exports = isArray; -function dumpSDP(description) { - return { - type: description.type, - sdp: description.sdp - }; -} +},{}],38:[function(require,module,exports){ +exports.lines = function (sdp) { + return sdp.split('\r\n').filter(function (line) { + return line.length > 0; + }); +}; -function dumpStream(stream) { - var info = { - label: stream.id, - }; - if (stream.getAudioTracks().length) { - info.audio = stream.getAudioTracks().map(function (track) { - return track.id; - }); +exports.findLine = function (prefix, mediaLines, sessionLines) { + var prefixLength = prefix.length; + for (var i = 0; i < mediaLines.length; i++) { + if (mediaLines[i].substr(0, prefixLength) === prefix) { + return mediaLines[i]; + } } - if (stream.getVideoTracks().length) { - info.video = stream.getVideoTracks().map(function (track) { - return track.id; - }); + // Continue searching in parent session section + if (!sessionLines) { + return false; } - return info; -} - -function TraceablePeerConnection(config, constraints) { - var self = this; - WildEmitter.call(this); - this.peerconnection = new webrtc.PeerConnection(config, constraints); + for (var j = 0; j < sessionLines.length; j++) { + if (sessionLines[j].substr(0, prefixLength) === prefix) { + return sessionLines[j]; + } + } - this.trace = function (what, info) { - self.emit('PeerConnectionTrace', { - time: new Date(), - type: what, - value: info || "" - }); - }; + return false; +}; - this.onicecandidate = null; - this.peerconnection.onicecandidate = function (event) { - self.trace('onicecandidate', event.candidate); - if (self.onicecandidate !== null) { - self.onicecandidate(event); - } - }; - this.onaddstream = null; - this.peerconnection.onaddstream = function (event) { - self.trace('onaddstream', dumpStream(event.stream)); - if (self.onaddstream !== null) { - self.onaddstream(event); +exports.findLines = function (prefix, mediaLines, sessionLines) { + var results = []; + var prefixLength = prefix.length; + for (var i = 0; i < mediaLines.length; i++) { + if (mediaLines[i].substr(0, prefixLength) === prefix) { + results.push(mediaLines[i]); } - }; - this.onremovestream = null; - this.peerconnection.onremovestream = function (event) { - self.trace('onremovestream', dumpStream(event.stream)); - if (self.onremovestream !== null) { - self.onremovestream(event); + } + if (results.length || !sessionLines) { + return results; + } + for (var j = 0; j < sessionLines.length; j++) { + if (sessionLines[j].substr(0, prefixLength) === prefix) { + results.push(sessionLines[j]); } + } + return results; +}; + +exports.mline = function (line) { + var parts = line.substr(2).split(' '); + var parsed = { + media: parts[0], + port: parts[1], + proto: parts[2], + formats: [] }; - this.onsignalingstatechange = null; - this.peerconnection.onsignalingstatechange = function (event) { - self.trace('onsignalingstatechange', self.signalingState); - if (self.onsignalingstatechange !== null) { - self.onsignalingstatechange(event); + for (var i = 3; i < parts.length; i++) { + if (parts[i]) { + parsed.formats.push(parts[i]); } + } + return parsed; +}; + +exports.rtpmap = function (line) { + var parts = line.substr(9).split(' '); + var parsed = { + id: parts.shift() }; - this.oniceconnectionstatechange = null; - this.peerconnection.oniceconnectionstatechange = function (event) { - self.trace('oniceconnectionstatechange', self.iceConnectionState); - if (self.oniceconnectionstatechange !== null) { - self.oniceconnectionstatechange(event); - } + + parts = parts[0].split('/'); + + parsed.name = parts[0]; + parsed.clockrate = parts[1]; + parsed.channels = parts.length == 3 ? parts[2] : '1'; + return parsed; +}; + +exports.sctpmap = function (line) { + // based on -05 draft + var parts = line.substr(10).split(' '); + var parsed = { + number: parts.shift(), + protocol: parts.shift(), + streams: parts.shift() }; - this.onnegotiationneeded = null; - this.peerconnection.onnegotiationneeded = function (event) { - self.trace('onnegotiationneeded'); - if (self.onnegotiationneeded !== null) { - self.onnegotiationneeded(event); + return parsed; +}; + + +exports.fmtp = function (line) { + var kv, key, value; + var parts = line.substr(line.indexOf(' ') + 1).split(';'); + var parsed = []; + for (var i = 0; i < parts.length; i++) { + kv = parts[i].split('='); + key = kv[0].trim(); + value = kv[1]; + if (key && value) { + parsed.push({key: key, value: value}); + } else if (key) { + parsed.push({key: '', value: key}); } + } + return parsed; +}; + +exports.crypto = function (line) { + var parts = line.substr(9).split(' '); + var parsed = { + tag: parts[0], + cipherSuite: parts[1], + keyParams: parts[2], + sessionParams: parts.slice(3).join(' ') }; - self.ondatachannel = null; - this.peerconnection.ondatachannel = function (event) { - self.trace('ondatachannel', event); - if (self.ondatachannel !== null) { - self.ondatachannel(event); - } + return parsed; +}; + +exports.fingerprint = function (line) { + var parts = line.substr(14).split(' '); + return { + hash: parts[0], + value: parts[1] }; - this.getLocalStreams = this.peerconnection.getLocalStreams.bind(this.peerconnection); - this.getRemoteStreams = this.peerconnection.getRemoteStreams.bind(this.peerconnection); -} +}; -util.inherits(TraceablePeerConnection, WildEmitter); +exports.extmap = function (line) { + var parts = line.substr(9).split(' '); + var parsed = {}; -Object.defineProperty(TraceablePeerConnection.prototype, 'signalingState', { - get: function () { - return this.peerconnection.signalingState; + var idpart = parts.shift(); + var sp = idpart.indexOf('/'); + if (sp >= 0) { + parsed.id = idpart.substr(0, sp); + parsed.senders = idpart.substr(sp + 1); + } else { + parsed.id = idpart; + parsed.senders = 'sendrecv'; } -}); -Object.defineProperty(TraceablePeerConnection.prototype, 'iceConnectionState', { - get: function () { - return this.peerconnection.iceConnectionState; + parsed.uri = parts.shift() || ''; + + return parsed; +}; + +exports.rtcpfb = function (line) { + var parts = line.substr(10).split(' '); + var parsed = {}; + parsed.id = parts.shift(); + parsed.type = parts.shift(); + if (parsed.type === 'trr-int') { + parsed.value = parts.shift(); + } else { + parsed.subtype = parts.shift() || ''; } -}); + parsed.parameters = parts; + return parsed; +}; -Object.defineProperty(TraceablePeerConnection.prototype, 'localDescription', { - get: function () { - return this.peerconnection.localDescription; +exports.candidate = function (line) { + var parts; + if (line.indexOf('a=candidate:') === 0) { + parts = line.substring(12).split(' '); + } else { // no a=candidate + parts = line.substring(10).split(' '); } -}); -Object.defineProperty(TraceablePeerConnection.prototype, 'remoteDescription', { - get: function () { - return this.peerconnection.remoteDescription; + var candidate = { + foundation: parts[0], + component: parts[1], + protocol: parts[2].toLowerCase(), + priority: parts[3], + ip: parts[4], + port: parts[5], + // skip parts[6] == 'typ' + type: parts[7], + generation: '0' + }; + + for (var i = 8; i < parts.length; i += 2) { + if (parts[i] === 'raddr') { + candidate.relAddr = parts[i + 1]; + } else if (parts[i] === 'rport') { + candidate.relPort = parts[i + 1]; + } else if (parts[i] === 'generation') { + candidate.generation = parts[i + 1]; + } else if (parts[i] === 'tcptype') { + candidate.tcpType = parts[i + 1]; + } } -}); -TraceablePeerConnection.prototype.addStream = function (stream) { - this.trace('addStream', dumpStream(stream)); - this.peerconnection.addStream(stream); -}; + candidate.network = '1'; -TraceablePeerConnection.prototype.removeStream = function (stream) { - this.trace('removeStream', dumpStream(stream)); - this.peerconnection.removeStream(stream); + return candidate; }; -TraceablePeerConnection.prototype.createDataChannel = function (label, opts) { - this.trace('createDataChannel', label, opts); - return this.peerconnection.createDataChannel(label, opts); +exports.sourceGroups = function (lines) { + var parsed = []; + for (var i = 0; i < lines.length; i++) { + var parts = lines[i].substr(13).split(' '); + parsed.push({ + semantics: parts.shift(), + sources: parts + }); + } + return parsed; }; -TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) { - var self = this; - this.trace('setLocalDescription', dumpSDP(description)); - this.peerconnection.setLocalDescription(description, - function () { - self.trace('setLocalDescriptionOnSuccess'); - successCallback(); - }, - function (err) { - self.trace('setLocalDescriptionOnFailure', err); - failureCallback(err); - } - ); -}; +exports.sources = function (lines) { + // http://tools.ietf.org/html/rfc5576 + var parsed = []; + var sources = {}; + for (var i = 0; i < lines.length; i++) { + var parts = lines[i].substr(7).split(' '); + var ssrc = parts.shift(); -TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) { - var self = this; - this.trace('setRemoteDescription', dumpSDP(description)); - this.peerconnection.setRemoteDescription(description, - function () { - self.trace('setRemoteDescriptionOnSuccess'); - successCallback(); - }, - function (err) { - self.trace('setRemoteDescriptionOnFailure', err); - failureCallback(err); + if (!sources[ssrc]) { + var source = { + ssrc: ssrc, + parameters: [] + }; + parsed.push(source); + + // Keep an index + sources[ssrc] = source; } - ); -}; -TraceablePeerConnection.prototype.close = function () { - this.trace('stop'); - if (this.statsinterval !== null) { - window.clearInterval(this.statsinterval); - this.statsinterval = null; - } - if (this.peerconnection.signalingState != 'closed') { - this.peerconnection.close(); + parts = parts.join(' ').split(':'); + var attribute = parts.shift(); + var value = parts.join(':') || null; + + sources[ssrc].parameters.push({ + key: attribute, + value: value + }); } -}; -TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) { - var self = this; - this.trace('createOffer', constraints); - this.peerconnection.createOffer( - function (offer) { - self.trace('createOfferOnSuccess', dumpSDP(offer)); - successCallback(offer); - }, - function (err) { - self.trace('createOfferOnFailure', err); - failureCallback(err); - }, - constraints - ); + return parsed; }; -TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) { - var self = this; - this.trace('createAnswer', constraints); - this.peerconnection.createAnswer( - function (answer) { - self.trace('createAnswerOnSuccess', dumpSDP(answer)); - successCallback(answer); - }, - function (err) { - self.trace('createAnswerOnFailure', err); - failureCallback(err); - }, - constraints - ); +exports.groups = function (lines) { + // http://tools.ietf.org/html/rfc5888 + var parsed = []; + var parts; + for (var i = 0; i < lines.length; i++) { + parts = lines[i].substr(8).split(' '); + parsed.push({ + semantics: parts.shift(), + contents: parts + }); + } + return parsed; }; -TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) { - var self = this; - this.trace('addIceCandidate', candidate); - this.peerconnection.addIceCandidate(candidate, - function () { - //self.trace('addIceCandidateOnSuccess'); - if (successCallback) successCallback(); - }, - function (err) { - self.trace('addIceCandidateOnFailure', err); - if (failureCallback) failureCallback(err); - } - ); +exports.bandwidth = function (line) { + var parts = line.substr(2).split(':'); + var parsed = {}; + parsed.type = parts.shift(); + parsed.bandwidth = parts.shift(); + return parsed; }; -TraceablePeerConnection.prototype.getStats = function (callback, errback) { - if (navigator.mozGetUserMedia) { - this.peerconnection.getStats(null, callback, errback); - } else { - this.peerconnection.getStats(callback); - } +exports.msid = function (line) { + var data = line.substr(7); + var parts = data.split(' '); + return { + msid: data, + mslabel: parts[0], + label: parts[1] + }; }; -module.exports = TraceablePeerConnection; - -},{"util":8,"webrtcsupport":5,"wildemitter":4}],35:[function(require,module,exports){ +},{}],37:[function(require,module,exports){ module.exports = { initiator: { incoming: { @@ -8996,283 +10186,235 @@ module.exports = { recvonly: 'responder', sendonly: 'initiator', sendrecv: 'both', - inactive: 'none' - }, - outgoing: { - initiator: 'recvonly', - responder: 'sendonly', - both: 'sendrecv', - none: 'inactive', - recvonly: 'initiator', - sendonly: 'responder', - sendrecv: 'both', - inactive: 'none' - } - } -}; - -},{}],36:[function(require,module,exports){ -exports.lines = function (sdp) { - return sdp.split('\r\n').filter(function (line) { - return line.length > 0; - }); -}; - -exports.findLine = function (prefix, mediaLines, sessionLines) { - var prefixLength = prefix.length; - for (var i = 0; i < mediaLines.length; i++) { - if (mediaLines[i].substr(0, prefixLength) === prefix) { - return mediaLines[i]; - } - } - // Continue searching in parent session section - if (!sessionLines) { - return false; - } - - for (var j = 0; j < sessionLines.length; j++) { - if (sessionLines[j].substr(0, prefixLength) === prefix) { - return sessionLines[j]; - } - } - - return false; -}; - -exports.findLines = function (prefix, mediaLines, sessionLines) { - var results = []; - var prefixLength = prefix.length; - for (var i = 0; i < mediaLines.length; i++) { - if (mediaLines[i].substr(0, prefixLength) === prefix) { - results.push(mediaLines[i]); - } - } - if (results.length || !sessionLines) { - return results; - } - for (var j = 0; j < sessionLines.length; j++) { - if (sessionLines[j].substr(0, prefixLength) === prefix) { - results.push(sessionLines[j]); + inactive: 'none' + }, + outgoing: { + initiator: 'recvonly', + responder: 'sendonly', + both: 'sendrecv', + none: 'inactive', + recvonly: 'initiator', + sendonly: 'responder', + sendrecv: 'both', + inactive: 'none' } } - return results; }; -exports.mline = function (line) { - var parts = line.substr(2).split(' '); - var parsed = { - media: parts[0], - port: parts[1], - proto: parts[2], - formats: [] +},{}],22:[function(require,module,exports){ +// based on https://github.com/ESTOS/strophe.jingle/ +// adds wildemitter support +var util = require('util'); +var adapter = require('webrtc-adapter-test'); +var WildEmitter = require('wildemitter'); + +function dumpSDP(description) { + return { + type: description.type, + sdp: description.sdp }; - for (var i = 3; i < parts.length; i++) { - if (parts[i]) { - parsed.formats.push(parts[i]); - } - } - return parsed; -}; +} -exports.rtpmap = function (line) { - var parts = line.substr(9).split(' '); - var parsed = { - id: parts.shift() +function dumpStream(stream) { + var info = { + label: stream.id, }; + if (stream.getAudioTracks().length) { + info.audio = stream.getAudioTracks().map(function (track) { + return track.id; + }); + } + if (stream.getVideoTracks().length) { + info.video = stream.getVideoTracks().map(function (track) { + return track.id; + }); + } + return info; +} - parts = parts[0].split('/'); +function TraceablePeerConnection(config, constraints) { + var self = this; + WildEmitter.call(this); - parsed.name = parts[0]; - parsed.clockrate = parts[1]; - parsed.channels = parts.length == 3 ? parts[2] : '1'; - return parsed; -}; + this.peerconnection = new window.RTCPeerConnection(config, constraints); -exports.sctpmap = function (line) { - // based on -05 draft - var parts = line.substr(10).split(' '); - var parsed = { - number: parts.shift(), - protocol: parts.shift(), - streams: parts.shift() + this.trace = function (what, info) { + self.emit('PeerConnectionTrace', { + time: new Date(), + type: what, + value: info || "" + }); }; - return parsed; -}; - -exports.fmtp = function (line) { - var kv, key, value; - var parts = line.substr(line.indexOf(' ') + 1).split(';'); - var parsed = []; - for (var i = 0; i < parts.length; i++) { - kv = parts[i].split('='); - key = kv[0].trim(); - value = kv[1]; - if (key && value) { - parsed.push({key: key, value: value}); - } else if (key) { - parsed.push({key: '', value: key}); + this.onicecandidate = null; + this.peerconnection.onicecandidate = function (event) { + self.trace('onicecandidate', event.candidate); + if (self.onicecandidate !== null) { + self.onicecandidate(event); } - } - return parsed; -}; - -exports.crypto = function (line) { - var parts = line.substr(9).split(' '); - var parsed = { - tag: parts[0], - cipherSuite: parts[1], - keyParams: parts[2], - sessionParams: parts.slice(3).join(' ') }; - return parsed; -}; - -exports.fingerprint = function (line) { - var parts = line.substr(14).split(' '); - return { - hash: parts[0], - value: parts[1] + this.onaddstream = null; + this.peerconnection.onaddstream = function (event) { + self.trace('onaddstream', dumpStream(event.stream)); + if (self.onaddstream !== null) { + self.onaddstream(event); + } }; -}; - -exports.extmap = function (line) { - var parts = line.substr(9).split(' '); - var parsed = {}; + this.onremovestream = null; + this.peerconnection.onremovestream = function (event) { + self.trace('onremovestream', dumpStream(event.stream)); + if (self.onremovestream !== null) { + self.onremovestream(event); + } + }; + this.onsignalingstatechange = null; + this.peerconnection.onsignalingstatechange = function (event) { + self.trace('onsignalingstatechange', self.signalingState); + if (self.onsignalingstatechange !== null) { + self.onsignalingstatechange(event); + } + }; + this.oniceconnectionstatechange = null; + this.peerconnection.oniceconnectionstatechange = function (event) { + self.trace('oniceconnectionstatechange', self.iceConnectionState); + if (self.oniceconnectionstatechange !== null) { + self.oniceconnectionstatechange(event); + } + }; + this.onnegotiationneeded = null; + this.peerconnection.onnegotiationneeded = function (event) { + self.trace('onnegotiationneeded'); + if (self.onnegotiationneeded !== null) { + self.onnegotiationneeded(event); + } + }; + self.ondatachannel = null; + this.peerconnection.ondatachannel = function (event) { + self.trace('ondatachannel', event); + if (self.ondatachannel !== null) { + self.ondatachannel(event); + } + }; + this.getLocalStreams = this.peerconnection.getLocalStreams.bind(this.peerconnection); + this.getRemoteStreams = this.peerconnection.getRemoteStreams.bind(this.peerconnection); +} - var idpart = parts.shift(); - var sp = idpart.indexOf('/'); - if (sp >= 0) { - parsed.id = idpart.substr(0, sp); - parsed.senders = idpart.substr(sp + 1); - } else { - parsed.id = idpart; - parsed.senders = 'sendrecv'; - } +util.inherits(TraceablePeerConnection, WildEmitter); - parsed.uri = parts.shift() || ''; +['signalingState', 'iceConnectionState', 'localDescription', 'remoteDescription'].forEach(function (prop) { + Object.defineProperty(TraceablePeerConnection.prototype, prop, { + get: function () { + return this.peerconnection[prop]; + } + }); +}); - return parsed; +TraceablePeerConnection.prototype.addStream = function (stream) { + this.trace('addStream', dumpStream(stream)); + this.peerconnection.addStream(stream); }; -exports.rtcpfb = function (line) { - var parts = line.substr(10).split(' '); - var parsed = {}; - parsed.id = parts.shift(); - parsed.type = parts.shift(); - if (parsed.type === 'trr-int') { - parsed.value = parts.shift(); - } else { - parsed.subtype = parts.shift() || ''; - } - parsed.parameters = parts; - return parsed; +TraceablePeerConnection.prototype.removeStream = function (stream) { + this.trace('removeStream', dumpStream(stream)); + this.peerconnection.removeStream(stream); }; -exports.candidate = function (line) { - var parts; - if (line.indexOf('a=candidate:') === 0) { - parts = line.substring(12).split(' '); - } else { // no a=candidate - parts = line.substring(10).split(' '); - } - - var candidate = { - foundation: parts[0], - component: parts[1], - protocol: parts[2].toLowerCase(), - priority: parts[3], - ip: parts[4], - port: parts[5], - // skip parts[6] == 'typ' - type: parts[7], - generation: '0' - }; +TraceablePeerConnection.prototype.createDataChannel = function (label, opts) { + this.trace('createDataChannel', label, opts); + return this.peerconnection.createDataChannel(label, opts); +}; - for (var i = 8; i < parts.length; i += 2) { - if (parts[i] === 'raddr') { - candidate.relAddr = parts[i + 1]; - } else if (parts[i] === 'rport') { - candidate.relPort = parts[i + 1]; - } else if (parts[i] === 'generation') { - candidate.generation = parts[i + 1]; - } else if (parts[i] === 'tcptype') { - candidate.tcpType = parts[i + 1]; +TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) { + var self = this; + this.trace('setLocalDescription', dumpSDP(description)); + this.peerconnection.setLocalDescription(description, + function () { + self.trace('setLocalDescriptionOnSuccess'); + if (successCallback) successCallback(); + }, + function (err) { + self.trace('setLocalDescriptionOnFailure', err); + if (failureCallback) failureCallback(err); } - } - - candidate.network = '1'; + ); +}; - return candidate; +TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) { + var self = this; + this.trace('setRemoteDescription', dumpSDP(description)); + this.peerconnection.setRemoteDescription(description, + function () { + self.trace('setRemoteDescriptionOnSuccess'); + if (successCallback) successCallback(); + }, + function (err) { + self.trace('setRemoteDescriptionOnFailure', err); + if (failureCallback) failureCallback(err); + } + ); }; -exports.sourceGroups = function (lines) { - var parsed = []; - for (var i = 0; i < lines.length; i++) { - var parts = lines[i].substr(13).split(' '); - parsed.push({ - semantics: parts.shift(), - sources: parts - }); +TraceablePeerConnection.prototype.close = function () { + this.trace('stop'); + if (this.peerconnection.signalingState != 'closed') { + this.peerconnection.close(); } - return parsed; }; -exports.sources = function (lines) { - // http://tools.ietf.org/html/rfc5576 - var parsed = []; - var sources = {}; - for (var i = 0; i < lines.length; i++) { - var parts = lines[i].substr(7).split(' '); - var ssrc = parts.shift(); +TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) { + var self = this; + this.trace('createOffer', constraints); + this.peerconnection.createOffer( + function (offer) { + self.trace('createOfferOnSuccess', dumpSDP(offer)); + if (successCallback) successCallback(offer); + }, + function (err) { + self.trace('createOfferOnFailure', err); + if (failureCallback) failureCallback(err); + }, + constraints + ); +}; - if (!sources[ssrc]) { - var source = { - ssrc: ssrc, - parameters: [] - }; - parsed.push(source); +TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) { + var self = this; + this.trace('createAnswer', constraints); + this.peerconnection.createAnswer( + function (answer) { + self.trace('createAnswerOnSuccess', dumpSDP(answer)); + if (successCallback) successCallback(answer); + }, + function (err) { + self.trace('createAnswerOnFailure', err); + if (failureCallback) failureCallback(err); + }, + constraints + ); +}; - // Keep an index - sources[ssrc] = source; +TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) { + var self = this; + this.trace('addIceCandidate', candidate); + this.peerconnection.addIceCandidate(candidate, + function () { + //self.trace('addIceCandidateOnSuccess'); + if (successCallback) successCallback(); + }, + function (err) { + self.trace('addIceCandidateOnFailure', err); + if (failureCallback) failureCallback(err); } - - parts = parts.join(' ').split(':'); - var attribute = parts.shift(); - var value = parts.join(':') || null; - - sources[ssrc].parameters.push({ - key: attribute, - value: value - }); - } - - return parsed; + ); }; -exports.groups = function (lines) { - // http://tools.ietf.org/html/rfc5888 - var parsed = []; - var parts; - for (var i = 0; i < lines.length; i++) { - parts = lines[i].substr(8).split(' '); - parsed.push({ - semantics: parts.shift(), - contents: parts - }); - } - return parsed; +TraceablePeerConnection.prototype.getStats = function () { + this.peerconnection.getStats.apply(this.peerconnection, arguments); }; -exports.bandwidth = function (line) { - var parts = line.substr(2).split(':'); - var parsed = {}; - parsed.type = parts.shift(); - parsed.bandwidth = parts.shift(); - return parsed; -}; +module.exports = TraceablePeerConnection; -},{}],28:[function(require,module,exports){ +},{"util":8,"webrtc-adapter-test":20,"wildemitter":4}],30:[function(require,module,exports){ /** * lodash 3.0.4 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -9455,7 +10597,88 @@ function isObject(value) { module.exports = baseEach; -},{"lodash.keys":37}],32:[function(require,module,exports){ +},{"lodash.keys":39}],40:[function(require,module,exports){ +/** + * lodash 3.0.0 (Custom Build) + * Build: `lodash modern modularize exports="npm" -o ./` + * Copyright 2012-2015 The Dojo Foundation + * Based on Underscore.js 1.7.0 + * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ + +/** + * A specialized version of `_.map` for arrays without support for callback + * shorthands or `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ +function arrayMap(array, iteratee) { + var index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; +} + +module.exports = arrayMap; + +},{}],34:[function(require,module,exports){ +/** + * lodash 3.8.1 (Custom Build) + * Build: `lodash modern modularize exports="npm" -o ./` + * Copyright 2012-2015 The Dojo Foundation + * Based on Underscore.js 1.8.3 + * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ +var isArray = require('lodash.isarray'); + +/** Used to match property names within property paths. */ +var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g; + +/** Used to match backslashes in property paths. */ +var reEscapeChar = /\\(\\)?/g; + +/** + * Converts `value` to a string if it's not one. An empty string is returned + * for `null` or `undefined` values. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ +function baseToString(value) { + return value == null ? '' : (value + ''); +} + +/** + * Converts `value` to property path array if it's not one. + * + * @private + * @param {*} value The value to process. + * @returns {Array} Returns the property path array. + */ +function toPath(value) { + if (isArray(value)) { + return value; + } + var result = []; + baseToString(value).replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; +} + +module.exports = toPath; + +},{"lodash.isarray":35}],36:[function(require,module,exports){ /** * lodash 3.1.4 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -9592,108 +10815,24 @@ function isLength(value) { * * var users = [ * { 'user': 'barney' }, - * { 'user': 'fred' } - * ]; - * - * // using the `_.property` callback shorthand - * _.map(users, 'user'); - * // => ['barney', 'fred'] - */ -function map(collection, iteratee, thisArg) { - var func = isArray(collection) ? arrayMap : baseMap; - iteratee = baseCallback(iteratee, thisArg, 3); - return func(collection, iteratee); -} - -module.exports = map; - -},{"lodash._arraymap":39,"lodash._basecallback":38,"lodash._baseeach":40,"lodash.isarray":31}],34:[function(require,module,exports){ -/** - * lodash 3.8.0 (Custom Build) - * Build: `lodash modern modularize exports="npm" -o ./` - * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.8.3 - * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - * Available under MIT license - */ -var isArray = require('lodash.isarray'); - -/** Used to match property names within property paths. */ -var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g; - -/** Used to match backslashes in property paths. */ -var reEscapeChar = /\\(\\)?/g; - -/** - * Converts `value` to a string if it is not one. An empty string is returned - * for `null` or `undefined` values. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ -function baseToString(value) { - if (typeof value == 'string') { - return value; - } - return value == null ? '' : (value + ''); -} - -/** - * Converts `value` to property path array if it is not one. - * - * @private - * @param {*} value The value to process. - * @returns {Array} Returns the property path array. - */ -function toPath(value) { - if (isArray(value)) { - return value; - } - var result = []; - baseToString(value).replace(rePropName, function(match, number, quote, string) { - result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); - }); - return result; -} - -module.exports = toPath; - -},{"lodash.isarray":31}],39:[function(require,module,exports){ -/** - * lodash 3.0.0 (Custom Build) - * Build: `lodash modern modularize exports="npm" -o ./` - * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.7.0 - * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - * Available under MIT license - */ - -/** - * A specialized version of `_.map` for arrays without support for callback - * shorthands or `this` binding. + * { 'user': 'fred' } + * ]; * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. + * // using the `_.property` callback shorthand + * _.map(users, 'user'); + * // => ['barney', 'fred'] */ -function arrayMap(array, iteratee) { - var index = -1, - length = array.length, - result = Array(length); - - while (++index < length) { - result[index] = iteratee(array[index], index, array); - } - return result; +function map(collection, iteratee, thisArg) { + var func = isArray(collection) ? arrayMap : baseMap; + iteratee = baseCallback(iteratee, thisArg, 3); + return func(collection, iteratee); } -module.exports = arrayMap; +module.exports = map; -},{}],41:[function(require,module,exports){ +},{"lodash._arraymap":40,"lodash._basecallback":41,"lodash._baseeach":42,"lodash.isarray":35}],43:[function(require,module,exports){ /** - * lodash 3.9.0 (Custom Build) + * lodash 3.9.1 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` * Copyright 2012-2015 The Dojo Foundation * Based on Underscore.js 1.8.3 @@ -9704,33 +10843,10 @@ module.exports = arrayMap; /** `Object#toString` result references. */ var funcTag = '[object Function]'; -/** - * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special). - * In addition to special characters the forward slash is escaped to allow for - * easier `eval` use and `Function` compilation. - */ -var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g, - reHasRegExpChars = RegExp(reRegExpChars.source); - /** Used to detect host constructors (Safari > 5). */ var reIsHostCtor = /^\[object .+?Constructor\]$/; /** - * Converts `value` to a string if it's not one. An empty string is returned - * for `null` or `undefined` values. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ -function baseToString(value) { - if (typeof value == 'string') { - return value; - } - return value == null ? '' : (value + ''); -} - -/** * Checks if `value` is object-like. * * @private @@ -9751,14 +10867,14 @@ var fnToString = Function.prototype.toString; var hasOwnProperty = objectProto.hasOwnProperty; /** - * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring) + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) * of values. */ var objToString = objectProto.toString; /** Used to detect if a method is native. */ var reIsNative = RegExp('^' + - escapeRegExp(fnToString.call(hasOwnProperty)) + fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); @@ -9776,6 +10892,56 @@ function getNative(object, key) { } /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ +function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in older versions of Chrome and Safari which return 'function' for regexes + // and Safari 8 equivalents which return 'object' for typed array constructors. + return isObject(value) && objToString.call(value) == funcTag; +} + +/** + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */ +function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); +} + +/** * Checks if `value` is a native function. * * @static @@ -9795,38 +10961,17 @@ function isNative(value) { if (value == null) { return false; } - if (objToString.call(value) == funcTag) { + if (isFunction(value)) { return reIsNative.test(fnToString.call(value)); } return isObjectLike(value) && reIsHostCtor.test(value); } -/** - * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?", - * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escapeRegExp('[lodash](https://lodash.com/)'); - * // => '\[lodash\]\(https:\/\/lodash\.com\/\)' - */ -function escapeRegExp(string) { - string = baseToString(string); - return (string && reHasRegExpChars.test(string)) - ? string.replace(reRegExpChars, '\\$&') - : string; -} - module.exports = getNative; -},{}],42:[function(require,module,exports){ +},{}],44:[function(require,module,exports){ /** - * lodash 3.0.3 (Custom Build) + * lodash 3.0.4 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` * Copyright 2012-2015 The Dojo Foundation * Based on Underscore.js 1.8.3 @@ -9834,9 +10979,6 @@ module.exports = getNative; * Available under MIT license */ -/** `Object#toString` result references. */ -var argsTag = '[object Arguments]'; - /** * Checks if `value` is object-like. * @@ -9851,14 +10993,14 @@ function isObjectLike(value) { /** Used for native method references. */ var objectProto = Object.prototype; -/** - * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring) - * of values. - */ -var objToString = objectProto.toString; +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** Native method references. */ +var propertyIsEnumerable = objectProto.propertyIsEnumerable; /** - * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer) + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) * of an array-like value. */ var MAX_SAFE_INTEGER = 9007199254740991; @@ -9902,7 +11044,7 @@ function isArrayLike(value) { /** * Checks if `value` is a valid array-like length. * - * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength). + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). * * @private * @param {*} value The value to check. @@ -9929,12 +11071,80 @@ function isLength(value) { * // => false */ function isArguments(value) { - return isObjectLike(value) && isArrayLike(value) && objToString.call(value) == argsTag; + return isObjectLike(value) && isArrayLike(value) && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); } module.exports = isArguments; -},{}],40:[function(require,module,exports){ +},{}],45:[function(require,module,exports){ +/** + * lodash 3.0.1 (Custom Build) + * Build: `lodash modern modularize exports="npm" -o ./` + * Copyright 2012-2015 The Dojo Foundation + * Based on Underscore.js 1.8.3 + * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ + +/** + * A specialized version of `baseCallback` which only supports `this` binding + * and specifying the number of arguments to provide to `func`. + * + * @private + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {number} [argCount] The number of arguments to provide to `func`. + * @returns {Function} Returns the callback. + */ +function bindCallback(func, thisArg, argCount) { + if (typeof func != 'function') { + return identity; + } + if (thisArg === undefined) { + return func; + } + switch (argCount) { + case 1: return function(value) { + return func.call(thisArg, value); + }; + case 3: return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + case 5: return function(value, other, key, object, source) { + return func.call(thisArg, value, other, key, object, source); + }; + } + return function() { + return func.apply(thisArg, arguments); + }; +} + +/** + * This method returns the first argument provided to it. + * + * @static + * @memberOf _ + * @category Utility + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'user': 'fred' }; + * + * _.identity(object) === object; + * // => true + */ +function identity(value) { + return value; +} + +module.exports = bindCallback; + +},{}],42:[function(require,module,exports){ /** * lodash 3.0.4 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -10079,114 +11289,47 @@ function isLength(value) { /** * Converts `value` to an object if it's not one. - * - * @private - * @param {*} value The value to process. - * @returns {Object} Returns the object. - */ -function toObject(value) { - return isObject(value) ? value : Object(value); -} - -/** - * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. - * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(1); - * // => false - */ -function isObject(value) { - // Avoid a V8 JIT bug in Chrome 19-20. - // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. - var type = typeof value; - return !!value && (type == 'object' || type == 'function'); -} - -module.exports = baseEach; - -},{"lodash.keys":43}],44:[function(require,module,exports){ -/** - * lodash 3.0.1 (Custom Build) - * Build: `lodash modern modularize exports="npm" -o ./` - * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.8.3 - * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - * Available under MIT license - */ - -/** - * A specialized version of `baseCallback` which only supports `this` binding - * and specifying the number of arguments to provide to `func`. - * - * @private - * @param {Function} func The function to bind. - * @param {*} thisArg The `this` binding of `func`. - * @param {number} [argCount] The number of arguments to provide to `func`. - * @returns {Function} Returns the callback. - */ -function bindCallback(func, thisArg, argCount) { - if (typeof func != 'function') { - return identity; - } - if (thisArg === undefined) { - return func; - } - switch (argCount) { - case 1: return function(value) { - return func.call(thisArg, value); - }; - case 3: return function(value, index, collection) { - return func.call(thisArg, value, index, collection); - }; - case 4: return function(accumulator, value, index, collection) { - return func.call(thisArg, accumulator, value, index, collection); - }; - case 5: return function(value, other, key, object, source) { - return func.call(thisArg, value, other, key, object, source); - }; - } - return function() { - return func.apply(thisArg, arguments); - }; + * + * @private + * @param {*} value The value to process. + * @returns {Object} Returns the object. + */ +function toObject(value) { + return isObject(value) ? value : Object(value); } /** - * This method returns the first argument provided to it. + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ - * @category Utility - * @param {*} value Any value. - * @returns {*} Returns `value`. + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * - * var object = { 'user': 'fred' }; + * _.isObject({}); + * // => true * - * _.identity(object) === object; + * _.isObject([1, 2, 3]); * // => true + * + * _.isObject(1); + * // => false */ -function identity(value) { - return value; +function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); } -module.exports = bindCallback; +module.exports = baseEach; -},{}],37:[function(require,module,exports){ +},{"lodash.keys":46}],39:[function(require,module,exports){ /** - * lodash 3.1.1 (Custom Build) + * lodash 3.1.2 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` * Copyright 2012-2015 The Dojo Foundation * Based on Underscore.js 1.8.3 @@ -10210,7 +11353,7 @@ var hasOwnProperty = objectProto.hasOwnProperty; var nativeKeys = getNative(Object, 'keys'); /** - * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer) + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) * of an array-like value. */ var MAX_SAFE_INTEGER = 9007199254740991; @@ -10268,7 +11411,7 @@ function isIndex(value, length) { /** * Checks if `value` is a valid array-like length. * - * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength). + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). * * @private * @param {*} value The value to check. @@ -10337,7 +11480,7 @@ function isObject(value) { * Creates an array of the own enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. See the - * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys) + * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys) * for more details. * * @static @@ -10361,7 +11504,7 @@ function isObject(value) { * // => ['0', '1'] */ var keys = !nativeKeys ? shimKeys : function(object) { - var Ctor = object == null ? null : object.constructor; + var Ctor = object == null ? undefined : object.constructor; if ((typeof Ctor == 'function' && Ctor.prototype === object) || (typeof object != 'function' && isArrayLike(object))) { return shimKeys(object); @@ -10422,9 +11565,9 @@ function keysIn(object) { module.exports = keys; -},{"lodash._getnative":41,"lodash.isarguments":42,"lodash.isarray":27}],38:[function(require,module,exports){ +},{"lodash._getnative":43,"lodash.isarguments":44,"lodash.isarray":32}],41:[function(require,module,exports){ /** - * lodash 3.3.0 (Custom Build) + * lodash 3.3.1 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` * Copyright 2012-2015 The Dojo Foundation * Based on Underscore.js 1.8.3 @@ -10453,9 +11596,6 @@ var reEscapeChar = /\\(\\)?/g; * @returns {string} Returns the string. */ function baseToString(value) { - if (typeof value == 'string') { - return value; - } return value == null ? '' : (value + ''); } @@ -10587,8 +11727,7 @@ function baseMatches(source) { } /** - * The base implementation of `_.matchesProperty` which does not which does - * not clone `value`. + * The base implementation of `_.matchesProperty` which does not clone `srcValue`. * * @private * @param {string} path The path of the property to get. @@ -10823,7 +11962,7 @@ function identity(value) { } /** - * Creates a function which returns the property value at `path` on a + * Creates a function that returns the property value at `path` on a * given object. * * @static @@ -10850,7 +11989,7 @@ function property(path) { module.exports = baseCallback; -},{"lodash._baseisequal":45,"lodash._bindcallback":44,"lodash.isarray":31,"lodash.pairs":46}],47:[function(require,module,exports){ +},{"lodash._baseisequal":47,"lodash._bindcallback":45,"lodash.isarray":35,"lodash.pairs":48}],49:[function(require,module,exports){ /** * lodash 3.0.2 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -10962,9 +12101,9 @@ function isTypedArray(value) { module.exports = isTypedArray; -},{}],48:[function(require,module,exports){ +},{}],50:[function(require,module,exports){ /** - * lodash 3.0.3 (Custom Build) + * lodash 3.9.1 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` * Copyright 2012-2015 The Dojo Foundation * Based on Underscore.js 1.8.3 @@ -10973,7 +12112,10 @@ module.exports = isTypedArray; */ /** `Object#toString` result references. */ -var argsTag = '[object Arguments]'; +var funcTag = '[object Function]'; + +/** Used to detect host constructors (Safari > 5). */ +var reIsHostCtor = /^\[object .+?Constructor\]$/; /** * Checks if `value` is object-like. @@ -10989,92 +12131,118 @@ function isObjectLike(value) { /** Used for native method references. */ var objectProto = Object.prototype; +/** Used to resolve the decompiled source of functions. */ +var fnToString = Function.prototype.toString; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + /** - * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring) + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) * of values. */ var objToString = objectProto.toString; -/** - * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer) - * of an array-like value. - */ -var MAX_SAFE_INTEGER = 9007199254740991; +/** Used to detect if a method is native. */ +var reIsNative = RegExp('^' + + fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' +); /** - * The base implementation of `_.property` without support for deep paths. + * Gets the native function at `key` of `object`. * * @private - * @param {string} key The key of the property to get. - * @returns {Function} Returns the new function. + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. */ -function baseProperty(key) { - return function(object) { - return object == null ? undefined : object[key]; - }; +function getNative(object, key) { + var value = object == null ? undefined : object[key]; + return isNative(value) ? value : undefined; } /** - * Gets the "length" property value of `object`. + * Checks if `value` is classified as a `Function` object. * - * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) - * that affects Safari on at least iOS 8.1-8.3 ARM64. + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example * - * @private - * @param {Object} object The object to query. - * @returns {*} Returns the "length" value. - */ -var getLength = baseProperty('length'); - -/** - * Checks if `value` is array-like. + * _.isFunction(_); + * // => true * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * _.isFunction(/abc/); + * // => false */ -function isArrayLike(value) { - return value != null && isLength(getLength(value)); +function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in older versions of Chrome and Safari which return 'function' for regexes + // and Safari 8 equivalents which return 'object' for typed array constructors. + return isObject(value) && objToString.call(value) == funcTag; } /** - * Checks if `value` is a valid array-like length. - * - * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength). + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * - * @private + * @static + * @memberOf _ + * @category Lang * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false */ -function isLength(value) { - return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; +function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); } /** - * Checks if `value` is classified as an `arguments` object. + * Checks if `value` is a native function. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @returns {boolean} Returns `true` if `value` is a native function, else `false`. * @example * - * _.isArguments(function() { return arguments; }()); + * _.isNative(Array.prototype.push); * // => true * - * _.isArguments([1, 2, 3]); + * _.isNative(_); * // => false */ -function isArguments(value) { - return isObjectLike(value) && isArrayLike(value) && objToString.call(value) == argsTag; +function isNative(value) { + if (value == null) { + return false; + } + if (isFunction(value)) { + return reIsNative.test(fnToString.call(value)); + } + return isObjectLike(value) && reIsHostCtor.test(value); } -module.exports = isArguments; +module.exports = getNative; -},{}],49:[function(require,module,exports){ +},{}],51:[function(require,module,exports){ /** - * lodash 3.9.0 (Custom Build) + * lodash 3.0.4 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` * Copyright 2012-2015 The Dojo Foundation * Based on Underscore.js 1.8.3 @@ -11082,35 +12250,6 @@ module.exports = isArguments; * Available under MIT license */ -/** `Object#toString` result references. */ -var funcTag = '[object Function]'; - -/** - * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special). - * In addition to special characters the forward slash is escaped to allow for - * easier `eval` use and `Function` compilation. - */ -var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g, - reHasRegExpChars = RegExp(reRegExpChars.source); - -/** Used to detect host constructors (Safari > 5). */ -var reIsHostCtor = /^\[object .+?Constructor\]$/; - -/** - * Converts `value` to a string if it's not one. An empty string is returned - * for `null` or `undefined` values. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ -function baseToString(value) { - if (typeof value == 'string') { - return value; - } - return value == null ? '' : (value + ''); -} - /** * Checks if `value` is object-like. * @@ -11125,87 +12264,91 @@ function isObjectLike(value) { /** Used for native method references. */ var objectProto = Object.prototype; -/** Used to resolve the decompiled source of functions. */ -var fnToString = Function.prototype.toString; - /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; +/** Native method references. */ +var propertyIsEnumerable = objectProto.propertyIsEnumerable; + /** - * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring) - * of values. + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. */ -var objToString = objectProto.toString; +var MAX_SAFE_INTEGER = 9007199254740991; -/** Used to detect if a method is native. */ -var reIsNative = RegExp('^' + - escapeRegExp(fnToString.call(hasOwnProperty)) - .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' -); +/** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new function. + */ +function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; +} /** - * Gets the native function at `key` of `object`. + * Gets the "length" property value of `object`. + * + * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) + * that affects Safari on at least iOS 8.1-8.3 ARM64. * * @private * @param {Object} object The object to query. - * @param {string} key The key of the method to get. - * @returns {*} Returns the function if it's native, else `undefined`. + * @returns {*} Returns the "length" value. */ -function getNative(object, key) { - var value = object == null ? undefined : object[key]; - return isNative(value) ? value : undefined; -} +var getLength = baseProperty('length'); /** - * Checks if `value` is a native function. + * Checks if `value` is array-like. * - * @static - * @memberOf _ - * @category Lang + * @private * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, else `false`. - * @example + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + */ +function isArrayLike(value) { + return value != null && isLength(getLength(value)); +} + +/** + * Checks if `value` is a valid array-like length. * - * _.isNative(Array.prototype.push); - * // => true + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). * - * _.isNative(_); - * // => false + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. */ -function isNative(value) { - if (value == null) { - return false; - } - if (objToString.call(value) == funcTag) { - return reIsNative.test(fnToString.call(value)); - } - return isObjectLike(value) && reIsHostCtor.test(value); +function isLength(value) { + return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } /** - * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?", - * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`. + * Checks if `value` is classified as an `arguments` object. * * @static * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * - * _.escapeRegExp('[lodash](https://lodash.com/)'); - * // => '\[lodash\]\(https:\/\/lodash\.com\/\)' + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false */ -function escapeRegExp(string) { - string = baseToString(string); - return (string && reHasRegExpChars.test(string)) - ? string.replace(reRegExpChars, '\\$&') - : string; +function isArguments(value) { + return isObjectLike(value) && isArrayLike(value) && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); } -module.exports = getNative; +module.exports = isArguments; -},{}],46:[function(require,module,exports){ +},{}],48:[function(require,module,exports){ /** * lodash 3.0.1 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -11285,7 +12428,7 @@ function pairs(object) { module.exports = pairs; -},{"lodash.keys":43}],45:[function(require,module,exports){ +},{"lodash.keys":46}],47:[function(require,module,exports){ /** * lodash 3.0.7 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -11629,9 +12772,9 @@ function isObject(value) { module.exports = baseIsEqual; -},{"lodash.isarray":31,"lodash.istypedarray":47,"lodash.keys":43}],43:[function(require,module,exports){ +},{"lodash.isarray":35,"lodash.istypedarray":49,"lodash.keys":46}],46:[function(require,module,exports){ /** - * lodash 3.1.1 (Custom Build) + * lodash 3.1.2 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` * Copyright 2012-2015 The Dojo Foundation * Based on Underscore.js 1.8.3 @@ -11655,7 +12798,7 @@ var hasOwnProperty = objectProto.hasOwnProperty; var nativeKeys = getNative(Object, 'keys'); /** - * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer) + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) * of an array-like value. */ var MAX_SAFE_INTEGER = 9007199254740991; @@ -11713,7 +12856,7 @@ function isIndex(value, length) { /** * Checks if `value` is a valid array-like length. * - * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength). + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). * * @private * @param {*} value The value to check. @@ -11782,7 +12925,7 @@ function isObject(value) { * Creates an array of the own enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. See the - * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys) + * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys) * for more details. * * @static @@ -11806,7 +12949,7 @@ function isObject(value) { * // => ['0', '1'] */ var keys = !nativeKeys ? shimKeys : function(object) { - var Ctor = object == null ? null : object.constructor; + var Ctor = object == null ? undefined : object.constructor; if ((typeof Ctor == 'function' && Ctor.prototype === object) || (typeof object != 'function' && isArrayLike(object))) { return shimKeys(object); @@ -11867,6 +13010,6 @@ function keysIn(object) { module.exports = keys; -},{"lodash._getnative":49,"lodash.isarguments":48,"lodash.isarray":31}]},{},[1])(1) +},{"lodash._getnative":50,"lodash.isarguments":51,"lodash.isarray":35}]},{},[1])(1) }); ;