You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by dg...@apache.org on 2018/09/19 09:30:27 UTC

[2/3] incubator-unomi git commit: UNOMI-187 created unomi plugin for analytics.js, cleanup files, renamed web-tracker-core to web-tracker-wab, renamed analytics.js file to unomi-tracker.js and global variable analytics to unomiTracker so we can use the t

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/b32fce9e/extensions/web-tracker/wab/src/main/webapp/javascript/unomi-tracker.js
----------------------------------------------------------------------
diff --git a/extensions/web-tracker/wab/src/main/webapp/javascript/unomi-tracker.js b/extensions/web-tracker/wab/src/main/webapp/javascript/unomi-tracker.js
new file mode 100644
index 0000000..5dc9271
--- /dev/null
+++ b/extensions/web-tracker/wab/src/main/webapp/javascript/unomi-tracker.js
@@ -0,0 +1,13069 @@
+(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)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){
+'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":56}],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){
+'use strict';
+
+var _analytics = global.analytics;
+
+/*
+ * Module dependencies.
+ */
+
+var Alias = require('segmentio-facade').Alias;
+var Emitter = require('component-emitter');
+var Group = require('segmentio-facade').Group;
+var Identify = require('segmentio-facade').Identify;
+var Page = require('segmentio-facade').Page;
+var Track = require('segmentio-facade').Track;
+var after = require('@ndhoule/after');
+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._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;
+};
+
+/**
+ * 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 integrationCount = keys(integrations).length;
+  var ready = after(integrationCount, function() {
+    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 = [];
+  each(function(integration) {
+    if (
+      options.initialPageview &&
+      integration.options.initialPageview === false
+    ) {
+      integration.page = after(2, integration.page);
+    }
+
+    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.
+  // TODO: remove
+  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;
+  metrics.increment('analytics_js.invoke', {
+    method: method
+  });
+  this.emit('invoke', facade);
+
+  var failedInitializations = self.failedInitializations || [];
+  each(function(integration, name) {
+    if (!facade.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 invokation of .%s method of %s integration. Integation failed to initialize properly.',
+        method,
+        name
+      );
+    } else {
+      try {
+        metrics.increment('analytics_js.integration.invoke', {
+          method: method,
+          integration_name: integration.name
+        });
+        integration.invoke.call(integration, method, facade);
+      } 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
+        );
+      }
+    }
+  }, this._integrations);
+
+  return this;
+};
+
+/**
+ * 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,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./cookie":16,"./group":18,"./memory":20,"./metrics":21,"./normalize":22,"./pageDefaults":23,"./store":24,"./user":25,"@ndhoule/after":1,"@ndhoule/clone":3,"@ndhoule/defaults":4,"@ndhoule/each":6,"@ndhoule/foldl":9,"@ndhoule/keys":11,"@ndhoule/pick":13,"@segment/is-meta":34,"@segment/prevent-default":38,"bind-all":43,"component-emitter":51,"component-event":52,"component-querystring":54,"component-type":56,"debug":26,"extend":61,"is":65,"next-tick":74,"segmentio-facade":84}],16:[function(require,module,exports){
+'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":41,"bind-all":43,"component-cookie":45,"debug":26,"json3":66}],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() {
+  var ret = this._options.persist
+    ? this.storage().get(this._options.cookie.key)
+    : this._id;
+  return ret === undefined ? null : ret;
+};
+
+/**
+ * Set the entity's `id`.
+ *
+ * @param {String} id
+ */
+
+Entity.prototype._setId = function(id) {
+  if (this._options.persist) {
+    this.storage().set(this._options.cookie.key, id);
+  } else {
+    this._id = 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;
+  cookie.set(this._options.cookie.key, this.id());
+  store.set(this._options.localStorage.key, this.traits());
+  return true;
+};
+
+/**
+ * Log the entity out, reseting `id` and `traits` to defaults.
+ */
+
+Entity.prototype.logout = function() {
+  this.id(null);
+  this.traits({});
+  cookie.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(cookie.get(this._options.cookie.key));
+  this.traits(store.get(this._options.localStorage.key));
+};
+
+},{"./cookie":16,"./memory":20,"./store":24,"@ndhoule/clone":3,"@ndhoule/defaults":4,"@ndhoule/extend":8,"@segment/isodate-traverse":35,"debug":26}],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":43,"debug":26,"inherits":63}],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":27,"./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":43}],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":39,"bind-all":43,"debug":26}],22:[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');
+
+/**
+ * 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);
+
+  // 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":56,"debug":26}],23:[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":32,"component-url":57}],24:[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":40,"bind-all":43}],25:[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');
+
+/**
+ * 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);
+    return this;
+  }
+
+  // new
+  anonymousId = store.get('ajs_anonymous_id');
+  if (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);
+    store.remove('_sio');
+    return anonymousId;
+  }
+
+  // empty
+  anonymousId = uuid.v4();
+  store.set('ajs_anonymous_id', anonymousId);
+  return store.get('ajs_anonymous_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,"bind-all":43,"component-cookie":45,"debug":26,"inherits":63,"uuid":98}],26:[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){}
+
+},{}],27:[function(require,module,exports){
+module.exports={
+  "name": "@segment/analytics.js-core",
+  "author": "Segment <fr...@segment.com>",
+  "version": "3.7.2",
+  "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"
+  },
+  "lint-staged": {
+    "linters": {
+      "*.{js,json,md}": [
+        "prettier-eslint --write",
+        "git add"
+      ]
+    }
+  },
+  "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/after": "^1.0.0",
+    "@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.1",
+    "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",
+    "uuid": "^2.0.2"
+  },
+  "devDependencies": {
+    "@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",
+    "compat-trigger-event": "^1.0.0",
+    "component-each": "^0.2.6",
+    "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"
+  }
+}
+
+},{}],28:[function(require,module,exports){
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+
+var bind = require('component-bind');
+var clone = require('@ndhoule/clone');
+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(clone(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":29,"./statics":30,"@ndhoule/clone":3,"@ndhoule/defaults":4,"@ndhoule/extend":8,"component-bind":44,"debug":58,"slug-component":90}],29:[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);
+
+  this.debug('%s with %o', method, args);
+  return this[method].apply(this, args);
+};
+
+/**
+ * Queue a `method` with `args`. If the integration assumes an initial
+ * pageview, then let the first call to `page` pass through.
+ *
+ * @api private
+ * @param {string} method
+ * @param {Array} args
+ */
+
+exports.queue = function(method, args) {
+  if (method === 'page' && this._assumesPageview && !this._initialized) {
+    return this.page.apply(this, 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 de

<TRUNCATED>