Skip to content

Instantly share code, notes, and snippets.

@derwaldgeist
Created August 1, 2017 12:53
Show Gist options
  • Save derwaldgeist/89c0a39b9368061265fd5d15381eaa05 to your computer and use it in GitHub Desktop.
Save derwaldgeist/89c0a39b9368061265fd5d15381eaa05 to your computer and use it in GitHub Desktop.

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(function(resolve) {
-          var infos = [{
+    navigator.mediaDevices.enumerateDevices = navigator.mediaDevices.enumerateDevices || function() {
+      return new Promise(function(resolve) {
+        var infos = [
+          {
             kind: 'audioinput',
             deviceId: 'default',
             label: '',
             groupId: ''
@@ -240,17 +234,17 @@
             kind: 'videoinput',
             deviceId: 'default',
             label: '',
             groupId: ''
-          }];
-          resolve(infos);
-        });
-      };
+          }
+        ];
+        resolve(infos);
+      });
+    };
 
     if (webrtcDetectedVersion < 41) {
       // Work around http://bugzil.la/1169665
-      var orgEnumerateDevices =
-        navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
+      var orgEnumerateDevices = navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
       navigator.mediaDevices.enumerateDevices = function() {
         return orgEnumerateDevices().then(undefined, function(e) {
           if (e.name === 'NotFoundError') {
             return [];
@@ -264,10 +258,9 @@
 
     webrtcDetectedBrowser = 'chrome';
 
     // the detected chrome version.
-    webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent,
-      /Chrom(e|ium)\/([0-9]+)\./, 2);
+    webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, /Chrom(e|ium)\/([0-9]+)\./, 2);
 
     // the minimum chrome version still supported by adapter.
     webrtcMinimumVersion = 38;
 
@@ -339,24 +332,21 @@
     if (webkitRTCPeerConnection.generateCertificate) {
       Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
         get: function() {
           if (arguments.length) {
-            return webkitRTCPeerConnection.generateCertificate.apply(null,
-              arguments);
+            return webkitRTCPeerConnection.generateCertificate.apply(null, arguments);
           } else {
             return webkitRTCPeerConnection.generateCertificate;
           }
         }
       });
     }
 
-    // add promise support
-    ['createOffer', 'createAnswer'].forEach(function(method) {
+    // 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')) {
+        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]);
           });
@@ -365,17 +355,16 @@
         }
       };
     });
 
-    ['setLocalDescription', 'setRemoteDescription',
-      'addIceCandidate'
-    ].forEach(function(method) {
+    ['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],
+          nativeMethod.apply(self, [
+            args[0],
             function() {
               resolve();
               if (args.length >= 2) {
                 args[1].apply(null, []);
@@ -491,10 +480,9 @@
     } 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);
+      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);
@@ -533,15 +521,13 @@
         to.src = from.src;
       }
     };
 
-  } else if (navigator.mediaDevices && navigator.userAgent.match(
-      /Edge\/(\d+).(\d+)$/)) {
+  } 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);
+    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;
@@ -633,10 +619,9 @@
 
         var type = candidate.type;
         sdp.push('typ');
         sdp.push(type);
-        if (type !== 'host' && candidate.relatedAddress &&
-          candidate.relatedPort) {
+        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
@@ -669,10 +654,9 @@
         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';
+        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
@@ -708,12 +692,9 @@
       // 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(' ')
-        };
+        return {type: parts.shift(), parameter: parts.join(' ')};
       };
       // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
       SDPUtils.writeRtcpFb = function(codec) {
         var lines = '';
@@ -723,10 +704,9 @@
         }
         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';
+            lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + ' ' + fb.parameter + '\r\n';
           });
         }
         return lines;
       };
@@ -735,9 +715,9 @@
       // a=ssrc:3735928559 cname:something
       SDPUtils.parseSsrcMedia = function(line) {
         var sp = line.indexOf(' ');
         var parts = {
-          ssrc: line.substr(7, sp - 7),
+          ssrc: line.substr(7, sp - 7)
         };
         var colon = line.indexOf(':', sp);
         if (colon > -1) {
           parts.attribute = line.substr(sp + 1, colon - sp - 1);
@@ -759,12 +739,14 @@
         })[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]
