You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by jk...@apache.org on 2022/07/07 13:28:02 UTC

[unomi] 01/01: UNOMI-626: improve migration system to prepare Unomi 2.0.0 data models migrations

This is an automated email from the ASF dual-hosted git repository.

jkevan pushed a commit to branch improveMigrationSystem
in repository https://gitbox.apache.org/repos/asf/unomi.git

commit c1e07cfd10672710e768d232a51804d8f7c33b26
Author: Kevan <ke...@jahia.com>
AuthorDate: Thu Jul 7 15:27:44 2022 +0200

    UNOMI-626: improve migration system to prepare Unomi 2.0.0 data models migrations
---
 extensions/web-tracker/javascript/dist/snippet.js  |    89 +
 .../web-tracker/javascript/dist/snippet.min.js     |     2 +
 .../web-tracker/javascript/dist/snippet.min.js.map |     1 +
 .../web-tracker/javascript/dist/unomi-tracker.js   | 16228 +++++++++++++++++++
 .../javascript/dist/unomi-tracker.min.js           |    27 +
 .../javascript/dist/unomi-tracker.min.js.map       |     1 +
 tools/shell-commands/pom.xml                       |    12 +
 .../unomi/shell/migration/MigrateScript.java       |   118 +
 .../apache/unomi/shell/migration/Migration.java    |    34 +-
 .../unomi/shell/migration/actions/Migrate.java     |   197 +-
 .../unomi/shell/migration/impl/MigrationTo121.java |    22 +-
 .../unomi/shell/migration/impl/MigrationTo122.java |    21 +-
 .../unomi/shell/migration/impl/MigrationTo150.java |    24 +-
 .../unomi/shell/migration/impl/MigrationTo200.java |    52 +-
 .../unomi/shell/migration/utils/HttpUtils.java     |     5 +-
 .../shell/migration/utils/MigrationUtils.java      |    44 +
 .../migration/migrate-1.2.1-00-migrateTags.groovy  |    20 +
 .../migrate-1.2.2-00-deleteOldIndexTemplate.groovy |    20 +
 .../migrate-1.5.0-00-elasticSearch7.4.groovy       |    20 +
 ...migrate-2.0.0-00-scopesAndProfileAliases.groovy |    20 +
 .../migrate-2.0.0-01-segmentReindex.groovy         |    21 +
 .../resources/OSGI-INF/blueprint/blueprint.xml     |     8 -
 .../requestBody/2.0.0/base_reindex_request.json    |     8 +
 .../2.0.0/base_set_index_readonly_request.json     |     5 +
 .../resources/requestBody/2.0.0/segment_index.json |    64 +
 25 files changed, 16857 insertions(+), 206 deletions(-)

