text: // Composited file - DO NOT EDIT
  //----------------------------------------------------------------------
  //
  // ECMAScript 5 Polyfills
  //
  //----------------------------------------------------------------------
  
  //----------------------------------------------------------------------
  // ES5 15.2 Object Objects
  //----------------------------------------------------------------------
  
  //
  // ES5 15.2.3 Properties of the Object Constructor
  //
  
  // ES5 15.2.3.2 Object.getPrototypeOf ( O )
  // From http://ejohn.org/blog/objectgetprototypeof/
  // NOTE: won't work for typical function T() {}; T.prototype = {}; new T; case
  // since the constructor property is destroyed.
  if (!Object.getPrototypeOf) {
    Object.getPrototypeOf = function (o) {
      if (o !== Object(o)) { throw TypeError("Object.getPrototypeOf called on non-object"); }
      return o.__proto__ || o.constructor.prototype || Object.prototype;
    };
  }
  
  //    // ES5 15.2.3.3 Object.getOwnPropertyDescriptor ( O, P )
  //    if (typeof Object.getOwnPropertyDescriptor !== "function") {
  //        Object.getOwnPropertyDescriptor = function (o, name) {
  //            if (o !== Object(o)) { throw TypeError(); }
  //            if (o.hasOwnProperty(name)) {
  //                return {
  //                    value: o[name],
  //                    enumerable: true,
  //                    writable: true,
  //                    configurable: true
  //                };
  //            }
  //        };
  //    }
  
  // ES5 15.2.3.4 Object.getOwnPropertyNames ( O )
  if (typeof Object.getOwnPropertyNames !== "function") {
    Object.getOwnPropertyNames = function (o) {
      if (o !== Object(o)) { throw TypeError("Object.getOwnPropertyNames called on non-object"); }
      var props = [], p;
      for (p in o) {
        if (Object.prototype.hasOwnProperty.call(o, p)) {
          props.push(p);
        }
      }
      return props;
    };
  }
  
  // ES5 15.2.3.5 Object.create ( O [, Properties] )
  if (typeof Object.create !== "function") {
    Object.create = function (prototype, properties) {
      if (typeof prototype !== "object") { throw TypeError(); }
      function Ctor() {}
      Ctor.prototype = prototype;
      var o = new Ctor();
      if (prototype) { o.constructor = Ctor; }
      if (properties !== undefined) {
        if (properties !== Object(properties)) { throw TypeError(); }
        Object.defineProperties(o, properties);
      }
      return o;
    };
  }
  
  // ES 15.2.3.6 Object.defineProperty ( O, P, Attributes )
  // Partial support for most common case - getters, setters, and values
  (function() {
    if (!Object.defineProperty ||
        !(function () { try { Object.defineProperty({}, 'x', {}); return true; } catch (e) { return false; } } ())) {
      var orig = Object.defineProperty;
      Object.defineProperty = function (o, prop, desc) {
        // In IE8 try built-in implementation for defining properties on DOM prototypes.
        if (orig) { try { return orig(o, prop, desc); } catch (e) {} }
  
        if (o !== Object(o)) { throw TypeError("Object.defineProperty called on non-object"); }
        if (Object.prototype.__defineGetter__ && ('get' in desc)) {
          Object.prototype.__defineGetter__.call(o, prop, desc.get);
        }
        if (Object.prototype.__defineSetter__ && ('set' in desc)) {
          Object.prototype.__defineSetter__.call(o, prop, desc.set);
        }
        if ('value' in desc) {
          o[prop] = desc.value;
        }
        return o;
      };
    }
  }());
  
  // ES 15.2.3.7 Object.defineProperties ( O, Properties )
  if (typeof Object.defineProperties !== "function") {
    Object.defineProperties = function (o, properties) {
      if (o !== Object(o)) { throw TypeError("Object.defineProperties called on non-object"); }
      var name;
      for (name in properties) {
        if (Object.prototype.hasOwnProperty.call(properties, name)) {
          Object.defineProperty(o, name, properties[name]);
        }
      }
      return o;
    };
  }
  
  
  // ES5 15.2.3.14 Object.keys ( O )
  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
  if (!Object.keys) {
    Object.keys = function (o) {
      if (o !== Object(o)) { throw TypeError('Object.keys called on non-object'); }
      var ret = [], p;
      for (p in o) {
        if (Object.prototype.hasOwnProperty.call(o, p)) {
          ret.push(p);
        }
      }
      return ret;
    };
  }
  
  //----------------------------------------------------------------------
  // ES5 15.3 Function Objects
  //----------------------------------------------------------------------
  
  //
  // ES5 15.3.4 Properties of the Function Prototype Object
  //
  
  // ES5 15.3.4.5 Function.prototype.bind ( thisArg [, arg1 [, arg2, ... ]] )
  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
  if (!Function.prototype.bind) {
    Function.prototype.bind = function (o) {
      if (typeof this !== 'function') { throw TypeError("Bind must be called on a function"); }
  
      var args = Array.prototype.slice.call(arguments, 1),
          self = this,
          nop = function() {},
          bound = function () {
            return self.apply(this instanceof nop ? this : o,
                              args.concat(Array.prototype.slice.call(arguments)));
          };
  
      if (this.prototype)
        nop.prototype = this.prototype;
      bound.prototype = new nop();
      return bound;
    };
  }
  
  
  //----------------------------------------------------------------------
  // ES5 15.4 Array Objects
  //----------------------------------------------------------------------
  
  //
  // ES5 15.4.3 Properties of the Array Constructor
  //
  
  
  // ES5 15.4.3.2 Array.isArray ( arg )
  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
  Array.isArray = Array.isArray || function (o) { return Boolean(o && Object.prototype.toString.call(Object(o)) === '[object Array]'); };
  
  
  //
  // ES5 15.4.4 Properties of the Array Prototype Object
  //
  
  // ES5 15.4.4.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
  // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
  if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (searchElement /*, fromIndex */) {
      if (this === void 0 || this === null) { throw TypeError(); }
  
      var t = Object(this);
      var len = t.length >>> 0;
      if (len === 0) { return -1; }
  
      var n = 0;
      if (arguments.length > 0) {
        n = Number(arguments[1]);
        if (isNaN(n)) {
          n = 0;
        } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
          n = (n > 0 || -1) * Math.floor(Math.abs(n));
        }
      }
  
      if (n >= len) { return -1; }
  
      var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
  
      for (; k < len; k++) {
        if (k in t && t[k] === searchElement) {
          return k;
        }
      }
      return -1;
    };
  }
  
  // ES5 15.4.4.15 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
  // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
  if (!Array.prototype.lastIndexOf) {
    Array.prototype.lastIndexOf = function (searchElement /*, fromIndex*/) {
      if (this === void 0 || this === null) { throw TypeError(); }
  
      var t = Object(this);
      var len = t.length >>> 0;
      if (len === 0) { return -1; }
  
      var n = len;
      if (arguments.length > 1) {
        n = Number(arguments[1]);
        if (n !== n) {
          n = 0;
        } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
          n = (n > 0 || -1) * Math.floor(Math.abs(n));
        }
      }
  
      var k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n);
  
      for (; k >= 0; k--) {
        if (k in t && t[k] === searchElement) {
          return k;
        }
      }
      return -1;
    };
  }
  
  // ES5 15.4.4.16 Array.prototype.every ( callbackfn [ , thisArg ] )
  // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
  if (!Array.prototype.every) {
    Array.prototype.every = function (fun /*, thisp */) {
      if (this === void 0 || this === null) { throw TypeError(); }
  
      var t = Object(this);
      var len = t.length >>> 0;
      if (typeof fun !== "function") { throw TypeError(); }
  
      var thisp = arguments[1], i;
      for (i = 0; i < len; i++) {
        if (i in t && !fun.call(thisp, t[i], i, t)) {
          return false;
        }
      }
  
      return true;
    };
  }
  
  // ES5 15.4.4.17 Array.prototype.some ( callbackfn [ , thisArg ] )
  // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
  if (!Array.prototype.some) {
    Array.prototype.some = function (fun /*, thisp */) {
      if (this === void 0 || this === null) { throw TypeError(); }
  
      var t = Object(this);
      var len = t.length >>> 0;
      if (typeof fun !== "function") { throw TypeError(); }
  
      var thisp = arguments[1], i;
      for (i = 0; i < len; i++) {
        if (i in t && fun.call(thisp, t[i], i, t)) {
          return true;
        }
      }
  
      return false;
    };
  }
  
  // ES5 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )
  // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
  if (!Array.prototype.forEach) {
    Array.prototype.forEach = function (fun /*, thisp */) {
      if (this === void 0 || this === null) { throw TypeError(); }
  
      var t = Object(this);
      var len = t.length >>> 0;
      if (typeof fun !== "function") { throw TypeError(); }
  
      var thisp = arguments[1], i;
      for (i = 0; i < len; i++) {
        if (i in t) {
          fun.call(thisp, t[i], i, t);
        }
      }
    };
  }
  
  
  // ES5 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] )
  // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/Map
  if (!Array.prototype.map) {
    Array.prototype.map = function (fun /*, thisp */) {
      if (this === void 0 || this === null) { throw TypeError(); }
  
      var t = Object(this);
      var len = t.length >>> 0;
      if (typeof fun !== "function") { throw TypeError(); }
  
      var res = []; res.length = len;
      var thisp = arguments[1], i;
      for (i = 0; i < len; i++) {
        if (i in t) {
          res[i] = fun.call(thisp, t[i], i, t);
        }
      }
  
      return res;
    };
  }
  
  // ES5 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] )
  // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/Filter
  if (!Array.prototype.filter) {
    Array.prototype.filter = function (fun /*, thisp */) {
      if (this === void 0 || this === null) { throw TypeError(); }
  
      var t = Object(this);
      var len = t.length >>> 0;
      if (typeof fun !== "function") { throw TypeError(); }
  
      var res = [];
      var thisp = arguments[1], i;
      for (i = 0; i < len; i++) {
        if (i in t) {
          var val = t[i]; // in case fun mutates this
          if (fun.call(thisp, val, i, t)) {
            res.push(val);
          }
        }
      }
  
      return res;
    };
  }
  
  
  // ES5 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue ] )
  // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/Reduce
  if (!Array.prototype.reduce) {
    Array.prototype.reduce = function (fun /*, initialValue */) {
      if (this === void 0 || this === null) { throw TypeError(); }
  
      var t = Object(this);
      var len = t.length >>> 0;
      if (typeof fun !== "function") { throw TypeError(); }
  
      // no value to return if no initial value and an empty array
      if (len === 0 && arguments.length === 1) { throw TypeError(); }
  
      var k = 0;
      var accumulator;
      if (arguments.length >= 2) {
        accumulator = arguments[1];
      } else {
        do {
          if (k in t) {
            accumulator = t[k++];
            break;
          }
  
          // if array contains no values, no initial value to return
          if (++k >= len) { throw TypeError(); }
        }
        while (true);
      }
  
      while (k < len) {
        if (k in t) {
          accumulator = fun.call(undefined, accumulator, t[k], k, t);
        }
        k++;
      }
  
      return accumulator;
    };
  }
  
  
  // ES5 15.4.4.22 Array.prototype.reduceRight ( callbackfn [, initialValue ] )
  // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/ReduceRight
  if (!Array.prototype.reduceRight) {
    Array.prototype.reduceRight = function (callbackfn /*, initialValue */) {
      if (this === void 0 || this === null) { throw TypeError(); }
  
      var t = Object(this);
      var len = t.length >>> 0;
      if (typeof callbackfn !== "function") { throw TypeError(); }
  
      // no value to return if no initial value, empty array
      if (len === 0 && arguments.length === 1) { throw TypeError(); }
  
      var k = len - 1;
      var accumulator;
      if (arguments.length >= 2) {
        accumulator = arguments[1];
      } else {
        do {
          if (k in this) {
            accumulator = this[k--];
            break;
          }
  
          // if array contains no values, no initial value to return
          if (--k < 0) { throw TypeError(); }
        }
        while (true);
      }
  
      while (k >= 0) {
        if (k in t) {
          accumulator = callbackfn.call(undefined, accumulator, t[k], k, t);
        }
        k--;
      }
  
      return accumulator;
    };
  }
  
  
  //----------------------------------------------------------------------
  // ES5 15.5 String Objects
  //----------------------------------------------------------------------
  
  //
  // ES5 15.5.4 Properties of the String Prototype Object
  //
  
  
  // ES5 15.5.4.20 String.prototype.trim()
  if (!String.prototype.trim) {
    String.prototype.trim = function () {
      return String(this).replace(/^\s+/, '').replace(/\s+$/, '');
    };
  }
  
  
  
  //----------------------------------------------------------------------
  // ES5 15.9 Date Objects
  //----------------------------------------------------------------------
  
  
  //
  // ES 15.9.4 Properties of the Date Constructor
  //
  
  // ES5 15.9.4.4 Date.now ( )
  // From https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/now
  if (!Date.now) {
    Date.now = function now() {
      return Number(new Date());
    };
  }
  
  
  //
  // ES5 15.9.5 Properties of the Date Prototype Object
  //
  
  // ES5 15.9.4.43 Date.prototype.toISOString ( )
  // Inspired by http://www.json.org/json2.js
  if (!Date.prototype.toISOString) {
    Date.prototype.toISOString = function () {
      function pad2(n) { return ('00' + n).slice(-2); }
      function pad3(n) { return ('000' + n).slice(-3); }
  
      return this.getUTCFullYear() + '-' +
        pad2(this.getUTCMonth() + 1) + '-' +
        pad2(this.getUTCDate()) + 'T' +
        pad2(this.getUTCHours()) + ':' +
        pad2(this.getUTCMinutes()) + ':' +
        pad2(this.getUTCSeconds()) + '.' +
        pad3(this.getUTCMilliseconds()) + 'Z';
    };
  }
  //----------------------------------------------------------------------
  //
  // ECMAScript 2015 Polyfills
  //
  //----------------------------------------------------------------------
  
  (function (global) {
    "use strict";
  
    // Set this to always override native implementations, for testing
    // the polyfill in browsers with partial/full ES2015 support.
    var OVERRIDE_NATIVE_FOR_TESTING = false;
  
    var undefined = (void 0); // Paranoia
  
    // Helpers
  
    function strict(o) {
      return o === global ? undefined : o;
    }
  
    function isSymbol(s) {
      return (typeof s === 'symbol') || ('Symbol' in global && s instanceof global.Symbol);
    }
  
    function getPropertyDescriptor(target, name) {
      var desc = Object.getOwnPropertyDescriptor(target, name);
      var proto = Object.getPrototypeOf(target);
      while (!desc && proto) {
        desc = Object.getOwnPropertyDescriptor(proto, name);
        proto = Object.getPrototypeOf(proto);
      }
      return desc;
    }
  
    var enqueue = (function(nativePromise, nativeSetImmediate) {
      if (nativePromise)
        return function(job) { nativePromise.resolve().then(function() { job(); }); };
      if (nativeSetImmediate)
        return function(job) { nativeSetImmediate(job); };
      return function(job) { setTimeout(job, 0); };
    }(global['Promise'], global['setImmediate']));
  
    function define(o, p, v, override) {
      if (p in o && !override && !OVERRIDE_NATIVE_FOR_TESTING)
        return;
  
      if (typeof v === 'function') {
        // Sanity check that functions are appropriately named (where possible)
        console.assert(isSymbol(p) || !('name' in v) || v.name === p || v.name === p + '_', 'Expected function name "' + p.toString() + '", was "' + v.name + '"');
        Object.defineProperty(o, p, {
          value: v,
          configurable: true,
          enumerable: false,
          writable: true
        });
      } else {
        Object.defineProperty(o, p, {
          value: v,
          configurable: false,
          enumerable: false,
          writable: false
        });
      }
    }
  
    function set_internal(o, p, v) {
      Object.defineProperty(o, p, {
        value: v,
        configurable: false,
        enumerable: false,
        writable: true
      });
    }
  
    // Snapshot intrinsic functions
    var $isNaN = global.isNaN,
        $parseInt = global.parseInt,
        $parseFloat = global.parseFloat;
  
    var E = Math.E,
        LOG10E = Math.LOG10E,
        LOG2E = Math.LOG2E,
        abs = Math.abs,
        ceil = Math.ceil,
        exp = Math.exp,
        floor = Math.floor,
        log = Math.log,
        max = Math.max,
        min = Math.min,
        pow = Math.pow,
        random = Math.random,
        sqrt = Math.sqrt;
  
    var orig_match = String.prototype.match,
        orig_replace = String.prototype.replace,
        orig_search = String.prototype.search,
        orig_split = String.prototype.split;
  
    // These are used for implementing the polyfills, but not exported.
  
    // Inspired by https://gist.github.com/1638059
    /** @constructor */
    function EphemeronTable() {
      var secretKey = ObjectCreate(null);
  
      function conceal(o) {
        var oValueOf = o.valueOf, secrets = ObjectCreate(null);
        Object.defineProperty(o, 'valueOf', {
            value: (function(secretKey) {
              return function (k) {
                return (k === secretKey) ? secrets : oValueOf.apply(o, arguments);
              };
            }(secretKey)),
          configurable: true,
          writeable: true,
          enumerable: false
          });
        return secrets;
      }
  
      function reveal(o) {
        var v = typeof o.valueOf === 'function' && o.valueOf(secretKey);
        return v === o ? null : v;
      }
  
      return {
        clear: function() {
          secretKey = ObjectCreate(null);
        },
        remove: function(key) {
          var secrets = reveal(key);
          if (secrets && HasOwnProperty(secrets, 'value')) {
            delete secrets.value;
            return true;
          }
          return false;
        },
        get: function(key, defaultValue) {
          var secrets = reveal(key);
          return (secrets && HasOwnProperty(secrets, 'value')) ? secrets.value : defaultValue;
        },
        has: function(key) {
          var secrets = reveal(key);
          return Boolean(secrets && HasOwnProperty(secrets, 'value'));
        },
        set: function(key, value) {
          var secrets = reveal(key) || conceal(key);
          secrets.value = value;
        }
      };
    }
  
    var empty = Object.create(null);
  
    //----------------------------------------------------------------------
    //
    // ECMAScript 2015
    // http://www.ecma-international.org/ecma-262/6.0/
    //
    //----------------------------------------------------------------------
  
    // ---------------------------------------
    // 19.4 Symbol Objects
    // ---------------------------------------
  
    // NOTE: Symbols are defined here - out of spec order - since we need the
    // properties and prototype to be populated for other polyfills.
  
    // NOTE: Not secure, nor is obj[$$symbol] hidden from Object.keys()
  
    var symbolForKey;
    (function() {
      var secret = Object.create(null);
      var symbolMap = {};
      symbolForKey = function(k) {
        return symbolMap[k];
      };
  
      var GlobalSymbolRegistry = [];
  
      function unique(bits) {
        return Array(bits + 1).join('x').replace(/x/g, function() {
          return random() < 0.5 ? '\u200C' : '\u200D'; // JWNJ / ZWJ
        });
      }
  
      // 19.4.1 The Symbol Constructor
      // 19.4.1.1 Symbol ( description=undefined )
      function Symbol(description) {
        if (!(this instanceof Symbol)) return new Symbol(description, secret);
        if (this instanceof Symbol && arguments[1] !== secret) throw TypeError();
  
        var descString = description === undefined ? undefined : String(description);
  
        set_internal(this, '[[SymbolData]]', unique(128));
        set_internal(this, '[[Description]]', descString);
  
        symbolMap[this] = this;
        return this;
      }
  
      if (!('Symbol' in global) || OVERRIDE_NATIVE_FOR_TESTING)
        global.Symbol = Symbol;
  
      // 19.4.2 Properties of the Symbol Constructor
  
      // 19.4.2.1 Symbol.for (key)
      define(Symbol, 'for', function for_(key) {
        var stringKey = String(key);
        for (var i = 0; i < GlobalSymbolRegistry.length; ++i) {
          var e = GlobalSymbolRegistry[i];
          if (SameValue(e['[[key]]'], stringKey)) return e['[[symbol]]'];
        }
        var newSymbol = Symbol(key);
        GlobalSymbolRegistry.push({'[[key]]': stringKey, '[[symbol]]': newSymbol});
        return newSymbol;
      });
  
      // 19.4.2.2 Symbol.hasInstance
      // 19.4.2.3 Symbol.isConcatSpreadable
  
      // 19.4.2.4 Symbol.iterator
      define(global.Symbol, 'iterator', global.Symbol('Symbol.iterator'));
  
      // 19.4.2.5 Symbol.keyFor (sym)
      define(Symbol, 'keyFor', function keyFor(sym) {
        if (!(sym instanceof Symbol)) throw TypeError();
        for (var i = 0; i < GlobalSymbolRegistry.length; ++i) {
          var e = GlobalSymbolRegistry[i];
          if (SameValue(e['[[symbol]]'], sym)) return e['[[key]]'];
        }
        return undefined;
      });
  
      // 19.4.2.6 Symbol.match
      define(global.Symbol, 'match', global.Symbol('Symbol.match'));
  
      // 19.4.2.7 Symbol.prototype
  
      // 19.4.2.8 Symbol.replace
      define(global.Symbol, 'replace', global.Symbol('Symbol.replace'));
  
      // 19.4.2.9 Symbol.search
      define(global.Symbol, 'search', global.Symbol('Symbol.search'));
  
      // 19.4.2.10 Symbol.species
  
      // 19.4.2.11 Symbol.search
      define(global.Symbol, 'split', global.Symbol('Symbol.split'));
  
      // 19.4.2.12 Symbol.toPrimitive
  
      // 19.4.2.13 Symbol.toStringTag
      define(global.Symbol, 'toStringTag', global.Symbol('Symbol.toStringTag'));
  
      // 19.4.2.14 Symbol.unscopables
  
      // 19.4.3 Properties of the Symbol Prototype Object
      // 19.4.3.1 Symbol.prototype.constructor
  
      // 19.4.3.2 Symbol.prototype.toString ( )
      Object.defineProperty(Symbol.prototype, 'toString', {
        value: function toString() {
          var s = strict(this);
          var desc = s['[[Description]]'];
          return 'Symbol(' + (desc === undefined ? '' : desc) + s['[[SymbolData]]'] + ')';
        },
        configurable: true, writeable: true, enumerable: false });
  
      // 19.4.3.3 Symbol.prototype.valueOf ( )
      Object.defineProperty(Symbol.prototype, 'valueOf', {
        value: function valueOf() {
          // To prevent automatic string conversion:
          throw TypeError();
  
          // Spec has approximately the following:
          //var s = strict(this);
          //if (Type(s) === 'symbol') return s;
          //if (Type(s) !== 'object') throw TypeError();
          //if (!('[[SymbolData]]' in s)) throw TypeError();
          //return s['[[SymbolData]]'];
        },
        configurable: true, writeable: true, enumerable: false });
  
      // 19.4.3.4 Symbol.prototype [ @@toStringTag ]
      // (Done later to polyfill partial implementations)
  
      // 19.4.4 Properties of Symbol Instances
    }());
  
    console.assert(typeof global.Symbol() === 'symbol' || symbolForKey(String(global.Symbol('x'))) !== undefined);
  
    // Defined here so that other prototypes can reference it
    // 25.1.2 The %IteratorPrototype% Object
    var $IteratorPrototype$ = {};
  
    //----------------------------------------
    // 6 ECMAScript Data Types and Values
    //----------------------------------------
  
    // 6.1 ECMAScript Language Types
  
    // "Type(x)" is used as shorthand for "the type of x"...
    function Type(v) {
      switch (typeof v) {
      case 'undefined': return 'undefined';
      case 'boolean': return 'boolean';
      case 'number': return 'number';
      case 'string': return 'string';
      case 'symbol': return 'symbol';
      default:
        if (v === null) return 'null';
        if (v instanceof global.Symbol) return 'symbol';
        return 'object';
      }
    }
  
    // 6.1.5.1 Well-Known Symbols
    var $$iterator = global.Symbol.iterator,
        $$match = global.Symbol.match,
        $$replace = global.Symbol.replace,
        $$search = global.Symbol.search,
        $$split = global.Symbol.split,
        $$toStringTag = global.Symbol.toStringTag;
  
    //----------------------------------------
    // 7 Abstract Operations
    //----------------------------------------
  
    //----------------------------------------
    // 7.1 Type Conversion
    //----------------------------------------
  
    // 7.1.1 ToPrimitive ( input [, PreferredType] )
    // just use valueOf()
  
    // 7.1.2 ToBoolean ( argument )
    // just use Boolean()
  
    // 7.1.3 ToNumber ( argument )
    // just use Number()
  
    // 7.1.4 ToInteger ( argument )
    function ToInteger(n) {
      n = Number(n);
      if ($isNaN(n)) return 0;
      if (n === 0 || n === Infinity || n === -Infinity) return n;
      return ((n < 0) ? -1 : 1) * floor(abs(n));
    }
  
    // 7.1.5 ToInt32 ( argument )
    function ToInt32(v) { return v >> 0; }
  
    // 7.1.6 ToUint32 ( argument )
    function ToUint32(v) { return v >>> 0; }
  
    // 7.1.7 ToInt16 ( argument )
    function ToInt16(v) { return (v << 16) >> 16; }
  
    // 7.1.8 ToUint16 ( argument )
    function ToUint16(v) { return v & 0xFFFF; }
  
    // 7.1.9 ToInt8 ( argument )
    function ToInt8(v) { return (v << 24) >> 24; }
  
    // 7.1.10 ToUint8 ( argument )
    function ToUint8(v) { return v & 0xFF; }
  
    // 7.1.11 ToUint8Clamp ( argument )
    function ToUint8Clamp(argument) {
      var number = Number(argument);
      if ($isNaN(number)) return 0;
      if (number <= 0) return 0;
      if (number >= 255) return 255;
      var f = floor(number);
      if ((f + 0.5) < number) return f + 1;
      if (number < (f + 0.5)) return f;
      if (f % 2) return f + 1;
      return f;
    }
  
    // 7.1.12 ToString ( argument )
    // just use String()
  
    // 7.1.13 ToObject ( argument )
    function ToObject(v) {
      if (v === null || v === undefined) throw TypeError();
      return Object(v);
    }
  
    // 7.1.14 ToPropertyKey ( argument )
    function ToPropertyKey(v) {
      return String(v);
    }
  
    // 7.1.15 ToLength ( argument )
    function ToLength(v) {
      var len = ToInteger(v);
      if (len <= 0) return 0;
      if (len === Infinity) return 0x20000000000000 - 1; // 2^53-1
      return min(len, 0x20000000000000 - 1); // 2^53-1
    }
  
    // 7.1.16 CanonicalNumericIndexString ( argument )
  
    //----------------------------------------
    // 7.2 Testing and Comparison Operations
    //----------------------------------------
  
    // 7.2.1 RequireObjectCoercible ( argument )
    // 7.2.2 IsArray ( argument )
  
    // 7.2.3 IsCallable ( argument )
    function IsCallable(o) { return typeof o === 'function'; }
  
    // 7.2.4 IsConstructor ( argument )
    function IsConstructor(o) {
      // Hacks for Safari 7 TypedArray XXXConstructor objects
      if (/Constructor/.test(Object.prototype.toString.call(o))) return true;
      if (/Function/.test(Object.prototype.toString.call(o))) return true;
      // TODO: Can this be improved on?
      return typeof o === 'function';
    }
  
    // 7.2.5 IsExtensible (O)
    // 7.2.6 IsInteger ( argument )
  
    // 7.2.7 IsPropertyKey ( argument )
    function IsPropertyKey(argument) {
      if (Type(argument) === 'string') return true;
      if (Type(argument) === 'symbol') return true;
      return false;
    }
  
    // 7.2.8 IsRegExp ( argument )
  
    // 7.2.9 SameValue(x, y)
    function SameValue(x, y) {
      if (typeof x !== typeof y) return false;
      switch (typeof x) {
      case 'undefined':
        return true;
      case 'number':
        if (x !== x && y !== y) return true;
        if (x === 0 && y === 0) return 1/x === 1/y;
        return x === y;
      case 'boolean':
      case 'string':
      case 'object':
      default:
        return x === y;
      }
    }
  
    // 7.2.10 SameValueZero(x, y)
    function SameValueZero(x, y) {
      if (typeof x !== typeof y) return false;
      switch (typeof x) {
      case 'undefined':
        return true;
      case 'number':
        if (x !== x && y !== y) return true;
        return x === y;
      case 'boolean':
      case 'string':
      case 'object':
      default:
        return x === y;
      }
    }
  
    //----------------------------------------
    // 7.3 Operations on Objects
    //----------------------------------------
  
    // 7.3.1 Get (O, P)
    // - just use o.p or o[p]
  
    // 7.3.2 GetV (V, P)
    function GetV(v, p) {
      var o = ToObject(v);
      return o[p];
    }
  
    // 7.3.3 Set (O, P, V, Throw)
    // - just use o.p = v or o[p] = v
  
  
  
  
    // 7.3.9 GetMethod (O, P)
    function GetMethod(o, p) {
      var func = GetV(o, p);
      if (func === undefined || func === null) return undefined;
      if (!IsCallable(func)) throw TypeError();
      return func;
    }
  
    // 7.3.10 HasProperty (O, P)
    function HasProperty(o, p) {
      while (o) {
        if (Object.prototype.hasOwnProperty.call(o, p)) return true;
        if (Type(o) !== 'object') return false;
        var op = Object.getPrototypeOf(o);
        if (op === o) return false; // IE8 has self-referential prototypes
        o = op;
      }
      return false;
    }
  
    // 7.3.11 HasOwnProperty (O, P)
    function HasOwnProperty(o, p) {
      return Object.prototype.hasOwnProperty.call(o, p);
    }
  
    //----------------------------------------
    // 7.4 Operations on Iterator Objects
    //----------------------------------------
  
    // 7.4.1 GetIterator ( obj, method )
    function GetIterator(obj, method) {
      if (arguments.length < 2)
        method = GetMethod(obj, $$iterator);
      var iterator = method.call(obj);
      if (Type(iterator) !== 'object') throw TypeError();
      return iterator;
    }
  
    // 7.4.2 IteratorNext ( iterator, value )
    function IteratorNext(iterator, value) {
      if (arguments.length < 2)
        var result = iterator.next();
      else
        result = iterator.next(value);
      if (Type(result) !== 'object') throw TypeError();
      return result;
    }
  
    // 7.4.3 IteratorComplete ( iterResult )
    function IteratorComplete(iterResult) {
      console.assert(Type(iterResult) === 'object');
      return Boolean(iterResult.done);
    }
  
    // 7.4.4 IteratorValue ( iterResult )
    function IteratorValue(iterResult) {
      console.assert(Type(iterResult) === 'object');
      return iterResult.value;
    }
  
    // 7.4.5 IteratorStep ( iterator )
    function IteratorStep( iterator, value ) {
      var result = IteratorNext(iterator, value);
      var done = result['done'];
      if (Boolean(done) === true) return false;
      return result;
    }
  
    // 7.4.6 IteratorClose( iterator, completion )
    function IteratorClose( iterator, completion ) {
      console.assert(Type(iterator) === 'object');
      var _return = GetMethod(iterator, 'return');
      if (_return === undefined) return completion;
      try {
        var innerResult = _return[iterator]();
      } catch (result) {
        // TODO: If completion.[[type]] is throw, return completion
        return result;
      }
      if (Type(innerResult) !== 'object') throw TypeError();
      return completion;
    }
  
    // 7.4.7 CreateIterResultObject (value, done)
    function CreateIterResultObject(value, done) {
      console.assert(Type(done) === 'boolean');
      var obj = {};
      obj["value"] = value;
      obj["done"] = done;
      return obj;
    }
  
    // 7.4.8 CreateListIterator (list)
    // 7.4.8.1 ListIterator next( )
  
    //----------------------------------------
    // 8 Executable Code and Execution Contexts
    //----------------------------------------
  
    //----------------------------------------
    // 8.4 Jobs and Job Queues
    //----------------------------------------
  
    // 8.4.1 EnqueueJob ( queueName, job, arguments)
    function EnqueueJob(queueName, job, args) {
      var fn = function() { job.apply(undefined, args); };
      enqueue(fn);
    }
  
    // 8.4.2 NextJob result
    function NextJob(result) {
      // no-op
    }
  
    //----------------------------------------
    // 9 Ordinary and Exotic Objects Behaviors
    //----------------------------------------
  
    // 9.1.11 [[Enumerate]] ()
    function Enumerate(obj) {
      var e = [];
      if (Object(obj) !== obj) return e;
      var visited = new Set;
      while (obj !== null) {
        Object.getOwnPropertyNames(obj).forEach(function(name) {
          if (!visited.has(name)) {
            var desc = Object.getOwnPropertyDescriptor(obj, name);
            if (desc) {
              visited.add(name);
              if (desc.enumerable) e.push(name);
            }
          }
        });
        obj = Object.getPrototypeOf(obj);
      }
      return e[$$iterator]();
    }
  
    // 9.1.12 [[OwnPropertyKeys]] ( )
    function OwnPropertyKeys(o) {
      return Object.getOwnPropertyNames(o);
    }
  
    // 9.1.13 ObjectCreate(proto, internalSlotsList)
    function ObjectCreate(proto, internalSlotsList) {
      return Object.create(proto, internalSlotsList);
    }
  
    // ---------------------------------------
    // 19 Fundamental Objects
    // ---------------------------------------
  
    // ---------------------------------------
    // 19.1 Object Objects
    // ---------------------------------------
  
    // 19.1.1 The Object Constructor
    // 19.1.1.1 Object ( [ value ] )
    // 19.1.2 Properties of the Object Constructor
    // 19.1.2.1 Object.assign ( target, ...sources )
    define(
      Object, 'assign',
      function assign(target, /*...*/sources) {
        var to = ToObject(target);
        if (arguments.length < 2) return to;
  
        var sourcesIndex = 1;
        while (sourcesIndex < arguments.length) {
          var nextSource = arguments[sourcesIndex++];
          if (nextSource === undefined || nextSource === null) {
            var keys = [];
          } else {
            var from = ToObject(nextSource);
            keys = OwnPropertyKeys(from);
          }
          for (var keysIndex = 0; keysIndex < keys.length; ++keysIndex) {
            var nextKey = keys[keysIndex];
            var desc = Object.getOwnPropertyDescriptor(from, nextKey);
            if (desc !== undefined && desc.enumerable) {
              var propValue = from[nextKey];
              to[nextKey] = propValue;
            }
          }
        }
        return to;
      });
  
    // 19.1.2.2 Object.create ( O [ , Properties ] )
    // 19.1.2.3 Object.defineProperties ( O, Properties )
    // 19.1.2.4 Object.defineProperty ( O, P, Attributes )
    // 19.1.2.5 Object.freeze ( O )
    // 19.1.2.6 Object.getOwnPropertyDescriptor ( O, P )
  
    (function() {
      var nativeSymbols = (typeof global.Symbol() === 'symbol'),
          $getOwnPropertyNames = Object.getOwnPropertyNames,
          $keys = Object.keys,
          $window_names = (typeof window === 'object' ? $getOwnPropertyNames(window) : []);
  
      function isStringKey(k) { return !symbolForKey(k); }
  
      // 19.1.2.7 Object.getOwnPropertyNames ( O )
      define(
        Object, 'getOwnPropertyNames',
        function getOwnPropertyNames(o) {
          if (Object.prototype.toString.call(o) === '[object Window]') {
            // Workaround for cross-realm calling by IE itself.
            // https://github.com/inexorabletash/polyfill/issues/96
            try {
              return $getOwnPropertyNames(o).filter(isStringKey);
            } catch (_) {
              return $window_names.slice();
            }
          }
          return $getOwnPropertyNames(o).filter(isStringKey);
        }, !nativeSymbols);
  
      // 19.1.2.8 Object.getOwnPropertySymbols ( O )
      define(
        Object, 'getOwnPropertySymbols',
        function getOwnPropertySymbols(o) {
          return $getOwnPropertyNames(o).filter(symbolForKey).map(symbolForKey);
        }, !nativeSymbols);
  
      // 19.1.2.14 Object.keys ( O )
      define(
        Object, 'keys',
        function keys(o) {
          return $keys(o).filter(isStringKey);
        }, !nativeSymbols);
    }());
  
    // 19.1.2.9 Object.getPrototypeOf ( O )
    // 19.1.2.10 Object.is ( value1, value2 )
    define(
      Object, 'is',
      function is(value1, value2) {
        return SameValue(value1, value2);
      });
  
    // 19.1.2.11 Object.isExtensible ( O )
    // 19.1.2.12 Object.isFrozen ( O )
    // 19.1.2.13 Object.isSealed ( O )
  
    // 19.1.2.14 Object.keys ( O )
    // see above
  
    // 19.1.2.15 Object.preventExtensions ( O )
    // 19.1.2.16 Object.prototype
    // 19.1.2.17 Object.seal ( O )
  
    // 19.1.2.18 Object.setPrototypeOf ( O, proto )
    define(
      Object, 'setPrototypeOf',
      function setPrototypeOf(o, proto) {
        if (Type(o) !== 'object') throw TypeError();
        if (Type(proto) !== 'object' && Type(proto) !== 'null') throw TypeError();
        o.__proto__ = proto;
        return o;
      }
    );
  
    // 19.1.3 Properties of the Object Prototype Object
    // 19.1.3.1 Object.prototype.constructor
    // 19.1.3.2 Object.prototype.hasOwnProperty ( V )
    // 19.1.3.3 Object.prototype.isPrototypeOf ( V )
    // 19.1.3.4 Object.prototype.propertyIsEnumerable ( V )
    // 19.1.3.5 Object.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )
    // 19.1.3.6 Object.prototype.toString ( )
    var o_p_ts = Object.prototype.toString;
    define(Object.prototype, 'toString',
         function() {
           var o = strict(this);
           if (o === Object(o) && $$toStringTag in o) {
             return '[object ' + o[$$toStringTag] + ']';
           }
           return o_p_ts.apply(o, arguments);
         });
  
    // 19.1.3.7 Object.prototype.valueOf ( )
    // 19.1.4 Properties of Object Instances
  
    // ---------------------------------------
    // 19.2 Function Objects
    // ---------------------------------------
  
    // 19.2.1 The Function Constructor
    // 19.2.1.1 Function ( p1, p2, … , pn, body )
    // 19.2.2 Properties of the Function Constructor
    // 19.2.2.1 Function.length
    // 19.2.2.2 Function.prototype
    // 19.2.3 Properties of the Function Prototype Object
    // 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
    // 19.2.3.2 Function.prototype.bind ( thisArg , ...args)
    // 19.2.3.3 Function.prototype.call (thisArg , ...args)
    // 19.2.3.4 Function.prototype.constructor
    // 19.2.3.5 Function.prototype.toString ( )
    // 19.2.3.6 Function.prototype[@@hasInstance] ( V )
    // 19.2.4 Function Instances
    // 19.2.4.1 length
    // 19.2.4.2 name
    // 19.2.4.3 prototype
  
    // (No polyfillable changes from ES5)
  
    // ---------------------------------------
    // 19.3 Boolean Objects
    // ---------------------------------------
  
    // 19.3.1 The Boolean Constructor
    // 19.3.1.1 Boolean ( value )
    // 19.3.2 Properties of the Boolean Constructor
    // 19.3.2.1 Boolean.prototype
    // 19.3.3 Properties of the Boolean Prototype Object
    // 19.3.3.1 Boolean.prototype.constructor
    // 19.3.3.2 Boolean.prototype.toString ( )
    // 19.3.3.3 Boolean.prototype.valueOf ( )
    // 19.3.4 Properties of Boolean Instances
  
    // (No polyfillable changes from ES5)
  
    // ---------------------------------------
    // 19.4 Symbol Objects
    // ---------------------------------------
  
    // Moved earlier in this script, so that other polyfills can depend on them.
  
    // 19.4.3.4 Symbol.prototype [ @@toStringTag ]
    define(global.Symbol.prototype, global.Symbol.toStringTag, 'Symbol');
  
    // ---------------------------------------
    // 19.5 Error Objects
    // ---------------------------------------
  
    // 19.5.1 The Error Constructor
    // 19.5.1.1 Error ( message )
    // 19.5.1.2 new Error( ...argumentsList )
    // 19.5.2 Properties of the Error Constructor
    // 19.5.2.1 Error.prototype
    // 19.5.3 Properties of the Error Prototype Object
    // 19.5.3.1 Error.prototype.constructor
    // 19.5.3.2 Error.prototype.message
    // 19.5.3.3 Error.prototype.name
    // 19.5.3.4 Error.prototype.toString ( )
    // 19.5.4 Properties of Error Instances
    // 19.5.5 Native Error Types Used in This Standard
    // 19.5.5.1 EvalError
    // 19.5.5.2 RangeError
    // 19.5.5.3 ReferenceError
    // 19.5.5.4 SyntaxError
    // 19.5.5.5 TypeError
    // 19.5.5.6 URIError
    // 19.5.6 NativeError Object Structure
    // 19.5.6.1 NativeError Constructors
    // 19.5.6.1.1 NativeError ( message )
    // 19.5.6.1.2 new NativeError ( ...argumentsList )
    // 19.5.6.2 Properties of the NativeError Constructors
    // 19.5.6.2.1 NativeError.prototype
    // 19.5.6.3 Properties of the NativeError Prototype Objects
    // 19.5.6.4 Properties of NativeError Instances
  
    // (No polyfillable changes from ES5)
  
    // ---------------------------------------
    // 20 Numbers and Dates
    // ---------------------------------------
  
    // ---------------------------------------
    // 20.1 Number Objects
    // ---------------------------------------
  
    // 20.1.1 The Number Constructor
    // 20.1.1.1 Number ( [ value ] )
    // 20.1.1.2 new Number ( ...argumentsList )
    // 20.1.2 Properties of the Number Constructor
  
    // 20.1.2.1 Number.EPSILON
    define(
      Number, 'EPSILON',
      (function () {
        var next, result;
        for (next = 1; 1 + next !== 1; next = next / 2)
          result = next;
        return result;
      }()));
  
    // 20.1.2.2 Number.isFinite ( number )
    define(
      Number, 'isFinite',
      function isFinite(number) {
        if (Type(number) !== 'number') return false;
        if (number !== number || number === +Infinity || number === -Infinity) return false;
        return true;
      });
  
    // 20.1.2.3 Number.isInteger ( number )
    define(
      Number, 'isInteger',
      function isInteger(number) {
        if (Type(number) !== 'number') return false;
        if (number !== number || number === +Infinity || number === -Infinity) return false;
        var integer = ToInteger(number);
        if (integer !== number) return false;
        return true;
      });
  
    // 20.1.2.4 Number.isNaN ( number )
    define(
      Number, 'isNaN',
      function isNaN(number) {
        if (Type(number) !== 'number') return false;
        if (number !== number) return true;
        return false;
      });
  
    // 20.1.2.5 Number.isSafeInteger ( number )
    define(
      Number, 'isSafeInteger',
      function isSafeInteger(number) {
        if (Type(number) !== 'number') return false;
        if (number !== number || number === +Infinity || number === -Infinity) return false;
        var integer = ToInteger(number);
        if (integer !== number) return false;
        if (abs(integer) <= (0x20000000000000 - 1)) // 2^53-1
          return true;
        return false;
      });
  
    // 20.1.2.6 Number.MAX_SAFE_INTEGER
    define(
      Number, 'MAX_SAFE_INTEGER',
      9007199254740991); // 2^53-1
  
    // 20.1.2.7 Number.MAX_VALUE
  
    // 20.1.2.8 Number.MIN_SAFE_INTEGER
    define(
      Number, 'MIN_SAFE_INTEGER',
      -9007199254740991); // -2^53+1
  
    // 20.1.2.9 Number.MIN_VALUE
    // 20.1.2.10 Number.NaN
    // 20.1.2.11 Number.NEGATIVE_INFINITY
  
    // 20.1.2.12 Number.parseFloat ( string )
    define(Number, 'parseFloat', $parseFloat);
  
    // 20.1.2.13 Number.parseInt ( string, radix )
    define(Number, 'parseInt', $parseInt);
  
    // 20.1.2.14 Number.POSITIVE_INFINITY
    // 20.1.2.15 Number.prototype
  
    // 20.1.3 Properties of the Number Prototype Object
    // 20.1.3.1 Number.prototype.constructor
    // 20.1.3.2 Number.prototype.toExponential ( fractionDigits )
    // 20.1.3.3 Number.prototype.toFixed ( fractionDigits )
    // 20.1.3.4 Number.prototype.toLocaleString( [ reserved1 [ , reserved2 ] ])
    // 20.1.3.5 Number.prototype.toPrecision ( precision )
    // 20.1.3.6 Number.prototype.toString ( [ radix ] )
    // 20.1.3.7 Number.prototype.valueOf ( )
    // 20.1.4 Properties of Number Instances
  
    // ---------------------------------------
    // 20.2 The Math Object
    // ---------------------------------------
  
    // 20.2.1 Value Properties of the Math Object
    // 20.2.1.1 Math.E
    // 20.2.1.2 Math.LN10
    // 20.2.1.3 Math.LN2
    // 20.2.1.4 Math.LOG10E
    // 20.2.1.5 Math.LOG2E
    // 20.2.1.6 Math.PI
    // 20.2.1.7 Math.SQRT1_2
    // 20.2.1.8 Math.SQRT2
  
    // 20.2.1.9 Math [ @@toStringTag ]
    define(Math, $$toStringTag, 'Math');
  
    // 20.2.2 Function Properties of the Math Object
    // 20.2.2.1 Math.abs ( x )
    // 20.2.2.2 Math.acos ( x )
  
    // 20.2.2.3 Math.acosh(x)
    define(
      Math, 'acosh',
      function acosh(x) {
        x = Number(x);
        return log(x + sqrt(x * x - 1));
      });
  
    // 20.2.2.4 Math.asin ( x )
  
    // 20.2.2.5 Math.asinh( x )
    define(
      Math, 'asinh',
      function asinh(x) {
        x = Number(x);
        if (SameValue(x, -0)) {
          return x;
        }
        var s = sqrt(x * x + 1);
        return (s === -x) ? log(0) : log(x + s);
      });
  
    // 20.2.2.6 Math.atan ( x )
  
    // 20.2.2.7 Math.atanh( x )
    define(
      Math, 'atanh',
      function atanh(x) {
        x = Number(x);
        return (x === 0) ? x : log((1 + x) / (1 - x)) / 2;
      });
  
    // 20.2.2.8 Math.atan2 ( y, x )
  
    // 20.2.2.9 Math.cbrt ( x )
    define(
      Math, 'cbrt',
      function cbrt(x) {
        x = Number(x);
        if ($isNaN(x/x)) {
          return x;
        }
        var r = pow(abs(x), 1/3);
        var t = x/r/r;
        return r + (r * (t-r) / (2*r + t));
      });
  
    // 20.2.2.10 Math.ceil ( x )
  
    // 20.2.2.11 Math.clz32 ( x )
    define(
      Math, 'clz32',
      function clz32(x) {
        function clz8(x) {
          return (x & 0xf0) ? (x & 0x80 ? 0 : x & 0x40 ? 1 : x & 0x20 ? 2 : 3) :
          (x & 0x08 ? 4 : x & 0x04 ? 5 : x & 0x02 ? 6 : x & 0x01 ? 7 : 8);
        }
        x = ToUint32(x);
        return x & 0xff000000 ? clz8(x >> 24) :
          x & 0xff0000 ? clz8(x >> 16) + 8 :
          x & 0xff00 ? clz8(x >> 8) + 16 : clz8(x) + 24;
      });
  
  
  
    // 20.2.2.12 Math.cos ( x )
  
    // 20.2.2.13 Math.cosh ( x )
    define(
      Math, 'cosh',
      function cosh(x) {
        x = Number(x);
        return (pow(E, x) + pow(E, -x)) / 2;
      });
  
    // 20.2.2.14 Math.exp ( x )
  
    // 20.2.2.15 Math.expm1 ( x )
    define(
      Math, 'expm1',
      function expm1(x) {
        x = Number(x);
        // from: http://www.johndcook.com/cpp_log1p.html
        if (SameValue(x, -0)) {
          return -0;
        } else if (abs(x) < 1e-5) {
          return x + 0.5 * x * x; // two terms of Taylor expansion
        } else {
          return exp(x) - 1;
        }
      });
  
    // 20.2.2.16 Math.floor ( x )
  
    // 20.2.2.17 Math.fround ( x )
    define(
      Math, 'fround',
      function fround(x) {
        if ($isNaN(x)) {
          return NaN;
        }
        if (1/x === +Infinity || 1/x === -Infinity || x === +Infinity || x === -Infinity) {
          return x;
        }
        return (new Float32Array([x]))[0];
      });
  
    // 20.2.2.18 Math.hypot ( value1 [, value2 [ ... ] ] )
    define(
      Math, 'hypot',
      function hypot() {
        var values = [];
        var m = 0, sawNaN = false;
        for (var i = 0; i < arguments.length; ++i) {
          var n = abs(Number(arguments[i]));
          if (n === Infinity) return n;
          if (n !== n) sawNaN = true;
          if (n > m) m = n;
          values[i] = n;
        }
        if (sawNaN) return NaN;
        if (m === 0) return +0;
        var sum = +0;
        for (i = 0; i < values.length; ++i) {
          var r = values[i] / m;
          sum = sum + r * r;
        }
        return m * sqrt(sum);
      });
  
    // 20.2.2.19 Math.imul ( x, y )
    define(
      Math, 'imul',
      function imul(x, y) {
        var a = ToUint32(x);
        var b = ToUint32(y);
        // (slow but accurate)
        var ah  = (a >>> 16) & 0xffff;
        var al = a & 0xffff;
        var bh  = (b >>> 16) & 0xffff;
        var bl = b & 0xffff;
        return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0);
      }, ('imul' in Math && Math.imul(1, 0x80000000) === 0) // Safari 7 bug
    );
  
    // 20.2.2.20 Math.log ( x )
  
    // 20.2.2.21 Math.log1p ( x )
    define(
      Math, 'log1p',
      function log1p(x) {
        x = Number(x);
        // from: http://www.johndcook.com/cpp_expm1.html
        if (x < -1) {
          return NaN;
        } else if (SameValue(x, -0)) {
          return -0;
        } else if (abs(x) > 1e-4) {
          return log(1 + x);
        } else {
          return (-0.5 * x + 1) * x;
        }
      });
  
    // 20.2.2.22 Math.log10 ( x )
    define(
      Math, 'log10',
      function log10(x) {
        x = Number(x);
        return log(x) * LOG10E;
      });
  
    // 20.2.2.23 Math.log2 ( x )
    define(
      Math, 'log2',
      function log2(x) {
        x = Number(x);
        return log(x) * LOG2E;
      });
  
    // 20.2.2.24 Math.max ( value1, value2 , ...values )
    // 20.2.2.25 Math.min ( value1, value2 , ...values )
    // 20.2.2.26 Math.pow ( x, y )
    // 20.2.2.27 Math.random ( )
    // 20.2.2.28 Math.round ( x )
  
    // 20.2.2.29 Math.sign(x)
    define(
      Math, 'sign',
      function sign(x) {
        x = Number(x);
        return x < 0 ? -1 : x > 0 ? 1 : x;
      });
  
    // 20.2.2.30 Math.sin ( x )
  
    // 20.2.2.31 Math.sinh( x )
    define(
      Math, 'sinh',
      function sinh(x) {
        x = Number(x);
        return SameValue(x, -0) ? x : (pow(E, x) - pow(E, -x)) / 2;
      });
  
    // 20.2.2.32 Math.sqrt ( x )
    // 20.2.2.33 Math.tan ( x )
  
    // 20.2.2.34 Math.tanh ( x )
    define(
      Math, 'tanh',
      function tanh(x) {
        x = Number(x);
        var n = pow(E, 2 * x) - 1,
            d = pow(E, 2 * x) + 1;
        if (SameValue(x, -0))
          return x;
        return (n === d) ? 1 : n / d; // Handle Infinity/Infinity
      });
  
    // 20.2.2.35 Math.trunc ( x )
    define(
      Math, 'trunc',
      function trunc(x) {
        x = Number(x);
        return $isNaN(x) ? NaN :
          x < 0 ? ceil(x) : floor(x);
      });
  
    // ---------------------------------------
    // 20.3 Date Objects
    // ---------------------------------------
  
    // 20.3.1 Overview of Date Objects and Definitions of Abstract Operations
    // 20.3.1.1 Time Values and Time Range
    // 20.3.1.2 Day Number and Time within Day
    // 20.3.1.3 Year Number
    // 20.3.1.4 Month Number
    // 20.3.1.5 Date Number
    // 20.3.1.6 Week Day
    // 20.3.1.7 Local Time Zone Adjustment
    // 20.3.1.8 Daylight Saving Time Adjustment
    // 20.3.1.9 Local Time
    // 20.3.1.10 Hours, Minutes, Second, and Milliseconds
    // 20.3.1.11 MakeTime (hour, min, sec, ms)
    // 20.3.1.12 MakeDay (year, month, date)
    // 20.3.1.13 MakeDate (day, time)
    // 20.3.1.14 TimeClip (time)
    // 20.3.1.15 Date Time String Format
    // 20.3.1.15.1 Extended years
    // 20.3.2 The Date Constructor
    // 20.3.2.1 Date ( year, month [, date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ] )
    // 20.3.2.2 Date ( value )
    // 20.3.2.3 Date ( )
    // 20.3.3 Properties of the Date Constructor
    // 20.3.3.1 Date.now ( )
    // 20.3.3.2 Date.parse (string)
    // 20.3.3.3 Date.prototype
    // 20.3.3.4 Date.UTC ( year, month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ] )
    // 20.3.4 Properties of the Date Prototype Object
    // 20.3.4.1 Date.prototype.constructor
    // 20.3.4.2 Date.prototype.getDate ( )
    // 20.3.4.3 Date.prototype.getDay ( )
    // 20.3.4.4 Date.prototype.getFullYear ( )
    // 20.3.4.5 Date.prototype.getHours ( )
    // 20.3.4.6 Date.prototype.getMilliseconds ( )
    // 20.3.4.7 Date.prototype.getMinutes ( )
    // 20.3.4.8 Date.prototype.getMonth ( )
    // 20.3.4.9 Date.prototype.getSeconds ( )
    // 20.3.4.10 Date.prototype.getTime ( )
    // 20.3.4.11 Date.prototype.getTimezoneOffset ( )
    // 20.3.4.12 Date.prototype.getUTCDate ( )
    // 20.3.4.13 Date.prototype.getUTCDay ( )
    // 20.3.4.14 Date.prototype.getUTCFullYear ( )
    // 20.3.4.15 Date.prototype.getUTCHours ( )
    // 20.3.4.16 Date.prototype.getUTCMilliseconds ( )
    // 20.3.4.17 Date.prototype.getUTCMinutes ( )
    // 20.3.4.18 Date.prototype.getUTCMonth ( )
    // 20.3.4.19 Date.prototype.getUTCSeconds ( )
    // 20.3.4.20 Date.prototype.setDate ( date )
    // 20.3.4.21 Date.prototype.setFullYear ( year [ , month [ , date ] ] )
    // 20.3.4.22 Date.prototype.setHours ( hour [ , min [ , sec [ , ms ] ] ] )
    // 20.3.4.23 Date.prototype.setMilliseconds ( ms )
    // 20.3.4.24 Date.prototype.setMinutes ( min [ , sec [ , ms ] ] )
    // 20.3.4.25 Date.prototype.setMonth ( month [ , date ] )
    // 20.3.4.26 Date.prototype.setSeconds ( sec [ , ms ] )
    // 20.3.4.27 Date.prototype.setTime ( time )
    // 20.3.4.28 Date.prototype.setUTCDate ( date )
    // 20.3.4.29 Date.prototype.setUTCFullYear ( year [ , month [ , date ] ] )
    // 20.3.4.30 Date.prototype.setUTCHours ( hour [ , min [ , sec [ , ms ] ] ] )
    // 20.3.4.31 Date.prototype.setUTCMilliseconds ( ms )
    // 20.3.4.32 Date.prototype.setUTCMinutes ( min [ , sec [, ms ] ] )
    // 20.3.4.33 Date.prototype.setUTCMonth ( month [ , date ] )
    // 20.3.4.34 Date.prototype.setUTCSeconds ( sec [ , ms ] )
    // 20.3.4.35 Date.prototype.toDateString ( )
    // 20.3.4.36 Date.prototype.toISOString ( )
    // 20.3.4.37 Date.prototype.toJSON ( key )
    // 20.3.4.38 Date.prototype.toLocaleDateString ( [ reserved1 [ , reserved2 ] ] )
    // 20.3.4.39 Date.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )
    // 20.3.4.40 Date.prototype.toLocaleTimeString ( [ reserved1 [ , reserved2 ] ] )
    // 20.3.4.41 Date.prototype.toString ( )
    // 20.3.4.42 Date.prototype.toTimeString ( )
    // 20.3.4.43 Date.prototype.toUTCString ( )
    // 20.3.4.44 Date.prototype.valueOf ( )
    // 20.3.4.45 Date.prototype [ @@toPrimitive ] ( hint )
    // 20.3.5 Properties of Date Instances
  
    // (No polyfillable changes from ES5)
  
    // ---------------------------------------
    // 21 Text Processing
    // ---------------------------------------
  
    var string_regexp_dispatch = (function() {
      var faux = {}, secret = Symbol();
      faux[Symbol.match] = function() { return secret; };
      return ("").match(faux) === secret;
    }());
  
    // 21.1 String Objects
    // 21.1.1 The String Constructor
    // 21.1.1.1 String ( value )
    // 21.1.2 Properties of the String Constructor
    // 21.1.2.1 String.fromCharCode ( ...codeUnits )
  
    // 21.1.2.2 String.fromCodePoint ( ...codePoints )
    define(
      String, 'fromCodePoint',
      function fromCodePoint(/*...codePoints*/) {
        var codePoints = arguments,
            length = codePoints.length,
            elements = [],
            nextIndex = 0;
        while (nextIndex < length) {
          var next = codePoints[nextIndex];
          var nextCP = Number(next);
          if (!SameValue(nextCP, ToInteger(nextCP)) ||
              nextCP < 0 || nextCP > 0x10FFFF) {
            throw RangeError('Invalid code point ' + nextCP);
          }
          if (nextCP < 0x10000) {
            elements.push(String.fromCharCode(nextCP));
          } else {
            nextCP -= 0x10000;
            elements.push(String.fromCharCode((nextCP >> 10) + 0xD800));
            elements.push(String.fromCharCode((nextCP % 0x400) + 0xDC00));
          }
          nextIndex += 1;
        }
        return elements.join('');
      });
  
    // 21.1.2.3 String.prototype
  
    // 21.1.2.4 String.raw ( template , ...substitutions )
    define(
      String, 'raw',
      function raw(template /*, ...substitutions*/) {
        var substitutions = [].slice.call(arguments, 1);
  
        var cooked = Object(template);
        var rawValue = cooked['raw'];
        var raw = Object(rawValue);
        var len = raw['length'];
        var literalSegments = ToLength(len);
        if (literalSegments <= 0) return '';
        var stringElements = [];
        var nextIndex = 0;
        while (true) {
          var next = raw[nextIndex];
          var nextSeg = String(next);
          stringElements.push(nextSeg);
          if (nextIndex + 1 === literalSegments)
            return stringElements.join('');
          next = substitutions[nextIndex];
          var nextSub = String(next);
          stringElements.push(nextSub);
          nextIndex = nextIndex + 1;
        }
      });
  
    // See https://githib.com/inexorabletash/uate for a more useful version.
  
    // 21.1.3 Properties of the String Prototype Object
    // 21.1.3.1 String.prototype.charAt ( pos )
    // 21.1.3.2 String.prototype.charCodeAt ( pos )
  
    // 21.1.3.3 String.prototype.codePointAt ( pos )
    define(
      String.prototype, 'codePointAt',
      function codePointAt(pos) {
        var o = strict(this);
        var s = String(o);
        var position = ToInteger(pos);
        var size = s.length;
        if (position < 0 || position >= size) return undefined;
        var first = s.charCodeAt(position);
        if (first < 0xD800 || first > 0xDBFF || position + 1 === size) return first;
        var second = s.charCodeAt(position + 1);
        if (second < 0xDC00 || second > 0xDFFF) return first;
        return ((first - 0xD800) * 1024) + (second - 0xDC00) + 0x10000;
      });
  
    // 21.1.3.4 String.prototype.concat ( ...args )
    // 21.1.3.5 String.prototype.constructor
  
    // 21.1.3.6 String.prototype.endsWith ( searchString [ , endPosition] )
    define(
      String.prototype, 'endsWith',
      function endsWith(searchString) {
        var endPosition = arguments[1];
  
        var o = strict(this);
        var s = String(o);
        var searchStr = String(searchString);
        var len = s.length;
        var pos = (endPosition === undefined) ? len : ToInteger(endPosition);
        var end = min(max(pos, 0), len);
        var searchLength = searchStr.length;
        var start = end - searchLength;
        if (start < 0) return false;
        if (s.substring(start, start + searchLength) === searchStr) return true;
        return false;
      });
  
    // 21.1.3.7 String.prototype.includes ( searchString [ , position ] )
    define(
      String.prototype, 'includes',
      function includes(searchString) {
        var position = arguments[1];
  
        var o = strict(this);
        var s = String(o);
        var searchStr = String(searchString);
        var pos = ToInteger(position);
        var len = s.length;
        var start = min(max(pos, 0), len);
        return s.indexOf(searchStr, start) !== -1;
      });
  
    // 21.1.3.8 String.prototype.indexOf ( searchString [ , position ] )
    // 21.1.3.9 String.prototype.lastIndexOf ( searchString [ , position ] )
    // 21.1.3.10 String.prototype.localeCompare ( that [, reserved1 [ , reserved2 ] ] )
    // 21.1.3.11 String.prototype.match ( regexp )
    define(
      String.prototype, 'match',
      function match(regexp) {
        var o = strict(this);
        var s = String(o);
        if (HasProperty(regexp, $$match)) var rx = regexp;
        else rx = new RegExp(regexp);
        return rx[$$match](s);
      }, !string_regexp_dispatch);
  
    // 21.1.3.12 String.prototype.normalize ( [ form ] )
  
    // Not practical due to table sizes; if needed, pull in:
    // https://github.com/walling/unorm/
  
    // 21.1.3.13 String.prototype.repeat ( count )
    define(
      String.prototype, 'repeat',
      function repeat(count) {
        var o = strict(this);
        var s = String(o);
        var n = ToInteger(count);
        if (n < 0) throw RangeError();
        if (n === Infinity) throw RangeError();
        var t = new Array(n + 1).join(s);
        return t;
      });
  
    // 21.1.3.14 String.prototype.replace (searchValue, replaceValue )
    define(
      String.prototype, 'replace',
      function replace(searchValue, replaceValue) {
        var o = strict(this);
        if (HasProperty(searchValue, $$replace))
          return searchValue[$$replace](o, replaceValue);
        return orig_replace.call(o, searchValue, replaceValue);
      }, !string_regexp_dispatch);
  
    // 21.1.3.15 String.prototype.search ( regexp )
    define(
      String.prototype, 'search',
      function search(regexp) {
        var o = strict(this);
        var string = String(o);
        if (HasProperty(regexp, $$search)) var rx = regexp;
        else rx = new RegExp(regexp);
        return rx[$$search](string);
      }, !string_regexp_dispatch);
  
    // 21.1.3.16 String.prototype.slice ( start, end )
    // 21.1.3.17 String.prototype.split ( separator, limit )
    define(
      String.prototype, 'split',
      function split(separator, limit) {
        var o = strict(this);
        if (HasProperty(separator, $$split))
          return separator[$$split](o, limit);
        return orig_split.call(o, separator, limit);
      }, !string_regexp_dispatch);
  
    // 21.1.3.18 String.prototype.startsWith ( searchString [, position ] )
    define(
      String.prototype, 'startsWith',
      function startsWith(searchString) {
        var position = arguments[1];
  
        var o = strict(this);
        var s = String(o);
        var searchStr = String(searchString);
        var pos = ToInteger(position);
        var len = s.length;
        var start = min(max(pos, 0), len);
        var searchLength = searchStr.length;
        if (searchLength + start > len) return false;
        if (s.substring(start, start + searchLength) === searchStr) return true;
        return false;
      });
  
    // 21.1.3.19 String.prototype.substring ( start, end )
    // 21.1.3.20 String.prototype.toLocaleLowerCase ( [ reserved1 [ , reserved2 ] ] )
    // 21.1.3.21 String.prototype.toLocaleUpperCase ([ reserved1 [ , reserved2 ] ] )
    // 21.1.3.22 String.prototype.toLowerCase ( )
    // 21.1.3.23 String.prototype.toString ( )
    // 21.1.3.24 String.prototype.toUpperCase ( )
    // 21.1.3.25 String.prototype.trim ( )
    // 21.1.3.26 String.prototype.valueOf ( )
  
    // 21.1.3.27 String.prototype [ @@iterator ]( )
    define(
      String.prototype, $$iterator,
      function entries() {
        return CreateStringIterator(this, 'value');
      });
  
    // 21.1.4 Properties of String Instances
    // 21.1.4.1 length
  
    // 21.1.5 String Iterator Objects
    /** @constructor */
    function StringIterator() {}
  
    // 21.1.5.1 CreateStringIterator Abstract Operation
    function CreateStringIterator(string, kind) {
      var s = String(string);
      var iterator = new StringIterator;
      set_internal(iterator, '[[IteratedString]]', s);
      set_internal(iterator, '[[StringIteratorNextIndex]]', 0);
      set_internal(iterator, '[[StringIterationKind]]', kind);
      return iterator;
    }
  
    // 21.1.5.2 The %StringIteratorPrototype% Object
    var $StringIteratorPrototype$ = Object.create($IteratorPrototype$);
    StringIterator.prototype = $StringIteratorPrototype$;
  
    // 21.1.5.2.1 %StringIteratorPrototype%.next ( )
    define(
      $StringIteratorPrototype$, 'next',
      function next() {
        var o = ToObject(this);
        var s = String(o['[[IteratedString]]']),
            index = o['[[StringIteratorNextIndex]]'],
            len = s.length;
        if (index >= len) {
          set_internal(o, '[[StringIteratorNextIndex]]', Infinity);
          return CreateIterResultObject(undefined, true);
        }
        var cp = s.codePointAt(index);
        set_internal(o, '[[StringIteratorNextIndex]]', index + (cp > 0xFFFF ? 2 : 1));
        return CreateIterResultObject(String.fromCodePoint(cp), false);
      });
  
    // 21.1.5.2.2 %StringIteratorPrototype% [ @@toStringTag ]
    define($StringIteratorPrototype$, $$toStringTag, 'String Iterator');
  
    // 21.1.5.3 Properties of String Iterator Instances
  
    // ---------------------------------------
    // 21.2 RegExp (Regular Expression) Objects
    // ---------------------------------------
  
    // 21.2.1 Patterns
    // 21.2.2 Pattern Semantics
    // 21.2.2.1 Notation
    // 21.2.2.2 Pattern
    // 21.2.2.3 Disjunction
    // 21.2.2.4 Alternative
    // 21.2.2.5 Term
    // 21.2.2.6 Assertion
    // 21.2.2.7 Quantifier
    // 21.2.2.8 Atom
    // 21.2.2.9 AtomEscape
    // 21.2.2.10 CharacterEscape
    // 21.2.2.11 DecimalEscape
    // 21.2.2.12 CharacterClassEscape
    // 21.2.2.13 CharacterClass
    // 21.2.2.14 ClassRanges
    // 21.2.2.15 NonemptyClassRanges
    // 21.2.2.16 NonemptyClassRangesNoDash
    // 21.2.2.17 ClassAtom
    // 21.2.2.18 ClassAtomNoDash
    // 21.2.2.19 ClassEscape
    // 21.2.3 The RegExp Constructor
    // 21.2.3.1 RegExp ( pattern, flags )
    // 21.2.3.2 new RegExp( ...argumentsList )
    // 21.2.3.3 Abstract Operations for the RegExp Constructor
    // 21.2.4 Properties of the RegExp Constructor
    // 21.2.4.1 RegExp.prototype
    // 21.2.5 Properties of the RegExp Prototype Object
    // 21.2.5.1 RegExp.prototype.constructor
    // 21.2.5.2 RegExp.prototype.exec ( string )
  
    // 21.2.5.3 get RegExp.prototype.flags
    if (!('flags' in RegExp.prototype)) {
      Object.defineProperty(
        RegExp.prototype, 'flags', {
          get: function() {
            var s = String(this);
            return s.substring(s.lastIndexOf('/') + 1);
          }
        });
    }
  
    // 21.2.5.4 get RegExp.prototype.global
    // 21.2.5.5 get RegExp.prototype.ignoreCase
  
    // 21.2.5.6 RegExp.prototype [ @@match ] ( string )
    define(RegExp.prototype, $$match, function(string) {
      var o = strict(this);
      return orig_match.call(string, o);
    });
  
    // 21.2.5.7 get RegExp.prototype.multiline
  
    // 21.2.5.8 RegExp.prototype [ @@replace ] ( string, replaceValue )
    define(RegExp.prototype, $$replace, function(string, replaceValue) {
      var o = strict(this);
      return orig_replace.call(string, o, replaceValue);
    });
  
    // 21.2.5.9 RegExp.prototype [ @@search ] ( string )
    define(RegExp.prototype, $$search, function(string) {
      var o = strict(this);
      return orig_search.call(string, o);
    });
  
    // 21.2.5.10 get RegExp.prototype.source
  
    // 21.2.5.11 RegExp.prototype [ @@split ] ( string, limit )
    define(RegExp.prototype, $$split, function(string, limit) {
      var o = strict(this);
      return orig_split.call(string, o, limit);
    });
  
    // 21.2.5.12 get RegExp.prototype.sticky
    // 21.2.5.13 RegExp.prototype.test( S )
    // 21.2.5.14 RegExp.prototype.toString ( )
    // 21.2.5.15 get RegExp.prototype.unicode
  
    // 21.2.6 Properties of RegExp Instances
    // 21.2.6.1 lastIndex
  
    // (No polyfillable changes from ES5)
  
    // ---------------------------------------
    // 22 Indexed Collections
    // ---------------------------------------
  
    // ---------------------------------------
    // 22.1 Array Objects
    // ---------------------------------------
  
    // 22.1.1 The Array Constructor
    // 22.1.1.1 Array ( )
    // 22.1.1.2 Array (len)
    // 22.1.1.3 Array (...items )
  
    // 22.1.2 Properties of the Array Constructor
  
    // 22.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] )
    define(
      Array, 'from',
      function from(items) {
        var mapfn = arguments[1];
        var thisArg = arguments[2];
  
        var c = strict(this);
        if (mapfn === undefined) {
          var mapping = false;
        } else {
          if (!IsCallable(mapfn)) throw TypeError();
          var t = thisArg;
          mapping = true;
        }
        var usingIterator = GetMethod(items, $$iterator);
        if (usingIterator !== undefined) {
           if (IsConstructor(c)) {
            var a = new c();
          } else {
            a = new Array(0);
          }
          var iterator = GetIterator(items, usingIterator);
          var k = 0;
          while (true) {
            var next = IteratorStep(iterator);
            if (next === false) {
              a.length = k;
              return a;
            }
            var nextValue = IteratorValue(next);
            if (mapping)
              var mappedValue = mapfn.call(t, nextValue);
            else
              mappedValue = nextValue;
            a[k] = mappedValue;
            k += 1;
          }
        }
        var arrayLike = ToObject(items);
        var lenValue = arrayLike.length;
        var len = ToLength(lenValue);
        if (IsConstructor(c)) {
          a = new c(len);
        } else {
          a = new Array(len);
        }
        k = 0;
        while (k < len) {
          var kValue = arrayLike[k];
          if (mapping)
            mappedValue = mapfn.call(t, kValue, k);
          else
            mappedValue = kValue;
          a[k] = mappedValue;
          k += 1;
        }
        a.length = len;
        return a;
      });
  
    // 22.1.2.2 Array.isArray ( arg )
  
    // 22.1.2.3 Array.of ( ...items )
    define(
      Array, 'of',
      function of() {
        var items = arguments;
  
        var lenValue = items.length;
        var len = ToUint32(lenValue);
        var c = strict(this), a;
        if (IsConstructor(c)) {
          a = new c(len);
          a = ToObject(a);
        } else {
          a = new Array(len);
        }
        var k = 0;
        while (k < len) {
          a[k] = items[k];
          k += 1;
        }
        a.length = len;
        return a;
      });
  
    // 22.1.2.4 Array.prototype
    // 22.1.2.5 get Array [ @@species ]
    // 22.1.3 Properties of the Array Prototype Object
    // 22.1.3.1 Array.prototype.concat ( ...arguments )
    // 22.1.3.1.1 Runtime Semantics: IsConcatSpreadable ( O )
    // 22.1.3.2 Array.prototype.constructor
    // 22.1.3.3 Array.prototype.copyWithin (target, start [ , end ] )
    define(
      Array.prototype, 'copyWithin',
      function copyWithin(target, start/*, end*/) {
        var end = arguments[2];
  
        var o = ToObject(this);
        var lenVal = o.length;
        var len = ToLength(lenVal);
        len = max(len, 0);
        var relativeTarget = ToInteger(target);
        var to;
        if (relativeTarget < 0)
          to = max(len + relativeTarget, 0);
        else
          to = min(relativeTarget, len);
        var relativeStart = ToInteger(start);
        var from;
        if (relativeStart < 0)
          from = max(len + relativeStart, 0);
        else
          from = min(relativeStart, len);
        var relativeEnd;
        if (end === undefined)
          relativeEnd = len;
        else
          relativeEnd = ToInteger(end);
        var final;
        if (relativeEnd < 0)
          final = max(len + relativeEnd, 0);
        else
          final = min(relativeEnd, len);
        var count = min(final - from, len - to);
        var direction;
        if (from < to && to < from + count) {
          direction = -1;
          from = from + count - 1;
          to = to + count - 1;
        } else {
          direction = 1;
        }
        while (count > 0) {
          var fromKey = String(from);
          var toKey = String(to);
          var fromPresent = HasProperty(o, fromKey);
          if (fromPresent) {
            var fromVal = o[fromKey];
            o[toKey] = fromVal;
          } else {
            delete o[toKey];
          }
          from = from + direction;
          to = to + direction;
          count = count - 1;
        }
        return o;
      });
  
    // 22.1.3.4 Array.prototype.entries ( )
    var nativeArrayIteratorMethods =
          ('entries' in Array.prototype && 'next' in [].entries());
  
    define(
      Array.prototype, 'entries',
      function entries() {
        return CreateArrayIterator(this, 'key+value');
      }, !nativeArrayIteratorMethods);
  
    // 22.1.3.5 Array.prototype.every ( callbackfn [ , thisArg] )
  
    // 22.1.3.6 Array.prototype.fill (value [ , start [ , end ] ] )
    define(
      Array.prototype, 'fill',
      function fill(value/*, start, end*/) {
        var start = arguments[1],
            end = arguments[2];
  
        var o = ToObject(this);
        var lenVal = o.length;
        var len = ToLength(lenVal);
        len = max(len, 0);
        var relativeStart = ToInteger(start);
        var k;
        if (relativeStart < 0)
          k = max((len + relativeStart), 0);
        else
          k = min(relativeStart, len);
        var relativeEnd;
        if (end === undefined)
          relativeEnd = len;
        else
          relativeEnd = ToInteger(end);
        var final;
        if (relativeEnd < 0)
          final = max((len + relativeEnd), 0);
        else
          final = min(relativeEnd, len);
        while (k < final) {
          var pk = String(k);
          o[pk] = value;
          k += 1;
        }
        return o;
      });
  
    // 22.1.3.7 Array.prototype.filter ( callbackfn [ , thisArg ] )
  
    // 22.1.3.8 Array.prototype.find ( predicate [ , thisArg ] )
    define(
      Array.prototype, 'find',
      function find(predicate) {
        var o = ToObject(this);
        var lenValue = o.length;
        var len = ToInteger(lenValue);
        if (!IsCallable(predicate)) throw TypeError();
        var t = arguments.length > 1 ? arguments[1] : undefined;
        var k = 0;
        while (k < len) {
          var pk = String(k);
          var kPresent = HasProperty(o, pk);
          if (kPresent) {
            var kValue = o[pk];
            var testResult = predicate.call(t, kValue, k, o);
            if (Boolean(testResult)) {
              return kValue;
            }
          }
          ++k;
        }
        return undefined;
      });
  
    // 22.1.3.9 Array.prototype.findIndex ( predicate [ , thisArg ] )
    define(
      Array.prototype, 'findIndex',
      function findIndex(predicate) {
        var o = ToObject(this);
        var lenValue = o.length;
        var len = ToLength(lenValue);
        if (!IsCallable(predicate)) throw TypeError();
        var t = arguments.length > 1 ? arguments[1] : undefined;
        var k = 0;
        while (k < len) {
          var pk = String(k);
          var kPresent = HasProperty(o, pk);
          if (kPresent) {
            var kValue = o[pk];
            var testResult = predicate.call(t, kValue, k, o);
            if (Boolean(testResult)) {
              return k;
            }
          }
          ++k;
        }
        return -1;
      });
  
    // 22.1.3.10 Array.prototype.forEach ( callbackfn [ , thisArg ] )
    // 22.1.3.11 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
    // 22.1.3.12 Array.prototype.join (separator)
  
    // 22.1.3.13 Array.prototype.keys ( )
    define(
      Array.prototype, 'keys',
      function keys() {
        return CreateArrayIterator(this, 'key');
      }, !nativeArrayIteratorMethods);
  
    // 22.1.3.14 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
    // 22.1.3.15 Array.prototype.map ( callbackfn [ , thisArg ] )
    // 22.1.3.16 Array.prototype.pop ( )
    // 22.1.3.17 Array.prototype.push ( ...items )
    // 22.1.3.18 Array.prototype.reduce ( callbackfn [ , initialValue ] )
    // 22.1.3.19 Array.prototype.reduceRight ( callbackfn [ , initialValue ] )
    // 22.1.3.20 Array.prototype.reverse ( )
    // 22.1.3.21 Array.prototype.shift ( )
    // 22.1.3.22 Array.prototype.slice (start, end)
    // 22.1.3.23 Array.prototype.some ( callbackfn [ , thisArg ] )
    // 22.1.3.24 Array.prototype.sort (comparefn)
    // 22.1.3.25 Array.prototype.splice (start, deleteCount , ...items )
    // 22.1.3.26 Array.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )
    // 22.1.3.27 Array.prototype.toString ( )
    // 22.1.3.28 Array.prototype.unshift ( ...items )
  
    // 22.1.3.29 Array.prototype.values ( )
    define(
      Array.prototype, 'values',
      function values() {
        return CreateArrayIterator(this, 'value');
      }, !nativeArrayIteratorMethods);
  
    // 22.1.3.30 Array.prototype [ @@iterator ] ( )
    define(
      Array.prototype, $$iterator,
      Array.prototype.values
      );
  
    // 22.1.3.31 Array.prototype [ @@unscopables ]
    // 22.1.4 Properties of Array Instances
    // 22.1.4.1 length
  
    // 22.1.5 Array Iterator Objects
    function ArrayIterator() {}
  
    // 22.1.5.1 CreateArrayIterator Abstract Operation
    function CreateArrayIterator(array, kind) {
      var o = ToObject(array);
      var iterator = new ArrayIterator;
      set_internal(iterator, '[[IteratedObject]]', o);
      set_internal(iterator, '[[ArrayIteratorNextIndex]]', 0);
      set_internal(iterator, '[[ArrayIterationKind]]', kind);
      return iterator;
    }
  
    // 22.1.5.2 The %ArrayIteratorPrototype% Object
    var $ArrayIteratorPrototype$ = Object.create($IteratorPrototype$);
    ArrayIterator.prototype = $ArrayIteratorPrototype$;
  
    // 22.1.5.2.1 %ArrayIteratorPrototype%. next( )
    define(
      $ArrayIteratorPrototype$, 'next',
      function next() {
        var o = strict(this);
        if (Type(o) !== 'object') throw TypeError();
        var a = o['[[IteratedObject]]'],
            index = o['[[ArrayIteratorNextIndex]]'],
            itemKind = o['[[ArrayIterationKind]]'],
            lenValue = a.length,
            len = ToUint32(lenValue),
            elementKey,
            elementValue;
        if (itemKind.indexOf('sparse') !== -1) {
          var found = false;
          while (!found && index < len) {
            elementKey = String(index);
            found = HasProperty(a, elementKey);
            if (!found) {
              index += 1;
            }
          }
        }
        if (index >= len) {
          set_internal(o, '[[ArrayIteratorNextIndex]]', Infinity);
          return CreateIterResultObject(undefined, true);
        }
        elementKey = index;
        set_internal(o, '[[ArrayIteratorNextIndex]]', index + 1);
        if (itemKind.indexOf('value') !== -1)
          elementValue = a[elementKey];
        if (itemKind.indexOf('key+value') !== -1)
          return CreateIterResultObject([elementKey, elementValue], false);
        if (itemKind.indexOf('key') !== -1)
          return CreateIterResultObject(elementKey, false);
        if (itemKind === 'value')
          return CreateIterResultObject(elementValue, false);
        throw Error('Internal error');
      });
  
    // 22.1.5.2.2 %ArrayIteratorPrototype% [ @@toStringTag ]
    define($ArrayIteratorPrototype$, $$toStringTag, 'Array Iterator');
  
    // 22.1.5.3 Properties of Array Iterator Instances
  
  
    // ---------------------------------------
    // 22.2 TypedArray Objects
    // ---------------------------------------
  
    // See typedarray.js for TypedArray polyfill
  
    ['Int8Array', 'Uint8Array', 'Uint8ClampedArray',
     'Int16Array', 'Uint16Array',
     'Int32Array', 'Uint32Array',
     'Float32Array', 'Float64Array'].forEach(function ($TypedArrayName$) {
       if (!($TypedArrayName$ in global))
         return;
       var $TypedArray$ = global[$TypedArrayName$];
  
       // 22.2.1 The %TypedArray% Intrinsic Object
       // 22.2.1.1 %TypedArray% ( length )
       // 22.2.1.2 %TypedArray% ( typedArray )
       // 22.2.1.3 %TypedArray% ( object )
       // 22.2.1.4 %TypedArray% ( buffer [ , byteOffset [ , length ] ] )
       // 22.2.1.5 %TypedArray% ( all other argument combinations )
       // 22.2.2 Properties of the %TypedArray% Intrinsic Object
  
       // 22.2.2.1 %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )
       define(
         $TypedArray$, 'from',
         function from(source) {
           var mapfn = arguments[1];
           var thisArg = arguments[2];
  
           var c = strict(this);
           if (!IsConstructor(c)) throw TypeError();
           if (mapfn === undefined) {
             var mapping = false;
           } else {
             if (IsCallable(mapfn)) throw TypeError();
             var t = thisArg;
             mapping = true;
           }
           var usingIterator = GetMethod(source, $$iterator);
           if (usingIterator !== undefined) {
             var iterator = GetIterator(source, usingIterator);
             var values = [];
             var next = true;
             while (next !== false) {
               next = IteratorStep(iterator);
               if (next !== false) {
                 var nextValue = IteratorValue(next);
                 values.push(nextValue);
               }
             }
             var len = values.length;
             var newObj = new c(len);
             var k = 0;
             while (k < len) {
               var kValue = values.shift();
               if (mapping) {
                 var mappedValue = mapfn.call(t, kValue);
               } else {
                 mappedValue = kValue;
               }
               newObj[k] = mappedValue;
               ++k;
             }
             console.assert(values.length === 0);
             return newObj;
           }
           var arrayLike = ToObject(source);
           var lenValue = arrayLike.length;
           len = ToLength(lenValue);
           newObj = new c(len);
           k = 0;
           while (k < len) {
             kValue = arrayLike[k];
             if (mapping) {
               mappedValue = mapfn.call(t, kValue, k);
             } else {
               mappedValue = kValue;
             }
             newObj[k] = mappedValue;
             ++k;
           }
           return newObj;
         });
  
       // 22.2.2.2 %TypedArray%.of ( ...items )
       define(
         $TypedArray$, 'of',
         function of() {
           var items = arguments;
  
           var len = items.length;
           var c = strict(this);
           var newObj = new c(len);
           var k = 0;
           while (k < len) {
             newObj[k] = items[k];
             ++k;
           }
           return newObj;
         });
  
       // 22.2.2.3 %TypedArray%.prototype
       // 22.2.2.4 get %TypedArray% [ @@species ]
       // 22.2.3 Properties of the %TypedArrayPrototype% Object
       // 22.2.3.1 get %TypedArray%.prototype.buffer
       // 22.2.3.2 get %TypedArray%.prototype.byteLength
       // 22.2.3.3 get %TypedArray%.prototype.byteOffset
       // 22.2.3.4 %TypedArray%.prototype.constructor
  
       // 22.2.3.5 %TypedArray%.prototype.copyWithin (target, start [, end ] )
       define($TypedArray$.prototype, 'copyWithin', Array.prototype.copyWithin);
  
       // 22.2.3.6 %TypedArray%.prototype.entries ( )
       define($TypedArray$.prototype, 'entries', Array.prototype.entries);
  
       // 22.2.3.7 %TypedArray%.prototype.every ( callbackfn [ , thisArg ] )
       define($TypedArray$.prototype, 'every', Array.prototype.every);
  
       // 22.2.3.8 %TypedArray%.prototype.fill (value [ , start [ , end ] ] )
       define(
         $TypedArray$.prototype, 'fill',
         //Array.prototype.fill // Doesn't work in Safari 7
         function fill(value/*, start, end*/) {
           var start = arguments[1],
               end = arguments[2];
  
           var o = ToObject(this);
           var lenVal = o.length;
           var len = ToLength(lenVal);
           len = max(len, 0);
           var relativeStart = ToInteger(start);
           var k;
           if (relativeStart < 0) k = max((len + relativeStart), 0);
           else k = min(relativeStart, len);
           var relativeEnd;
           if (end === undefined) relativeEnd = len;
           else relativeEnd = ToInteger(end);
           var final;
           if (relativeEnd < 0) final = max((len + relativeEnd), 0);
           else final = min(relativeEnd, len);
           while (k < final) {
             var pk = String(k);
             o[pk] = value;
             k += 1;
           }
           return o;
         });
  
       // 22.2.3.9 %TypedArray%.prototype.filter ( callbackfn [ , thisArg ] )
       define(
         $TypedArray$.prototype, 'filter',
         function filter(callbackfn) {
           var thisArg = arguments[1];
  
           var o = ToObject(this);
           var lenVal = o.length;
           var len = ToLength(lenVal);
           if (!IsCallable(callbackfn)) throw TypeError();
           var t = thisArg;
           var c = o.constructor;
           var kept = [];
           var k = 0;
           var captured = 0;
           while (k < len) {
             var kValue = o[k];
             var selected = callbackfn.call(t, kValue, k, o);
             if (selected) {
               kept.push(kValue);
               ++captured;
             }
             ++k;
           }
           var a = new c(captured);
           var n = 0;
           for (var i = 0; i < kept.length; ++i) {
             var e = kept[i];
             a[n] = e;
             ++n;
           }
           return a;
         });
  
       // 22.2.3.10 %TypedArray%.prototype.find (predicate [ , thisArg ] )
       define($TypedArray$.prototype, 'find', Array.prototype.find);
  
       // 22.2.3.11 %TypedArray%.prototype.findIndex ( predicate [ , thisArg ] )
       define($TypedArray$.prototype, 'findIndex', Array.prototype.findIndex);
  
       // 22.2.3.12 %TypedArray%.prototype.forEach ( callbackfn [ , thisArg ] )
       define($TypedArray$.prototype, 'forEach', Array.prototype.forEach);
  
       // 22.2.3.13 %TypedArray%.prototype.indexOf (searchElement [ , fromIndex ] )
       define($TypedArray$.prototype, 'indexOf', Array.prototype.indexOf);
  
       // 22.2.3.14 %TypedArray%.prototype.join ( separator )
       define($TypedArray$.prototype, 'join', Array.prototype.join);
  
       // 22.2.3.15 %TypedArray%.prototype.keys ( )
       define($TypedArray$.prototype, 'keys', Array.prototype.keys);
  
       // 22.2.3.16 %TypedArray%.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
       define($TypedArray$.prototype, 'lastIndexOf', Array.prototype.lastIndexOf);
  
       // 22.2.3.17 get %TypedArray%.prototype.length
  
       // 22.2.3.18 %TypedArray%.prototype.map ( callbackfn [ , thisArg ] )
       define(
         $TypedArray$.prototype, 'map',
         function map(callbackfn) {
           var thisArg = arguments[1];
  
           var o = ToObject(this);
           var lenValue = o.length;
           var len = ToLength(lenValue);
           if (!IsCallable(callbackfn)) throw TypeError();
           var t = thisArg;
           var a = undefined;
           var c = o.constructor;
           if (IsConstructor(c))
             a = new c(len);
           if (a === undefined)
             a = new Array(len);
           var k = 0;
           while (k < len) {
             var kPresent = HasProperty(o, k);
             if (kPresent) {
               var kValue = o[k];
               var mappedValue = callbackfn.call(t, kValue, k, o);
               a[k] = mappedValue;
             }
             ++k;
           }
           return a;
         });
  
       // 22.2.3.19 %TypedArray%.prototype.reduce ( callbackfn [, initialValue] )
       define($TypedArray$.prototype, 'reduce', Array.prototype.reduce);
  
       // 22.2.3.20 %TypedArray%.prototype.reduceRight ( callbackfn [, initialValue] )
       define($TypedArray$.prototype, 'reduceRight', Array.prototype.reduceRight);
  
       // 22.2.3.21 %TypedArray%.prototype.reverse ( )
       define($TypedArray$.prototype, 'reverse', Array.prototype.reverse);
  
       // 22.2.3.22 %TypedArray%.prototype.set ( overloaded [ , offset ])
       // 22.2.3.22.1 %TypedArray%.prototype.set (array [ , offset ] )
       // 22.2.3.22.2 %TypedArray%.prototype.set(typedArray [, offset ] )
  
       // 22.2.3.23 %TypedArray%.prototype.slice ( start, end )
       define(
         $TypedArray$.prototype, 'slice',
         function slice(start, end) {
           var o = ToObject(this);
           var lenVal = o.length;
           var len = ToLength(lenVal);
           var relativeStart = ToInteger(start);
           var k = (relativeStart < 0) ? max(len + relativeStart, 0) : min(relativeStart, len);
           var relativeEnd = (end === undefined) ? len : ToInteger(end);
           var final = (relativeEnd < 0) ? max(len + relativeEnd, 0) : min(relativeEnd, len);
           var count = final - k;
           var c = o.constructor;
           if (IsConstructor(c)) {
             var a = new c(count);
           } else {
             throw TypeError();
           }
           var n = 0;
           while (k < final) {
             var kValue = o[k];
             a[n] = kValue;
             ++k;
             ++n;
           }
           return a;
         });
  
       // 22.2.3.24 %TypedArray%.prototype.some ( callbackfn [ , thisArg ] )
       define($TypedArray$.prototype, 'some', Array.prototype.some);
  
       // 22.2.3.25 %TypedArray%.prototype.sort ( comparefn )
       define(
         $TypedArray$.prototype, 'sort',
         function sort() {
           var comparefn = arguments[0];
  
           function sortCompare(x, y) {
             console.assert(Type(x) === 'number' && Type(y) === 'number');
             if (x !== x && y !== y) return +0;
             if (x !== x) return 1;
             if (y !== y) return -1;
             if (comparefn !== undefined) {
               return comparefn(x, y);
             }
             if (x < y) return -1;
             if (x > y) return 1;
             return +0;
           }
           return Array.prototype.sort.call(this, sortCompare);
         });
  
       // 22.2.3.26 %TypedArray%.prototype.subarray( [ begin [ , end ] ] )
       // 22.2.3.27 %TypedArray%.prototype.toLocaleString ([ reserved1 [ , reserved2 ] ])
       // 22.2.3.28 %TypedArray%.prototype.toString ( )
  
       // 22.2.3.29 %TypedArray%.prototype.values ( )
       define($TypedArray$.prototype, 'values', Array.prototype.values);
  
       // 22.2.3.30 %TypedArray%.prototype [ @@iterator ] ( )
       define(
         $TypedArray$.prototype, $$iterator,
         $TypedArray$.prototype.values
       );
  
       // 22.2.3.31 get %TypedArray%.prototype [ @@toStringTag ]
       define($TypedArray$.prototype, $$toStringTag, $TypedArrayName$);
  
       // 22.2.4 The TypedArray Constructors
       // 22.2.4.1TypedArray( ... argumentsList)
       // 22.2.5 Properties of the TypedArray Constructors
       // 22.2.5.1 TypedArray.BYTES_PER_ELEMENT
       // 22.2.5.2 TypedArray.prototype
       // 22.2.6 Properties of TypedArray Prototype Objects
       // 22.2.6.1 TypedArray.prototype.BYTES_PER_ELEMENT
       // 22.2.6.2 TypedArray.prototype.constructor
       // 22.2.7 Properties of TypedArray Instances
     });
  
    // ---------------------------------------
    // 23 Keyed Collection
    // ---------------------------------------
  
    // ---------------------------------------
    // 23.1 Map Objects
    // ---------------------------------------
  
    (function() {
      // 23.1.1 The Map Constructor
  
      // 23.1.1.1 Map ( [ iterable ] )
      /** @constructor */
      function Map(/*iterable*/) {
        var map = strict(this);
        var iterable = arguments[0];
  
        if (Type(map) !== 'object') throw TypeError();
        if ('[[MapData]]' in map) throw TypeError();
  
        if (iterable !== undefined) {
          var adder = map['set'];
          if (!IsCallable(adder)) throw TypeError();
          var iter = GetIterator(ToObject(iterable));
        }
        set_internal(map, '[[MapData]]', { keys: [], values: [] });
        if (iter === undefined) return map;
        while (true) {
          var next = IteratorStep(iter);
          if (next === false)
            return map;
          var nextItem = IteratorValue(next);
          if (Type(nextItem) !== 'object') throw TypeError();
          var k = nextItem[0];
          var v = nextItem[1];
          adder.call(map, k, v);
        }
  
        return map;
      }
  
      if (!('Map' in global) || OVERRIDE_NATIVE_FOR_TESTING ||
          (function() { try { new global.Map([]); return false; } catch (_) { return true; } }()) ||
          (function() { try { return !new global.Map().entries().next; } catch (_) { return true; } }()) ||
          (new global.Map([['a', 1]]).size !== 1))
        global.Map = Map;
  
  
      function MapDataIndexOf(mapData, key) {
        var i;
        if (key === key) return mapData.keys.indexOf(key);
        // Slow case for NaN
        for (i = 0; i < mapData.keys.length; i += 1)
          if (SameValueZero(mapData.keys[i], key)) return i;
        return -1;
      }
  
      // 23.1.1.2 new Map ( ... argumentsList )
      // 23.1.2 Properties of the Map Constructor
      // 23.1.2.1 Map.prototype
      var $MapPrototype$ = {};
      Map.prototype = $MapPrototype$;
  
      // 23.1.2.2 get Map [ @@species ]
  
      // 23.1.3 Properties of the Map Prototype Object
      // 23.1.3.1 Map.prototype.clear ()
      define(
        Map.prototype, 'clear',
        function clear() {
          var m = strict(this);
          if (Type(m) !== 'object') throw TypeError();
          if (!('[[MapData]]' in m)) throw TypeError();
          if (m['[[MapData]]'] === undefined) throw TypeError();
          var entries = m['[[MapData]]'];
          entries.keys.length = 0;
          entries.values.length = 0;
          return undefined;
        });
  
      // 23.1.3.2 Map.prototype.constructor
  
      // 23.1.3.3 Map.prototype.delete ( key )
      define(
        Map.prototype, 'delete',
        function delete_(key) {
          var m = strict(this);
          if (Type(m) !== 'object') throw TypeError();
          if (!('[[MapData]]' in m)) throw TypeError();
          if (m['[[MapData]]'] === undefined) throw TypeError();
          var entries = m['[[MapData]]'];
          var i = MapDataIndexOf(entries, key);
          if (i < 0) return false;
          entries.keys[i] = empty;
          entries.values[i] = empty;
          return true;
        });
  
      // 23.1.3.4 Map.prototype.entries ( )
      define(
        Map.prototype, 'entries',
        function entries() {
          var m = strict(this);
          if (Type(m) !== 'object') throw TypeError();
          return CreateMapIterator(m, 'key+value');
        });
  
      // 23.1.3.5 Map.prototype.forEach ( callbackfn [ , thisArg ] )
      define(
        Map.prototype, 'forEach',
        function forEach(callbackfn /*, thisArg*/) {
          var thisArg = arguments[1];
  
          var m = strict(this);
          if (Type(m) !== 'object') throw TypeError();
          if (!('[[MapData]]' in m)) throw TypeError();
          if (m['[[MapData]]'] === undefined) throw TypeError();
          var entries = m['[[MapData]]'];
  
          if (!IsCallable(callbackfn)) {
            throw TypeError('First argument to forEach is not callable.');
          }
          for (var i = 0; i < entries.keys.length; ++i) {
            if (entries.keys[i] !== empty) {
              callbackfn.call(thisArg, entries.values[i], entries.keys[i], m);
            }
          }
          return undefined;
        });
  
      // 23.1.3.6 Map.prototype.get ( key )
      define(
        Map.prototype, 'get',
        function get(key) {
          var m = strict(this);
          if (Type(m) !== 'object') throw TypeError();
          if (!('[[MapData]]' in m)) throw TypeError();
          if (m['[[MapData]]'] === undefined) throw TypeError();
          var entries = m['[[MapData]]'];
          var i = MapDataIndexOf(entries, key);
          if (i >= 0) return entries.values[i];
          return undefined;
        });
  
      // 23.1.3.7 Map.prototype.has ( key )
      define(
        Map.prototype, 'has',
        function has(key) {
          var m = strict(this);
          if (Type(m) !== 'object') throw TypeError();
          if (!('[[MapData]]' in m)) throw TypeError();
          if (m['[[MapData]]'] === undefined) throw TypeError();
          var entries = m['[[MapData]]'];
          if (MapDataIndexOf(entries, key) >= 0) return true;
          return false;
        });
  
      // 23.1.3.8 Map.prototype.keys ( )
      define(
        Map.prototype, 'keys',
        function keys() {
          var m = strict(this);
          if (Type(m) !== 'object') throw TypeError();
          return CreateMapIterator(m, 'key');
        });
  
      // 23.1.3.9 Map.prototype.set ( key , value )
      define(
        Map.prototype, 'set',
        function set(key, value) {
          var m = strict(this);
          if (Type(m) !== 'object') throw TypeError();
          if (!('[[MapData]]' in m)) throw TypeError();
          if (m['[[MapData]]'] === undefined) throw TypeError();
          var entries = m['[[MapData]]'];
          var i = MapDataIndexOf(entries, key);
          if (i < 0) i = entries.keys.length;
          if (SameValue(key, -0)) key = 0;
          entries.keys[i] = key;
          entries.values[i] = value;
          return m;
        });
  
      // 23.1.3.10 get Map.prototype.size
      Object.defineProperty(
        Map.prototype, 'size', {
          get: function() {
            var m = strict(this);
            if (Type(m) !== 'object') throw TypeError();
            if (!('[[MapData]]' in m)) throw TypeError();
            if (m['[[MapData]]'] === undefined) throw TypeError();
            var entries = m['[[MapData]]'];
            var count = 0;
            for (var i = 0; i < entries.keys.length; ++i) {
              if (entries.keys[i] !== empty)
                count = count + 1;
            }
            return count;
          }
        });
  
      // 23.1.3.11 Map.prototype.values ( )
      define(
        Map.prototype, 'values',
        function values() {
          var m = strict(this);
          if (Type(m) !== 'object') throw TypeError();
          return CreateMapIterator(m, 'value');
        });
  
      // 23.1.3.12 Map.prototype [ @@iterator ]( )
      define(
        Map.prototype, $$iterator,
        function() {
          var m = strict(this);
          if (Type(m) !== 'object') throw TypeError();
          return CreateMapIterator(m, 'key+value');
        });
  
      // 23.1.3.13 Map.prototype [ @@toStringTag ]
      define(global.Map.prototype, $$toStringTag, 'Map');
  
      // 23.1.4 Properties of Map Instances
      // 23.1.5 Map Iterator Objects
  
      /** @constructor */
      function MapIterator() {}
  
      // 23.1.5.1 CreateMapIterator Abstract Operation
      function CreateMapIterator(map, kind) {
        if (Type(map) !== 'object') throw TypeError();
        if (!('[[MapData]]' in map)) throw TypeError();
        if (map['[[MapData]]'] === undefined) throw TypeError();
        var iterator = new MapIterator;
        set_internal(iterator, '[[Map]]', map);
        set_internal(iterator, '[[MapNextIndex]]', 0);
        set_internal(iterator, '[[MapIterationKind]]', kind);
        return iterator;
      }
  
      // 23.1.5.2 The %MapIteratorPrototype% Object
      var $MapIteratorPrototype$ = Object.create($IteratorPrototype$);
      MapIterator.prototype = $MapIteratorPrototype$;
  
      // 23.1.5.2.1 %MapIteratorPrototype%.next ( )
      define(
        $MapIteratorPrototype$, 'next',
        function next() {
          var o = strict(this);
          if (Type(o) !== 'object') throw TypeError();
          var m = o['[[Map]]'],
              index = o['[[MapNextIndex]]'],
              itemKind = o['[[MapIterationKind]]'],
              entries = m['[[MapData]]'];
          while (index < entries.keys.length) {
            var e = {key: entries.keys[index], value: entries.values[index]};
            index = index += 1;
            set_internal(o, '[[MapNextIndex]]', index);
            if (e.key !== empty) {
              if (itemKind === 'key') {
                return CreateIterResultObject(e.key, false);
              } else if (itemKind === 'value') {
                return CreateIterResultObject(e.value, false);
              } else {
                return CreateIterResultObject([e.key, e.value], false);
              }
            }
          }
          return CreateIterResultObject(undefined, true);
        });
  
      // 23.1.5.2.2 %MapIteratorPrototype% [ @@toStringTag ]
      define($MapIteratorPrototype$, $$toStringTag, 'Map Iterator');
  
      // 23.1.5.3 Properties of Map Iterator Instances
    }());
  
    // ---------------------------------------
    // 23.2 Set Objects
    // ---------------------------------------
  
    (function() {
      // 23.2.1 The Set Constructor
      // 23.2.1.1 Set ( [ iterable ] )
  
      /** @constructor */
      function Set(/*iterable*/) {
        var set = strict(this);
        var iterable = arguments[0];
  
        if (Type(set) !== 'object') throw TypeError();
        if ('[[SetData]]' in set) throw TypeError();
  
        if (iterable !== undefined) {
          var adder = set['add'];
          if (!IsCallable(adder)) throw TypeError();
          var iter = GetIterator(ToObject(iterable));
        }
        set_internal(set, '[[SetData]]', []);
        if (iter === undefined) return set;
        while (true) {
          var next = IteratorStep(iter);
          if (next === false)
            return set;
          var nextValue = IteratorValue(next);
          adder.call(set, nextValue);
        }
  
        return set;
      }
  
      if (!('Set' in global) || OVERRIDE_NATIVE_FOR_TESTING ||
          (function() { try { return !new global.Set().entries().next; } catch (_) { return true; } }()) ||
          (new global.Set([1]).size !== 1))
        global.Set = Set;
  
      function SetDataIndexOf(setData, key) {
        var i;
        if (key === key)
          return setData.indexOf(key);
        // Slow case for NaN
        for (i = 0; i < setData.length; i += 1)
          if (SameValueZero(setData[i], key)) return i;
        return -1;
      }
  
      // 23.2.1.2 new Set ( ...argumentsList )
      // 23.2.2 Properties of the Set Constructor
  
      // 23.2.2.1 Set.prototype
      var $SetPrototype$ =  {};
      Set.prototype = $SetPrototype$;
  
      // 23.2.2.2 get Set [ @@species ]
      // 23.2.3 Properties of the Set Prototype Object
  
      // 23.2.3.1 Set.prototype.add (value )
      define(
        Set.prototype, 'add',
        function add(value) {
          var s = strict(this);
          if (Type(s) !== 'object') throw TypeError();
          if (!('[[SetData]]' in s)) throw TypeError();
          if (s['[[SetData]]'] === undefined) throw TypeError();
          if (SameValue(value, -0)) value = 0;
          var entries = s['[[SetData]]'];
          var i = SetDataIndexOf(entries, value);
          if (i < 0) i = s['[[SetData]]'].length;
          s['[[SetData]]'][i] = value;
  
          return s;
        });
  
      // 23.2.3.2 Set.prototype.clear ()
      define(
        Set.prototype, 'clear',
        function clear() {
          var s = strict(this);
          if (Type(s) !== 'object') throw TypeError();
          if (!('[[SetData]]' in s)) throw TypeError();
          if (s['[[SetData]]'] === undefined) throw TypeError();
          var entries = s['[[SetData]]'];
          entries.length = 0;
          return undefined;
        });
  
      // 23.2.3.3 Set.prototype.constructor
      // 23.2.3.4 Set.prototype.delete ( value )
      define(
        Set.prototype, 'delete',
        function delete_(value) {
          var s = strict(this);
          if (Type(s) !== 'object') throw TypeError();
          if (!('[[SetData]]' in s)) throw TypeError();
          if (s['[[SetData]]'] === undefined) throw TypeError();
          var entries = s['[[SetData]]'];
          var i = SetDataIndexOf(entries, value);
          if (i < 0) return false;
          entries[i] = empty;
          return true;
        });
  
      // 23.2.3.5 Set.prototype.entries ( )
      define(
        Set.prototype, 'entries',
        function entries() {
          var s = strict(this);
          if (Type(s) !== 'object') throw TypeError();
          return CreateSetIterator(s, 'key+value');
        });
  
      // 23.2.3.6 Set.prototype.forEach ( callbackfn [ , thisArg ] )
      define(
        Set.prototype, 'forEach',
        function forEach(callbackfn/*, thisArg*/) {
          var thisArg = arguments[1];
  
          var s = strict(this);
          if (Type(s) !== 'object') throw TypeError();
          if (!('[[SetData]]' in s)) throw TypeError();
          if (s['[[SetData]]'] === undefined) throw TypeError();
          var entries = s['[[SetData]]'];
  
          if (!IsCallable(callbackfn)) {
            throw TypeError('First argument to forEach is not callable.');
          }
          for (var i = 0; i < entries.length; ++i) {
            if (entries[i] !== empty) {
              callbackfn.call(thisArg, entries[i], entries[i], s);
            }
          }
        });
  
      // 23.2.3.7 Set.prototype.has ( value )
      define(
        Set.prototype, 'has',
        function has(key) {
          var s = strict(this);
          if (Type(s) !== 'object') throw TypeError();
          if (!('[[SetData]]' in s)) throw TypeError();
          if (s['[[SetData]]'] === undefined) throw TypeError();
          var entries = s['[[SetData]]'];
          return SetDataIndexOf(entries, key) !== -1;
        });
  
      // 23.2.3.8 Set.prototype.keys ( )
      // See Set.prototype.values
  
      // 23.2.3.9 get Set.prototype.size
      Object.defineProperty(
        Set.prototype, 'size', {
          get: function() {
            var s = strict(this);
            if (Type(s) !== 'object') throw TypeError();
            if (!('[[SetData]]' in s)) throw TypeError();
            if (s['[[SetData]]'] === undefined) throw TypeError();
            var entries = s['[[SetData]]'];
            var count = 0;
            for (var i = 0; i < entries.length; ++i) {
              if (entries[i] !== empty)
                count = count + 1;
            }
            return count;
          }
        });
  
      // 23.2.3.10 Set.prototype.values ( )
      define(
        Set.prototype, 'values',
        function values() {
          var s = strict(this);
          if (Type(s) !== 'object') throw TypeError();
          return CreateSetIterator(s, 'value');
        });
      // NOTE: function name is still 'values':
      Set.prototype.keys = Set.prototype.values;
  
      // 23.2.3.11 Set.prototype [@@iterator ] ( )
      define(
        Set.prototype, $$iterator,
        function() {
          var s = strict(this);
          if (Type(s) !== 'object') throw TypeError();
          return CreateSetIterator(s);
        });
  
      // 23.2.3.12 Set.prototype [ @@toStringTag ]
      define(global.Set.prototype, $$toStringTag, 'Set');
  
      // 23.2.4 Properties of Set Instances
      // 23.2.5 Set Iterator Objects
      /** @constructor */
      function SetIterator() {}
  
      // 23.2.5.1 CreateSetIterator Abstract Operation
      function CreateSetIterator(set, kind) {
        if (Type(set) !== 'object') throw TypeError();
        if (!('[[SetData]]' in set)) throw TypeError();
        if (set['[[SetData]]'] === undefined) throw TypeError();
        var iterator = new SetIterator;
        set_internal(iterator, '[[IteratedSet]]', set);
        set_internal(iterator, '[[SetNextIndex]]', 0);
        set_internal(iterator, '[[SetIterationKind]]', kind);
        return iterator;
      }
  
      // 23.2.5.2 The %SetIteratorPrototype% Object
      var $SetIteratorPrototype$ = Object.create($IteratorPrototype$);
      SetIterator.prototype = $SetIteratorPrototype$;
  
      // 23.2.5.2.1 %SetIteratorPrototype%.next( )
      define(
        $SetIteratorPrototype$, 'next',
        function next() {
          var o = strict(this);
          if (Type(o) !== 'object') throw TypeError();
          var s = o['[[IteratedSet]]'],
              index = o['[[SetNextIndex]]'],
              itemKind = o['[[SetIterationKind]]'],
              entries = s['[[SetData]]'];
          while (index < entries.length) {
            var e = entries[index];
            index = index += 1;
            set_internal(o, '[[SetNextIndex]]', index);
            if (e !== empty) {
              if (itemKind === 'key+value')
                return CreateIterResultObject([e, e], false);
              return CreateIterResultObject(e, false);
            }
          }
          return CreateIterResultObject(undefined, true);
        });
  
      // 23.2.5.2.2 %SetIteratorPrototype% [ @@toStringTag ]
      define($SetIteratorPrototype$, $$toStringTag, 'Set Iterator');
  
      // 23.2.5.3 Properties of Set Iterator Instances
  
    }());
  
    // ---------------------------------------
    // 23.3 WeakMap Objects
    // ---------------------------------------
  
    (function() {
      // 23.3.1 The WeakMap Constructor
      // 23.3.1.1 WeakMap ( [ iterable ] )
      /** @constructor */
      function WeakMap(/*iterable*/) {
        var map = strict(this);
        var iterable = arguments[0];
  
        if (Type(map) !== 'object') throw TypeError();
        if ('[[WeakMapData]]' in map) throw TypeError();
  
        if (iterable !== undefined) {
          var adder = map['set'];
          if (!IsCallable(adder)) throw TypeError();
          var iter = GetIterator(ToObject(iterable));
        }
        set_internal(map, '[[WeakMapData]]', new EphemeronTable);
        if (iter === undefined) return map;
        while (true) {
          var next = IteratorStep(iter);
          if (next === false)
            return map;
          var nextValue = IteratorValue(next);
          if (Type(nextValue) !== 'object') throw TypeError();
          var k = nextValue[0];
          var v = nextValue[1];
          adder.call(map, k, v);
        }
  
        return map;
      }
  
      if (!('WeakMap' in global) || OVERRIDE_NATIVE_FOR_TESTING)
        global.WeakMap = WeakMap;
  
      // 23.3.2 Properties of the WeakMap Constructor
      // 23.3.2.1 WeakMap.prototype
      var $WeakMapPrototype$ = {};
      WeakMap.prototype = $WeakMapPrototype$;
  
  
  
     // 23.3.2.2 WeakMap[ @@create ] ( )
      // 23.3.3 Properties of the WeakMap Prototype Object
  
      // 23.3.3.1 WeakMap.prototype.constructor
  
      // 23.3.3.2 WeakMap.prototype.delete ( key )
      define(
        WeakMap.prototype, 'delete',
        function delete_(key) {
          var M = strict(this);
          if (Type(M) !== 'object') throw TypeError();
          if (M['[[WeakMapData]]'] === undefined) throw TypeError();
          if (Type(key) !== 'object') throw TypeError('Expected object');
          return M['[[WeakMapData]]'].remove(key);
        });
  
      // 23.3.3.3 WeakMap.prototype.get ( key )
      define(
        WeakMap.prototype, 'get',
        function get(key, defaultValue) {
          var M = strict(this);
          if (Type(M) !== 'object') throw TypeError();
          if (M['[[WeakMapData]]'] === undefined) throw TypeError();
          if (Type(key) !== 'object') throw TypeError('Expected object');
          return M['[[WeakMapData]]'].get(key, defaultValue);
        });
  
      // 23.3.3.4 WeakMap.prototype.has ( key )
      define(
        WeakMap.prototype, 'has',
        function has(key) {
          var M = strict(this);
          if (Type(M) !== 'object') throw TypeError();
          if (M['[[WeakMapData]]'] === undefined) throw TypeError();
          if (Type(key) !== 'object') throw TypeError('Expected object');
          return M['[[WeakMapData]]'].has(key);
        });
  
      // 23.3.3.5 WeakMap.prototype.set ( key , value )
      define(
        WeakMap.prototype, 'set',
        function set(key, value) {
          var M = strict(this);
          if (Type(M) !== 'object') throw TypeError();
          if (M['[[WeakMapData]]'] === undefined) throw TypeError();
          if (Type(key) !== 'object') throw TypeError('Expected object');
          M['[[WeakMapData]]'].set(key, value);
          return M;
        });
  
      // 23.3.3.6 WeakMap.prototype [ @@toStringTag ]
      define(global.WeakMap.prototype, $$toStringTag, 'WeakMap');
  
      // 23.3.4 Properties of WeakMap Instances
  
      // Polyfills for incomplete native implementations:
      (function() {
        var wm = new global.WeakMap();
        var orig = global.WeakMap.prototype.set;
        define(global.WeakMap.prototype, 'set', function set() {
          orig.apply(this, arguments);
          return this;
        }, wm.set({}, 0) !== wm);
      }());
    }());
  
    // ---------------------------------------
    // 23.4 WeakSet Objects
    // ---------------------------------------
  
    (function() {
      // 23.4.1 The WeakSet Constructor
      // 23.4.1.1 WeakSet ( [ iterable ] )
      /** @constructor */
      function WeakSet(/*iterable*/) {
        var set = strict(this);
        var iterable = arguments[0];
  
        if (Type(set) !== 'object') throw TypeError();
        if ('[[WeakSetData]]' in set) throw TypeError();
  
        if (iterable !== undefined) {
          var adder = set['add'];
          if (!IsCallable(adder)) throw TypeError();
          var iter = GetIterator(ToObject(iterable));
        }
        set_internal(set, '[[WeakSetData]]', new EphemeronTable);
        if (iter === undefined) return set;
        while (true) {
          var next = IteratorStep(iter);
          if (next === false)
            return set;
          var nextValue = IteratorValue(next);
          adder.call(set, nextValue);
        }
  
        return set;
      }
  
      if (!('WeakSet' in global) || OVERRIDE_NATIVE_FOR_TESTING)
        global.WeakSet = WeakSet;
  
      // 23.4.2 Properties of the WeakSet Constructor
      // 23.4.2.1 WeakSet.prototype
      var $WeakSetPrototype$ = {};
      WeakSet.prototype = $WeakSetPrototype$;
  
      // 23.4.3 Properties of the WeakSet Prototype Object
      // 23.4.3.1 WeakSet.prototype.add (value )
      define(
        WeakSet.prototype, 'add',
        function add(value) {
          var S = strict(this);
          if (Type(S) !== 'object') throw TypeError();
          if (S['[[WeakSetData]]'] === undefined) throw TypeError();
          if (Type(value) !== 'object') throw TypeError('Expected object');
          S['[[WeakSetData]]'].set(value, true);
          return S;
        });
  
      // 23.4.3.2 WeakSet.prototype.constructor
      // 23.4.3.3 WeakSet.prototype.delete ( value )
      define(
        WeakSet.prototype, 'delete',
        function delete_(value) {
          var S = strict(this);
          if (Type(S) !== 'object') throw TypeError();
          if (S['[[WeakSetData]]'] === undefined) throw TypeError();
          if (Type(value) !== 'object') throw TypeError('Expected object');
          return S['[[WeakSetData]]'].remove(value);
        });
  
      // 23.4.3.4 WeakSet.prototype.has ( value )
      define(
        WeakSet.prototype, 'has',
        function has(key) {
          var S = strict(this);
          if (Type(S) !== 'object') throw TypeError();
          if (S['[[WeakSetData]]'] === undefined) throw TypeError();
          if (Type(key) !== 'object') throw TypeError('Expected object');
          return S['[[WeakSetData]]'].has(key);
        });
  
      // 23.4.3.5 WeakSet.prototype [ @@toStringTag ]
      define(global.WeakSet.prototype, $$toStringTag, 'WeakSet');
  
      // 23.4.4 Properties of WeakSet Instances
  
      // Polyfills for incomplete native implementations:
      (function() {
        var ws = new global.WeakSet();
        var orig = global.WeakSet.prototype.add;
        define(global.WeakSet.prototype, 'add', function add() {
          orig.apply(this, arguments);
          return this;
        }, ws.add({}) !== ws);
      }());
    }());
  
    // ---------------------------------------
    // 24 Structured Data
    // ---------------------------------------
  
    // ---------------------------------------
    // 24.1 ArrayBuffer Objects
    // ---------------------------------------
  
    // See typedarray.js for TypedArray polyfill
  
    (function() {
      if (!('ArrayBuffer' in global))
        return;
  
      // 24.1.1 Abstract Operations For ArrayBuffer Objects
      // 24.1.1.1 AllocateArrayBuffer( constructor, byteLength )
      // 24.1.1.2 IsDetachedBuffer( arrayBuffer )
      // 24.1.1.3 DetachArrayBuffer( arrayBuffer )
      // 24.1.1.4 CloneArrayBuffer( srcBuffer, srcByteOffset [, cloneConstructor] )
      // 24.1.1.5 GetValueFromBuffer ( arrayBuffer, byteIndex, type, isLittleEndian )
      // 24.1.1.6 SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isLittleEndian )
      // 24.1.2 The ArrayBuffer Constructor
      // 24.1.2.1 ArrayBuffer( length )
      // 24.1.3 Properties of the ArrayBuffer Constructor
  
      // 24.1.3.1 ArrayBuffer.isView ( arg )
      define(
        ArrayBuffer, 'isView',
        function isView(arg) {
          if (Type(arg) !== 'object') return false;
          if ('buffer' in arg && arg.buffer instanceof ArrayBuffer) return true;
          return false;
        });
  
      // 24.1.3.2 ArrayBuffer.prototype
      // 24.1.3.3 get ArrayBuffer [ @@species ]
      // 24.1.4 Properties of the ArrayBuffer Prototype Object
      // 24.1.4.1 get ArrayBuffer.prototype.byteLength
      // 24.1.4.2 ArrayBuffer.prototype.constructor
      // 24.1.4.3 ArrayBuffer.prototype.slice ( start , end)
  
      // 24.1.4.4 ArrayBuffer.prototype [ @@toStringTag ]
      define(ArrayBuffer.prototype, $$toStringTag, 'ArrayBuffer');
  
      // 24.1.5 Properties of the ArrayBuffer Instances
    }());
  
    // ---------------------------------------
    // 24.2 DataView Objects
    // ---------------------------------------
  
    // See typedarray.js for TypedArray polyfill
  
    (function() {
      if (!('DataView' in global))
        return;
  
      // 24.2.1 Abstract Operations For DataView Objects
      // 24.2.1.1 GetViewValue(view, requestIndex, isLittleEndian, type)
      // 24.2.1.2 SetViewValue(view, requestIndex, isLittleEndian, type, value)
      // 24.2.2 The DataView Constructor
      // 24.2.2.1 DataView (buffer [ , byteOffset [ , byteLength ] ] )
      // 24.2.3 Properties of the DataView Constructor
      // 24.2.3.1 DataView.prototype
      // 24.2.4 Properties of the DataView Prototype Object
      // 24.2.4.1 get DataView.prototype.buffer
      // 24.2.4.2 get DataView.prototype.byteLength
      // 24.2.4.3 get DataView.prototype.byteOffset
      // 24.2.4.4 DataView.prototype.constructor
      // 24.2.4.5 DataView.prototype.getFloat32 ( byteOffset [ , littleEndian ] )
      // 24.2.4.6 DataView.prototype.getFloat64 ( byteOffset [ , littleEndian ] )
      // 24.2.4.7 DataView.prototype.getInt8 ( byteOffset )
      // 24.2.4.8 DataView.prototype.getInt16 ( byteOffset [ , littleEndian ] )
      // 24.2.4.9 DataView.prototype.getInt32 ( byteOffset [ , littleEndian ] )
      // 24.2.4.10 DataView.prototype.getUint8 ( byteOffset )
      // 24.2.4.11 DataView.prototype.getUint16 ( byteOffset [ , littleEndian ] )
      // 24.2.4.12 DataView.prototype.getUint32 ( byteOffset [ , littleEndian ] )
      // 24.2.4.13 DataView.prototype.setFloat32 ( byteOffset, value [ , littleEndian ] )
      // 24.2.4.14 DataView.prototype.setFloat64 ( byteOffset, value [ , littleEndian ] )
      // 24.2.4.15 DataView.prototype.setInt8 ( byteOffset, value )
      // 24.2.4.16 DataView.prototype.setInt16 ( byteOffset, value [ , littleEndian ] )
      // 24.2.4.17 DataView.prototype.setInt32 ( byteOffset, value [ , littleEndian ] )
      // 24.2.4.18 DataView.prototype.setUint8 ( byteOffset, value )
      // 24.2.4.19 DataView.prototype.setUint16 ( byteOffset, value [ , littleEndian ] )
      // 24.2.4.20 DataView.prototype.setUint32 ( byteOffset, value [ , littleEndian ] )
  
      // 24.2.4.21 DataView.prototype[ @@toStringTag ]
      define(DataView.prototype, $$toStringTag, 'DataView');
  
      // 24.2.5 Properties of DataView Instances
    }());
  
    // ---------------------------------------
    // 24.3 The JSON Object
    // ---------------------------------------
  
    // 24.3.1 JSON.parse ( text [ , reviver ] )
    // 24.3.2 JSON.stringify ( value [ , replacer [ , space ] ] )
    // 24.3.3 JSON [ @@toStringTag ]
    define(JSON, $$toStringTag, 'JSON');
  
    // ---------------------------------------
    // 25.1 Iteration
    // ---------------------------------------
  
    // 25.1.1 Common Iteration Interfaces
    // 25.1.1.1 The Iterable Interface
    // 25.1.1.2 The Iterator Interface
    // 25.1.1.3 The IteratorResult Interface
  
    // 25.1.2 The %IteratorPrototype% Object
    // Defined earlier, so other prototypes can reference it.
    // 25.1.2.1 %IteratorPrototype% [ @@iterator ] ( )
    define($IteratorPrototype$, $$iterator, function() {
      return this;
    });
  
  
    // ---------------------------------------
    // 25.4 Promise Objects
    // ---------------------------------------
  
    (function() {
      // 25.4 Promise Objects
  
      // 25.4.1 Promise Abstract Operations
  
      // 25.4.1.1 PromiseCapability Records
      // 25.4.1.1.1 IfAbruptRejectPromise ( value, capability )
  
      function IfAbruptRejectPromise(value, capability) {
        var rejectResult = capability['[[Reject]]'].call(undefined, value);
        return capability['[[Promise]]'];
      }
  
      // 25.4.1.2 PromiseReaction Records
  
      // 25.4.1.3 CreateResolvingFunctions ( promise )
  
      function CreateResolvingFunctions(promise) {
        var alreadyResolved = {'[[value]]': false};
        var resolve = PromiseResolveFunction();
        set_internal(resolve, '[[Promise]]',  promise);
        set_internal(resolve, '[[AlreadyResolved]]', alreadyResolved);
        var reject = PromiseRejectFunction();
        set_internal(reject, '[[Promise]]', promise);
        set_internal(reject, '[[AlreadyResolved]]', alreadyResolved);
        return { '[[Resolve]]': resolve, '[[Reject]]': reject};
      }
  
      // 25.4.1.3.1 Promise Reject Functions
  
      function PromiseRejectFunction() {
        var F = function(reason) {
          console.assert(Type(F['[[Promise]]']) === 'object');
          var promise = F['[[Promise]]'];
          var alreadyResolved = F['[[AlreadyResolved]]'];
          if (alreadyResolved['[[value]]']) return undefined;
          set_internal(alreadyResolved, '[[value]]', true);
          return RejectPromise(promise, reason);
        };
        return F;
      }
  
      // 25.4.1.3.2 Promise Resolve Functions
  
      function PromiseResolveFunction() {
        var F = function(resolution) {
          console.assert(Type(F['[[Promise]]']) === 'object');
          var promise = F['[[Promise]]'];
          var alreadyResolved = F['[[AlreadyResolved]]'];
          if (alreadyResolved['[[value]]']) return undefined;
          set_internal(alreadyResolved, '[[value]]', true);
  
          if (SameValue(resolution, promise))  {
            var selfResolutionError = TypeError();
            return RejectPromise(promise, selfResolutionError);
          }
          if (Type(resolution) !== 'object')
            return FulfillPromise(promise, resolution);
          try {
            var then = resolution['then'];
          } catch(then) {
            return RejectPromise(promise, then);
          }
          if (!IsCallable(then))
            return FulfillPromise(promise, resolution);
          EnqueueJob('PromiseJobs', PromiseResolveThenableJob, [promise, resolution, then]);
          return undefined;
        };
        return F;
      }
  
      // 25.4.1.4 FulfillPromise ( promise, value )
  
      function FulfillPromise(promise, value) {
        console.assert(promise['[[PromiseState]]'] === 'pending');
        var reactions = promise['[[PromiseFulfillReactions]]'];
        set_internal(promise, '[[PromiseResult]]', value);
        set_internal(promise, '[[PromiseFulfillReactions]]', undefined);
        set_internal(promise, '[[PromiseRejectReactions]]', undefined);
        set_internal(promise, '[[PromiseState]]', 'fulfilled');
        return TriggerPromiseReactions(reactions, value);
      }
  
      // 25.4.1.5 NewPromiseCapability ( C )
  
      function NewPromiseCapability(c) {
        // To keep Promise hermetic, this doesn't look much like the spec.
        return CreatePromiseCapabilityRecord(undefined, c);
      }
  
      // 25.4.1.5.1 CreatePromiseCapabilityRecord ( promise, constructor )
  
      function CreatePromiseCapabilityRecord(promise, constructor) {
        // To keep Promise hermetic, this doesn't look much like the spec.
        console.assert(IsConstructor(constructor));
        var promiseCapability = {};
        set_internal(promiseCapability, '[[Promise]]', promise);
        set_internal(promiseCapability, '[[Resolve]]', undefined);
        set_internal(promiseCapability, '[[Reject]]', undefined);
        var executor = GetCapabilitiesExecutor();
        set_internal(executor, '[[Capability]]', promiseCapability);
  
        // NOTE: Differs from spec; object is constructed here
        var constructorResult = promise = new constructor(executor);
        set_internal(promiseCapability, '[[Promise]]', promise);
  
        if (!IsCallable(promiseCapability['[[Resolve]]'])) throw TypeError();
        if (!IsCallable(promiseCapability['[[Reject]]'])) throw TypeError();
        if (Type(constructorResult) === 'object' && !SameValue(promise, constructorResult)) throw TypeError();
        return promiseCapability;
      }
  
      // 25.4.1.5.2 GetCapabilitiesExecutor Functions
  
      function GetCapabilitiesExecutor() {
        var F = function(resolve, reject) {
          console.assert(F['[[Capability]]']);
          var promiseCapability = F['[[Capability]]'];
          if (promiseCapability['[[Resolve]]'] !== undefined) throw TypeError();
          if (promiseCapability['[[Reject]]'] !== undefined) throw TypeError();
          set_internal(promiseCapability, '[[Resolve]]', resolve);
          set_internal(promiseCapability, '[[Reject]]', reject);
          return undefined;
        };
        return F;
      }
  
      // 25.4.1.6 IsPromise ( x )
  
      function IsPromise(x) {
        if (Type(x) !== 'object') return false;
        if (!('[[PromiseState]]' in x)) return false;
        if (x['[[PromiseState]]'] === undefined) return false;
        return true;
      }
  
      // 25.4.1.7 RejectPromise ( promise, reason )
  
      function RejectPromise(promise, reason) {
        console.assert(promise['[[PromiseState]]'] === 'pending');
        var reactions = promise['[[PromiseRejectReactions]]'];
        set_internal(promise, '[[PromiseResult]]', reason);
        set_internal(promise, '[[PromiseFulfillReactions]]', undefined);
        set_internal(promise, '[[PromiseRejectReactions]]', undefined);
        set_internal(promise, '[[PromiseState]]', 'rejected');
        return TriggerPromiseReactions(reactions, reason);
      }
  
      // 25.4.1.8 TriggerPromiseReactions ( reactions, argument )
  
      function TriggerPromiseReactions(reactions, argument) {
        for (var i = 0, len = reactions.length; i < len; ++i)
          EnqueueJob('PromiseJobs', PromiseReactionJob, [reactions[i], argument]);
        return undefined;
      }
  
      // 25.4.2 Promise Jobs
  
      // 25.4.2.1 PromiseReactionJob ( reaction, argument )
  
      function PromiseReactionJob(reaction, argument) {
        var promiseCapability = reaction['[[Capabilities]]'];
        var handler = reaction['[[Handler]]'];
        var handlerResult, status;
        try {
          if (handler === 'Identity') handlerResult = argument;
          else if (handler === 'Thrower') throw argument;
          else handlerResult = handler.call(undefined, argument);
        } catch (handlerResult) {
          status = promiseCapability['[[Reject]]'].call(undefined, handlerResult);
          NextJob(status); return;
        }
        status = promiseCapability['[[Resolve]]'].call(undefined, handlerResult);
        NextJob(status);
      }
  
      // 25.4.2.2 PromiseResolveThenableJob ( promiseToResolve, thenable, then)
  
      function PromiseResolveThenableJob(promiseToResolve, thenable, then) {
        // SPEC BUG: promise vs. promiseToResolve
        var resolvingFunctions = CreateResolvingFunctions(promiseToResolve);
        try {
          var thenCallResult = then.call(thenable, resolvingFunctions['[[Resolve]]'],
                                         resolvingFunctions['[[Reject]]']);
        } catch (thenCallResult) {
          var status = resolvingFunctions['[[Reject]]'].call(undefined, thenCallResult);
          NextJob(status); return;
        }
        NextJob(thenCallResult);
      }
  
      // 25.4.3 The Promise Constructor
  
      // 25.4.3.1 Promise ( executor )
  
      function Promise(executor) {
        var config = { configurable: false, enumerable: false, writable: true, value: undefined };
        Object.defineProperty(this, '[[PromiseState]]', config);
        Object.defineProperty(this, '[[PromiseConstructor]]', config);
        Object.defineProperty(this, '[[PromiseResult]]', config);
        Object.defineProperty(this, '[[PromiseFulfillReactions]]', config);
        Object.defineProperty(this, '[[PromiseRejectReactions]]', config);
  
        var promise = this;
        if (Type(promise) !== 'object') throw new TypeError();
        if (!('[[PromiseState]]' in promise)) throw TypeError();
        if (promise['[[PromiseState]]'] !== undefined) throw TypeError();
        if (!IsCallable(executor)) throw TypeError();
  
        set_internal(promise, '[[PromiseConstructor]]', Promise);
  
        return InitializePromise(promise, executor);
      }
  
      // 25.4.3.1.1 InitializePromise ( promise, executor )
  
      function InitializePromise(promise, executor) {
        console.assert('[[PromiseState]]' in promise);
        console.assert(IsCallable(executor));
        set_internal(promise, '[[PromiseState]]', 'pending');
        set_internal(promise, '[[PromiseFulfillReactions]]', []);
        set_internal(promise, '[[PromiseRejectReactions]]', []);
        var resolvingFunctions = CreateResolvingFunctions(promise);
        try {
          var completion = executor.call(undefined, resolvingFunctions['[[Resolve]]'],
                                         resolvingFunctions['[[Reject]]']);
        } catch (completion) {
          var status = resolvingFunctions['[[Reject]]'].call(undefined, completion);
        }
        return promise;
      }
  
      // 25.4.4 Properties of the Promise Constructor
      // 25.4.4.1 Promise.all ( iterable )
  
      define(Promise, 'all', function all(iterable) {
        var c = strict(this);
        var promiseCapability = NewPromiseCapability(c);
        try {
          var iterator = GetIterator(iterable);
        } catch (value) {
          promiseCapability['[[Reject]]'].call(undefined, value);
          return promiseCapability['[[Promise]]'];
        }
        var values = [];
        var remainingElementsCount = { value: 1 };
        var index = 0;
        while (true) {
          try {
            var next = IteratorStep(iterator);
          } catch (value) {
            promiseCapability['[[Reject]]'].call(undefined, value);
            return promiseCapability['[[Promise]]'];
          }
          if (!next) {
            remainingElementsCount.value -= 1;
            if (remainingElementsCount.value === 0) {
              var resolveResult = promiseCapability['[[Resolve]]'].apply(undefined, values);
  
  
            }
            return promiseCapability['[[Promise]]'];
          }
          try {
            var nextValue = IteratorValue(next);
          } catch (value) {
            promiseCapability['[[Reject]]'].call(undefined, value);
            return promiseCapability['[[Promise]]'];
          }
          try {
            var nextPromise = c.resolve(nextValue);
          } catch (value) {
            promiseCapability['[[Reject]]'].call(undefined, value);
            return promiseCapability['[[Promise]]'];
          }
          var resolveElement = PromiseAllResolveElementFunction();
          set_internal(resolveElement, '[[AlreadyCalled]]', { value: false });
          set_internal(resolveElement, '[[Index]]', index);
          set_internal(resolveElement, '[[Values]]', values);
          set_internal(resolveElement, '[[Capabilities]]', promiseCapability);
          set_internal(resolveElement, '[[RemainingElements]]', remainingElementsCount);
          remainingElementsCount.value += 1;
          try {
            var result = nextPromise.then(resolveElement, promiseCapability['[[Reject]]']);
          } catch (value) {
            promiseCapability['[[Reject]]'].call(undefined, value);
            return promiseCapability['[[Promise]]'];
          }
          index += 1;
        }
      });
  
      // 25.4.4.1.1 Promise.all Resolve Element Functions
  
      function PromiseAllResolveElementFunction() {
        var F = function(x) {
          var alreadyCalled = F['[[AlreadyCalled]]'];
          if (alreadyCalled.value) return undefined;
          alreadyCalled.value = true;
          var index = F['[[Index]]'];
          var values = F['[[Values]]'];
          var promiseCapability = F['[[Capabilities]]'];
          var remainingElementsCount = F['[[RemainingElements]]'];
          try {
            values[index] = x;
          } catch (result) {
            promiseCapability['[[Reject]]'].call(undefined, result);
            return promiseCapability['[[Promise]]'];
          }
          remainingElementsCount.value -= 1;
          if (remainingElementsCount.value === 0)
            return promiseCapability['[[Resolve]]'].call(undefined, values);
          return undefined;
        };
        return F;
      }
  
      // 25.4.4.2 Promise.prototype
  
      Promise.prototype = {};
  
      // 25.4.4.3 Promise.race ( iterable )
  
      define(Promise, 'race', function race(iterable) {
        var c = strict(this);
        var promiseCapability = NewPromiseCapability(c);
        try {
          var iterator = GetIterator(iterable);
        } catch (value) {
          promiseCapability['[[Reject]]'].call(undefined, value);
          return promiseCapability['[[Promise]]'];
        }
        while (true) {
          try {
            var next = IteratorStep(iterator);
          } catch (value) {
            promiseCapability['[[Reject]]'].call(undefined, value);
            return promiseCapability['[[Promise]]'];
          }
          if (!next) return promiseCapability['[[Promise]]'];
          try {
            var nextValue = IteratorValue(next);
          } catch (value) {
            promiseCapability['[[Reject]]'].call(undefined, value);
            return promiseCapability['[[Promise]]'];
          }
          try {
            var nextPromise = c.resolve(nextValue);
          } catch (value) {
            promiseCapability['[[Reject]]'].call(undefined, value);
            return promiseCapability['[[Promise]]'];
          }
          try {
            nextPromise.then(promiseCapability['[[Resolve]]'], promiseCapability['[[Reject]]']);
          } catch (value) {
            promiseCapability['[[Reject]]'].call(undefined, value);
            return promiseCapability['[[Promise]]'];
          }
        }
      });
  
      // 25.4.4.4 Promise.reject ( r )
  
      define(Promise, 'reject', function reject(r) {
        var c = strict(this);
        var promiseCapability = NewPromiseCapability(c);
        var rejectResult = promiseCapability['[[Reject]]'].call(undefined, r);
        return promiseCapability['[[Promise]]'];
      });
  
      // 25.4.4.5 Promise.resolve ( x )
  
      define(Promise, 'resolve', function resolve(x) {
        var c = strict(this);
        if (IsPromise(x)) {
          var constructor = x['[[PromiseConstructor]]'];
          if (SameValue(constructor, c)) return x;
        }
        var promiseCapability = NewPromiseCapability(c);
        var resolveResult = promiseCapability['[[Resolve]]'].call(undefined, x);
        return promiseCapability['[[Promise]]'];
      });
  
      // 25.4.4.6 Promise [ @@create ] ( )
      // 25.4.4.6.1 AllocatePromise ( constructor )
      // 25.4.5 Properties of the Promise Prototype Object
      // 25.4.5.1 Promise.prototype.catch ( onRejected )
  
      define(Promise.prototype, 'catch', function catch_(onRejected) {
        var promise = this;
        return promise.then(undefined, onRejected);
      });
  
      // 25.4.5.2 Promise.prototype.constructor
  
      Promise.prototype.constructor = Promise;
  
      // 25.4.5.3 Promise.prototype.then ( onFulfilled , onRejected )
  
      define(Promise.prototype, 'then', function then(onFulfilled, onRejected) {
        var promise = this;
        if (!IsPromise(promise)) throw TypeError();
        if (!IsCallable(onFulfilled)) onFulfilled = 'Identity';
        if (!IsCallable(onRejected)) onRejected = 'Thrower';
        var c = promise.constructor;
        var promiseCapability = NewPromiseCapability(c);
        var fulfillReaction = { '[[Capabilities]]': promiseCapability,
                                '[[Handler]]': onFulfilled };
        var rejectReaction = { '[[Capabilities]]': promiseCapability,
                               '[[Handler]]': onRejected };
        if (promise['[[PromiseState]]'] === 'pending') {
          promise['[[PromiseFulfillReactions]]'].push(fulfillReaction);
          promise['[[PromiseRejectReactions]]'].push(rejectReaction);
        } else if (promise['[[PromiseState]]'] === 'fulfilled') {
          var value = promise['[[PromiseResult]]'];
          EnqueueJob('PromiseJobs', PromiseReactionJob, [fulfillReaction, value]);
        } else if (promise['[[PromiseState]]'] === 'rejected') {
          var reason = promise['[[PromiseResult]]'];
          EnqueueJob('PromiseJobs', PromiseReactionJob, [rejectReaction, reason]);
        }
        return promiseCapability['[[Promise]]'];
      });
  
      // 25.4.6 Properties of Promise Instances
  
      if (!('Promise' in global) || OVERRIDE_NATIVE_FOR_TESTING)
        global.Promise = Promise;
  
      // Patch early Promise.cast vs. Promise.resolve implementations
      if ('cast' in global.Promise) global.Promise.resolve = global.Promise.cast;
    }());
  
    // 25.4.5.1 Promise.prototype [ @@toStringTag ]
    define(Promise.prototype, $$toStringTag, 'Promise');
  
    // ---------------------------------------
    // 26 Reflection
    // ---------------------------------------
  
    (function() {
      // 26.1 The Reflect Object
      if (!('Reflect' in global) || OVERRIDE_NATIVE_FOR_TESTING)
        global.Reflect = {};
  
      // 26.1.1 Reflect.apply ( target, thisArgument, argumentsList )
      define(
        Reflect, 'apply',
        function apply(target, thisArgument, argumentsList) {
          if (!IsCallable(target)) throw TypeError();
          return Function.prototype.apply.call(target, thisArgument, argumentsList);
        });
  
      // 26.1.2 Reflect.construct ( target, argumentsList [, newTarget] )
      define(
        Reflect, 'construct',
        function construct(target, argumentsList) {
          return __cons(target, argumentsList);
        });
  
      // 26.1.3 Reflect.defineProperty ( target, propertyKey, attributes )
      define(
        Reflect, 'defineProperty',
        function defineProperty(target, propertyKey, attributes) {
          try {
            Object.defineProperty(target, propertyKey, attributes);
            return true;
          } catch (_) {
            return false;
          }
        });
  
      // 26.1.4 Reflect.deleteProperty ( target, propertyKey )
      define(
        Reflect, 'deleteProperty',
        function deleteProperty(target,name) {
          try {
            delete target[name];
            return !HasOwnProperty(target, name);
          } catch (_) {
            return false;
          }
        });
  
      // 26.1.5 Reflect.enumerate ( target )
      define(
        Reflect, 'enumerate',
        function enumerate(target) {
          target = ToObject(target);
          var iterator = Enumerate(target);
          return iterator;
        });
  
      // 26.1.6 Reflect.get ( target, propertyKey [ , receiver ])
      define(
        Reflect, 'get',
        function get(target, name, receiver) {
          target = ToObject(target);
          name = String(name);
          receiver = (receiver === undefined) ? target : ToObject(receiver);
          var desc = getPropertyDescriptor(target, name);
          if (desc && 'get' in desc)
            return Function.prototype.call.call(desc['get'], receiver);
          return target[name];
        });
  
      // 26.1.7 Reflect.getOwnPropertyDescriptor ( target, propertyKey )
      define(
        Reflect, 'getOwnPropertyDescriptor',
        Object.getOwnPropertyDescriptor);
  
      // 26.1.8 Reflect.getPrototypeOf ( target )
      define(
        Reflect, 'getPrototypeOf',
        Object.getPrototypeOf);
  
      // 26.1.9 Reflect.has ( target, propertyKey )
      define(
        Reflect, 'has',
        function has(target,name) {
          return String(name) in ToObject(target);
        });
  
      // 26.1.10 Reflect.isExtensible (target)
      define(
        Reflect, 'isExtensible',
        Object.isExtensible);
  
      // 26.1.11 Reflect.ownKeys ( target )
      define(
        Reflect, 'ownKeys',
        function ownKeys(target) {
          var obj = ToObject(target);
          return Object.getOwnPropertyNames(obj);
        });
  
      // 26.1.12 Reflect.preventExtensions ( target )
      define(
        Reflect, 'preventExtensions',
        function preventExtensions(target) {
          try { Object.preventExtensions(target); return true; } catch (_) { return false; }
        });
  
      // 26.1.13 Reflect.set ( target, propertyKey, V [ , receiver ] )
      define(
        Reflect, 'set',
        function set(target, name, value, receiver) {
          target = ToObject(target);
          name = String(name);
          receiver = (receiver === undefined) ? target : ToObject(receiver);
          var desc = getPropertyDescriptor(target, name);
          try {
            if (desc && 'set' in desc)
              Function.prototype.call.call(desc['set'], receiver, value);
            else
              target[name] = value;
            return true;
          } catch (_) {
            return false;
          }
        });
  
      // 26.1.14 Reflect.setPrototypeOf ( target, proto )
      define(
        Reflect, 'setPrototypeOf',
        function setPrototypeOf(target, proto) {
          try {
            target.__proto__ = proto;
            return Reflect.getPrototypeOf(target) === proto;
          } catch(_) {
            return false;
          }
        });
  
    }());
  
    // ---------------------------------------
    // 26.2 Proxy Objects
    // ---------------------------------------
  
    // Not polyfillable.
  
  }(self));
  
  // This helper is defined outside the main scope so that the use of
  // 'eval' does not taint the scope for minifiers.
  function __cons(t, a) {
    return eval('new t(' + a.map(function(_, i) { return 'a[' + i + ']'; }).join(',') + ')');
  }
  //----------------------------------------------------------------------
  //
  // ECMAScript 2016 Polyfills
  //
  //----------------------------------------------------------------------
  
  (function (global) {
    "use strict";
    var undefined = (void 0); // Paranoia
  
    // Helpers
  
    function isSymbol(s) {
      return (typeof s === 'symbol') || ('Symbol' in global && s instanceof global.Symbol);
    }
  
    function define(o, p, v, override) {
      if (p in o && !override)
        return;
  
      if (typeof v === 'function') {
        // Sanity check that functions are appropriately named (where possible)
        console.assert(isSymbol(p) || !('name' in v) || v.name === p || v.name === p + '_', 'Expected function name "' + p.toString() + '", was "' + v.name + '"');
        Object.defineProperty(o, p, {
          value: v,
          configurable: true,
          enumerable: false,
          writable: true
        });
      } else {
        Object.defineProperty(o, p, {
          value: v,
          configurable: false,
          enumerable: false,
          writable: false
        });
      }
    }
  
  
    // Snapshot intrinsic functions
    var $isNaN = global.isNaN;
  
    var abs = Math.abs,
        floor = Math.floor,
        max = Math.max,
        min = Math.min;
  
    //----------------------------------------
    // 7 Abstract Operations
    //----------------------------------------
  
    // 7.1.4
    function ToInteger(n) {
      n = Number(n);
      if ($isNaN(n)) return 0;
      if (n === 0 || n === Infinity || n === -Infinity) return n;
      return ((n < 0) ? -1 : 1) * floor(abs(n));
    }
  
    // 7.1.13 ToObject
    function ToObject(v) {
      if (v === null || v === undefined) throw TypeError();
      return Object(v);
    }
  
    // 7.1.15 ToLength ( argument )
    function ToLength(v) {
      var len = ToInteger(v);
      if (len <= 0) {
        return 0;
      }
      return min(len, 0x20000000000000 - 1); // 2^53-1
    }
  
    //----------------------------------------
    // 7.2 Testing and Comparison Operations
    //----------------------------------------
  
    // 7.2.10 SameValueZero(x, y)
    function SameValueZero(x, y) {
      if (typeof x !== typeof y) return false;
      switch (typeof x) {
      case 'undefined':
        return true;
      case 'number':
        if (x !== x && y !== y) return true;
        return x === y;
      case 'boolean':
      case 'string':
      case 'object':
      default:
        return x === y;
      }
    }
  
    //----------------------------------------------------------------------
    //
    // ECMAScript 2016
    //
    //----------------------------------------------------------------------
  
    // https://github.com/tc39/Array.prototype.includes/
    define(
      Array.prototype, 'includes',
      function includes(target) {
        var fromIndex = arguments[1];
  
        var o = ToObject(this);
        var len = ToLength(o["length"]);
        if (len === 0) return false;
        var n = ToInteger(fromIndex);
        if (n >= 0) {
          var k = n;
        } else {
          k = len + n;
          if (k < 0) k = 0;
        }
        while (k < len) {
          var elementK = o[k];
          if (SameValueZero(o[k], target))
            return true;
          k += 1;
        }
        return false;
      });
  
  }(this));
  //----------------------------------------------------------------------
  //
  // ECMAScript 2017 Polyfills
  //
  //----------------------------------------------------------------------
  
  (function (global) {
    'use strict';
    var undefined = (void 0); // Paranoia
  
    // Helpers
  
    function isSymbol(s) {
      return (typeof s === 'symbol') || ('Symbol' in global && s instanceof global.Symbol);
    }
  
    function define(o, p, v, override) {
      if (p in o && !override)
        return;
  
      if (typeof v === 'function') {
        // Sanity check that functions are appropriately named (where possible)
        console.assert(isSymbol(p) || !('name' in v) || v.name === p || v.name === p + '_', 'Expected function name "' + p.toString() + '", was "' + v.name + '"');
        Object.defineProperty(o, p, {
          value: v,
          configurable: true,
          enumerable: false,
          writable: true
        });
      } else {
        Object.defineProperty(o, p, {
          value: v,
          configurable: false,
          enumerable: false,
          writable: false
        });
      }
    }
  
    // Snapshot intrinsic functions
    var $isNaN = global.isNaN;
  
    var abs = Math.abs,
        floor = Math.floor,
        min = Math.min;
  
    //----------------------------------------
    // 7 Abstract Operations
    //----------------------------------------
  
    // 7.1.4
    function ToInteger(n) {
      n = Number(n);
      if ($isNaN(n)) return 0;
      if (n === 0 || n === Infinity || n === -Infinity) return n;
      return ((n < 0) ? -1 : 1) * floor(abs(n));
    }
  
    // 7.1.13 ToObject
    function ToObject(v) {
      if (v === null || v === undefined) throw TypeError();
      return Object(v);
    }
  
    // 7.1.15 ToLength ( argument )
    function ToLength(v) {
      var len = ToInteger(v);
      if (len <= 0) {
        return 0;
      }
      return min(len, 0x20000000000000 - 1); // 2^53-1
    }
  
    //----------------------------------------
    // 7.3 Operations on Objects
    //----------------------------------------
  
    // 7.3.4
    function CreateDataProperty(O, P, V) {
      Object.defineProperty(O, P, {
        value: V,
        writable: true,
        enumerable: true,
        configurable: true
      });
    }
  
    // 7.3.21
    function EnumerableOwnProperties(o, kind) {
      var ownKeys = Object.keys(o);
      var properties = [];
      ownKeys.forEach(function(key) {
        var desc = Object.getOwnPropertyDescriptor(o, key);
        if (desc && desc.enumerable) {
          if (kind === 'key') properties.push(key);
          else {
            var value = o[key];
            if (kind === 'value') properties.push(value);
            else properties.push([key, value]);
          }
        }
      });
      return properties;
    }
  
  
    //----------------------------------------------------------------------
    // 19 Fundamental Objects
    //----------------------------------------------------------------------
  
    // 19.1 Object Objects
    // 19.1.2 Properties of the Object Constructor
  
    // 19.1.2.5 Object.entries
    define(
      Object, 'entries',
      function entries(o) {
        var obj = ToObject(o);
        return EnumerableOwnProperties(obj, 'key+value');
      });
  
    // 19.1.2.8 Object.getOwnPropertyDescriptors ( O )
    define(
      Object, 'getOwnPropertyDescriptors',
      function getOwnPropertyDescriptors(o) {
        var obj = ToObject(o);
        // ReturnIfAbrupt(obj)
        var keys = Object.getOwnPropertyNames(obj);
        // ReturnIfAbrupt(keys)
        var descriptors = {};
        for (var i = 0; i < keys.length; ++i) {
          var nextKey = keys[i];
          var descriptor = Object.getOwnPropertyDescriptor(obj, nextKey);
          // ReturnIfAbrupt(desc)
          // ReturnIfAbrupt(descriptor)
          CreateDataProperty(descriptors, nextKey, descriptor);
        }
        return descriptors;
      });
  
    // 19.1.2.21 Object.values
    define(
      Object, 'values',
      function values(o) {
        var obj = ToObject(o);
        return EnumerableOwnProperties(obj, 'value');
      });
  
  
  
    //----------------------------------------------------------------------
    // 21 Text Processing
    //----------------------------------------------------------------------
  
    // 21.1 String Objects
    // 21.1.3 Properties of the String Prototype Object
  
    // 21.1.3.13 String.prototype.padEnd( maxLength [ , fillString ] )
    define(
      String.prototype, 'padEnd',
      function padEnd(maxLength) {
        var fillString = arguments[1];
  
        var o = this;
        // ReturnIfAbrupt(o)
        var s = String(this);
        // ReturnIfAbrupt(s)
        var stringLength = s.length;
        if (fillString === undefined) var fillStr = '';
        else fillStr = String(fillString);
        // ReturnIfAbrupt(fillStr)
        if (fillStr === '') fillStr = ' ';
        var intMaxLength = ToLength(maxLength);
        // ReturnIfAbrupt(intMaxLength)
        if (intMaxLength <= stringLength) return s;
        var fillLen = intMaxLength - stringLength;
        var stringFiller = '';
        while (stringFiller.length < fillLen)
          stringFiller = stringFiller + fillStr;
        return s + stringFiller.substring(0, fillLen);
      });
  
    // 21.1.3.14 String.prototype.padStart( maxLength [ , fillString ] )
    define(
      String.prototype, 'padStart',
      function padStart(maxLength) {
        var fillString = arguments[1];
  
        var o = this;
        // ReturnIfAbrupt(o)
        var s = String(this);
        // ReturnIfAbrupt(s)
        var stringLength = s.length;
        if (fillString === undefined) var fillStr = '';
        else fillStr = String(fillString);
        // ReturnIfAbrupt(fillStr)
        if (fillStr === '') fillStr = ' ';
        var intMaxLength = ToLength(maxLength);
        // ReturnIfAbrupt(intMaxLength)
        if (intMaxLength <= stringLength) return s;
        var fillLen = intMaxLength - stringLength;
        var stringFiller = '';
        while (stringFiller.length < fillLen)
          stringFiller = stringFiller + fillStr;
        return stringFiller.substring(0, fillLen) + s;
      });
  
  }(this));
  (function(global) {
    'use strict';
  
    if (!('window' in global && 'document' in global))
      return;
  
    //----------------------------------------------------------------------
    //
    // HTML
    // https://html.spec.whatwg.org
    //
    //----------------------------------------------------------------------
  
    // document.head attribute
    // Needed for: IE8-
    if (!('head' in document))
      document.head = document.getElementsByTagName('head')[0];
  
    // Ensure correct parsing of newish elements ("shiv")
    // Needed for: IE8-
    [
      'abbr', 'article', 'aside', 'audio', 'bdi', 'canvas', 'data', 'datalist',
      'details', 'dialog', 'figcaption', 'figure', 'footer', 'header', 'hgroup',
      'main', 'mark', 'meter', 'nav', 'output', 'picture', 'progress', 'section',
      'summary', 'template', 'time', 'video'].forEach(function(tag) {
       document.createElement(tag);
     });
  
    // HTMLElement.dataset
    // Needed for: IE10-
    if (!('dataset' in document.createElement('span')) &&
        'Element' in global && Element.prototype && Object.defineProperty) {
      Object.defineProperty(Element.prototype, 'dataset', { get: function() {
        var result = Object.create(null);
        for (var i = 0; i < this.attributes.length; ++i) {
          var attr = this.attributes[i];
          if (attr.specified && attr.name.substring(0, 5) === 'data-') {
            (function(element, name) {
              var prop = name.replace(/-([a-z])/g, function(m, p) {
                return p.toUpperCase();
              });
              result[prop] = element.getAttribute('data-' + name); // Read-only, for IE8-
              Object.defineProperty(result, prop, {
                get: function() {
                  return element.getAttribute('data-' + name);
                },
                set: function(value) {
                  element.setAttribute('data-' + name, value);
                }});
            }(this, attr.name.substring(5)));
          }
        }
        return result;
      }});
    }
  
    // Base64 utility methods
    // Needed for: IE9-
    (function() {
      if ('atob' in global && 'btoa' in global)
        return;
  
      var B64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
      function atob(input) {
        input = String(input);
        var position = 0,
            output = [],
            buffer = 0, bits = 0, n;
  
        input = input.replace(/\s/g, '');
        if ((input.length % 4) === 0) { input = input.replace(/=+$/, ''); }
        if ((input.length % 4) === 1) { throw Error("InvalidCharacterError"); }
        if (/[^+/0-9A-Za-z]/.test(input)) { throw Error("InvalidCharacterError"); }
  
        while (position < input.length) {
          n = B64_ALPHABET.indexOf(input.charAt(position));
          buffer = (buffer << 6) | n;
          bits += 6;
  
          if (bits === 24) {
            output.push(String.fromCharCode((buffer >> 16) & 0xFF));
            output.push(String.fromCharCode((buffer >>  8) & 0xFF));
            output.push(String.fromCharCode(buffer & 0xFF));
            bits = 0;
            buffer = 0;
          }
          position += 1;
        }
  
        if (bits === 12) {
          buffer = buffer >> 4;
          output.push(String.fromCharCode(buffer & 0xFF));
        } else if (bits === 18) {
          buffer = buffer >> 2;
          output.push(String.fromCharCode((buffer >> 8) & 0xFF));
          output.push(String.fromCharCode(buffer & 0xFF));
        }
  
        return output.join('');
      };
  
      function btoa(input) {
        input = String(input);
        var position = 0,
            out = [],
            o1, o2, o3,
            e1, e2, e3, e4;
  
        if (/[^\x00-\xFF]/.test(input)) { throw Error("InvalidCharacterError"); }
  
        while (position < input.length) {
          o1 = input.charCodeAt(position++);
          o2 = input.charCodeAt(position++);
          o3 = input.charCodeAt(position++);
  
          // 111111 112222 222233 333333
          e1 = o1 >> 2;
          e2 = ((o1 & 0x3) << 4) | (o2 >> 4);
          e3 = ((o2 & 0xf) << 2) | (o3 >> 6);
          e4 = o3 & 0x3f;
  
          if (position === input.length + 2) {
            e3 = 64;
            e4 = 64;
          }
          else if (position === input.length + 1) {
            e4 = 64;
          }
  
          out.push(B64_ALPHABET.charAt(e1),
                   B64_ALPHABET.charAt(e2),
                   B64_ALPHABET.charAt(e3),
                   B64_ALPHABET.charAt(e4));
        }
  
        return out.join('');
      };
  
      global.atob = atob;
      global.btoa = btoa;
    }());
  
    // requestAnimationFrame - needed for IE9-
    (function() {
      if ('requestAnimationFrame' in global)
        return;
  
      var TARGET_FPS = 60,
          requests = Object.create(null),
          raf_handle = 0,
          timeout_handle = -1;
  
      function isVisible(element) {
        return element.offsetWidth > 0 && element.offsetHeight > 0;
      }
  
      function onFrameTimer() {
        var cur_requests = requests;
        requests = Object.create(null);
        timeout_handle = -1;
        Object.keys(cur_requests).forEach(function(id) {
          var request = cur_requests[id];
          if (!request.element || isVisible(request.element))
            request.callback(Date.now());
        });
      }
  
      function requestAnimationFrame(callback, element) {
        var cb_handle = ++raf_handle;
        requests[cb_handle] = {callback: callback, element: element};
        if (timeout_handle === -1)
          timeout_handle = global.setTimeout(onFrameTimer, 1000 / TARGET_FPS);
        return cb_handle;
      }
  
      function cancelAnimationFrame(handle) {
        delete requests[handle];
        if (Object.keys(requests).length === 0) {
          global.clearTimeout(timeout_handle);
          timeout_handle = -1;
        }
      }
  
      global.requestAnimationFrame = requestAnimationFrame;
      global.cancelAnimationFrame = cancelAnimationFrame;
    }());
  
  }(self));
  (function(global) {
    'use strict';
    if (!('window' in global && 'document' in global))
      return;
  
    //----------------------------------------------------------------------
    //
    // DOM
    // https://dom.spec.whatwg.org/
    //
    //----------------------------------------------------------------------
  
    // Document.querySelectorAll method
    // http://ajaxian.com/archives/creating-a-queryselector-for-ie-that-runs-at-native-speed
    // Needed for: IE7-
    if (!document.querySelectorAll) {
      document.querySelectorAll = function(selectors) {
        var style = document.createElement('style'), elements = [], element;
        document.documentElement.firstChild.appendChild(style);
        document._qsa = [];
  
        style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
        window.scrollBy(0, 0);
        style.parentNode.removeChild(style);
  
        while (document._qsa.length) {
          element = document._qsa.shift();
          element.style.removeAttribute('x-qsa');
          elements.push(element);
        }
        document._qsa = null;
        return elements;
      };
    }
  
    // Document.querySelector method
    // Needed for: IE7-
    if (!document.querySelector) {
      document.querySelector = function(selectors) {
        var elements = document.querySelectorAll(selectors);
        return (elements.length) ? elements[0] : null;
      };
    }
  
    // Document.getElementsByClassName method
    // Needed for: IE8-
    if (!document.getElementsByClassName) {
      document.getElementsByClassName = function(classNames) {
        classNames = String(classNames).replace(/^|\s+/g, '.');
        return document.querySelectorAll(classNames);
      };
    }
  
    // Node interface constants
    // Needed for: IE8-
    global.Node = global.Node || function() { throw TypeError("Illegal constructor"); };
    [
      ['ELEMENT_NODE', 1],
      ['ATTRIBUTE_NODE', 2],
      ['TEXT_NODE', 3],
      ['CDATA_SECTION_NODE', 4],
      ['ENTITY_REFERENCE_NODE', 5],
      ['ENTITY_NODE', 6],
      ['PROCESSING_INSTRUCTION_NODE', 7],
      ['COMMENT_NODE', 8],
      ['DOCUMENT_NODE', 9],
      ['DOCUMENT_TYPE_NODE', 10],
      ['DOCUMENT_FRAGMENT_NODE', 11],
      ['NOTATION_NODE', 12]
    ].forEach(function(p) { if (!(p[0] in global.Node)) global.Node[p[0]] = p[1]; });
  
    // DOMException constants
    // Needed for: IE8-
    global.DOMException = global.DOMException || function() { throw TypeError("Illegal constructor"); };
    [
      ['INDEX_SIZE_ERR', 1],
      ['DOMSTRING_SIZE_ERR', 2],
      ['HIERARCHY_REQUEST_ERR', 3],
      ['WRONG_DOCUMENT_ERR', 4],
      ['INVALID_CHARACTER_ERR', 5],
      ['NO_DATA_ALLOWED_ERR', 6],
      ['NO_MODIFICATION_ALLOWED_ERR', 7],
      ['NOT_FOUND_ERR', 8],
      ['NOT_SUPPORTED_ERR', 9],
      ['INUSE_ATTRIBUTE_ERR', 10],
      ['INVALID_STATE_ERR', 11],
      ['SYNTAX_ERR', 12],
      ['INVALID_MODIFICATION_ERR', 13],
      ['NAMESPACE_ERR', 14],
      ['INVALID_ACCESS_ERR', 15]
    ].forEach(function(p) { if (!(p[0] in global.DOMException)) global.DOMException[p[0]] = p[1]; });
  
    // Event and EventTargets interfaces
    // Needed for: IE8
    (function(){
      if (!('Element' in global) || Element.prototype.addEventListener || !Object.defineProperty)
        return;
  
      // interface Event
  
      // PhaseType (const unsigned short)
      Event.CAPTURING_PHASE = 1;
      Event.AT_TARGET       = 2;
      Event.BUBBLING_PHASE  = 3;
  
      Object.defineProperties(Event.prototype, {
        CAPTURING_PHASE: { get: function() { return 1; } },
        AT_TARGET:       { get: function() { return 2; } },
        BUBBLING_PHASE:   { get: function() { return 3; } },
        target: {
          get: function() {
            return this.srcElement;
          }},
        currentTarget: {
          get: function() {
            return this._currentTarget;
          }},
        eventPhase: {
          get: function() {
            return (this.srcElement === this.currentTarget) ? Event.AT_TARGET : Event.BUBBLING_PHASE;
          }},
        bubbles: {
          get: function() {
            switch (this.type) {
              // Mouse
            case 'click':
            case 'dblclick':
            case 'mousedown':
            case 'mouseup':
            case 'mouseover':
            case 'mousemove':
            case 'mouseout':
            case 'mousewheel':
              // Keyboard
            case 'keydown':
            case 'keypress':
            case 'keyup':
              // Frame/Object
            case 'resize':
            case 'scroll':
              // Form
            case 'select':
            case 'change':
            case 'submit':
            case 'reset':
              return true;
            }
            return false;
          }},
        cancelable: {
          get: function() {
            switch (this.type) {
              // Mouse
            case 'click':
            case 'dblclick':
            case 'mousedown':
            case 'mouseup':
            case 'mouseover':
            case 'mouseout':
            case 'mousewheel':
              // Keyboard
            case 'keydown':
            case 'keypress':
            case 'keyup':
              // Form
            case 'submit':
              return true;
            }
            return false;
          }},
        timeStamp: {
          get: function() {
            return this._timeStamp;
          }},
        stopPropagation: {
          value: function() {
            this.cancelBubble = true;
          }},
        preventDefault: {
          value: function() {
            this.returnValue = false;
          }},
        defaultPrevented: {
          get: function() {
            return this.returnValue === false;
          }}
      });
  
      // interface EventTarget
  
      function addEventListener(type, listener, useCapture) {
        if (typeof listener !== 'function') return;
        if (type === 'DOMContentLoaded') type = 'load';
        var target = this;
        var f = function(e) {
          e._timeStamp = Date.now();
          e._currentTarget = target;
          listener.call(this, e);
          e._currentTarget = null;
        };
        this['_' + type + listener] = f;
        this.attachEvent('on' + type, f);
      }
  
      function removeEventListener(type, listener, useCapture) {
        if (typeof listener !== 'function') return;
        if (type === 'DOMContentLoaded') type = 'load';
        var f = this['_' + type + listener];
        if (f) {
          this.detachEvent('on' + type, f);
          this['_' + type + listener] = null;
        }
      }
  
      [Window, HTMLDocument, Element].forEach(function(o) {
        o.prototype.addEventListener = addEventListener;
        o.prototype.removeEventListener = removeEventListener;
      });
    }());
  
    // CustomEvent
    // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
    // Needed for: IE
    (function () {
      if ('CustomEvent' in global && typeof global.CustomEvent === "function")
        return;
      function CustomEvent ( event, params ) {
        params = params || { bubbles: false, cancelable: false, detail: undefined };
        var evt = document.createEvent( 'CustomEvent' );
        evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
        return evt;
      }
      CustomEvent.prototype = global.Event.prototype;
      global.CustomEvent = CustomEvent;
    })();
  
    // Shim for DOM Events for IE7-
    // http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html
    // Use addEvent(object, event, handler) instead of object.addEventListener(event, handler)
    window.addEvent = function(obj, type, fn) {
      if (obj.addEventListener) {
        obj.addEventListener(type, fn, false);
      } else if (obj.attachEvent) {
        obj["e" + type + fn] = fn;
        obj[type + fn] = function() {
          var e = window.event;
          e.currentTarget = obj;
          e.preventDefault = function() { e.returnValue = false; };
          e.stopPropagation = function() { e.cancelBubble = true; };
          e.target = e.srcElement;
          e.timeStamp = Date.now();
          obj["e" + type + fn].call(this, e);
        };
        obj.attachEvent("on" + type, obj[type + fn]);
      }
    };
  
    window.removeEvent = function(obj, type, fn) {
      if (obj.removeEventListener) {
        obj.removeEventListener(type, fn, false);
      } else if (obj.detachEvent) {
        obj.detachEvent("on" + type, obj[type + fn]);
        obj[type + fn] = null;
        obj["e" + type + fn] = null;
      }
    };
  
    // DOMTokenList interface and Element.classList / Element.relList
    // Needed for: IE9-
    // Use getClassList(elem) instead of elem.classList() if IE7- support is needed
    // Use getRelList(elem) instead of elem.relList() if IE7- support is needed
    (function() {
      function DOMTokenListShim(o, p) {
        function split(s) { return s.length ? s.split(/\s+/g) : []; }
  
        // NOTE: This does not exactly match the spec.
        function removeTokenFromString(token, string) {
          var tokens = split(string),
              index = tokens.indexOf(token);
          if (index !== -1) {
            tokens.splice(index, 1);
          }
          return tokens.join(' ');
        }
  
        Object.defineProperties(
          this,
          {
            length: {
              get: function() { return split(o[p]).length; }
            },
  
            item: {
              value: function(idx) {
                var tokens = split(o[p]);
                return 0 <= idx && idx < tokens.length ? tokens[idx] : null;
              }
            },
  
            contains: {
              value: function(token) {
                token = String(token);
                if (token.length === 0) { throw SyntaxError(); }
                if (/\s/.test(token)) { throw Error("InvalidCharacterError"); }
                var tokens = split(o[p]);
  
                return tokens.indexOf(token) !== -1;
              }
            },
  
            add: {
              value: function(/*tokens...*/) {
                var tokens = Array.prototype.slice.call(arguments).map(String);
                if (tokens.some(function(token) { return token.length === 0; })) {
                  throw SyntaxError();
                }
                if (tokens.some(function(token) { return (/\s/).test(token); })) {
                  throw Error("InvalidCharacterError");
                }
  
                try {
                  var underlying_string = o[p];
                  var token_list = split(underlying_string);
                  tokens = tokens.filter(function(token) { return token_list.indexOf(token) === -1; });
                  if (tokens.length === 0) {
                    return;
                  }
                  if (underlying_string.length !== 0 && !(/\s$/).test(underlying_string)) {
                    underlying_string += ' ';
                  }
                  underlying_string += tokens.join(' ');
                  o[p] = underlying_string;
                } finally {
                  var length = split(o[p]).length;
                  if (this.length !== length) { this.length = length; }
                }
              }
            },
  
            remove: {
              value: function(/*tokens...*/) {
                var tokens = Array.prototype.slice.call(arguments).map(String);
                if (tokens.some(function(token) { return token.length === 0; })) {
                  throw SyntaxError();
                }
                if (tokens.some(function(token) { return (/\s/).test(token); })) {
                  throw Error("InvalidCharacterError");
                }
  
                try {
                  var underlying_string = o[p];
                  tokens.forEach(function(token) {
                    underlying_string = removeTokenFromString(token, underlying_string);
                  });
                  o[p] = underlying_string;
                } finally {
                  var length = split(o[p]).length;
                  if (this.length !== length) { this.length = length; }
                }
              }
            },
  
            toggle: {
              value: function(token/*, force*/) {
                var force = arguments[1];
                try {
                  token = String(token);
                  if (token.length === 0) { throw SyntaxError(); }
                  if (/\s/.test(token)) { throw Error("InvalidCharacterError"); }
                  var tokens = split(o[p]),
                      index = tokens.indexOf(token);
  
                  if (index !== -1 && (!force || force === (void 0))) {
                    o[p] = removeTokenFromString(token, o[p]);
                    return false;
                  }
                  if (index !== -1 && force) {
                    return true;
                  }
                  var underlying_string = o[p];
                  if (underlying_string.length !== 0 && !/\s$/.test(underlying_string)) {
                    underlying_string += ' ';
                  }
                  underlying_string += token;
                  o[p] = underlying_string;
                  return true;
                } finally {
                  var length = split(o[p]).length;
                  if (this.length !== length) { this.length = length; }
                }
              }
            },
  
            toString: {
              value: function() {
                return o[p];
              }
            }
          });
        if (!('length' in this)) {
          // In case getters are not supported
          this.length = split(o[p]).length;
        } else {
          // If they are, shim in index getters (up to 100)
          for (var i = 0; i < 100; ++i) {
            Object.defineProperty(this, String(i), {
              get: (function(n) { return function() { return this.item(n); }; }(i))
            });
          }
        }
      }
  
      function addToElementPrototype(p, f) {
        if ('Element' in global && Element.prototype && Object.defineProperty) {
          Object.defineProperty(Element.prototype, p, { get: f });
        }
      }
  
      // HTML - https://html.spec.whatwg.org
      // Element.classList
      if ('classList' in document.createElement('span')) {
        window.getClassList = function(elem) { return elem.classList; };
      } else {
        window.getClassList = function(elem) { return new DOMTokenListShim(elem, 'className'); };
        addToElementPrototype('classList', function() { return new DOMTokenListShim(this, 'className'); } );
      }
  
      // HTML - https://html.spec.whatwg.org
      // HTMLAnchorElement.relList
      // HTMLLinkElement.relList
      if ('relList' in document.createElement('link')) {
        window.getRelList = function(elem) { return elem.relList; };
      } else {
        window.getRelList = function(elem) { return new DOMTokenListShim(elem, 'rel'); };
        addToElementPrototype('relList', function() { return new DOMTokenListShim(this, 'rel'); } );
      }
  
      // Add second argument to native DOMTokenList.toggle() if necessary
      (function() {
        if (!('DOMTokenList' in global)) return;
        var e = document.createElement('span');
        if (!('classList' in e)) return;
        e.classList.toggle('x', false);
        if (!e.classList.contains('x')) return;
        global.DOMTokenList.prototype.toggle = function toggle(token/*, force*/) {
          var force = arguments[1];
          if (force === undefined) {
            var add = !this.contains(token);
            this[add ? 'add' : 'remove'](token);
            return add;
          }
          force = !!force;
          this[force ? 'add' : 'remove'](token);
          return force;
        };
      }());
  
  
      // DOM - Interface NonDocumentTypeChildNode
      // Interface NonDocumentTypeChildNode
      // previousElementSibling / nextElementSibling - for IE8
  
      if (!('previousElementSibling' in document.documentElement)) {
        addToElementPrototype('previousElementSibling', function() {
          var n = this.previousSibling;
          while (n && n.nodeType !== Node.ELEMENT_NODE)
            n = n.previousSibling;
          return n;
        });
      }
  
      if (!('nextElementSibling' in document.documentElement)) {
        addToElementPrototype('nextElementSibling', function() {
          var n = this.nextSibling;
          while (n && n.nodeType !== Node.ELEMENT_NODE)
            n = n.nextSibling;
          return n;
        });
      }
    }());
  
    // Element.matches
    // https://developer.mozilla.org/en/docs/Web/API/Element/matches
    // Needed for: IE, Firefox 3.6, early Webkit and Opera 15.0
    // Use msMatchesSelector(selector) for IE
    // Use oMatchesSelector(selector) for Opera 15.0
    // Use mozMatchesSelector(selector) for Firefox 3.6
    // Use webkitMatchesSelector(selector) for early Webkit
    // Use polyfill if no matches() support, but querySelectorAll() support
    if ('Element' in global && !Element.prototype.matches) {
      if (Element.prototype.msMatchesSelector) {
        Element.prototype.matches = Element.prototype.msMatchesSelector;
      } else if (Element.prototype.oMatchesSelector) {
        Element.prototype.matches = Element.prototype.oMatchesSelector;
      } else if (Element.prototype.mozMatchesSelector) {
        Element.prototype.matches = Element.prototype.mozMatchesSelector;
      } else if (Element.prototype.webkitMatchesSelector) {
        Element.prototype.matches = Element.prototype.webkitMatchesSelector;
      } else if (document.querySelectorAll) {
        Element.prototype.matches = function matches(selector) {
          var matches = (this.document || this.ownerDocument).querySelectorAll(selector),
              i = matches.length;
          while (--i >= 0 && matches.item(i) !== this) {}
          return i > -1;
        };
      }
    }
  
    // Element.closest
    // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
    if (window.Element && !Element.prototype.closest) {
      Element.prototype.closest = function(s) {
        var matches = (this.document || this.ownerDocument).querySelectorAll(s),
            i,
            el = this;
        do {
          i = matches.length;
          while (--i >= 0 && matches.item(i) !== el) {};
        } while ((i < 0) && (el = el.parentElement)); 
        return el;
      };
    }
  
    function mixin(o, ps) {
      if (!o) return;
      Object.keys(ps).forEach(function(p) {
        if ((p in o) || (p in o.prototype)) return;
        try {
          Object.defineProperty(
            o.prototype,
            p,
            Object.getOwnPropertyDescriptor(ps, p)
          );
        } catch (ex) {
          // Throws in IE8; just copy it
          o[p] = ps[p];
        }
      });
    }
  
    // Mixin ParentNode
    // https://dom.spec.whatwg.org/#interface-parentnode
  
    function convertNodesIntoANode(nodes) {
      var node = null;
      nodes = nodes.map(function(node) {
        return !(node instanceof Node) ? document.createTextNode(node) : node;
      });
      if (nodes.length === 1) {
        node = nodes[0];
      } else {
        node = document.createDocumentFragment();
        nodes.forEach(function(n) { node.appendChild(n); });
      }
      return node;
    }
  
    var ParentNode = {
      prepend: function(/*...nodes*/) {
        var nodes = [].slice.call(arguments);
        nodes = convertNodesIntoANode(nodes);
        this.insertBefore(nodes, this.firstChild);
      },
      append: function(/*...nodes*/) {
        var nodes = [].slice.call(arguments);
        nodes = convertNodesIntoANode(nodes);
        this.appendChild(nodes);
      }
    };
  
    mixin(global.Document || global.HTMLDocument, ParentNode); // HTMLDocument for IE8
    mixin(global.DocumentFragment, ParentNode);
    mixin(global.Element, ParentNode);
  
    // Mixin ChildNode
    // https://dom.spec.whatwg.org/#interface-childnode
  
    var ChildNode = {
      before: function(/*...nodes*/) {
        var nodes = [].slice.call(arguments);
        var parent = this.parentNode;
        if (!parent) return;
        var viablePreviousSibling = this.previousSibling;
        while (nodes.indexOf(viablePreviousSibling) !== -1)
          viablePreviousSibling = viablePreviousSibling.previousSibling;
        var node = convertNodesIntoANode(nodes);
        parent.insertBefore(node, viablePreviousSibling ?
                            viablePreviousSibling.nextSibling : parent.firstChild);
      },
      after: function(/*...nodes*/) {
        var nodes = [].slice.call(arguments);
        var parent = this.parentNode;
        if (!parent) return;
        var viableNextSibling = this.nextSibling;
        while (nodes.indexOf(viableNextSibling) !== -1)
          viableNextSibling = viableNextSibling.nextSibling;
        var node = convertNodesIntoANode(nodes);
        parent.insertBefore(node, viableNextSibling);
      },
      replaceWith: function(/*...nodes*/) {
        var nodes = [].slice.call(arguments);
        var parent = this.parentNode;
        if (!parent) return;
        var viableNextSibling = this.nextSibling;
        while (nodes.indexOf(viableNextSibling) !== -1)
          viableNextSibling = viableNextSibling.nextSibling;
        var node = convertNodesIntoANode(nodes);
  
        if (this.parentNode === parent)
          parent.replaceChild(node, this);
        else
          parent.insertBefore(node, viableNextSibling);
      },
      remove: function() {
        if (!this.parentNode) return;
        this.parentNode.removeChild(this);
      }
    };
  
    mixin(global.DocumentType, ChildNode);
    mixin(global.Element, ChildNode);
    mixin(global.CharacterData, ChildNode);
  
  }(self));
  (function(global) {
    'use strict';
  
    if (!('window' in global && 'document' in global))
      return;
  
    //----------------------------------------------------------------------
    //
    // XMLHttpRequest
    // https://xhr.spec.whatwg.org
    //
    //----------------------------------------------------------------------
  
    // XMLHttpRequest interface
    // Needed for: IE7-
    global.XMLHttpRequest = global.XMLHttpRequest || function() {
      try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (_) { }
      try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (_) { }
      try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (_) { }
      throw Error("This browser does not support XMLHttpRequest.");
    };
  
    // XMLHttpRequest interface constants
    // Needed for IE8-
    [
      ['UNSENT', 0],
      ['OPENED', 1],
      ['HEADERS_RECEIVED', 2],
      ['LOADING', 3],
      ['DONE', 4],
    ].forEach(function(p) { if (!(p[0] in global.XMLHttpRequest)) global.XMLHttpRequest[p[0]] = p[1]; });
  
    // FormData interface
    // Needed for: IE9-
    (function() {
      if ('FormData' in global)
        return;
  
      function FormData(form) {
        this._data = [];
        if (!form) return;
        for (var i = 0; i < form.elements.length; ++i) {
          var element = form.elements[i];
          if (element.name !== '')
            this.append(element.name, element.value);
        }
      }
  
      FormData.prototype = {
        append: function(name, value /*, filename */) {
          if ('Blob' in global && value instanceof global.Blob)
            throw TypeError("Blob not supported");
          name = String(name);
          this._data.push([name, value]);
        },
  
        toString: function() {
          return this._data.map(function(pair) {
            return encodeURIComponent(pair[0]) + '=' + encodeURIComponent(pair[1]);
          }).join('&');
        }
      };
  
      global.FormData = FormData;
      var send = global.XMLHttpRequest.prototype.send;
      global.XMLHttpRequest.prototype.send = function(body) {
        if (body instanceof FormData) {
          this.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
          arguments[0] = body.toString();
        }
        return send.apply(this, arguments);
      };
    }());
  
  }(self));
  (function(global) {
    'use strict';
  
    if (!('window' in global && 'document' in global))
      return;
  
    //----------------------------------------------------------------------
    //
    // CSSOM View Module
    // https://dev.w3.org/csswg/cssom-view/
    //
    //----------------------------------------------------------------------
  
    // Fix for IE8-'s Element.getBoundingClientRect()
    if ('TextRectangle' in global && !('width' in global.TextRectangle.prototype)) {
      Object.defineProperties(global.TextRectangle.prototype, {
        width: { get: function() { return this.right - this.left; } },
        height: { get: function() { return this.bottom - this.top; } }
      });
    }
  }(self));
  // URL Polyfill
  // Draft specification: https://url.spec.whatwg.org
  
  // Notes:
  // - Primarily useful for parsing URLs and modifying query parameters
  // - Should work in IE8+ and everything more modern, with es5.js polyfills
  
  (function (global) {
    'use strict';
  
    function isSequence(o) {
      if (!o) return false;
      if ('Symbol' in global && 'iterator' in global.Symbol &&
          typeof o[Symbol.iterator] === 'function') return true;
      if (Array.isArray(o)) return true;
      return false;
    }
  
    function toArray(iter) {
      return ('from' in Array) ? Array.from(iter) : Array.prototype.slice.call(iter);
    }
  
    (function() {
  
      // Browsers may have:
      // * No global URL object
      // * URL with static methods only - may have a dummy constructor
      // * URL with members except searchParams
      // * Full URL API support
      var origURL = global.URL;
      var nativeURL;
      try {
        if (origURL) {
          nativeURL = new global.URL('http://example.com');
          if ('searchParams' in nativeURL)
            return;
          if (!('href' in nativeURL))
            nativeURL = undefined;
        }
      } catch (_) {}
  
      // NOTE: Doesn't do the encoding/decoding dance
      function urlencoded_serialize(pairs) {
        var output = '', first = true;
        pairs.forEach(function (pair) {
          var name = encodeURIComponent(pair.name);
          var value = encodeURIComponent(pair.value);
          if (!first) output += '&';
          output += name + '=' + value;
          first = false;
        });
        return output.replace(/%20/g, '+');
      }
  
      // NOTE: Doesn't do the encoding/decoding dance
      function urlencoded_parse(input, isindex) {
        var sequences = input.split('&');
        if (isindex && sequences[0].indexOf('=') === -1)
          sequences[0] = '=' + sequences[0];
        var pairs = [];
        sequences.forEach(function (bytes) {
          if (bytes.length === 0) return;
          var index = bytes.indexOf('=');
          if (index !== -1) {
            var name = bytes.substring(0, index);
            var value = bytes.substring(index + 1);
          } else {
            name = bytes;
            value = '';
          }
          name = name.replace(/\+/g, ' ');
          value = value.replace(/\+/g, ' ');
          pairs.push({ name: name, value: value });
        });
        var output = [];
        pairs.forEach(function (pair) {
          output.push({
            name: decodeURIComponent(pair.name),
            value: decodeURIComponent(pair.value)
          });
        });
        return output;
      }
  
      function URLUtils(url) {
        if (nativeURL)
          return new origURL(url);
        var anchor = document.createElement('a');
        anchor.href = url;
        return anchor;
      }
  
      function URLSearchParams(init) {
        var $this = this;
        this._list = [];
  
        if (init === undefined || init === null) {
          // no-op
        } else if (init instanceof URLSearchParams) {
          // In ES6 init would be a sequence, but special case for ES5.
          this._list = urlencoded_parse(String(init));
        } else if (typeof init === 'object' && isSequence(init)) {
          toArray(init).forEach(function(e) {
            if (!isSequence(e)) throw TypeError();
            var nv = toArray(e);
            if (nv.length !== 2) throw TypeError();
            $this._list.push({name: String(nv[0]), value: String(nv[1])});
          });
        } else if (typeof init === 'object' && init) {
          Object.keys(init).forEach(function(key) {
            $this._list.push({name: String(key), value: String(init[key])});
          });
        } else {
          init = String(init);
          if (init.substring(0, 1) === '?')
            init = init.substring(1);
          this._list = urlencoded_parse(init);
        }
  
        this._url_object = null;
        this._setList = function (list) { if (!updating) $this._list = list; };
  
        var updating = false;
        this._update_steps = function() {
          if (updating) return;
          updating = true;
  
          if (!$this._url_object) return;
  
          // Partial workaround for IE issue with 'about:'
          if ($this._url_object.protocol === 'about:' &&
              $this._url_object.pathname.indexOf('?') !== -1) {
            $this._url_object.pathname = $this._url_object.pathname.split('?')[0];
          }
  
          $this._url_object.search = urlencoded_serialize($this._list);
  
          updating = false;
        };
      }
  
  
      Object.defineProperties(URLSearchParams.prototype, {
        append: {
          value: function (name, value) {
            this._list.push({ name: name, value: value });
            this._update_steps();
          }, writable: true, enumerable: true, configurable: true
        },
  
        'delete': {
          value: function (name) {
            for (var i = 0; i < this._list.length;) {
              if (this._list[i].name === name)
                this._list.splice(i, 1);
              else
                ++i;
            }
            this._update_steps();
          }, writable: true, enumerable: true, configurable: true
        },
  
        get: {
          value: function (name) {
            for (var i = 0; i < this._list.length; ++i) {
              if (this._list[i].name === name)
                return this._list[i].value;
            }
            return null;
          }, writable: true, enumerable: true, configurable: true
        },
  
        getAll: {
          value: function (name) {
            var result = [];
            for (var i = 0; i < this._list.length; ++i) {
              if (this._list[i].name === name)
                result.push(this._list[i].value);
            }
            return result;
          }, writable: true, enumerable: true, configurable: true
        },
  
        has: {
          value: function (name) {
            for (var i = 0; i < this._list.length; ++i) {
              if (this._list[i].name === name)
                return true;
            }
            return false;
          }, writable: true, enumerable: true, configurable: true
        },
  
        set: {
          value: function (name, value) {
            var found = false;
            for (var i = 0; i < this._list.length;) {
              if (this._list[i].name === name) {
                if (!found) {
                  this._list[i].value = value;
                  found = true;
                  ++i;
                } else {
                  this._list.splice(i, 1);
                }
              } else {
                ++i;
              }
            }
  
            if (!found)
              this._list.push({ name: name, value: value });
  
            this._update_steps();
          }, writable: true, enumerable: true, configurable: true
        },
  
        entries: {
          value: function() { return new Iterator(this._list, 'key+value'); },
          writable: true, enumerable: true, configurable: true
        },
  
        keys: {
          value: function() { return new Iterator(this._list, 'key'); },
          writable: true, enumerable: true, configurable: true
        },
  
        values: {
          value: function() { return new Iterator(this._list, 'value'); },
          writable: true, enumerable: true, configurable: true
        },
  
        forEach: {
          value: function(callback) {
            var thisArg = (arguments.length > 1) ? arguments[1] : undefined;
            this._list.forEach(function(pair, index) {
              callback.call(thisArg, pair.value, pair.name);
            });
  
          }, writable: true, enumerable: true, configurable: true
        },
  
        toString: {
          value: function () {
            return urlencoded_serialize(this._list);
          }, writable: true, enumerable: false, configurable: true
        }
      });
  
      function Iterator(source, kind) {
        var index = 0;
        this['next'] = function() {
          if (index >= source.length)
            return {done: true, value: undefined};
          var pair = source[index++];
          return {done: false, value:
                  kind === 'key' ? pair.name :
                  kind === 'value' ? pair.value :
                  [pair.name, pair.value]};
        };
      }
  
      if ('Symbol' in global && 'iterator' in global.Symbol) {
        Object.defineProperty(URLSearchParams.prototype, global.Symbol.iterator, {
          value: URLSearchParams.prototype.entries,
          writable: true, enumerable: true, configurable: true});
        Object.defineProperty(Iterator.prototype, global.Symbol.iterator, {
          value: function() { return this; },
          writable: true, enumerable: true, configurable: true});
      }
  
      function URL(url, base) {
        if (!(this instanceof global.URL))
          throw new TypeError("Failed to construct 'URL': Please use the 'new' operator.");
  
        if (base) {
          url = (function () {
            if (nativeURL) return new origURL(url, base).href;
            var iframe;
            try {
              var doc;
              // Use another document/base tag/anchor for relative URL resolution, if possible
              if (Object.prototype.toString.call(window.operamini) === "[object OperaMini]") {
                iframe = document.createElement('iframe');
                iframe.style.display = 'none';
                document.documentElement.appendChild(iframe);
                doc = iframe.contentWindow.document;
              } else if (document.implementation && document.implementation.createHTMLDocument) {
                doc = document.implementation.createHTMLDocument('');
              } else if (document.implementation && document.implementation.createDocument) {
                doc = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null);
                doc.documentElement.appendChild(doc.createElement('head'));
                doc.documentElement.appendChild(doc.createElement('body'));
              } else if (window.ActiveXObject) {
                doc = new window.ActiveXObject('htmlfile');
                doc.write('<head><\/head><body><\/body>');
                doc.close();
              }
  
              if (!doc) throw Error('base not supported');
  
              var baseTag = doc.createElement('base');
              baseTag.href = base;
              doc.getElementsByTagName('head')[0].appendChild(baseTag);
              var anchor = doc.createElement('a');
              anchor.href = url;
              return anchor.href;
            } finally {
              if (iframe)
                iframe.parentNode.removeChild(iframe);
            }
          }());
        }
  
        // An inner object implementing URLUtils (either a native URL
        // object or an HTMLAnchorElement instance) is used to perform the
        // URL algorithms. With full ES5 getter/setter support, return a
        // regular object For IE8's limited getter/setter support, a
        // different HTMLAnchorElement is returned with properties
        // overridden
  
        var instance = URLUtils(url || '');
  
        // Detect for ES5 getter/setter support
        // (an Object.defineProperties polyfill that doesn't support getters/setters may throw)
        var ES5_GET_SET = (function() {
          if (!('defineProperties' in Object)) return false;
          try {
            var obj = {};
            Object.defineProperties(obj, { prop: { 'get': function () { return true; } } });
            return obj.prop;
          } catch (_) {
            return false;
          }
        })();
  
        var self = ES5_GET_SET ? this : document.createElement('a');
  
  
  
        var query_object = new URLSearchParams(
          instance.search ? instance.search.substring(1) : null);
        query_object._url_object = self;
  
        Object.defineProperties(self, {
          href: {
            get: function () { return instance.href; },
            set: function (v) { instance.href = v; tidy_instance(); update_steps(); },
            enumerable: true, configurable: true
          },
          origin: {
            get: function () {
              if ('origin' in instance) return instance.origin;
              return this.protocol + '//' + this.host;
            },
            enumerable: true, configurable: true
          },
          protocol: {
            get: function () { return instance.protocol; },
            set: function (v) { instance.protocol = v; },
            enumerable: true, configurable: true
          },
          username: {
            get: function () { return instance.username; },
            set: function (v) { instance.username = v; },
            enumerable: true, configurable: true
          },
          password: {
            get: function () { return instance.password; },
            set: function (v) { instance.password = v; },
            enumerable: true, configurable: true
          },
          host: {
            get: function () {
              // IE returns default port in |host|
              var re = {'http:': /:80$/, 'https:': /:443$/, 'ftp:': /:21$/}[instance.protocol];
              return re ? instance.host.replace(re, '') : instance.host;
            },
            set: function (v) { instance.host = v; },
            enumerable: true, configurable: true
          },
          hostname: {
            get: function () { return instance.hostname; },
            set: function (v) { instance.hostname = v; },
            enumerable: true, configurable: true
          },
          port: {
            get: function () { return instance.port; },
            set: function (v) { instance.port = v; },
            enumerable: true, configurable: true
          },
          pathname: {
            get: function () {
              // IE does not include leading '/' in |pathname|
              if (instance.pathname.charAt(0) !== '/') return '/' + instance.pathname;
              return instance.pathname;
            },
            set: function (v) { instance.pathname = v; },
            enumerable: true, configurable: true
          },
          search: {
            get: function () { return instance.search; },
            set: function (v) {
              if (instance.search === v) return;
              instance.search = v; tidy_instance(); update_steps();
            },
            enumerable: true, configurable: true
          },
          searchParams: {
            get: function () { return query_object; },
            enumerable: true, configurable: true
          },
          hash: {
            get: function () { return instance.hash; },
            set: function (v) { instance.hash = v; tidy_instance(); },
            enumerable: true, configurable: true
          },
          toString: {
            value: function() { return instance.toString(); },
            enumerable: false, configurable: true
          },
          valueOf: {
            value: function() { return instance.valueOf(); },
            enumerable: false, configurable: true
          }
        });
  
        function tidy_instance() {
          var href = instance.href.replace(/#$|\?$|\?(?=#)/g, '');
          if (instance.href !== href)
            instance.href = href;
        }
  
        function update_steps() {
          query_object._setList(instance.search ? urlencoded_parse(instance.search.substring(1)) : []);
          query_object._update_steps();
        };
  
        return self;
      }
  
      if (origURL) {
        for (var i in origURL) {
          if (origURL.hasOwnProperty(i) && typeof origURL[i] === 'function')
            URL[i] = origURL[i];
        }
      }
  
      global.URL = URL;
      global.URLSearchParams = URLSearchParams;
    }());
  
    // Patch native URLSearchParams constructor to handle sequences/records
    // if necessary.
    (function() {
      if (new global.URLSearchParams([['a', 1]]).get('a') === '1' &&
          new global.URLSearchParams({a: 1}).get('a') === '1')
        return;
      var orig = global.URLSearchParams;
      global.URLSearchParams = function(init) {
        if (init && typeof init === 'object' && isSequence(init)) {
          var o = new orig();
          toArray(init).forEach(function(e) {
            if (!isSequence(e)) throw TypeError();
            var nv = toArray(e);
            if (nv.length !== 2) throw TypeError();
            o.append(nv[0], nv[1]);
          });
          return o;
        } else if (init && typeof init === 'object') {
          o = new orig();
          Object.keys(init).forEach(function(key) {
            o.set(key, init[key]);
          });
          return o;
        } else {
          return new orig(init);
        }
      };
    }());
  
  }(self));
  // Work-In-Progress 'prollyfill' for Fetch API
  // Standard: https://fetch.spec.whatwg.org/#fetch-api
  //
  // As usual, the intent is to produce a forward-compatible
  // subset so that code can be written using future standard
  // functionality; not every case is considered or supported.
  
  // Requires ES2015: Promise, Symbol.iterator (or polyfill)
  // Requires: URL (or polyfill)
  
  // Example:
  //   fetch('README.md')
  //     .then(function(response) { return response.text(); })
  //     .then(function(text) { alert(text); });
  
  (function(global) {
    'use strict';
  
    // Web IDL concepts
  
    // https://heycam.github.io/webidl/#idl-ByteString
    function ByteString(value) {
      value = String(value);
      if (value.match(/[^\x00-\xFF]/)) throw TypeError('Not a valid ByteString');
      return value;
    }
  
    // https://heycam.github.io/webidl/#idl-USVString
    function USVString(value) {
      value = String(value);
      return value.replace(
          /([\u0000-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDFFF])/g,
        function (c) {
          if (/^[\uD800-\uDFFF]$/.test(c)) return '\uFFFD';
          return c;
        });
    }
  
    function ushort(x) { return x & 0xFFFF; }
  
    // 2 Terminology
  
    function byteLowerCase(s) {
      return String(s).replace(/[A-Z]/g, function(c) { return c.toLowerCase(); });
    }
  
    function byteUpperCase(s) {
      return String(s).replace(/[a-z]/g, function(c) { return c.toUpperCase(); });
    }
  
    function byteCaseInsensitiveMatch(a, b) {
      return byteLowerCase(a) === byteLowerCase(b);
    }
  
    // 2.1 HTTP
  
    // 2.1.1 Methods
  
    function isForbiddenMethod(m) {
      m = byteUpperCase(m);
      return m === 'CONNECT' || m === 'TRACE' || m === 'TRACK';
    }
  
    function normalizeMethod(m) {
      var u = byteUpperCase(m);
      if (u === 'DELETE' || u === 'GET' || u === 'HEAD' || u === 'OPTIONS' ||
          u === 'POST' || u === 'PUT') return u;
      return m;
    }
  
    function isName(s) {
      return /^[!#$%&'*+\-.09A-Z^_`a-z|~]+$/.test(s);
    }
    function isValue(s) {
      // TODO: Implement me
      return true;
    }
    function isForbiddenHeaderName(n) {
      n = String(n).toLowerCase();
      var forbidden = {
        'accept-charset': true,
        'accept-encoding': true,
        'access-control-request-headers': true,
        'access-control-request-method': true,
        'connection': true,
        'content-length': true,
        'cookie': true,
        'cookie2': true,
        'date': true,
        'dnt': true,
        'expect': true,
        'host': true,
        'keep-alive': true,
        'origin': true,
        'referer': true,
        'te': true,
        'trailer': true,
        'transfer-encoding': true,
        'upgrade': true,
        'user-agent': true,
        'via': true
      };
      return forbidden[n] || n.substring(0, 6) === 'proxy-' || n.substring(0, 4) === 'sec-';
    }
  
    function isForbiddenResponseHeaderName(n) {
      n = String(n).toLowerCase();
      var forbidden = {
        'set-cookie': true,
        'set-cookie2': true
      };
      return forbidden[n];
    }
  
    function isSimpleHeader(name, value) {
      name = String(name).toLowerCase();
      return name === 'accept' || name === 'accept-language' || name === 'content-language' ||
        (name === 'content-type' &&
         ['application/x-www-form-encoded', 'multipart/form-data', 'text/plain'].indexOf(value) !== -1);
    }
  
    //
    // 5.1 Headers class
    //
  
    // typedef (Headers or sequence<sequence<ByteString>> or OpenEndedDictionary<ByteString>) HeadersInit;
  
    // Constructor(optional HeadersInit init)
    function Headers(init) {
      this._guard = 'none';
      this._headerList = [];
      if (init) fill(this, init);
    }
  
    function fill(headers, init) {
      if (init instanceof Headers) {
        init._headerList.forEach(function(header) {
          headers.append(header[0], header[1]);
        });
      } else if (Array.isArray(init)) {
        init.forEach(function(header) {
          if (!Array.isArray(header) || header.length !== 2) throw TypeError();
          headers.append(header[0], header[1]);
        });
      } else {
        init = Object(init);
        Object.keys(init).forEach(function(key) {
          headers.append(key, init[key]);
        });
      }
    }
  
    // interface Headers
    Headers.prototype = {
      // void append(ByteString name, ByteString value);
      append: function append(name, value) {
        name = ByteString(name);
        if (!isName(name) || !isValue(value)) throw TypeError();
        if (this._guard === 'immutable') throw TypeError();
        else if (this._guard === 'request' && isForbiddenHeaderName(name)) return;
        else if (this._guard === 'request-no-CORS' && !isSimpleHeader(name, value)) return;
        else if (this._guard === 'response' && isForbiddenResponseHeaderName(name)) return;
  
        name = name.toLowerCase();
        this._headerList.push([name, value]);
      },
  
      // void delete(ByteString name);
      'delete': function delete_(name) {
        name = ByteString(name);
        if (!isName(name)) throw TypeError();
        if (this._guard === 'immutable') throw TypeError();
        else if (this._guard === 'request' && isForbiddenHeaderName(name)) return;
        else if (this._guard === 'request-no-CORS' && !isSimpleHeader(name, 'invalid')) return;
        else if (this._guard === 'response' && isForbiddenResponseHeaderName(name)) return;
  
        name = name.toLowerCase();
        var index = 0;
        while (index < this._headerList.length) {
          if (this._headerList[index][0] === name)
            this._headerList.splice(index, 1);
          else
            ++index;
        }
      },
  
      // ByteString? get(ByteString name);
      get: function get(name) {
        name = ByteString(name);
        if (!isName(name)) throw TypeError();
        name = name.toLowerCase();
        for (var index = 0; index < this._headerList.length; ++index) {
          if (this._headerList[index][0] === name)
            return this._headerList[index][1];
        }
        return null;
      },
  
      // sequence<ByteString> getAll(ByteString name);
      getAll: function getAll(name) {
        name = ByteString(name);
        if (!isName(name)) throw TypeError();
        name = name.toLowerCase();
        var sequence = [];
        for (var index = 0; index < this._headerList.length; ++index) {
          if (this._headerList[index][0] === name)
            sequence.push(this._headerList[index][1]);
        }
        return sequence;
      },
  
      // boolean has(ByteString name);
      has: function has(name) {
        name = ByteString(name);
        if (!isName(name)) throw TypeError();
        name = name.toLowerCase();
        for (var index = 0; index < this._headerList.length; ++index) {
          if (this._headerList[index][0] === name)
            return true;
        }
        return false;
      },
  
      // void set(ByteString name, ByteString value);
      set: function set(name, value) {
        name = ByteString(name);
        if (!isName(name) || !isValue(value)) throw TypeError();
        if (this._guard === 'immutable') throw TypeError();
        else if (this._guard === 'request' && isForbiddenHeaderName(name)) return;
        else if (this._guard === 'request-no-CORS' && !isSimpleHeader(name, value)) return;
        else if (this._guard === 'response' && isForbiddenResponseHeaderName(name)) return;
  
        name = name.toLowerCase();
        for (var index = 0; index < this._headerList.length; ++index) {
          if (this._headerList[index][0] === name) {
            this._headerList[index++][1] = value;
            while (index < this._headerList.length) {
              if (this._headerList[index][0] === name)
                this._headerList.splice(index, 1);
              else
                ++index;
            }
            return;
          }
        }
        this._headerList.push([name, value]);
      }
    };
    Headers.prototype[Symbol.iterator] = function() {
      return new HeadersIterator(this);
    };
  
    function HeadersIterator(headers) {
      this._headers = headers;
      this._index = 0;
    }
    HeadersIterator.prototype = {};
    HeadersIterator.prototype.next = function() {
      if (this._index >= this._headers._headerList.length)
        return { value: undefined, done: true };
      return { value: this._headers._headerList[this._index++], done: false };
    };
    HeadersIterator.prototype[Symbol.iterator] = function() { return this; };
  
  
    //
    // 5.2 Body mixin
    //
  
    function Body(_stream) {
      // TODO: Handle initialization from other types
      this._stream = _stream;
      this.bodyUsed = false;
    }
  
    // interface FetchBodyStream
    Body.prototype = {
      // Promise<ArrayBuffer> arrayBuffer();
      arrayBuffer: function() {
        if (this.bodyUsed) return Promise.reject(TypeError());
        this.bodyUsed = true;
        if (this._stream instanceof ArrayBuffer) return Promise.resolve(this._stream);
        var value = this._stream;
        return new Promise(function(resolve, reject) {
          var octets = unescape(encodeURIComponent(value)).split('').map(function(c) {
            return c.charCodeAt(0);
          });
          resolve(new Uint8Array(octets).buffer);
        });
      },
      // Promise<Blob> blob();
      blob: function() {
        if (this.bodyUsed) return Promise.reject(TypeError());
        this.bodyUsed = true;
        if (this._stream instanceof Blob) return Promise.resolve(this._stream);
        return Promise.resolve(new Blob([this._stream]));
      },
      // Promise<FormData> formData();
      formData: function() {
        if (this.bodyUsed) return Promise.reject(TypeError());
        this.bodyUsed = true;
        if (this._stream instanceof FormData) return Promise.resolve(this._stream);
        return Promise.reject(Error('Not yet implemented'));
      },
      // Promise<JSON> json();
      json: function() {
        if (this.bodyUsed) return Promise.reject(TypeError());
        this.bodyUsed = true;
        var that = this;
        return new Promise(function(resolve, reject) {
          resolve(JSON.parse(that._stream));
        });
      },
      // Promise<USVString> text();
      text: function() {
        if (this.bodyUsed) return Promise.reject(TypeError());
        this.bodyUsed = true;
        return Promise.resolve(String(this._stream));
      }
    };
  
    //
    // 5.3 Request class
    //
  
    // typedef (Request or USVString) RequestInfo;
  
    // Constructor(RequestInfo input, optional RequestInit init)
    function Request(input, init) {
      if (arguments.length < 1) throw TypeError('Not enough arguments');
  
      Body.call(this, null);
  
      // readonly attribute ByteString method;
      this.method = 'GET';
  
      // readonly attribute USVString url;
      this.url = '';
  
      // readonly attribute Headers headers;
      this.headers = new Headers();
      this.headers._guard = 'request';
  
      // readonly attribute DOMString referrer;
      this.referrer = null; // TODO: Implement.
  
      // readonly attribute RequestMode mode;
      this.mode = null; // TODO: Implement.
  
      // readonly attribute RequestCredentials credentials;
      this.credentials = 'omit';
  
      if (input instanceof Request) {
        if (input.bodyUsed) throw TypeError();
        input.bodyUsed = true;
        this.method = input.method;
        this.url = input.url;
        this.headers = new Headers(input.headers);
        this.headers._guard = input.headers._guard;
        this.credentials = input.credentials;
        this._stream = input._stream;
      } else {
        input = USVString(input);
        this.url = String(new URL(input, self.location));
      }
  
      init = Object(init);
  
      if ('method' in init) {
        var method = ByteString(init.method);
        if (isForbiddenMethod(method)) throw TypeError();
        this.method = normalizeMethod(method);
      }
  
      if ('headers' in init) {
        this.headers = new Headers();
        fill(this.headers, init.headers);
      }
  
      if ('body' in init)
        this._stream = init.body;
  
      if ('credentials' in init &&
          (['omit', 'same-origin', 'include'].indexOf(init.credentials) !== -1))
        this.credentials = init.credentials;
    }
  
    // interface Request
    Request.prototype = Body.prototype;
  
    //
    // 5.4 Response class
    //
  
    // Constructor(optional FetchBodyInit body, optional ResponseInit init)
    function Response(body, init) {
      if (arguments.length < 1)
        body = '';
  
      this.headers = new Headers();
      this.headers._guard = 'response';
  
      // Internal
      if (body instanceof XMLHttpRequest && '_url' in body) {
        var xhr = body;
        this.type = 'basic'; // TODO: ResponseType
        this.url = USVString(xhr._url);
        this.status = xhr.status;
        this.ok = 200 <= this.status && this.status <= 299;
        this.statusText = xhr.statusText;
        xhr.getAllResponseHeaders()
          .split(/\r?\n/)
          .filter(function(header) { return header.length; })
          .forEach(function(header) {
            var i = header.indexOf(':');
            this.headers.append(header.substring(0, i), header.substring(i + 2));
          }, this);
        Body.call(this, xhr.responseText);
        return;
      }
  
      Body.call(this, body);
  
      init = Object(init) || {};
  
      // readonly attribute USVString url;
      this.url = '';
  
      // readonly attribute unsigned short status;
      var status = 'status' in init ? ushort(init.status) : 200;
      if (status < 200 || status > 599) throw RangeError();
      this.status = status;
  
      // readonly attribute boolean ok;
      this.ok = 200 <= this.status && this.status <= 299;
  
      // readonly attribute ByteString statusText;
      var statusText = 'statusText' in init ? String(init.statusText) : 'OK';
      if (/[^\x00-\xFF]/.test(statusText)) throw TypeError();
      this.statusText = statusText;
  
      // readonly attribute Headers headers;
      if ('headers' in init) fill(this.headers, init);
  
      // TODO: Implement these
      // readonly attribute ResponseType type;
      this.type = 'basic'; // TODO: ResponseType
    }
  
    // interface Response
    Response.prototype = Body.prototype;
  
    Response.redirect = function() {
      // TODO: Implement?
      throw Error('Not supported');
    };
  
    //
    // 5.5 Structured cloning of Headers, FetchBodyStream, Request, Response
    //
  
    //
    // 5.6 Fetch method
    //
  
    // Promise<Response> fetch(RequestInfo input, optional RequestInit init);
    function fetch(input, init) {
      return new Promise(function(resolve, reject) {
        var r = new Request(input, init);
  
        var xhr = new XMLHttpRequest(), async = true;
        xhr._url = r.url;
  
        try { xhr.open(r.method, r.url, async); } catch (e) { throw TypeError(e.message); }
  
        for (var iter = r.headers[Symbol.iterator](), step = iter.next();
             !step.done; step = iter.next())
          xhr.setRequestHeader(step.value[0], step.value[1]);
  
        if (r.credentials === 'include')
          xhr.withCredentials = true;
  
        xhr.onreadystatechange = function() {
          if (xhr.readyState !== XMLHttpRequest.DONE) return;
          if (xhr.status === 0)
            reject(new TypeError('Network error'));
          else
            resolve(new Response(xhr));
        };
  
        xhr.send(r._stream);
      });
    }
  
    // Exported
    if (!('fetch' in global)) {
      global.Headers = Headers;
      global.Request = Request;
      global.Response = Response;
      global.fetch = fetch;
    }
  
  }(self));
  