-          }]
+          fingerprints: [
+            {
+              algorithm: fpLine.split(' ')[0],
+              value: fpLine.split(' ')[1]
+            }
+          ]
         };
         return dtlsParameters;
       };
 
@@ -794,10 +776,9 @@
       };
 
       // Serializes ICE parameters to SDP.
       SDPUtils.writeIceParameters = function(params) {
-        return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
-          'a=ice-pwd:' + params.password + '\r\n';
+        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) {
@@ -810,19 +791,15 @@
         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];
+          var rtpmapline = SDPUtils.matchPrefix(mediaSection, 'a=rtpmap:' + pt + ' ')[0];
           if (rtpmapline) {
             var codec = SDPUtils.parseRtpMap(rtpmapline);
-            var fmtps = SDPUtils.matchPrefix(
-              mediaSection, 'a=fmtp:' + pt + ' ');
+            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);
+            codec.rtcpFeedback = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:' + pt + ' ').map(SDPUtils.parseRtcpFb);
             description.codecs.push(codec);
           }
         }
         // FIXME: parse headerExtensions, fecMechanisms and rtcp.
@@ -859,25 +836,19 @@
       };
 
       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';
+        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());
+        sdp += SDPUtils.writeIceParameters(transceiver.iceGatherer.getLocalParameters());
 
         // Map DTLS parameters to SDP.
-        sdp += SDPUtils.writeDtlsParameters(
-          transceiver.dtlsTransport.getLocalParameters(),
-          type === 'offer' ? 'actpass' : 'active');
+        sdp += SDPUtils.writeDtlsParameters(transceiver.dtlsTransport.getLocalParameters(), type === 'offer' ? 'actpass' : 'active');
 
         sdp += 'a=mid:' + transceiver.mid + '\r\n';
 
         if (transceiver.rtpSender && transceiver.rtpReceiver) {
@@ -891,16 +862,14 @@
         }
 
         // 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';
+          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';
+        sdp += 'a=ssrc:' + transceiver.sendSsrc + ' cname:' + localCName + '\r\n';
         return sdp;
       };
 
       // Gets the direction from the mediaSection or the sessionpart.
@@ -957,16 +926,10 @@
         this.getRemoteStreams = function() {
           return self.remoteStreams;
         };
 
-        this.localDescription = new RTCSessionDescription({
-          type: '',
-          sdp: ''
-        });
-        this.remoteDescription = new RTCSessionDescription({
-          type: '',
-          sdp: ''
-        });
+        this.localDescription = new RTCSessionDescription({type: '', sdp: ''});
+        this.remoteDescription = new RTCSessionDescription({type: '', sdp: ''});
         this.signalingState = 'stable';
         this.iceConnectionState = 'new';
 
         this.iceOptions = {
@@ -997,13 +960,9 @@
               } else {
                 url = server.urls[0];
               }
               if (url.indexOf('transport=udp') !== -1) {
-                self.iceServers.push({
-                  username: server.username,
-                  credential: server.credential,
-                  urls: url
-                });
+                self.iceServers.push({username: server.username, credential: server.credential, urls: url});
               }
             }
           });
         }
@@ -1044,372 +1003,342 @@
         }
       };
 
       // 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);
+      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;
-              }
+              // 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;
-              }
+        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;
-        };
+        // 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
-            };
+      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 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';
             }
 
-            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.
+            // 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);
+          }
 
-            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({});
-                }
+          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();
-          };
+          }
+        };
+        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
-          };
+        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);
+      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.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.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';
+      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');
+            if (!rejected) {
+              var remoteIceParameters = SDPUtils.getIceParameters(mediaSection, sessionpart);
+              iceTransport.start(iceGatherer, remoteIceParameters, 'controlled');
 
-                var remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
-                  sessionpart);
-                dtlsTransport.start(remoteDtlsParameters);
+              var remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection, sessionpart);
+              dtlsTransport.start(remoteDtlsParameters);
 
