Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

Atom Beautify - Debugging information

The following debugging information was generated by Atom Beautify on Tue Aug 01 2017 14:52:28 GMT+0200 (CEST).


Table Of Contents


Platform: darwin

Versions

Atom Version: 1.18.0

Atom Beautify Version: 0.30.4

Original file to be beautified

Original File Path: /Users/waldgeist/Documents/Development/htdocs/aiddogs/webapp/packages/guzz-live-session-standard/vendor/adapter.js

Original File Grammar: Babel ES6 JavaScript

Original File Language: JSX

Language namespace: jsx

Supported Beautifiers: JS Beautify, Pretty Diff

Selected Beautifier: Pretty Diff

Original File Contents

/*
 *  Copyright (c) 2016 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,
MediaStream, RTCIceGatherer, RTCIceTransport, RTCDtlsTransport,
RTCRtpSender, RTCRtpReceiver*/
/* exported trace,requestUserMedia */

'use strict';

app.livesession.adapterJS = _.bind(function() {

  // app.log('adding adapter.js');

  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);
    },
    extractVersion: function(uastring, expr, pos) {
      var match = uastring.match(expr);
      return match && match.length >= pos && parseInt(match[pos], 10);
    }
  };

  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);
    }
  }

  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) {
    webrtcUtils.log('This appears to be Firefox');

    webrtcDetectedBrowser = 'firefox';

    // the detected firefox version.
    webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent,
      /Firefox\/([0-9]+)\./, 1);

    // the minimum firefox version still supported by adapter.
    webrtcMinimumVersion = 31;

    // Shim for RTCPeerConnection on older versions.
    if (!window.RTCPeerConnection) {
      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
      };

      // wrap static methods. Currently just generateCertificate.
      if (mozRTCPeerConnection.generateCertificate) {
        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
          get: function() {
            if (arguments.length) {
              return mozRTCPeerConnection.generateCertificate.apply(null,
                arguments);
            } else {
              return mozRTCPeerConnection.generateCertificate;
            }
          }
        });
      }

      window.RTCSessionDescription = mozRTCSessionDescription;
      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().then(undefined, function(e) {
          if (e.name === 'NotFoundError') {
            return [];
          }
          throw e;
        });
      };
    }
  } else if (navigator.webkitGetUserMedia && window.webkitRTCPeerConnection) {
    webrtcUtils.log('This appears to be Chrome');

    webrtcDetectedBrowser = 'chrome';

    // the detected chrome version.
    webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent,
      /Chrom(e|ium)\/([0-9]+)\./, 2);

    // 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;
    };

    // wrap static methods. Currently just generateCertificate.
    if (webkitRTCPeerConnection.generateCertificate) {
      Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
        get: function() {
          if (arguments.length) {
            return webkitRTCPeerConnection.generateCertificate.apply(null,
              arguments);
          } else {
            return webkitRTCPeerConnection.generateCertificate;
          }
        }
      });
    }

    // 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 = webrtcUtils.extractVersion(navigator.userAgent,
      /Edge\/(\d+).(\d+)$/, 2);

    // The minimum version still supported by adapter.
    // This is the build number for Edge.
    webrtcMinimumVersion = 10547;

    if (window.RTCIceGatherer) {
      // Generate an alphanumeric identifier for cname or mids.
      // TODO: use UUIDs instead? https://gist.github.com/jed/982883
      var generateIdentifier = function() {
        return Math.random().toString(36).substr(2, 10);
      };

      // The RTCP CNAME used by all peerconnections from the same JS.
      var localCName = generateIdentifier();

      // SDP helpers - to be moved into separate module.
      var SDPUtils = {};

      // Splits SDP into lines, dealing with both CRLF and LF.
      SDPUtils.splitLines = function(blob) {
        return blob.trim().split('\n').map(function(line) {
          return line.trim();
        });
      };

      // Splits SDP into sessionpart and mediasections. Ensures CRLF.
      SDPUtils.splitSections = function(blob) {
        var parts = blob.split('\r\nm=');
        return parts.map(function(part, index) {
          return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
        });
      };

      // Returns lines that start with a certain prefix.
      SDPUtils.matchPrefix = function(blob, prefix) {
        return SDPUtils.splitLines(blob).filter(function(line) {
          return line.indexOf(prefix) === 0;
        });
      };

      // Parses an ICE candidate line. Sample input:
      // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 rport 55996"
      SDPUtils.parseCandidate = function(line) {
        var parts;
        // Parse both variants.
        if (line.indexOf('a=candidate:') === 0) {
          parts = line.substring(12).split(' ');
        } else {
          parts = line.substring(10).split(' ');
        }

        var candidate = {
          foundation: parts[0],
          component: parts[1],
          protocol: parts[2].toLowerCase(),
          priority: parseInt(parts[3], 10),
          ip: parts[4],
          port: parseInt(parts[5], 10),
          // skip parts[6] == 'typ'
          type: parts[7]
        };

        for (var i = 8; i < parts.length; i += 2) {
          switch (parts[i]) {
            case 'raddr':
              candidate.relatedAddress = parts[i + 1];
              break;
            case 'rport':
              candidate.relatedPort = parseInt(parts[i + 1], 10);
              break;
            case 'tcptype':
              candidate.tcpType = parts[i + 1];
              break;
            default: // Unknown extensions are silently ignored.
              break;
          }
        }
        return candidate;
      };

      // Translates a candidate object into SDP candidate attribute.
      SDPUtils.writeCandidate = function(candidate) {
        var sdp = [];
        sdp.push(candidate.foundation);
        sdp.push(candidate.component);
        sdp.push(candidate.protocol.toUpperCase());
        sdp.push(candidate.priority);
        sdp.push(candidate.ip);
        sdp.push(candidate.port);

        var type = candidate.type;
        sdp.push('typ');
        sdp.push(type);
        if (type !== 'host' && candidate.relatedAddress &&
          candidate.relatedPort) {
          sdp.push('raddr');
          sdp.push(candidate.relatedAddress); // was: relAddr
          sdp.push('rport');
          sdp.push(candidate.relatedPort); // was: relPort
        }
        if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
          sdp.push('tcptype');
          sdp.push(candidate.tcpType);
        }
        return 'candidate:' + sdp.join(' ');
      };

      // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
      // a=rtpmap:111 opus/48000/2
      SDPUtils.parseRtpMap = function(line) {
        var parts = line.substr(9).split(' ');
        var parsed = {
          payloadType: parseInt(parts.shift(), 10) // was: id
        };

        parts = parts[0].split('/');

        parsed.name = parts[0];
        parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
        parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1; // was: channels
        return parsed;
      };

      // Generate an a=rtpmap line from RTCRtpCodecCapability or RTCRtpCodecParameters.
      SDPUtils.writeRtpMap = function(codec) {
        var pt = codec.payloadType;
        if (codec.preferredPayloadType !== undefined) {
          pt = codec.preferredPayloadType;
        }
        return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
          (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\r\n';
      };

      // Parses an ftmp line, returns dictionary. Sample input:
      // a=fmtp:96 vbr=on;cng=on
      // Also deals with vbr=on; cng=on
      SDPUtils.parseFmtp = function(line) {
        var parsed = {};
        var kv;
        var parts = line.substr(line.indexOf(' ') + 1).split(';');
        for (var j = 0; j < parts.length; j++) {
          kv = parts[j].trim().split('=');
          parsed[kv[0].trim()] = kv[1];
        }
        return parsed;
      };

      // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
      SDPUtils.writeFtmp = function(codec) {
        var line = '';
        var pt = codec.payloadType;
        if (codec.preferredPayloadType !== undefined) {
          pt = codec.preferredPayloadType;
        }
        if (codec.parameters && codec.parameters.length) {
          var params = [];
          Object.keys(codec.parameters).forEach(function(param) {
            params.push(param + '=' + codec.parameters[param]);
          });
          line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
        }
        return line;
      };

      // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
      // a=rtcp-fb:98 nack rpsi
      SDPUtils.parseRtcpFb = function(line) {
        var parts = line.substr(line.indexOf(' ') + 1).split(' ');
        return {
          type: parts.shift(),
          parameter: parts.join(' ')
        };
      };
      // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
      SDPUtils.writeRtcpFb = function(codec) {
        var lines = '';
        var pt = codec.payloadType;
        if (codec.preferredPayloadType !== undefined) {
          pt = codec.preferredPayloadType;
        }
        if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
          // FIXME: special handling for trr-int?
          codec.rtcpFeedback.forEach(function(fb) {
            lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + ' ' + fb.parameter +
              '\r\n';
          });
        }
        return lines;
      };

      // Parses an RFC 5576 ssrc media attribute. Sample input:
      // a=ssrc:3735928559 cname:something
      SDPUtils.parseSsrcMedia = function(line) {
        var sp = line.indexOf(' ');
        var parts = {
          ssrc: line.substr(7, sp - 7),
        };
        var colon = line.indexOf(':', sp);
        if (colon > -1) {
          parts.attribute = line.substr(sp + 1, colon - sp - 1);
          parts.value = line.substr(colon + 1);
        } else {
          parts.attribute = line.substr(sp + 1);
        }
        return parts;
      };

      // Extracts DTLS parameters from SDP media section or sessionpart.
      // FIXME: for consistency with other functions this should only
      //   get the fingerprint line as input. See also getIceParameters.
      SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
        var lines = SDPUtils.splitLines(mediaSection);
        lines = lines.concat(SDPUtils.splitLines(sessionpart)); // Search in session part, too.
        var fpLine = lines.filter(function(line) {
          return line.indexOf('a=fingerprint:') === 0;
        })[0].substr(14);
        // Note: a=setup line is ignored since we use the 'auto' role.
        var dtlsParameters = {
          role: 'auto',
          fingerprints: [{
            algorithm: fpLine.split(' ')[0],
            value: fpLine.split(' ')[1]
          }]
        };
        return dtlsParameters;
      };

      // Serializes DTLS parameters to SDP.
      SDPUtils.writeDtlsParameters = function(params, setupType) {
        var sdp = 'a=setup:' + setupType + '\r\n';
        params.fingerprints.forEach(function(fp) {
          sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
        });
        return sdp;
      };
      // Parses ICE information from SDP media section or sessionpart.
      // FIXME: for consistency with other functions this should only
      //   get the ice-ufrag and ice-pwd lines as input.
      SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
        var lines = SDPUtils.splitLines(mediaSection);
        lines = lines.concat(SDPUtils.splitLines(sessionpart)); // Search in session part, too.
        var iceParameters = {
          usernameFragment: lines.filter(function(line) {
            return line.indexOf('a=ice-ufrag:') === 0;
          })[0].substr(12),
          password: lines.filter(function(line) {
            return line.indexOf('a=ice-pwd:') === 0;
          })[0].substr(10)
        };
        return iceParameters;
      };

      // Serializes ICE parameters to SDP.
      SDPUtils.writeIceParameters = function(params) {
        return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
          'a=ice-pwd:' + params.password + '\r\n';
      };

      // Parses the SDP media section and returns RTCRtpParameters.
      SDPUtils.parseRtpParameters = function(mediaSection) {
        var description = {
          codecs: [],
          headerExtensions: [],
          fecMechanisms: [],
          rtcp: []
        };
        var lines = SDPUtils.splitLines(mediaSection);
        var mline = lines[0].split(' ');
        for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
          var pt = mline[i];
          var rtpmapline = SDPUtils.matchPrefix(
            mediaSection, 'a=rtpmap:' + pt + ' ')[0];
          if (rtpmapline) {
            var codec = SDPUtils.parseRtpMap(rtpmapline);
            var fmtps = SDPUtils.matchPrefix(
              mediaSection, 'a=fmtp:' + pt + ' ');
            // Only the first a=fmtp:<pt> is considered.
            codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
            codec.rtcpFeedback = SDPUtils.matchPrefix(
                mediaSection, 'a=rtcp-fb:' + pt + ' ')
              .map(SDPUtils.parseRtcpFb);
            description.codecs.push(codec);
          }
        }
        // FIXME: parse headerExtensions, fecMechanisms and rtcp.
        return description;
      };

      // Generates parts of the SDP media section describing the capabilities / parameters.
      SDPUtils.writeRtpDescription = function(kind, caps) {
        var sdp = '';

        // Build the mline.
        sdp += 'm=' + kind + ' ';
        sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
        sdp += ' UDP/TLS/RTP/SAVPF ';
        sdp += caps.codecs.map(function(codec) {
          if (codec.preferredPayloadType !== undefined) {
            return codec.preferredPayloadType;
          }
          return codec.payloadType;
        }).join(' ') + '\r\n';

        sdp += 'c=IN IP4 0.0.0.0\r\n';
        sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';

        // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
        caps.codecs.forEach(function(codec) {
          sdp += SDPUtils.writeRtpMap(codec);
          sdp += SDPUtils.writeFtmp(codec);
          sdp += SDPUtils.writeRtcpFb(codec);
        });
        // FIXME: add headerExtensions, fecMechanismş and rtcp.
        sdp += 'a=rtcp-mux\r\n';
        return sdp;
      };

      SDPUtils.writeSessionBoilerplate = function() {
        // FIXME: sess-id should be an NTP timestamp.
        return 'v=0\r\n' +
          'o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\n' +
          's=-\r\n' +
          't=0 0\r\n';
      };

      SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
        var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);

        // Map ICE parameters (ufrag, pwd) to SDP.
        sdp += SDPUtils.writeIceParameters(
          transceiver.iceGatherer.getLocalParameters());

        // Map DTLS parameters to SDP.
        sdp += SDPUtils.writeDtlsParameters(
          transceiver.dtlsTransport.getLocalParameters(),
          type === 'offer' ? 'actpass' : 'active');

        sdp += 'a=mid:' + transceiver.mid + '\r\n';

        if (transceiver.rtpSender && transceiver.rtpReceiver) {
          sdp += 'a=sendrecv\r\n';
        } else if (transceiver.rtpSender) {
          sdp += 'a=sendonly\r\n';
        } else if (transceiver.rtpReceiver) {
          sdp += 'a=recvonly\r\n';
        } else {
          sdp += 'a=inactive\r\n';
        }

        // FIXME: for RTX there might be multiple SSRCs. Not implemented in Edge yet.
        if (transceiver.rtpSender) {
          var msid = 'msid:' + stream.id + ' ' +
            transceiver.rtpSender.track.id + '\r\n';
          sdp += 'a=' + msid;
          sdp += 'a=ssrc:' + transceiver.sendSsrc + ' ' + msid;
        }
        // FIXME: this should be written by writeRtpDescription.
        sdp += 'a=ssrc:' + transceiver.sendSsrc + ' cname:' +
          localCName + '\r\n';
        return sdp;
      };

      // Gets the direction from the mediaSection or the sessionpart.
      SDPUtils.getDirection = function(mediaSection, sessionpart) {
        // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
        var lines = SDPUtils.splitLines(mediaSection);
        for (var i = 0; i < lines.length; i++) {
          switch (lines[i]) {
            case 'a=sendrecv':
            case 'a=sendonly':
            case 'a=recvonly':
            case 'a=inactive':
              return lines[i].substr(2);
          }
        }
        if (sessionpart) {
          return SDPUtils.getDirection(sessionpart);
        }
        return 'sendrecv';
      };

      // ORTC defines an RTCIceCandidate object but no constructor.
      // Not implemented in Edge.
      if (!window.RTCIceCandidate) {
        window.RTCIceCandidate = function(args) {
          return args;
        };
      }
      // ORTC does not have a session description object but
      // other browsers (i.e. Chrome) that will support both PC and ORTC
      // in the future might have this defined already.
      if (!window.RTCSessionDescription) {
        window.RTCSessionDescription = function(args) {
          return args;
        };
      }

      window.RTCPeerConnection = function(config) {
        var self = this;

        this.onicecandidate = null;
        this.onaddstream = null;
        this.onremovestream = null;
        this.onsignalingstatechange = null;
        this.oniceconnectionstatechange = null;
        this.onnegotiationneeded = null;
        this.ondatachannel = null;

        this.localStreams = [];
        this.remoteStreams = [];
        this.getLocalStreams = function() {
          return self.localStreams;
        };
        this.getRemoteStreams = function() {
          return self.remoteStreams;
        };

        this.localDescription = new RTCSessionDescription({
          type: '',
          sdp: ''
        });
        this.remoteDescription = new RTCSessionDescription({
          type: '',
          sdp: ''
        });
        this.signalingState = 'stable';
        this.iceConnectionState = 'new';

        this.iceOptions = {
          gatherPolicy: 'all',
          iceServers: []
        };
        if (config && config.iceTransportPolicy) {
          switch (config.iceTransportPolicy) {
            case 'all':
            case 'relay':
              this.iceOptions.gatherPolicy = config.iceTransportPolicy;
              break;
            case 'none':
              // FIXME: remove once implementation and spec have added this.
              throw new TypeError('iceTransportPolicy "none" not supported');
          }
        }
        if (config && config.iceServers) {
          // Edge does not like
          // 1) stun:
          // 2) turn: that does not have all of turn:host:port?transport=udp
          // 3) an array of urls
          config.iceServers.forEach(function(server) {
            if (server.urls) {
              var url;
              if (typeof(server.urls) === 'string') {
                url = server.urls;
              } else {
                url = server.urls[0];
              }
              if (url.indexOf('transport=udp') !== -1) {
                self.iceServers.push({
                  username: server.username,
                  credential: server.credential,
                  urls: url
                });
              }
            }
          });
        }

        // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
        // everything that is needed to describe a SDP m-line.
        this.transceivers = [];

        // since the iceGatherer is currently created in createOffer but we
        // must not emit candidates until after setLocalDescription we buffer
        // them in this array.
        this._localIceCandidatesBuffer = [];
      };

      window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {
        var self = this;
        // FIXME: need to apply ice candidates in a way which is async but in-order
        this._localIceCandidatesBuffer.forEach(function(event) {
          if (self.onicecandidate !== null) {
            self.onicecandidate(event);
          }
        });
        this._localIceCandidatesBuffer = [];
      };

      window.RTCPeerConnection.prototype.addStream = function(stream) {
        // Clone is necessary for local demos mostly, attaching directly
        // to two different senders does not work (build 10547).
        this.localStreams.push(stream.clone());
        this._maybeFireNegotiationNeeded();
      };

      window.RTCPeerConnection.prototype.removeStream = function(stream) {
        var idx = this.localStreams.indexOf(stream);
        if (idx > -1) {
          this.localStreams.splice(idx, 1);
          this._maybeFireNegotiationNeeded();
        }
      };

      // Determines the intersection of local and remote capabilities.
      window.RTCPeerConnection.prototype._getCommonCapabilities =
        function(localCapabilities, remoteCapabilities) {
          var commonCapabilities = {
            codecs: [],
            headerExtensions: [],
            fecMechanisms: []
          };
          localCapabilities.codecs.forEach(function(lCodec) {
            for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
              var rCodec = remoteCapabilities.codecs[i];
              if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
                lCodec.clockRate === rCodec.clockRate &&
                lCodec.numChannels === rCodec.numChannels) {
                // push rCodec so we reply with offerer payload type
                commonCapabilities.codecs.push(rCodec);

                // FIXME: also need to determine intersection between
                // .rtcpFeedback and .parameters
                break;
              }
            }
          });

          localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
            for (var i = 0; i < remoteCapabilities.headerExtensions.length; i++) {
              var rHeaderExtension = remoteCapabilities.headerExtensions[i];
              if (lHeaderExtension.uri === rHeaderExtension.uri) {
                commonCapabilities.headerExtensions.push(rHeaderExtension);
                break;
              }
            }
          });

          // FIXME: fecMechanisms
          return commonCapabilities;
        };

      // Create ICE gatherer, ICE transport and DTLS transport.
      window.RTCPeerConnection.prototype._createIceAndDtlsTransports =
        function(mid, sdpMLineIndex) {
          var self = this;
          var iceGatherer = new RTCIceGatherer(self.iceOptions);
          var iceTransport = new RTCIceTransport(iceGatherer);
          iceGatherer.onlocalcandidate = function(evt) {
            var event = {};
            event.candidate = {
              sdpMid: mid,
              sdpMLineIndex: sdpMLineIndex
            };

            var cand = evt.candidate;
            // Edge emits an empty object for RTCIceCandidateComplete‥
            if (!cand || Object.keys(cand).length === 0) {
              // polyfill since RTCIceGatherer.state is not implemented in Edge 10547 yet.
              if (iceGatherer.state === undefined) {
                iceGatherer.state = 'completed';
              }

              // Emit a candidate with type endOfCandidates to make the samples work.
              // Edge requires addIceCandidate with this empty candidate to start checking.
              // The real solution is to signal end-of-candidates to the other side when
              // getting the null candidate but some apps (like the samples) don't do that.
              event.candidate.candidate =
                'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';
            } else {
              // RTCIceCandidate doesn't have a component, needs to be added
              cand.component = iceTransport.component === 'RTCP' ? 2 : 1;
              event.candidate.candidate = SDPUtils.writeCandidate(cand);
            }

            var complete = self.transceivers.every(function(transceiver) {
              return transceiver.iceGatherer &&
                transceiver.iceGatherer.state === 'completed';
            });
            // FIXME: update .localDescription with candidate and (potentially) end-of-candidates.
            //     To make this harder, the gatherer might emit candidates before localdescription
            //     is set. To make things worse, gather.getLocalCandidates still errors in
            //     Edge 10547 when no candidates have been gathered yet.

            if (self.onicecandidate !== null) {
              // Emit candidate if localDescription is set.
              // Also emits null candidate when all gatherers are complete.
              if (self.localDescription && self.localDescription.type === '') {
                self._localIceCandidatesBuffer.push(event);
                if (complete) {
                  self._localIceCandidatesBuffer.push({});
                }
              } else {
                self.onicecandidate(event);
                if (complete) {
                  self.onicecandidate({});
                }
              }
            }
          };
          iceTransport.onicestatechange = function() {
            self._updateConnectionState();
          };

          var dtlsTransport = new RTCDtlsTransport(iceTransport);
          dtlsTransport.ondtlsstatechange = function() {
            self._updateConnectionState();
          };
          dtlsTransport.onerror = function() {
            // onerror does not set state to failed by itself.
            dtlsTransport.state = 'failed';
            self._updateConnectionState();
          };

          return {
            iceGatherer: iceGatherer,
            iceTransport: iceTransport,
            dtlsTransport: dtlsTransport
          };
        };

      // Start the RTP Sender and Receiver for a transceiver.
      window.RTCPeerConnection.prototype._transceive = function(transceiver,
        send, recv) {
        var params = this._getCommonCapabilities(transceiver.localCapabilities,
          transceiver.remoteCapabilities);
        if (send && transceiver.rtpSender) {
          params.encodings = [{
            ssrc: transceiver.sendSsrc
          }];
          params.rtcp = {
            cname: localCName,
            ssrc: transceiver.recvSsrc
          };
          transceiver.rtpSender.send(params);
        }
        if (recv && transceiver.rtpReceiver) {
          params.encodings = [{
            ssrc: transceiver.recvSsrc
          }];
          params.rtcp = {
            cname: transceiver.cname,
            ssrc: transceiver.sendSsrc
          };
          transceiver.rtpReceiver.receive(params);
        }
      };

      window.RTCPeerConnection.prototype.setLocalDescription =
        function(description) {
          var self = this;
          if (description.type === 'offer') {
            if (!this._pendingOffer) {} else {
              this.transceivers = this._pendingOffer;
              delete this._pendingOffer;
            }
          } else if (description.type === 'answer') {
            var sections = SDPUtils.splitSections(self.remoteDescription.sdp);
            var sessionpart = sections.shift();
            sections.forEach(function(mediaSection, sdpMLineIndex) {
              var transceiver = self.transceivers[sdpMLineIndex];
              var iceGatherer = transceiver.iceGatherer;
              var iceTransport = transceiver.iceTransport;
              var dtlsTransport = transceiver.dtlsTransport;
              var localCapabilities = transceiver.localCapabilities;
              var remoteCapabilities = transceiver.remoteCapabilities;
              var rejected = mediaSection.split('\n', 1)[0]
                .split(' ', 2)[1] === '0';

              if (!rejected) {
                var remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
                  sessionpart);
                iceTransport.start(iceGatherer, remoteIceParameters, 'controlled');

                var remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
                  sessionpart);
                dtlsTransport.start(remoteDtlsParameters);

                // Calculate intersection of capabilities.
                var params = self._getCommonCapabilities(localCapabilities,
                  remoteCapabilities);

                // Start the RTCRtpSender. The RTCRtpReceiver for this transceiver
                // has already been started in setRemoteDescription.
                self._transceive(transceiver,
                  params.codecs.length > 0,
                  false);
              }
            });
          }

          this.localDescription = description;
          switch (description.type) {
            case 'offer':
              this._updateSignalingState('have-local-offer');
              break;
            case 'answer':
              this._updateSignalingState('stable');
              break;
            default:
              throw new TypeError('unsupported type "' + description.type + '"');
          }

          // If a success callback was provided, emit ICE candidates after it has been
          // executed. Otherwise, emit callback after the Promise is resolved.
          var hasCallback = arguments.length > 1 &&
            typeof arguments[1] === 'function';
          if (hasCallback) {
            var cb = arguments[1];
            window.setTimeout(function() {
              cb();
              self._emitBufferedCandidates();
            }, 0);
          }
          var p = Promise.resolve();
          p.then(function() {
            if (!hasCallback) {
              window.setTimeout(self._emitBufferedCandidates.bind(self), 0);
            }
          });
          return p;
        };

      window.RTCPeerConnection.prototype.setRemoteDescription =
        function(description) {
          var self = this;
          var stream = new MediaStream();
          var sections = SDPUtils.splitSections(description.sdp);
          var sessionpart = sections.shift();
          sections.forEach(function(mediaSection, sdpMLineIndex) {
            var lines = SDPUtils.splitLines(mediaSection);
            var mline = lines[0].substr(2).split(' ');
            var kind = mline[0];
            var rejected = mline[1] === '0';
            var direction = SDPUtils.getDirection(mediaSection, sessionpart);

            var transceiver;
            var iceGatherer;
            var iceTransport;
            var dtlsTransport;
            var rtpSender;
            var rtpReceiver;
            var sendSsrc;
            var recvSsrc;
            var localCapabilities;

            // FIXME: ensure the mediaSection has rtcp-mux set.
            var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
            var remoteIceParameters;
            var remoteDtlsParameters;
            if (!rejected) {
              remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
                sessionpart);
              remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
                sessionpart);
            }
            var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0].substr(6);

            var cname;
            // Gets the first SSRC. Note that with RTX there might be multiple SSRCs.
            var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
              .map(function(line) {
                return SDPUtils.parseSsrcMedia(line);
              })
              .filter(function(obj) {
                return obj.attribute === 'cname';
              })[0];
            if (remoteSsrc) {
              recvSsrc = parseInt(remoteSsrc.ssrc, 10);
              cname = remoteSsrc.value;
            }

            if (description.type === 'offer') {
              var transports = self._createIceAndDtlsTransports(mid, sdpMLineIndex);

              localCapabilities = RTCRtpReceiver.getCapabilities(kind);
              sendSsrc = (2 * sdpMLineIndex + 2) * 1001;

              rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);

              // FIXME: not correct when there are multiple streams but that is
              // not currently supported in this shim.
              stream.addTrack(rtpReceiver.track);

              // FIXME: look at direction.
              if (self.localStreams.length > 0 &&
                self.localStreams[0].getTracks().length >= sdpMLineIndex) {
                // FIXME: actually more complicated, needs to match types etc
                var localtrack = self.localStreams[0].getTracks()[sdpMLineIndex];
                rtpSender = new RTCRtpSender(localtrack, transports.dtlsTransport);
              }

              self.transceivers[sdpMLineIndex] = {
                iceGatherer: transports.iceGatherer,
                iceTransport: transports.iceTransport,
                dtlsTransport: transports.dtlsTransport,
                localCapabilities: localCapabilities,
                remoteCapabilities: remoteCapabilities,
                rtpSender: rtpSender,
                rtpReceiver: rtpReceiver,
                kind: kind,
                mid: mid,
                cname: cname,
                sendSsrc: sendSsrc,
                recvSsrc: recvSsrc
              };
              // Start the RTCRtpReceiver now. The RTPSender is started in setLocalDescription.
              self._transceive(self.transceivers[sdpMLineIndex],
                false,
                direction === 'sendrecv' || direction === 'sendonly');
            } else if (description.type === 'answer' && !rejected) {
              transceiver = self.transceivers[sdpMLineIndex];
              iceGatherer = transceiver.iceGatherer;
              iceTransport = transceiver.iceTransport;
              dtlsTransport = transceiver.dtlsTransport;
              rtpSender = transceiver.rtpSender;
              rtpReceiver = transceiver.rtpReceiver;
              sendSsrc = transceiver.sendSsrc;
              //recvSsrc = transceiver.recvSsrc;
              localCapabilities = transceiver.localCapabilities;

              self.transceivers[sdpMLineIndex].recvSsrc = recvSsrc;
              self.transceivers[sdpMLineIndex].remoteCapabilities =
                remoteCapabilities;
              self.transceivers[sdpMLineIndex].cname = cname;

              iceTransport.start(iceGatherer, remoteIceParameters, 'controlling');
              dtlsTransport.start(remoteDtlsParameters);

              self._transceive(transceiver,
                direction === 'sendrecv' || direction === 'recvonly',
                direction === 'sendrecv' || direction === 'sendonly');

              if (rtpReceiver &&
                (direction === 'sendrecv' || direction === 'sendonly')) {
                stream.addTrack(rtpReceiver.track);
              } else {
                // FIXME: actually the receiver should be created later.
                delete transceiver.rtpReceiver;
              }
            }
          });

          this.remoteDescription = description;
          switch (description.type) {
            case 'offer':
              this._updateSignalingState('have-remote-offer');
              break;
            case 'answer':
              this._updateSignalingState('stable');
              break;
            default:
              throw new TypeError('unsupported type "' + description.type + '"');
          }
          window.setTimeout(function() {
            if (self.onaddstream !== null && stream.getTracks().length) {
              self.remoteStreams.push(stream);
              window.setTimeout(function() {
                self.onaddstream({
                  stream: stream
                });
              }, 0);
            }
          }, 0);
          if (arguments.length > 1 && typeof arguments[1] === 'function') {
            window.setTimeout(arguments[1], 0);
          }
          return Promise.resolve();
        };

      window.RTCPeerConnection.prototype.close = function() {
        this.transceivers.forEach(function(transceiver) {
          /* not yet
          if (transceiver.iceGatherer) {
            transceiver.iceGatherer.close();
          }
          */
          if (transceiver.iceTransport) {
            transceiver.iceTransport.stop();
          }
          if (transceiver.dtlsTransport) {
            transceiver.dtlsTransport.stop();
          }
          if (transceiver.rtpSender) {
            transceiver.rtpSender.stop();
          }
          if (transceiver.rtpReceiver) {
            transceiver.rtpReceiver.stop();
          }
        });
        // FIXME: clean up tracks, local streams, remote streams, etc
        this._updateSignalingState('closed');
      };

      // Update the signaling state.
      window.RTCPeerConnection.prototype._updateSignalingState =
        function(newState) {
          this.signalingState = newState;
          if (this.onsignalingstatechange !== null) {
            this.onsignalingstatechange();
          }
        };

      // Determine whether to fire the negotiationneeded event.
      window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =
        function() {
          // Fire away (for now).
          if (this.onnegotiationneeded !== null) {
            this.onnegotiationneeded();
          }
        };

      // Update the connection state.
      window.RTCPeerConnection.prototype._updateConnectionState =
        function() {
          var self = this;
          var newState;
          var states = {
            'new': 0,
            closed: 0,
            connecting: 0,
            checking: 0,
            connected: 0,
            completed: 0,
            failed: 0
          };
          this.transceivers.forEach(function(transceiver) {
            states[transceiver.iceTransport.state]++;
            states[transceiver.dtlsTransport.state]++;
          });
          // ICETransport.completed and connected are the same for this purpose.
          states.connected += states.completed;

          newState = 'new';
          if (states.failed > 0) {
            newState = 'failed';
          } else if (states.connecting > 0 || states.checking > 0) {
            newState = 'connecting';
          } else if (states.disconnected > 0) {
            newState = 'disconnected';
          } else if (states.new > 0) {
            newState = 'new';
          } else if (states.connecting > 0 || states.completed > 0) {
            newState = 'connected';
          }

          if (newState !== self.iceConnectionState) {
            self.iceConnectionState = newState;
            if (this.oniceconnectionstatechange !== null) {
              this.oniceconnectionstatechange();
            }
          }
        };

      window.RTCPeerConnection.prototype.createOffer = function() {
        var self = this;
        if (this._pendingOffer) {
          throw new Error('createOffer called while there is a pending offer.');
        }
        var offerOptions;
        if (arguments.length === 1 && typeof arguments[0] !== 'function') {
          offerOptions = arguments[0];
        } else if (arguments.length === 3) {
          offerOptions = arguments[2];
        }

        var tracks = [];
        var numAudioTracks = 0;
        var numVideoTracks = 0;
        // Default to sendrecv.
        if (this.localStreams.length) {
          numAudioTracks = this.localStreams[0].getAudioTracks().length;
          numVideoTracks = this.localStreams[0].getVideoTracks().length;
        }
        // Determine number of audio and video tracks we need to send/recv.
        if (offerOptions) {
          // Reject Chrome legacy constraints.
          if (offerOptions.mandatory || offerOptions.optional) {
            throw new TypeError(
              'Legacy mandatory/optional constraints not supported.');
          }
          if (offerOptions.offerToReceiveAudio !== undefined) {
            numAudioTracks = offerOptions.offerToReceiveAudio;
          }
          if (offerOptions.offerToReceiveVideo !== undefined) {
            numVideoTracks = offerOptions.offerToReceiveVideo;
          }
        }
        if (this.localStreams.length) {
          // Push local streams.
          this.localStreams[0].getTracks().forEach(function(track) {
            tracks.push({
              kind: track.kind,
              track: track,
              wantReceive: track.kind === 'audio' ?
                numAudioTracks > 0 : numVideoTracks > 0
            });
            if (track.kind === 'audio') {
              numAudioTracks--;
            } else if (track.kind === 'video') {
              numVideoTracks--;
            }
          });
        }
        // Create M-lines for recvonly streams.
        while (numAudioTracks > 0 || numVideoTracks > 0) {
          if (numAudioTracks > 0) {
            tracks.push({
              kind: 'audio',
              wantReceive: true
            });
            numAudioTracks--;
          }
          if (numVideoTracks > 0) {
            tracks.push({
              kind: 'video',
              wantReceive: true
            });
            numVideoTracks--;
          }
        }

        var sdp = SDPUtils.writeSessionBoilerplate();
        var transceivers = [];
        tracks.forEach(function(mline, sdpMLineIndex) {
          // For each track, create an ice gatherer, ice transport, dtls transport,
          // potentially rtpsender and rtpreceiver.
          var track = mline.track;
          var kind = mline.kind;
          var mid = generateIdentifier();

          var transports = self._createIceAndDtlsTransports(mid, sdpMLineIndex);

          var localCapabilities = RTCRtpSender.getCapabilities(kind);
          var rtpSender;
          var rtpReceiver;

          // generate an ssrc now, to be used later in rtpSender.send
          var sendSsrc = (2 * sdpMLineIndex + 1) * 1001;
          if (track) {
            rtpSender = new RTCRtpSender(track, transports.dtlsTransport);
          }

          if (mline.wantReceive) {
            rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
          }

          transceivers[sdpMLineIndex] = {
            iceGatherer: transports.iceGatherer,
            iceTransport: transports.iceTransport,
            dtlsTransport: transports.dtlsTransport,
            localCapabilities: localCapabilities,
            remoteCapabilities: null,
            rtpSender: rtpSender,
            rtpReceiver: rtpReceiver,
            kind: kind,
            mid: mid,
            sendSsrc: sendSsrc,
            recvSsrc: null
          };
          var transceiver = transceivers[sdpMLineIndex];
          sdp += SDPUtils.writeMediaSection(transceiver,
            transceiver.localCapabilities, 'offer', self.localStreams[0]);
        });

        this._pendingOffer = transceivers;
        var desc = new RTCSessionDescription({
          type: 'offer',
          sdp: sdp
        });
        if (arguments.length && typeof arguments[0] === 'function') {
          window.setTimeout(arguments[0], 0, desc);
        }
        return Promise.resolve(desc);
      };

      window.RTCPeerConnection.prototype.createAnswer = function() {
        var self = this;
        var answerOptions;
        if (arguments.length === 1 && typeof arguments[0] !== 'function') {
          answerOptions = arguments[0];
        } else if (arguments.length === 3) {
          answerOptions = arguments[2];
        }

        var sdp = SDPUtils.writeSessionBoilerplate();
        this.transceivers.forEach(function(transceiver) {
          // Calculate intersection of capabilities.
          var commonCapabilities = self._getCommonCapabilities(
            transceiver.localCapabilities,
            transceiver.remoteCapabilities);

          sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,
            'answer', self.localStreams[0]);
        });

        var desc = new RTCSessionDescription({
          type: 'answer',
          sdp: sdp
        });
        if (arguments.length && typeof arguments[0] === 'function') {
          window.setTimeout(arguments[0], 0, desc);
        }
        return Promise.resolve(desc);
      };

      window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
        var mLineIndex = candidate.sdpMLineIndex;
        if (candidate.sdpMid) {
          for (var i = 0; i < this.transceivers.length; i++) {
            if (this.transceivers[i].mid === candidate.sdpMid) {
              mLineIndex = i;
              break;
            }
          }
        }
        var transceiver = this.transceivers[mLineIndex];
        if (transceiver) {
          var cand = Object.keys(candidate.candidate).length > 0 ?
            SDPUtils.parseCandidate(candidate.candidate) : {};
          // Ignore Chrome's invalid candidates since Edge does not like them.
          if (cand.protocol === 'tcp' && cand.port === 0) {
            return;
          }
          // Ignore RTCP candidates, we assume RTCP-MUX.
          if (cand.component !== '1') {
            return;
          }
          // A dirty hack to make samples work.
          if (cand.type === 'endOfCandidates') {
            cand = {};
          }
          transceiver.iceTransport.addRemoteCandidate(cand);
        }
        if (arguments.length > 1 && typeof arguments[1] === 'function') {
          window.setTimeout(arguments[1], 0);
        }
        return Promise.resolve();
      };

      window.RTCPeerConnection.prototype.getStats = function() {
        var promises = [];
        this.transceivers.forEach(function(transceiver) {
          ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
            'dtlsTransport'
          ].forEach(function(method) {
            if (transceiver[method]) {
              promises.push(transceiver[method].getStats());
            }
          });
        });
        var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&
          arguments[1];
        return new Promise(function(resolve) {
          var results = {};
          Promise.all(promises).then(function(res) {
            res.forEach(function(result) {
              Object.keys(result).forEach(function(id) {
                results[id] = result[id];
              });
            });
            if (cb) {
              window.setTimeout(cb, 0, results);
            }
            resolve(results);
          });
        });
      };
    }
  } else {
    webrtcUtils.log('Browser does not appear to be WebRTC-capable');
  }

  // Polyfill ontrack on browsers that don't yet have it
  if (typeof window === 'object' && !('ontrack' in window.RTCPeerConnection)) {
    Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
      get: function() {
        return this._ontrack;
      },
      set: function(f) {
        if (this._ontrack) {
          this.removeEventListener('track', this._ontrack);
          this.removeEventListener('addstream', this._ontrackpoly);
        }
        this.addEventListener('track', this._ontrack = f);
        this.addEventListener('addstream', this._ontrackpoly = function(e) {
          e.stream.getTracks().forEach(function(track) {
            var event = new Event('track');
            event.track = track;
            event.receiver = {
              track: track
            };
            event.streams = [e.stream];
            this.dispatchEvent(event);
          }.bind(this));
        }.bind(this));
      }
    });
  }

  // Returns the result of getUserMedia as a Promise.
  function requestUserMedia(constraints) {
    return new Promise(function(resolve, reject) {
      getUserMedia(constraints, resolve, reject);
    });
  }

  var webrtcTesting = {};
  try {
    Object.defineProperty(webrtcTesting, 'version', {
      set: function(version) {
        webrtcDetectedVersion = version;
      }
    });
  } catch (e) {}

  if (typeof module !== 'undefined') {
    var RTCPeerConnection;
    var RTCIceCandidate;
    var RTCSessionDescription;
    if (typeof window !== 'undefined') {
      RTCPeerConnection = window.RTCPeerConnection;
      RTCIceCandidate = window.RTCIceCandidate;
      RTCSessionDescription = window.RTCSessionDescription;
    }
    module.exports = {
      RTCPeerConnection: RTCPeerConnection,
      RTCIceCandidate: RTCIceCandidate,
      RTCSessionDescription: RTCSessionDescription,
      getUserMedia: getUserMedia,
      attachMediaStream: attachMediaStream,
      reattachMediaStream: reattachMediaStream,
      webrtcDetectedBrowser: webrtcDetectedBrowser,
      webrtcDetectedVersion: webrtcDetectedVersion,
      webrtcMinimumVersion: webrtcMinimumVersion,
      webrtcTesting: webrtcTesting,
      webrtcUtils: webrtcUtils
        //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,
        RTCIceCandidate: window.RTCIceCandidate,
        RTCSessionDescription: window.RTCSessionDescription,
        getUserMedia: getUserMedia,
        attachMediaStream: attachMediaStream,
        reattachMediaStream: reattachMediaStream,
        webrtcDetectedBrowser: webrtcDetectedBrowser,
        webrtcDetectedVersion: webrtcDetectedVersion,
        webrtcMinimumVersion: webrtcMinimumVersion,
        webrtcTesting: webrtcTesting,
        webrtcUtils: webrtcUtils
          //requestUserMedia: not exposed on purpose.
          //trace: not exposed on purpose.
      };
    });
  }

}, this);