diff --git a/extensions/web-tracker/javascript/dist/snippet.js b/extensions/web-tracker/javascript/dist/snippet.js
new file mode 100644
index 000000000..97b0698fe
--- /dev/null
+++ b/extensions/web-tracker/javascript/dist/snippet.js
@@ -0,0 +1,89 @@
+(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+window.unomiTracker || (window.unomiTracker = {});
+(function () {
+    var unomiTracker_queue = [];
+
+    var methods = ['trackSubmit', 'trackClick', 'trackLink', 'trackForm', 'initialize', 'pageview', 'identify', 'reset', 'group', 'track', 'ready', 'alias', 'debug', 'page', 'once', 'off', 'on', 'personalize'];
+
+    var factory = function (method) {
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            args.unshift(method);
+            unomiTracker_queue.push(args);
+            return window.unomiTracker;
+        };
+    };
+
+    // For each of our methods, generate a queueing stub.
+    for (var i = 0; i < methods.length; i++) {
+        var method = methods[i];
+        window.unomiTracker[method] = factory(method);
+    }
+
+    function callback(e) {
+        unomiTracker.initialize({
+            'Apache Unomi': unomiOption
+        });
+
+        // Loop through the interim analytics queue and reapply the calls to their
+        // proper analytics.js method.
+        while (unomiTracker_queue.length > 0) {
+            var item = unomiTracker_queue.shift();
+            var method = item.shift();
+            if (unomiTracker[method]) {
+                unomiTracker[method].apply(unomiTracker, item);
+            }
+        }
+    }
+
+    // Define a method to load Analytics.js from our CDN,
+    // and that will be sure to only ever load it once.
+    unomiTracker.load = function() {
+        // Create an async script element based on your key.
+        var script = document.createElement('script');
+        script.type = 'text/javascript';
+        script.async = true;
+        // TODO we might want to add a check on the url to see if it ends with / or not
+        script.src = unomiOption.url + '/tracker/unomi-tracker.min.js';
+
+        if (script.addEventListener) {
+            script.addEventListener('load', function (e) {
+                if (typeof callback === 'function') {
+                    callback(e);
+                }
+            }, false);
+        } else {
+            script.onreadystatechange = function () {
+                if (this.readyState === 'complete' || this.readyState === 'loaded') {
+                    callback(window.event);
+                }
+            };
+        }
+
+        // Insert our script next to the first script element.
+        var first = document.getElementsByTagName('script')[0];
+        first.parentNode.insertBefore(script, first);
+    };
+
+    document.addEventListener('DOMContentLoaded', unomiTracker.load);
+
+    unomiTracker.page();
+})();
+
+},{}]},{},[1]);
diff --git a/extensions/web-tracker/javascript/dist/snippet.min.js b/extensions/web-tracker/javascript/dist/snippet.min.js
new file mode 100644
index 000000000..376e4ec4e
--- /dev/null
+++ b/extensions/web-tracker/javascript/dist/snippet.min.js
@@ -0,0 +1,2 @@
+!function t(i,o,a){function c(r,e){if(!o[r]){if(!i[r]){var n="function"==typeof require&&require;if(!e&&n)return n(r,!0);if(u)return u(r,!0);throw(n=new Error("Cannot find module '"+r+"'")).code="MODULE_NOT_FOUND",n}n=o[r]={exports:{}},i[r][0].call(n.exports,function(e){return c(i[r][1][e]||e)},n,n.exports,t,i,o,a)}return o[r].exports}for(var u="function"==typeof require&&require,e=0;e<a.length;e++)c(a[e]);return c}({1:[function(e,r,n){window.unomiTracker||(window.unomiTracker={}),functi [...]
+//# sourceMappingURL=snipper.min.js.map
\ No newline at end of file
diff --git a/extensions/web-tracker/javascript/dist/snippet.min.js.map b/extensions/web-tracker/javascript/dist/snippet.min.js.map
new file mode 100644
index 000000000..2546eac71
--- /dev/null
+++ b/extensions/web-tracker/javascript/dist/snippet.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["snippet.js"],"names":["r","e","n","t","o","i","f","c","require","u","a","Error","code","p","exports","call","length","1","module","window","unomiTracker","unomiTracker_queue","methods","method","args","Array","prototype","slice","arguments","unshift","push","factory","callback","initialize","Apache Unomi","unomiOption","item","shift","apply","load","script","document","createElement","type","async","src","url","addEventListener","onreadystatechange","this","ready [...]
\ No newline at end of file
diff --git a/extensions/web-tracker/javascript/dist/unomi-tracker.js b/extensions/web-tracker/javascript/dist/unomi-tracker.js
new file mode 100644
index 000000000..340cb8e58
--- /dev/null
+++ b/extensions/web-tracker/javascript/dist/unomi-tracker.js
@@ -0,0 +1,16228 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @license Apache-2.0
+ */
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.unomiTracker = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)ret [...]
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var arity = require('@ndhoule/arity');
+
+var objToString = Object.prototype.toString;
+
+/**
+ * Determine if a value is a function.
+ *
+ * @param {*} val
+ * @return {boolean}
+ */
+// TODO: Move to lib
+var isFunction = function(val) {
+  return typeof val === 'function';
+};
+
+/**
+ * Determine if a value is a number.
+ *
+ * @param {*} val
+ * @return {boolean}
+ */
+// TODO: Move to lib
+var isNumber = function(val) {
+  var type = typeof val;
+  return type === 'number' || (type === 'object' && objToString.call(val) === '[object Number]');
+};
+
+/**
+ * Wrap a function `fn` in a function that will invoke `fn` when invoked `n` or
+ * more times.
+ *
+ * @name after
+ * @api public
+ * @category Function
+ * @param {Number} n The number of
+ * @param {Function} fn The function to wrap.
+ * @return {Function} A function that will call `fn` after `n` or more
+ * invocations.
+ * @example
+ */
+var after = function after(n, fn) {
+  if (!isNumber(n)) {
+    throw new TypeError('Expected a number but received ' + typeof n);
+  }
+
+  if (!isFunction(fn)) {
+    throw new TypeError('Expected a function but received ' + typeof fn);
+  }
+
+  var callCount = 0;
+
+  return arity(fn.length, function() {
+    callCount += 1;
+
+    if (callCount < n) {
+      return;
+    }
+
+    return fn.apply(this, arguments);
+  });
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = after;
+
+},{"@ndhoule/arity":2}],2:[function(require,module,exports){
+'use strict';
+
+var objToString = Object.prototype.toString;
+
+/**
+ * Determine if a value is a function.
+ *
+ * @param {*} val
+ * @return {boolean}
+ */
+// TODO: Move to lib
+var isFunction = function(val) {
+  return typeof val === 'function';
+};
+
+/**
+ * Determine if a value is a number.
+ *
+ * @param {*} val
+ * @return {boolean}
+ */
+// TODO: Move to lib
+var isNumber = function(val) {
+  var type = typeof val;
+  return type === 'number' || (type === 'object' && objToString.call(val) === '[object Number]');
+};
+
+ /**
+  * Creates an array of generic, numbered argument names.
+  *
+  * @name createParams
+  * @api private
+  * @param {number} n
+  * @return {Array}
+  * @example
+  * argNames(2);
+  * //=> ['arg1', 'arg2']
+  */
+var createParams = function createParams(n) {
+  var args = [];
+
+  for (var i = 1; i <= n; i += 1) {
+    args.push('arg' + i);
+  }
+
+  return args;
+};
+
+ /**
+  * Dynamically construct a wrapper function of `n` arity that.
+  *
+  * If at all possible, prefer a function from the arity wrapper cache above to
+  * avoid allocating a new function at runtime.
+  *
+  * @name createArityWrapper
+  * @api private
+  * @param {number} n
+  * @return {Function(Function)}
+  */
+var createArityWrapper = function createArityWrapper(n) {
+  var paramNames = createParams(n).join(', ');
+  var wrapperBody = ''.concat(
+    '  return function(', paramNames, ') {\n',
+    '    return func.apply(this, arguments);\n',
+    '  };'
+  );
+
+  /* eslint-disable no-new-func */
+  return new Function('func', wrapperBody);
+  /* eslint-enable no-new-func */
+};
+
+// Cache common arity wrappers to avoid constructing them at runtime
+var arityWrapperCache = [
+  /* eslint-disable no-unused-vars */
+  function(fn) {
+    return function() {
+      return fn.apply(this, arguments);
+    };
+  },
+
+  function(fn) {
+    return function(arg1) {
+      return fn.apply(this, arguments);
+    };
+  },
+
+  function(fn) {
+    return function(arg1, arg2) {
+      return fn.apply(this, arguments);
+    };
+  },
+
+  function(fn) {
+    return function(arg1, arg2, arg3) {
+      return fn.apply(this, arguments);
+    };
+  },
+
+  function(fn) {
+    return function(arg1, arg2, arg3, arg4) {
+      return fn.apply(this, arguments);
+    };
+  },
+
+  function(fn) {
+    return function(arg1, arg2, arg3, arg4, arg5) {
+      return fn.apply(this, arguments);
+    };
+  }
+  /* eslint-enable no-unused-vars */
+];
+
+/**
+ * Takes a function and an [arity](https://en.wikipedia.org/wiki/Arity) `n`, and returns a new
+ * function that expects `n` arguments.
+ *
+ * @name arity
+ * @api public
+ * @category Function
+ * @see {@link curry}
+ * @param {Number} n The desired arity of the returned function.
+ * @param {Function} fn The function to wrap.
+ * @return {Function} A function of n arity, wrapping `fn`.
+ * @example
+ * var add = function(a, b) {
+ *   return a + b;
+ * };
+ *
+ * // Check the number of arguments this function expects by accessing `.length`:
+ * add.length;
+ * //=> 2
+ *
+ * var unaryAdd = arity(1, add);
+ * unaryAdd.length;
+ * //=> 1
+ */
+var arity = function arity(n, func) {
+  if (!isFunction(func)) {
+    throw new TypeError('Expected a function but got ' + typeof func);
+  }
+
+  n = Math.max(isNumber(n) ? n : 0, 0);
+
+  if (!arityWrapperCache[n]) {
+    arityWrapperCache[n] = createArityWrapper(n);
+  }
+
+  return arityWrapperCache[n](func);
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = arity;
+
+},{}],3:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var type = require('component-type');
+
+/**
+ * Deeply clone an object.
+ *
+ * @param {*} obj Any object.
+ */
+
+var clone = function clone(obj) {
+  var t = type(obj);
+
+  if (t === 'object') {
+    var copy = {};
+    for (var key in obj) {
+      if (obj.hasOwnProperty(key)) {
+        copy[key] = clone(obj[key]);
+      }
+    }
+    return copy;
+  }
+
+  if (t === 'array') {
+    var copy = new Array(obj.length);
+    for (var i = 0, l = obj.length; i < l; i++) {
+      copy[i] = clone(obj[i]);
+    }
+    return copy;
+  }
+
+  if (t === 'regexp') {
+    // from millermedeiros/amd-utils - MIT
+    var flags = '';
+    flags += obj.multiline ? 'm' : '';
+    flags += obj.global ? 'g' : '';
+    flags += obj.ignoreCase ? 'i' : '';
+    return new RegExp(obj.source, flags);
+  }
+
+  if (t === 'date') {
+    return new Date(obj.getTime());
+  }
+
+  // string, number, boolean, etc.
+  return obj;
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = clone;
+
+},{"component-type":57}],4:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var drop = require('@ndhoule/drop');
+var rest = require('@ndhoule/rest');
+
+var has = Object.prototype.hasOwnProperty;
+var objToString = Object.prototype.toString;
+
+/**
+ * Returns `true` if a value is an object, otherwise `false`.
+ *
+ * @name isObject
+ * @api private
+ * @param {*} val The value to test.
+ * @return {boolean}
+ */
+// TODO: Move to a library
+var isObject = function isObject(value) {
+  return Boolean(value) && typeof value === 'object';
+};
+
+/**
+ * Returns `true` if a value is a plain object, otherwise `false`.
+ *
+ * @name isPlainObject
+ * @api private
+ * @param {*} val The value to test.
+ * @return {boolean}
+ */
+// TODO: Move to a library
+var isPlainObject = function isPlainObject(value) {
+  return Boolean(value) && objToString.call(value) === '[object Object]';
+};
+
+/**
+ * Assigns a key-value pair to a target object when the value assigned is owned,
+ * and where target[key] is undefined.
+ *
+ * @name shallowCombiner
+ * @api private
+ * @param {Object} target
+ * @param {Object} source
+ * @param {*} value
+ * @param {string} key
+ */
+var shallowCombiner = function shallowCombiner(target, source, value, key) {
+  if (has.call(source, key) && target[key] === undefined) {
+    target[key] = value;
+  }
+  return source;
+};
+
+/**
+ * Assigns a key-value pair to a target object when the value assigned is owned,
+ * and where target[key] is undefined; also merges objects recursively.
+ *
+ * @name deepCombiner
+ * @api private
+ * @param {Object} target
+ * @param {Object} source
+ * @param {*} value
+ * @param {string} key
+ * @return {Object}
+ */
+var deepCombiner = function(target, source, value, key) {
+  if (has.call(source, key)) {
+    if (isPlainObject(target[key]) && isPlainObject(value)) {
+        target[key] = defaultsDeep(target[key], value);
+    } else if (target[key] === undefined) {
+        target[key] = value;
+    }
+  }
+
+  return source;
+};
+
+/**
+ * TODO: Document
+ *
+ * @name defaultsWith
+ * @api private
+ * @param {Function} combiner
+ * @param {Object} target
+ * @param {...Object} sources
+ * @return {Object} Return the input `target`.
+ */
+var defaultsWith = function(combiner, target /*, ...sources */) {
+  if (!isObject(target)) {
+    return target;
+  }
+
+  combiner = combiner || shallowCombiner;
+  var sources = drop(2, arguments);
+
+  for (var i = 0; i < sources.length; i += 1) {
+    for (var key in sources[i]) {
+      combiner(target, sources[i], sources[i][key], key);
+    }
+  }
+
+  return target;
+};
+
+/**
+ * Copies owned, enumerable properties from a source object(s) to a target
+ * object when the value of that property on the source object is `undefined`.
+ * Recurses on objects.
+ *
+ * @name defaultsDeep
+ * @api public
+ * @param {Object} target
+ * @param {...Object} sources
+ * @return {Object} The input `target`.
+ */
+var defaultsDeep = function defaultsDeep(target /*, sources */) {
+  // TODO: Replace with `partial` call?
+  return defaultsWith.apply(null, [deepCombiner, target].concat(rest(arguments)));
+};
+
+/**
+ * Copies owned, enumerable properties from a source object(s) to a target
+ * object when the value of that property on the source object is `undefined`.
+ *
+ * @name defaults
+ * @api public
+ * @param {Object} target
+ * @param {...Object} sources
+ * @return {Object}
+ * @example
+ * var a = { a: 1 };
+ * var b = { a: 2, b: 2 };
+ *
+ * defaults(a, b);
+ * console.log(a); //=> { a: 1, b: 2 }
+ */
+var defaults = function(target /*, ...sources */) {
+  // TODO: Replace with `partial` call?
+  return defaultsWith.apply(null, [null, target].concat(rest(arguments)));
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = defaults;
+module.exports.deep = defaultsDeep;
+
+},{"@ndhoule/drop":5,"@ndhoule/rest":14}],5:[function(require,module,exports){
+'use strict';
+
+var max = Math.max;
+
+/**
+ * Produce a new array composed of all but the first `n` elements of an input `collection`.
+ *
+ * @name drop
+ * @api public
+ * @param {number} count The number of elements to drop.
+ * @param {Array} collection The collection to iterate over.
+ * @return {Array} A new array containing all but the first element from `collection`.
+ * @example
+ * drop(0, [1, 2, 3]); // => [1, 2, 3]
+ * drop(1, [1, 2, 3]); // => [2, 3]
+ * drop(2, [1, 2, 3]); // => [3]
+ * drop(3, [1, 2, 3]); // => []
+ * drop(4, [1, 2, 3]); // => []
+ */
+var drop = function drop(count, collection) {
+  var length = collection ? collection.length : 0;
+
+  if (!length) {
+    return [];
+  }
+
+  // Preallocating an array *significantly* boosts performance when dealing with
+  // `arguments` objects on v8. For a summary, see:
+  // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
+  var toDrop = max(Number(count) || 0, 0);
+  var resultsLength = max(length - toDrop, 0);
+  var results = new Array(resultsLength);
+
+  for (var i = 0; i < resultsLength; i += 1) {
+    results[i] = collection[i + toDrop];
+  }
+
+  return results;
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = drop;
+
+},{}],6:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var keys = require('@ndhoule/keys');
+
+var objToString = Object.prototype.toString;
+
+/**
+ * Tests if a value is a number.
+ *
+ * @name isNumber
+ * @api private
+ * @param {*} val The value to test.
+ * @return {boolean} Returns `true` if `val` is a number, otherwise `false`.
+ */
+// TODO: Move to library
+var isNumber = function isNumber(val) {
+  var type = typeof val;
+  return type === 'number' || (type === 'object' && objToString.call(val) === '[object Number]');
+};
+
+/**
+ * Tests if a value is an array.
+ *
+ * @name isArray
+ * @api private
+ * @param {*} val The value to test.
+ * @return {boolean} Returns `true` if the value is an array, otherwise `false`.
+ */
+// TODO: Move to library
+var isArray = typeof Array.isArray === 'function' ? Array.isArray : function isArray(val) {
+  return objToString.call(val) === '[object Array]';
+};
+
+/**
+ * Tests if a value is array-like. Array-like means the value is not a function and has a numeric
+ * `.length` property.
+ *
+ * @name isArrayLike
+ * @api private
+ * @param {*} val
+ * @return {boolean}
+ */
+// TODO: Move to library
+var isArrayLike = function isArrayLike(val) {
+  return val != null && (isArray(val) || (val !== 'function' && isNumber(val.length)));
+};
+
+/**
+ * Internal implementation of `each`. Works on arrays and array-like data structures.
+ *
+ * @name arrayEach
+ * @api private
+ * @param {Function(value, key, collection)} iterator The function to invoke per iteration.
+ * @param {Array} array The array(-like) structure to iterate over.
+ * @return {undefined}
+ */
+var arrayEach = function arrayEach(iterator, array) {
+  for (var i = 0; i < array.length; i += 1) {
+    // Break iteration early if `iterator` returns `false`
+    if (iterator(array[i], i, array) === false) {
+      break;
+    }
+  }
+};
+
+/**
+ * Internal implementation of `each`. Works on objects.
+ *
+ * @name baseEach
+ * @api private
+ * @param {Function(value, key, collection)} iterator The function to invoke per iteration.
+ * @param {Object} object The object to iterate over.
+ * @return {undefined}
+ */
+var baseEach = function baseEach(iterator, object) {
+  var ks = keys(object);
+
+  for (var i = 0; i < ks.length; i += 1) {
+    // Break iteration early if `iterator` returns `false`
+    if (iterator(object[ks[i]], ks[i], object) === false) {
+      break;
+    }
+  }
+};
+
+/**
+ * Iterate over an input collection, invoking an `iterator` function for each element in the
+ * collection and passing to it three arguments: `(value, index, collection)`. The `iterator`
+ * function can end iteration early by returning `false`.
+ *
+ * @name each
+ * @api public
+ * @param {Function(value, key, collection)} iterator The function to invoke per iteration.
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @return {undefined} Because `each` is run only for side effects, always returns `undefined`.
+ * @example
+ * var log = console.log.bind(console);
+ *
+ * each(log, ['a', 'b', 'c']);
+ * //-> 'a', 0, ['a', 'b', 'c']
+ * //-> 'b', 1, ['a', 'b', 'c']
+ * //-> 'c', 2, ['a', 'b', 'c']
+ * //=> undefined
+ *
+ * each(log, 'tim');
+ * //-> 't', 2, 'tim'
+ * //-> 'i', 1, 'tim'
+ * //-> 'm', 0, 'tim'
+ * //=> undefined
+ *
+ * // Note: Iteration order not guaranteed across environments
+ * each(log, { name: 'tim', occupation: 'enchanter' });
+ * //-> 'tim', 'name', { name: 'tim', occupation: 'enchanter' }
+ * //-> 'enchanter', 'occupation', { name: 'tim', occupation: 'enchanter' }
+ * //=> undefined
+ */
+var each = function each(iterator, collection) {
+  return (isArrayLike(collection) ? arrayEach : baseEach).call(this, iterator, collection);
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = each;
+
+},{"@ndhoule/keys":11}],7:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var each = require('@ndhoule/each');
+
+/**
+ * Check if a predicate function returns `true` for all values in a `collection`.
+ * Checks owned, enumerable values and exits early when `predicate` returns
+ * `false`.
+ *
+ * @name every
+ * @param {Function} predicate The function used to test values.
+ * @param {Array|Object|string} collection The collection to search.
+ * @return {boolean} True if all values passes the predicate test, otherwise false.
+ * @example
+ * var isEven = function(num) { return num % 2 === 0; };
+ *
+ * every(isEven, []); // => true
+ * every(isEven, [1, 2]); // => false
+ * every(isEven, [2, 4, 6]); // => true
+ */
+var every = function every(predicate, collection) {
+  if (typeof predicate !== 'function') {
+    throw new TypeError('`predicate` must be a function but was a ' + typeof predicate);
+  }
+
+  var result = true;
+
+  each(function(val, key, collection) {
+    result = !!predicate(val, key, collection);
+
+    // Exit early
+    if (!result) {
+      return false;
+    }
+  }, collection);
+
+  return result;
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = every;
+
+},{"@ndhoule/each":6}],8:[function(require,module,exports){
+'use strict';
+
+var has = Object.prototype.hasOwnProperty;
+
+/**
+ * Copy the properties of one or more `objects` onto a destination object. Input objects are iterated over
+ * in left-to-right order, so duplicate properties on later objects will overwrite those from
+ * erevious ones. Only enumerable and own properties of the input objects are copied onto the
+ * resulting object.
+ *
+ * @name extend
+ * @api public
+ * @category Object
+ * @param {Object} dest The destination object.
+ * @param {...Object} sources The source objects.
+ * @return {Object} `dest`, extended with the properties of all `sources`.
+ * @example
+ * var a = { a: 'a' };
+ * var b = { b: 'b' };
+ * var c = { c: 'c' };
+ *
+ * extend(a, b, c);
+ * //=> { a: 'a', b: 'b', c: 'c' };
+ */
+var extend = function extend(dest /*, sources */) {
+  var sources = Array.prototype.slice.call(arguments, 1);
+
+  for (var i = 0; i < sources.length; i += 1) {
+    for (var key in sources[i]) {
+      if (has.call(sources[i], key)) {
+        dest[key] = sources[i][key];
+      }
+    }
+  }
+
+  return dest;
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = extend;
+
+},{}],9:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var each = require('@ndhoule/each');
+
+/**
+ * Reduces all the values in a collection down into a single value. Does so by iterating through the
+ * collection from left to right, repeatedly calling an `iterator` function and passing to it four
+ * arguments: `(accumulator, value, index, collection)`.
+ *
+ * Returns the final return value of the `iterator` function.
+ *
+ * @name foldl
+ * @api public
+ * @param {Function} iterator The function to invoke per iteration.
+ * @param {*} accumulator The initial accumulator value, passed to the first invocation of `iterator`.
+ * @param {Array|Object} collection The collection to iterate over.
+ * @return {*} The return value of the final call to `iterator`.
+ * @example
+ * foldl(function(total, n) {
+ *   return total + n;
+ * }, 0, [1, 2, 3]);
+ * //=> 6
+ *
+ * var phonebook = { bob: '555-111-2345', tim: '655-222-6789', sheila: '655-333-1298' };
+ *
+ * foldl(function(results, phoneNumber) {
+ *  if (phoneNumber[0] === '6') {
+ *    return results.concat(phoneNumber);
+ *  }
+ *  return results;
+ * }, [], phonebook);
+ * // => ['655-222-6789', '655-333-1298']
+ */
+var foldl = function foldl(iterator, accumulator, collection) {
+  if (typeof iterator !== 'function') {
+    throw new TypeError('Expected a function but received a ' + typeof iterator);
+  }
+
+  each(function(val, i, collection) {
+    accumulator = iterator(accumulator, val, i, collection);
+  }, collection);
+
+  return accumulator;
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = foldl;
+
+},{"@ndhoule/each":6}],10:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var each = require('@ndhoule/each');
+
+var strIndexOf = String.prototype.indexOf;
+
+/**
+ * Object.is/sameValueZero polyfill.
+ *
+ * @api private
+ * @param {*} value1
+ * @param {*} value2
+ * @return {boolean}
+ */
+// TODO: Move to library
+var sameValueZero = function sameValueZero(value1, value2) {
+  // Normal values and check for 0 / -0
+  if (value1 === value2) {
+    return value1 !== 0 || 1 / value1 === 1 / value2;
+  }
+  // NaN
+  return value1 !== value1 && value2 !== value2;
+};
+
+/**
+ * Searches a given `collection` for a value, returning true if the collection
+ * contains the value and false otherwise. Can search strings, arrays, and
+ * objects.
+ *
+ * @name includes
+ * @api public
+ * @param {*} searchElement The element to search for.
+ * @param {Object|Array|string} collection The collection to search.
+ * @return {boolean}
+ * @example
+ * includes(2, [1, 2, 3]);
+ * //=> true
+ *
+ * includes(4, [1, 2, 3]);
+ * //=> false
+ *
+ * includes(2, { a: 1, b: 2, c: 3 });
+ * //=> true
+ *
+ * includes('a', { a: 1, b: 2, c: 3 });
+ * //=> false
+ *
+ * includes('abc', 'xyzabc opq');
+ * //=> true
+ *
+ * includes('nope', 'xyzabc opq');
+ * //=> false
+ */
+var includes = function includes(searchElement, collection) {
+  var found = false;
+
+  // Delegate to String.prototype.indexOf when `collection` is a string
+  if (typeof collection === 'string') {
+    return strIndexOf.call(collection, searchElement) !== -1;
+  }
+
+  // Iterate through enumerable/own array elements and object properties.
+  each(function(value) {
+    if (sameValueZero(value, searchElement)) {
+      found = true;
+      // Exit iteration early when found
+      return false;
+    }
+  }, collection);
+
+  return found;
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = includes;
+
+},{"@ndhoule/each":6}],11:[function(require,module,exports){
+'use strict';
+
+var hop = Object.prototype.hasOwnProperty;
+var strCharAt = String.prototype.charAt;
+var toStr = Object.prototype.toString;
+
+/**
+ * Returns the character at a given index.
+ *
+ * @param {string} str
+ * @param {number} index
+ * @return {string|undefined}
+ */
+// TODO: Move to a library
+var charAt = function(str, index) {
+  return strCharAt.call(str, index);
+};
+
+/**
+ * hasOwnProperty, wrapped as a function.
+ *
+ * @name has
+ * @api private
+ * @param {*} context
+ * @param {string|number} prop
+ * @return {boolean}
+ */
+
+// TODO: Move to a library
+var has = function has(context, prop) {
+  return hop.call(context, prop);
+};
+
+/**
+ * Returns true if a value is a string, otherwise false.
+ *
+ * @name isString
+ * @api private
+ * @param {*} val
+ * @return {boolean}
+ */
+
+// TODO: Move to a library
+var isString = function isString(val) {
+  return toStr.call(val) === '[object String]';
+};
+
+/**
+ * Returns true if a value is array-like, otherwise false. Array-like means a
+ * value is not null, undefined, or a function, and has a numeric `length`
+ * property.
+ *
+ * @name isArrayLike
+ * @api private
+ * @param {*} val
+ * @return {boolean}
+ */
+// TODO: Move to a library
+var isArrayLike = function isArrayLike(val) {
+  return val != null && (typeof val !== 'function' && typeof val.length === 'number');
+};
+
+
+/**
+ * indexKeys
+ *
+ * @name indexKeys
+ * @api private
+ * @param {} target
+ * @param {Function} pred
+ * @return {Array}
+ */
+var indexKeys = function indexKeys(target, pred) {
+  pred = pred || has;
+
+  var results = [];
+
+  for (var i = 0, len = target.length; i < len; i += 1) {
+    if (pred(target, i)) {
+      results.push(String(i));
+    }
+  }
+
+  return results;
+};
+
+/**
+ * Returns an array of an object's owned keys.
+ *
+ * @name objectKeys
+ * @api private
+ * @param {*} target
+ * @param {Function} pred Predicate function used to include/exclude values from
+ * the resulting array.
+ * @return {Array}
+ */
+var objectKeys = function objectKeys(target, pred) {
+  pred = pred || has;
+
+  var results = [];
+
+  for (var key in target) {
+    if (pred(target, key)) {
+      results.push(String(key));
+    }
+  }
+
+  return results;
+};
+
+/**
+ * Creates an array composed of all keys on the input object. Ignores any non-enumerable properties.
+ * More permissive than the native `Object.keys` function (non-objects will not throw errors).
+ *
+ * @name keys
+ * @api public
+ * @category Object
+ * @param {Object} source The value to retrieve keys from.
+ * @return {Array} An array containing all the input `source`'s keys.
+ * @example
+ * keys({ likes: 'avocado', hates: 'pineapple' });
+ * //=> ['likes', 'pineapple'];
+ *
+ * // Ignores non-enumerable properties
+ * var hasHiddenKey = { name: 'Tim' };
+ * Object.defineProperty(hasHiddenKey, 'hidden', {
+ *   value: 'i am not enumerable!',
+ *   enumerable: false
+ * })
+ * keys(hasHiddenKey);
+ * //=> ['name'];
+ *
+ * // Works on arrays
+ * keys(['a', 'b', 'c']);
+ * //=> ['0', '1', '2']
+ *
+ * // Skips unpopulated indices in sparse arrays
+ * var arr = [1];
+ * arr[4] = 4;
+ * keys(arr);
+ * //=> ['0', '4']
+ */
+var keys = function keys(source) {
+  if (source == null) {
+    return [];
+  }
+
+  // IE6-8 compatibility (string)
+  if (isString(source)) {
+    return indexKeys(source, charAt);
+  }
+
+  // IE6-8 compatibility (arguments)
+  if (isArrayLike(source)) {
+    return indexKeys(source, has);
+  }
+
+  return objectKeys(source);
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = keys;
+
+},{}],12:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var each = require('@ndhoule/each');
+
+/**
+ * Produce a new array by passing each value in the input `collection` through a transformative
+ * `iterator` function. The `iterator` function is passed three arguments:
+ * `(value, index, collection)`.
+ *
+ * @name map
+ * @api public
+ * @param {Function} iterator The transformer function to invoke per iteration.
+ * @param {Array} collection The collection to iterate over.
+ * @return {Array} A new array containing the results of each `iterator` invocation.
+ * @example
+ * var square = function(x) { return x * x; };
+ *
+ * map(square, [1, 2, 3]);
+ * //=> [1, 4, 9]
+ */
+var map = function map(iterator, collection) {
+  if (typeof iterator !== 'function') {
+    throw new TypeError('Expected a function but received a ' + typeof iterator);
+  }
+
+  var result = [];
+
+  each(function(val, i, collection) {
+    result.push(iterator(val, i, collection));
+  }, collection);
+
+  return result;
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = map;
+
+},{"@ndhoule/each":6}],13:[function(require,module,exports){
+'use strict';
+
+var objToString = Object.prototype.toString;
+
+// TODO: Move to lib
+var existy = function(val) {
+  return val != null;
+};
+
+// TODO: Move to lib
+var isArray = function(val) {
+  return objToString.call(val) === '[object Array]';
+};
+
+// TODO: Move to lib
+var isString = function(val) {
+   return typeof val === 'string' || objToString.call(val) === '[object String]';
+};
+
+// TODO: Move to lib
+var isObject = function(val) {
+  return val != null && typeof val === 'object';
+};
+
+/**
+ * Returns a copy of the new `object` containing only the specified properties.
+ *
+ * @name pick
+ * @api public
+ * @param {string|string[]} props The property or properties to keep.
+ * @param {Object} object The object to iterate over.
+ * @return {Object} A new object containing only the specified properties from `object`.
+ * @example
+ * var person = { name: 'Tim', occupation: 'enchanter', fears: 'rabbits' };
+ *
+ * pick('name', person);
+ * //=> { name: 'Tim' }
+ *
+ * pick(['name', 'fears'], person);
+ * //=> { name: 'Tim', fears: 'rabbits' }
+ */
+var pick = function pick(props, object) {
+  if (!existy(object) || !isObject(object)) {
+    return {};
+  }
+
+  if (isString(props)) {
+    props = [props];
+  }
+
+  if (!isArray(props)) {
+    props = [];
+  }
+
+  var result = {};
+
+  for (var i = 0; i < props.length; i += 1) {
+    if (isString(props[i]) && props[i] in object) {
+      result[props[i]] = object[props[i]];
+    }
+  }
+
+  return result;
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = pick;
+
+},{}],14:[function(require,module,exports){
+'use strict';
+
+var max = Math.max;
+
+/**
+ * Produce a new array by passing each value in the input `collection` through a transformative
+ * `iterator` function. The `iterator` function is passed three arguments:
+ * `(value, index, collection)`.
+ *
+ * @name rest
+ * @api public
+ * @param {Array} collection The collection to iterate over.
+ * @return {Array} A new array containing all but the first element from `collection`.
+ * @example
+ * rest([1, 2, 3]); // => [2, 3]
+ */
+var rest = function rest(collection) {
+  if (collection == null || !collection.length) {
+    return [];
+  }
+
+  // Preallocating an array *significantly* boosts performance when dealing with
+  // `arguments` objects on v8. For a summary, see:
+  // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
+  var results = new Array(max(collection.length - 2, 0));
+
+  for (var i = 1; i < collection.length; i += 1) {
+    results[i - 1] = collection[i];
+  }
+
+  return results;
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = rest;
+
+},{}],15:[function(require,module,exports){
+(function (global){(function (){
+'use strict';
+
+var _analytics = global.analytics;
+
+/*
+ * Module dependencies.
+ */
+
+var Alias = require('segmentio-facade').Alias;
+var Emitter = require('component-emitter');
+var Facade = require('segmentio-facade');
+var Group = require('segmentio-facade').Group;
+var Identify = require('segmentio-facade').Identify;
+var SourceMiddlewareChain = require('./middleware').SourceMiddlewareChain;
+var IntegrationMiddlewareChain = require('./middleware')
+  .IntegrationMiddlewareChain;
+var Page = require('segmentio-facade').Page;
+var Track = require('segmentio-facade').Track;
+var bindAll = require('bind-all');
+var clone = require('@ndhoule/clone');
+var extend = require('extend');
+var cookie = require('./cookie');
+var metrics = require('./metrics');
+var debug = require('debug');
+var defaults = require('@ndhoule/defaults');
+var each = require('@ndhoule/each');
+var foldl = require('@ndhoule/foldl');
+var group = require('./group');
+var is = require('is');
+var isMeta = require('@segment/is-meta');
+var keys = require('@ndhoule/keys');
+var memory = require('./memory');
+var nextTick = require('next-tick');
+var normalize = require('./normalize');
+var on = require('component-event').bind;
+var pageDefaults = require('./pageDefaults');
+var pick = require('@ndhoule/pick');
+var prevent = require('@segment/prevent-default');
+var querystring = require('component-querystring');
+var store = require('./store');
+var user = require('./user');
+var type = require('component-type');
+
+/**
+ * Initialize a new `Analytics` instance.
+ */
+
+function Analytics() {
+  this._options({});
+  this.Integrations = {};
+  this._sourceMiddlewares = new SourceMiddlewareChain();
+  this._integrationMiddlewares = new IntegrationMiddlewareChain();
+  this._integrations = {};
+  this._readied = false;
+  this._timeout = 300;
+  // XXX: BACKWARDS COMPATIBILITY
+  this._user = user;
+  this.log = debug('analytics.js');
+  bindAll(this);
+
+  var self = this;
+  this.on('initialize', function(settings, options) {
+    if (options.initialPageview) self.page();
+    self._parseQuery(window.location.search);
+  });
+}
+
+/**
+ * Mix in event emitter.
+ */
+
+Emitter(Analytics.prototype);
+
+/**
+ * Use a `plugin`.
+ *
+ * @param {Function} plugin
+ * @return {Analytics}
+ */
+
+Analytics.prototype.use = function(plugin) {
+  plugin(this);
+  return this;
+};
+
+/**
+ * Define a new `Integration`.
+ *
+ * @param {Function} Integration
+ * @return {Analytics}
+ */
+
+Analytics.prototype.addIntegration = function(Integration) {
+  var name = Integration.prototype.name;
+  if (!name) throw new TypeError('attempted to add an invalid integration');
+  this.Integrations[name] = Integration;
+  return this;
+};
+
+/**
+ * Define a new `SourceMiddleware`
+ *
+ * @param {Function} Middleware
+ * @return {Analytics}
+ */
+
+Analytics.prototype.addSourceMiddleware = function(middleware) {
+  if (this.initialized)
+    throw new Error(
+      'attempted to add a source middleware after initialization'
+    );
+
+  this._sourceMiddlewares.add(middleware);
+  return this;
+};
+
+/**
+ * Define a new `IntegrationMiddleware`
+ *
+ * @param {Function} Middleware
+ * @return {Analytics}
+ */
+
+Analytics.prototype.addIntegrationMiddleware = function(middleware) {
+  if (this.initialized)
+    throw new Error(
+      'attempted to add an integration middleware after initialization'
+    );
+
+  this._integrationMiddlewares.add(middleware);
+  return this;
+};
+
+/**
+ * Initialize with the given integration `settings` and `options`.
+ *
+ * Aliased to `init` for convenience.
+ *
+ * @param {Object} [settings={}]
+ * @param {Object} [options={}]
+ * @return {Analytics}
+ */
+
+Analytics.prototype.init = Analytics.prototype.initialize = function(
+  settings,
+  options
+) {
+  settings = settings || {};
+  options = options || {};
+
+  this._options(options);
+  this._readied = false;
+
+  // clean unknown integrations from settings
+  var self = this;
+  each(function(opts, name) {
+    var Integration = self.Integrations[name];
+    if (!Integration) delete settings[name];
+  }, settings);
+
+  // add integrations
+  each(function(opts, name) {
+    // Don't load disabled integrations
+    if (options.integrations) {
+      if (
+        options.integrations[name] === false ||
+        (options.integrations.All === false && !options.integrations[name])
+      ) {
+        return;
+      }
+    }
+
+    var Integration = self.Integrations[name];
+    var clonedOpts = {};
+    extend(true, clonedOpts, opts); // deep clone opts
+    var integration = new Integration(clonedOpts);
+    self.log('initialize %o - %o', name, opts);
+    self.add(integration);
+  }, settings);
+
+  var integrations = this._integrations;
+
+  // load user now that options are set
+  user.load();
+  group.load();
+
+  // make ready callback
+  var readyCallCount = 0;
+  var integrationCount = keys(integrations).length;
+  var ready = function() {
+    readyCallCount++;
+    if (readyCallCount >= integrationCount) {
+      self._readied = true;
+      self.emit('ready');
+    }
+  };
+
+  // init if no integrations
+  if (integrationCount <= 0) {
+    ready();
+  }
+
+  // initialize integrations, passing ready
+  // create a list of any integrations that did not initialize - this will be passed with all events for replay support:
+  this.failedInitializations = [];
+  var initialPageSkipped = false;
+  each(function(integration) {
+    if (
+      options.initialPageview &&
+      integration.options.initialPageview === false
+    ) {
+      // We've assumed one initial pageview, so make sure we don't count the first page call.
+      var page = integration.page;
+      integration.page = function() {
+        if (initialPageSkipped) {
+          return page.apply(this, arguments);
+        }
+        initialPageSkipped = true;
+        return;
+      };
+    }
+
+    integration.analytics = self;
+
+    integration.once('ready', ready);
+    try {
+      metrics.increment('analytics_js.integration.invoke', {
+        method: 'initialize',
+        integration_name: integration.name
+      });
+      integration.initialize();
+    } catch (e) {
+      var integrationName = integration.name;
+      metrics.increment('analytics_js.integration.invoke.error', {
+        method: 'initialize',
+        integration_name: integration.name
+      });
+      self.failedInitializations.push(integrationName);
+      self.log('Error initializing %s integration: %o', integrationName, e);
+      // Mark integration as ready to prevent blocking of anyone listening to analytics.ready()
+
+      integration.ready();
+    }
+  }, integrations);
+
+  // backwards compat with angular plugin and used for init logic checks
+  this.initialized = true;
+
+  this.emit('initialize', settings, options);
+  return this;
+};
+
+/**
+ * Set the user's `id`.
+ *
+ * @param {Mixed} id
+ */
+
+Analytics.prototype.setAnonymousId = function(id) {
+  this.user().anonymousId(id);
+  return this;
+};
+
+/**
+ * Add an integration.
+ *
+ * @param {Integration} integration
+ */
+
+Analytics.prototype.add = function(integration) {
+  this._integrations[integration.name] = integration;
+  return this;
+};
+
+/**
+ * Identify a user by optional `id` and `traits`.
+ *
+ * @param {string} [id=user.id()] User ID.
+ * @param {Object} [traits=null] User traits.
+ * @param {Object} [options=null]
+ * @param {Function} [fn]
+ * @return {Analytics}
+ */
+
+Analytics.prototype.identify = function(id, traits, options, fn) {
+  // Argument reshuffling.
+  /* eslint-disable no-unused-expressions, no-sequences */
+  if (is.fn(options)) (fn = options), (options = null);
+  if (is.fn(traits)) (fn = traits), (options = null), (traits = null);
+  if (is.object(id)) (options = traits), (traits = id), (id = user.id());
+  /* eslint-enable no-unused-expressions, no-sequences */
+
+  // clone traits before we manipulate so we don't do anything uncouth, and take
+  // from `user` so that we carryover anonymous traits
+  user.identify(id, traits);
+
+  var msg = this.normalize({
+    options: options,
+    traits: user.traits(),
+    userId: user.id()
+  });
+
+  // Add the initialize integrations so the server-side ones can be disabled too
+  if (this.options.integrations) {
+    defaults(msg.integrations, this.options.integrations);
+  }
+
+  this._invoke('identify', new Identify(msg));
+
+  // emit
+  this.emit('identify', id, traits, options);
+  this._callback(fn);
+  return this;
+};
+
+/**
+ * Return the current user.
+ *
+ * @return {Object}
+ */
+
+Analytics.prototype.user = function() {
+  return user;
+};
+
+/**
+ * Identify a group by optional `id` and `traits`. Or, if no arguments are
+ * supplied, return the current group.
+ *
+ * @param {string} [id=group.id()] Group ID.
+ * @param {Object} [traits=null] Group traits.
+ * @param {Object} [options=null]
+ * @param {Function} [fn]
+ * @return {Analytics|Object}
+ */
+
+Analytics.prototype.group = function(id, traits, options, fn) {
+  /* eslint-disable no-unused-expressions, no-sequences */
+  if (!arguments.length) return group;
+  if (is.fn(options)) (fn = options), (options = null);
+  if (is.fn(traits)) (fn = traits), (options = null), (traits = null);
+  if (is.object(id)) (options = traits), (traits = id), (id = group.id());
+  /* eslint-enable no-unused-expressions, no-sequences */
+
+  // grab from group again to make sure we're taking from the source
+  group.identify(id, traits);
+
+  var msg = this.normalize({
+    options: options,
+    traits: group.traits(),
+    groupId: group.id()
+  });
+
+  // Add the initialize integrations so the server-side ones can be disabled too
+  if (this.options.integrations) {
+    defaults(msg.integrations, this.options.integrations);
+  }
+
+  this._invoke('group', new Group(msg));
+
+  this.emit('group', id, traits, options);
+  this._callback(fn);
+  return this;
+};
+
+/**
+ * Track an `event` that a user has triggered with optional `properties`.
+ *
+ * @param {string} event
+ * @param {Object} [properties=null]
+ * @param {Object} [options=null]
+ * @param {Function} [fn]
+ * @return {Analytics}
+ */
+
+Analytics.prototype.track = function(event, properties, options, fn) {
+  // Argument reshuffling.
+  /* eslint-disable no-unused-expressions, no-sequences */
+  if (is.fn(options)) (fn = options), (options = null);
+  if (is.fn(properties))
+    (fn = properties), (options = null), (properties = null);
+  /* eslint-enable no-unused-expressions, no-sequences */
+
+  // figure out if the event is archived.
+  var plan = this.options.plan || {};
+  var events = plan.track || {};
+  var planIntegrationOptions = {};
+
+  // normalize
+  var msg = this.normalize({
+    properties: properties,
+    options: options,
+    event: event
+  });
+
+  // plan.
+  plan = events[event];
+  if (plan) {
+    this.log('plan %o - %o', event, plan);
+    if (plan.enabled === false) {
+      // Disabled events should always be sent to Segment.
+      planIntegrationOptions = { All: false, 'Segment.io': true };
+    } else {
+      planIntegrationOptions = plan.integrations || {};
+    }
+  } else {
+    var defaultPlan = events.__default || { enabled: true };
+    if (!defaultPlan.enabled) {
+      // Disabled events should always be sent to Segment.
+      planIntegrationOptions = { All: false, 'Segment.io': true };
+    }
+  }
+
+  // Add the initialize integrations so the server-side ones can be disabled too
+  defaults(
+    msg.integrations,
+    this._mergeInitializeAndPlanIntegrations(planIntegrationOptions)
+  );
+
+  this._invoke('track', new Track(msg));
+
+  this.emit('track', event, properties, options);
+  this._callback(fn);
+  return this;
+};
+
+/**
+ * Helper method to track an outbound link that would normally navigate away
+ * from the page before the analytics calls were sent.
+ *
+ * BACKWARDS COMPATIBILITY: aliased to `trackClick`.
+ *
+ * @param {Element|Array} links
+ * @param {string|Function} event
+ * @param {Object|Function} properties (optional)
+ * @return {Analytics}
+ */
+
+Analytics.prototype.trackClick = Analytics.prototype.trackLink = function(
+  links,
+  event,
+  properties
+) {
+  if (!links) return this;
+  // always arrays, handles jquery
+  if (type(links) === 'element') links = [links];
+
+  var self = this;
+  each(function(el) {
+    if (type(el) !== 'element') {
+      throw new TypeError('Must pass HTMLElement to `analytics.trackLink`.');
+    }
+    on(el, 'click', function(e) {
+      var ev = is.fn(event) ? event(el) : event;
+      var props = is.fn(properties) ? properties(el) : properties;
+      var href =
+        el.getAttribute('href') ||
+        el.getAttributeNS('http://www.w3.org/1999/xlink', 'href') ||
+        el.getAttribute('xlink:href');
+
+      self.track(ev, props);
+
+      if (href && el.target !== '_blank' && !isMeta(e)) {
+        prevent(e);
+        self._callback(function() {
+          window.location.href = href;
+        });
+      }
+    });
+  }, links);
+
+  return this;
+};
+
+/**
+ * Helper method to track an outbound form that would normally navigate away
+ * from the page before the analytics calls were sent.
+ *
+ * BACKWARDS COMPATIBILITY: aliased to `trackSubmit`.
+ *
+ * @param {Element|Array} forms
+ * @param {string|Function} event
+ * @param {Object|Function} properties (optional)
+ * @return {Analytics}
+ */
+
+Analytics.prototype.trackSubmit = Analytics.prototype.trackForm = function(
+  forms,
+  event,
+  properties
+) {
+  if (!forms) return this;
+  // always arrays, handles jquery
+  if (type(forms) === 'element') forms = [forms];
+
+  var self = this;
+  each(function(el) {
+    if (type(el) !== 'element')
+      throw new TypeError('Must pass HTMLElement to `analytics.trackForm`.');
+    function handler(e) {
+      prevent(e);
+
+      var ev = is.fn(event) ? event(el) : event;
+      var props = is.fn(properties) ? properties(el) : properties;
+      self.track(ev, props);
+
+      self._callback(function() {
+        el.submit();
+      });
+    }
+
+    // Support the events happening through jQuery or Zepto instead of through
+    // the normal DOM API, because `el.submit` doesn't bubble up events...
+    var $ = window.jQuery || window.Zepto;
+    if ($) {
+      $(el).submit(handler);
+    } else {
+      on(el, 'submit', handler);
+    }
+  }, forms);
+
+  return this;
+};
+
+/**
+ * Trigger a pageview, labeling the current page with an optional `category`,
+ * `name` and `properties`.
+ *
+ * @param {string} [category]
+ * @param {string} [name]
+ * @param {Object|string} [properties] (or path)
+ * @param {Object} [options]
+ * @param {Function} [fn]
+ * @return {Analytics}
+ */
+
+Analytics.prototype.page = function(category, name, properties, options, fn) {
+  // Argument reshuffling.
+  /* eslint-disable no-unused-expressions, no-sequences */
+  if (is.fn(options)) (fn = options), (options = null);
+  if (is.fn(properties)) (fn = properties), (options = properties = null);
+  if (is.fn(name)) (fn = name), (options = properties = name = null);
+  if (type(category) === 'object')
+    (options = name), (properties = category), (name = category = null);
+  if (type(name) === 'object')
+    (options = properties), (properties = name), (name = null);
+  if (type(category) === 'string' && type(name) !== 'string')
+    (name = category), (category = null);
+  /* eslint-enable no-unused-expressions, no-sequences */
+
+  properties = clone(properties) || {};
+  if (name) properties.name = name;
+  if (category) properties.category = category;
+
+  // Ensure properties has baseline spec properties.
+  // TODO: Eventually move these entirely to `options.context.page`
+  var defs = pageDefaults();
+  defaults(properties, defs);
+
+  // Mirror user overrides to `options.context.page` (but exclude custom properties)
+  // (Any page defaults get applied in `this.normalize` for consistency.)
+  // Weird, yeah--moving special props to `context.page` will fix this in the long term.
+  var overrides = pick(keys(defs), properties);
+  if (!is.empty(overrides)) {
+    options = options || {};
+    options.context = options.context || {};
+    options.context.page = overrides;
+  }
+
+  var msg = this.normalize({
+    properties: properties,
+    category: category,
+    options: options,
+    name: name
+  });
+
+  // Add the initialize integrations so the server-side ones can be disabled too
+  if (this.options.integrations) {
+    defaults(msg.integrations, this.options.integrations);
+  }
+
+  this._invoke('page', new Page(msg));
+
+  this.emit('page', category, name, properties, options);
+  this._callback(fn);
+  return this;
+};
+
+/**
+ * FIXME: BACKWARDS COMPATIBILITY: convert an old `pageview` to a `page` call.
+ *
+ * @param {string} [url]
+ * @return {Analytics}
+ * @api private
+ */
+
+Analytics.prototype.pageview = function(url) {
+  var properties = {};
+  if (url) properties.path = url;
+  this.page(properties);
+  return this;
+};
+
+/**
+ * Merge two previously unassociated user identities.
+ *
+ * @param {string} to
+ * @param {string} from (optional)
+ * @param {Object} options (optional)
+ * @param {Function} fn (optional)
+ * @return {Analytics}
+ */
+
+Analytics.prototype.alias = function(to, from, options, fn) {
+  // Argument reshuffling.
+  /* eslint-disable no-unused-expressions, no-sequences */
+  if (is.fn(options)) (fn = options), (options = null);
+  if (is.fn(from)) (fn = from), (options = null), (from = null);
+  if (is.object(from)) (options = from), (from = null);
+  /* eslint-enable no-unused-expressions, no-sequences */
+
+  var msg = this.normalize({
+    options: options,
+    previousId: from,
+    userId: to
+  });
+
+  // Add the initialize integrations so the server-side ones can be disabled too
+  if (this.options.integrations) {
+    defaults(msg.integrations, this.options.integrations);
+  }
+
+  this._invoke('alias', new Alias(msg));
+
+  this.emit('alias', to, from, options);
+  this._callback(fn);
+  return this;
+};
+
+/**
+ * Register a `fn` to be fired when all the analytics services are ready.
+ *
+ * @param {Function} fn
+ * @return {Analytics}
+ */
+
+Analytics.prototype.ready = function(fn) {
+  if (is.fn(fn)) {
+    if (this._readied) {
+      nextTick(fn);
+    } else {
+      this.once('ready', fn);
+    }
+  }
+  return this;
+};
+
+/**
+ * Set the `timeout` (in milliseconds) used for callbacks.
+ *
+ * @param {Number} timeout
+ */
+
+Analytics.prototype.timeout = function(timeout) {
+  this._timeout = timeout;
+};
+
+/**
+ * Enable or disable debug.
+ *
+ * @param {string|boolean} str
+ */
+
+Analytics.prototype.debug = function(str) {
+  if (!arguments.length || str) {
+    debug.enable('analytics:' + (str || '*'));
+  } else {
+    debug.disable();
+  }
+};
+
+/**
+ * Apply options.
+ *
+ * @param {Object} options
+ * @return {Analytics}
+ * @api private
+ */
+
+Analytics.prototype._options = function(options) {
+  options = options || {};
+  this.options = options;
+  cookie.options(options.cookie);
+  metrics.options(options.metrics);
+  store.options(options.localStorage);
+  user.options(options.user);
+  group.options(options.group);
+  return this;
+};
+
+/**
+ * Callback a `fn` after our defined timeout period.
+ *
+ * @param {Function} fn
+ * @return {Analytics}
+ * @api private
+ */
+
+Analytics.prototype._callback = function(fn) {
+  if (is.fn(fn)) {
+    this._timeout ? setTimeout(fn, this._timeout) : nextTick(fn);
+  }
+  return this;
+};
+
+/**
+ * Call `method` with `facade` on all enabled integrations.
+ *
+ * @param {string} method
+ * @param {Facade} facade
+ * @return {Analytics}
+ * @api private
+ */
+
+Analytics.prototype._invoke = function(method, facade) {
+  var self = this;
+
+  try {
+    this._sourceMiddlewares.applyMiddlewares(
+      extend(true, new Facade({}), facade),
+      this._integrations,
+      function(result) {
+        // A nullified payload should not be sent.
+        if (result === null) {
+          self.log(
+            'Payload with method "%s" was null and dropped by source a middleware.',
+            method
+          );
+          return;
+        }
+
+        // Check if the payload is still a Facade. If not, convert it to one.
+        if (!(result instanceof Facade)) {
+          result = new Facade(result);
+        }
+
+        self.emit('invoke', result);
+        metrics.increment('analytics_js.invoke', {
+          method: method
+        });
+
+        applyIntegrationMiddlewares(result);
+      }
+    );
+  } catch (e) {
+    metrics.increment('analytics_js.invoke.error', {
+      method: method
+    });
+    self.log(
+      'Error invoking .%s method of %s integration: %o',
+      method,
+      name,
+      e
+    );
+  }
+
+  return this;
+
+  function applyIntegrationMiddlewares(facade) {
+    var failedInitializations = self.failedInitializations || [];
+    each(function(integration, name) {
+      var facadeCopy = extend(true, new Facade({}), facade);
+
+      if (!facadeCopy.enabled(name)) return;
+      // Check if an integration failed to initialize.
+      // If so, do not process the message as the integration is in an unstable state.
+      if (failedInitializations.indexOf(name) >= 0) {
+        self.log(
+          'Skipping invocation of .%s method of %s integration. Integration failed to initialize properly.',
+          method,
+          name
+        );
+      } else {
+        try {
+          // Apply any integration middlewares that exist, then invoke the integration with the result.
+          self._integrationMiddlewares.applyMiddlewares(
+            facadeCopy,
+            integration.name,
+            function(result) {
+              // A nullified payload should not be sent to an integration.
+              if (result === null) {
+                self.log(
+                  'Payload to integration "%s" was null and dropped by a middleware.',
+                  name
+                );
+                return;
+              }
+
+              // Check if the payload is still a Facade. If not, convert it to one.
+              if (!(result instanceof Facade)) {
+                result = new Facade(result);
+              }
+
+              metrics.increment('analytics_js.integration.invoke', {
+                method: method,
+                integration_name: integration.name
+              });
+
+              integration.invoke.call(integration, method, result);
+            }
+          );
+        } catch (e) {
+          metrics.increment('analytics_js.integration.invoke.error', {
+            method: method,
+            integration_name: integration.name
+          });
+          self.log(
+            'Error invoking .%s method of %s integration: %o',
+            method,
+            name,
+            e
+          );
+        }
+      }
+    }, self._integrations);
+  }
+};
+
+/**
+ * Push `args`.
+ *
+ * @param {Array} args
+ * @api private
+ */
+
+Analytics.prototype.push = function(args) {
+  var method = args.shift();
+  if (!this[method]) return;
+  this[method].apply(this, args);
+};
+
+/**
+ * Reset group and user traits and id's.
+ *
+ * @api public
+ */
+
+Analytics.prototype.reset = function() {
+  this.user().logout();
+  this.group().logout();
+};
+
+/**
+ * Parse the query string for callable methods.
+ *
+ * @param {String} query
+ * @return {Analytics}
+ * @api private
+ */
+
+Analytics.prototype._parseQuery = function(query) {
+  // Parse querystring to an object
+  var q = querystring.parse(query);
+  // Create traits and properties objects, populate from querysting params
+  var traits = pickPrefix('ajs_trait_', q);
+  var props = pickPrefix('ajs_prop_', q);
+  // Trigger based on callable parameters in the URL
+  if (q.ajs_uid) this.identify(q.ajs_uid, traits);
+  if (q.ajs_event) this.track(q.ajs_event, props);
+  if (q.ajs_aid) user.anonymousId(q.ajs_aid);
+  return this;
+
+  /**
+   * Create a shallow copy of an input object containing only the properties
+   * whose keys are specified by a prefix, stripped of that prefix
+   *
+   * @param {String} prefix
+   * @param {Object} object
+   * @return {Object}
+   * @api private
+   */
+
+  function pickPrefix(prefix, object) {
+    var length = prefix.length;
+    var sub;
+    return foldl(
+      function(acc, val, key) {
+        if (key.substr(0, length) === prefix) {
+          sub = key.substr(length);
+          acc[sub] = val;
+        }
+        return acc;
+      },
+      {},
+      object
+    );
+  }
+};
+
+/**
+ * Normalize the given `msg`.
+ *
+ * @param {Object} msg
+ * @return {Object}
+ */
+
+Analytics.prototype.normalize = function(msg) {
+  msg = normalize(msg, keys(this._integrations));
+  if (msg.anonymousId) user.anonymousId(msg.anonymousId);
+  msg.anonymousId = user.anonymousId();
+
+  // Ensure all outgoing requests include page data in their contexts.
+  msg.context.page = defaults(msg.context.page || {}, pageDefaults());
+
+  return msg;
+};
+
+/**
+ * Merges the tracking plan and initialization integration options.
+ *
+ * @param  {Object} planIntegrations Tracking plan integrations.
+ * @return {Object}                  The merged integrations.
+ */
+Analytics.prototype._mergeInitializeAndPlanIntegrations = function(
+  planIntegrations
+) {
+  // Do nothing if there are no initialization integrations
+  if (!this.options.integrations) {
+    return planIntegrations;
+  }
+
+  // Clone the initialization integrations
+  var integrations = extend({}, this.options.integrations);
+  var integrationName;
+
+  // Allow the tracking plan to disable integrations that were explicitly
+  // enabled on initialization
+  if (planIntegrations.All === false) {
+    integrations = { All: false };
+  }
+
+  for (integrationName in planIntegrations) {
+    if (planIntegrations.hasOwnProperty(integrationName)) {
+      // Don't allow the tracking plan to re-enable disabled integrations
+      if (this.options.integrations[integrationName] !== false) {
+        integrations[integrationName] = planIntegrations[integrationName];
+      }
+    }
+  }
+
+  return integrations;
+};
+
+/**
+ * No conflict support.
+ */
+
+Analytics.prototype.noConflict = function() {
+  window.analytics = _analytics;
+  return this;
+};
+
+/*
+ * Exports.
+ */
+
+module.exports = Analytics;
+module.exports.cookie = cookie;
+module.exports.memory = memory;
+module.exports.store = store;
+module.exports.metrics = metrics;
+
+}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./cookie":16,"./group":18,"./memory":20,"./metrics":21,"./middleware":22,"./normalize":23,"./pageDefaults":24,"./store":25,"./user":26,"@ndhoule/clone":3,"@ndhoule/defaults":4,"@ndhoule/each":6,"@ndhoule/foldl":9,"@ndhoule/keys":11,"@ndhoule/pick":13,"@segment/is-meta":35,"@segment/prevent-default":39,"bind-all":44,"component-emitter":52,"component-event":53,"component-querystring":55,"component-type":57,"debug":27,"extend":62,"is":66,"next-tick":76,"segmentio-facade":86}],16:[functi [...]
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+
+var bindAll = require('bind-all');
+var clone = require('@ndhoule/clone');
+var cookie = require('component-cookie');
+var debug = require('debug')('analytics.js:cookie');
+var defaults = require('@ndhoule/defaults');
+var json = require('json3');
+var topDomain = require('@segment/top-domain');
+
+/**
+ * Initialize a new `Cookie` with `options`.
+ *
+ * @param {Object} options
+ */
+
+function Cookie(options) {
+  this.options(options);
+}
+
+/**
+ * Get or set the cookie options.
+ *
+ * @param {Object} options
+ *   @field {Number} maxage (1 year)
+ *   @field {String} domain
+ *   @field {String} path
+ *   @field {Boolean} secure
+ */
+
+Cookie.prototype.options = function(options) {
+  if (arguments.length === 0) return this._options;
+
+  options = options || {};
+
+  var domain = '.' + topDomain(window.location.href);
+  if (domain === '.') domain = null;
+
+  this._options = defaults(options, {
+    // default to a year
+    maxage: 31536000000,
+    path: '/',
+    domain: domain
+  });
+
+  // http://curl.haxx.se/rfc/cookie_spec.html
+  // https://publicsuffix.org/list/effective_tld_names.dat
+  //
+  // try setting a dummy cookie with the options
+  // if the cookie isn't set, it probably means
+  // that the domain is on the public suffix list
+  // like myapp.herokuapp.com or localhost / ip.
+  this.set('ajs:test', true);
+  if (!this.get('ajs:test')) {
+    debug('fallback to domain=null');
+    this._options.domain = null;
+  }
+  this.remove('ajs:test');
+};
+
+/**
+ * Set a `key` and `value` in our cookie.
+ *
+ * @param {String} key
+ * @param {Object} value
+ * @return {Boolean} saved
+ */
+
+Cookie.prototype.set = function(key, value) {
+  try {
+    value = json.stringify(value);
+    cookie(key, value, clone(this._options));
+    return true;
+  } catch (e) {
+    return false;
+  }
+};
+
+/**
+ * Get a value from our cookie by `key`.
+ *
+ * @param {String} key
+ * @return {Object} value
+ */
+
+Cookie.prototype.get = function(key) {
+  try {
+    var value = cookie(key);
+    value = value ? json.parse(value) : null;
+    return value;
+  } catch (e) {
+    return null;
+  }
+};
+
+/**
+ * Remove a value from our cookie by `key`.
+ *
+ * @param {String} key
+ * @return {Boolean} removed
+ */
+
+Cookie.prototype.remove = function(key) {
+  try {
+    cookie(key, null, clone(this._options));
+    return true;
+  } catch (e) {
+    return false;
+  }
+};
+
+/**
+ * Expose the cookie singleton.
+ */
+
+module.exports = bindAll(new Cookie());
+
+/**
+ * Expose the `Cookie` constructor.
+ */
+
+module.exports.Cookie = Cookie;
+
+},{"@ndhoule/clone":3,"@ndhoule/defaults":4,"@segment/top-domain":42,"bind-all":44,"component-cookie":46,"debug":27,"json3":67}],17:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var clone = require('@ndhoule/clone');
+var cookie = require('./cookie');
+var debug = require('debug')('analytics:entity');
+var defaults = require('@ndhoule/defaults');
+var extend = require('@ndhoule/extend');
+var memory = require('./memory');
+var store = require('./store');
+var isodateTraverse = require('@segment/isodate-traverse');
+
+/**
+ * Expose `Entity`
+ */
+
+module.exports = Entity;
+
+/**
+ * Initialize new `Entity` with `options`.
+ *
+ * @param {Object} options
+ */
+
+function Entity(options) {
+  this.options(options);
+  this.initialize();
+}
+
+/**
+ * Initialize picks the storage.
+ *
+ * Checks to see if cookies can be set
+ * otherwise fallsback to localStorage.
+ */
+
+Entity.prototype.initialize = function() {
+  cookie.set('ajs:cookies', true);
+
+  // cookies are enabled.
+  if (cookie.get('ajs:cookies')) {
+    cookie.remove('ajs:cookies');
+    this._storage = cookie;
+    return;
+  }
+
+  // localStorage is enabled.
+  if (store.enabled) {
+    this._storage = store;
+    return;
+  }
+
+  // fallback to memory storage.
+  debug(
+    'warning using memory store both cookies and localStorage are disabled'
+  );
+  this._storage = memory;
+};
+
+/**
+ * Get the storage.
+ */
+
+Entity.prototype.storage = function() {
+  return this._storage;
+};
+
+/**
+ * Get or set storage `options`.
+ *
+ * @param {Object} options
+ *   @property {Object} cookie
+ *   @property {Object} localStorage
+ *   @property {Boolean} persist (default: `true`)
+ */
+
+Entity.prototype.options = function(options) {
+  if (arguments.length === 0) return this._options;
+  this._options = defaults(options || {}, this.defaults || {});
+};
+
+/**
+ * Get or set the entity's `id`.
+ *
+ * @param {String} id
+ */
+
+Entity.prototype.id = function(id) {
+  switch (arguments.length) {
+    case 0:
+      return this._getId();
+    case 1:
+      return this._setId(id);
+    default:
+    // No default case
+  }
+};
+
+/**
+ * Get the entity's id.
+ *
+ * @return {String}
+ */
+
+Entity.prototype._getId = function() {
+  if (!this._options.persist) {
+    return this._id === undefined ? null : this._id;
+  }
+
+  // Check cookies.
+  var cookieId = this._getIdFromCookie();
+  if (cookieId) {
+    return cookieId;
+  }
+
+  // Check localStorage.
+  var lsId = this._getIdFromLocalStorage();
+  if (lsId) {
+    // Copy the id to cookies so we can read it directly from cookies next time.
+    this._setIdInCookies(lsId);
+    return lsId;
+  }
+
+  return null;
+};
+
+/**
+ * Get the entity's id from cookies.
+ *
+ * @return {String}
+ */
+
+Entity.prototype._getIdFromCookie = function() {
+  return this.storage().get(this._options.cookie.key);
+};
+
+/**
+ * Get the entity's id from cookies.
+ *
+ * @return {String}
+ */
+
+Entity.prototype._getIdFromLocalStorage = function() {
+  if (!this._options.localStorageFallbackDisabled) {
+    return store.get(this._options.cookie.key);
+  }
+  return null;
+};
+
+/**
+ * Set the entity's `id`.
+ *
+ * @param {String} id
+ */
+
+Entity.prototype._setId = function(id) {
+  if (this._options.persist) {
+    this._setIdInCookies(id);
+    this._setIdInLocalStorage(id);
+  } else {
+    this._id = id;
+  }
+};
+
+/**
+ * Set the entity's `id` in cookies.
+ *
+ * @param {String} id
+ */
+
+Entity.prototype._setIdInCookies = function(id) {
+  this.storage().set(this._options.cookie.key, id);
+};
+
+/**
+ * Set the entity's `id` in local storage.
+ *
+ * @param {String} id
+ */
+
+Entity.prototype._setIdInLocalStorage = function(id) {
+  if (!this._options.localStorageFallbackDisabled) {
+    store.set(this._options.cookie.key, id);
+  }
+};
+
+/**
+ * Get or set the entity's `traits`.
+ *
+ * BACKWARDS COMPATIBILITY: aliased to `properties`
+ *
+ * @param {Object} traits
+ */
+
+Entity.prototype.properties = Entity.prototype.traits = function(traits) {
+  switch (arguments.length) {
+    case 0:
+      return this._getTraits();
+    case 1:
+      return this._setTraits(traits);
+    default:
+    // No default case
+  }
+};
+
+/**
+ * Get the entity's traits. Always convert ISO date strings into real dates,
+ * since they aren't parsed back from local storage.
+ *
+ * @return {Object}
+ */
+
+Entity.prototype._getTraits = function() {
+  var ret = this._options.persist
+    ? store.get(this._options.localStorage.key)
+    : this._traits;
+  return ret ? isodateTraverse(clone(ret)) : {};
+};
+
+/**
+ * Set the entity's `traits`.
+ *
+ * @param {Object} traits
+ */
+
+Entity.prototype._setTraits = function(traits) {
+  traits = traits || {};
+  if (this._options.persist) {
+    store.set(this._options.localStorage.key, traits);
+  } else {
+    this._traits = traits;
+  }
+};
+
+/**
+ * Identify the entity with an `id` and `traits`. If we it's the same entity,
+ * extend the existing `traits` instead of overwriting.
+ *
+ * @param {String} id
+ * @param {Object} traits
+ */
+
+Entity.prototype.identify = function(id, traits) {
+  traits = traits || {};
+  var current = this.id();
+  if (current === null || current === id)
+    traits = extend(this.traits(), traits);
+  if (id) this.id(id);
+  this.debug('identify %o, %o', id, traits);
+  this.traits(traits);
+  this.save();
+};
+
+/**
+ * Save the entity to local storage and the cookie.
+ *
+ * @return {Boolean}
+ */
+
+Entity.prototype.save = function() {
+  if (!this._options.persist) return false;
+  this._setId(this.id());
+  this._setTraits(this.traits());
+  return true;
+};
+
+/**
+ * Log the entity out, reseting `id` and `traits` to defaults.
+ */
+
+Entity.prototype.logout = function() {
+  this.id(null);
+  this.traits({});
+  this.storage().remove(this._options.cookie.key);
+  store.remove(this._options.cookie.key);
+  store.remove(this._options.localStorage.key);
+};
+
+/**
+ * Reset all entity state, logging out and returning options to defaults.
+ */
+
+Entity.prototype.reset = function() {
+  this.logout();
+  this.options({});
+};
+
+/**
+ * Load saved entity `id` or `traits` from storage.
+ */
+
+Entity.prototype.load = function() {
+  this.id(this.id());
+  this.traits(this.traits());
+};
+
+},{"./cookie":16,"./memory":20,"./store":25,"@ndhoule/clone":3,"@ndhoule/defaults":4,"@ndhoule/extend":8,"@segment/isodate-traverse":36,"debug":27}],18:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var Entity = require('./entity');
+var bindAll = require('bind-all');
+var debug = require('debug')('analytics:group');
+var inherit = require('inherits');
+
+/**
+ * Group defaults
+ */
+
+Group.defaults = {
+  persist: true,
+  cookie: {
+    key: 'ajs_group_id'
+  },
+  localStorage: {
+    key: 'ajs_group_properties'
+  }
+};
+
+/**
+ * Initialize a new `Group` with `options`.
+ *
+ * @param {Object} options
+ */
+
+function Group(options) {
+  this.defaults = Group.defaults;
+  this.debug = debug;
+  Entity.call(this, options);
+}
+
+/**
+ * Inherit `Entity`
+ */
+
+inherit(Group, Entity);
+
+/**
+ * Expose the group singleton.
+ */
+
+module.exports = bindAll(new Group());
+
+/**
+ * Expose the `Group` constructor.
+ */
+
+module.exports.Group = Group;
+
+},{"./entity":17,"bind-all":44,"debug":27,"inherits":64}],19:[function(require,module,exports){
+'use strict';
+
+/**
+ * Analytics.js
+ *
+ * (C) 2013-2016 Segment.io Inc.
+ */
+
+var Analytics = require('./analytics');
+
+// Create a new `analytics` singleton.
+var analytics = new Analytics();
+
+// Expose `require`.
+// TODO(ndhoule): Look into deprecating, we no longer need to expose it in tests
+//analytics.require = require;
+
+// Expose package version.
+analytics.VERSION = require('../package.json').version;
+
+/*
+ * Exports.
+ */
+
+module.exports = analytics;
+
+},{"../package.json":28,"./analytics":15}],20:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module Dependencies.
+ */
+
+var bindAll = require('bind-all');
+var clone = require('@ndhoule/clone');
+
+/**
+ * HOP.
+ */
+
+var has = Object.prototype.hasOwnProperty;
+
+/**
+ * Expose `Memory`
+ */
+
+module.exports = bindAll(new Memory());
+
+/**
+ * Initialize `Memory` store
+ */
+
+function Memory() {
+  this.store = {};
+}
+
+/**
+ * Set a `key` and `value`.
+ *
+ * @param {String} key
+ * @param {Mixed} value
+ * @return {Boolean}
+ */
+
+Memory.prototype.set = function(key, value) {
+  this.store[key] = clone(value);
+  return true;
+};
+
+/**
+ * Get a `key`.
+ *
+ * @param {String} key
+ */
+
+Memory.prototype.get = function(key) {
+  if (!has.call(this.store, key)) return;
+  return clone(this.store[key]);
+};
+
+/**
+ * Remove a `key`.
+ *
+ * @param {String} key
+ * @return {Boolean}
+ */
+
+Memory.prototype.remove = function(key) {
+  delete this.store[key];
+  return true;
+};
+
+},{"@ndhoule/clone":3,"bind-all":44}],21:[function(require,module,exports){
+'use strict';
+
+var bindAll = require('bind-all');
+var send = require('@segment/send-json');
+var debug = require('debug')('analytics.js:metrics');
+
+function Metrics(options) {
+  this.options(options);
+}
+
+/**
+ * Set the metrics options.
+ *
+ * @param {Object} options
+ *   @field {String} host
+ *   @field {Number} sampleRate
+ *   @field {Number} flushTimer
+ */
+
+Metrics.prototype.options = function(options) {
+  options = options || {};
+
+  this.host = options.host || 'api.segment.io/v1';
+  this.sampleRate = options.sampleRate || 0; // disable metrics by default.
+  this.flushTimer = options.flushTimer || 30 * 1000 /* 30s */;
+  this.maxQueueSize = options.maxQueueSize || 20;
+
+  this.queue = [];
+
+  if (this.sampleRate > 0) {
+    var self = this;
+    setInterval(function() {
+      self._flush();
+    }, this.flushTimer);
+  }
+};
+
+/**
+ * Increments the counter identified by name and tags by one.
+ *
+ * @param {String} metric Name of the metric to increment.
+ * @param {Object} tags Dimensions associated with the metric.
+ */
+Metrics.prototype.increment = function(metric, tags) {
+  if (Math.random() > this.sampleRate) {
+    return;
+  }
+
+  if (this.queue.length >= this.maxQueueSize) {
+    return;
+  }
+
+  this.queue.push({ type: 'Counter', metric: metric, value: 1, tags: tags });
+
+  // Trigger a flush if this is an error metric.
+  if (metric.indexOf('error') > 0) {
+    this._flush();
+  }
+};
+
+/**
+ * Flush all queued metrics.
+ */
+Metrics.prototype._flush = function() {
+  var self = this;
+
+  if (self.queue.length <= 0) {
+    return;
+  }
+
+  var payload = { series: this.queue };
+  var headers = { 'Content-Type': 'text/plain' };
+
+  self.queue = [];
+
+  // This endpoint does not support jsonp, so only proceed if the browser
+  // supports xhr.
+  if (send.type !== 'xhr') return;
+
+  send('https://' + this.host + '/m', payload, headers, function(err, res) {
+    debug('sent %O, received %O', payload, [err, res]);
+  });
+};
+
+/**
+ * Expose the metrics singleton.
+ */
+
+module.exports = bindAll(new Metrics());
+
+/**
+ * Expose the `Metrics` constructor.
+ */
+
+module.exports.Metrics = Metrics;
+
+},{"@segment/send-json":40,"bind-all":44,"debug":27}],22:[function(require,module,exports){
+'use strict';
+
+var Facade = require('segmentio-facade');
+
+module.exports.SourceMiddlewareChain = function SourceMiddlewareChain() {
+  var apply = middlewareChain(this);
+
+  this.applyMiddlewares = function(facade, integrations, callback) {
+    return apply(
+      function(mw, payload, next) {
+        mw({
+          integrations: integrations,
+          next: next,
+          payload: payload
+        });
+      },
+      facade,
+      callback
+    );
+  };
+};
+
+module.exports.IntegrationMiddlewareChain = function IntegrationMiddlewareChain() {
+  var apply = middlewareChain(this);
+
+  this.applyMiddlewares = function(facade, integration, callback) {
+    return apply(
+      function(mw, payload, next) {
+        mw(payload, integration, next);
+      },
+      facade,
+      callback
+    );
+  };
+};
+
+// Chain is essentially a linked list of middlewares to run in order.
+function middlewareChain(dest) {
+  var middlewares = [];
+
+  // Return a copy to prevent external mutations.
+  dest.getMiddlewares = function() {
+    return middlewares.slice();
+  };
+
+  dest.add = function(middleware) {
+    if (typeof middleware !== 'function')
+      throw new Error('attempted to add non-function middleware');
+
+    // Check for identical object references - bug check.
+    if (middlewares.indexOf(middleware) !== -1)
+      throw new Error('middleware is already registered');
+    middlewares.push(middleware);
+  };
+
+  // fn is the callback to be run once all middlewares have been applied.
+  return function applyMiddlewares(run, facade, callback) {
+    if (typeof facade !== 'object')
+      throw new Error('applyMiddlewares requires a payload object');
+    if (typeof callback !== 'function')
+      throw new Error('applyMiddlewares requires a function callback');
+
+    // Attach callback to the end of the chain.
+    var middlewaresToApply = middlewares.slice();
+    middlewaresToApply.push(callback);
+    executeChain(run, facade, middlewaresToApply, 0);
+  };
+}
+
+// Go over all middlewares until all have been applied.
+function executeChain(run, payload, middlewares, index) {
+  // If the facade has been nullified, immediately skip to the final middleware.
+  if (payload === null) {
+    middlewares[middlewares.length - 1](null);
+    return;
+  }
+
+  // Check if the payload is still a Facade. If not, convert it to one.
+  if (!(payload instanceof Facade)) {
+    payload = new Facade(payload);
+  }
+
+  var mw = middlewares[index];
+  if (mw) {
+    // If there's another middleware, continue down the chain. Otherwise, call the final function.
+    if (middlewares[index + 1]) {
+      run(mw, payload, function(result) {
+        executeChain(run, result, middlewares, ++index);
+      });
+    } else {
+      mw(payload);
+    }
+  }
+}
+
+module.exports.middlewareChain = middlewareChain;
+
+},{"segmentio-facade":86}],23:[function(require,module,exports){
+'use strict';
+
+/**
+ * Module Dependencies.
+ */
+
+var debug = require('debug')('analytics.js:normalize');
+var defaults = require('@ndhoule/defaults');
+var each = require('@ndhoule/each');
+var includes = require('@ndhoule/includes');
+var map = require('@ndhoule/map');
+var type = require('component-type');
+var uuid = require('uuid').v4;
+var json = require('json3');
+var md5 = require('spark-md5').hash;
+
+/**
+ * HOP.
+ */
+
+var has = Object.prototype.hasOwnProperty;
+
+/**
+ * Expose `normalize`
+ */
+
+module.exports = normalize;
+
+/**
+ * Toplevel properties.
+ */
+
+var toplevel = ['integrations', 'anonymousId', 'timestamp', 'context'];
+
+/**
+ * Normalize `msg` based on integrations `list`.
+ *
+ * @param {Object} msg
+ * @param {Array} list
+ * @return {Function}
+ */
+
+function normalize(msg, list) {
+  var lower = map(function(s) {
+    return s.toLowerCase();
+  }, list);
+  var opts = msg.options || {};
+  var integrations = opts.integrations || {};
+  var providers = opts.providers || {};
+  var context = opts.context || {};
+  var ret = {};
+  debug('<-', msg);
+
+  // integrations.
+  each(function(value, key) {
+    if (!integration(key)) return;
+    if (!has.call(integrations, key)) integrations[key] = value;
+    delete opts[key];
+  }, opts);
+
+  // providers.
+  delete opts.providers;
+  each(function(value, key) {
+    if (!integration(key)) return;
+    if (type(integrations[key]) === 'object') return;
+    if (has.call(integrations, key) && typeof providers[key] === 'boolean')
+      return;
+    integrations[key] = value;
+  }, providers);
+
+  // move all toplevel options to msg
+  // and the rest to context.
+  each(function(value, key) {
+    if (includes(key, toplevel)) {
+      ret[key] = opts[key];
+    } else {
+      context[key] = opts[key];
+    }
+  }, opts);
+
+  // generate and attach a messageId to msg
+  msg.messageId = 'ajs-' + md5(json.stringify(msg) + uuid());
+
+  // cleanup
+  delete msg.options;
+  ret.integrations = integrations;
+  ret.context = context;
+  ret = defaults(ret, msg);
+  debug('->', ret);
+  return ret;
+
+  function integration(name) {
+    return !!(
+      includes(name, list) ||
+      name.toLowerCase() === 'all' ||
+      includes(name.toLowerCase(), lower)
+    );
+  }
+}
+
+},{"@ndhoule/defaults":4,"@ndhoule/each":6,"@ndhoule/includes":10,"@ndhoule/map":12,"component-type":57,"debug":27,"json3":67,"spark-md5":93,"uuid":101}],24:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var canonical = require('@segment/canonical');
+var includes = require('@ndhoule/includes');
+var url = require('component-url');
+
+/**
+ * Return a default `options.context.page` object.
+ *
+ * https://segment.com/docs/spec/page/#properties
+ *
+ * @return {Object}
+ */
+
+function pageDefaults() {
+  return {
+    path: canonicalPath(),
+    referrer: document.referrer,
+    search: location.search,
+    title: document.title,
+    url: canonicalUrl(location.search)
+  };
+}
+
+/**
+ * Return the canonical path for the page.
+ *
+ * @return {string}
+ */
+
+function canonicalPath() {
+  var canon = canonical();
+  if (!canon) return window.location.pathname;
+  var parsed = url.parse(canon);
+  return parsed.pathname;
+}
+
+/**
+ * Return the canonical URL for the page concat the given `search`
+ * and strip the hash.
+ *
+ * @param {string} search
+ * @return {string}
+ */
+
+function canonicalUrl(search) {
+  var canon = canonical();
+  if (canon) return includes('?', canon) ? canon : canon + search;
+  var url = window.location.href;
+  var i = url.indexOf('#');
+  return i === -1 ? url : url.slice(0, i);
+}
+
+/*
+ * Exports.
+ */
+
+module.exports = pageDefaults;
+
+},{"@ndhoule/includes":10,"@segment/canonical":33,"component-url":58}],25:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var bindAll = require('bind-all');
+var defaults = require('@ndhoule/defaults');
+var store = require('@segment/store');
+
+/**
+ * Initialize a new `Store` with `options`.
+ *
+ * @param {Object} options
+ */
+
+function Store(options) {
+  this.options(options);
+}
+
+/**
+ * Set the `options` for the store.
+ *
+ * @param {Object} options
+ *   @field {Boolean} enabled (true)
+ */
+
+Store.prototype.options = function(options) {
+  if (arguments.length === 0) return this._options;
+
+  options = options || {};
+  defaults(options, { enabled: true });
+
+  this.enabled = options.enabled && store.enabled;
+  this._options = options;
+};
+
+/**
+ * Set a `key` and `value` in local storage.
+ *
+ * @param {string} key
+ * @param {Object} value
+ */
+
+Store.prototype.set = function(key, value) {
+  if (!this.enabled) return false;
+  return store.set(key, value);
+};
+
+/**
+ * Get a value from local storage by `key`.
+ *
+ * @param {string} key
+ * @return {Object}
+ */
+
+Store.prototype.get = function(key) {
+  if (!this.enabled) return null;
+  return store.get(key);
+};
+
+/**
+ * Remove a value from local storage by `key`.
+ *
+ * @param {string} key
+ */
+
+Store.prototype.remove = function(key) {
+  if (!this.enabled) return false;
+  return store.remove(key);
+};
+
+/**
+ * Expose the store singleton.
+ */
+
+module.exports = bindAll(new Store());
+
+/**
+ * Expose the `Store` constructor.
+ */
+
+module.exports.Store = Store;
+
+},{"@ndhoule/defaults":4,"@segment/store":41,"bind-all":44}],26:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var Entity = require('./entity');
+var bindAll = require('bind-all');
+var cookie = require('./cookie');
+var debug = require('debug')('analytics:user');
+var inherit = require('inherits');
+var rawCookie = require('component-cookie');
+var uuid = require('uuid');
+var localStorage = require('./store');
+
+/**
+ * User defaults
+ */
+
+User.defaults = {
+  persist: true,
+  cookie: {
+    key: 'ajs_user_id',
+    oldKey: 'ajs_user'
+  },
+  localStorage: {
+    key: 'ajs_user_traits'
+  }
+};
+
+/**
+ * Initialize a new `User` with `options`.
+ *
+ * @param {Object} options
+ */
+
+function User(options) {
+  this.defaults = User.defaults;
+  this.debug = debug;
+  Entity.call(this, options);
+}
+
+/**
+ * Inherit `Entity`
+ */
+
+inherit(User, Entity);
+
+/**
+ * Set/get the user id.
+ *
+ * When the user id changes, the method will reset his anonymousId to a new one.
+ *
+ * // FIXME: What are the mixed types?
+ * @param {string} id
+ * @return {Mixed}
+ * @example
+ * // didn't change because the user didn't have previous id.
+ * anonymousId = user.anonymousId();
+ * user.id('foo');
+ * assert.equal(anonymousId, user.anonymousId());
+ *
+ * // didn't change because the user id changed to null.
+ * anonymousId = user.anonymousId();
+ * user.id('foo');
+ * user.id(null);
+ * assert.equal(anonymousId, user.anonymousId());
+ *
+ * // change because the user had previous id.
+ * anonymousId = user.anonymousId();
+ * user.id('foo');
+ * user.id('baz'); // triggers change
+ * user.id('baz'); // no change
+ * assert.notEqual(anonymousId, user.anonymousId());
+ */
+
+User.prototype.id = function(id) {
+  var prev = this._getId();
+  var ret = Entity.prototype.id.apply(this, arguments);
+  if (prev == null) return ret;
+  // FIXME: We're relying on coercion here (1 == "1"), but our API treats these
+  // two values differently. Figure out what will break if we remove this and
+  // change to strict equality
+  /* eslint-disable eqeqeq */
+  if (prev != id && id) this.anonymousId(null);
+  /* eslint-enable eqeqeq */
+  return ret;
+};
+
+/**
+ * Set / get / remove anonymousId.
+ *
+ * @param {String} anonymousId
+ * @return {String|User}
+ */
+
+User.prototype.anonymousId = function(anonymousId) {
+  var store = this.storage();
+
+  // set / remove
+  if (arguments.length) {
+    store.set('ajs_anonymous_id', anonymousId);
+    this._setAnonymousIdInLocalStorage(anonymousId);
+    return this;
+  }
+
+  // new
+  anonymousId = store.get('ajs_anonymous_id');
+  if (anonymousId) {
+    // value exists in cookie, copy it to localStorage
+    this._setAnonymousIdInLocalStorage(anonymousId);
+    // refresh cookie to extend expiry
+    store.set('ajs_anonymous_id', anonymousId);
+    return anonymousId;
+  }
+
+  if (!this._options.localStorageFallbackDisabled) {
+    // if anonymousId doesn't exist in cookies, check localStorage
+    anonymousId = localStorage.get('ajs_anonymous_id');
+    if (anonymousId) {
+      // Write to cookies if available in localStorage but not cookies
+      store.set('ajs_anonymous_id', anonymousId);
+      return anonymousId;
+    }
+  }
+
+  // old - it is not stringified so we use the raw cookie.
+  anonymousId = rawCookie('_sio');
+  if (anonymousId) {
+    anonymousId = anonymousId.split('----')[0];
+    store.set('ajs_anonymous_id', anonymousId);
+    this._setAnonymousIdInLocalStorage(anonymousId);
+    store.remove('_sio');
+    return anonymousId;
+  }
+
+  // empty
+  anonymousId = uuid.v4();
+  store.set('ajs_anonymous_id', anonymousId);
+  this._setAnonymousIdInLocalStorage(anonymousId);
+  return store.get('ajs_anonymous_id');
+};
+
+/**
+ * Set the user's `anonymousid` in local storage.
+ *
+ * @param {String} id
+ */
+
+User.prototype._setAnonymousIdInLocalStorage = function(id) {
+  if (!this._options.localStorageFallbackDisabled) {
+    localStorage.set('ajs_anonymous_id', id);
+  }
+};
+
+/**
+ * Remove anonymous id on logout too.
+ */
+
+User.prototype.logout = function() {
+  Entity.prototype.logout.call(this);
+  this.anonymousId(null);
+};
+
+/**
+ * Load saved user `id` or `traits` from storage.
+ */
+
+User.prototype.load = function() {
+  if (this._loadOldCookie()) return;
+  Entity.prototype.load.call(this);
+};
+
+/**
+ * BACKWARDS COMPATIBILITY: Load the old user from the cookie.
+ *
+ * @api private
+ * @return {boolean}
+ */
+
+User.prototype._loadOldCookie = function() {
+  var user = cookie.get(this._options.cookie.oldKey);
+  if (!user) return false;
+
+  this.id(user.id);
+  this.traits(user.traits);
+  cookie.remove(this._options.cookie.oldKey);
+  return true;
+};
+
+/**
+ * Expose the user singleton.
+ */
+
+module.exports = bindAll(new User());
+
+/**
+ * Expose the `User` constructor.
+ */
+
+module.exports.User = User;
+
+},{"./cookie":16,"./entity":17,"./store":25,"bind-all":44,"component-cookie":46,"debug":27,"inherits":64,"uuid":101}],27:[function(require,module,exports){
+
+/**
+ * Expose `debug()` as the module.
+ */
+
+module.exports = debug;
+
+/**
+ * Create a debugger with the given `name`.
+ *
+ * @param {String} name
+ * @return {Type}
+ * @api public
+ */
+
+function debug(name) {
+  if (!debug.enabled(name)) return function(){};
+
+  return function(fmt){
+    fmt = coerce(fmt);
+
+    var curr = new Date;
+    var ms = curr - (debug[name] || curr);
+    debug[name] = curr;
+
+    fmt = name
+      + ' '
+      + fmt
+      + ' +' + debug.humanize(ms);
+
+    // This hackery is required for IE8
+    // where `console.log` doesn't have 'apply'
+    window.console
+      && console.log
+      && Function.prototype.apply.call(console.log, console, arguments);
+  }
+}
+
+/**
+ * The currently active debug mode names.
+ */
+
+debug.names = [];
+debug.skips = [];
+
+/**
+ * Enables a debug mode by name. This can include modes
+ * separated by a colon and wildcards.
+ *
+ * @param {String} name
+ * @api public
+ */
+
+debug.enable = function(name) {
+  try {
+    localStorage.debug = name;
+  } catch(e){}
+
+  var split = (name || '').split(/[\s,]+/)
+    , len = split.length;
+
+  for (var i = 0; i < len; i++) {
+    name = split[i].replace('*', '.*?');
+    if (name[0] === '-') {
+      debug.skips.push(new RegExp('^' + name.substr(1) + '$'));
+    }
+    else {
+      debug.names.push(new RegExp('^' + name + '$'));
+    }
+  }
+};
+
+/**
+ * Disable debug output.
+ *
+ * @api public
+ */
+
+debug.disable = function(){
+  debug.enable('');
+};
+
+/**
+ * Humanize the given `ms`.
+ *
+ * @param {Number} m
+ * @return {String}
+ * @api private
+ */
+
+debug.humanize = function(ms) {
+  var sec = 1000
+    , min = 60 * 1000
+    , hour = 60 * min;
+
+  if (ms >= hour) return (ms / hour).toFixed(1) + 'h';
+  if (ms >= min) return (ms / min).toFixed(1) + 'm';
+  if (ms >= sec) return (ms / sec | 0) + 's';
+  return ms + 'ms';
+};
+
+/**
+ * Returns true if the given mode name is enabled, false otherwise.
+ *
+ * @param {String} name
+ * @return {Boolean}
+ * @api public
+ */
+
+debug.enabled = function(name) {
+  for (var i = 0, len = debug.skips.length; i < len; i++) {
+    if (debug.skips[i].test(name)) {
+      return false;
+    }
+  }
+  for (var i = 0, len = debug.names.length; i < len; i++) {
+    if (debug.names[i].test(name)) {
+      return true;
+    }
+  }
+  return false;
+};
+
+/**
+ * Coerce `val`.
+ */
+
+function coerce(val) {
+  if (val instanceof Error) return val.stack || val.message;
+  return val;
+}
+
+// persist
+
+try {
+  if (window.localStorage) debug.enable(localStorage.debug);
+} catch(e){}
+
+},{}],28:[function(require,module,exports){
+module.exports={
+  "name": "@segment/analytics.js-core",
+  "author": "Segment <fr...@segment.com>",
+  "version": "3.10.1",
+  "description": "The hassle-free way to integrate analytics into any web application.",
+  "keywords": [
+    "analytics",
+    "analytics.js",
+    "segment",
+    "segment.io"
+  ],
+  "main": "lib/index.js",
+  "scripts": {
+    "test": "make test",
+    "lint": "eslint \"./{lib,test}/**/*.js\"",
+    "format": "prettier-eslint --write --list-different \"./{lib,test}/**/*.{js,json,md}\"",
+    "precommit": "lint-staged",
+    "np": "np --no-publish",
+    "cz": "git-cz",
+    "commitmsg": "commitlint -E GIT_PARAMS"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/segmentio/analytics.js-core"
+  },
+  "license": "SEE LICENSE IN LICENSE",
+  "bugs": {
+    "url": "https://github.com/segmentio/analytics.js-core/issues"
+  },
+  "homepage": "https://github.com/segmentio/analytics.js-core#readme",
+  "dependencies": {
+    "@ndhoule/clone": "^1.0.0",
+    "@ndhoule/defaults": "^2.0.1",
+    "@ndhoule/each": "^2.0.1",
+    "@ndhoule/extend": "^2.0.0",
+    "@ndhoule/foldl": "^2.0.1",
+    "@ndhoule/includes": "^2.0.1",
+    "@ndhoule/keys": "^2.0.0",
+    "@ndhoule/map": "^2.0.1",
+    "@ndhoule/pick": "^2.0.0",
+    "@segment/canonical": "^1.0.0",
+    "@segment/is-meta": "^1.0.0",
+    "@segment/isodate": "^1.0.2",
+    "@segment/isodate-traverse": "^1.0.1",
+    "@segment/prevent-default": "^1.0.0",
+    "@segment/send-json": "^3.0.0",
+    "@segment/store": "^1.3.20",
+    "@segment/top-domain": "^3.0.0",
+    "bind-all": "^1.0.0",
+    "component-cookie": "^1.1.2",
+    "component-emitter": "^1.2.1",
+    "component-event": "^0.1.4",
+    "component-querystring": "^2.0.0",
+    "component-type": "^1.2.1",
+    "component-url": "^0.2.1",
+    "debug": "^0.7.4",
+    "extend": "3.0.2",
+    "inherits": "^2.0.1",
+    "install": "^0.7.3",
+    "is": "^3.1.0",
+    "json3": "^3.3.2",
+    "new-date": "^1.0.0",
+    "next-tick": "^0.2.2",
+    "segmentio-facade": "^3.0.2",
+    "spark-md5": "^2.0.2",
+    "uuid": "^2.0.2"
+  },
+  "devDependencies": {
+    "@commitlint/cli": "^7.0.0",
+    "@commitlint/config-conventional": "^7.0.1",
+    "@segment/analytics.js-integration": "^3.2.1",
+    "@segment/eslint-config": "^4.0.0",
+    "browserify": "13.0.0",
+    "browserify-istanbul": "^2.0.0",
+    "codecov": "^3.0.2",
+    "commitizen": "^2.10.1",
+    "commitlint-circle": "^1.0.0",
+    "compat-trigger-event": "^1.0.0",
+    "component-each": "^0.2.6",
+    "cz-conventional-changelog": "^2.1.0",
+    "eslint": "^4.19.1",
+    "eslint-config-prettier": "^2.9.0",
+    "eslint-plugin-mocha": "^5.0.0",
+    "eslint-plugin-react": "^7.9.1",
+    "eslint-plugin-require-path-exists": "^1.1.8",
+    "husky": "^0.14.3",
+    "istanbul": "^0.4.3",
+    "jquery": "^3.2.1",
+    "karma": "1.3.0",
+    "karma-browserify": "^5.0.4",
+    "karma-chrome-launcher": "^1.0.1",
+    "karma-coverage": "^1.0.0",
+    "karma-junit-reporter": "^1.0.0",
+    "karma-mocha": "1.0.1",
+    "karma-phantomjs-launcher": "^1.0.0",
+    "karma-sauce-launcher": "^1.0.0",
+    "karma-spec-reporter": "0.0.26",
+    "karma-summary-reporter": "^1.5.0",
+    "lint-staged": "^7.2.0",
+    "mocha": "^2.2.5",
+    "np": "^3.0.4",
+    "phantomjs-prebuilt": "^2.1.7",
+    "prettier-eslint-cli": "^4.7.1",
+    "proclaim": "^3.4.1",
+    "sinon": "^1.7.3",
+    "snyk": "^1.83.0",
+    "watchify": "^3.7.0"
+  },
+  "commitlint": {
+    "extends": [
+      "@commitlint/config-conventional"
+    ]
+  },
+  "lint-staged": {
+    "linters": {
+      "*.{js,json,md}": [
+        "prettier-eslint --write",
+        "git add"
+      ]
+    }
+  },
+  "config": {
+    "commitizen": {
+      "path": "cz-conventional-changelog"
+    }
+  }
+}
+
+},{}],29:[function(require,module,exports){
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+
+var bind = require('component-bind');
+var cloneDeep = require('lodash.clonedeep');
+var debug = require('debug');
+var defaults = require('@ndhoule/defaults');
+var extend = require('@ndhoule/extend');
+var slug = require('slug-component');
+var protos = require('./protos');
+var statics = require('./statics');
+
+/**
+ * Create a new `Integration` constructor.
+ *
+ * @constructs Integration
+ * @param {string} name
+ * @return {Function} Integration
+ */
+
+function createIntegration(name) {
+  /**
+   * Initialize a new `Integration`.
+   *
+   * @class
+   * @param {Object} options
+   */
+
+  function Integration(options) {
+    if (options && options.addIntegration) {
+      // plugin
+      return options.addIntegration(Integration);
+    }
+    this.debug = debug('analytics:integration:' + slug(name));
+    this.options = defaults(cloneDeep(options) || {}, this.defaults);
+    this._queue = [];
+    this.once('ready', bind(this, this.flush));
+
+    Integration.emit('construct', this);
+    this.ready = bind(this, this.ready);
+    this._wrapInitialize();
+    this._wrapPage();
+    this._wrapTrack();
+  }
+
+  Integration.prototype.defaults = {};
+  Integration.prototype.globals = [];
+  Integration.prototype.templates = {};
+  Integration.prototype.name = name;
+  extend(Integration, statics);
+  extend(Integration.prototype, protos);
+
+  return Integration;
+}
+
+/**
+ * Exports.
+ */
+
+module.exports = createIntegration;
+
+},{"./protos":30,"./statics":31,"@ndhoule/defaults":4,"@ndhoule/extend":8,"component-bind":45,"debug":59,"lodash.clonedeep":70,"slug-component":92}],30:[function(require,module,exports){
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+
+var Emitter = require('component-emitter');
+var after = require('@ndhoule/after');
+var each = require('@ndhoule/each');
+var events = require('analytics-events');
+var every = require('@ndhoule/every');
+var fmt = require('@segment/fmt');
+var foldl = require('@ndhoule/foldl');
+var is = require('is');
+var loadIframe = require('load-iframe');
+var loadScript = require('@segment/load-script');
+var nextTick = require('next-tick');
+var normalize = require('to-no-case');
+
+/**
+ * hasOwnProperty reference.
+ */
+
+var has = Object.prototype.hasOwnProperty;
+
+/**
+ * No operation.
+ */
+
+var noop = function noop() {};
+
+/**
+ * Window defaults.
+ */
+
+var onerror = window.onerror;
+var onload = null;
+
+/**
+ * Mixin emitter.
+ */
+
+/* eslint-disable new-cap */
+Emitter(exports);
+/* eslint-enable new-cap */
+
+/**
+ * Initialize.
+ */
+
+exports.initialize = function() {
+  var ready = this.ready;
+  nextTick(ready);
+};
+
+/**
+ * Loaded?
+ *
+ * @api private
+ * @return {boolean}
+ */
+
+exports.loaded = function() {
+  return false;
+};
+
+/**
+ * Page.
+ *
+ * @api public
+ * @param {Page} page
+ */
+
+/* eslint-disable no-unused-vars */
+exports.page = function(page) {};
+/* eslint-enable no-unused-vars */
+
+/**
+ * Track.
+ *
+ * @api public
+ * @param {Track} track
+ */
+
+/* eslint-disable no-unused-vars */
+exports.track = function(track) {};
+/* eslint-enable no-unused-vars */
+
+/**
+ * Get values from items in `options` that are mapped to `key`.
+ * `options` is an integration setting which is a collection
+ * of type 'map', 'array', or 'mixed'
+ *
+ * Use cases include mapping events to pixelIds (map), sending generic
+ * conversion pixels only for specific events (array), or configuring dynamic
+ * mappings of event properties to query string parameters based on event (mixed)
+ *
+ * @api public
+ * @param {Object|Object[]|String[]} options An object, array of objects, or
+ * array of strings pulled from settings.mapping.
+ * @param {string} key The name of the item in options whose metadata
+ * we're looking for.
+ * @return {Array} An array of settings that match the input `key` name.
+ * @example
+ *
+ * // 'Map'
+ * var events = { my_event: 'a4991b88' };
+ * .map(events, 'My Event');
+ * // => ["a4991b88"]
+ * .map(events, 'whatever');
+ * // => []
+ *
+ * // 'Array'
+ * * var events = ['Completed Order', 'My Event'];
+ * .map(events, 'My Event');
+ * // => ["My Event"]
+ * .map(events, 'whatever');
+ * // => []
+ *
+ * // 'Mixed'
+ * var events = [{ key: 'my event', value: '9b5eb1fa' }];
+ * .map(events, 'my_event');
+ * // => ["9b5eb1fa"]
+ * .map(events, 'whatever');
+ * // => []
+ */
+
+exports.map = function(options, key) {
+  var normalizedComparator = normalize(key);
+  var mappingType = getMappingType(options);
+
+  if (mappingType === 'unknown') {
+    return [];
+  }
+
+  return foldl(function(matchingValues, val, key) {
+    var compare;
+    var result;
+
+    if (mappingType === 'map') {
+      compare = key;
+      result = val;
+    }
+
+    if (mappingType === 'array') {
+      compare = val;
+      result = val;
+    }
+
+    if (mappingType === 'mixed') {
+      compare = val.key;
+      result = val.value;
+    }
+
+    if (normalize(compare) === normalizedComparator) {
+      matchingValues.push(result);
+    }
+
+    return matchingValues;
+  }, [], options);
+};
+
+/**
+ * Invoke a `method` that may or may not exist on the prototype with `args`,
+ * queueing or not depending on whether the integration is "ready". Don't
+ * trust the method call, since it contains integration party code.
+ *
+ * @api private
+ * @param {string} method
+ * @param {...*} args
+ */
+
+exports.invoke = function(method) {
+  if (!this[method]) return;
+  var args = Array.prototype.slice.call(arguments, 1);
+  if (!this._ready) return this.queue(method, args);
+  var ret;
+
+  try {
+    this.debug('%s with %o', method, args);
+    ret = this[method].apply(this, args);
+  } catch (e) {
+    this.debug('error %o calling %s with %o', e, method, args);
+  }
+
+  return ret;
+};
+
+/**
+ * Queue a `method` with `args`.
+ *
+ * @api private
+ * @param {string} method
+ * @param {Array} args
+ */
+
+exports.queue = function(method, args) {
+  this._queue.push({ method: method, args: args });
+};
+
+/**
+ * Flush the internal queue.
+ *
+ * @api private
+ */
+
+exports.flush = function() {
+  this._ready = true;
+  var self = this;
+
+  each(function(call) {
+    self[call.method].apply(self, call.args);
+  }, this._queue);
+
+  // Empty the queue.
+  this._queue.length = 0;
+};
+
+/**
+ * Reset the integration, removing its global variables.
+ *
+ * @api private
+ */
+
+exports.reset = function() {
+  for (var i = 0; i < this.globals.length; i++) {
+    window[this.globals[i]] = undefined;
+  }
+
+  window.onerror = onerror;
+  window.onload = onload;
+};
+
+/**
+ * Load a tag by `name`.
+ *
+ * @param {string} name The name of the tag.
+ * @param {Object} locals Locals used to populate the tag's template variables
+ * (e.g. `userId` in '<img src="https://whatever.com/{{ userId }}">').
+ * @param {Function} [callback=noop] A callback, invoked when the tag finishes
+ * loading.
+ */
+
+exports.load = function(name, locals, callback) {
+  // Argument shuffling
+  if (typeof name === 'function') { callback = name; locals = null; name = null; }
+  if (name && typeof name === 'object') { callback = locals; locals = name; name = null; }
+  if (typeof locals === 'function') { callback = locals; locals = null; }
+
+  // Default arguments
+  name = name || 'library';
+  locals = locals || {};
+
+  locals = this.locals(locals);
+  var template = this.templates[name];
+  if (!template) throw new Error(fmt('template "%s" not defined.', name));
+  var attrs = render(template, locals);
+  callback = callback || noop;
+  var self = this;
+  var el;
+
+  switch (template.type) {
+  case 'img':
+    attrs.width = 1;
+    attrs.height = 1;
+    el = loadImage(attrs, callback);
+    break;
+  case 'script':
+    el = loadScript(attrs, function(err) {
+      if (!err) return callback();
+      self.debug('error loading "%s" error="%s"', self.name, err);
+    });
+      // TODO: hack until refactoring load-script
+    delete attrs.src;
+    each(function(val, key) {
+      el.setAttribute(key, val);
+    }, attrs);
+    break;
+  case 'iframe':
+    el = loadIframe(attrs, callback);
+    break;
+  default:
+      // No default case
+  }
+
+  return el;
+};
+
+/**
+ * Locals for tag templates.
+ *
+ * By default it includes a cache buster and all of the options.
+ *
+ * @param {Object} [locals]
+ * @return {Object}
+ */
+
+exports.locals = function(locals) {
+  locals = locals || {};
+  var cache = Math.floor(new Date().getTime() / 3600000);
+  if (!locals.hasOwnProperty('cache')) locals.cache = cache;
+  each(function(val, key) {
+    if (!locals.hasOwnProperty(key)) locals[key] = val;
+  }, this.options);
+  return locals;
+};
+
+/**
+ * Simple way to emit ready.
+ *
+ * @api public
+ */
+
+exports.ready = function() {
+  this.emit('ready');
+};
+
+/**
+ * Wrap the initialize method in an exists check, so we don't have to do it for
+ * every single integration.
+ *
+ * @api private
+ */
+
+exports._wrapInitialize = function() {
+  var initialize = this.initialize;
+  this.initialize = function() {
+    this.debug('initialize');
+    this._initialized = true;
+    var ret = initialize.apply(this, arguments);
+    this.emit('initialize');
+    return ret;
+  };
+};
+
+/**
+ * Wrap the page method to call to noop the first page call if the integration assumes
+ * a pageview.
+ *
+ * @api private
+ */
+
+exports._wrapPage = function() {
+  // Noop the first page call if integration assumes pageview
+  if (this._assumesPageview) return this.page = after(2, this.page);
+};
+
+/**
+ * Wrap the track method to call other ecommerce methods if available depending
+ * on the `track.event()`.
+ *
+ * @api private
+ */
+
+exports._wrapTrack = function() {
+  var t = this.track;
+  this.track = function(track) {
+    var event = track.event();
+    var called;
+    var ret;
+
+    for (var method in events) {
+      if (has.call(events, method)) {
+        var regexp = events[method];
+        if (!this[method]) continue;
+        if (!regexp.test(event)) continue;
+        ret = this[method].apply(this, arguments);
+        called = true;
+        break;
+      }
+    }
+
+    if (!called) ret = t.apply(this, arguments);
+    return ret;
+  };
+};
+
+/**
+ * Determine the type of the option passed to `#map`
+ *
+ * @api private
+ * @param {Object|Object[]} mapping
+ * @return {String} mappingType
+ */
+
+function getMappingType(mapping) {
+  if (is.array(mapping)) {
+    return every(isMixed, mapping) ? 'mixed' : 'array';
+  }
+  if (is.object(mapping)) return 'map';
+  return 'unknown';
+}
+
+/**
+ * Determine if item in mapping array is a valid "mixed" type value
+ *
+ * Must be an object with properties "key" (of type string)
+ * and "value" (of any type)
+ *
+ * @api private
+ * @param {*} item
+ * @return {Boolean}
+ */
+
+function isMixed(item) {
+  if (!is.object(item)) return false;
+  if (!is.string(item.key)) return false;
+  if (!has.call(item, 'value')) return false;
+  return true;
+}
+
+/**
+ * TODO: Document me
+ *
+ * @api private
+ * @param {Object} attrs
+ * @param {Function} fn
+ * @return {Image}
+ */
+
+function loadImage(attrs, fn) {
+  fn = fn || function() {};
+  var img = new Image();
+  img.onerror = error(fn, 'failed to load pixel', img);
+  img.onload = function() { fn(); };
+  img.src = attrs.src;
+  img.width = 1;
+  img.height = 1;
+  return img;
+}
+
+/**
+ * TODO: Document me
+ *
+ * @api private
+ * @param {Function} fn
+ * @param {string} message
+ * @param {Element} img
+ * @return {Function}
+ */
+
+function error(fn, message, img) {
+  return function(e) {
+    e = e || window.event;
+    var err = new Error(message);
+    err.event = e;
+    err.source = img;
+    fn(err);
+  };
+}
+
+/**
+ * Render template + locals into an `attrs` object.
+ *
+ * @api private
+ * @param {Object} template
+ * @param {Object} locals
+ * @return {Object}
+ */
+
+function render(template, locals) {
+  return foldl(function(attrs, val, key) {
+    attrs[key] = val.replace(/\{\{\ *(\w+)\ *\}\}/g, function(_, $1) {
+      return locals[$1];
+    });
+    return attrs;
+  }, {}, template.attrs);
+}
+
+},{"@ndhoule/after":1,"@ndhoule/each":6,"@ndhoule/every":7,"@ndhoule/foldl":9,"@segment/fmt":34,"@segment/load-script":38,"analytics-events":43,"component-emitter":52,"is":66,"load-iframe":69,"next-tick":76,"to-no-case":96}],31:[function(require,module,exports){
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+
+var Emitter = require('component-emitter');
+var domify = require('domify');
+var each = require('@ndhoule/each');
+var includes = require('@ndhoule/includes');
+
+/**
+ * Mix in emitter.
+ */
+
+/* eslint-disable new-cap */
+Emitter(exports);
+/* eslint-enable new-cap */
+
+/**
+ * Add a new option to the integration by `key` with default `value`.
+ *
+ * @api public
+ * @param {string} key
+ * @param {*} value
+ * @return {Integration}
+ */
+
+exports.option = function(key, value) {
+  this.prototype.defaults[key] = value;
+  return this;
+};
+
+/**
+ * Add a new mapping option.
+ *
+ * This will create a method `name` that will return a mapping for you to use.
+ *
+ * @api public
+ * @param {string} name
+ * @return {Integration}
+ * @example
+ * Integration('My Integration')
+ *   .mapping('events');
+ *
+ * new MyIntegration().track('My Event');
+ *
+ * .track = function(track){
+ *   var events = this.events(track.event());
+ *   each(send, events);
+ *  };
+ */
+
+exports.mapping = function(name) {
+  this.option(name, []);
+  this.prototype[name] = function(key) {
+    return this.map(this.options[name], key);
+  };
+  return this;
+};
+
+/**
+ * Register a new global variable `key` owned by the integration, which will be
+ * used to test whether the integration is already on the page.
+ *
+ * @api public
+ * @param {string} key
+ * @return {Integration}
+ */
+
+exports.global = function(key) {
+  this.prototype.globals.push(key);
+  return this;
+};
+
+/**
+ * Mark the integration as assuming an initial pageview, so to defer the first page call, keep track of
+ * whether we already nooped the first page call.
+ *
+ * @api public
+ * @return {Integration}
+ */
+
+exports.assumesPageview = function() {
+  this.prototype._assumesPageview = true;
+  return this;
+};
+
+/**
+ * Mark the integration as being "ready" once `load` is called.
+ *
+ * @api public
+ * @return {Integration}
+ */
+
+exports.readyOnLoad = function() {
+  this.prototype._readyOnLoad = true;
+  return this;
+};
+
+/**
+ * Mark the integration as being "ready" once `initialize` is called.
+ *
+ * @api public
+ * @return {Integration}
+ */
+
+exports.readyOnInitialize = function() {
+  this.prototype._readyOnInitialize = true;
+  return this;
+};
+
+/**
+ * Define a tag to be loaded.
+ *
+ * @api public
+ * @param {string} [name='library'] A nicename for the tag, commonly used in
+ * #load. Helpful when the integration has multiple tags and you need a way to
+ * specify which of the tags you want to load at a given time.
+ * @param {String} str DOM tag as string or URL.
+ * @return {Integration}
+ */
+
+exports.tag = function(name, tag) {
+  if (tag == null) {
+    tag = name;
+    name = 'library';
+  }
+  this.prototype.templates[name] = objectify(tag);
+  return this;
+};
+
+/**
+ * Given a string, give back DOM attributes.
+ *
+ * Do it in a way where the browser doesn't load images or iframes. It turns
+ * out domify will load images/iframes because whenever you construct those
+ * DOM elements, the browser immediately loads them.
+ *
+ * @api private
+ * @param {string} str
+ * @return {Object}
+ */
+
+function objectify(str) {
+  // replace `src` with `data-src` to prevent image loading
+  str = str.replace(' src="', ' data-src="');
+
+  var el = domify(str);
+  var attrs = {};
+
+  each(function(attr) {
+    // then replace it back
+    var name = attr.name === 'data-src' ? 'src' : attr.name;
+    if (!includes(attr.name + '=', str)) return;
+    attrs[name] = attr.value;
+  }, el.attributes);
+
+  return {
+    type: el.tagName.toLowerCase(),
+    attrs: attrs
+  };
+}
+
+},{"@ndhoule/each":6,"@ndhoule/includes":10,"component-emitter":52,"domify":61}],32:[function(require,module,exports){
+var utf8Encode = require('utf8-encode');
+var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+
+module.exports = encode;
+function encode(input) {
+    var output = "";
+    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+    var i = 0;
+
+    input = utf8Encode(input);
+
+    while (i < input.length) {
+
+        chr1 = input.charCodeAt(i++);
+        chr2 = input.charCodeAt(i++);
+        chr3 = input.charCodeAt(i++);
+
+        enc1 = chr1 >> 2;
+        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+        enc4 = chr3 & 63;
+
+        if (isNaN(chr2)) {
+            enc3 = enc4 = 64;
+        } else if (isNaN(chr3)) {
+            enc4 = 64;
+        }
+
+        output = output +
+            keyStr.charAt(enc1) + keyStr.charAt(enc2) +
+            keyStr.charAt(enc3) + keyStr.charAt(enc4);
+
+    }
+
+    return output;
+}
+},{"utf8-encode":99}],33:[function(require,module,exports){
+'use strict';
+
+/**
+ * Get the current page's canonical URL.
+ *
+ * @return {string|undefined}
+ */
+function canonical() {
+  var tags = document.getElementsByTagName('link');
+  // eslint-disable-next-line no-cond-assign
+  for (var i = 0, tag; tag = tags[i]; i++) {
+    if (tag.getAttribute('rel') === 'canonical') {
+      return tag.getAttribute('href');
+    }
+  }
+}
+
+/*
+ * Exports.
+ */
+
+module.exports = canonical;
+
+},{}],34:[function(require,module,exports){
+(function (global){(function (){
+'use strict';
+
+// Stringifier
+var toString = global.JSON && typeof JSON.stringify === 'function' ? JSON.stringify : String;
+
+/**
+ * Format the given `str`.
+ *
+ * @param {string} str
+ * @param {...*} [args]
+ * @return {string}
+ */
+function fmt(str) {
+  var args = Array.prototype.slice.call(arguments, 1);
+  var j = 0;
+
+  return str.replace(/%([a-z])/gi, function(match, f) {
+    return fmt[f] ? fmt[f](args[j++]) : match + f;
+  });
+}
+
+// Formatters
+fmt.o = toString;
+fmt.s = String;
+fmt.d = parseInt;
+
+/*
+ * Exports.
+ */
+
+module.exports = fmt;
+
+}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],35:[function(require,module,exports){
+'use strict';
+
+function isMeta(e) {
+  if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) {
+    return true;
+  }
+
+  // Logic that handles checks for the middle mouse button, based
+  // on [jQuery](https://github.com/jquery/jquery/blob/master/src/event.js#L466).
+  var which = e.which;
+  var button = e.button;
+  if (!which && button !== undefined) {
+    // eslint-disable-next-line no-bitwise, no-extra-parens
+    return (!button & 1) && (!button & 2) && (button & 4);
+  } else if (which === 2) {
+    return true;
+  }
+
+  return false;
+}
+
+/*
+ * Exports.
+ */
+
+module.exports = isMeta;
+
+},{}],36:[function(require,module,exports){
+'use strict';
+
+var type = require('component-type');
+var each = require('component-each');
+var isodate = require('@segment/isodate');
+
+/**
+ * Expose `traverse`.
+ */
+
+module.exports = traverse;
+
+/**
+ * Traverse an object or array, and return a clone with all ISO strings parsed
+ * into Date objects.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ */
+
+function traverse(input, strict) {
+  if (strict === undefined) strict = true;
+
+  if (type(input) === 'object') return object(input, strict);
+  if (type(input) === 'array') return array(input, strict);
+  return input;
+}
+
+/**
+ * Object traverser.
+ *
+ * @param {Object} obj
+ * @param {Boolean} strict
+ * @return {Object}
+ */
+
+function object(obj, strict) {
+  // 'each' utility uses obj.length to check whether the obj is array. To avoid incorrect classification, wrap call to 'each' with rename of obj.length
+  if (obj.length && typeof obj.length === 'number' && !(obj.length - 1 in obj)) { // cross browser compatible way of checking has length and is not array
+    obj.lengthNonArray = obj.length;
+    delete obj.length;
+  }
+  each(obj, function(key, val) {
+    if (isodate.is(val, strict)) {
+      obj[key] = isodate.parse(val);
+    } else if (type(val) === 'object' || type(val) === 'array') {
+      traverse(val, strict);
+    }
+  });
+  // restore obj.length if it was renamed
+  if (obj.lengthNonArray) {
+    obj.length = obj.lengthNonArray;
+    delete obj.lengthNonArray;
+  }
+  return obj;
+}
+
+/**
+ * Array traverser.
+ *
+ * @param {Array} arr
+ * @param {Boolean} strict
+ * @return {Array}
+ */
+
+function array(arr, strict) {
+  each(arr, function(val, x) {
+    if (type(val) === 'object') {
+      traverse(val, strict);
+    } else if (isodate.is(val, strict)) {
+      arr[x] = isodate.parse(val);
+    }
+  });
+  return arr;
+}
+
+},{"@segment/isodate":37,"component-each":50,"component-type":57}],37:[function(require,module,exports){
+'use strict';
+
+/**
+ * Matcher, slightly modified from:
+ *
+ * https://github.com/csnover/js-iso8601/blob/lax/iso8601.js
+ */
+
+var matcher = /^(\d{4})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:([ T])(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/;
+
+/**
+ * Convert an ISO date string to a date. Fallback to native `Date.parse`.
+ *
+ * https://github.com/csnover/js-iso8601/blob/lax/iso8601.js
+ *
+ * @param {String} iso
+ * @return {Date}
+ */
+
+exports.parse = function(iso) {
+  var numericKeys = [1, 5, 6, 7, 11, 12];
+  var arr = matcher.exec(iso);
+  var offset = 0;
+
+  // fallback to native parsing
+  if (!arr) {
+    return new Date(iso);
+  }
+
+  /* eslint-disable no-cond-assign */
+  // remove undefined values
+  for (var i = 0, val; val = numericKeys[i]; i++) {
+    arr[val] = parseInt(arr[val], 10) || 0;
+  }
+  /* eslint-enable no-cond-assign */
+
+  // allow undefined days and months
+  arr[2] = parseInt(arr[2], 10) || 1;
+  arr[3] = parseInt(arr[3], 10) || 1;
+
+  // month is 0-11
+  arr[2]--;
+
+  // allow abitrary sub-second precision
+  arr[8] = arr[8] ? (arr[8] + '00').substring(0, 3) : 0;
+
+  // apply timezone if one exists
+  if (arr[4] === ' ') {
+    offset = new Date().getTimezoneOffset();
+  } else if (arr[9] !== 'Z' && arr[10]) {
+    offset = arr[11] * 60 + arr[12];
+    if (arr[10] === '+') {
+      offset = 0 - offset;
+    }
+  }
+
+  var millis = Date.UTC(arr[1], arr[2], arr[3], arr[5], arr[6] + offset, arr[7], arr[8]);
+  return new Date(millis);
+};
+
+
+/**
+ * Checks whether a `string` is an ISO date string. `strict` mode requires that
+ * the date string at least have a year, month and date.
+ *
+ * @param {String} string
+ * @param {Boolean} strict
+ * @return {Boolean}
+ */
+
+exports.is = function(string, strict) {
+  if (typeof string !== 'string') {
+    return false;
+  }
+  if (strict && (/^\d{4}-\d{2}-\d{2}/).test(string) === false) {
+    return false;
+  }
+  return matcher.test(string);
+};
+
+},{}],38:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var onload = require('script-onload');
+var tick = require('next-tick');
+var type = require('component-type');
+
+/**
+ * Loads a script asynchronously.
+ *
+ * @param {Object} options
+ * @param {Function} cb
+ */
+function loadScript(options, cb) {
+  if (!options) {
+    throw new Error('Can\'t load nothing...');
+  }
+
+  // Allow for the simplest case, just passing a `src` string.
+  if (type(options) === 'string') {
+    options = { src : options };
+  }
+
+  var https = document.location.protocol === 'https:' || document.location.protocol === 'chrome-extension:';
+
+  // If you use protocol relative URLs, third-party scripts like Google
+  // Analytics break when testing with `file:` so this fixes that.
+  if (options.src && options.src.indexOf('//') === 0) {
+    options.src = (https ? 'https:' : 'http:') + options.src;
+  }
+
+  // Allow them to pass in different URLs depending on the protocol.
+  if (https && options.https) {
+    options.src = options.https;
+  } else if (!https && options.http) {
+    options.src = options.http;
+  }
+
+  // Make the `<script>` element and insert it before the first script on the
+  // page, which is guaranteed to exist since this Javascript is running.
+  var script = document.createElement('script');
+  script.type = 'text/javascript';
+  script.async = true;
+  script.src = options.src;
+
+  // If we have a cb, attach event handlers. Does not work on < IE9 because
+  // older browser versions don't register element.onerror
+  if (type(cb) === 'function') {
+    onload(script, cb);
+  }
+
+  tick(function() {
+    // Append after event listeners are attached for IE.
+    var firstScript = document.getElementsByTagName('script')[0];
+    firstScript.parentNode.insertBefore(script, firstScript);
+  });
+
+  // Return the script element in case they want to do anything special, like
+  // give it an ID or attributes.
+  return script;
+}
+
+/*
+ * Exports.
+ */
+
+module.exports = loadScript;
+
+},{"component-type":57,"next-tick":76,"script-onload":79}],39:[function(require,module,exports){
+'use strict';
+
+/**
+ * Prevent default on a given event.
+ *
+ * @param {Event} e
+ * @example
+ * anchor.onclick = prevent;
+ * anchor.onclick = function(e){
+ *   if (something) return prevent(e);
+ * };
+ */
+
+function preventDefault(e) {
+  e = e || window.event;
+  return e.preventDefault ? e.preventDefault() : e.returnValue = false;
+}
+
+/*
+ * Exports.
+ */
+
+module.exports = preventDefault;
+
+},{}],40:[function(require,module,exports){
+'use strict';
+
+/*
+ * Module dependencies.
+ */
+
+var JSON = require('json3');
+var base64encode = require('@segment/base64-encode');
+var cors = require('has-cors');
+var jsonp = require('jsonp');
+
+/*
+ * Exports.
+ */
+
+exports = module.exports = cors ? json : base64;
+
+/**
+ * Expose `callback`
+ */
+
+exports.callback = 'callback';
+
+/**
+ * Expose `prefix`
+ */
+
+exports.prefix = 'data';
+
+/**
+ * Expose `json`.
+ */
+
+exports.json = json;
+
+/**
+ * Expose `base64`.
+ */
+
+exports.base64 = base64;
+
+/**
+ * Expose `type`
+ */
+
+exports.type = cors ? 'xhr' : 'jsonp';
+
+/**
+ * Send the given `obj` to `url` with `fn(err, req)`.
+ *
+ * @param {String} url
+ * @param {Object} obj
+ * @param {Object} headers
+ * @param {Function} fn
+ * @api private
+ */
+
+function json(url, obj, headers, fn) {
+  if (arguments.length === 3) fn = headers, headers = {};
+
+  var req = new XMLHttpRequest;
+  req.onerror = fn;
+  req.onreadystatechange = done;
+  req.open('POST', url, true);
+
+  // TODO: Remove this eslint disable
+  // eslint-disable-next-line guard-for-in
+  for (var k in headers) {
+    req.setRequestHeader(k, headers[k]);
+  }
+  req.send(JSON.stringify(obj));
+
+  function done() {
+    if (req.readyState === 4) {
+      return fn(null, req);
+    }
+  }
+}
+
+/**
+ * Send the given `obj` to `url` with `fn(err, req)`.
+ *
+ * @param {String} url
+ * @param {Object} obj
+ * @param {Function} fn
+ * @api private
+ */
+
+function base64(url, obj, _, fn) {
+  if (arguments.length === 3) fn = _;
+
+  var prefix = exports.prefix;
+  var data = encode(obj);
+  url += '?' + prefix + '=' + data;
+  jsonp(url, { param: exports.callback }, function(err, obj) {
+    if (err) return fn(err);
+    fn(null, {
+      url: url,
+      body: obj
+    });
+  });
+}
+
+/**
+ * Encodes `obj`.
+ *
+ * @param {Object} obj
+ */
+
+function encode(obj) {
+  var str = '';
+  str = JSON.stringify(obj);
+  str = base64encode(str);
+  str = str.replace(/\+/g, '-').replace(/\//g, '_');
+  return encodeURIComponent(str);
+}
+
+},{"@segment/base64-encode":32,"has-cors":63,"json3":67,"jsonp":68}],41:[function(require,module,exports){
+(function (global){(function (){
+"use strict"
+
+var JSON = require('json3');
+
+module.exports = (function() {
+	// Store.js
+	var store = {},
+		win = (typeof window != 'undefined' ? window : global),
+		doc = win.document,
+		localStorageName = 'localStorage',
+		scriptTag = 'script',
+		storage
+
+	store.disabled = false
+	store.version = '1.3.20'
+	store.set = function(key, value) {}
+	store.get = function(key, defaultVal) {}
+	store.has = function(key) { return store.get(key) !== undefined }
+	store.remove = function(key) {}
+	store.clear = function() {}
+	store.transact = function(key, defaultVal, transactionFn) {
+		if (transactionFn == null) {
+			transactionFn = defaultVal
+			defaultVal = null
+		}
+		if (defaultVal == null) {
+			defaultVal = {}
+		}
+		var val = store.get(key, defaultVal)
+		transactionFn(val)
+		store.set(key, val)
+	}
+	store.getAll = function() {
+		var ret = {}
+		store.forEach(function(key, val) {
+			ret[key] = val
+		})
+		return ret
+	}
+	store.forEach = function() {}
+	store.serialize = function(value) {
+		return JSON.stringify(value)
+	}
+	store.deserialize = function(value) {
+		if (typeof value != 'string') { return undefined }
+		try { return JSON.parse(value) }
+		catch(e) { return value || undefined }
+	}
+
+	// Functions to encapsulate questionable FireFox 3.6.13 behavior
+	// when about.config::dom.storage.enabled === false
+	// See https://github.com/marcuswestin/store.js/issues#issue/13
+	function isLocalStorageNameSupported() {
+		try { return (localStorageName in win && win[localStorageName]) }
+		catch(err) { return false }
+	}
+
+	if (isLocalStorageNameSupported()) {
+		storage = win[localStorageName]
+		store.set = function(key, val) {
+			if (val === undefined) { return store.remove(key) }
+			storage.setItem(key, store.serialize(val))
+			return val
+		}
+		store.get = function(key, defaultVal) {
+			var val = store.deserialize(storage.getItem(key))
+			return (val === undefined ? defaultVal : val)
+		}
+		store.remove = function(key) { storage.removeItem(key) }
+		store.clear = function() { storage.clear() }
+		store.forEach = function(callback) {
+			for (var i=0; i<storage.length; i++) {
+				var key = storage.key(i)
+				callback(key, store.get(key))
+			}
+		}
+	} else if (doc && doc.documentElement.addBehavior) {
+		var storageOwner,
+			storageContainer
+		// Since #userData storage applies only to specific paths, we need to
+		// somehow link our data to a specific path.  We choose /favicon.ico
+		// as a pretty safe option, since all browsers already make a request to
+		// this URL anyway and being a 404 will not hurt us here.  We wrap an
+		// iframe pointing to the favicon in an ActiveXObject(htmlfile) object
+		// (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
+		// since the iframe access rules appear to allow direct access and
+		// manipulation of the document element, even for a 404 page.  This
+		// document can be used instead of the current document (which would
+		// have been limited to the current path) to perform #userData storage.
+		try {
+			storageContainer = new ActiveXObject('htmlfile')
+			storageContainer.open()
+			storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'><iframe src="/favicon.ico"></iframe>')
+			storageContainer.close()
+			storageOwner = storageContainer.w.frames[0].document
+			storage = storageOwner.createElement('div')
+		} catch(e) {
+			// somehow ActiveXObject instantiation failed (perhaps some special
+			// security settings or otherwse), fall back to per-path storage
+			storage = doc.createElement('div')
+			storageOwner = doc.body
+		}
+		var withIEStorage = function(storeFunction) {
+			return function() {
+				var args = Array.prototype.slice.call(arguments, 0)
+				args.unshift(storage)
+				// See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
+				// and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
+				storageOwner.appendChild(storage)
+				storage.addBehavior('#default#userData')
+				storage.load(localStorageName)
+				var result = storeFunction.apply(store, args)
+				storageOwner.removeChild(storage)
+				return result
+			}
+		}
+
+		// In IE7, keys cannot start with a digit or contain certain chars.
+		// See https://github.com/marcuswestin/store.js/issues/40
+		// See https://github.com/marcuswestin/store.js/issues/83
+		var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
+		var ieKeyFix = function(key) {
+			return key.replace(/^d/, '___$&').replace(forbiddenCharsRegex, '___')
+		}
+		store.set = withIEStorage(function(storage, key, val) {
+			key = ieKeyFix(key)
+			if (val === undefined) { return store.remove(key) }
+			storage.setAttribute(key, store.serialize(val))
+			storage.save(localStorageName)
+			return val
+		})
+		store.get = withIEStorage(function(storage, key, defaultVal) {
+			key = ieKeyFix(key)
+			var val = store.deserialize(storage.getAttribute(key))
+			return (val === undefined ? defaultVal : val)
+		})
+		store.remove = withIEStorage(function(storage, key) {
+			key = ieKeyFix(key)
+			storage.removeAttribute(key)
+			storage.save(localStorageName)
+		})
+		store.clear = withIEStorage(function(storage) {
+			var attributes = storage.XMLDocument.documentElement.attributes
+			storage.load(localStorageName)
+			for (var i=attributes.length-1; i>=0; i--) {
+				storage.removeAttribute(attributes[i].name)
+			}
+			storage.save(localStorageName)
+		})
+		store.forEach = withIEStorage(function(storage, callback) {
+			var attributes = storage.XMLDocument.documentElement.attributes
+			for (var i=0, attr; attr=attributes[i]; ++i) {
+				callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
+			}
+		})
+	}
+
+	try {
+		var testKey = '__storejs__'
+		store.set(testKey, testKey)
+		if (store.get(testKey) != testKey) { store.disabled = true }
+		store.remove(testKey)
+	} catch(e) {
+		store.disabled = true
+	}
+	store.enabled = !store.disabled
+	
+	return store
+}())
+
+}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"json3":67}],42:[function(require,module,exports){
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+
+var parse = require('component-url').parse;
+var cookie = require('component-cookie');
+
+/**
+ * Get the top domain.
+ *
+ * The function constructs the levels of domain and attempts to set a global
+ * cookie on each one when it succeeds it returns the top level domain.
+ *
+ * The method returns an empty string when the hostname is an ip or `localhost`.
+ *
+ * Example levels:
+ *
+ *      domain.levels('http://www.google.co.uk');
+ *      // => ["co.uk", "google.co.uk", "www.google.co.uk"]
+ *
+ * Example:
+ *
+ *      domain('http://localhost:3000/baz');
+ *      // => ''
+ *      domain('http://dev:3000/baz');
+ *      // => ''
+ *      domain('http://127.0.0.1:3000/baz');
+ *      // => ''
+ *      domain('http://segment.io/baz');
+ *      // => 'segment.io'
+ *
+ * @param {string} url
+ * @return {string}
+ * @api public
+ */
+function domain(url) {
+  var cookie = exports.cookie;
+  var levels = exports.levels(url);
+
+  // Lookup the real top level one.
+  for (var i = 0; i < levels.length; ++i) {
+    var cname = '__tld__';
+    var domain = levels[i];
+    var opts = { domain: '.' + domain };
+
+    cookie(cname, 1, opts);
+    if (cookie(cname)) {
+      cookie(cname, null, opts);
+      return domain;
+    }
+  }
+
+  return '';
+}
+
+/**
+ * Levels returns all levels of the given url.
+ *
+ * @param {string} url
+ * @return {Array}
+ * @api public
+ */
+domain.levels = function(url) {
+  var host = parse(url).hostname;
+  var parts = host.split('.');
+  var last = parts[parts.length - 1];
+  var levels = [];
+
+  // Ip address.
+  if (parts.length === 4 && last === parseInt(last, 10)) {
+    return levels;
+  }
+
+  // Localhost.
+  if (parts.length <= 1) {
+    return levels;
+  }
+
+  // Create levels.
+  for (var i = parts.length - 2; i >= 0; --i) {
+    levels.push(parts.slice(i).join('.'));
+  }
+
+  return levels;
+};
+
+/**
+ * Expose cookie on domain.
+ */
+domain.cookie = cookie;
+
+/*
+ * Exports.
+ */
+
+exports = module.exports = domain;
+
+},{"component-cookie":46,"component-url":58}],43:[function(require,module,exports){
+'use strict';
+
+/**
+ * Module Dependencies
+ */
+
+var map = require('@ndhoule/map');
+var foldl = require('@ndhoule/foldl');
+
+var eventMap = {
+  // Videos
+  videoPlaybackStarted: [{
+    object: 'video playback',
+    action: 'started'
+  }],
+  videoPlaybackPaused: [{
+    object: 'video playback',
+    action: 'paused'
+  }],
+  videoPlaybackInterrupted: [{
+    object: 'video playback',
+    action: 'interrupted'
+  }],
+  videoPlaybackResumed: [{
+    object: 'video playback',
+    action: 'resumed'
+  }],
+  videoPlaybackCompleted: [{
+    object: 'video playback',
+    action: 'completed'
+  }],
+  videoPlaybackBufferStarted: [{
+    object: 'video playback buffer',
+    action: 'started'
+  }],
+  videoPlaybackBufferCompleted: [{
+    object: 'video playback buffer',
+    action: 'completed'
+  }],
+  videoPlaybackSeekStarted: [{
+    object: 'video playback seek',
+    action: 'started'
+  }],
+  videoPlaybackSeekCompleted: [{
+    object: 'video playback seek',
+    action: 'completed'
+  }],
+  videoContentStarted: [{
+    object: 'video content',
+    action: 'started'
+  }],
+  videoContentPlaying: [{
+    object: 'video content',
+    action: 'playing'
+  }],
+  videoContentCompleted: [{
+    object: 'video content',
+    action: 'completed'
+  }],
+  videoAdStarted: [{
+    object: 'video ad',
+    action: 'started'
+  }],
+  videoAdPlaying: [{
+    object: 'video ad',
+    action: 'playing'
+  }],
+  videoAdCompleted: [{
+    object: 'video ad',
+    action: 'completed'
+  }],
+
+  // Promotions
+  promotionViewed: [{
+    object: 'promotion',
+    action: 'viewed'
+  }],
+  promotionClicked: [{
+    object: 'promotion',
+    action: 'clicked'
+  }],
+
+  // Browsing
+  productsSearched: [{
+    object: 'products',
+    action: 'searched'
+  }],
+  productListViewed: [{
+    object: 'product list',
+    action: 'viewed'
+  }, {
+    object: 'product category',
+    action: 'viewed'
+  }],
+  productListFiltered: [{
+    object: 'product list',
+    action: 'filtered'
+  }],
+
+  // Core Ordering
+  productClicked: [{
+    object: 'product',
+    action: 'clicked'
+  }],
+  productViewed: [{
+    object: 'product',
+    action: 'viewed'
+  }],
+  productAdded: [{
+    object: 'product',
+    action: 'added'
+  }],
+  productRemoved: [{
+    object: 'product',
+    action: 'removed'
+  }],
+  cartViewed: [{
+    object: 'cart',
+    action: 'viewed'
+  }],
+  orderUpdated: [{
+    object: 'order',
+    action: 'updated'
+  }],
+  orderCompleted: [{
+    object: 'order',
+    action: 'completed'
+  }],
+  orderRefunded: [{
+    object: 'order',
+    action: 'refunded'
+  }],
+  orderCancelled: [{
+    object: 'order',
+    action: 'cancelled'
+  }],
+  paymentInfoEntered: [{
+    object: 'payment info',
+    action: 'entered'
+  }],
+  checkoutStarted: [{
+    object: 'checkout',
+    action: 'started'
+  }],
+  checkoutStepViewed: [{
+    object: 'checkout step',
+    action: 'viewed'
+  }],
+  checkoutStepCompleted: [{
+    object: 'checkout step',
+    action: 'completed'
+  }],
+
+  // Coupons
+  couponEntered: [{
+    object: 'coupon',
+    action: 'entered'
+  }],
+  couponApplied: [{
+    object: 'coupon',
+    action: 'applied'
+  }],
+  couponDenied: [{
+    object: 'coupon',
+    action: 'denied'
+  }],
+  couponRemoved: [{
+    object: 'coupon',
+    action: 'removed'
+  }],
+
+  // Wishlisting
+  productAddedToWishlist: [{
+    object: 'product',
+    action: 'added to wishlist'
+  }],
+  productRemovedFromWishlist: [{
+    object: 'product',
+    action: 'removed from wishlist'
+  }],
+  productAddedFromWishlistToCart: [{
+    object: 'product',
+    action: 'added to cart from wishlist'
+  }, {
+    object: 'product',
+    action: 'added from wishlist to cart'
+  }],
+
+  // Sharing
+  productShared: [{
+    object: 'product',
+    action: 'shared'
+  }],
+  cartShared: [{
+    object: 'cart',
+    action: 'shared'
+  }],
+
+  // Reviewing
+  productReviewed: [{
+    object: 'product',
+    action: 'reviewed'
+  }],
+
+  // App Lifecycle
+  applicationInstalled: [{
+    object: 'application',
+    action: 'installed'
+  }],
+  applicationUpdated: [{
+    object: 'application',
+    action: 'updated'
+  }],
+  applicationOpened: [{
+    object: 'application',
+    action: 'opened'
+  }],
+  applicationBackgrounded: [{
+    object: 'application',
+    action: 'backgrounded'
+  }],
+  applicationUninstalled: [{
+    object: 'application',
+    action: 'uninstalled'
+  }],
+  applicationCrashed: [{
+    object: 'application',
+    action: 'crashed'
+  }],
+
+  // App Campaign and Referral Events
+  installAttributed: [{
+    object: 'install',
+    action: 'attributed'
+  }],
+  deepLinkOpened: [{
+    object: 'deep link',
+    action: 'opened'
+  }],
+  pushNotificationReceived: [{
+    object: 'push notification',
+    action: 'received'
+  }],
+  pushNotificationTapped: [{
+    object: 'push notification',
+    action: 'tapped'
+  }],
+  pushNotificationBounced: [{
+    object: 'push notification',
+    action: 'bounced'
+  }],
+
+  // Email
+  emailBounced: [{
+    object: 'email',
+    action: 'bounced'
+  }],
+  emailDelivered: [{
+    object: 'email',
+    action: 'delivered'
+  }],
+  emailLinkClicked: [{
+    object: 'email link',
+    action: 'clicked'
+  }],
+  emailMarkedAsSpam: [{
+    object: 'email',
+    action: 'marked as spam'
+  }],
+  emailOpened: [{
+    object: 'email',
+    action: 'opened'
+  }],
+  unsubscribed: [{
+    object: '',
+    action: 'unsubscribed'
+  }]
+};
+
+/**
+ * Export the event map
+ *
+ * For each method
+ *   - For each of its object:action alias pairs
+ *     - For each permutation of that pair
+ *       - Create a regex string
+ *   - Join them and assign it to its respective method value
+ *
+ *  [{
+ *    object: 'product list',
+ *    action: 'viewed'
+ *  },{
+ *    object: 'product category',
+ *    action: 'viewed'
+ *  }] => /
+ *    ^[ _]?product[ _]?list[ _]?viewed[ _]?
+ *   |^[ _]?viewed[ _]?product[ _]?list[ _]?
+ *   |^[ _]?product[ _]?category[ _]?viewed[ _]?
+ *   |^[ _]?viewed[ _]?product[ _]?category[ _]?
+ *   $/i
+ *
+ *  todo(cs/wj/nh): memoization strategy / build step?
+ */
+
+module.exports = foldl(function transform(ret, pairs, method) {
+  var values = map(function(pair) {
+    return map(function(permutation) {
+      var flattened = [].concat.apply([], map(function(words) {
+        return words.split(' ');
+      }, permutation));
+      return '^[ _]?' + flattened.join('[ _]?') + '[ _]?';
+    }, [
+      [pair.action, pair.object],
+      [pair.object, pair.action]
+    ]).join('|');
+  }, pairs);
+  var conjoined = values.join('|') + '$';
+  ret[method] = new RegExp(conjoined, 'i');
+  return ret;
+}, {}, eventMap);
+
+},{"@ndhoule/foldl":9,"@ndhoule/map":12}],44:[function(require,module,exports){
+'use strict';
+
+var bind = require('component-bind');
+
+function bindAll(obj) {
+  // eslint-disable-next-line guard-for-in
+  for (var key in obj) {
+    var val = obj[key];
+    if (typeof val === 'function') {
+      obj[key] = bind(obj, obj[key]);
+    }
+  }
+  return obj;
+}
+
+module.exports = bindAll;
+
+},{"component-bind":45}],45:[function(require,module,exports){
+/**
+ * Slice reference.
+ */
+
+var slice = [].slice;
+
+/**
+ * Bind `obj` to `fn`.
+ *
+ * @param {Object} obj
+ * @param {Function|String} fn or string
+ * @return {Function}
+ * @api public
+ */
+
+module.exports = function(obj, fn){
+  if ('string' == typeof fn) fn = obj[fn];
+  if ('function' != typeof fn) throw new Error('bind() requires a function');
+  var args = slice.call(arguments, 2);
+  return function(){
+    return fn.apply(obj, args.concat(slice.call(arguments)));
+  }
+};
+
+},{}],46:[function(require,module,exports){
+
+/**
+ * Module dependencies.
+ */
+
+var debug = require('debug')('cookie');
+
+/**
+ * Set or get cookie `name` with `value` and `options` object.
+ *
+ * @param {String} name
+ * @param {String} value
+ * @param {Object} options
+ * @return {Mixed}
+ * @api public
+ */
+
+module.exports = function(name, value, options){
+  switch (arguments.length) {
+    case 3:
+    case 2:
+      return set(name, value, options);
+    case 1:
+      return get(name);
+    default:
+      return all();
+  }
+};
+
+/**
+ * Set cookie `name` to `value`.
+ *
+ * @param {String} name
+ * @param {String} value
+ * @param {Object} options
+ * @api private
+ */
+
+function set(name, value, options) {
+  options = options || {};
+  var str = encode(name) + '=' + encode(value);
+
+  if (null == value) options.maxage = -1;
+
+  if (options.maxage) {
+    options.expires = new Date(+new Date + options.maxage);
+  }
+
+  if (options.path) str += '; path=' + options.path;
+  if (options.domain) str += '; domain=' + options.domain;
+  if (options.expires) str += '; expires=' + options.expires.toUTCString();
+  if (options.secure) str += '; secure';
+
+  document.cookie = str;
+}
+
+/**
+ * Return all cookies.
+ *
+ * @return {Object}
+ * @api private
+ */
+
+function all() {
+  var str;
+  try {
+    str = document.cookie;
+  } catch (err) {
+    if (typeof console !== 'undefined' && typeof console.error === 'function') {
+      console.error(err.stack || err);
+    }
+    return {};
+  }
+  return parse(str);
+}
+
+/**
+ * Get cookie `name`.
+ *
+ * @param {String} name
+ * @return {String}
+ * @api private
+ */
+
+function get(name) {
+  return all()[name];
+}
+
+/**
+ * Parse cookie `str`.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+function parse(str) {
+  var obj = {};
+  var pairs = str.split(/ *; */);
+  var pair;
+  if ('' == pairs[0]) return obj;
+  for (var i = 0; i < pairs.length; ++i) {
+    pair = pairs[i].split('=');
+    obj[decode(pair[0])] = decode(pair[1]);
+  }
+  return obj;
+}
+
+/**
+ * Encode.
+ */
+
+function encode(value){
+  try {
+    return encodeURIComponent(value);
+  } catch (e) {
+    debug('error `encode(%o)` - %o', value, e)
+  }
+}
+
+/**
+ * Decode.
+ */
+
+function decode(value) {
+  try {
+    return decodeURIComponent(value);
+  } catch (e) {
+    debug('error `decode(%o)` - %o', value, e)
+  }
+}
+
+},{"debug":47}],47:[function(require,module,exports){
+
+/**
+ * This is the web browser implementation of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = require('./debug');
+exports.log = log;
+exports.formatArgs = formatArgs;
+exports.save = save;
+exports.load = load;
+exports.useColors = useColors;
+exports.storage = 'undefined' != typeof chrome
+               && 'undefined' != typeof chrome.storage
+                  ? chrome.storage.local
+                  : localstorage();
+
+/**
+ * Colors.
+ */
+
+exports.colors = [
+  'lightseagreen',
+  'forestgreen',
+  'goldenrod',
+  'dodgerblue',
+  'darkorchid',
+  'crimson'
+];
+
+/**
+ * Currently only WebKit-based Web Inspectors, Firefox >= v31,
+ * and the Firebug extension (any Firefox version) are known
+ * to support "%c" CSS customizations.
+ *
+ * TODO: add a `localStorage` variable to explicitly enable/disable colors
+ */
+
+function useColors() {
+  // is webkit? http://stackoverflow.com/a/16459606/376773
+  return ('WebkitAppearance' in document.documentElement.style) ||
+    // is firebug? http://stackoverflow.com/a/398120/376773
+    (window.console && (console.firebug || (console.exception && console.table))) ||
+    // is firefox >= v31?
+    // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
+    (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31);
+}
+
+/**
+ * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
+ */
+
+exports.formatters.j = function(v) {
+  return JSON.stringify(v);
+};
+
+
+/**
+ * Colorize log arguments if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs() {
+  var args = arguments;
+  var useColors = this.useColors;
+
+  args[0] = (useColors ? '%c' : '')
+    + this.namespace
+    + (useColors ? ' %c' : ' ')
+    + args[0]
+    + (useColors ? '%c ' : ' ')
+    + '+' + exports.humanize(this.diff);
+
+  if (!useColors) return args;
+
+  var c = 'color: ' + this.color;
+  args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1));
+
+  // the final "%c" is somewhat tricky, because there could be other
+  // arguments passed either before or after the %c, so we need to
+  // figure out the correct index to insert the CSS into
+  var index = 0;
+  var lastC = 0;
+  args[0].replace(/%[a-z%]/g, function(match) {
+    if ('%%' === match) return;
+    index++;
+    if ('%c' === match) {
+      // we only are interested in the *last* %c
+      // (the user may have provided their own)
+      lastC = index;
+    }
+  });
+
+  args.splice(lastC, 0, c);
+  return args;
+}
+
+/**
+ * Invokes `console.log()` when available.
+ * No-op when `console.log` is not a "function".
+ *
+ * @api public
+ */
+
+function log() {
+  // this hackery is required for IE8/9, where
+  // the `console.log` function doesn't have 'apply'
+  return 'object' === typeof console
+    && console.log
+    && Function.prototype.apply.call(console.log, console, arguments);
+}
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+
+function save(namespaces) {
+  try {
+    if (null == namespaces) {
+      exports.storage.removeItem('debug');
+    } else {
+      exports.storage.debug = namespaces;
+    }
+  } catch(e) {}
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+
+function load() {
+  var r;
+  try {
+    r = exports.storage.debug;
+  } catch(e) {}
+  return r;
+}
+
+/**
+ * Enable namespaces listed in `localStorage.debug` initially.
+ */
+
+exports.enable(load());
+
+/**
+ * Localstorage attempts to return the localstorage.
+ *
+ * This is necessary because safari throws
+ * when a user disables cookies/localstorage
+ * and you attempt to access it.
+ *
+ * @return {LocalStorage}
+ * @api private
+ */
+
+function localstorage(){
+  try {
+    return window.localStorage;
+  } catch (e) {}
+}
+
+},{"./debug":48}],48:[function(require,module,exports){
+
+/**
+ * This is the common logic for both the Node.js and web browser
+ * implementations of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = debug;
+exports.coerce = coerce;
+exports.disable = disable;
+exports.enable = enable;
+exports.enabled = enabled;
+exports.humanize = require('ms');
+
+/**
+ * The currently active debug mode names, and names to skip.
+ */
+
+exports.names = [];
+exports.skips = [];
+
+/**
+ * Map of special "%n" handling functions, for the debug "format" argument.
+ *
+ * Valid key names are a single, lowercased letter, i.e. "n".
+ */
+
+exports.formatters = {};
+
+/**
+ * Previously assigned color.
+ */
+
+var prevColor = 0;
+
+/**
+ * Previous log timestamp.
+ */
+
+var prevTime;
+
+/**
+ * Select a color.
+ *
+ * @return {Number}
+ * @api private
+ */
+
+function selectColor() {
+  return exports.colors[prevColor++ % exports.colors.length];
+}
+
+/**
+ * Create a debugger with the given `namespace`.
+ *
+ * @param {String} namespace
+ * @return {Function}
+ * @api public
+ */
+
+function debug(namespace) {
+
+  // define the `disabled` version
+  function disabled() {
+  }
+  disabled.enabled = false;
+
+  // define the `enabled` version
+  function enabled() {
+
+    var self = enabled;
+
+    // set `diff` timestamp
+    var curr = +new Date();
+    var ms = curr - (prevTime || curr);
+    self.diff = ms;
+    self.prev = prevTime;
+    self.curr = curr;
+    prevTime = curr;
+
+    // add the `color` if not set
+    if (null == self.useColors) self.useColors = exports.useColors();
+    if (null == self.color && self.useColors) self.color = selectColor();
+
+    var args = Array.prototype.slice.call(arguments);
+
+    args[0] = exports.coerce(args[0]);
+
+    if ('string' !== typeof args[0]) {
+      // anything else let's inspect with %o
+      args = ['%o'].concat(args);
+    }
+
+    // apply any `formatters` transformations
+    var index = 0;
+    args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
+      // if we encounter an escaped % then don't increase the array index
+      if (match === '%%') return match;
+      index++;
+      var formatter = exports.formatters[format];
+      if ('function' === typeof formatter) {
+        var val = args[index];
+        match = formatter.call(self, val);
+
+        // now we need to remove `args[index]` since it's inlined in the `format`
+        args.splice(index, 1);
+        index--;
+      }
+      return match;
+    });
+
+    if ('function' === typeof exports.formatArgs) {
+      args = exports.formatArgs.apply(self, args);
+    }
+    var logFn = enabled.log || exports.log || console.log.bind(console);
+    logFn.apply(self, args);
+  }
+  enabled.enabled = true;
+
+  var fn = exports.enabled(namespace) ? enabled : disabled;
+
+  fn.namespace = namespace;
+
+  return fn;
+}
+
+/**
+ * Enables a debug mode by namespaces. This can include modes
+ * separated by a colon and wildcards.
+ *
+ * @param {String} namespaces
+ * @api public
+ */
+
+function enable(namespaces) {
+  exports.save(namespaces);
+
+  var split = (namespaces || '').split(/[\s,]+/);
+  var len = split.length;
+
+  for (var i = 0; i < len; i++) {
+    if (!split[i]) continue; // ignore empty strings
+    namespaces = split[i].replace(/\*/g, '.*?');
+    if (namespaces[0] === '-') {
+      exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
+    } else {
+      exports.names.push(new RegExp('^' + namespaces + '$'));
+    }
+  }
+}
+
+/**
+ * Disable debug output.
+ *
+ * @api public
+ */
+
+function disable() {
+  exports.enable('');
+}
+
+/**
+ * Returns true if the given mode name is enabled, false otherwise.
+ *
+ * @param {String} name
+ * @return {Boolean}
+ * @api public
+ */
+
+function enabled(name) {
+  var i, len;
+  for (i = 0, len = exports.skips.length; i < len; i++) {
+    if (exports.skips[i].test(name)) {
+      return false;
+    }
+  }
+  for (i = 0, len = exports.names.length; i < len; i++) {
+    if (exports.names[i].test(name)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Coerce `val`.
+ *
+ * @param {Mixed} val
+ * @return {Mixed}
+ * @api private
+ */
+
+function coerce(val) {
+  if (val instanceof Error) return val.stack || val.message;
+  return val;
+}
+
+},{"ms":49}],49:[function(require,module,exports){
+/**
+ * Helpers.
+ */
+
+var s = 1000;
+var m = s * 60;
+var h = m * 60;
+var d = h * 24;
+var y = d * 365.25;
+
+/**
+ * Parse or format the given `val`.
+ *
+ * Options:
+ *
+ *  - `long` verbose formatting [false]
+ *
+ * @param {String|Number} val
+ * @param {Object} options
+ * @return {String|Number}
+ * @api public
+ */
+
+module.exports = function(val, options){
+  options = options || {};
+  if ('string' == typeof val) return parse(val);
+  return options.long
+    ? long(val)
+    : short(val);
+};
+
+/**
+ * Parse the given `str` and return milliseconds.
+ *
+ * @param {String} str
+ * @return {Number}
+ * @api private
+ */
+
+function parse(str) {
+  str = '' + str;
+  if (str.length > 10000) return;
+  var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str);
+  if (!match) return;
+  var n = parseFloat(match[1]);
+  var type = (match[2] || 'ms').toLowerCase();
+  switch (type) {
+    case 'years':
+    case 'year':
+    case 'yrs':
+    case 'yr':
+    case 'y':
+      return n * y;
+    case 'days':
+    case 'day':
+    case 'd':
+      return n * d;
+    case 'hours':
+    case 'hour':
+    case 'hrs':
+    case 'hr':
+    case 'h':
+      return n * h;
+    case 'minutes':
+    case 'minute':
+    case 'mins':
+    case 'min':
+    case 'm':
+      return n * m;
+    case 'seconds':
+    case 'second':
+    case 'secs':
+    case 'sec':
+    case 's':
+      return n * s;
+    case 'milliseconds':
+    case 'millisecond':
+    case 'msecs':
+    case 'msec':
+    case 'ms':
+      return n;
+  }
+}
+
+/**
+ * Short format for `ms`.
+ *
+ * @param {Number} ms
+ * @return {String}
+ * @api private
+ */
+
+function short(ms) {
+  if (ms >= d) return Math.round(ms / d) + 'd';
+  if (ms >= h) return Math.round(ms / h) + 'h';
+  if (ms >= m) return Math.round(ms / m) + 'm';
+  if (ms >= s) return Math.round(ms / s) + 's';
+  return ms + 'ms';
+}
+
+/**
+ * Long format for `ms`.
+ *
+ * @param {Number} ms
+ * @return {String}
+ * @api private
+ */
+
+function long(ms) {
+  return plural(ms, d, 'day')
+    || plural(ms, h, 'hour')
+    || plural(ms, m, 'minute')
+    || plural(ms, s, 'second')
+    || ms + ' ms';
+}
+
+/**
+ * Pluralization helper.
+ */
+
+function plural(ms, n, name) {
+  if (ms < n) return;
+  if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name;
+  return Math.ceil(ms / n) + ' ' + name + 's';
+}
+
+},{}],50:[function(require,module,exports){
+
+/**
+ * Module dependencies.
+ */
+
+try {
+  var type = require('type');
+} catch (err) {
+  var type = require('component-type');
+}
+
+var toFunction = require('to-function');
+
+/**
+ * HOP reference.
+ */
+
+var has = Object.prototype.hasOwnProperty;
+
+/**
+ * Iterate the given `obj` and invoke `fn(val, i)`
+ * in optional context `ctx`.
+ *
+ * @param {String|Array|Object} obj
+ * @param {Function} fn
+ * @param {Object} [ctx]
+ * @api public
+ */
+
+module.exports = function(obj, fn, ctx){
+  fn = toFunction(fn);
+  ctx = ctx || this;
+  switch (type(obj)) {
+    case 'array':
+      return array(obj, fn, ctx);
+    case 'object':
+      if ('number' == typeof obj.length) return array(obj, fn, ctx);
+      return object(obj, fn, ctx);
+    case 'string':
+      return string(obj, fn, ctx);
+  }
+};
+
+/**
+ * Iterate string chars.
+ *
+ * @param {String} obj
+ * @param {Function} fn
+ * @param {Object} ctx
+ * @api private
+ */
+
+function string(obj, fn, ctx) {
+  for (var i = 0; i < obj.length; ++i) {
+    fn.call(ctx, obj.charAt(i), i);
+  }
+}
+
+/**
+ * Iterate object keys.
+ *
+ * @param {Object} obj
+ * @param {Function} fn
+ * @param {Object} ctx
+ * @api private
+ */
+
+function object(obj, fn, ctx) {
+  for (var key in obj) {
+    if (has.call(obj, key)) {
+      fn.call(ctx, key, obj[key]);
+    }
+  }
+}
+
+/**
+ * Iterate array-ish.
+ *
+ * @param {Array|Object} obj
+ * @param {Function} fn
+ * @param {Object} ctx
+ * @api private
+ */
+
+function array(obj, fn, ctx) {
+  for (var i = 0; i < obj.length; ++i) {
+    fn.call(ctx, obj[i], i);
+  }
+}
+
+},{"component-type":51,"to-function":95,"type":51}],51:[function(require,module,exports){
+
+/**
+ * toString ref.
+ */
+
+var toString = Object.prototype.toString;
+
+/**
+ * Return the type of `val`.
+ *
+ * @param {Mixed} val
+ * @return {String}
+ * @api public
+ */
+
+module.exports = function(val){
+  switch (toString.call(val)) {
+    case '[object Function]': return 'function';
+    case '[object Date]': return 'date';
+    case '[object RegExp]': return 'regexp';
+    case '[object Arguments]': return 'arguments';
+    case '[object Array]': return 'array';
+    case '[object String]': return 'string';
+  }
+
+  if (val === null) return 'null';
+  if (val === undefined) return 'undefined';
+  if (val && val.nodeType === 1) return 'element';
+  if (val === Object(val)) return 'object';
+
+  return typeof val;
+};
+
+},{}],52:[function(require,module,exports){
+
+/**
+ * Expose `Emitter`.
+ */
+
+if (typeof module !== 'undefined') {
+  module.exports = Emitter;
+}
+
+/**
+ * Initialize a new `Emitter`.
+ *
+ * @api public
+ */
+
+function Emitter(obj) {
+  if (obj) return mixin(obj);
+};
+
+/**
+ * Mixin the emitter properties.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+function mixin(obj) {
+  for (var key in Emitter.prototype) {
+    obj[key] = Emitter.prototype[key];
+  }
+  return obj;
+}
+
+/**
+ * Listen on the given `event` with `fn`.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.on =
+Emitter.prototype.addEventListener = function(event, fn){
+  this._callbacks = this._callbacks || {};
+  (this._callbacks['$' + event] = this._callbacks['$' + event] || [])
+    .push(fn);
+  return this;
+};
+
+/**
+ * Adds an `event` listener that will be invoked a single
+ * time then automatically removed.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.once = function(event, fn){
+  function on() {
+    this.off(event, on);
+    fn.apply(this, arguments);
+  }
+
+  on.fn = fn;
+  this.on(event, on);
+  return this;
+};
+
+/**
+ * Remove the given callback for `event` or all
+ * registered callbacks.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.off =
+Emitter.prototype.removeListener =
+Emitter.prototype.removeAllListeners =
+Emitter.prototype.removeEventListener = function(event, fn){
+  this._callbacks = this._callbacks || {};
+
+  // all
+  if (0 == arguments.length) {
+    this._callbacks = {};
+    return this;
+  }
+
+  // specific event
+  var callbacks = this._callbacks['$' + event];
+  if (!callbacks) return this;
+
+  // remove all handlers
+  if (1 == arguments.length) {
+    delete this._callbacks['$' + event];
+    return this;
+  }
+
+  // remove specific handler
+  var cb;
+  for (var i = 0; i < callbacks.length; i++) {
+    cb = callbacks[i];
+    if (cb === fn || cb.fn === fn) {
+      callbacks.splice(i, 1);
+      break;
+    }
+  }
+
+  // Remove event specific arrays for event types that no
+  // one is subscribed for to avoid memory leak.
+  if (callbacks.length === 0) {
+    delete this._callbacks['$' + event];
+  }
+
+  return this;
+};
+
+/**
+ * Emit `event` with the given args.
+ *
+ * @param {String} event
+ * @param {Mixed} ...
+ * @return {Emitter}
+ */
+
+Emitter.prototype.emit = function(event){
+  this._callbacks = this._callbacks || {};
+
+  var args = new Array(arguments.length - 1)
+    , callbacks = this._callbacks['$' + event];
+
+  for (var i = 1; i < arguments.length; i++) {
+    args[i - 1] = arguments[i];
+  }
+
+  if (callbacks) {
+    callbacks = callbacks.slice(0);
+    for (var i = 0, len = callbacks.length; i < len; ++i) {
+      callbacks[i].apply(this, args);
+    }
+  }
+
+  return this;
+};
+
+/**
+ * Return array of callbacks for `event`.
+ *
+ * @param {String} event
+ * @return {Array}
+ * @api public
+ */
+
+Emitter.prototype.listeners = function(event){
+  this._callbacks = this._callbacks || {};
+  return this._callbacks['$' + event] || [];
+};
+
+/**
+ * Check if this emitter has `event` handlers.
+ *
+ * @param {String} event
+ * @return {Boolean}
+ * @api public
+ */
+
+Emitter.prototype.hasListeners = function(event){
+  return !! this.listeners(event).length;
+};
+
+},{}],53:[function(require,module,exports){
+var bind = window.addEventListener ? 'addEventListener' : 'attachEvent',
+    unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
+    prefix = bind !== 'addEventListener' ? 'on' : '';
+
+/**
+ * Bind `el` event `type` to `fn`.
+ *
+ * @param {Element} el
+ * @param {String} type
+ * @param {Function} fn
+ * @param {Boolean} capture
+ * @return {Function}
+ * @api public
+ */
+
+exports.bind = function(el, type, fn, capture){
+  el[bind](prefix + type, fn, capture || false);
+  return fn;
+};
+
+/**
+ * Unbind `el` event `type`'s callback `fn`.
+ *
+ * @param {Element} el
+ * @param {String} type
+ * @param {Function} fn
+ * @param {Boolean} capture
+ * @return {Function}
+ * @api public
+ */
+
+exports.unbind = function(el, type, fn, capture){
+  el[unbind](prefix + type, fn, capture || false);
+  return fn;
+};
+},{}],54:[function(require,module,exports){
+/**
+ * Global Names
+ */
+
+var globals = /\b(Array|Date|Object|Math|JSON)\b/g;
+
+/**
+ * Return immediate identifiers parsed from `str`.
+ *
+ * @param {String} str
+ * @param {String|Function} map function or prefix
+ * @return {Array}
+ * @api public
+ */
+
+module.exports = function(str, fn){
+  var p = unique(props(str));
+  if (fn && 'string' == typeof fn) fn = prefixed(fn);
+  if (fn) return map(str, p, fn);
+  return p;
+};
+
+/**
+ * Return immediate identifiers in `str`.
+ *
+ * @param {String} str
+ * @return {Array}
+ * @api private
+ */
+
+function props(str) {
+  return str
+    .replace(/\.\w+|\w+ *\(|"[^"]*"|'[^']*'|\/([^/]+)\//g, '')
+    .replace(globals, '')
+    .match(/[a-zA-Z_]\w*/g)
+    || [];
+}
+
+/**
+ * Return `str` with `props` mapped with `fn`.
+ *
+ * @param {String} str
+ * @param {Array} props
+ * @param {Function} fn
+ * @return {String}
+ * @api private
+ */
+
+function map(str, props, fn) {
+  var re = /\.\w+|\w+ *\(|"[^"]*"|'[^']*'|\/([^/]+)\/|[a-zA-Z_]\w*/g;
+  return str.replace(re, function(_){
+    if ('(' == _[_.length - 1]) return fn(_);
+    if (!~props.indexOf(_)) return _;
+    return fn(_);
+  });
+}
+
+/**
+ * Return unique array.
+ *
+ * @param {Array} arr
+ * @return {Array}
+ * @api private
+ */
+
+function unique(arr) {
+  var ret = [];
+
+  for (var i = 0; i < arr.length; i++) {
+    if (~ret.indexOf(arr[i])) continue;
+    ret.push(arr[i]);
+  }
+
+  return ret;
+}
+
+/**
+ * Map with prefix `str`.
+ */
+
+function prefixed(str) {
+  return function(_){
+    return str + _;
+  };
+}
+
+},{}],55:[function(require,module,exports){
+
+/**
+ * Module dependencies.
+ */
+
+var trim = require('trim');
+var type = require('type');
+
+var pattern = /(\w+)\[(\d+)\]/
+
+/**
+ * Safely encode the given string
+ * 
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+var encode = function(str) {
+  try {
+    return encodeURIComponent(str);
+  } catch (e) {
+    return str;
+  }
+};
+
+/**
+ * Safely decode the string
+ * 
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+var decode = function(str) {
+  try {
+    return decodeURIComponent(str.replace(/\+/g, ' '));
+  } catch (e) {
+    return str;
+  }
+}
+
+/**
+ * Parse the given query `str`.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api public
+ */
+
+exports.parse = function(str){
+  if ('string' != typeof str) return {};
+
+  str = trim(str);
+  if ('' == str) return {};
+  if ('?' == str.charAt(0)) str = str.slice(1);
+
+  var obj = {};
+  var pairs = str.split('&');
+  for (var i = 0; i < pairs.length; i++) {
+    var parts = pairs[i].split('=');
+    var key = decode(parts[0]);
+    var m;
+
+    if (m = pattern.exec(key)) {
+      obj[m[1]] = obj[m[1]] || [];
+      obj[m[1]][m[2]] = decode(parts[1]);
+      continue;
+    }
+
+    obj[parts[0]] = null == parts[1]
+      ? ''
+      : decode(parts[1]);
+  }
+
+  return obj;
+};
+
+/**
+ * Stringify the given `obj`.
+ *
+ * @param {Object} obj
+ * @return {String}
+ * @api public
+ */
+
+exports.stringify = function(obj){
+  if (!obj) return '';
+  var pairs = [];
+
+  for (var key in obj) {
+    var value = obj[key];
+
+    if ('array' == type(value)) {
+      for (var i = 0; i < value.length; ++i) {
+        pairs.push(encode(key + '[' + i + ']') + '=' + encode(value[i]));
+      }
+      continue;
+    }
+
+    pairs.push(encode(key) + '=' + encode(obj[key]));
+  }
+
+  return pairs.join('&');
+};
+
+},{"trim":97,"type":56}],56:[function(require,module,exports){
+/**
+ * toString ref.
+ */
+
+var toString = Object.prototype.toString;
+
+/**
+ * Return the type of `val`.
+ *
+ * @param {Mixed} val
+ * @return {String}
+ * @api public
+ */
+
+module.exports = function(val){
+  switch (toString.call(val)) {
+    case '[object Date]': return 'date';
+    case '[object RegExp]': return 'regexp';
+    case '[object Arguments]': return 'arguments';
+    case '[object Array]': return 'array';
+    case '[object Error]': return 'error';
+  }
+
+  if (val === null) return 'null';
+  if (val === undefined) return 'undefined';
+  if (val !== val) return 'nan';
+  if (val && val.nodeType === 1) return 'element';
+
+  val = val.valueOf
+    ? val.valueOf()
+    : Object.prototype.valueOf.apply(val)
+
+  return typeof val;
+};
+
+},{}],57:[function(require,module,exports){
+/**
+ * toString ref.
+ */
+
+var toString = Object.prototype.toString;
+
+/**
+ * Return the type of `val`.
+ *
+ * @param {Mixed} val
+ * @return {String}
+ * @api public
+ */
+
+module.exports = function(val){
+  switch (toString.call(val)) {
+    case '[object Date]': return 'date';
+    case '[object RegExp]': return 'regexp';
+    case '[object Arguments]': return 'arguments';
+    case '[object Array]': return 'array';
+    case '[object Error]': return 'error';
+  }
+
+  if (val === null) return 'null';
+  if (val === undefined) return 'undefined';
+  if (val !== val) return 'nan';
+  if (val && val.nodeType === 1) return 'element';
+
+  if (isBuffer(val)) return 'buffer';
+
+  val = val.valueOf
+    ? val.valueOf()
+    : Object.prototype.valueOf.apply(val);
+
+  return typeof val;
+};
+
+// code borrowed from https://github.com/feross/is-buffer/blob/master/index.js
+function isBuffer(obj) {
+  return !!(obj != null &&
+    (obj._isBuffer || // For Safari 5-7 (missing Object.prototype.constructor)
+      (obj.constructor &&
+      typeof obj.constructor.isBuffer === 'function' &&
+      obj.constructor.isBuffer(obj))
+    ))
+}
+
+},{}],58:[function(require,module,exports){
+
+/**
+ * Parse the given `url`.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api public
+ */
+
+exports.parse = function(url){
+  var a = document.createElement('a');
+  a.href = url;
+  return {
+    href: a.href,
+    host: a.host || location.host,
+    port: ('0' === a.port || '' === a.port) ? port(a.protocol) : a.port,
+    hash: a.hash,
+    hostname: a.hostname || location.hostname,
+    pathname: a.pathname.charAt(0) != '/' ? '/' + a.pathname : a.pathname,
+    protocol: !a.protocol || ':' == a.protocol ? location.protocol : a.protocol,
+    search: a.search,
+    query: a.search.slice(1)
+  };
+};
+
+/**
+ * Check if `url` is absolute.
+ *
+ * @param {String} url
+ * @return {Boolean}
+ * @api public
+ */
+
+exports.isAbsolute = function(url){
+  return 0 == url.indexOf('//') || !!~url.indexOf('://');
+};
+
+/**
+ * Check if `url` is relative.
+ *
+ * @param {String} url
+ * @return {Boolean}
+ * @api public
+ */
+
+exports.isRelative = function(url){
+  return !exports.isAbsolute(url);
+};
+
+/**
+ * Check if `url` is cross domain.
+ *
+ * @param {String} url
+ * @return {Boolean}
+ * @api public
+ */
+
+exports.isCrossDomain = function(url){
+  url = exports.parse(url);
+  var location = exports.parse(window.location.href);
+  return url.hostname !== location.hostname
+    || url.port !== location.port
+    || url.protocol !== location.protocol;
+};
+
+/**
+ * Return default port for `protocol`.
+ *
+ * @param  {String} protocol
+ * @return {String}
+ * @api private
+ */
+function port (protocol){
+  switch (protocol) {
+    case 'http:':
+      return 80;
+    case 'https:':
+      return 443;
+    default:
+      return location.port;
+  }
+}
+
+},{}],59:[function(require,module,exports){
+(function (process){(function (){
+/**
+ * This is the web browser implementation of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = require('./debug');
+exports.log = log;
+exports.formatArgs = formatArgs;
+exports.save = save;
+exports.load = load;
+exports.useColors = useColors;
+exports.storage = 'undefined' != typeof chrome
+               && 'undefined' != typeof chrome.storage
+                  ? chrome.storage.local
+                  : localstorage();
+
+/**
+ * Colors.
+ */
+
+exports.colors = [
+  'lightseagreen',
+  'forestgreen',
+  'goldenrod',
+  'dodgerblue',
+  'darkorchid',
+  'crimson'
+];
+
+/**
+ * Currently only WebKit-based Web Inspectors, Firefox >= v31,
+ * and the Firebug extension (any Firefox version) are known
+ * to support "%c" CSS customizations.
+ *
+ * TODO: add a `localStorage` variable to explicitly enable/disable colors
+ */
+
+function useColors() {
+  // NB: In an Electron preload script, document will be defined but not fully
+  // initialized. Since we know we're in Chrome, we'll just detect this case
+  // explicitly
+  if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
+    return true;
+  }
+
+  // is webkit? http://stackoverflow.com/a/16459606/376773
+  // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
+  return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
+    // is firebug? http://stackoverflow.com/a/398120/376773
+    (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
+    // is firefox >= v31?
+    // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
+    (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
+    // double check webkit in userAgent just in case we are in a worker
+    (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
+}
+
+/**
+ * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
+ */
+
+exports.formatters.j = function(v) {
+  try {
+    return JSON.stringify(v);
+  } catch (err) {
+    return '[UnexpectedJSONParseError]: ' + err.message;
+  }
+};
+
+
+/**
+ * Colorize log arguments if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs(args) {
+  var useColors = this.useColors;
+
+  args[0] = (useColors ? '%c' : '')
+    + this.namespace
+    + (useColors ? ' %c' : ' ')
+    + args[0]
+    + (useColors ? '%c ' : ' ')
+    + '+' + exports.humanize(this.diff);
+
+  if (!useColors) return;
+
+  var c = 'color: ' + this.color;
+  args.splice(1, 0, c, 'color: inherit')
+
+  // the final "%c" is somewhat tricky, because there could be other
+  // arguments passed either before or after the %c, so we need to
+  // figure out the correct index to insert the CSS into
+  var index = 0;
+  var lastC = 0;
+  args[0].replace(/%[a-zA-Z%]/g, function(match) {
+    if ('%%' === match) return;
+    index++;
+    if ('%c' === match) {
+      // we only are interested in the *last* %c
+      // (the user may have provided their own)
+      lastC = index;
+    }
+  });
+
+  args.splice(lastC, 0, c);
+}
+
+/**
+ * Invokes `console.log()` when available.
+ * No-op when `console.log` is not a "function".
+ *
+ * @api public
+ */
+
+function log() {
+  // this hackery is required for IE8/9, where
+  // the `console.log` function doesn't have 'apply'
+  return 'object' === typeof console
+    && console.log
+    && Function.prototype.apply.call(console.log, console, arguments);
+}
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+
+function save(namespaces) {
+  try {
+    if (null == namespaces) {
+      exports.storage.removeItem('debug');
+    } else {
+      exports.storage.debug = namespaces;
+    }
+  } catch(e) {}
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+
+function load() {
+  var r;
+  try {
+    r = exports.storage.debug;
+  } catch(e) {}
+
+  // If debug isn't set in LS, and we're in Electron, try to load $DEBUG
+  if (!r && typeof process !== 'undefined' && 'env' in process) {
+    r = process.env.DEBUG;
+  }
+
+  return r;
+}
+
+/**
+ * Enable namespaces listed in `localStorage.debug` initially.
+ */
+
+exports.enable(load());
+
+/**
+ * Localstorage attempts to return the localstorage.
+ *
+ * This is necessary because safari throws
+ * when a user disables cookies/localstorage
+ * and you attempt to access it.
+ *
+ * @return {LocalStorage}
+ * @api private
+ */
+
+function localstorage() {
+  try {
+    return window.localStorage;
+  } catch (e) {}
+}
+
+}).call(this)}).call(this,require('_process'))
+},{"./debug":60,"_process":78}],60:[function(require,module,exports){
+
+/**
+ * This is the common logic for both the Node.js and web browser
+ * implementations of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = createDebug.debug = createDebug['default'] = createDebug;
+exports.coerce = coerce;
+exports.disable = disable;
+exports.enable = enable;
+exports.enabled = enabled;
+exports.humanize = require('ms');
+
+/**
+ * The currently active debug mode names, and names to skip.
+ */
+
+exports.names = [];
+exports.skips = [];
+
+/**
+ * Map of special "%n" handling functions, for the debug "format" argument.
+ *
+ * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
+ */
+
+exports.formatters = {};
+
+/**
+ * Previous log timestamp.
+ */
+
+var prevTime;
+
+/**
+ * Select a color.
+ * @param {String} namespace
+ * @return {Number}
+ * @api private
+ */
+
+function selectColor(namespace) {
+  var hash = 0, i;
+
+  for (i in namespace) {
+    hash  = ((hash << 5) - hash) + namespace.charCodeAt(i);
+    hash |= 0; // Convert to 32bit integer
+  }
+
+  return exports.colors[Math.abs(hash) % exports.colors.length];
+}
+
+/**
+ * Create a debugger with the given `namespace`.
+ *
+ * @param {String} namespace
+ * @return {Function}
+ * @api public
+ */
+
+function createDebug(namespace) {
+
+  function debug() {
+    // disabled?
+    if (!debug.enabled) return;
+
+    var self = debug;
+
+    // set `diff` timestamp
+    var curr = +new Date();
+    var ms = curr - (prevTime || curr);
+    self.diff = ms;
+    self.prev = prevTime;
+    self.curr = curr;
+    prevTime = curr;
+
+    // turn the `arguments` into a proper Array
+    var args = new Array(arguments.length);
+    for (var i = 0; i < args.length; i++) {
+      args[i] = arguments[i];
+    }
+
+    args[0] = exports.coerce(args[0]);
+
+    if ('string' !== typeof args[0]) {
+      // anything else let's inspect with %O
+      args.unshift('%O');
+    }
+
+    // apply any `formatters` transformations
+    var index = 0;
+    args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) {
+      // if we encounter an escaped % then don't increase the array index
+      if (match === '%%') return match;
+      index++;
+      var formatter = exports.formatters[format];
+      if ('function' === typeof formatter) {
+        var val = args[index];
+        match = formatter.call(self, val);
+
+        // now we need to remove `args[index]` since it's inlined in the `format`
+        args.splice(index, 1);
+        index--;
+      }
+      return match;
+    });
+
+    // apply env-specific formatting (colors, etc.)
+    exports.formatArgs.call(self, args);
+
+    var logFn = debug.log || exports.log || console.log.bind(console);
+    logFn.apply(self, args);
+  }
+
+  debug.namespace = namespace;
+  debug.enabled = exports.enabled(namespace);
+  debug.useColors = exports.useColors();
+  debug.color = selectColor(namespace);
+
+  // env-specific initialization logic for debug instances
+  if ('function' === typeof exports.init) {
+    exports.init(debug);
+  }
+
+  return debug;
+}
+
+/**
+ * Enables a debug mode by namespaces. This can include modes
+ * separated by a colon and wildcards.
+ *
+ * @param {String} namespaces
+ * @api public
+ */
+
+function enable(namespaces) {
+  exports.save(namespaces);
+
+  exports.names = [];
+  exports.skips = [];
+
+  var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
+  var len = split.length;
+
+  for (var i = 0; i < len; i++) {
+    if (!split[i]) continue; // ignore empty strings
+    namespaces = split[i].replace(/\*/g, '.*?');
+    if (namespaces[0] === '-') {
+      exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
+    } else {
+      exports.names.push(new RegExp('^' + namespaces + '$'));
+    }
+  }
+}
+
+/**
+ * Disable debug output.
+ *
+ * @api public
+ */
+
+function disable() {
+  exports.enable('');
+}
+
+/**
+ * Returns true if the given mode name is enabled, false otherwise.
+ *
+ * @param {String} name
+ * @return {Boolean}
+ * @api public
+ */
+
+function enabled(name) {
+  var i, len;
+  for (i = 0, len = exports.skips.length; i < len; i++) {
+    if (exports.skips[i].test(name)) {
+      return false;
+    }
+  }
+  for (i = 0, len = exports.names.length; i < len; i++) {
+    if (exports.names[i].test(name)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Coerce `val`.
+ *
+ * @param {Mixed} val
+ * @return {Mixed}
+ * @api private
+ */
+
+function coerce(val) {
+  if (val instanceof Error) return val.stack || val.message;
+  return val;
+}
+
+},{"ms":71}],61:[function(require,module,exports){
+
+/**
+ * Expose `parse`.
+ */
+
+module.exports = parse;
+
+/**
+ * Tests for browser support.
+ */
+
+var innerHTMLBug = false;
+var bugTestDiv;
+if (typeof document !== 'undefined') {
+  bugTestDiv = document.createElement('div');
+  // Setup
+  bugTestDiv.innerHTML = '  <link/><table></table><a href="/a">a</a><input type="checkbox"/>';
+  // Make sure that link elements get serialized correctly by innerHTML
+  // This requires a wrapper element in IE
+  innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length;
+  bugTestDiv = undefined;
+}
+
+/**
+ * Wrap map from jquery.
+ */
+
+var map = {
+  legend: [1, '<fieldset>', '</fieldset>'],
+  tr: [2, '<table><tbody>', '</tbody></table>'],
+  col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
+  // for script/link/style tags to work in IE6-8, you have to wrap
+  // in a div with a non-whitespace character in front, ha!
+  _default: innerHTMLBug ? [1, 'X<div>', '</div>'] : [0, '', '']
+};
+
+map.td =
+map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'];
+
+map.option =
+map.optgroup = [1, '<select multiple="multiple">', '</select>'];
+
+map.thead =
+map.tbody =
+map.colgroup =
+map.caption =
+map.tfoot = [1, '<table>', '</table>'];
+
+map.polyline =
+map.ellipse =
+map.polygon =
+map.circle =
+map.text =
+map.line =
+map.path =
+map.rect =
+map.g = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>'];
+
+/**
+ * Parse `html` and return a DOM Node instance, which could be a TextNode,
+ * HTML DOM Node of some kind (<div> for example), or a DocumentFragment
+ * instance, depending on the contents of the `html` string.
+ *
+ * @param {String} html - HTML string to "domify"
+ * @param {Document} doc - The `document` instance to create the Node for
+ * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance
+ * @api private
+ */
+
+function parse(html, doc) {
+  if ('string' != typeof html) throw new TypeError('String expected');
+
+  // default to the global `document` object
+  if (!doc) doc = document;
+
+  // tag name
+  var m = /<([\w:]+)/.exec(html);
+  if (!m) return doc.createTextNode(html);
+
+  html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace
+
+  var tag = m[1];
+
+  // body support
+  if (tag == 'body') {
+    var el = doc.createElement('html');
+    el.innerHTML = html;
+    return el.removeChild(el.lastChild);
+  }
+
+  // wrap map
+  var wrap = map[tag] || map._default;
+  var depth = wrap[0];
+  var prefix = wrap[1];
+  var suffix = wrap[2];
+  var el = doc.createElement('div');
+  el.innerHTML = prefix + html + suffix;
+  while (depth--) el = el.lastChild;
+
+  // one element
+  if (el.firstChild == el.lastChild) {
+    return el.removeChild(el.firstChild);
+  }
+
+  // several elements
+  var fragment = doc.createDocumentFragment();
+  while (el.firstChild) {
+    fragment.appendChild(el.removeChild(el.firstChild));
+  }
+
+  return fragment;
+}
+
+},{}],62:[function(require,module,exports){
+'use strict';
+
+var hasOwn = Object.prototype.hasOwnProperty;
+var toStr = Object.prototype.toString;
+var defineProperty = Object.defineProperty;
+var gOPD = Object.getOwnPropertyDescriptor;
+
+var isArray = function isArray(arr) {
+	if (typeof Array.isArray === 'function') {
+		return Array.isArray(arr);
+	}
+
+	return toStr.call(arr) === '[object Array]';
+};
+
+var isPlainObject = function isPlainObject(obj) {
+	if (!obj || toStr.call(obj) !== '[object Object]') {
+		return false;
+	}
+
+	var hasOwnConstructor = hasOwn.call(obj, 'constructor');
+	var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
+	// Not own constructor property must be Object
+	if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
+		return false;
+	}
+
+	// Own properties are enumerated firstly, so to speed up,
+	// if last one is own, then all properties are own.
+	var key;
+	for (key in obj) { /**/ }
+
+	return typeof key === 'undefined' || hasOwn.call(obj, key);
+};
+
+// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target
+var setProperty = function setProperty(target, options) {
+	if (defineProperty && options.name === '__proto__') {
+		defineProperty(target, options.name, {
+			enumerable: true,
+			configurable: true,
+			value: options.newValue,
+			writable: true
+		});
+	} else {
+		target[options.name] = options.newValue;
+	}
+};
+
+// Return undefined instead of __proto__ if '__proto__' is not an own property
+var getProperty = function getProperty(obj, name) {
+	if (name === '__proto__') {
+		if (!hasOwn.call(obj, name)) {
+			return void 0;
+		} else if (gOPD) {
+			// In early versions of node, obj['__proto__'] is buggy when obj has
+			// __proto__ as an own property. Object.getOwnPropertyDescriptor() works.
+			return gOPD(obj, name).value;
+		}
+	}
+
+	return obj[name];
+};
+
+module.exports = function extend() {
+	var options, name, src, copy, copyIsArray, clone;
+	var target = arguments[0];
+	var i = 1;
+	var length = arguments.length;
+	var deep = false;
+
+	// Handle a deep copy situation
+	if (typeof target === 'boolean') {
+		deep = target;
+		target = arguments[1] || {};
+		// skip the boolean and the target
+		i = 2;
+	}
+	if (target == null || (typeof target !== 'object' && typeof target !== 'function')) {
+		target = {};
+	}
+
+	for (; i < length; ++i) {
+		options = arguments[i];
+		// Only deal with non-null/undefined values
+		if (options != null) {
+			// Extend the base object
+			for (name in options) {
+				src = getProperty(target, name);
+				copy = getProperty(options, name);
+
+				// Prevent never-ending loop
+				if (target !== copy) {
+					// Recurse if we're merging plain objects or arrays
+					if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
+						if (copyIsArray) {
+							copyIsArray = false;
+							clone = src && isArray(src) ? src : [];
+						} else {
+							clone = src && isPlainObject(src) ? src : {};
+						}
+
+						// Never move original objects, clone them
+						setProperty(target, { name: name, newValue: extend(deep, clone, copy) });
+
+					// Don't bring in undefined values
+					} else if (typeof copy !== 'undefined') {
+						setProperty(target, { name: name, newValue: copy });
+					}
+				}
+			}
+		}
+	}
+
+	// Return the modified object
+	return target;
+};
+
+},{}],63:[function(require,module,exports){
+
+/**
+ * Module exports.
+ *
+ * Logic borrowed from Modernizr:
+ *
+ *   - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js
+ */
+
+try {
+  module.exports = typeof XMLHttpRequest !== 'undefined' &&
+    'withCredentials' in new XMLHttpRequest();
+} catch (err) {
+  // if XMLHttp support is disabled in IE then it will throw
+  // when trying to create
+  module.exports = false;
+}
+
+},{}],64:[function(require,module,exports){
+if (typeof Object.create === 'function') {
+  // implementation from standard node.js 'util' module
+  module.exports = function inherits(ctor, superCtor) {
+    if (superCtor) {
+      ctor.super_ = superCtor
+      ctor.prototype = Object.create(superCtor.prototype, {
+        constructor: {
+          value: ctor,
+          enumerable: false,
+          writable: true,
+          configurable: true
+        }
+      })
+    }
+  };
+} else {
+  // old school shim for old browsers
+  module.exports = function inherits(ctor, superCtor) {
+    if (superCtor) {
+      ctor.super_ = superCtor
+      var TempCtor = function () {}
+      TempCtor.prototype = superCtor.prototype
+      ctor.prototype = new TempCtor()
+      ctor.prototype.constructor = ctor
+    }
+  }
+}
+
+},{}],65:[function(require,module,exports){
+
+module.exports = function isEmail (string) {
+    return (/.+\@.+\..+/).test(string);
+};
+},{}],66:[function(require,module,exports){
+/* globals window, HTMLElement */
+
+'use strict';
+
+/**!
+ * is
+ * the definitive JavaScript type testing library
+ *
+ * @copyright 2013-2014 Enrico Marino / Jordan Harband
+ * @license MIT
+ */
+
+var objProto = Object.prototype;
+var owns = objProto.hasOwnProperty;
+var toStr = objProto.toString;
+var symbolValueOf;
+if (typeof Symbol === 'function') {
+  symbolValueOf = Symbol.prototype.valueOf;
+}
+var bigIntValueOf;
+if (typeof BigInt === 'function') {
+  bigIntValueOf = BigInt.prototype.valueOf;
+}
+var isActualNaN = function (value) {
+  return value !== value;
+};
+var NON_HOST_TYPES = {
+  'boolean': 1,
+  number: 1,
+  string: 1,
+  undefined: 1
+};
+
+var base64Regex = /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/;
+var hexRegex = /^[A-Fa-f0-9]+$/;
+
+/**
+ * Expose `is`
+ */
+
+var is = {};
+
+/**
+ * Test general.
+ */
+
+/**
+ * is.type
+ * Test if `value` is a type of `type`.
+ *
+ * @param {*} value value to test
+ * @param {String} type type
+ * @return {Boolean} true if `value` is a type of `type`, false otherwise
+ * @api public
+ */
+
+is.a = is.type = function (value, type) {
+  return typeof value === type;
+};
+
+/**
+ * is.defined
+ * Test if `value` is defined.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if 'value' is defined, false otherwise
+ * @api public
+ */
+
+is.defined = function (value) {
+  return typeof value !== 'undefined';
+};
+
+/**
+ * is.empty
+ * Test if `value` is empty.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is empty, false otherwise
+ * @api public
+ */
+
+is.empty = function (value) {
+  var type = toStr.call(value);
+  var key;
+
+  if (type === '[object Array]' || type === '[object Arguments]' || type === '[object String]') {
+    return value.length === 0;
+  }
+
+  if (type === '[object Object]') {
+    for (key in value) {
+      if (owns.call(value, key)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  return !value;
+};
+
+/**
+ * is.equal
+ * Test if `value` is equal to `other`.
+ *
+ * @param {*} value value to test
+ * @param {*} other value to compare with
+ * @return {Boolean} true if `value` is equal to `other`, false otherwise
+ */
+
+is.equal = function equal(value, other) {
+  if (value === other) {
+    return true;
+  }
+
+  var type = toStr.call(value);
+  var key;
+
+  if (type !== toStr.call(other)) {
+    return false;
+  }
+
+  if (type === '[object Object]') {
+    for (key in value) {
+      if (!is.equal(value[key], other[key]) || !(key in other)) {
+        return false;
+      }
+    }
+    for (key in other) {
+      if (!is.equal(value[key], other[key]) || !(key in value)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  if (type === '[object Array]') {
+    key = value.length;
+    if (key !== other.length) {
+      return false;
+    }
+    while (key--) {
+      if (!is.equal(value[key], other[key])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  if (type === '[object Function]') {
+    return value.prototype === other.prototype;
+  }
+
+  if (type === '[object Date]') {
+    return value.getTime() === other.getTime();
+  }
+
+  return false;
+};
+
+/**
+ * is.hosted
+ * Test if `value` is hosted by `host`.
+ *
+ * @param {*} value to test
+ * @param {*} host host to test with
+ * @return {Boolean} true if `value` is hosted by `host`, false otherwise
+ * @api public
+ */
+
+is.hosted = function (value, host) {
+  var type = typeof host[value];
+  return type === 'object' ? !!host[value] : !NON_HOST_TYPES[type];
+};
+
+/**
+ * is.instance
+ * Test if `value` is an instance of `constructor`.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is an instance of `constructor`
+ * @api public
+ */
+
+is.instance = is['instanceof'] = function (value, constructor) {
+  return value instanceof constructor;
+};
+
+/**
+ * is.nil / is.null
+ * Test if `value` is null.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is null, false otherwise
+ * @api public
+ */
+
+is.nil = is['null'] = function (value) {
+  return value === null;
+};
+
+/**
+ * is.undef / is.undefined
+ * Test if `value` is undefined.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is undefined, false otherwise
+ * @api public
+ */
+
+is.undef = is.undefined = function (value) {
+  return typeof value === 'undefined';
+};
+
+/**
+ * Test arguments.
+ */
+
+/**
+ * is.args
+ * Test if `value` is an arguments object.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is an arguments object, false otherwise
+ * @api public
+ */
+
+is.args = is.arguments = function (value) {
+  var isStandardArguments = toStr.call(value) === '[object Arguments]';
+  var isOldArguments = !is.array(value) && is.arraylike(value) && is.object(value) && is.fn(value.callee);
+  return isStandardArguments || isOldArguments;
+};
+
+/**
+ * Test array.
+ */
+
+/**
+ * is.array
+ * Test if 'value' is an array.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is an array, false otherwise
+ * @api public
+ */
+
+is.array = Array.isArray || function (value) {
+  return toStr.call(value) === '[object Array]';
+};
+
+/**
+ * is.arguments.empty
+ * Test if `value` is an empty arguments object.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is an empty arguments object, false otherwise
+ * @api public
+ */
+is.args.empty = function (value) {
+  return is.args(value) && value.length === 0;
+};
+
+/**
+ * is.array.empty
+ * Test if `value` is an empty array.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is an empty array, false otherwise
+ * @api public
+ */
+is.array.empty = function (value) {
+  return is.array(value) && value.length === 0;
+};
+
+/**
+ * is.arraylike
+ * Test if `value` is an arraylike object.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is an arguments object, false otherwise
+ * @api public
+ */
+
+is.arraylike = function (value) {
+  return !!value && !is.bool(value)
+    && owns.call(value, 'length')
+    && isFinite(value.length)
+    && is.number(value.length)
+    && value.length >= 0;
+};
+
+/**
+ * Test boolean.
+ */
+
+/**
+ * is.bool
+ * Test if `value` is a boolean.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is a boolean, false otherwise
+ * @api public
+ */
+
+is.bool = is['boolean'] = function (value) {
+  return toStr.call(value) === '[object Boolean]';
+};
+
+/**
+ * is.false
+ * Test if `value` is false.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is false, false otherwise
+ * @api public
+ */
+
+is['false'] = function (value) {
+  return is.bool(value) && Boolean(Number(value)) === false;
+};
+
+/**
+ * is.true
+ * Test if `value` is true.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is true, false otherwise
+ * @api public
+ */
+
+is['true'] = function (value) {
+  return is.bool(value) && Boolean(Number(value)) === true;
+};
+
+/**
+ * Test date.
+ */
+
+/**
+ * is.date
+ * Test if `value` is a date.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is a date, false otherwise
+ * @api public
+ */
+
+is.date = function (value) {
+  return toStr.call(value) === '[object Date]';
+};
+
+/**
+ * is.date.valid
+ * Test if `value` is a valid date.
+ *
+ * @param {*} value value to test
+ * @returns {Boolean} true if `value` is a valid date, false otherwise
+ */
+is.date.valid = function (value) {
+  return is.date(value) && !isNaN(Number(value));
+};
+
+/**
+ * Test element.
+ */
+
+/**
+ * is.element
+ * Test if `value` is an html element.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is an HTML Element, false otherwise
+ * @api public
+ */
+
+is.element = function (value) {
+  return value !== undefined
+    && typeof HTMLElement !== 'undefined'
+    && value instanceof HTMLElement
+    && value.nodeType === 1;
+};
+
+/**
+ * Test error.
+ */
+
+/**
+ * is.error
+ * Test if `value` is an error object.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is an error object, false otherwise
+ * @api public
+ */
+
+is.error = function (value) {
+  return toStr.call(value) === '[object Error]';
+};
+
+/**
+ * Test function.
+ */
+
+/**
+ * is.fn / is.function (deprecated)
+ * Test if `value` is a function.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is a function, false otherwise
+ * @api public
+ */
+
+is.fn = is['function'] = function (value) {
+  var isAlert = typeof window !== 'undefined' && value === window.alert;
+  if (isAlert) {
+    return true;
+  }
+  var str = toStr.call(value);
+  return str === '[object Function]' || str === '[object GeneratorFunction]' || str === '[object AsyncFunction]';
+};
+
+/**
+ * Test number.
+ */
+
+/**
+ * is.number
+ * Test if `value` is a number.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is a number, false otherwise
+ * @api public
+ */
+
+is.number = function (value) {
+  return toStr.call(value) === '[object Number]';
+};
+
+/**
+ * is.infinite
+ * Test if `value` is positive or negative infinity.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is positive or negative Infinity, false otherwise
+ * @api public
+ */
+is.infinite = function (value) {
+  return value === Infinity || value === -Infinity;
+};
+
+/**
+ * is.decimal
+ * Test if `value` is a decimal number.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is a decimal number, false otherwise
+ * @api public
+ */
+
+is.decimal = function (value) {
+  return is.number(value) && !isActualNaN(value) && !is.infinite(value) && value % 1 !== 0;
+};
+
+/**
+ * is.divisibleBy
+ * Test if `value` is divisible by `n`.
+ *
+ * @param {Number} value value to test
+ * @param {Number} n dividend
+ * @return {Boolean} true if `value` is divisible by `n`, false otherwise
+ * @api public
+ */
+
+is.divisibleBy = function (value, n) {
+  var isDividendInfinite = is.infinite(value);
+  var isDivisorInfinite = is.infinite(n);
+  var isNonZeroNumber = is.number(value) && !isActualNaN(value) && is.number(n) && !isActualNaN(n) && n !== 0;
+  return isDividendInfinite || isDivisorInfinite || (isNonZeroNumber && value % n === 0);
+};
+
+/**
+ * is.integer
+ * Test if `value` is an integer.
+ *
+ * @param value to test
+ * @return {Boolean} true if `value` is an integer, false otherwise
+ * @api public
+ */
+
+is.integer = is['int'] = function (value) {
+  return is.number(value) && !isActualNaN(value) && value % 1 === 0;
+};
+
+/**
+ * is.maximum
+ * Test if `value` is greater than 'others' values.
+ *
+ * @param {Number} value value to test
+ * @param {Array} others values to compare with
+ * @return {Boolean} true if `value` is greater than `others` values
+ * @api public
+ */
+
+is.maximum = function (value, others) {
+  if (isActualNaN(value)) {
+    throw new TypeError('NaN is not a valid value');
+  } else if (!is.arraylike(others)) {
+    throw new TypeError('second argument must be array-like');
+  }
+  var len = others.length;
+
+  while (--len >= 0) {
+    if (value < others[len]) {
+      return false;
+    }
+  }
+
+  return true;
+};
+
+/**
+ * is.minimum
+ * Test if `value` is less than `others` values.
+ *
+ * @param {Number} value value to test
+ * @param {Array} others values to compare with
+ * @return {Boolean} true if `value` is less than `others` values
+ * @api public
+ */
+
+is.minimum = function (value, others) {
+  if (isActualNaN(value)) {
+    throw new TypeError('NaN is not a valid value');
+  } else if (!is.arraylike(others)) {
+    throw new TypeError('second argument must be array-like');
+  }
+  var len = others.length;
+
+  while (--len >= 0) {
+    if (value > others[len]) {
+      return false;
+    }
+  }
+
+  return true;
+};
+
+/**
+ * is.nan
+ * Test if `value` is not a number.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is not a number, false otherwise
+ * @api public
+ */
+
+is.nan = function (value) {
+  return !is.number(value) || value !== value;
+};
+
+/**
+ * is.even
+ * Test if `value` is an even number.
+ *
+ * @param {Number} value value to test
+ * @return {Boolean} true if `value` is an even number, false otherwise
+ * @api public
+ */
+
+is.even = function (value) {
+  return is.infinite(value) || (is.number(value) && value === value && value % 2 === 0);
+};
+
+/**
+ * is.odd
+ * Test if `value` is an odd number.
+ *
+ * @param {Number} value value to test
+ * @return {Boolean} true if `value` is an odd number, false otherwise
+ * @api public
+ */
+
+is.odd = function (value) {
+  return is.infinite(value) || (is.number(value) && value === value && value % 2 !== 0);
+};
+
+/**
+ * is.ge
+ * Test if `value` is greater than or equal to `other`.
+ *
+ * @param {Number} value value to test
+ * @param {Number} other value to compare with
+ * @return {Boolean}
+ * @api public
+ */
+
+is.ge = function (value, other) {
+  if (isActualNaN(value) || isActualNaN(other)) {
+    throw new TypeError('NaN is not a valid value');
+  }
+  return !is.infinite(value) && !is.infinite(other) && value >= other;
+};
+
+/**
+ * is.gt
+ * Test if `value` is greater than `other`.
+ *
+ * @param {Number} value value to test
+ * @param {Number} other value to compare with
+ * @return {Boolean}
+ * @api public
+ */
+
+is.gt = function (value, other) {
+  if (isActualNaN(value) || isActualNaN(other)) {
+    throw new TypeError('NaN is not a valid value');
+  }
+  return !is.infinite(value) && !is.infinite(other) && value > other;
+};
+
+/**
+ * is.le
+ * Test if `value` is less than or equal to `other`.
+ *
+ * @param {Number} value value to test
+ * @param {Number} other value to compare with
+ * @return {Boolean} if 'value' is less than or equal to 'other'
+ * @api public
+ */
+
+is.le = function (value, other) {
+  if (isActualNaN(value) || isActualNaN(other)) {
+    throw new TypeError('NaN is not a valid value');
+  }
+  return !is.infinite(value) && !is.infinite(other) && value <= other;
+};
+
+/**
+ * is.lt
+ * Test if `value` is less than `other`.
+ *
+ * @param {Number} value value to test
+ * @param {Number} other value to compare with
+ * @return {Boolean} if `value` is less than `other`
+ * @api public
+ */
+
+is.lt = function (value, other) {
+  if (isActualNaN(value) || isActualNaN(other)) {
+    throw new TypeError('NaN is not a valid value');
+  }
+  return !is.infinite(value) && !is.infinite(other) && value < other;
+};
+
+/**
+ * is.within
+ * Test if `value` is within `start` and `finish`.
+ *
+ * @param {Number} value value to test
+ * @param {Number} start lower bound
+ * @param {Number} finish upper bound
+ * @return {Boolean} true if 'value' is is within 'start' and 'finish'
+ * @api public
+ */
+is.within = function (value, start, finish) {
+  if (isActualNaN(value) || isActualNaN(start) || isActualNaN(finish)) {
+    throw new TypeError('NaN is not a valid value');
+  } else if (!is.number(value) || !is.number(start) || !is.number(finish)) {
+    throw new TypeError('all arguments must be numbers');
+  }
+  var isAnyInfinite = is.infinite(value) || is.infinite(start) || is.infinite(finish);
+  return isAnyInfinite || (value >= start && value <= finish);
+};
+
+/**
+ * Test object.
+ */
+
+/**
+ * is.object
+ * Test if `value` is an object.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is an object, false otherwise
+ * @api public
+ */
+is.object = function (value) {
+  return toStr.call(value) === '[object Object]';
+};
+
+/**
+ * is.primitive
+ * Test if `value` is a primitive.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is a primitive, false otherwise
+ * @api public
+ */
+is.primitive = function isPrimitive(value) {
+  if (!value) {
+    return true;
+  }
+  if (typeof value === 'object' || is.object(value) || is.fn(value) || is.array(value)) {
+    return false;
+  }
+  return true;
+};
+
+/**
+ * is.hash
+ * Test if `value` is a hash - a plain object literal.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is a hash, false otherwise
+ * @api public
+ */
+
+is.hash = function (value) {
+  return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
+};
+
+/**
+ * Test regexp.
+ */
+
+/**
+ * is.regexp
+ * Test if `value` is a regular expression.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is a regexp, false otherwise
+ * @api public
+ */
+
+is.regexp = function (value) {
+  return toStr.call(value) === '[object RegExp]';
+};
+
+/**
+ * Test string.
+ */
+
+/**
+ * is.string
+ * Test if `value` is a string.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if 'value' is a string, false otherwise
+ * @api public
+ */
+
+is.string = function (value) {
+  return toStr.call(value) === '[object String]';
+};
+
+/**
+ * Test base64 string.
+ */
+
+/**
+ * is.base64
+ * Test if `value` is a valid base64 encoded string.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if 'value' is a base64 encoded string, false otherwise
+ * @api public
+ */
+
+is.base64 = function (value) {
+  return is.string(value) && (!value.length || base64Regex.test(value));
+};
+
+/**
+ * Test base64 string.
+ */
+
+/**
+ * is.hex
+ * Test if `value` is a valid hex encoded string.
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if 'value' is a hex encoded string, false otherwise
+ * @api public
+ */
+
+is.hex = function (value) {
+  return is.string(value) && (!value.length || hexRegex.test(value));
+};
+
+/**
+ * is.symbol
+ * Test if `value` is an ES6 Symbol
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is a Symbol, false otherise
+ * @api public
+ */
+
+is.symbol = function (value) {
+  return typeof Symbol === 'function' && toStr.call(value) === '[object Symbol]' && typeof symbolValueOf.call(value) === 'symbol';
+};
+
+/**
+ * is.bigint
+ * Test if `value` is an ES-proposed BigInt
+ *
+ * @param {*} value value to test
+ * @return {Boolean} true if `value` is a BigInt, false otherise
+ * @api public
+ */
+
+is.bigint = function (value) {
+  // eslint-disable-next-line valid-typeof
+  return typeof BigInt === 'function' && toStr.call(value) === '[object BigInt]' && typeof bigIntValueOf.call(value) === 'bigint';
+};
+
+module.exports = is;
+
+},{}],67:[function(require,module,exports){
+(function (global){(function (){
+/*! JSON v3.3.2 | https://bestiejs.github.io/json3 | Copyright 2012-2015, Kit Cambridge, Benjamin Tan | http://kit.mit-license.org */
+;(function () {
+  // Detect the `define` function exposed by asynchronous module loaders. The
+  // strict `define` check is necessary for compatibility with `r.js`.
+  var isLoader = typeof define === "function" && define.amd;
+
+  // A set of types used to distinguish objects from primitives.
+  var objectTypes = {
+    "function": true,
+    "object": true
+  };
+
+  // Detect the `exports` object exposed by CommonJS implementations.
+  var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
+
+  // Use the `global` object exposed by Node (including Browserify via
+  // `insert-module-globals`), Narwhal, and Ringo as the default context,
+  // and the `window` object in browsers. Rhino exports a `global` function
+  // instead.
+  var root = objectTypes[typeof window] && window || this,
+      freeGlobal = freeExports && objectTypes[typeof module] && module && !module.nodeType && typeof global == "object" && global;
+
+  if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) {
+    root = freeGlobal;
+  }
+
+  // Public: Initializes JSON 3 using the given `context` object, attaching the
+  // `stringify` and `parse` functions to the specified `exports` object.
+  function runInContext(context, exports) {
+    context || (context = root.Object());
+    exports || (exports = root.Object());
+
+    // Native constructor aliases.
+    var Number = context.Number || root.Number,
+        String = context.String || root.String,
+        Object = context.Object || root.Object,
+        Date = context.Date || root.Date,
+        SyntaxError = context.SyntaxError || root.SyntaxError,
+        TypeError = context.TypeError || root.TypeError,
+        Math = context.Math || root.Math,
+        nativeJSON = context.JSON || root.JSON;
+
+    // Delegate to the native `stringify` and `parse` implementations.
+    if (typeof nativeJSON == "object" && nativeJSON) {
+      exports.stringify = nativeJSON.stringify;
+      exports.parse = nativeJSON.parse;
+    }
+
+    // Convenience aliases.
+    var objectProto = Object.prototype,
+        getClass = objectProto.toString,
+        isProperty = objectProto.hasOwnProperty,
+        undefined;
+
+    // Internal: Contains `try...catch` logic used by other functions.
+    // This prevents other functions from being deoptimized.
+    function attempt(func, errorFunc) {
+      try {
+        func();
+      } catch (exception) {
+        if (errorFunc) {
+          errorFunc();
+        }
+      }
+    }
+
+    // Test the `Date#getUTC*` methods. Based on work by @Yaffle.
+    var isExtended = new Date(-3509827334573292);
+    attempt(function () {
+      // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical
+      // results for certain dates in Opera >= 10.53.
+      isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 &&
+        isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708;
+    });
+
+    // Internal: Determines whether the native `JSON.stringify` and `parse`
+    // implementations are spec-compliant. Based on work by Ken Snyder.
+    function has(name) {
+      if (has[name] != null) {
+        // Return cached feature test result.
+        return has[name];
+      }
+      var isSupported;
+      if (name == "bug-string-char-index") {
+        // IE <= 7 doesn't support accessing string characters using square
+        // bracket notation. IE 8 only supports this for primitives.
+        isSupported = "a"[0] != "a";
+      } else if (name == "json") {
+        // Indicates whether both `JSON.stringify` and `JSON.parse` are
+        // supported.
+        isSupported = has("json-stringify") && has("date-serialization") && has("json-parse");
+      } else if (name == "date-serialization") {
+        // Indicates whether `Date`s can be serialized accurately by `JSON.stringify`.
+        isSupported = has("json-stringify") && isExtended;
+        if (isSupported) {
+          var stringify = exports.stringify;
+          attempt(function () {
+            isSupported =
+              // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly
+              // serialize extended years.
+              stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' &&
+              // The milliseconds are optional in ES 5, but required in 5.1.
+              stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' &&
+              // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative
+              // four-digit years instead of six-digit years. Credits: @Yaffle.
+              stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' &&
+              // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond
+              // values less than 1000. Credits: @Yaffle.
+              stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"';
+          });
+        }
+      } else {
+        var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';
+        // Test `JSON.stringify`.
+        if (name == "json-stringify") {
+          var stringify = exports.stringify, stringifySupported = typeof stringify == "function";
+          if (stringifySupported) {
+            // A test function object with a custom `toJSON` method.
+            (value = function () {
+              return 1;
+            }).toJSON = value;
+            attempt(function () {
+              stringifySupported =
+                // Firefox 3.1b1 and b2 serialize string, number, and boolean
+                // primitives as object literals.
+                stringify(0) === "0" &&
+                // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object
+                // literals.
+                stringify(new Number()) === "0" &&
+                stringify(new String()) == '""' &&
+                // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or
+                // does not define a canonical JSON representation (this applies to
+                // objects with `toJSON` properties as well, *unless* they are nested
+                // within an object or array).
+                stringify(getClass) === undefined &&
+                // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and
+                // FF 3.1b3 pass this test.
+                stringify(undefined) === undefined &&
+                // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s,
+                // respectively, if the value is omitted entirely.
+                stringify() === undefined &&
+                // FF 3.1b1, 2 throw an error if the given value is not a number,
+                // string, array, object, Boolean, or `null` literal. This applies to
+                // objects with custom `toJSON` methods as well, unless they are nested
+                // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON`
+                // methods entirely.
+                stringify(value) === "1" &&
+                stringify([value]) == "[1]" &&
+                // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of
+                // `"[null]"`.
+                stringify([undefined]) == "[null]" &&
+                // YUI 3.0.0b1 fails to serialize `null` literals.
+                stringify(null) == "null" &&
+                // FF 3.1b1, 2 halts serialization if an array contains a function:
+                // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3
+                // elides non-JSON values from objects and arrays, unless they
+                // define custom `toJSON` methods.
+                stringify([undefined, getClass, null]) == "[null,null,null]" &&
+                // Simple serialization test. FF 3.1b1 uses Unicode escape sequences
+                // where character escape codes are expected (e.g., `\b` => `\u0008`).
+                stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized &&
+                // FF 3.1b1 and b2 ignore the `filter` and `width` arguments.
+                stringify(null, value) === "1" &&
+                stringify([1, 2], null, 1) == "[\n 1,\n 2\n]";
+            }, function () {
+              stringifySupported = false;
+            });
+          }
+          isSupported = stringifySupported;
+        }
+        // Test `JSON.parse`.
+        if (name == "json-parse") {
+          var parse = exports.parse, parseSupported;
+          if (typeof parse == "function") {
+            attempt(function () {
+              // FF 3.1b1, b2 will throw an exception if a bare literal is provided.
+              // Conforming implementations should also coerce the initial argument to
+              // a string prior to parsing.
+              if (parse("0") === 0 && !parse(false)) {
+                // Simple parsing test.
+                value = parse(serialized);
+                parseSupported = value["a"].length == 5 && value["a"][0] === 1;
+                if (parseSupported) {
+                  attempt(function () {
+                    // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings.
+                    parseSupported = !parse('"\t"');
+                  });
+                  if (parseSupported) {
+                    attempt(function () {
+                      // FF 4.0 and 4.0.1 allow leading `+` signs and leading
+                      // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow
+                      // certain octal literals.
+                      parseSupported = parse("01") !== 1;
+                    });
+                  }
+                  if (parseSupported) {
+                    attempt(function () {
+                      // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal
+                      // points. These environments, along with FF 3.1b1 and 2,
+                      // also allow trailing commas in JSON objects and arrays.
+                      parseSupported = parse("1.") !== 1;
+                    });
+                  }
+                }
+              }
+            }, function () {
+              parseSupported = false;
+            });
+          }
+          isSupported = parseSupported;
+        }
+      }
+      return has[name] = !!isSupported;
+    }
+    has["bug-string-char-index"] = has["date-serialization"] = has["json"] = has["json-stringify"] = has["json-parse"] = null;
+
+    if (!has("json")) {
+      // Common `[[Class]]` name aliases.
+      var functionClass = "[object Function]",
+          dateClass = "[object Date]",
+          numberClass = "[object Number]",
+          stringClass = "[object String]",
+          arrayClass = "[object Array]",
+          booleanClass = "[object Boolean]";
+
+      // Detect incomplete support for accessing string characters by index.
+      var charIndexBuggy = has("bug-string-char-index");
+
+      // Internal: Normalizes the `for...in` iteration algorithm across
+      // environments. Each enumerated key is yielded to a `callback` function.
+      var forOwn = function (object, callback) {
+        var size = 0, Properties, dontEnums, property;
+
+        // Tests for bugs in the current environment's `for...in` algorithm. The
+        // `valueOf` property inherits the non-enumerable flag from
+        // `Object.prototype` in older versions of IE, Netscape, and Mozilla.
+        (Properties = function () {
+          this.valueOf = 0;
+        }).prototype.valueOf = 0;
+
+        // Iterate over a new instance of the `Properties` class.
+        dontEnums = new Properties();
+        for (property in dontEnums) {
+          // Ignore all properties inherited from `Object.prototype`.
+          if (isProperty.call(dontEnums, property)) {
+            size++;
+          }
+        }
+        Properties = dontEnums = null;
+
+        // Normalize the iteration algorithm.
+        if (!size) {
+          // A list of non-enumerable properties inherited from `Object.prototype`.
+          dontEnums = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"];
+          // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable
+          // properties.
+          forOwn = function (object, callback) {
+            var isFunction = getClass.call(object) == functionClass, property, length;
+            var hasProperty = !isFunction && typeof object.constructor != "function" && objectTypes[typeof object.hasOwnProperty] && object.hasOwnProperty || isProperty;
+            for (property in object) {
+              // Gecko <= 1.0 enumerates the `prototype` property of functions under
+              // certain conditions; IE does not.
+              if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) {
+                callback(property);
+              }
+            }
+            // Manually invoke the callback for each non-enumerable property.
+            for (length = dontEnums.length; property = dontEnums[--length];) {
+              if (hasProperty.call(object, property)) {
+                callback(property);
+              }
+            }
+          };
+        } else {
+          // No bugs detected; use the standard `for...in` algorithm.
+          forOwn = function (object, callback) {
+            var isFunction = getClass.call(object) == functionClass, property, isConstructor;
+            for (property in object) {
+              if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) {
+                callback(property);
+              }
+            }
+            // Manually invoke the callback for the `constructor` property due to
+            // cross-environment inconsistencies.
+            if (isConstructor || isProperty.call(object, (property = "constructor"))) {
+              callback(property);
+            }
+          };
+        }
+        return forOwn(object, callback);
+      };
+
+      // Public: Serializes a JavaScript `value` as a JSON string. The optional
+      // `filter` argument may specify either a function that alters how object and
+      // array members are serialized, or an array of strings and numbers that
+      // indicates which properties should be serialized. The optional `width`
+      // argument may be either a string or number that specifies the indentation
+      // level of the output.
+      if (!has("json-stringify") && !has("date-serialization")) {
+        // Internal: A map of control characters and their escaped equivalents.
+        var Escapes = {
+          92: "\\\\",
+          34: '\\"',
+          8: "\\b",
+          12: "\\f",
+          10: "\\n",
+          13: "\\r",
+          9: "\\t"
+        };
+
+        // Internal: Converts `value` into a zero-padded string such that its
+        // length is at least equal to `width`. The `width` must be <= 6.
+        var leadingZeroes = "000000";
+        var toPaddedString = function (width, value) {
+          // The `|| 0` expression is necessary to work around a bug in
+          // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`.
+          return (leadingZeroes + (value || 0)).slice(-width);
+        };
+
+        // Internal: Serializes a date object.
+        var serializeDate = function (value) {
+          var getData, year, month, date, time, hours, minutes, seconds, milliseconds;
+          // Define additional utility methods if the `Date` methods are buggy.
+          if (!isExtended) {
+            var floor = Math.floor;
+            // A mapping between the months of the year and the number of days between
+            // January 1st and the first of the respective month.
+            var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
+            // Internal: Calculates the number of days between the Unix epoch and the
+            // first day of the given month.
+            var getDay = function (year, month) {
+              return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400);
+            };
+            getData = function (value) {
+              // Manually compute the year, month, date, hours, minutes,
+              // seconds, and milliseconds if the `getUTC*` methods are
+              // buggy. Adapted from @Yaffle's `date-shim` project.
+              date = floor(value / 864e5);
+              for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++);
+              for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++);
+              date = 1 + date - getDay(year, month);
+              // The `time` value specifies the time within the day (see ES
+              // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used
+              // to compute `A modulo B`, as the `%` operator does not
+              // correspond to the `modulo` operation for negative numbers.
+              time = (value % 864e5 + 864e5) % 864e5;
+              // The hours, minutes, seconds, and milliseconds are obtained by
+              // decomposing the time within the day. See section 15.9.1.10.
+              hours = floor(time / 36e5) % 24;
+              minutes = floor(time / 6e4) % 60;
+              seconds = floor(time / 1e3) % 60;
+              milliseconds = time % 1e3;
+            };
+          } else {
+            getData = function (value) {
+              year = value.getUTCFullYear();
+              month = value.getUTCMonth();
+              date = value.getUTCDate();
+              hours = value.getUTCHours();
+              minutes = value.getUTCMinutes();
+              seconds = value.getUTCSeconds();
+              milliseconds = value.getUTCMilliseconds();
+            };
+          }
+          serializeDate = function (value) {
+            if (value > -1 / 0 && value < 1 / 0) {
+              // Dates are serialized according to the `Date#toJSON` method
+              // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15
+              // for the ISO 8601 date time string format.
+              getData(value);
+              // Serialize extended years correctly.
+              value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) +
+              "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) +
+              // Months, dates, hours, minutes, and seconds should have two
+              // digits; milliseconds should have three.
+              "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) +
+              // Milliseconds are optional in ES 5.0, but required in 5.1.
+              "." + toPaddedString(3, milliseconds) + "Z";
+              year = month = date = hours = minutes = seconds = milliseconds = null;
+            } else {
+              value = null;
+            }
+            return value;
+          };
+          return serializeDate(value);
+        };
+
+        // For environments with `JSON.stringify` but buggy date serialization,
+        // we override the native `Date#toJSON` implementation with a
+        // spec-compliant one.
+        if (has("json-stringify") && !has("date-serialization")) {
+          // Internal: the `Date#toJSON` implementation used to override the native one.
+          function dateToJSON (key) {
+            return serializeDate(this);
+          }
+
+          // Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
+          var nativeStringify = exports.stringify;
+          exports.stringify = function (source, filter, width) {
+            var nativeToJSON = Date.prototype.toJSON;
+            Date.prototype.toJSON = dateToJSON;
+            var result = nativeStringify(source, filter, width);
+            Date.prototype.toJSON = nativeToJSON;
+            return result;
+          }
+        } else {
+          // Internal: Double-quotes a string `value`, replacing all ASCII control
+          // characters (characters with code unit values between 0 and 31) with
+          // their escaped equivalents. This is an implementation of the
+          // `Quote(value)` operation defined in ES 5.1 section 15.12.3.
+          var unicodePrefix = "\\u00";
+          var escapeChar = function (character) {
+            var charCode = character.charCodeAt(0), escaped = Escapes[charCode];
+            if (escaped) {
+              return escaped;
+            }
+            return unicodePrefix + toPaddedString(2, charCode.toString(16));
+          };
+          var reEscape = /[\x00-\x1f\x22\x5c]/g;
+          var quote = function (value) {
+            reEscape.lastIndex = 0;
+            return '"' +
+              (
+                reEscape.test(value)
+                  ? value.replace(reEscape, escapeChar)
+                  : value
+              ) +
+              '"';
+          };
+
+          // Internal: Recursively serializes an object. Implements the
+          // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations.
+          var serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
+            var value, type, className, results, element, index, length, prefix, result;
+            attempt(function () {
+              // Necessary for host object support.
+              value = object[property];
+            });
+            if (typeof value == "object" && value) {
+              if (value.getUTCFullYear && getClass.call(value) == dateClass && value.toJSON === Date.prototype.toJSON) {
+                value = serializeDate(value);
+              } else if (typeof value.toJSON == "function") {
+                value = value.toJSON(property);
+              }
+            }
+            if (callback) {
+              // If a replacement function was provided, call it to obtain the value
+              // for serialization.
+              value = callback.call(object, property, value);
+            }
+            // Exit early if value is `undefined` or `null`.
+            if (value == undefined) {
+              return value === undefined ? value : "null";
+            }
+            type = typeof value;
+            // Only call `getClass` if the value is an object.
+            if (type == "object") {
+              className = getClass.call(value);
+            }
+            switch (className || type) {
+              case "boolean":
+              case booleanClass:
+                // Booleans are represented literally.
+                return "" + value;
+              case "number":
+              case numberClass:
+                // JSON numbers must be finite. `Infinity` and `NaN` are serialized as
+                // `"null"`.
+                return value > -1 / 0 && value < 1 / 0 ? "" + value : "null";
+              case "string":
+              case stringClass:
+                // Strings are double-quoted and escaped.
+                return quote("" + value);
+            }
+            // Recursively serialize objects and arrays.
+            if (typeof value == "object") {
+              // Check for cyclic structures. This is a linear search; performance
+              // is inversely proportional to the number of unique nested objects.
+              for (length = stack.length; length--;) {
+                if (stack[length] === value) {
+                  // Cyclic structures cannot be serialized by `JSON.stringify`.
+                  throw TypeError();
+                }
+              }
+              // Add the object to the stack of traversed objects.
+              stack.push(value);
+              results = [];
+              // Save the current indentation level and indent one additional level.
+              prefix = indentation;
+              indentation += whitespace;
+              if (className == arrayClass) {
+                // Recursively serialize array elements.
+                for (index = 0, length = value.length; index < length; index++) {
+                  element = serialize(index, value, callback, properties, whitespace, indentation, stack);
+                  results.push(element === undefined ? "null" : element);
+                }
+                result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]";
+              } else {
+                // Recursively serialize object members. Members are selected from
+                // either a user-specified list of property names, or the object
+                // itself.
+                forOwn(properties || value, function (property) {
+                  var element = serialize(property, value, callback, properties, whitespace, indentation, stack);
+                  if (element !== undefined) {
+                    // According to ES 5.1 section 15.12.3: "If `gap` {whitespace}
+                    // is not the empty string, let `member` {quote(property) + ":"}
+                    // be the concatenation of `member` and the `space` character."
+                    // The "`space` character" refers to the literal space
+                    // character, not the `space` {width} argument provided to
+                    // `JSON.stringify`.
+                    results.push(quote(property) + ":" + (whitespace ? " " : "") + element);
+                  }
+                });
+                result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
+              }
+              // Remove the object from the traversed object stack.
+              stack.pop();
+              return result;
+            }
+          };
+
+          // Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
+          exports.stringify = function (source, filter, width) {
+            var whitespace, callback, properties, className;
+            if (objectTypes[typeof filter] && filter) {
+              className = getClass.call(filter);
+              if (className == functionClass) {
+                callback = filter;
+              } else if (className == arrayClass) {
+                // Convert the property names array into a makeshift set.
+                properties = {};
+                for (var index = 0, length = filter.length, value; index < length;) {
+                  value = filter[index++];
+                  className = getClass.call(value);
+                  if (className == "[object String]" || className == "[object Number]") {
+                    properties[value] = 1;
+                  }
+                }
+              }
+            }
+            if (width) {
+              className = getClass.call(width);
+              if (className == numberClass) {
+                // Convert the `width` to an integer and create a string containing
+                // `width` number of space characters.
+                if ((width -= width % 1) > 0) {
+                  if (width > 10) {
+                    width = 10;
+                  }
+                  for (whitespace = ""; whitespace.length < width;) {
+                    whitespace += " ";
+                  }
+                }
+              } else if (className == stringClass) {
+                whitespace = width.length <= 10 ? width : width.slice(0, 10);
+              }
+            }
+            // Opera <= 7.54u2 discards the values associated with empty string keys
+            // (`""`) only if they are used directly within an object member list
+            // (e.g., `!("" in { "": 1})`).
+            return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []);
+          };
+        }
+      }
+
+      // Public: Parses a JSON source string.
+      if (!has("json-parse")) {
+        var fromCharCode = String.fromCharCode;
+
+        // Internal: A map of escaped control characters and their unescaped
+        // equivalents.
+        var Unescapes = {
+          92: "\\",
+          34: '"',
+          47: "/",
+          98: "\b",
+          116: "\t",
+          110: "\n",
+          102: "\f",
+          114: "\r"
+        };
+
+        // Internal: Stores the parser state.
+        var Index, Source;
+
+        // Internal: Resets the parser state and throws a `SyntaxError`.
+        var abort = function () {
+          Index = Source = null;
+          throw SyntaxError();
+        };
+
+        // Internal: Returns the next token, or `"$"` if the parser has reached
+        // the end of the source string. A token may be a string, number, `null`
+        // literal, or Boolean literal.
+        var lex = function () {
+          var source = Source, length = source.length, value, begin, position, isSigned, charCode;
+          while (Index < length) {
+            charCode = source.charCodeAt(Index);
+            switch (charCode) {
+              case 9: case 10: case 13: case 32:
+                // Skip whitespace tokens, including tabs, carriage returns, line
+                // feeds, and space characters.
+                Index++;
+                break;
+              case 123: case 125: case 91: case 93: case 58: case 44:
+                // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at
+                // the current position.
+                value = charIndexBuggy ? source.charAt(Index) : source[Index];
+                Index++;
+                return value;
+              case 34:
+                // `"` delimits a JSON string; advance to the next character and
+                // begin parsing the string. String tokens are prefixed with the
+                // sentinel `@` character to distinguish them from punctuators and
+                // end-of-string tokens.
+                for (value = "@", Index++; Index < length;) {
+                  charCode = source.charCodeAt(Index);
+                  if (charCode < 32) {
+                    // Unescaped ASCII control characters (those with a code unit
+                    // less than the space character) are not permitted.
+                    abort();
+                  } else if (charCode == 92) {
+                    // A reverse solidus (`\`) marks the beginning of an escaped
+                    // control character (including `"`, `\`, and `/`) or Unicode
+                    // escape sequence.
+                    charCode = source.charCodeAt(++Index);
+                    switch (charCode) {
+                      case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114:
+                        // Revive escaped control characters.
+                        value += Unescapes[charCode];
+                        Index++;
+                        break;
+                      case 117:
+                        // `\u` marks the beginning of a Unicode escape sequence.
+                        // Advance to the first character and validate the
+                        // four-digit code point.
+                        begin = ++Index;
+                        for (position = Index + 4; Index < position; Index++) {
+                          charCode = source.charCodeAt(Index);
+                          // A valid sequence comprises four hexdigits (case-
+                          // insensitive) that form a single hexadecimal value.
+                          if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) {
+                            // Invalid Unicode escape sequence.
+                            abort();
+                          }
+                        }
+                        // Revive the escaped character.
+                        value += fromCharCode("0x" + source.slice(begin, Index));
+                        break;
+                      default:
+                        // Invalid escape sequence.
+                        abort();
+                    }
+                  } else {
+                    if (charCode == 34) {
+                      // An unescaped double-quote character marks the end of the
+                      // string.
+                      break;
+                    }
+                    charCode = source.charCodeAt(Index);
+                    begin = Index;
+                    // Optimize for the common case where a string is valid.
+                    while (charCode >= 32 && charCode != 92 && charCode != 34) {
+                      charCode = source.charCodeAt(++Index);
+                    }
+                    // Append the string as-is.
+                    value += source.slice(begin, Index);
+                  }
+                }
+                if (source.charCodeAt(Index) == 34) {
+                  // Advance to the next character and return the revived string.
+                  Index++;
+                  return value;
+                }
+                // Unterminated string.
+                abort();
+              default:
+                // Parse numbers and literals.
+                begin = Index;
+                // Advance past the negative sign, if one is specified.
+                if (charCode == 45) {
+                  isSigned = true;
+                  charCode = source.charCodeAt(++Index);
+                }
+                // Parse an integer or floating-point value.
+                if (charCode >= 48 && charCode <= 57) {
+                  // Leading zeroes are interpreted as octal literals.
+                  if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) {
+                    // Illegal octal literal.
+                    abort();
+                  }
+                  isSigned = false;
+                  // Parse the integer component.
+                  for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++);
+                  // Floats cannot contain a leading decimal point; however, this
+                  // case is already accounted for by the parser.
+                  if (source.charCodeAt(Index) == 46) {
+                    position = ++Index;
+                    // Parse the decimal component.
+                    for (; position < length; position++) {
+                      charCode = source.charCodeAt(position);
+                      if (charCode < 48 || charCode > 57) {
+                        break;
+                      }
+                    }
+                    if (position == Index) {
+                      // Illegal trailing decimal.
+                      abort();
+                    }
+                    Index = position;
+                  }
+                  // Parse exponents. The `e` denoting the exponent is
+                  // case-insensitive.
+                  charCode = source.charCodeAt(Index);
+                  if (charCode == 101 || charCode == 69) {
+                    charCode = source.charCodeAt(++Index);
+                    // Skip past the sign following the exponent, if one is
+                    // specified.
+                    if (charCode == 43 || charCode == 45) {
+                      Index++;
+                    }
+                    // Parse the exponential component.
+                    for (position = Index; position < length; position++) {
+                      charCode = source.charCodeAt(position);
+                      if (charCode < 48 || charCode > 57) {
+                        break;
+                      }
+                    }
+                    if (position == Index) {
+                      // Illegal empty exponent.
+                      abort();
+                    }
+                    Index = position;
+                  }
+                  // Coerce the parsed value to a JavaScript number.
+                  return +source.slice(begin, Index);
+                }
+                // A negative sign may only precede numbers.
+                if (isSigned) {
+                  abort();
+                }
+                // `true`, `false`, and `null` literals.
+                var temp = source.slice(Index, Index + 4);
+                if (temp == "true") {
+                  Index += 4;
+                  return true;
+                } else if (temp == "fals" && source.charCodeAt(Index + 4 ) == 101) {
+                  Index += 5;
+                  return false;
+                } else if (temp == "null") {
+                  Index += 4;
+                  return null;
+                }
+                // Unrecognized token.
+                abort();
+            }
+          }
+          // Return the sentinel `$` character if the parser has reached the end
+          // of the source string.
+          return "$";
+        };
+
+        // Internal: Parses a JSON `value` token.
+        var get = function (value) {
+          var results, hasMembers;
+          if (value == "$") {
+            // Unexpected end of input.
+            abort();
+          }
+          if (typeof value == "string") {
+            if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") {
+              // Remove the sentinel `@` character.
+              return value.slice(1);
+            }
+            // Parse object and array literals.
+            if (value == "[") {
+              // Parses a JSON array, returning a new JavaScript array.
+              results = [];
+              for (;;) {
+                value = lex();
+                // A closing square bracket marks the end of the array literal.
+                if (value == "]") {
+                  break;
+                }
+                // If the array literal contains elements, the current token
+                // should be a comma separating the previous element from the
+                // next.
+                if (hasMembers) {
+                  if (value == ",") {
+                    value = lex();
+                    if (value == "]") {
+                      // Unexpected trailing `,` in array literal.
+                      abort();
+                    }
+                  } else {
+                    // A `,` must separate each array element.
+                    abort();
+                  }
+                } else {
+                  hasMembers = true;
+                }
+                // Elisions and leading commas are not permitted.
+                if (value == ",") {
+                  abort();
+                }
+                results.push(get(value));
+              }
+              return results;
+            } else if (value == "{") {
+              // Parses a JSON object, returning a new JavaScript object.
+              results = {};
+              for (;;) {
+                value = lex();
+                // A closing curly brace marks the end of the object literal.
+                if (value == "}") {
+                  break;
+                }
+                // If the object literal contains members, the current token
+                // should be a comma separator.
+                if (hasMembers) {
+                  if (value == ",") {
+                    value = lex();
+                    if (value == "}") {
+                      // Unexpected trailing `,` in object literal.
+                      abort();
+                    }
+                  } else {
+                    // A `,` must separate each object member.
+                    abort();
+                  }
+                } else {
+                  hasMembers = true;
+                }
+                // Leading commas are not permitted, object property names must be
+                // double-quoted strings, and a `:` must separate each property
+                // name and value.
+                if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") {
+                  abort();
+                }
+                results[value.slice(1)] = get(lex());
+              }
+              return results;
+            }
+            // Unexpected token encountered.
+            abort();
+          }
+          return value;
+        };
+
+        // Internal: Updates a traversed object member.
+        var update = function (source, property, callback) {
+          var element = walk(source, property, callback);
+          if (element === undefined) {
+            delete source[property];
+          } else {
+            source[property] = element;
+          }
+        };
+
+        // Internal: Recursively traverses a parsed JSON object, invoking the
+        // `callback` function for each value. This is an implementation of the
+        // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2.
+        var walk = function (source, property, callback) {
+          var value = source[property], length;
+          if (typeof value == "object" && value) {
+            // `forOwn` can't be used to traverse an array in Opera <= 8.54
+            // because its `Object#hasOwnProperty` implementation returns `false`
+            // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`).
+            if (getClass.call(value) == arrayClass) {
+              for (length = value.length; length--;) {
+                update(getClass, forOwn, value, length, callback);
+              }
+            } else {
+              forOwn(value, function (property) {
+                update(value, property, callback);
+              });
+            }
+          }
+          return callback.call(source, property, value);
+        };
+
+        // Public: `JSON.parse`. See ES 5.1 section 15.12.2.
+        exports.parse = function (source, callback) {
+          var result, value;
+          Index = 0;
+          Source = "" + source;
+          result = get(lex());
+          // If a JSON string contains multiple tokens, it is invalid.
+          if (lex() != "$") {
+            abort();
+          }
+          // Reset the parser state.
+          Index = Source = null;
+          return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result;
+        };
+      }
+    }
+
+    exports.runInContext = runInContext;
+    return exports;
+  }
+
+  if (freeExports && !isLoader) {
+    // Export for CommonJS environments.
+    runInContext(root, freeExports);
+  } else {
+    // Export for web browsers and JavaScript engines.
+    var nativeJSON = root.JSON,
+        previousJSON = root.JSON3,
+        isRestored = false;
+
+    var JSON3 = runInContext(root, (root.JSON3 = {
+      // Public: Restores the original value of the global `JSON` object and
+      // returns a reference to the `JSON3` object.
+      "noConflict": function () {
+        if (!isRestored) {
+          isRestored = true;
+          root.JSON = nativeJSON;
+          root.JSON3 = previousJSON;
+          nativeJSON = previousJSON = null;
+        }
+        return JSON3;
+      }
+    }));
+
+    root.JSON = {
+      "parse": JSON3.parse,
+      "stringify": JSON3.stringify
+    };
+  }
+
+  // Export for asynchronous module loaders.
+  if (isLoader) {
+    define(function () {
+      return JSON3;
+    });
+  }
+}).call(this);
+
+}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],68:[function(require,module,exports){
+/**
+ * Module dependencies
+ */
+
+var debug = require('debug')('jsonp');
+
+/**
+ * Module exports.
+ */
+
+module.exports = jsonp;
+
+/**
+ * Callback index.
+ */
+
+var count = 0;
+
+/**
+ * Noop function.
+ */
+
+function noop(){}
+
+/**
+ * JSONP handler
+ *
+ * Options:
+ *  - param {String} qs parameter (`callback`)
+ *  - prefix {String} qs parameter (`__jp`)
+ *  - name {String} qs parameter (`prefix` + incr)
+ *  - timeout {Number} how long after a timeout error is emitted (`60000`)
+ *
+ * @param {String} url
+ * @param {Object|Function} optional options / callback
+ * @param {Function} optional callback
+ */
+
+function jsonp(url, opts, fn){
+  if ('function' == typeof opts) {
+    fn = opts;
+    opts = {};
+  }
+  if (!opts) opts = {};
+
+  var prefix = opts.prefix || '__jp';
+
+  // use the callback name that was passed if one was provided.
+  // otherwise generate a unique name by incrementing our counter.
+  var id = opts.name || (prefix + (count++));
+
+  var param = opts.param || 'callback';
+  var timeout = null != opts.timeout ? opts.timeout : 60000;
+  var enc = encodeURIComponent;
+  var target = document.getElementsByTagName('script')[0] || document.head;
+  var script;
+  var timer;
+
+
+  if (timeout) {
+    timer = setTimeout(function(){
+      cleanup();
+      if (fn) fn(new Error('Timeout'));
+    }, timeout);
+  }
+
+  function cleanup(){
+    if (script.parentNode) script.parentNode.removeChild(script);
+    window[id] = noop;
+    if (timer) clearTimeout(timer);
+  }
+
+  function cancel(){
+    if (window[id]) {
+      cleanup();
+    }
+  }
+
+  window[id] = function(data){
+    debug('jsonp got', data);
+    cleanup();
+    if (fn) fn(null, data);
+  };
+
+  // add qs component
+  url += (~url.indexOf('?') ? '&' : '?') + param + '=' + enc(id);
+  url = url.replace('?&', '?');
+
+  debug('jsonp req "%s"', url);
+
+  // create script
+  script = document.createElement('script');
+  script.src = url;
+  target.parentNode.insertBefore(script, target);
+
+  return cancel;
+}
+
+},{"debug":59}],69:[function(require,module,exports){
+/**
+ * Module dependencies.
+ */
+
+var is = require('is');
+var onload = require('script-onload');
+var tick = require('next-tick');
+
+/**
+ * Expose `loadScript`.
+ *
+ * @param {Object} options
+ * @param {Function} fn
+ * @api public
+ */
+
+module.exports = function loadIframe(options, fn){
+  if (!options) throw new Error('Cant load nothing...');
+
+  // Allow for the simplest case, just passing a `src` string.
+  if (is.string(options)) options = { src : options };
+
+  var https = document.location.protocol === 'https:' ||
+              document.location.protocol === 'chrome-extension:';
+
+  // If you use protocol relative URLs, third-party scripts like Google
+  // Analytics break when testing with `file:` so this fixes that.
+  if (options.src && options.src.indexOf('//') === 0) {
+    options.src = https ? 'https:' + options.src : 'http:' + options.src;
+  }
+
+  // Allow them to pass in different URLs depending on the protocol.
+  if (https && options.https) options.src = options.https;
+  else if (!https && options.http) options.src = options.http;
+
+  // Make the `<iframe>` element and insert it before the first iframe on the
+  // page, which is guaranteed to exist since this Javaiframe is running.
+  var iframe = document.createElement('iframe');
+  iframe.src = options.src;
+  iframe.width = options.width || 1;
+  iframe.height = options.height || 1;
+  iframe.style.display = 'none';
+
+  // If we have a fn, attach event handlers, even in IE. Based off of
+  // the Third-Party Javascript script loading example:
+  // https://github.com/thirdpartyjs/thirdpartyjs-code/blob/master/examples/templates/02/loading-files/index.html
+  if (is.fn(fn)) {
+    onload(iframe, fn);
+  }
+
+  tick(function(){
+    // Append after event listeners are attached for IE.
+    var firstScript = document.getElementsByTagName('script')[0];
+    firstScript.parentNode.insertBefore(iframe, firstScript);
+  });
+
+  // Return the iframe element in case they want to do anything special, like
+  // give it an ID or attributes.
+  return iframe;
+};
+
+},{"is":66,"next-tick":76,"script-onload":79}],70:[function(require,module,exports){
+(function (global){(function (){
+/**
+ * lodash (Custom Build) <https://lodash.com/>
+ * Build: `lodash modularize exports="npm" -o ./`
+ * Copyright jQuery Foundation and other contributors <https://jquery.org/>
+ * Released under MIT license <https://lodash.com/license>
+ * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ */
+
+/** Used as the size to enable large array optimizations. */
+var LARGE_ARRAY_SIZE = 200;
+
+/** Used to stand-in for `undefined` hash values. */
+var HASH_UNDEFINED = '__lodash_hash_undefined__';
+
+/** Used as references for various `Number` constants. */
+var MAX_SAFE_INTEGER = 9007199254740991;
+
+/** `Object#toString` result references. */
+var argsTag = '[object Arguments]',
+    arrayTag = '[object Array]',
+    boolTag = '[object Boolean]',
+    dateTag = '[object Date]',
+    errorTag = '[object Error]',
+    funcTag = '[object Function]',
+    genTag = '[object GeneratorFunction]',
+    mapTag = '[object Map]',
+    numberTag = '[object Number]',
+    objectTag = '[object Object]',
+    promiseTag = '[object Promise]',
+    regexpTag = '[object RegExp]',
+    setTag = '[object Set]',
+    stringTag = '[object String]',
+    symbolTag = '[object Symbol]',
+    weakMapTag = '[object WeakMap]';
+
+var arrayBufferTag = '[object ArrayBuffer]',
+    dataViewTag = '[object DataView]',
+    float32Tag = '[object Float32Array]',
+    float64Tag = '[object Float64Array]',
+    int8Tag = '[object Int8Array]',
+    int16Tag = '[object Int16Array]',
+    int32Tag = '[object Int32Array]',
+    uint8Tag = '[object Uint8Array]',
+    uint8ClampedTag = '[object Uint8ClampedArray]',
+    uint16Tag = '[object Uint16Array]',
+    uint32Tag = '[object Uint32Array]';
+
+/**
+ * Used to match `RegExp`
+ * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
+ */
+var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
+
+/** Used to match `RegExp` flags from their coerced string values. */
+var reFlags = /\w*$/;
+
+/** Used to detect host constructors (Safari). */
+var reIsHostCtor = /^\[object .+?Constructor\]$/;
+
+/** Used to detect unsigned integer values. */
+var reIsUint = /^(?:0|[1-9]\d*)$/;
+
+/** Used to identify `toStringTag` values supported by `_.clone`. */
+var cloneableTags = {};
+cloneableTags[argsTag] = cloneableTags[arrayTag] =
+cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
+cloneableTags[boolTag] = cloneableTags[dateTag] =
+cloneableTags[float32Tag] = cloneableTags[float64Tag] =
+cloneableTags[int8Tag] = cloneableTags[int16Tag] =
+cloneableTags[int32Tag] = cloneableTags[mapTag] =
+cloneableTags[numberTag] = cloneableTags[objectTag] =
+cloneableTags[regexpTag] = cloneableTags[setTag] =
+cloneableTags[stringTag] = cloneableTags[symbolTag] =
+cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
+cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
+cloneableTags[errorTag] = cloneableTags[funcTag] =
+cloneableTags[weakMapTag] = false;
+
+/** Detect free variable `global` from Node.js. */
+var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
+
+/** Detect free variable `self`. */
+var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
+
+/** Used as a reference to the global object. */
+var root = freeGlobal || freeSelf || Function('return this')();
+
+/** Detect free variable `exports`. */
+var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
+
+/** Detect free variable `module`. */
+var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
+
+/** Detect the popular CommonJS extension `module.exports`. */
+var moduleExports = freeModule && freeModule.exports === freeExports;
+
+/**
+ * Adds the key-value `pair` to `map`.
+ *
+ * @private
+ * @param {Object} map The map to modify.
+ * @param {Array} pair The key-value pair to add.
+ * @returns {Object} Returns `map`.
+ */
+function addMapEntry(map, pair) {
+  // Don't return `map.set` because it's not chainable in IE 11.
+  map.set(pair[0], pair[1]);
+  return map;
+}
+
+/**
+ * Adds `value` to `set`.
+ *
+ * @private
+ * @param {Object} set The set to modify.
+ * @param {*} value The value to add.
+ * @returns {Object} Returns `set`.
+ */
+function addSetEntry(set, value) {
+  // Don't return `set.add` because it's not chainable in IE 11.
+  set.add(value);
+  return set;
+}
+
+/**
+ * A specialized version of `_.forEach` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns `array`.
+ */
+function arrayEach(array, iteratee) {
+  var index = -1,
+      length = array ? array.length : 0;
+
+  while (++index < length) {
+    if (iteratee(array[index], index, array) === false) {
+      break;
+    }
+  }
+  return array;
+}
+
+/**
+ * Appends the elements of `values` to `array`.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to append.
+ * @returns {Array} Returns `array`.
+ */
+function arrayPush(array, values) {
+  var index = -1,
+      length = values.length,
+      offset = array.length;
+
+  while (++index < length) {
+    array[offset + index] = values[index];
+  }
+  return array;
+}
+
+/**
+ * A specialized version of `_.reduce` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @param {boolean} [initAccum] Specify using the first element of `array` as
+ *  the initial value.
+ * @returns {*} Returns the accumulated value.
+ */
+function arrayReduce(array, iteratee, accumulator, initAccum) {
+  var index = -1,
+      length = array ? array.length : 0;
+
+  if (initAccum && length) {
+    accumulator = array[++index];
+  }
+  while (++index < length) {
+    accumulator = iteratee(accumulator, array[index], index, array);
+  }
+  return accumulator;
+}
+
+/**
+ * The base implementation of `_.times` without support for iteratee shorthands
+ * or max array length checks.
+ *
+ * @private
+ * @param {number} n The number of times to invoke `iteratee`.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the array of results.
+ */
+function baseTimes(n, iteratee) {
+  var index = -1,
+      result = Array(n);
+
+  while (++index < n) {
+    result[index] = iteratee(index);
+  }
+  return result;
+}
+
+/**
+ * Gets the value at `key` of `object`.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {string} key The key of the property to get.
+ * @returns {*} Returns the property value.
+ */
+function getValue(object, key) {
+  return object == null ? undefined : object[key];
+}
+
+/**
+ * Checks if `value` is a host object in IE < 9.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
+ */
+function isHostObject(value) {
+  // Many host objects are `Object` objects that can coerce to strings
+  // despite having improperly defined `toString` methods.
+  var result = false;
+  if (value != null && typeof value.toString != 'function') {
+    try {
+      result = !!(value + '');
+    } catch (e) {}
+  }
+  return result;
+}
+
+/**
+ * Converts `map` to its key-value pairs.
+ *
+ * @private
+ * @param {Object} map The map to convert.
+ * @returns {Array} Returns the key-value pairs.
+ */
+function mapToArray(map) {
+  var index = -1,
+      result = Array(map.size);
+
+  map.forEach(function(value, key) {
+    result[++index] = [key, value];
+  });
+  return result;
+}
+
+/**
+ * Creates a unary function that invokes `func` with its argument transformed.
+ *
+ * @private
+ * @param {Function} func The function to wrap.
+ * @param {Function} transform The argument transform.
+ * @returns {Function} Returns the new function.
+ */
+function overArg(func, transform) {
+  return function(arg) {
+    return func(transform(arg));
+  };
+}
+
+/**
+ * Converts `set` to an array of its values.
+ *
+ * @private
+ * @param {Object} set The set to convert.
+ * @returns {Array} Returns the values.
+ */
+function setToArray(set) {
+  var index = -1,
+      result = Array(set.size);
+
+  set.forEach(function(value) {
+    result[++index] = value;
+  });
+  return result;
+}
+
+/** Used for built-in method references. */
+var arrayProto = Array.prototype,
+    funcProto = Function.prototype,
+    objectProto = Object.prototype;
+
+/** Used to detect overreaching core-js shims. */
+var coreJsData = root['__core-js_shared__'];
+
+/** Used to detect methods masquerading as native. */
+var maskSrcKey = (function() {
+  var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
+  return uid ? ('Symbol(src)_1.' + uid) : '';
+}());
+
+/** Used to resolve the decompiled source of functions. */
+var funcToString = funcProto.toString;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+var objectToString = objectProto.toString;
+
+/** Used to detect if a method is native. */
+var reIsNative = RegExp('^' +
+  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
+  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+);
+
+/** Built-in value references. */
+var Buffer = moduleExports ? root.Buffer : undefined,
+    Symbol = root.Symbol,
+    Uint8Array = root.Uint8Array,
+    getPrototype = overArg(Object.getPrototypeOf, Object),
+    objectCreate = Object.create,
+    propertyIsEnumerable = objectProto.propertyIsEnumerable,
+    splice = arrayProto.splice;
+
+/* Built-in method references for those with the same name as other `lodash` methods. */
+var nativeGetSymbols = Object.getOwnPropertySymbols,
+    nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,
+    nativeKeys = overArg(Object.keys, Object);
+
+/* Built-in method references that are verified to be native. */
+var DataView = getNative(root, 'DataView'),
+    Map = getNative(root, 'Map'),
+    Promise = getNative(root, 'Promise'),
+    Set = getNative(root, 'Set'),
+    WeakMap = getNative(root, 'WeakMap'),
+    nativeCreate = getNative(Object, 'create');
+
+/** Used to detect maps, sets, and weakmaps. */
+var dataViewCtorString = toSource(DataView),
+    mapCtorString = toSource(Map),
+    promiseCtorString = toSource(Promise),
+    setCtorString = toSource(Set),
+    weakMapCtorString = toSource(WeakMap);
+
+/** Used to convert symbols to primitives and strings. */
+var symbolProto = Symbol ? Symbol.prototype : undefined,
+    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;
+
+/**
+ * Creates a hash object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function Hash(entries) {
+  var index = -1,
+      length = entries ? entries.length : 0;
+
+  this.clear();
+  while (++index < length) {
+    var entry = entries[index];
+    this.set(entry[0], entry[1]);
+  }
+}
+
+/**
+ * Removes all key-value entries from the hash.
+ *
+ * @private
+ * @name clear
+ * @memberOf Hash
+ */
+function hashClear() {
+  this.__data__ = nativeCreate ? nativeCreate(null) : {};
+}
+
+/**
+ * Removes `key` and its value from the hash.
+ *
+ * @private
+ * @name delete
+ * @memberOf Hash
+ * @param {Object} hash The hash to modify.
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function hashDelete(key) {
+  return this.has(key) && delete this.__data__[key];
+}
+
+/**
+ * Gets the hash value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf Hash
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function hashGet(key) {
+  var data = this.__data__;
+  if (nativeCreate) {
+    var result = data[key];
+    return result === HASH_UNDEFINED ? undefined : result;
+  }
+  return hasOwnProperty.call(data, key) ? data[key] : undefined;
... 7502 lines suppressed ...