-                // Calculate intersection of capabilities.
-                var params = self._getCommonCapabilities(localCapabilities,
-                  remoteCapabilities);
+              // 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);
-              }
-            });
-          }
+              // 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 + '"');
-          }
+        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);
+        // 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);
           }
-          var p = Promise.resolve();
-          p.then(function() {
-            if (!hasCallback) {
-              window.setTimeout(self._emitBufferedCandidates.bind(self), 0);
-            }
-          });
-          return p;
-        };
+        });
+        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);
+      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;
+          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);
+          // 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;
-            }
+          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);
+          if (description.type === 'offer') {
+            var transports = self._createIceAndDtlsTransports(mid, sdpMLineIndex);
 
-              localCapabilities = RTCRtpReceiver.getCapabilities(kind);
-              sendSsrc = (2 * sdpMLineIndex + 2) * 1001;
+            localCapabilities = RTCRtpReceiver.getCapabilities(kind);
+            sendSsrc = (2 * sdpMLineIndex + 2) * 1001;
 
-              rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
+            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: 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);
-              }
+            // 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] = {
+              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;
+            self.transceivers[sdpMLineIndex].recvSsrc = recvSsrc;
+            self.transceivers[sdpMLineIndex].remoteCapabilities = remoteCapabilities;
+            self.transceivers[sdpMLineIndex].cname = cname;
 
-              iceTransport.start(iceGatherer, remoteIceParameters, 'controlling');
-              dtlsTransport.start(remoteDtlsParameters);
+            iceTransport.start(iceGatherer, remoteIceParameters, 'controlling');
+            dtlsTransport.start(remoteDtlsParameters);
 
-              self._transceive(transceiver,
-                direction === 'sendrecv' || direction === 'recvonly',
-                direction === 'sendrecv' || direction === 'sendonly');
+            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;
-              }
+            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 + '"');
+        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);
           }
-          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();
-        };
+        }, 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
@@ -1434,66 +1363,63 @@
         this._updateSignalingState('closed');
       };
 
       // Update the signaling state.
-      window.RTCPeerConnection.prototype._updateSignalingState =
-        function(newState) {
-          this.signalingState = newState;
-          if (this.onsignalingstatechange !== null) {
-            this.onsignalingstatechange();
-          }
-        };
+      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();
-          }
-        };
+      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;
+      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';
-          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';
-          }
+        } else if (states.connecting > 0 || states.completed > 0) {
+          newState = 'connected';
+        }
 
-          if (newState !== self.iceConnectionState) {
-            self.iceConnectionState = newState;
-            if (this.oniceconnectionstatechange !== null) {
-              this.oniceconnectionstatechange();
-            }
+        if (newState !== self.iceConnectionState) {
+          self.iceConnectionState = newState;
+          if (this.oniceconnectionstatechange !== null) {
+            this.oniceconnectionstatechange();
           }
-        };
+        }
+      };
 
       window.RTCPeerConnection.prototype.createOffer = function() {
         var self = this;
         if (this._pendingOffer) {
@@ -1517,10 +1443,9 @@
         // 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.');
+            throw new TypeError('Legacy mandatory/optional constraints not supported.');
           }
           if (offerOptions.offerToReceiveAudio !== undefined) {
             numAudioTracks = offerOptions.offerToReceiveAudio;
           }
@@ -1533,10 +1458,9 @@
           this.localStreams[0].getTracks().forEach(function(track) {
             tracks.push({
               kind: track.kind,
               track: track,
-              wantReceive: track.kind === 'audio' ?
-                numAudioTracks > 0 : numVideoTracks > 0
+              wantReceive: track.kind === 'audio' ? numAudioTracks > 0 : numVideoTracks > 0
             });
             if (track.kind === 'audio') {
               numAudioTracks--;
             } else if (track.kind === 'video') {
@@ -1546,19 +1470,13 @@
         }
         // Create M-lines for recvonly streams.
         while (numAudioTracks > 0 || numVideoTracks > 0) {
           if (numAudioTracks > 0) {
-            tracks.push({
-              kind: 'audio',
-              wantReceive: true
-            });
+            tracks.push({kind: 'audio', wantReceive: true});
             numAudioTracks--;
           }
           if (numVideoTracks > 0) {
-            tracks.push({
-              kind: 'video',
-              wantReceive: true
-            });
+            tracks.push({kind: 'video', wantReceive: true});
             numVideoTracks--;
           }
         }
 
@@ -1600,17 +1518,13 @@
             sendSsrc: sendSsrc,
             recvSsrc: null
           };
           var transceiver = transceivers[sdpMLineIndex];
-          sdp += SDPUtils.writeMediaSection(transceiver,
-            transceiver.localCapabilities, 'offer', self.localStreams[0]);
+          sdp += SDPUtils.writeMediaSection(transceiver, transceiver.localCapabilities, 'offer', self.localStreams[0]);
         });
 
         this._pendingOffer = transceivers;