Package Settings

The raw package settings options

{
    "apex": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "arduino": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "c": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "cfml": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "coffeescript": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 0,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "coffee-fmt",
        "beautify_on_save": false
    },
    "cpp": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "cs": {
        "beautify_on_save": true,
        "configPath": "/Users/waldgeist/Documents/Development/htdocs/marble/unity/.uncrustify.cfg",
        "disabled": false,
        "default_beautifier": "Uncrustify"
    },
    "css": {
        "beautify_on_save": true,
        "newline_between_rules": true,
        "preserve_newlines": true,
        "indent_size": 2,
        "indent_char": " ",
        "selector_separator_newline": false,
        "wrap_line_length": 0,
        "end_with_newline": false,
        "indent_comments": true,
        "force_indentation": false,
        "convert_quotes": "none",
        "align_assignments": false,
        "no_lead_zero": false,
        "configPath": "",
        "predefinedConfig": "csscomb",
        "disabled": false,
        "default_beautifier": "JS Beautify"
    },
    "csv": {
        "beautify_on_save": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "d": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "ejs": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 250,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "indent_inner_html": false,
        "indent_scripts": "normal",
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "JS Beautify",
        "beautify_on_save": false
    },
    "erb": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "fortran": {
        "emacs_path": "",
        "emacs_script_path": "",
        "disabled": false,
        "default_beautifier": "Fortran Beautifier",
        "beautify_on_save": false
    },
    "general": {
        "_analyticsUserId": "d518c5b5-8771-480d-a18e-3200d3353ec5",
        "analytics": false,
        "loggerLevel": "warn",
        "beautifyEntireFileOnSave": true,
        "muteUnsupportedLanguageErrors": false,
        "muteAllErrors": false,
        "showLoadingView": true
    },
    "gherkin": {
        "indent_size": 2,
        "indent_char": " ",
        "disabled": false,
        "default_beautifier": "Gherkin formatter",
        "beautify_on_save": false
    },
    "handlebars": {
        "beautify_on_save": true,
        "indent_inner_html": false,
        "indent_size": 2,
        "indent_char": " ",
        "brace_style": "collapse",
        "indent_scripts": "normal",
        "wrap_line_length": 250,
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "end_with_newline": false,
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "JS Beautify"
    },
    "html": {
        "beautify_on_save": true,
        "end_with_newline": true,
        "indent_inner_html": false,
        "indent_size": 2,
        "indent_char": " ",
        "brace_style": "collapse",
        "indent_scripts": "normal",
        "wrap_line_length": 250,
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "JS Beautify"
    },
    "jade": {
        "indent_size": 2,
        "indent_char": " ",
        "disabled": false,
        "default_beautifier": "Pug Beautify",
        "beautify_on_save": false
    },
    "java": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "js": {
        "beautify_on_save": true,
        "end_with_newline": true,
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 0,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "JS Beautify"
    },
    "json": {
        "beautify_on_save": true,
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 0,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "JS Beautify"
    },
    "jsx": {
        "beautify_on_save": true,
        "e4x": true,
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 0,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "latex": {
        "indent_char": " ",
        "indent_with_tabs": false,
        "indent_preamble": false,
        "always_look_for_split_braces": true,
        "always_look_for_split_brackets": false,
        "remove_trailing_whitespace": false,
        "align_columns_in_environments": [
            "tabular",
            "matrix",
            "bmatrix",
            "pmatrix"
        ],
        "disabled": false,
        "default_beautifier": "Latex Beautify",
        "beautify_on_save": false
    },
    "less": {
        "indent_size": 2,
        "indent_char": " ",
        "newline_between_rules": true,
        "preserve_newlines": false,
        "wrap_line_length": 0,
        "indent_comments": true,
        "force_indentation": false,
        "convert_quotes": "none",
        "align_assignments": false,
        "no_lead_zero": false,
        "configPath": "",
        "predefinedConfig": "csscomb",
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "marko": {
        "indent_size": 2,
        "indent_char": " ",
        "syntax": "html",
        "indent_inner_html": false,
        "brace_style": "collapse",
        "indent_scripts": "normal",
        "wrap_line_length": 250,
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "end_with_newline": false,
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "Marko Beautifier",
        "beautify_on_save": false
    },
    "mustache": {
        "indent_inner_html": false,
        "indent_size": 2,
        "indent_char": " ",
        "brace_style": "collapse",
        "indent_scripts": "normal",
        "wrap_line_length": 250,
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "end_with_newline": false,
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "JS Beautify",
        "beautify_on_save": false
    },
    "objectivec": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "pawn": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "perl": {
        "perltidy_profile": "",
        "disabled": false,
        "default_beautifier": "Perltidy",
        "beautify_on_save": false
    },
    "php": {
        "beautify_on_save": true,
        "cs_fixer_path": "",
        "cs_fixer_version": 2,
        "cs_fixer_config_file": "",
        "fixers": "",
        "level": "",
        "rules": "",
        "allow_risky": "no",
        "phpcbf_path": "",
        "phpcbf_version": 2,
        "standard": "PEAR",
        "disabled": false,
        "default_beautifier": "PHP-CS-Fixer"
    },
    "python": {
        "max_line_length": 79,
        "indent_size": 4,
        "ignore": [
            "E24"
        ],
        "formater": "autopep8",
        "style_config": "pep8",
        "sort_imports": false,
        "multi_line_output": "Hanging Grid Grouped",
        "disabled": false,
        "default_beautifier": "autopep8",
        "beautify_on_save": false
    },
    "riot": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "ruby": {
        "indent_size": 2,
        "indent_char": " ",
        "rubocop_path": "",
        "disabled": false,
        "default_beautifier": "Rubocop",
        "beautify_on_save": false
    },
    "rust": {
        "rustfmt_path": "",
        "disabled": false,
        "default_beautifier": "rustfmt",
        "beautify_on_save": false
    },
    "sass": {
        "disabled": false,
        "default_beautifier": "SassConvert",
        "beautify_on_save": false
    },
    "scss": {
        "beautify_on_save": true,
        "newline_between_rules": true,
        "preserve_newlines": true,
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 0,
        "indent_comments": true,
        "force_indentation": false,
        "convert_quotes": "none",
        "align_assignments": false,
        "no_lead_zero": false,
        "configPath": "",
        "predefinedConfig": "csscomb",
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "spacebars": {
        "beautify_on_save": true,
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "sql": {
        "indent_size": 2,
        "keywords": "upper",
        "identifiers": "unchanged",
        "disabled": false,
        "default_beautifier": "sqlformat",
        "beautify_on_save": false
    },
    "svg": {
        "beautify_on_save": true,
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "swig": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "tss": {
        "indent_size": 2,
        "indent_char": " ",
        "newline_between_rules": true,
        "preserve_newlines": false,
        "wrap_line_length": 0,
        "indent_comments": true,
        "force_indentation": false,
        "convert_quotes": "none",
        "align_assignments": false,
        "no_lead_zero": false,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "twig": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "space_in_paren": false,
        "space_after_anon_function": false,
        "break_chained_methods": false,
        "wrap_line_length": 250,
        "end_with_comma": false,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "typescript": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 0,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "TypeScript Formatter",
        "beautify_on_save": false
    },
    "vala": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "visualforce": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "xml": {
        "beautify_on_save": true,
        "indent_inner_html": false,
        "indent_size": 2,
        "indent_char": " ",
        "brace_style": "collapse",
        "indent_scripts": "normal",
        "wrap_line_length": 250,
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "end_with_newline": false,
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "xtemplate": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "bash": {
        "indent_size": 2,
        "disabled": false,
        "default_beautifier": "beautysh",
        "beautify_on_save": false
    },
    "clj": {
        "disabled": false,
        "default_beautifier": "cljfmt",
        "beautify_on_save": false
    },
    "crystal": {
        "disabled": false,
        "default_beautifier": "Crystal",
        "beautify_on_save": false
    },
    "elm": {
        "disabled": false,
        "default_beautifier": "elm-format",
        "beautify_on_save": false
    },
    "erlang": {
        "disabled": false,
        "default_beautifier": "erl_tidy",
        "beautify_on_save": false
    },
    "glsl": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "clang-format",
        "beautify_on_save": false
    },
    "go": {
        "disabled": false,
        "default_beautifier": "gofmt",
        "beautify_on_save": false
    },
    "gohtml": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "haskell": {
        "disabled": false,
        "default_beautifier": "stylish-haskell",
        "beautify_on_save": false
    },
    "lua": {
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "Lua beautifier",
        "beautify_on_save": false
    },
    "markdown": {
        "gfm": true,
        "yaml": true,
        "commonmark": false,
        "disabled": false,
        "default_beautifier": "Tidy Markdown",
        "beautify_on_save": false
    },
    "nginx": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_with_tabs": false,
        "dontJoinCurlyBracet": true,
        "disabled": false,
        "default_beautifier": "Nginx Beautify",
        "beautify_on_save": false
    },
    "nunjucks": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "ocaml": {
        "disabled": false,
        "default_beautifier": "ocp-indent",
        "beautify_on_save": false
    },
    "puppet": {
        "disabled": false,
        "default_beautifier": "puppet-lint",
        "beautify_on_save": false
    },
    "r": {
        "indent_size": 2,
        "disabled": false,
        "default_beautifier": "formatR",
        "beautify_on_save": false
    },
    "ux": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "vue": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 250,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "indent_inner_html": false,
        "indent_scripts": "normal",
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "Vue Beautifier",
        "beautify_on_save": false
    },
    "yaml": {
        "padding": 0,
        "disabled": false,
        "default_beautifier": "align-yaml",
        "beautify_on_save": false
    },
    "executables": {
        "uncrustify": {
            "path": ""
        },
        "autopep8": {
            "path": ""
        },
        "isort": {
            "path": ""
        },
        "clang-format": {
            "path": ""
        },
        "crystal": {
            "path": ""
        },
        "dfmt": {
            "path": ""
        },
        "elm-format": {
            "path": ""
        },
        "goimports": {
            "path": ""
        },
        "emacs": {
            "path": ""
        },
        "php": {
            "path": ""
        },
        "php-cs-fixer": {
            "path": ""
        },
        "phpcbf": {
            "path": ""
        },
        "sass-convert": {
            "path": ""
        },
        "rscript": {
            "path": ""
        },
        "beautysh": {
            "path": ""
        }
    }
}

Beautification options

Editor Options: Options from Atom Editor settings

{
    "_default": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_with_tabs": false
    }
}

Config Options: Options from Atom Beautify package settings

{
    "apex": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "arduino": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "bash": {
        "indent_size": 2,
        "disabled": false,
        "default_beautifier": "beautysh",
        "beautify_on_save": false
    },
    "cs": {
        "beautify_on_save": true,
        "configPath": "/Users/waldgeist/Documents/Development/htdocs/marble/unity/.uncrustify.cfg",
        "disabled": false,
        "default_beautifier": "Uncrustify"
    },
    "c": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "clj": {
        "disabled": false,
        "default_beautifier": "cljfmt",
        "beautify_on_save": false
    },
    "coffeescript": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 0,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "coffee-fmt",
        "beautify_on_save": false
    },
    "cfml": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "cpp": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "crystal": {
        "disabled": false,
        "default_beautifier": "Crystal",
        "beautify_on_save": false
    },
    "css": {
        "beautify_on_save": true,
        "newline_between_rules": true,
        "preserve_newlines": true,
        "indent_size": 2,
        "indent_char": " ",
        "selector_separator_newline": false,
        "wrap_line_length": 0,
        "end_with_newline": false,
        "indent_comments": true,
        "force_indentation": false,
        "convert_quotes": "none",
        "align_assignments": false,
        "no_lead_zero": false,
        "configPath": "",
        "predefinedConfig": "csscomb",
        "disabled": false,
        "default_beautifier": "JS Beautify"
    },
    "csv": {
        "beautify_on_save": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "d": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "ejs": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 250,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "indent_inner_html": false,
        "indent_scripts": "normal",
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "JS Beautify",
        "beautify_on_save": false
    },
    "elm": {
        "disabled": false,
        "default_beautifier": "elm-format",
        "beautify_on_save": false
    },
    "erb": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "erlang": {
        "disabled": false,
        "default_beautifier": "erl_tidy",
        "beautify_on_save": false
    },
    "gherkin": {
        "indent_size": 2,
        "indent_char": " ",
        "disabled": false,
        "default_beautifier": "Gherkin formatter",
        "beautify_on_save": false
    },
    "glsl": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "clang-format",
        "beautify_on_save": false
    },
    "go": {
        "disabled": false,
        "default_beautifier": "gofmt",
        "beautify_on_save": false
    },
    "gohtml": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "fortran": {
        "emacs_path": "",
        "emacs_script_path": "",
        "disabled": false,
        "default_beautifier": "Fortran Beautifier",
        "beautify_on_save": false
    },
    "handlebars": {
        "beautify_on_save": true,
        "indent_inner_html": false,
        "indent_size": 2,
        "indent_char": " ",
        "brace_style": "collapse",
        "indent_scripts": "normal",
        "wrap_line_length": 250,
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "end_with_newline": false,
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "JS Beautify"
    },
    "haskell": {
        "disabled": false,
        "default_beautifier": "stylish-haskell",
        "beautify_on_save": false
    },
    "html": {
        "beautify_on_save": true,
        "end_with_newline": true,
        "indent_inner_html": false,
        "indent_size": 2,
        "indent_char": " ",
        "brace_style": "collapse",
        "indent_scripts": "normal",
        "wrap_line_length": 250,
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "JS Beautify"
    },
    "jade": {
        "indent_size": 2,
        "indent_char": " ",
        "disabled": false,
        "default_beautifier": "Pug Beautify",
        "beautify_on_save": false
    },
    "java": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "js": {
        "beautify_on_save": true,
        "end_with_newline": true,
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 0,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "JS Beautify"
    },
    "json": {
        "beautify_on_save": true,
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 0,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "JS Beautify"
    },
    "jsx": {
        "beautify_on_save": true,
        "e4x": true,
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 0,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "latex": {
        "indent_char": " ",
        "indent_with_tabs": false,
        "indent_preamble": false,
        "always_look_for_split_braces": true,
        "always_look_for_split_brackets": false,
        "remove_trailing_whitespace": false,
        "align_columns_in_environments": [
            "tabular",
            "matrix",
            "bmatrix",
            "pmatrix"
        ],
        "disabled": false,
        "default_beautifier": "Latex Beautify",
        "beautify_on_save": false
    },
    "less": {
        "indent_size": 2,
        "indent_char": " ",
        "newline_between_rules": true,
        "preserve_newlines": false,
        "wrap_line_length": 0,
        "indent_comments": true,
        "force_indentation": false,
        "convert_quotes": "none",
        "align_assignments": false,
        "no_lead_zero": false,
        "configPath": "",
        "predefinedConfig": "csscomb",
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "lua": {
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "Lua beautifier",
        "beautify_on_save": false
    },
    "markdown": {
        "gfm": true,
        "yaml": true,
        "commonmark": false,
        "disabled": false,
        "default_beautifier": "Tidy Markdown",
        "beautify_on_save": false
    },
    "marko": {
        "indent_size": 2,
        "indent_char": " ",
        "syntax": "html",
        "indent_inner_html": false,
        "brace_style": "collapse",
        "indent_scripts": "normal",
        "wrap_line_length": 250,
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "end_with_newline": false,
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "Marko Beautifier",
        "beautify_on_save": false
    },
    "mustache": {
        "indent_inner_html": false,
        "indent_size": 2,
        "indent_char": " ",
        "brace_style": "collapse",
        "indent_scripts": "normal",
        "wrap_line_length": 250,
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "end_with_newline": false,
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "JS Beautify",
        "beautify_on_save": false
    },
    "nginx": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_with_tabs": false,
        "dontJoinCurlyBracet": true,
        "disabled": false,
        "default_beautifier": "Nginx Beautify",
        "beautify_on_save": false
    },
    "nunjucks": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "objectivec": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "ocaml": {
        "disabled": false,
        "default_beautifier": "ocp-indent",
        "beautify_on_save": false
    },
    "pawn": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "perl": {
        "perltidy_profile": "",
        "disabled": false,
        "default_beautifier": "Perltidy",
        "beautify_on_save": false
    },
    "php": {
        "beautify_on_save": true,
        "cs_fixer_path": "",
        "cs_fixer_version": 2,
        "cs_fixer_config_file": "",
        "fixers": "",
        "level": "",
        "rules": "",
        "allow_risky": "no",
        "phpcbf_path": "",
        "phpcbf_version": 2,
        "standard": "PEAR",
        "disabled": false,
        "default_beautifier": "PHP-CS-Fixer"
    },
    "puppet": {
        "disabled": false,
        "default_beautifier": "puppet-lint",
        "beautify_on_save": false
    },
    "python": {
        "max_line_length": 79,
        "indent_size": 4,
        "ignore": [
            "E24"
        ],
        "formater": "autopep8",
        "style_config": "pep8",
        "sort_imports": false,
        "multi_line_output": "Hanging Grid Grouped",
        "disabled": false,
        "default_beautifier": "autopep8",
        "beautify_on_save": false
    },
    "r": {
        "indent_size": 2,
        "disabled": false,
        "default_beautifier": "formatR",
        "beautify_on_save": false
    },
    "riot": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "ruby": {
        "indent_size": 2,
        "indent_char": " ",
        "rubocop_path": "",
        "disabled": false,
        "default_beautifier": "Rubocop",
        "beautify_on_save": false
    },
    "rust": {
        "rustfmt_path": "",
        "disabled": false,
        "default_beautifier": "rustfmt",
        "beautify_on_save": false
    },
    "sass": {
        "disabled": false,
        "default_beautifier": "SassConvert",
        "beautify_on_save": false
    },
    "scss": {
        "beautify_on_save": true,
        "newline_between_rules": true,
        "preserve_newlines": true,
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 0,
        "indent_comments": true,
        "force_indentation": false,
        "convert_quotes": "none",
        "align_assignments": false,
        "no_lead_zero": false,
        "configPath": "",
        "predefinedConfig": "csscomb",
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "spacebars": {
        "beautify_on_save": true,
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "sql": {
        "indent_size": 2,
        "keywords": "upper",
        "identifiers": "unchanged",
        "disabled": false,
        "default_beautifier": "sqlformat",
        "beautify_on_save": false
    },
    "svg": {
        "beautify_on_save": true,
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "swig": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "tss": {
        "indent_size": 2,
        "indent_char": " ",
        "newline_between_rules": true,
        "preserve_newlines": false,
        "wrap_line_length": 0,
        "indent_comments": true,
        "force_indentation": false,
        "convert_quotes": "none",
        "align_assignments": false,
        "no_lead_zero": false,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "twig": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "space_in_paren": false,
        "space_after_anon_function": false,
        "break_chained_methods": false,
        "wrap_line_length": 250,
        "end_with_comma": false,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "typescript": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 0,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "disabled": false,
        "default_beautifier": "TypeScript Formatter",
        "beautify_on_save": false
    },
    "ux": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "vala": {
        "configPath": "",
        "disabled": false,
        "default_beautifier": "Uncrustify",
        "beautify_on_save": false
    },
    "vue": {
        "indent_size": 2,
        "indent_char": " ",
        "indent_level": 0,
        "indent_with_tabs": false,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "space_in_paren": false,
        "jslint_happy": false,
        "space_after_anon_function": false,
        "brace_style": "collapse",
        "break_chained_methods": false,
        "keep_array_indentation": false,
        "keep_function_indentation": false,
        "space_before_conditional": true,
        "eval_code": false,
        "unescape_strings": false,
        "wrap_line_length": 250,
        "end_with_newline": false,
        "end_with_comma": false,
        "end_of_line": "System Default",
        "indent_inner_html": false,
        "indent_scripts": "normal",
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "Vue Beautifier",
        "beautify_on_save": false
    },
    "visualforce": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "xml": {
        "beautify_on_save": true,
        "indent_inner_html": false,
        "indent_size": 2,
        "indent_char": " ",
        "brace_style": "collapse",
        "indent_scripts": "normal",
        "wrap_line_length": 250,
        "wrap_attributes": "auto",
        "wrap_attributes_indent_size": 2,
        "preserve_newlines": true,
        "max_preserve_newlines": 10,
        "unformatted": [
            "a",
            "abbr",
            "area",
            "audio",
            "b",
            "bdi",
            "bdo",
            "br",
            "button",
            "canvas",
            "cite",
            "code",
            "data",
            "datalist",
            "del",
            "dfn",
            "em",
            "embed",
            "i",
            "iframe",
            "img",
            "input",
            "ins",
            "kbd",
            "keygen",
            "label",
            "map",
            "mark",
            "math",
            "meter",
            "noscript",
            "object",
            "output",
            "progress",
            "q",
            "ruby",
            "s",
            "samp",
            "select",
            "small",
            "span",
            "strong",
            "sub",
            "sup",
            "svg",
            "template",
            "textarea",
            "time",
            "u",
            "var",
            "video",
            "wbr",
            "text",
            "acronym",
            "address",
            "big",
            "dt",
            "ins",
            "small",
            "strike",
            "tt",
            "pre",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6"
        ],
        "end_with_newline": false,
        "extra_liners": [
            "head",
            "body",
            "/html"
        ],
        "disabled": false,
        "default_beautifier": "Pretty Diff"
    },
    "xtemplate": {
        "indent_size": 2,
        "indent_char": " ",
        "wrap_line_length": 250,
        "preserve_newlines": true,
        "disabled": false,
        "default_beautifier": "Pretty Diff",
        "beautify_on_save": false
    },
    "yaml": {
        "padding": 0,
        "disabled": false,
        "default_beautifier": "align-yaml",
        "beautify_on_save": false
    }
}

Home Options: Options from /Users/waldgeist/.jsbeautifyrc

{
    "_default": {}
}

EditorConfig Options: Options from EditorConfig file

{
    "_default": {
        "indent_style": "space",
        "indent_size": 2,
        "end_of_line": "lf",
        "charset": "utf-8",
        "trim_trailing_whitespace": true,
        "insert_final_newline": true,
        "tab_width": 2,
        "indent_char": " "
    }
}

Project Options: Options from .jsbeautifyrc files starting from directory /Users/waldgeist/Documents/Development/htdocs/aiddogs/webapp/packages/guzz-live-session-standard/vendor and going up to root

[
    {
        "_default": {}
    },
    {
        "_default": {}
    },
    {
        "_default": {}
    },
    {
        "_default": {}
    },
    {
        "_default": {}
    },
    {
        "_default": {}
    },
    {
        "js": {
            "preserve_ternary_lines": true,
            "formatObject": true
        },
        "jsx": {
            "preserve_ternary_lines": true
        }
    },
    {
        "_default": {}
    },
    {
        "_default": {}
    },
    {
        "_default": {}
    }
]

Pre-Transformed Options: Combined options before transforming them given a beautifier's specifications

{
    "indent_size": 2,
    "indent_char": " ",
    "indent_with_tabs": false,
    "beautify_on_save": true,
    "end_with_newline": false,
    "indent_level": 0,
    "preserve_newlines": true,
    "max_preserve_newlines": 10,
    "space_in_paren": false,
    "jslint_happy": false,
    "space_after_anon_function": false,
    "brace_style": "collapse",
    "break_chained_methods": false,
    "keep_array_indentation": false,
    "keep_function_indentation": false,
    "space_before_conditional": true,
    "eval_code": false,
    "unescape_strings": false,
    "wrap_line_length": 0,
    "end_with_comma": false,
    "end_of_line": "lf",
    "disabled": false,
    "default_beautifier": "Pretty Diff",
    "e4x": true,
    "indent_style": "space",
    "charset": "utf-8",
    "trim_trailing_whitespace": true,
    "insert_final_newline": true,
    "tab_width": 2,
    "preserve_ternary_lines": true,
    "formatObject": true
}

Final Options

Final combined and transformed options that are used

{
    "inchar": " ",
    "insize": 2,
    "objsort": false,
    "preserve": "all",
    "comments": "indent",
    "vertical": "none",
    "wrap": 0,
    "space": false,
    "endcomma": false,
    "methodchain": true,
    "ternaryline": true,
    "bracepadding": false
}

Results

Beautified File Contents:

/*
 *  Copyright (c) 2016 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,
MediaStream, RTCIceGatherer, RTCIceTransport, RTCDtlsTransport,
RTCRtpSender, RTCRtpReceiver*/
/* exported trace,requestUserMedia */

'use strict';

app.livesession.adapterJS = _.bind(function() {

  // app.log('adding adapter.js');

  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);
    },
    extractVersion: function(uastring, expr, pos) {
      var match = uastring.match(expr);
      return match && match.length >= pos && parseInt(match[pos], 10);
    }
  };

  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);
    }
  }

  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) {
    webrtcUtils.log('This appears to be Firefox');

    webrtcDetectedBrowser = 'firefox';

    // the detected firefox version.
    webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, /Firefox\/([0-9]+)\./, 1);

    // the minimum firefox version still supported by adapter.
    webrtcMinimumVersion = 31;

    // Shim for RTCPeerConnection on older versions.
    if (!window.RTCPeerConnection) {
      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
      };

      // wrap static methods. Currently just generateCertificate.
      if (mozRTCPeerConnection.generateCertificate) {
        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
          get: function() {
            if (arguments.length) {
              return mozRTCPeerConnection.generateCertificate.apply(null, arguments);
            } else {
              return mozRTCPeerConnection.generateCertificate;
            }
          }
        });
      }

      window.RTCSessionDescription = mozRTCSessionDescription;
      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().then(undefined, function(e) {
          if (e.name === 'NotFoundError') {
            return [];
          }
          throw e;
        });
      };
    }
  } else if (navigator.webkitGetUserMedia && window.webkitRTCPeerConnection) {
    webrtcUtils.log('This appears to be Chrome');

    webrtcDetectedBrowser = 'chrome';

    // the detected chrome version.
    webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, /Chrom(e|ium)\/([0-9]+)\./, 2);

    // 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;
    };

    // wrap static methods. Currently just generateCertificate.
    if (webkitRTCPeerConnection.generateCertificate) {
      Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
        get: function() {
          if (arguments.length) {
            return webkitRTCPeerConnection.generateCertificate.apply(null, arguments);
          } else {
            return webkitRTCPeerConnection.generateCertificate;
          }
        }
      });
    }

    // 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 = webrtcUtils.extractVersion(navigator.userAgent, /Edge\/(\d+).(\d+)$/, 2);

    // The minimum version still supported by adapter.
    // This is the build number for Edge.
    webrtcMinimumVersion = 10547;

    if (window.RTCIceGatherer) {
      // Generate an alphanumeric identifier for cname or mids.
      // TODO: use UUIDs instead? https://gist.github.com/jed/982883
      var generateIdentifier = function() {
        return Math.random().toString(36).substr(2, 10);
      };

      // The RTCP CNAME used by all peerconnections from the same JS.
      var localCName = generateIdentifier();

      // SDP helpers - to be moved into separate module.
      var SDPUtils = {};

      // Splits SDP into lines, dealing with both CRLF and LF.
      SDPUtils.splitLines = function(blob) {
        return blob.trim().split('\n').map(function(line) {
          return line.trim();
        });
      };

      // Splits SDP into sessionpart and mediasections. Ensures CRLF.
      SDPUtils.splitSections = function(blob) {
        var parts = blob.split('\r\nm=');
        return parts.map(function(part, index) {
          return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
        });
      };

      // Returns lines that start with a certain prefix.
      SDPUtils.matchPrefix = function(blob, prefix) {
        return SDPUtils.splitLines(blob).filter(function(line) {
          return line.indexOf(prefix) === 0;
        });
      };

      // Parses an ICE candidate line. Sample input:
      // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 rport 55996"
      SDPUtils.parseCandidate = function(line) {
        var parts;
        // Parse both variants.
        if (line.indexOf('a=candidate:') === 0) {
          parts = line.substring(12).split(' ');
        } else {
          parts = line.substring(10).split(' ');
        }

        var candidate = {
          foundation: parts[0],
          component: parts[1],
          protocol: parts[2].toLowerCase(),
          priority: parseInt(parts[3], 10),
          ip: parts[4],
          port: parseInt(parts[5], 10),
          // skip parts[6] == 'typ'
          type: parts[7]
        };

        for (var i = 8; i < parts.length; i += 2) {
          switch (parts[i]) {
            case 'raddr':
              candidate.relatedAddress = parts[i + 1];
              break;
            case 'rport':
              candidate.relatedPort = parseInt(parts[i + 1], 10);
              break;
            case 'tcptype':
              candidate.tcpType = parts[i + 1];
              break;
            default: // Unknown extensions are silently ignored.
              break;
          }
        }
        return candidate;
      };

      // Translates a candidate object into SDP candidate attribute.
      SDPUtils.writeCandidate = function(candidate) {
        var sdp = [];
        sdp.push(candidate.foundation);
        sdp.push(candidate.component);
        sdp.push(candidate.protocol.toUpperCase());
        sdp.push(candidate.priority);
        sdp.push(candidate.ip);
        sdp.push(candidate.port);

        var type = candidate.type;
        sdp.push('typ');
        sdp.push(type);
        if (type !== 'host' && candidate.relatedAddress && candidate.relatedPort) {
          sdp.push('raddr');
          sdp.push(candidate.relatedAddress); // was: relAddr
          sdp.push('rport');
          sdp.push(candidate.relatedPort); // was: relPort
        }
        if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
          sdp.push('tcptype');
          sdp.push(candidate.tcpType);
        }
        return 'candidate:' + sdp.join(' ');
      };

      // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
      // a=rtpmap:111 opus/48000/2
      SDPUtils.parseRtpMap = function(line) {
        var parts = line.substr(9).split(' ');
        var parsed = {
          payloadType: parseInt(parts.shift(), 10) // was: id
        };

        parts = parts[0].split('/');

        parsed.name = parts[0];
        parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
        parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1; // was: channels
        return parsed;
      };

      // Generate an a=rtpmap line from RTCRtpCodecCapability or RTCRtpCodecParameters.
      SDPUtils.writeRtpMap = function(codec) {
        var pt = codec.payloadType;
        if (codec.preferredPayloadType !== undefined) {
          pt = codec.preferredPayloadType;
        }
        return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\r\n';
      };

      // Parses an ftmp line, returns dictionary. Sample input:
      // a=fmtp:96 vbr=on;cng=on
      // Also deals with vbr=on; cng=on
      SDPUtils.parseFmtp = function(line) {
        var parsed = {};
        var kv;
        var parts = line.substr(line.indexOf(' ') + 1).split(';');
        for (var j = 0; j < parts.length; j++) {
          kv = parts[j].trim().split('=');
          parsed[kv[0].trim()] = kv[1];
        }
        return parsed;
      };

      // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
      SDPUtils.writeFtmp = function(codec) {
        var line = '';
        var pt = codec.payloadType;
        if (codec.preferredPayloadType !== undefined) {
          pt = codec.preferredPayloadType;
        }
        if (codec.parameters && codec.parameters.length) {
          var params = [];
          Object.keys(codec.parameters).forEach(function(param) {
            params.push(param + '=' + codec.parameters[param]);
          });
          line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
        }
        return line;
      };

      // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
      // a=rtcp-fb:98 nack rpsi
      SDPUtils.parseRtcpFb = function(line) {
        var parts = line.substr(line.indexOf(' ') + 1).split(' ');
        return {type: parts.shift(), parameter: parts.join(' ')};
      };
      // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
      SDPUtils.writeRtcpFb = function(codec) {
        var lines = '';
        var pt = codec.payloadType;
        if (codec.preferredPayloadType !== undefined) {
          pt = codec.preferredPayloadType;
        }
        if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
          // FIXME: special handling for trr-int?
          codec.rtcpFeedback.forEach(function(fb) {
            lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + ' ' + fb.parameter + '\r\n';
          });
        }
        return lines;
      };

      // Parses an RFC 5576 ssrc media attribute. Sample input:
      // a=ssrc:3735928559 cname:something
      SDPUtils.parseSsrcMedia = function(line) {
        var sp = line.indexOf(' ');
        var parts = {
          ssrc: line.substr(7, sp - 7)
        };
        var colon = line.indexOf(':', sp);
        if (colon > -1) {
          parts.attribute = line.substr(sp + 1, colon - sp - 1);
          parts.value = line.substr(colon + 1);
        } else {
          parts.attribute = line.substr(sp + 1);
        }
        return parts;
      };

      // Extracts DTLS parameters from SDP media section or sessionpart.
      // FIXME: for consistency with other functions this should only
      //   get the fingerprint line as input. See also getIceParameters.
      SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
        var lines = SDPUtils.splitLines(mediaSection);
        lines = lines.concat(SDPUtils.splitLines(sessionpart)); // Search in session part, too.
        var fpLine = lines.filter(function(line) {
          return line.indexOf('a=fingerprint:') === 0;
        })[0].substr(14);
        // Note: a=setup line is ignored since we use the 'auto' role.
        var dtlsParameters = {
          role: 'auto',
          fingerprints: [
            {
              algorithm: fpLine.split(' ')[0],
              value: fpLine.split(' ')[1]
            }
          ]
        };
        return dtlsParameters;
      };

      // Serializes DTLS parameters to SDP.
      SDPUtils.writeDtlsParameters = function(params, setupType) {
        var sdp = 'a=setup:' + setupType + '\r\n';
        params.fingerprints.forEach(function(fp) {
          sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
        });
        return sdp;
      };
      // Parses ICE information from SDP media section or sessionpart.
      // FIXME: for consistency with other functions this should only
      //   get the ice-ufrag and ice-pwd lines as input.
      SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
        var lines = SDPUtils.splitLines(mediaSection);
        lines = lines.concat(SDPUtils.splitLines(sessionpart)); // Search in session part, too.
        var iceParameters = {
          usernameFragment: lines.filter(function(line) {
            return line.indexOf('a=ice-ufrag:') === 0;
          })[0].substr(12),
          password: lines.filter(function(line) {
            return line.indexOf('a=ice-pwd:') === 0;
          })[0].substr(10)
        };
        return iceParameters;
      };

      // Serializes ICE parameters to SDP.
      SDPUtils.writeIceParameters = function(params) {
        return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + 'a=ice-pwd:' + params.password + '\r\n';
      };

      // Parses the SDP media section and returns RTCRtpParameters.
      SDPUtils.parseRtpParameters = function(mediaSection) {
        var description = {
          codecs: [],
          headerExtensions: [],
          fecMechanisms: [],
          rtcp: []
        };
        var lines = SDPUtils.splitLines(mediaSection);
        var mline = lines[0].split(' ');
        for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
          var pt = mline[i];
          var rtpmapline = SDPUtils.matchPrefix(mediaSection, 'a=rtpmap:' + pt + ' ')[0];
          if (rtpmapline) {
            var codec = SDPUtils.parseRtpMap(rtpmapline);
            var fmtps = SDPUtils.matchPrefix(mediaSection, 'a=fmtp:' + pt + ' ');
            // Only the first a=fmtp:<pt> is considered.
            codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
            codec.rtcpFeedback = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:' + pt + ' ').map(SDPUtils.parseRtcpFb);
            description.codecs.push(codec);
          }
        }
        // FIXME: parse headerExtensions, fecMechanisms and rtcp.
        return description;
      };

      // Generates parts of the SDP media section describing the capabilities / parameters.
      SDPUtils.writeRtpDescription = function(kind, caps) {
        var sdp = '';

        // Build the mline.
        sdp += 'm=' + kind + ' ';
        sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
        sdp += ' UDP/TLS/RTP/SAVPF ';
        sdp += caps.codecs.map(function(codec) {
          if (codec.preferredPayloadType !== undefined) {
            return codec.preferredPayloadType;
          }
          return codec.payloadType;
        }).join(' ') + '\r\n';

        sdp += 'c=IN IP4 0.0.0.0\r\n';
        sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';

        // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
        caps.codecs.forEach(function(codec) {
          sdp += SDPUtils.writeRtpMap(codec);
          sdp += SDPUtils.writeFtmp(codec);
          sdp += SDPUtils.writeRtcpFb(codec);
        });
        // FIXME: add headerExtensions, fecMechanismş and rtcp.
        sdp += 'a=rtcp-mux\r\n';
        return sdp;
      };

      SDPUtils.writeSessionBoilerplate = function() {
        // FIXME: sess-id should be an NTP timestamp.
        return 'v=0\r\n' + 'o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\n' + 's=-\r\n' + 't=0 0\r\n';
      };

      SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
        var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);

        // Map ICE parameters (ufrag, pwd) to SDP.
        sdp += SDPUtils.writeIceParameters(transceiver.iceGatherer.getLocalParameters());

        // Map DTLS parameters to SDP.
        sdp += SDPUtils.writeDtlsParameters(transceiver.dtlsTransport.getLocalParameters(), type === 'offer' ? 'actpass' : 'active');

        sdp += 'a=mid:' + transceiver.mid + '\r\n';

        if (transceiver.rtpSender && transceiver.rtpReceiver) {
          sdp += 'a=sendrecv\r\n';
        } else if (transceiver.rtpSender) {
          sdp += 'a=sendonly\r\n';
        } else if (transceiver.rtpReceiver) {
          sdp += 'a=recvonly\r\n';
        } else {
          sdp += 'a=inactive\r\n';
        }

        // FIXME: for RTX there might be multiple SSRCs. Not implemented in Edge yet.
        if (transceiver.rtpSender) {
          var msid = 'msid:' + stream.id + ' ' + transceiver.rtpSender.track.id + '\r\n';
          sdp += 'a=' + msid;
          sdp += 'a=ssrc:' + transceiver.sendSsrc + ' ' + msid;
        }
        // FIXME: this should be written by writeRtpDescription.
        sdp += 'a=ssrc:' + transceiver.sendSsrc + ' cname:' + localCName + '\r\n';
        return sdp;
      };

      // Gets the direction from the mediaSection or the sessionpart.
      SDPUtils.getDirection = function(mediaSection, sessionpart) {
        // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
        var lines = SDPUtils.splitLines(mediaSection);
        for (var i = 0; i < lines.length; i++) {
          switch (lines[i]) {
            case 'a=sendrecv':
            case 'a=sendonly':
            case 'a=recvonly':
            case 'a=inactive':
              return lines[i].substr(2);
          }
        }
        if (sessionpart) {
          return SDPUtils.getDirection(sessionpart);
        }
        return 'sendrecv';
      };

      // ORTC defines an RTCIceCandidate object but no constructor.
      // Not implemented in Edge.
      if (!window.RTCIceCandidate) {
        window.RTCIceCandidate = function(args) {
          return args;
        };
      }
      // ORTC does not have a session description object but
      // other browsers (i.e. Chrome) that will support both PC and ORTC
      // in the future might have this defined already.
      if (!window.RTCSessionDescription) {
        window.RTCSessionDescription = function(args) {
          return args;
        };
      }

      window.RTCPeerConnection = function(config) {
        var self = this;

        this.onicecandidate = null;
        this.onaddstream = null;
        this.onremovestream = null;
        this.onsignalingstatechange = null;
        this.oniceconnectionstatechange = null;
        this.onnegotiationneeded = null;
        this.ondatachannel = null;

        this.localStreams = [];
        this.remoteStreams = [];
        this.getLocalStreams = function() {
          return self.localStreams;
        };
        this.getRemoteStreams = function() {
          return self.remoteStreams;
        };

        this.localDescription = new RTCSessionDescription({type: '', sdp: ''});
        this.remoteDescription = new RTCSessionDescription({type: '', sdp: ''});
        this.signalingState = 'stable';
        this.iceConnectionState = 'new';

        this.iceOptions = {
          gatherPolicy: 'all',
          iceServers: []
        };
        if (config && config.iceTransportPolicy) {
          switch (config.iceTransportPolicy) {
            case 'all':
            case 'relay':
              this.iceOptions.gatherPolicy = config.iceTransportPolicy;
              break;
            case 'none':
              // FIXME: remove once implementation and spec have added this.
              throw new TypeError('iceTransportPolicy "none" not supported');
          }
        }
        if (config && config.iceServers) {
          // Edge does not like
          // 1) stun:
          // 2) turn: that does not have all of turn:host:port?transport=udp
          // 3) an array of urls
          config.iceServers.forEach(function(server) {
            if (server.urls) {
              var url;
              if (typeof(server.urls) === 'string') {
                url = server.urls;
              } else {
                url = server.urls[0];
              }
              if (url.indexOf('transport=udp') !== -1) {
                self.iceServers.push({username: server.username, credential: server.credential, urls: url});
              }
            }
          });
        }

        // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
        // everything that is needed to describe a SDP m-line.
        this.transceivers = [];

        // since the iceGatherer is currently created in createOffer but we
        // must not emit candidates until after setLocalDescription we buffer
        // them in this array.
        this._localIceCandidatesBuffer = [];
      };

      window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {
        var self = this;
        // FIXME: need to apply ice candidates in a way which is async but in-order
        this._localIceCandidatesBuffer.forEach(function(event) {
          if (self.onicecandidate !== null) {
            self.onicecandidate(event);
          }
        });
        this._localIceCandidatesBuffer = [];
      };

      window.RTCPeerConnection.prototype.addStream = function(stream) {
        // Clone is necessary for local demos mostly, attaching directly
        // to two different senders does not work (build 10547).
        this.localStreams.push(stream.clone());
        this._maybeFireNegotiationNeeded();
      };

      window.RTCPeerConnection.prototype.removeStream = function(stream) {
        var idx = this.localStreams.indexOf(stream);
        if (idx > -1) {
          this.localStreams.splice(idx, 1);
          this._maybeFireNegotiationNeeded();
        }
      };

      // Determines the intersection of local and remote capabilities.
      window.RTCPeerConnection.prototype._getCommonCapabilities = function(localCapabilities, remoteCapabilities) {
        var commonCapabilities = {
          codecs: [],
          headerExtensions: [],
          fecMechanisms: []
        };
        localCapabilities.codecs.forEach(function(lCodec) {
          for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
            var rCodec = remoteCapabilities.codecs[i];
            if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() && lCodec.clockRate === rCodec.clockRate && lCodec.numChannels === rCodec.numChannels) {
              // push rCodec so we reply with offerer payload type
              commonCapabilities.codecs.push(rCodec);

              // FIXME: also need to determine intersection between
              // .rtcpFeedback and .parameters
              break;
            }
          }
        });

        localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
          for (var i = 0; i < remoteCapabilities.headerExtensions.length; i++) {
            var rHeaderExtension = remoteCapabilities.headerExtensions[i];
            if (lHeaderExtension.uri === rHeaderExtension.uri) {
              commonCapabilities.headerExtensions.push(rHeaderExtension);
              break;
            }
          }
        });

        // FIXME: fecMechanisms
        return commonCapabilities;
      };

      // Create ICE gatherer, ICE transport and DTLS transport.
      window.RTCPeerConnection.prototype._createIceAndDtlsTransports = function(mid, sdpMLineIndex) {
        var self = this;
        var iceGatherer = new RTCIceGatherer(self.iceOptions);
        var iceTransport = new RTCIceTransport(iceGatherer);
        iceGatherer.onlocalcandidate = function(evt) {
          var event = {};
          event.candidate = {
            sdpMid: mid,
            sdpMLineIndex: sdpMLineIndex
          };

          var cand = evt.candidate;
          // Edge emits an empty object for RTCIceCandidateComplete‥
          if (!cand || Object.keys(cand).length === 0) {
            // polyfill since RTCIceGatherer.state is not implemented in Edge 10547 yet.
            if (iceGatherer.state === undefined) {
              iceGatherer.state = 'completed';
            }

            // Emit a candidate with type endOfCandidates to make the samples work.
            // Edge requires addIceCandidate with this empty candidate to start checking.
            // The real solution is to signal end-of-candidates to the other side when
            // getting the null candidate but some apps (like the samples) don't do that.
            event.candidate.candidate = 'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';
          } else {
            // RTCIceCandidate doesn't have a component, needs to be added
            cand.component = iceTransport.component === 'RTCP' ? 2 : 1;
            event.candidate.candidate = SDPUtils.writeCandidate(cand);
          }

          var complete = self.transceivers.every(function(transceiver) {
            return transceiver.iceGatherer && transceiver.iceGatherer.state === 'completed';
          });
          // FIXME: update .localDescription with candidate and (potentially) end-of-candidates.
          //     To make this harder, the gatherer might emit candidates before localdescription
          //     is set. To make things worse, gather.getLocalCandidates still errors in
          //     Edge 10547 when no candidates have been gathered yet.

          if (self.onicecandidate !== null) {
            // Emit candidate if localDescription is set.
            // Also emits null candidate when all gatherers are complete.
            if (self.localDescription && self.localDescription.type === '') {
              self._localIceCandidatesBuffer.push(event);
              if (complete) {
                self._localIceCandidatesBuffer.push({});
              }
            } else {
              self.onicecandidate(event);
              if (complete) {
                self.onicecandidate({});
              }
            }
          }
        };
        iceTransport.onicestatechange = function() {
          self._updateConnectionState();
        };

        var dtlsTransport = new RTCDtlsTransport(iceTransport);
        dtlsTransport.ondtlsstatechange = function() {
          self._updateConnectionState();
        };
        dtlsTransport.onerror = function() {
          // onerror does not set state to failed by itself.
          dtlsTransport.state = 'failed';
          self._updateConnectionState();
        };

        return {iceGatherer: iceGatherer, iceTransport: iceTransport, dtlsTransport: dtlsTransport};
      };

      // Start the RTP Sender and Receiver for a transceiver.
      window.RTCPeerConnection.prototype._transceive = function(transceiver, send, recv) {
        var params = this._getCommonCapabilities(transceiver.localCapabilities, transceiver.remoteCapabilities);
        if (send && transceiver.rtpSender) {
          params.encodings = [
            {
              ssrc: transceiver.sendSsrc
            }
          ];
          params.rtcp = {
            cname: localCName,
            ssrc: transceiver.recvSsrc
          };
          transceiver.rtpSender.send(params);
        }
        if (recv && transceiver.rtpReceiver) {
          params.encodings = [
            {
              ssrc: transceiver.recvSsrc
            }
          ];
          params.rtcp = {
            cname: transceiver.cname,
            ssrc: transceiver.sendSsrc
          };
          transceiver.rtpReceiver.receive(params);
        }
      };

      window.RTCPeerConnection.prototype.setLocalDescription = function(description) {
        var self = this;
        if (description.type === 'offer') {
          if (!this._pendingOffer) {} else {
            this.transceivers = this._pendingOffer;
            delete this._pendingOffer;
          }
        } else if (description.type === 'answer') {
          var sections = SDPUtils.splitSections(self.remoteDescription.sdp);
          var sessionpart = sections.shift();
          sections.forEach(function(mediaSection, sdpMLineIndex) {
            var transceiver = self.transceivers[sdpMLineIndex];
            var iceGatherer = transceiver.iceGatherer;
            var iceTransport = transceiver.iceTransport;
            var dtlsTransport = transceiver.dtlsTransport;
            var localCapabilities = transceiver.localCapabilities;
            var remoteCapabilities = transceiver.remoteCapabilities;
            var rejected = mediaSection.split('\n', 1)[0].split(' ', 2)[1] === '0';

            if (!rejected) {
              var remoteIceParameters = SDPUtils.getIceParameters(mediaSection, sessionpart);
              iceTransport.start(iceGatherer, remoteIceParameters, 'controlled');

              var remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection, sessionpart);
              dtlsTransport.start(remoteDtlsParameters);

              // Calculate intersection of capabilities.
              var params = self._getCommonCapabilities(localCapabilities, remoteCapabilities);

              // Start the RTCRtpSender. The RTCRtpReceiver for this transceiver
              // has already been started in setRemoteDescription.
              self._transceive(transceiver, params.codecs.length > 0, false);
            }
          });
        }

        this.localDescription = description;
        switch (description.type) {
          case 'offer':
            this._updateSignalingState('have-local-offer');
            break;
          case 'answer':
            this._updateSignalingState('stable');
            break;
          default:
            throw new TypeError('unsupported type "' + description.type + '"');
        }

        // If a success callback was provided, emit ICE candidates after it has been
        // executed. Otherwise, emit callback after the Promise is resolved.
        var hasCallback = arguments.length > 1 && typeof arguments[1] === 'function';
        if (hasCallback) {
          var cb = arguments[1];
          window.setTimeout(function() {
            cb();
            self._emitBufferedCandidates();
          }, 0);
        }
        var p = Promise.resolve();
        p.then(function() {
          if (!hasCallback) {
            window.setTimeout(self._emitBufferedCandidates.bind(self), 0);
          }
        });
        return p;
      };

      window.RTCPeerConnection.prototype.setRemoteDescription = function(description) {
        var self = this;
        var stream = new MediaStream();
        var sections = SDPUtils.splitSections(description.sdp);
        var sessionpart = sections.shift();
        sections.forEach(function(mediaSection, sdpMLineIndex) {
          var lines = SDPUtils.splitLines(mediaSection);
          var mline = lines[0].substr(2).split(' ');
          var kind = mline[0];
          var rejected = mline[1] === '0';
          var direction = SDPUtils.getDirection(mediaSection, sessionpart);

          var transceiver;
          var iceGatherer;
          var iceTransport;
          var dtlsTransport;
          var rtpSender;
          var rtpReceiver;
          var sendSsrc;
          var recvSsrc;
          var localCapabilities;

          // FIXME: ensure the mediaSection has rtcp-mux set.
          var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
          var remoteIceParameters;
          var remoteDtlsParameters;
          if (!rejected) {
            remoteIceParameters = SDPUtils.getIceParameters(mediaSection, sessionpart);
            remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection, sessionpart);
          }
          var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0].substr(6);

          var cname;
          // Gets the first SSRC. Note that with RTX there might be multiple SSRCs.
          var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function(line) {
            return SDPUtils.parseSsrcMedia(line);
          }).filter(function(obj) {
            return obj.attribute === 'cname';
          })[0];
          if (remoteSsrc) {
            recvSsrc = parseInt(remoteSsrc.ssrc, 10);
            cname = remoteSsrc.value;
          }

          if (description.type === 'offer') {
            var transports = self._createIceAndDtlsTransports(mid, sdpMLineIndex);

            localCapabilities = RTCRtpReceiver.getCapabilities(kind);
            sendSsrc = (2 * sdpMLineIndex + 2) * 1001;

            rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);

            // FIXME: not correct when there are multiple streams but that is
            // not currently supported in this shim.
            stream.addTrack(rtpReceiver.track);

            // FIXME: look at direction.
            if (self.localStreams.length > 0 && self.localStreams[0].getTracks().length >= sdpMLineIndex) {
              // FIXME: actually more complicated, needs to match types etc
              var localtrack = self.localStreams[0].getTracks()[sdpMLineIndex];
              rtpSender = new RTCRtpSender(localtrack, transports.dtlsTransport);
            }

            self.transceivers[sdpMLineIndex] = {
              iceGatherer: transports.iceGatherer,
              iceTransport: transports.iceTransport,
              dtlsTransport: transports.dtlsTransport,
              localCapabilities: localCapabilities,
              remoteCapabilities: remoteCapabilities,
              rtpSender: rtpSender,
              rtpReceiver: rtpReceiver,
              kind: kind,
              mid: mid,
              cname: cname,
              sendSsrc: sendSsrc,
              recvSsrc: recvSsrc
            };
            // Start the RTCRtpReceiver now. The RTPSender is started in setLocalDescription.
            self._transceive(self.transceivers[sdpMLineIndex], false, direction === 'sendrecv' || direction === 'sendonly');
          } else if (description.type === 'answer' && !rejected) {
            transceiver = self.transceivers[sdpMLineIndex];
            iceGatherer = transceiver.iceGatherer;
            iceTransport = transceiver.iceTransport;
            dtlsTransport = transceiver.dtlsTransport;
            rtpSender = transceiver.rtpSender;
            rtpReceiver = transceiver.rtpReceiver;
            sendSsrc = transceiver.sendSsrc;
            //recvSsrc = transceiver.recvSsrc;
            localCapabilities = transceiver.localCapabilities;

            self.transceivers[sdpMLineIndex].recvSsrc = recvSsrc;
            self.transceivers[sdpMLineIndex].remoteCapabilities = remoteCapabilities;
            self.transceivers[sdpMLineIndex].cname = cname;

            iceTransport.start(iceGatherer, remoteIceParameters, 'controlling');
            dtlsTransport.start(remoteDtlsParameters);

            self._transceive(transceiver, direction === 'sendrecv' || direction === 'recvonly', direction === 'sendrecv' || direction === 'sendonly');

            if (rtpReceiver && (direction === 'sendrecv' || direction === 'sendonly')) {
              stream.addTrack(rtpReceiver.track);
            } else {
              // FIXME: actually the receiver should be created later.
              delete transceiver.rtpReceiver;
            }
          }
        });

        this.remoteDescription = description;
        switch (description.type) {
          case 'offer':
            this._updateSignalingState('have-remote-offer');
            break;
          case 'answer':
            this._updateSignalingState('stable');
            break;
          default:
            throw new TypeError('unsupported type "' + description.type + '"');
        }
        window.setTimeout(function() {
          if (self.onaddstream !== null && stream.getTracks().length) {
            self.remoteStreams.push(stream);
            window.setTimeout(function() {
              self.onaddstream({stream: stream});
            }, 0);
          }
        }, 0);
        if (arguments.length > 1 && typeof arguments[1] === 'function') {
          window.setTimeout(arguments[1], 0);
        }
        return Promise.resolve();
      };

      window.RTCPeerConnection.prototype.close = function() {
        this.transceivers.forEach(function(transceiver) {
          /* not yet
          if (transceiver.iceGatherer) {
            transceiver.iceGatherer.close();
          }
          */
          if (transceiver.iceTransport) {
            transceiver.iceTransport.stop();
          }
          if (transceiver.dtlsTransport) {
            transceiver.dtlsTransport.stop();
          }
          if (transceiver.rtpSender) {
            transceiver.rtpSender.stop();
          }
          if (transceiver.rtpReceiver) {
            transceiver.rtpReceiver.stop();
          }
        });
        // FIXME: clean up tracks, local streams, remote streams, etc
        this._updateSignalingState('closed');
      };

      // Update the signaling state.
      window.RTCPeerConnection.prototype._updateSignalingState = function(newState) {
        this.signalingState = newState;
        if (this.onsignalingstatechange !== null) {
          this.onsignalingstatechange();
        }
      };

      // Determine whether to fire the negotiationneeded event.
      window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {
        // Fire away (for now).
        if (this.onnegotiationneeded !== null) {
          this.onnegotiationneeded();
        }
      };

      // Update the connection state.
      window.RTCPeerConnection.prototype._updateConnectionState = function() {
        var self = this;
        var newState;
        var states = {
          'new': 0,
          closed: 0,
          connecting: 0,
          checking: 0,
          connected: 0,
          completed: 0,
          failed: 0
        };
        this.transceivers.forEach(function(transceiver) {
          states[transceiver.iceTransport.state]++;
          states[transceiver.dtlsTransport.state]++;
        });
        // ICETransport.completed and connected are the same for this purpose.
        states.connected += states.completed;

        newState = 'new';
        if (states.failed > 0) {
          newState = 'failed';
        } else if (states.connecting > 0 || states.checking > 0) {
          newState = 'connecting';
        } else if (states.disconnected > 0) {
          newState = 'disconnected';
        } else if (states.new > 0) {
          newState = 'new';
        } else if (states.connecting > 0 || states.completed > 0) {
          newState = 'connected';
        }

        if (newState !== self.iceConnectionState) {
          self.iceConnectionState = newState;
          if (this.oniceconnectionstatechange !== null) {
            this.oniceconnectionstatechange();
          }
        }
      };

      window.RTCPeerConnection.prototype.createOffer = function() {
        var self = this;
        if (this._pendingOffer) {
          throw new Error('createOffer called while there is a pending offer.');
        }
        var offerOptions;
        if (arguments.length === 1 && typeof arguments[0] !== 'function') {
          offerOptions = arguments[0];
        } else if (arguments.length === 3) {
          offerOptions = arguments[2];
        }

        var tracks = [];
        var numAudioTracks = 0;
        var numVideoTracks = 0;
        // Default to sendrecv.
        if (this.localStreams.length) {
          numAudioTracks = this.localStreams[0].getAudioTracks().length;
          numVideoTracks = this.localStreams[0].getVideoTracks().length;
        }
        // Determine number of audio and video tracks we need to send/recv.
        if (offerOptions) {
          // Reject Chrome legacy constraints.
          if (offerOptions.mandatory || offerOptions.optional) {
            throw new TypeError('Legacy mandatory/optional constraints not supported.');
          }
          if (offerOptions.offerToReceiveAudio !== undefined) {
            numAudioTracks = offerOptions.offerToReceiveAudio;
          }
          if (offerOptions.offerToReceiveVideo !== undefined) {
            numVideoTracks = offerOptions.offerToReceiveVideo;
          }
        }
        if (this.localStreams.length) {
          // Push local streams.
          this.localStreams[0].getTracks().forEach(function(track) {
            tracks.push({
              kind: track.kind,
              track: track,
              wantReceive: track.kind === 'audio' ? numAudioTracks > 0 : numVideoTracks > 0
            });
            if (track.kind === 'audio') {
              numAudioTracks--;
            } else if (track.kind === 'video') {
              numVideoTracks--;
            }
          });
        }
        // Create M-lines for recvonly streams.
        while (numAudioTracks > 0 || numVideoTracks > 0) {
          if (numAudioTracks > 0) {
            tracks.push({kind: 'audio', wantReceive: true});
            numAudioTracks--;
          }
          if (numVideoTracks > 0) {
            tracks.push({kind: 'video', wantReceive: true});
            numVideoTracks--;
          }
        }

        var sdp = SDPUtils.writeSessionBoilerplate();
        var transceivers = [];
        tracks.forEach(function(mline, sdpMLineIndex) {
          // For each track, create an ice gatherer, ice transport, dtls transport,
          // potentially rtpsender and rtpreceiver.
          var track = mline.track;
          var kind = mline.kind;
          var mid = generateIdentifier();

          var transports = self._createIceAndDtlsTransports(mid, sdpMLineIndex);

          var localCapabilities = RTCRtpSender.getCapabilities(kind);
          var rtpSender;
          var rtpReceiver;

          // generate an ssrc now, to be used later in rtpSender.send
          var sendSsrc = (2 * sdpMLineIndex + 1) * 1001;
          if (track) {
            rtpSender = new RTCRtpSender(track, transports.dtlsTransport);
          }

          if (mline.wantReceive) {
            rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
          }

          transceivers[sdpMLineIndex] = {
            iceGatherer: transports.iceGatherer,
            iceTransport: transports.iceTransport,
            dtlsTransport: transports.dtlsTransport,
            localCapabilities: localCapabilities,
            remoteCapabilities: null,
            rtpSender: rtpSender,
            rtpReceiver: rtpReceiver,
            kind: kind,
            mid: mid,
            sendSsrc: sendSsrc,
            recvSsrc: null
          };
          var transceiver = transceivers[sdpMLineIndex];
          sdp += SDPUtils.writeMediaSection(transceiver, transceiver.localCapabilities, 'offer', self.localStreams[0]);
        });

        this._pendingOffer = transceivers;
        var desc = new RTCSessionDescription({type: 'offer', sdp: sdp});
        if (arguments.length && typeof arguments[0] === 'function') {
          window.setTimeout(arguments[0], 0, desc);
        }
        return Promise.resolve(desc);
      };

      window.RTCPeerConnection.prototype.createAnswer = function() {
        var self = this;
        var answerOptions;
        if (arguments.length === 1 && typeof arguments[0] !== 'function') {
          answerOptions = arguments[0];
        } else if (arguments.length === 3) {
          answerOptions = arguments[2];
        }

        var sdp = SDPUtils.writeSessionBoilerplate();
        this.transceivers.forEach(function(transceiver) {
          // Calculate intersection of capabilities.
          var commonCapabilities = self._getCommonCapabilities(transceiver.localCapabilities, transceiver.remoteCapabilities);

          sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities, 'answer', self.localStreams[0]);
        });

        var desc = new RTCSessionDescription({type: 'answer', sdp: sdp});
        if (arguments.length && typeof arguments[0] === 'function') {
          window.setTimeout(arguments[0], 0, desc);
        }
        return Promise.resolve(desc);
      };

      window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
        var mLineIndex = candidate.sdpMLineIndex;
        if (candidate.sdpMid) {
          for (var i = 0; i < this.transceivers.length; i++) {
            if (this.transceivers[i].mid === candidate.sdpMid) {
              mLineIndex = i;
              break;
            }
          }
        }
        var transceiver = this.transceivers[mLineIndex];
        if (transceiver) {
          var cand = Object.keys(candidate.candidate).length > 0 ? SDPUtils.parseCandidate(candidate.candidate) : {};
          // Ignore Chrome's invalid candidates since Edge does not like them.
          if (cand.protocol === 'tcp' && cand.port === 0) {
            return;
          }
          // Ignore RTCP candidates, we assume RTCP-MUX.
          if (cand.component !== '1') {
            return;
          }
          // A dirty hack to make samples work.
          if (cand.type === 'endOfCandidates') {
            cand = {};
          }
          transceiver.iceTransport.addRemoteCandidate(cand);
        }
        if (arguments.length > 1 && typeof arguments[1] === 'function') {
          window.setTimeout(arguments[1], 0);
        }
        return Promise.resolve();
      };

      window.RTCPeerConnection.prototype.getStats = function() {
        var promises = [];
        this.transceivers.forEach(function(transceiver) {
          ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport', 'dtlsTransport'].forEach(function(method) {
            if (transceiver[method]) {
              promises.push(transceiver[method].getStats());
            }
          });
        });
        var cb = arguments.length > 1 && typeof arguments[1] === 'function' && arguments[1];
        return new Promise(function(resolve) {
          var results = {};
          Promise.all(promises).then(function(res) {
            res.forEach(function(result) {
              Object.keys(result).forEach(function(id) {
                results[id] = result[id];
              });
            });
            if (cb) {
              window.setTimeout(cb, 0, results);
            }
            resolve(results);
          });
        });
      };
    }
  } else {
    webrtcUtils.log('Browser does not appear to be WebRTC-capable');
  }

  // Polyfill ontrack on browsers that don't yet have it
  if (typeof window === 'object' && !('ontrack' in window.RTCPeerConnection)) {
    Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
      get: function() {
        return this._ontrack;
      },
      set: function(f) {
        if (this._ontrack) {
          this.removeEventListener('track', this._ontrack);
          this.removeEventListener('addstream', this._ontrackpoly);
        }
        this.addEventListener('track', this._ontrack = f);
        this.addEventListener('addstream', this._ontrackpoly = function(e) {
          e.stream.getTracks().forEach(function(track) {
            var event = new Event('track');
            event.track = track;
            event.receiver = {
              track: track
            };
            event.streams = [e.stream];
            this.dispatchEvent(event);
          }.bind(this));
        }.bind(this));
      }
    });
  }

  // Returns the result of getUserMedia as a Promise.
  function requestUserMedia(constraints) {
    return new Promise(function(resolve, reject) {
      getUserMedia(constraints, resolve, reject);
    });
  }

  var webrtcTesting = {};
  try {
    Object.defineProperty(webrtcTesting, 'version', {
      set: function(version) {
        webrtcDetectedVersion = version;
      }
    });
  } catch (e) {}

  if (typeof module !== 'undefined') {
    var RTCPeerConnection;
    var RTCIceCandidate;
    var RTCSessionDescription;
    if (typeof window !== 'undefined') {
      RTCPeerConnection = window.RTCPeerConnection;
      RTCIceCandidate = window.RTCIceCandidate;
      RTCSessionDescription = window.RTCSessionDescription;
    }
    module.exports = {
      RTCPeerConnection: RTCPeerConnection,
      RTCIceCandidate: RTCIceCandidate,
      RTCSessionDescription: RTCSessionDescription,
      getUserMedia: getUserMedia,
      attachMediaStream: attachMediaStream,
      reattachMediaStream: reattachMediaStream,
      webrtcDetectedBrowser: webrtcDetectedBrowser,
      webrtcDetectedVersion: webrtcDetectedVersion,
      webrtcMinimumVersion: webrtcMinimumVersion,
      webrtcTesting: webrtcTesting,
      webrtcUtils: webrtcUtils
      //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,
        RTCIceCandidate: window.RTCIceCandidate,
        RTCSessionDescription: window.RTCSessionDescription,
        getUserMedia: getUserMedia,
        attachMediaStream: attachMediaStream,
        reattachMediaStream: reattachMediaStream,
        webrtcDetectedBrowser: webrtcDetectedBrowser,
        webrtcDetectedVersion: webrtcDetectedVersion,
        webrtcMinimumVersion: webrtcMinimumVersion,
        webrtcTesting: webrtcTesting,
        webrtcUtils: webrtcUtils
        //requestUserMedia: not exposed on purpose.
        //trace: not exposed on purpose.
      };
    });
  }

}, this);

Original vs. Beautified Diff:

Index: /Users/waldgeist/Documents/Development/htdocs/aiddogs/webapp/packages/guzz-live-session-standard/vendor/adapter.js
===================================================================
--- /Users/waldgeist/Documents/Development/htdocs/aiddogs/webapp/packages/guzz-live-session-standard/vendor/adapter.js	original
+++ /Users/waldgeist/Documents/Development/htdocs/aiddogs/webapp/packages/guzz-live-session-standard/vendor/adapter.js	beautified
@@ -30,10 +30,9 @@
   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') {
+      if (typeof module !== 'undefined' || typeof require === 'function' && typeof define === 'function') {
         return;
       }
       console.log.apply(console, arguments);
     },
@@ -56,10 +55,9 @@
     }
   }
 
   if (typeof window === 'object') {
-    if (window.HTMLMediaElement &&
-      !('srcObject' in window.HTMLMediaElement.prototype)) {
+    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.
@@ -99,10 +97,9 @@
 
     webrtcDetectedBrowser = 'firefox';
 
     // the detected firefox version.
-    webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent,
-      /Firefox\/([0-9]+)\./, 1);
+    webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, /Firefox\/([0-9]+)\./, 1);
 
     // the minimum firefox version still supported by adapter.
     webrtcMinimumVersion = 31;
 
@@ -141,10 +138,9 @@
       if (mozRTCPeerConnection.generateCertificate) {
         Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
           get: function() {
             if (arguments.length) {
-              return mozRTCPeerConnection.generateCertificate.apply(null,
-                arguments);
+              return mozRTCPeerConnection.generateCertificate.apply(null, arguments);
             } else {
               return mozRTCPeerConnection.generateCertificate;
             }
           }
@@ -165,14 +161,12 @@
         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) {
+          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') {
@@ -227,12 +221,12 @@
         addEventListener: function() {},
         removeEventListener: function() {}
       };
     }
-    navigator.mediaDevices.enumerateDevices =
-      navigator.mediaDevices.enumerateDevices || function() {
-        return new Promise(