http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/e13e8a10/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 index e264b03..6545d98 100644 --- a/extensions/web-tracker/wab/src/main/webapp/javascript/unomi-tracker.js +++ b/extensions/web-tracker/wab/src/main/webapp/javascript/unomi-tracker.js @@ -1,1438 +1,483 @@ (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){ -// shim for using process in browser -var process = module.exports = {}; - -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. - -var cachedSetTimeout; -var cachedClearTimeout; - -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; - } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } - } - - -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } - } - - - -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; - -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } -} - -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} - -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; - -process.listeners = function (name) { return [] } - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],2:[function(require,module,exports){ -(function (setImmediate,clearImmediate){ -var nextTick = require('process/browser.js').nextTick; -var apply = Function.prototype.apply; -var slice = Array.prototype.slice; -var immediateIds = {}; -var nextImmediateId = 0; - -// DOM APIs, for completeness - -exports.setTimeout = function() { - return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); -}; -exports.setInterval = function() { - return new Timeout(apply.call(setInterval, window, arguments), clearInterval); -}; -exports.clearTimeout = -exports.clearInterval = function(timeout) { timeout.close(); }; - -function Timeout(id, clearFn) { - this._id = id; - this._clearFn = clearFn; -} -Timeout.prototype.unref = Timeout.prototype.ref = function() {}; -Timeout.prototype.close = function() { - this._clearFn.call(window, this._id); -}; - -// Does not start the time, just sets up the members needed. -exports.enroll = function(item, msecs) { - clearTimeout(item._idleTimeoutId); - item._idleTimeout = msecs; -}; - -exports.unenroll = function(item) { - clearTimeout(item._idleTimeoutId); - item._idleTimeout = -1; -}; - -exports._unrefActive = exports.active = function(item) { - clearTimeout(item._idleTimeoutId); - - var msecs = item._idleTimeout; - if (msecs >= 0) { - item._idleTimeoutId = setTimeout(function onTimeout() { - if (item._onTimeout) - item._onTimeout(); - }, msecs); - } -}; - -// That's not how node.js implements it but the exposed api is the same. -exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) { - var id = nextImmediateId++; - var args = arguments.length < 2 ? false : slice.call(arguments, 1); - - immediateIds[id] = true; - - nextTick(function onNextTick() { - if (immediateIds[id]) { - // fn.call() is faster so we optimize for the common use-case - // @see http://jsperf.com/call-apply-segu - if (args) { - fn.apply(null, args); - } else { - fn.call(null); - } - // Prevent ids from leaking - exports.clearImmediate(id); - } - }); - - return id; -}; - -exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) { - delete immediateIds[id]; -}; -}).call(this,require("timers").setImmediate,require("timers").clearImmediate) -},{"process/browser.js":1,"timers":2}],3:[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":4}],4:[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; - -},{}],5:[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":58}],6:[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":7,"@ndhoule/rest":16}],7:[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; - -},{}],8:[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":13}],9:[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":8}],10:[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; - -},{}],11:[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":8}],12:[function(require,module,exports){ 'use strict'; /* * Module dependencies. */ -var each = require('@ndhoule/each'); +var arity = require('@ndhoule/arity'); -var strIndexOf = String.prototype.indexOf; +var objToString = Object.prototype.toString; /** - * Object.is/sameValueZero polyfill. + * Determine if a value is a function. * - * @api private - * @param {*} value1 - * @param {*} value2 + * @param {*} val * @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; +// TODO: Move to lib +var isFunction = function(val) { + return typeof val === 'function'; }; /** - * Searches a given `collection` for a value, returning true if the collection - * contains the value and false otherwise. Can search strings, arrays, and - * objects. + * Determine if a value is a number. * - * @name includes - * @api public - * @param {*} searchElement The element to search for. - * @param {Object|Array|string} collection The collection to search. + * @param {*} val * @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 + */ +// 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. * - * includes('nope', 'xyzabc opq'); - * //=> false + * @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 includes = function includes(searchElement, collection) { - var found = false; +var after = function after(n, fn) { + if (!isNumber(n)) { + throw new TypeError('Expected a number but received ' + typeof n); + } - // Delegate to String.prototype.indexOf when `collection` is a string - if (typeof collection === 'string') { - return strIndexOf.call(collection, searchElement) !== -1; + if (!isFunction(fn)) { + throw new TypeError('Expected a function but received ' + typeof fn); } - // 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; + var callCount = 0; + + return arity(fn.length, function() { + callCount += 1; + + if (callCount < n) { + return; } - }, collection); - return found; + return fn.apply(this, arguments); + }); }; /* * Exports. */ -module.exports = includes; +module.exports = after; -},{"@ndhoule/each":8}],13:[function(require,module,exports){ +},{"@ndhoule/arity":2}],2:[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); -}; +var objToString = Object.prototype.toString; /** - * hasOwnProperty, wrapped as a function. + * Determine if a value is a function. * - * @name has - * @api private - * @param {*} context - * @param {string|number} prop + * @param {*} val * @return {boolean} */ - -// TODO: Move to a library -var has = function has(context, prop) { - return hop.call(context, prop); +// TODO: Move to lib +var isFunction = function(val) { + return typeof val === 'function'; }; /** - * Returns true if a value is a string, otherwise false. + * Determine if a value is a number. * - * @name isString - * @api private * @param {*} val * @return {boolean} */ - -// TODO: Move to a library -var isString = function isString(val) { - return toStr.call(val) === '[object String]'; +// TODO: Move to lib +var isNumber = function(val) { + var type = typeof val; + return type === 'number' || (type === 'object' && objToString.call(val) === '[object Number]'); }; -/** - * 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'); + /** + * 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', + ' };' + ); -/** - * indexKeys - * - * @name indexKeys - * @api private - * @param {} target - * @param {Function} pred - * @return {Array} - */ -var indexKeys = function indexKeys(target, pred) { - pred = pred || has; + /* eslint-disable no-new-func */ + return new Function('func', wrapperBody); + /* eslint-enable no-new-func */ +}; - var results = []; +// 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); + }; + }, - for (var i = 0, len = target.length; i < len; i += 1) { - if (pred(target, i)) { - results.push(String(i)); - } - } + function(fn) { + return function(arg1) { + return fn.apply(this, arguments); + }; + }, - return results; -}; + function(fn) { + return function(arg1, arg2) { + return fn.apply(this, arguments); + }; + }, -/** - * 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; + function(fn) { + return function(arg1, arg2, arg3) { + return fn.apply(this, arguments); + }; + }, - var results = []; + function(fn) { + return function(arg1, arg2, arg3, arg4) { + return fn.apply(this, arguments); + }; + }, - for (var key in target) { - if (pred(target, key)) { - results.push(String(key)); - } + function(fn) { + return function(arg1, arg2, arg3, arg4, arg5) { + return fn.apply(this, arguments); + }; } - - return results; -}; + /* eslint-enable no-unused-vars */ +]; /** - * 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). + * Takes a function and an [arity](https://en.wikipedia.org/wiki/Arity) `n`, and returns a new + * function that expects `n` arguments. * - * @name keys + * @name arity * @api public - * @category Object - * @param {Object} source The value to retrieve keys from. - * @return {Array} An array containing all the input `source`'s keys. + * @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 - * 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']; + * var add = function(a, b) { + * return a + b; + * }; * - * // Works on arrays - * keys(['a', 'b', 'c']); - * //=> ['0', '1', '2'] + * // Check the number of arguments this function expects by accessing `.length`: + * add.length; + * //=> 2 * - * // Skips unpopulated indices in sparse arrays - * var arr = [1]; - * arr[4] = 4; - * keys(arr); - * //=> ['0', '4'] + * var unaryAdd = arity(1, add); + * unaryAdd.length; + * //=> 1 */ -var keys = function keys(source) { - if (source == null) { - return []; +var arity = function arity(n, func) { + if (!isFunction(func)) { + throw new TypeError('Expected a function but got ' + typeof func); } - // IE6-8 compatibility (string) - if (isString(source)) { - return indexKeys(source, charAt); - } + n = Math.max(isNumber(n) ? n : 0, 0); - // IE6-8 compatibility (arguments) - if (isArrayLike(source)) { - return indexKeys(source, has); + if (!arityWrapperCache[n]) { + arityWrapperCache[n] = createArityWrapper(n); } - return objectKeys(source); + return arityWrapperCache[n](func); }; /* * Exports. */ -module.exports = keys; +module.exports = arity; -},{}],14:[function(require,module,exports){ +},{}],3:[function(require,module,exports){ 'use strict'; /* * Module dependencies. */ -var each = require('@ndhoule/each'); +var type = require('component-type'); /** - * 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; }; + * Deeply clone an object. * - * map(square, [1, 2, 3]); - * //=> [1, 4, 9] + * @param {*} obj Any object. */ -var map = function map(iterator, collection) { - if (typeof iterator !== 'function') { - throw new TypeError('Expected a function but received a ' + typeof iterator); + +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; } - var result = []; + 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; + } - each(function(val, i, collection) { - result.push(iterator(val, i, collection)); - }, collection); + 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); + } - return result; + if (t === 'date') { + return new Date(obj.getTime()); + } + + // string, number, boolean, etc. + return obj; }; /* * Exports. */ -module.exports = map; +module.exports = clone; -},{"@ndhoule/each":8}],15:[function(require,module,exports){ +},{"component-type":53}],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; -// TODO: Move to lib -var existy = function(val) { - return val != null; +/** + * 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'; }; -// TODO: Move to lib -var isArray = function(val) { - return objToString.call(val) === '[object Array]'; +/** + * 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]'; }; -// TODO: Move to lib -var isString = function(val) { - return typeof val === 'string' || objToString.call(val) === '[object String]'; +/** + * 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; }; -// TODO: Move to lib -var isObject = function(val) { - return val != null && typeof val === 'object'; +/** + * 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; }; /** - * 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' } + * TODO: Document * - * pick(['name', 'fears'], person); - * //=> { name: 'Tim', fears: 'rabbits' } + * @name defaultsWith + * @api private + * @param {Function} combiner + * @param {Object} target + * @param {...Object} sources + * @return {Object} Return the input `target`. */ -var pick = function pick(props, object) { - if (!existy(object) || !isObject(object)) { - return {}; +var defaultsWith = function(combiner, target /*, ...sources */) { + if (!isObject(target)) { + return target; } - if (isString(props)) { - props = [props]; - } + combiner = combiner || shallowCombiner; + var sources = drop(2, arguments); - if (!isArray(props)) { - props = []; + for (var i = 0; i < sources.length; i += 1) { + for (var key in sources[i]) { + combiner(target, sources[i], sources[i][key], key); + } } - var result = {}; + return target; +}; - for (var i = 0; i < props.length; i += 1) { - if (isString(props[i]) && props[i] in object) { - result[props[i]] = object[props[i]]; - } - } +/** + * 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))); +}; - return result; +/** + * 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 = pick; +module.exports = defaults; +module.exports.deep = defaultsDeep; -},{}],16:[function(require,module,exports){ +},{"@ndhoule/drop":5,"@ndhoule/rest":14}],5:[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)`. + * Produce a new array composed of all but the first `n` elements of an input `collection`. * - * @name rest + * @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 - * rest([1, 2, 3]); // => [2, 3] + * 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 rest = function rest(collection) { - if (collection == null || !collection.length) { +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 results = new Array(max(collection.length - 2, 0)); + var toDrop = max(Number(count) || 0, 0); + var resultsLength = max(length - toDrop, 0); + var results = new Array(resultsLength); - for (var i = 1; i < collection.length; i += 1) { - results[i - 1] = collection[i]; + for (var i = 0; i < resultsLength; i += 1) { + results[i] = collection[i + toDrop]; } return results; @@ -1442,2691 +487,2905 @@ var rest = function rest(collection) { * Exports. */ -module.exports = rest; +module.exports = drop; -},{}],17:[function(require,module,exports){ -(function (global){ +},{}],6:[function(require,module,exports){ '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. +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. */ -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); +module.exports = each; - var self = this; - this.on('initialize', function(settings, options) { - if (options.initialPageview) self.page(); - self._parseQuery(window.location.search); - }); -} +},{"@ndhoule/keys":11}],7:[function(require,module,exports){ +'use strict'; -/** - * Mix in event emitter. +/* + * Module dependencies. */ -Emitter(Analytics.prototype); +var each = require('@ndhoule/each'); /** - * Use a `plugin`. + * Check if a predicate function returns `true` for all values in a `collection`. + * Checks owned, enumerable values and exits early when `predicate` returns + * `false`. * - * @param {Function} plugin - * @return {Analytics} + * @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); + } -Analytics.prototype.use = function(plugin) { - plugin(this); - return this; + var result = true; + + each(function(val, key, collection) { + result = !!predicate(val, key, collection); + + // Exit early + if (!result) { + return false; + } + }, collection); + + return result; }; -/** - * Define a new `Integration`. - * - * @param {Function} Integration - * @return {Analytics} +/* + * Exports. */ -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; -}; +module.exports = every; + +},{"@ndhoule/each":6}],8:[function(require,module,exports){ +'use strict'; + +var has = Object.prototype.hasOwnProperty; /** - * Initialize with the given integration `settings` and `options`. + * 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. * - * Aliased to `init` for convenience. + * @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' }; * - * @param {Object} [settings={}] - * @param {Object} [options={}] - * @return {Analytics} + * extend(a, b, c); + * //=> { a: 'a', b: 'b', c: 'c' }; */ +var extend = function extend(dest /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1); -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; + 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]; } } - - 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); - } + return dest; +}; - 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); +/* + * Exports. + */ - // backwards compat with angular plugin. - // TODO: remove - this.initialized = true; +module.exports = extend; - this.emit('initialize', settings, options); - return this; -}; +},{}],9:[function(require,module,exports){ +'use strict'; -/** - * Set the user's `id`. - * - * @param {Mixed} id +/* + * Module dependencies. */ -Analytics.prototype.setAnonymousId = function(id) { - this.user().anonymousId(id); - return this; -}; +var each = require('@ndhoule/each'); /** - * Add an integration. + * 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)`. * - * @param {Integration} integration + * 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); + } -Analytics.prototype.add = function(integration) { - this._integrations[integration.name] = integration; - return this; + each(function(val, i, collection) { + accumulator = iterator(accumulator, val, i, collection); + }, collection); + + return accumulator; }; -/** - * 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} +/* + * Exports. */ -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); +module.exports = foldl; - var msg = this.normalize({ - options: options, - traits: user.traits(), - userId: user.id() - }); +},{"@ndhoule/each":6}],10:[function(require,module,exports){ +'use strict'; - // Add the initialize integrations so the server-side ones can be disabled too - if (this.options.integrations) { - defaults(msg.integrations, this.options.integrations); - } +/* + * Module dependencies. + */ - this._invoke('identify', new Identify(msg)); +var each = require('@ndhoule/each'); - // emit - this.emit('identify', id, traits, options); - this._callback(fn); - return this; -}; +var strIndexOf = String.prototype.indexOf; /** - * Return the current user. + * Object.is/sameValueZero polyfill. * - * @return {Object} + * @api private + * @param {*} value1 + * @param {*} value2 + * @return {boolean} */ - -Analytics.prototype.user = function() { - return user; +// 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; }; /** - * Identify a group by optional `id` and `traits`. Or, if no arguments are - * supplied, return the current group. + * Searches a given `collection` for a value, returning true if the collection + * contains the value and false otherwise. Can search strings, arrays, and + * objects. * - * @param {string} [id=group.id()] Group ID. - * @param {Object} [traits=null] Group traits. - * @param {Object} [options=null] - * @param {Function} [fn] - * @return {Analytics|Object} + * @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; -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); + // Delegate to String.prototype.indexOf when `collection` is a string + if (typeof collection === 'string') { + return strIndexOf.call(collection, searchElement) !== -1; } - this._invoke('group', new Group(msg)); + // 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); - this.emit('group', id, traits, options); - this._callback(fn); - return this; + return found; }; -/** - * 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} +/* + * Exports. */ -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 }; - } - } +module.exports = includes; - // Add the initialize integrations so the server-side ones can be disabled too - defaults( - msg.integrations, - this._mergeInitializeAndPlanIntegrations(planIntegrationOptions) - ); +},{"@ndhoule/each":6}],11:[function(require,module,exports){ +'use strict'; - this._invoke('track', new Track(msg)); +var hop = Object.prototype.hasOwnProperty; +var strCharAt = String.prototype.charAt; +var toStr = Object.prototype.toString; - this.emit('track', event, properties, options); - this._callback(fn); - return this; +/** + * 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); }; /** - * 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`. + * hasOwnProperty, wrapped as a function. * - * @param {Element|Array} links - * @param {string|Function} event - * @param {Object|Function} properties (optional) - * @return {Analytics} + * @name has + * @api private + * @param {*} context + * @param {string|number} prop + * @return {boolean} */ -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); +// TODO: Move to a library +var has = function has(context, prop) { + return hop.call(context, prop); +}; - if (href && el.target !== '_blank' && !isMeta(e)) { - prevent(e); - self._callback(function() { - window.location.href = href; - }); - } - }); - }, links); +/** + * Returns true if a value is a string, otherwise false. + * + * @name isString + * @api private + * @param {*} val + * @return {boolean} + */ - return this; +// TODO: Move to a library +var isString = function isString(val) { + return toStr.call(val) === '[object String]'; }; /** - * 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`. + * 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. * - * @param {Element|Array} forms - * @param {string|Function} event - * @param {Object|Function} properties (optional) - * @return {Analytics} + * @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'); +}; -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); +/** + * indexKeys + * + * @name indexKeys + * @api private + * @param {} target + * @param {Function} pred + * @return {Array} + */ +var indexKeys = function indexKeys(target, pred) { + pred = pred || has; - self._callback(function() { - el.submit(); - }); - } + var results = []; - // 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); + for (var i = 0, len = target.length; i < len; i += 1) { + if (pred(target, i)) { + results.push(String(i)); } - }, forms); + } - return this; + return results; }; /** - * Trigger a pageview, labeling the current page with an optional `category`, - * `name` and `properties`. + * Returns an array of an object's owned keys. * - * @param {string} [category] - * @param {string} [name] - * @param {Object|string} [properties] (or path) - * @param {Object} [options] - * @param {Function} [fn] - * @return {Analytics} + * @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; -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 */ + var results = []; - properties = clone(properties) || {}; - if (name) properties.name = name; - if (category) properties.category = category; + for (var key in target) { + if (pred(target, key)) { + results.push(String(key)); + } + } - // Ensure properties has baseline spec properties. - // TODO: Eventually move these entirely to `options.context.page` - var defs = pageDefaults(); - defaults(properties, defs); + return results; +}; - // 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; +/** + * 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 []; } - 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); + // IE6-8 compatibility (string) + if (isString(source)) { + return indexKeys(source, charAt); } - this._invoke('page', new Page(msg)); + // IE6-8 compatibility (arguments) + if (isArrayLike(source)) { + return indexKeys(source, has); + } - this.emit('page', category, name, properties, options); - this._callback(fn); - return this; + return objectKeys(source); }; -/** - * FIXME: BACKWARDS COMPATIBILITY: convert an old `pageview` to a `page` call. - * - * @param {string} [url] - * @return {Analytics} - * @api private +/* + * Exports. */ -Analytics.prototype.pageview = function(url) { - var properties = {}; - if (url) properties.path = url; - this.page(properties); - return this; -}; +module.exports = keys; -/** - * Merge two previously unassociated user identities. - * - * @param {string} to - * @param {string} from (optional) - * @param {Object} options (optional) - * @param {Function} fn (optional) - * @return {Analytics} - */ +},{}],12:[function(require,module,exports){ +'use strict'; -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 */ +/* + * Module dependencies. + */ - var msg = this.normalize({ - options: options, - previousId: from, - userId: to - }); +var each = require('@ndhoule/each'); - // Add the initialize integrations so the server-side ones can be disabled too - if (this.options.integrations) { - defaults(msg.integrations, this.options.integrations); +/** + * 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); } - this._invoke('alias', new Alias(msg)); + var result = []; + + each(function(val, i, collection) { + result.push(iterator(val, i, collection)); + }, collection); - this.emit('alias', to, from, options); - this._callback(fn); - return this; + return result; }; -/** - * Register a `fn` to be fired when all the analytics services are ready. - * - * @param {Function} fn - * @return {Analytics} +/* + * Exports. */ -Analytics.prototype.ready = function(fn) { - if (is.fn(fn)) { - if (this._readied) { - nextTick(fn); - } else { - this.once('ready', fn); - } - } - return this; +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; }; -/** - * Set the `timeout` (in milliseconds) used for callbacks. - * - * @param {Number} timeout - */ +// TODO: Move to lib +var isArray = function(val) { + return objToString.call(val) === '[object Array]'; +}; -Analytics.prototype.timeout = function(timeout) { - this._timeout = timeout; +// 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'; }; /** - * Enable or disable debug. + * Returns a copy of the new `object` containing only the specified properties. * - * @param {string|boolean} str + * @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 {}; + } -Analytics.prototype.debug = function(str) { - if (!arguments.length || str) { - debug.enable('analytics:' + (str || '*')); - } else { - debug.disable(); + 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; }; -/** - * Apply options. - * - * @param {Object} options - * @return {Analytics} - * @api private +/* + * Exports. */ -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; -}; +module.exports = pick; + +},{}],14:[function(require,module,exports){ +'use strict'; + +var max = Math.max; /** - * Callback a `fn` after our defined timeout period. + * 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)`. * - * @param {Function} fn - * @return {Analytics} - * @api private + * @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 []; + } -Analytics.prototype._callback = function(fn) { - if (is.fn(fn)) { - this._timeout ? setTimeout(fn, this._timeout) : nextTick(fn); + // 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 this; + + return results; }; -/** - * Call `method` with `facade` on all enabled integrations. - * - * @param {string} method - * @param {Facade} facade - * @return {Analytics} - * @api private +/* + * Exports. */ -Analytics.prototype._invoke = function(method, facade) { - var self = this; - metrics.increment('analytics_js.invoke', { - method: method - }); - this.emit('invoke', facade); +module.exports = rest; - 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); +},{}],15:[function(require,module,exports){ +(function (global){ +'use strict'; - return this; -}; +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'); /** - * Push `args`. - * - * @param {Array} args - * @api private + * Initialize a new `Analytics` instance. */ -Analytics.prototype.push = function(args) { - var method = args.shift(); - if (!this[method]) return; - this[method].apply(this, args); -}; +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); + }); +} /** - * Reset group and user traits and id's. - * - * @api public + * Mix in event emitter. */ -Analytics.prototype.reset = function() { - this.user().logout(); - this.group().logout(); -}; +Emitter(Analytics.prototype); /** - * Parse the query string for callable methods. + * Use a `plugin`. * - * @param {String} query + * @param {Function} plugin * @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); +Analytics.prototype.use = function(plugin) { + plugin(this); 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`. + * Define a new `Integration`. * - * @param {Object} msg - * @return {Object} + * @param {Function} Integration + * @return {Analytics} */ -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; +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; }; /** - * Merges the tracking plan and initialization integration options. + * Initialize with the given integration `settings` and `options`. * - * @param {Object} planIntegrations Tracking plan integrations. - * @return {Object} The merged integrations. + * Aliased to `init` for convenience. + * + * @param {Object} [settings={}
<TRUNCATED>