-        var desc = new RTCSessionDescription({
-          type: 'offer',
-          sdp: sdp
-        });
+        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);
@@ -1627,20 +1541,14 @@
 
         var sdp = SDPUtils.writeSessionBoilerplate();
         this.transceivers.forEach(function(transceiver) {
           // Calculate intersection of capabilities.
-          var commonCapabilities = self._getCommonCapabilities(
-            transceiver.localCapabilities,
-            transceiver.remoteCapabilities);
+          var commonCapabilities = self._getCommonCapabilities(transceiver.localCapabilities, transceiver.remoteCapabilities);
 
-          sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,
-            'answer', self.localStreams[0]);
+          sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities, 'answer', self.localStreams[0]);
         });
 
-        var desc = new RTCSessionDescription({
-          type: 'answer',
-          sdp: sdp
-        });
+        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);
@@ -1657,10 +1565,9 @@
           }
         }
         var transceiver = this.transceivers[mLineIndex];
         if (transceiver) {
-          var cand = Object.keys(candidate.candidate).length > 0 ?
-            SDPUtils.parseCandidate(candidate.candidate) : {};
+          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;
           }
@@ -1682,18 +1589,15 @@
 
       window.RTCPeerConnection.prototype.getStats = function() {
         var promises = [];
         this.transceivers.forEach(function(transceiver) {
-          ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
-            'dtlsTransport'
-          ].forEach(function(method) {
+          ['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];
+        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) {
@@ -1776,10 +1680,10 @@
       webrtcDetectedVersion: webrtcDetectedVersion,
       webrtcMinimumVersion: webrtcMinimumVersion,
       webrtcTesting: webrtcTesting,
       webrtcUtils: webrtcUtils
-        //requestUserMedia: not exposed on purpose.
-        //trace: not exposed on purpose.
+      //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() {
@@ -1794,10 +1698,10 @@
         webrtcDetectedVersion: webrtcDetectedVersion,
         webrtcMinimumVersion: webrtcMinimumVersion,
         webrtcTesting: webrtcTesting,
         webrtcUtils: webrtcUtils
-          //requestUserMedia: not exposed on purpose.
-          //trace: not exposed on purpose.
+        //requestUserMedia: not exposed on purpose.
+        //trace: not exposed on purpose.
       };
     });
   }
 

Logs

2017-08-01T12:52:28.702Z - debug: [beautifiers/index.coffee] beautify /*
 *  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);
 [ { _default: { indent_size: 2, indent_char: ' ', indent_with_tabs: false } },
  { 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: [Object],
       extra_liners: [Object],
       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: [Object],
       end_with_newline: false,
       extra_liners: [Object],
       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: [Object],
       extra_liners: [Object],
       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: [Object],
       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: [Object],
       end_with_newline: false,
       extra_liners: [Object],
       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: [Object],
       end_with_newline: false,
       extra_liners: [Object],
       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: [Object],
       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: [Object],
       extra_liners: [Object],
       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: [Object],
       end_with_newline: false,
       extra_liners: [Object],
       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 } },
  { _default: {} },
  { _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: ' ' } },
  { _default: {} },
  { _default: {} },
  { _default: {} },
  { _default: {} },
  { _default: {} },
  { _default: {} },
  { js: { preserve_ternary_lines: true, formatObject: true },
    jsx: { preserve_ternary_lines: true } },
  { _default: {} },
  { _default: {} },
  { _default: {} } ] Babel ES6 JavaScript /Users/waldgeist/Documents/Development/htdocs/aiddogs/webapp/packages/guzz-live-session-standard/vendor/adapter.js undefined
2017-08-01T12:52:28.702Z - verbose: [beautifiers/index.coffee]  indent_size=2, indent_char= , indent_with_tabs=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, indent_size=2, disabled=false, default_beautifier=beautysh, beautify_on_save=false, beautify_on_save=true, configPath=/Users/waldgeist/Documents/Development/htdocs/marble/unity/.uncrustify.cfg, disabled=false, default_beautifier=Uncrustify, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=cljfmt, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=Crystal, beautify_on_save=false, 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, beautify_on_save=true, disabled=false, default_beautifier=Pretty Diff, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, disabled=false, default_beautifier=elm-format, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, disabled=false, default_beautifier=erl_tidy, beautify_on_save=false, indent_size=2, indent_char= , disabled=false, default_beautifier=Gherkin formatter, beautify_on_save=false, configPath=, disabled=false, default_beautifier=clang-format, beautify_on_save=false, disabled=false, default_beautifier=gofmt, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, emacs_path=, emacs_script_path=, disabled=false, default_beautifier=Fortran Beautifier, beautify_on_save=false, 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, disabled=false, default_beautifier=stylish-haskell, beautify_on_save=false, 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, indent_size=2, indent_char= , disabled=false, default_beautifier=Pug Beautify, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, 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, 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, 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, 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, end_of_line=System Default, disabled=false, default_beautifier=Lua beautifier, beautify_on_save=false, gfm=true, yaml=true, commonmark=false, disabled=false, default_beautifier=Tidy Markdown, beautify_on_save=false, 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, 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, indent_size=2, indent_char= , indent_with_tabs=false, dontJoinCurlyBracet=true, disabled=false, default_beautifier=Nginx Beautify, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=ocp-indent, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, perltidy_profile=, disabled=false, default_beautifier=Perltidy, beautify_on_save=false, 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, disabled=false, default_beautifier=puppet-lint, beautify_on_save=false, 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, indent_size=2, disabled=false, default_beautifier=formatR, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, indent_size=2, indent_char= , rubocop_path=, disabled=false, default_beautifier=Rubocop, beautify_on_save=false, rustfmt_path=, disabled=false, default_beautifier=rustfmt, beautify_on_save=false, disabled=false, default_beautifier=SassConvert, beautify_on_save=false, 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, beautify_on_save=true, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, indent_size=2, keywords=upper, identifiers=unchanged, disabled=false, default_beautifier=sqlformat, beautify_on_save=false, beautify_on_save=true, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, 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, 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, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, padding=0, disabled=false, default_beautifier=align-yaml, beautify_on_save=false, , 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= , , , , , , , preserve_ternary_lines=true, formatObject=true, preserve_ternary_lines=true, , , 
2017-08-01T12:52:28.705Z - verbose: [beautifiers/index.coffee] [ { name: 'JSX',
    namespace: 'jsx',
    fallback: [ 'js' ],
    grammars: 
     [ 'JSX',
       'JavaScript (JSX)',
       'Babel ES6 JavaScript',
       'JavaScript with JSX' ],
    extensions: [ 'jsx', 'js' ],
    defaultBeautifier: 'Pretty Diff',
    options: { e4x: [Object] } },
  { name: 'JavaScript',
    namespace: 'js',
    scope: [ 'source.js' ],
    grammars: [ 'JavaScript' ],
    extensions: [ 'js' ],
    defaultBeautifier: 'JS Beautify',
    options: 
     { indent_size: [Object],
       indent_char: [Object],
       indent_level: [Object],
       indent_with_tabs: [Object],
       preserve_newlines: [Object],
       max_preserve_newlines: [Object],
       space_in_paren: [Object],
       jslint_happy: [Object],
       space_after_anon_function: [Object],
       brace_style: [Object],
       break_chained_methods: [Object],
       keep_array_indentation: [Object],
       keep_function_indentation: [Object],
       space_before_conditional: [Object],
       eval_code: [Object],
       unescape_strings: [Object],
       wrap_line_length: [Object],
       end_with_newline: [Object],
       end_with_comma: [Object],
       end_of_line: [Object] } } ] 'Babel ES6 JavaScript' 'js'
2017-08-01T12:52:28.705Z - verbose: [beautifiers/index.coffee] Language JSX supported
2017-08-01T12:52:28.705Z - verbose: [beautifiers/index.coffee] getOptions selections [ 'js', 'jsx' ] indent_size=2, indent_char= , indent_with_tabs=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, indent_size=2, disabled=false, default_beautifier=beautysh, beautify_on_save=false, beautify_on_save=true, configPath=/Users/waldgeist/Documents/Development/htdocs/marble/unity/.uncrustify.cfg, disabled=false, default_beautifier=Uncrustify, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=cljfmt, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=Crystal, beautify_on_save=false, 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, beautify_on_save=true, disabled=false, default_beautifier=Pretty Diff, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, disabled=false, default_beautifier=elm-format, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, disabled=false, default_beautifier=erl_tidy, beautify_on_save=false, indent_size=2, indent_char= , disabled=false, default_beautifier=Gherkin formatter, beautify_on_save=false, configPath=, disabled=false, default_beautifier=clang-format, beautify_on_save=false, disabled=false, default_beautifier=gofmt, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, emacs_path=, emacs_script_path=, disabled=false, default_beautifier=Fortran Beautifier, beautify_on_save=false, 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, disabled=false, default_beautifier=stylish-haskell, beautify_on_save=false, 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, indent_size=2, indent_char= , disabled=false, default_beautifier=Pug Beautify, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, 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, 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, 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, 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, end_of_line=System Default, disabled=false, default_beautifier=Lua beautifier, beautify_on_save=false, gfm=true, yaml=true, commonmark=false, disabled=false, default_beautifier=Tidy Markdown, beautify_on_save=false, 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, 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, indent_size=2, indent_char= , indent_with_tabs=false, dontJoinCurlyBracet=true, disabled=false, default_beautifier=Nginx Beautify, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=ocp-indent, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, perltidy_profile=, disabled=false, default_beautifier=Perltidy, beautify_on_save=false, 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, disabled=false, default_beautifier=puppet-lint, beautify_on_save=false, 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, indent_size=2, disabled=false, default_beautifier=formatR, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, indent_size=2, indent_char= , rubocop_path=, disabled=false, default_beautifier=Rubocop, beautify_on_save=false, rustfmt_path=, disabled=false, default_beautifier=rustfmt, beautify_on_save=false, disabled=false, default_beautifier=SassConvert, beautify_on_save=false, 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, beautify_on_save=true, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, indent_size=2, keywords=upper, identifiers=unchanged, disabled=false, default_beautifier=sqlformat, beautify_on_save=false, beautify_on_save=true, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, 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, 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, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, padding=0, disabled=false, default_beautifier=align-yaml, beautify_on_save=false, , 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= , , , , , , , preserve_ternary_lines=true, formatObject=true, preserve_ternary_lines=true, , , 
2017-08-01T12:52:28.707Z - verbose: [beautifiers/index.coffee] true indent_size=2, indent_char= , indent_with_tabs=false
2017-08-01T12:52:28.707Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.708Z - verbose: [beautifiers/index.coffee] options js indent_size=2, indent_char= , indent_with_tabs=false
2017-08-01T12:52:28.708Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.708Z - verbose: [beautifiers/index.coffee] options jsx indent_size=2, indent_char= , indent_with_tabs=false
2017-08-01T12:52:28.708Z - verbose: [beautifiers/index.coffee] true configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, indent_size=2, disabled=false, default_beautifier=beautysh, beautify_on_save=false, beautify_on_save=true, configPath=/Users/waldgeist/Documents/Development/htdocs/marble/unity/.uncrustify.cfg, disabled=false, default_beautifier=Uncrustify, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=cljfmt, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=Crystal, beautify_on_save=false, 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, beautify_on_save=true, disabled=false, default_beautifier=Pretty Diff, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, disabled=false, default_beautifier=elm-format, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, disabled=false, default_beautifier=erl_tidy, beautify_on_save=false, indent_size=2, indent_char= , disabled=false, default_beautifier=Gherkin formatter, beautify_on_save=false, configPath=, disabled=false, default_beautifier=clang-format, beautify_on_save=false, disabled=false, default_beautifier=gofmt, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, emacs_path=, emacs_script_path=, disabled=false, default_beautifier=Fortran Beautifier, beautify_on_save=false, 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, disabled=false, default_beautifier=stylish-haskell, beautify_on_save=false, 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, indent_size=2, indent_char= , disabled=false, default_beautifier=Pug Beautify, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, 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, 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, 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, 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, end_of_line=System Default, disabled=false, default_beautifier=Lua beautifier, beautify_on_save=false, gfm=true, yaml=true, commonmark=false, disabled=false, default_beautifier=Tidy Markdown, beautify_on_save=false, 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, 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, indent_size=2, indent_char= , indent_with_tabs=false, dontJoinCurlyBracet=true, disabled=false, default_beautifier=Nginx Beautify, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=ocp-indent, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, perltidy_profile=, disabled=false, default_beautifier=Perltidy, beautify_on_save=false, 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, disabled=false, default_beautifier=puppet-lint, beautify_on_save=false, 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, indent_size=2, disabled=false, default_beautifier=formatR, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, indent_size=2, indent_char= , rubocop_path=, disabled=false, default_beautifier=Rubocop, beautify_on_save=false, rustfmt_path=, disabled=false, default_beautifier=rustfmt, beautify_on_save=false, disabled=false, default_beautifier=SassConvert, beautify_on_save=false, 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, beautify_on_save=true, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, indent_size=2, keywords=upper, identifiers=unchanged, disabled=false, default_beautifier=sqlformat, beautify_on_save=false, beautify_on_save=true, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, 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, 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, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, padding=0, disabled=false, default_beautifier=align-yaml, beautify_on_save=false
2017-08-01T12:52:28.709Z - verbose: [beautifiers/index.coffee] options 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
2017-08-01T12:52:28.709Z - verbose: [beautifiers/index.coffee] options 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
2017-08-01T12:52:28.709Z - verbose: [beautifiers/index.coffee] options 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
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options jsx beautify_on_save=true, end_with_newline=false, 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=Pretty Diff, e4x=true
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] true 
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] true 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= 
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options js 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= 
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options jsx 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= 
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] true 
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] true 
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.710Z - verbose: [beautifiers/index.coffee] true 
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] true 
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] true 
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] true 
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] true preserve_ternary_lines=true, formatObject=true, preserve_ternary_lines=true
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options js preserve_ternary_lines=true, formatObject=true
2017-08-01T12:52:28.711Z - verbose: [beautifiers/index.coffee] options js preserve_ternary_lines=true, formatObject=true
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options jsx preserve_ternary_lines=true
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options jsx preserve_ternary_lines=true, formatObject=true
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] true 
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] true 
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] true 
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options js
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] options jsx
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] Babel ES6 JavaScript name=JSX, namespace=jsx, fallback=[js], grammars=[JSX, JavaScript (JSX), Babel ES6 JavaScript, JavaScript with JSX], extensions=[jsx, js], defaultBeautifier=Pretty Diff, type=boolean, default=true, description=Support e4x/jsx syntax
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] language options: {
    "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
}
2017-08-01T12:52:28.712Z - verbose: [beautifiers/index.coffee] JSX /Users/waldgeist/Documents/Development/htdocs/aiddogs/webapp/packages/guzz-live-session-standard/vendor/adapter.js { 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 } indent_size=2, indent_char= , indent_with_tabs=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, indent_size=2, disabled=false, default_beautifier=beautysh, beautify_on_save=false, beautify_on_save=true, configPath=/Users/waldgeist/Documents/Development/htdocs/marble/unity/.uncrustify.cfg, disabled=false, default_beautifier=Uncrustify, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=cljfmt, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=Crystal, beautify_on_save=false, 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, beautify_on_save=true, disabled=false, default_beautifier=Pretty Diff, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, disabled=false, default_beautifier=elm-format, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, disabled=false, default_beautifier=erl_tidy, beautify_on_save=false, indent_size=2, indent_char= , disabled=false, default_beautifier=Gherkin formatter, beautify_on_save=false, configPath=, disabled=false, default_beautifier=clang-format, beautify_on_save=false, disabled=false, default_beautifier=gofmt, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, emacs_path=, emacs_script_path=, disabled=false, default_beautifier=Fortran Beautifier, beautify_on_save=false, 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, disabled=false, default_beautifier=stylish-haskell, beautify_on_save=false, 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, indent_size=2, indent_char= , disabled=false, default_beautifier=Pug Beautify, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, 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, 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, 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, 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, end_of_line=System Default, disabled=false, default_beautifier=Lua beautifier, beautify_on_save=false, gfm=true, yaml=true, commonmark=false, disabled=false, default_beautifier=Tidy Markdown, beautify_on_save=false, 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, 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, indent_size=2, indent_char= , indent_with_tabs=false, dontJoinCurlyBracet=true, disabled=false, default_beautifier=Nginx Beautify, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, disabled=false, default_beautifier=ocp-indent, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, perltidy_profile=, disabled=false, default_beautifier=Perltidy, beautify_on_save=false, 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, disabled=false, default_beautifier=puppet-lint, beautify_on_save=false, 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, indent_size=2, disabled=false, default_beautifier=formatR, beautify_on_save=false, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, indent_size=2, indent_char= , rubocop_path=, disabled=false, default_beautifier=Rubocop, beautify_on_save=false, rustfmt_path=, disabled=false, default_beautifier=rustfmt, beautify_on_save=false, disabled=false, default_beautifier=SassConvert, beautify_on_save=false, 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, beautify_on_save=true, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, indent_size=2, keywords=upper, identifiers=unchanged, disabled=false, default_beautifier=sqlformat, beautify_on_save=false, beautify_on_save=true, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, 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, 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, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, configPath=, disabled=false, default_beautifier=Uncrustify, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, 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, indent_size=2, indent_char= , wrap_line_length=250, preserve_newlines=true, disabled=false, default_beautifier=Pretty Diff, beautify_on_save=false, padding=0, disabled=false, default_beautifier=align-yaml, beautify_on_save=false, , 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= , , , , , , , preserve_ternary_lines=true, formatObject=true, preserve_ternary_lines=true, , , 
2017-08-01T12:52:28.715Z - verbose: [beautifiers/index.coffee] beautifiers 0=JS Beautify, 1=Pretty Diff
2017-08-01T12:52:28.715Z - verbose: [beautifiers/index.coffee] beautifier Pretty Diff
2017-08-01T12:52:28.715Z - debug: [beautifiers/beautifier.coffee] Load executables
2017-08-01T12:52:28.715Z - info: [beautifiers/index.coffee] Analytics is disabled.
2017-08-01T12:52:28.715Z - info: [beautifiers/index.coffee] Analytics is disabled.
2017-08-01T12:52:28.715Z - verbose: [beautifiers/index.coffee] executables
2017-08-01T12:52:28.715Z - verbose: [beautifiers/beautifier.coffee] prettydiff inchar= , insize=2, objsort=false, preserve=all, cssinsertlines=undefined, comments=indent, force=undefined, quoteConvert=undefined, vertical=none, wrap=0, space=false, noleadzero=undefined, endcomma=false, methodchain=true, ternaryline=true, bracepadding=false, crlf=false, source=/*
 *  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);
, lang=jsx, mode=beautify
2017-08-01T12:52:28.777Z - info: [beautifiers/index.coffee] Analytics is disabled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment