From 4a5272e42af9f5a00939af8792a86fd042c252af Mon Sep 17 00:00:00 2001 From: Nathan Houle Date: Mon, 8 Jun 2015 18:36:12 -0700 Subject: [PATCH] Depend on extracted analytics.js-integrations repositories Adapts build scripts to work with split repositories and adds each split repo to component.json. Bump Duo to 0.12 Update templates --- Makefile | 4 +- analytics.js | 27341 +++++++++++++++++++++--------------------- analytics.min.js | 16 +- component.json | 86 + lib/index.js | 3 +- lib/integrations.js | 91 + package.json | 2 +- 7 files changed, 14156 insertions(+), 13387 deletions(-) create mode 100644 lib/integrations.js diff --git a/Makefile b/Makefile index 3fcc7d7b6..ddc7d1ba8 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ hooks: $(HOOKS) # Build analytics.js. analytics.js: node_modules $(SRC) package.json - @$(DUO) --standalone analytics lib/index.js > $@ + @$(DUO) --stdout --standalone analytics lib/index.js > $@ # Build minified analytics.js. analytics.min.js: analytics.js @@ -82,7 +82,7 @@ analytics.min.js: analytics.js # Target for build files. # TODO: Document this one better $(BUILD): analytics.js analytics.min.js $(TESTS) - @$(DUO) --development test/tests.js > $(BUILD) + @$(DUO) --stdout --development test/tests.js > $(BUILD) # $(BUILD) shortcut. build: $(BUILD) diff --git a/analytics.js b/analytics.js index 4061fcb3c..c2ddbe570 100644 --- a/analytics.js +++ b/analytics.js @@ -1,7 +1,7 @@ (function umd(require){ if ('object' == typeof exports) { module.exports = require('1'); - } else if ('function' == typeof define && define.amd) { + } else if ('function' == typeof define && (define.amd || define.cmd)) { define(function(){ return require('1'); }); } else { this['analytics'] = require('1'); @@ -38,16 +38,19 @@ */ function call(id, require){ - var m = cache[id] = { exports: {} }; + var m = { exports: {} }; var mod = modules[id]; var name = mod[2]; var fn = mod[0]; fn.call(m.exports, function(req){ var dep = modules[id][1][req]; - return require(dep ? dep : req); + return require(dep || req); }, m, m.exports, outer, modules, cache, entries); + // store to cache after successful resolve + cache[id] = m; + // expose as `name`. if (name) cache[name] = cache[id]; @@ -98,11 +101,10 @@ * (C) 2013 Segment.io Inc. */ -var Integrations = require('analytics.js-integrations'); var Analytics = require('./analytics'); +var Integrations = require('./integrations'); var each = require('each'); - /** * Expose the `analytics` singleton. */ @@ -129,12263 +131,12370 @@ each(Integrations, function(name, Integration) { analytics.use(Integration); }); -}, {"analytics.js-integrations":2,"./analytics":3,"each":4,"../bower.json":5}], +}, {"./analytics":2,"./integrations":3,"each":4,"../bower.json":5}], 2: [function(require, module, exports) { /** * Module dependencies. */ +var _analytics = window.analytics; +var Emitter = require('emitter'); +var Facade = require('facade'); +var after = require('after'); +var bind = require('bind'); +var callback = require('callback'); +var clone = require('clone'); +var cookie = require('./cookie'); +var debug = require('debug'); +var defaults = require('defaults'); var each = require('each'); -var plugins = require('./integrations.js'); +var group = require('./group'); +var is = require('is'); +var isMeta = require('is-meta'); +var keys = require('object').keys; +var memory = require('./memory'); +var normalize = require('./normalize'); +var on = require('event').bind; +var pageDefaults = require('./pageDefaults'); +var pick = require('pick'); +var prevent = require('prevent'); +var querystring = require('querystring'); +var size = require('object').length; +var store = require('./store'); +var user = require('./user'); +var Alias = Facade.Alias; +var Group = Facade.Group; +var Identify = Facade.Identify; +var Page = Facade.Page; +var Track = Facade.Track; /** - * Expose the integrations, using their own `name` from their `prototype`. + * Expose `Analytics`. */ -each(plugins, function(plugin){ - var name = (plugin.Integration || plugin).prototype.name; - exports[name] = plugin; -}); - - - -}, {"each":4,"./integrations.js":6}], -4: [function(require, module, exports) { +exports = module.exports = Analytics; /** - * Module dependencies. + * Expose storage. */ -var type = require('type'); +exports.cookie = cookie; +exports.store = store; +exports.memory = memory; /** - * HOP reference. + * Initialize a new `Analytics` instance. */ -var has = Object.prototype.hasOwnProperty; - -/** - * Iterate the given `obj` and invoke `fn(val, i)`. - * - * @param {String|Array|Object} obj - * @param {Function} fn - * @api public - */ +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'); + bind.all(this); -module.exports = function(obj, fn){ - switch (type(obj)) { - case 'array': - return array(obj, fn); - case 'object': - if ('number' == typeof obj.length) return array(obj, fn); - return object(obj, fn); - case 'string': - return string(obj, fn); - } -}; + var self = this; + this.on('initialize', function(settings, options){ + if (options.initialPageview) self.page(); + self._parseQuery(); + }); +} /** - * Iterate string chars. - * - * @param {String} obj - * @param {Function} fn - * @api private + * Event Emitter. */ -function string(obj, fn) { - for (var i = 0; i < obj.length; ++i) { - fn(obj.charAt(i), i); - } -} +Emitter(Analytics.prototype); /** - * Iterate object keys. + * Use a `plugin`. * - * @param {Object} obj - * @param {Function} fn - * @api private + * @param {Function} plugin + * @return {Analytics} */ -function object(obj, fn) { - for (var key in obj) { - if (has.call(obj, key)) { - fn(key, obj[key]); - } - } -} +Analytics.prototype.use = function(plugin) { + plugin(this); + return this; +}; /** - * Iterate array-ish. + * Define a new `Integration`. * - * @param {Array|Object} obj - * @param {Function} fn - * @api private - */ - -function array(obj, fn) { - for (var i = 0; i < obj.length; ++i) { - fn(obj[i], i); - } -} -}, {"type":7}], -7: [function(require, module, exports) { -/** - * toString ref. + * @param {Function} Integration + * @return {Analytics} */ -var toString = Object.prototype.toString; +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; +}; /** - * Return the type of `val`. + * Initialize with the given integration `settings` and `options`. * - * @param {Mixed} val - * @return {String} - * @api public + * Aliased to `init` for convenience. + * + * @param {Object} [settings={}] + * @param {Object} [options={}] + * @return {Analytics} */ -module.exports = function(val){ - switch (toString.call(val)) { - case '[object Date]': return 'date'; - case '[object RegExp]': return 'regexp'; - case '[object Arguments]': return 'arguments'; - case '[object Array]': return 'array'; - case '[object Error]': return 'error'; - } +Analytics.prototype.init = Analytics.prototype.initialize = function(settings, options) { + settings = settings || {}; + options = options || {}; - if (val === null) return 'null'; - if (val === undefined) return 'undefined'; - if (val !== val) return 'nan'; - if (val && val.nodeType === 1) return 'element'; + this._options(options); + this._readied = false; - val = val.valueOf - ? val.valueOf() - : Object.prototype.valueOf.apply(val) + // clean unknown integrations from settings + var self = this; + each(settings, function(name) { + var Integration = self.Integrations[name]; + if (!Integration) delete settings[name]; + }); - return typeof val; -}; + // add integrations + each(settings, function(name, opts) { + var Integration = self.Integrations[name]; + var integration = new Integration(clone(opts)); + self.log('initialize %o - %o', name, opts); + self.add(integration); + }); -}, {}], -6: [function(require, module, exports) { + var integrations = this._integrations; -/** - * DON'T EDIT THIS FILE. It's automatically generated! - */ - -module.exports = [ - require('./lib/adroll'), - require('./lib/adwords'), - require('./lib/alexa'), - require('./lib/amplitude'), - require('./lib/appcues'), - require('./lib/atatus'), - require('./lib/autosend'), - require('./lib/awesm'), - require('./lib/bing-ads'), - require('./lib/blueshift'), - require('./lib/bronto'), - require('./lib/bugherd'), - require('./lib/bugsnag'), - require('./lib/chameleon'), - require('./lib/chartbeat'), - require('./lib/clicktale'), - require('./lib/clicky'), - require('./lib/comscore'), - require('./lib/crazy-egg'), - require('./lib/curebit'), - require('./lib/customerio'), - require('./lib/drip'), - require('./lib/errorception'), - require('./lib/evergage'), - require('./lib/extole'), - require('./lib/facebook-conversion-tracking'), - require('./lib/foxmetrics'), - require('./lib/frontleaf'), - require('./lib/fullstory'), - require('./lib/gauges'), - require('./lib/get-satisfaction'), - require('./lib/google-analytics'), - require('./lib/google-tag-manager'), - require('./lib/gosquared'), - require('./lib/heap'), - require('./lib/hellobar'), - require('./lib/hittail'), - require('./lib/hubspot'), - require('./lib/improvely'), - require('./lib/insidevault'), - require('./lib/inspectlet'), - require('./lib/intercom'), - require('./lib/keen-io'), - require('./lib/kenshoo'), - require('./lib/kissmetrics'), - require('./lib/klaviyo'), - require('./lib/livechat'), - require('./lib/lucky-orange'), - require('./lib/lytics'), - require('./lib/mixpanel'), - require('./lib/mojn'), - require('./lib/mouseflow'), - require('./lib/mousestats'), - require('./lib/navilytics'), - require('./lib/nudgespot'), - require('./lib/olark'), - require('./lib/optimizely'), - require('./lib/outbound'), - require('./lib/perfect-audience'), - require('./lib/pingdom'), - require('./lib/piwik'), - require('./lib/preact'), - require('./lib/qualaroo'), - require('./lib/quantcast'), - require('./lib/rollbar'), - require('./lib/saasquatch'), - require('./lib/satismeter'), - require('./lib/segmentio'), - require('./lib/sentry'), - require('./lib/snapengage'), - require('./lib/spinnakr'), - require('./lib/supporthero'), - require('./lib/tapstream'), - require('./lib/trakio'), - require('./lib/twitter-ads'), - require('./lib/userlike'), - require('./lib/uservoice'), - require('./lib/vero'), - require('./lib/visual-website-optimizer'), - require('./lib/webengage'), - require('./lib/woopra'), - require('./lib/yandex-metrica') -]; + // load user now that options are set + user.load(); + group.load(); -}, {"./lib/adroll":8,"./lib/adwords":9,"./lib/alexa":10,"./lib/amplitude":11,"./lib/appcues":12,"./lib/atatus":13,"./lib/autosend":14,"./lib/awesm":15,"./lib/bing-ads":16,"./lib/blueshift":17,"./lib/bronto":18,"./lib/bugherd":19,"./lib/bugsnag":20,"./lib/chameleon":21,"./lib/chartbeat":22,"./lib/clicktale":23,"./lib/clicky":24,"./lib/comscore":25,"./lib/crazy-egg":26,"./lib/curebit":27,"./lib/customerio":28,"./lib/drip":29,"./lib/errorception":30,"./lib/evergage":31,"./lib/extole":32,"./lib/facebook-conversion-tracking":33,"./lib/foxmetrics":34,"./lib/frontleaf":35,"./lib/fullstory":36,"./lib/gauges":37,"./lib/get-satisfaction":38,"./lib/google-analytics":39,"./lib/google-tag-manager":40,"./lib/gosquared":41,"./lib/heap":42,"./lib/hellobar":43,"./lib/hittail":44,"./lib/hubspot":45,"./lib/improvely":46,"./lib/insidevault":47,"./lib/inspectlet":48,"./lib/intercom":49,"./lib/keen-io":50,"./lib/kenshoo":51,"./lib/kissmetrics":52,"./lib/klaviyo":53,"./lib/livechat":54,"./lib/lucky-orange":55,"./lib/lytics":56,"./lib/mixpanel":57,"./lib/mojn":58,"./lib/mouseflow":59,"./lib/mousestats":60,"./lib/navilytics":61,"./lib/nudgespot":62,"./lib/olark":63,"./lib/optimizely":64,"./lib/outbound":65,"./lib/perfect-audience":66,"./lib/pingdom":67,"./lib/piwik":68,"./lib/preact":69,"./lib/qualaroo":70,"./lib/quantcast":71,"./lib/rollbar":72,"./lib/saasquatch":73,"./lib/satismeter":74,"./lib/segmentio":75,"./lib/sentry":76,"./lib/snapengage":77,"./lib/spinnakr":78,"./lib/supporthero":79,"./lib/tapstream":80,"./lib/trakio":81,"./lib/twitter-ads":82,"./lib/userlike":83,"./lib/uservoice":84,"./lib/vero":85,"./lib/visual-website-optimizer":86,"./lib/webengage":87,"./lib/woopra":88,"./lib/yandex-metrica":89}], -8: [function(require, module, exports) { + // make ready callback + var ready = after(size(integrations), function() { + self._readied = true; + self.emit('ready'); + }); -/** - * Module dependencies. - */ + // initialize integrations, passing ready + each(integrations, function(name, integration) { + if (options.initialPageview && integration.options.initialPageview === false) { + integration.page = after(2, integration.page); + } -var integration = require('analytics.js-integration'); -var snake = require('to-snake-case'); -var useHttps = require('use-https'); -var each = require('each'); -var is = require('is'); -var del = require('obj-case').del; + integration.analytics = self; + integration.once('ready', ready); + integration.initialize(); + }); -/** - * HOP - */ + // backwards compat with angular plugin. + // TODO: remove + this.initialized = true; -var has = Object.prototype.hasOwnProperty; + this.emit('initialize', settings, options); + return this; +}; /** - * Expose `AdRoll` integration. + * Set the user's `id`. + * + * @param {Mixed} id */ -var AdRoll = module.exports = integration('AdRoll') - .assumesPageview() - .global('__adroll_loaded') - .global('adroll_adv_id') - .global('adroll_pix_id') - .global('adroll_custom_data') - .option('advId', '') - .option('pixId', '') - .tag('http', ''); +function defaultToFunction(val) { + return function(obj){ + return val === obj; + }; +} /** - * Initialize Chameleon. + * Convert `re` to a function. * - * @param {Facade} page + * @param {RegExp} re + * @return {Function} + * @api private */ -Chameleon.prototype.initialize = function(page){ - var chmln=window.chmln||(window.chmln={}),names='setup alias track set'.split(' ');for (var i=0;i 20" + if (/^ *\W+/.test(str)) return new Function('_', 'return _ ' + str); + + // properties such as "name.first" or "age > 18" or "age > 18 && age < 36" + return new Function('_', 'return ' + get(str)); +} /** - * Identify a user. + * Convert `object` to a function. * - * @param {Facade} identify + * @param {Object} object + * @return {Function} + * @api private */ -Chameleon.prototype.identify = function(identify){ - var options = identify.traits(); - - options.uid = options.id || identify.userId() || identify.anonymousId(); - delete options.id; - - window.chmln.setup(options); -}; +function objectToFunction(obj) { + var match = {}; + for (var key in obj) { + match[key] = typeof obj[key] === 'string' + ? defaultToFunction(obj[key]) + : toFunction(obj[key]); + } + return function(val){ + if (typeof val !== 'object') return false; + for (var key in match) { + if (!(key in val)) return false; + if (!match[key](val[key])) return false; + } + return true; + }; +} /** - * Associate the current user with a group of users. + * Built the getter function. Supports getter style functions * - * @param {Facade} group + * @param {String} str + * @return {String} + * @api private */ -Chameleon.prototype.group = function(group){ - var options = {}; +function get(str) { + var props = expr(str); + if (!props.length) return '_.' + str; - each(group.traits(), function(key, value){ - options['group:'+key] = value; - }); + var val, i, prop; + for (i = 0; i < props.length; i++) { + prop = props[i]; + val = '_.' + prop; + val = "('function' == typeof " + val + " ? " + val + "() : " + val + ")"; - options['group:id'] = group.groupId(); + // mimic negative lookbehind to avoid problems with nested properties + str = stripNested(prop, str, val); + } - window.chmln.set(options); -}; + return str; +} /** - * Track an event. + * Mimic negative lookbehind to avoid problems with nested properties. * - * @param {Facade} track + * See: http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript + * + * @param {String} prop + * @param {String} str + * @param {String} val + * @return {String} + * @api private */ -Chameleon.prototype.track = function(track){ - window.chmln.track(track.event(), track.properties()); -}; +function stripNested (prop, str, val) { + return str.replace(new RegExp('(\\.)?' + prop, 'g'), function($0, $1) { + return $1 ? $0 : val; + }); +} +}, {"props":73,"component-props":73}], +73: [function(require, module, exports) { /** - * Change the user identifier after we know who they are. - * - * @param {Facade} alias + * Global Names */ -Chameleon.prototype.alias = function(alias){ - var fromId = alias.previousId() || alias.anonymousId(); - - window.chmln.alias({ from: fromId, to: alias.userId() }); -}; - -}, {"analytics.js-integration":90,"each":4}], -22: [function(require, module, exports) { +var globals = /\b(this|Array|Date|Object|Math|JSON)\b/g; /** - * Module dependencies. + * Return immediate identifiers parsed from `str`. + * + * @param {String} str + * @param {String|Function} map function or prefix + * @return {Array} + * @api public */ -var integration = require('analytics.js-integration'); -var defaults = require('defaults'); -var onBody = require('on-body'); +module.exports = function(str, fn){ + var p = unique(props(str)); + if (fn && 'string' == typeof fn) fn = prefixed(fn); + if (fn) return map(str, p, fn); + return p; +}; /** - * Expose `Chartbeat` integration. + * Return immediate identifiers in `str`. + * + * @param {String} str + * @return {Array} + * @api private */ -var Chartbeat = module.exports = integration('Chartbeat') - .assumesPageview() - .global('_sf_async_config') - .global('_sf_endpt') - .global('pSUPERFLY') - .option('domain', '') - .option('uid', null) - .tag(''); +var objToString = Object.prototype.toString; -/** - * Initialize. - * - * http://clicky.com/help/customization - * - * @param {Object} page - */ +// TODO: Move to lib +var existy = function(val) { + return val != null; +}; -Clicky.prototype.initialize = function(page){ - var user = this.analytics.user(); - window.clicky_site_ids = window.clicky_site_ids || [this.options.siteId]; - this.identify(new Identify({ - userId: user.id(), - traits: user.traits() - })); - this.load(this.ready); +// TODO: Move to lib +var isArray = function(val) { + return objToString.call(val) === '[object Array]'; }; -/** - * Loaded? - * - * @return {Boolean} - */ +// TODO: Move to lib +var isString = function(val) { + return typeof val === 'string' || objToString.call(val) === '[object String]'; +}; -Clicky.prototype.loaded = function(){ - return is.object(window.clicky); +// TODO: Move to lib +var isObject = function(val) { + return val != null && typeof val === 'object'; }; /** - * Page. + * Returns a copy of the new `object` containing only the specified properties. * - * http://clicky.com/help/customization#/help/custom/manual + * @name pick + * @api public + * @category Object + * @see {@link omit} + * @param {Array.|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' }; * - * @param {Page} page + * pick('name', person); + * //=> { name: 'Tim' } + * + * pick(['name', 'fears'], person); + * //=> { name: 'Tim', fears: 'rabbits' } */ -Clicky.prototype.page = function(page){ - var properties = page.properties(); - var category = page.category(); - var name = page.fullName(); - window.clicky.log(properties.path, name || properties.title); -}; +var pick = function pick(props, object) { + if (!existy(object) || !isObject(object)) { + return {}; + } -/** - * Identify. - * - * @param {Identify} id (optional) - */ + if (isString(props)) { + props = [props]; + } -Clicky.prototype.identify = function(identify){ - window.clicky_custom = window.clicky_custom || {}; - window.clicky_custom.session = window.clicky_custom.session || {}; - var traits = identify.traits(); + if (!isArray(props)) { + props = []; + } - var username = identify.username(); - var email = identify.email(); - var name = identify.name(); + var result = {}; - if (username || email || name) traits.username = username || email || name; + for (var i = 0; i < props.length; i += 1) { + if (isString(props[i]) && props[i] in object) { + result[props[i]] = object[props[i]]; + } + } - extend(window.clicky_custom.session, traits); + return result; }; /** - * Track. - * - * http://clicky.com/help/customization#/help/custom/manual - * - * @param {Track} event + * Exports. */ -Clicky.prototype.track = function(track){ - window.clicky.goal(track.event(), track.revenue()); -}; +module.exports = pick; -}, {"facade":134,"extend":132,"analytics.js-integration":90,"is":93}], -25: [function(require, module, exports) { +}, {}], +24: [function(require, module, exports) { /** - * Module dependencies. + * prevent default on the given `e`. + * + * examples: + * + * anchor.onclick = prevent; + * anchor.onclick = function(e){ + * if (something) return prevent(e); + * }; + * + * @param {Event} e */ -var integration = require('analytics.js-integration'); -var useHttps = require('use-https'); - -/** - * Expose `Comscore` integration. - */ +module.exports = function(e){ + e = e || window.event + return e.preventDefault + ? e.preventDefault() + : e.returnValue = false; +}; -var Comscore = module.exports = integration('comScore') - .assumesPageview() - .global('_comscore') - .global('COMSCORE') - .option('c1', '2') - .option('c2', '') - .tag('http', ''); +exports.loaded = function(){ + return false; }; -}, {"bind":103,"domify":121,"each":4,"extend":132,"analytics.js-integration":90,"json":166}], -166: [function(require, module, exports) { +/** + * Page. + * + * @api public + * @param {Page} page + */ -var json = window.JSON || {}; -var stringify = json.stringify; -var parse = json.parse; +/* eslint-disable no-unused-vars */ +exports.page = function(page){}; +/* eslint-enable no-unused-vars */ -module.exports = parse && stringify - ? JSON - : require('json-fallback'); +/** + * Track. + * + * @api public + * @param {Track} track + */ -}, {"json-fallback":167}], -167: [function(require, module, exports) { -/* - json2.js - 2014-02-04 +/* eslint-disable no-unused-vars */ +exports.track = function(track){}; +/* eslint-enable no-unused-vars */ - Public Domain. +/** + * Get events that match `event`. + * + * @api public + * @param {Object|Object[]} events An object or array of objects pulled from + * settings.mapping. + * @param {string} event The name of the event whose metdata we're looking for. + * @return {Array} An array of settings that match the input `event` name. + * @example + * var events = { my_event: 'a4991b88' }; + * .map(events, 'My Event'); + * // => ["a4991b88"] + * .map(events, 'whatever'); + * // => [] + * + * var events = [{ key: 'my event', value: '9b5eb1fa' }]; + * .map(events, 'my_event'); + * // => ["9b5eb1fa"] + * .map(events, 'whatever'); + * // => [] + */ + +exports.map = function(events, event){ + var normalizedEvent = normalize(event); + + return foldl(function(matchingEvents, val, key, events) { + // If true, this is a `mixed` value, which is structured like so: + // { key: 'testEvent', value: { event: 'testEvent', someValue: 'xyz' } } + // We need to extract the key, which we use to match against + // `normalizedEvent`, and return `value` as part of `matchingEvents` if that + // match succeds. + if (type(events) === 'array') { + // If there's no key attached to this event mapping (unusual), skip this + // item. + if (!val.key) return matchingEvents; + // Extract the key and value from the `mixed` object. + key = val.key; + val = val.value; + } - NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + if (normalize(key) === normalizedEvent) { + matchingEvents.push(val); + } - See http://www.JSON.org/js.html + return matchingEvents; + }, [], events); +}; +/** + * 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 + */ - This code should be minified before deployment. - See http://javascript.crockford.com/jsmin.html +exports.invoke = function(method){ + if (!this[method]) return; + var args = Array.prototype.slice.call(arguments, 1); + if (!this._ready) return this.queue(method, args); + var ret; - USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO - NOT CONTROL. + try { + this.debug('%s with %o', method, args); + ret = this[method].apply(this, args); + } catch (e) { + this.debug('error %o calling %s with %o', e, method, args); + } + return ret; +}; - This file creates a global JSON object containing two methods: stringify - and parse. +/** + * 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 + */ - JSON.stringify(value, replacer, space) - value any JavaScript value, usually an object or array. +exports.queue = function(method, args){ + if (method === 'page' && this._assumesPageview && !this._initialized) { + return this.page.apply(this, args); + } - replacer an optional parameter that determines how object - values are stringified for objects. It can be a - function or an array of strings. - - space an optional parameter that specifies the indentation - of nested structures. If it is omitted, the text will - be packed without extra whitespace. If it is a number, - it will specify the number of spaces to indent at each - level. If it is a string (such as '\t' or ' '), - it contains the characters used to indent at each level. - - This method produces a JSON text from a JavaScript value. - - When an object value is found, if the object contains a toJSON - method, its toJSON method will be called and the result will be - stringified. A toJSON method does not serialize: it returns the - value represented by the name/value pair that should be serialized, - or undefined if nothing should be serialized. The toJSON method - will be passed the key associated with the value, and this will be - bound to the value - - For example, this would serialize Dates as ISO strings. - - Date.prototype.toJSON = function (key) { - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - return this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z'; - }; + this._queue.push({ method: method, args: args }); +}; - You can provide an optional replacer method. It will be passed the - key and value of each member, with this bound to the containing - object. The value that is returned from your method will be - serialized. If your method returns undefined, then the member will - be excluded from the serialization. +/** + * Flush the internal queue. + * + * @api private + */ - If the replacer parameter is an array of strings, then it will be - used to select the members to be serialized. It filters the results - such that only members with keys listed in the replacer array are - stringified. +exports.flush = function(){ + this._ready = true; + var self = this; - Values that do not have JSON representations, such as undefined or - functions, will not be serialized. Such values in objects will be - dropped; in arrays they will be replaced with null. You can use - a replacer function to replace those with JSON values. - JSON.stringify(undefined) returns undefined. + each(this._queue, function(call){ + self[call.method].apply(self, call.args); + }); - The optional space parameter produces a stringification of the - value that is filled with line breaks and indentation to make it - easier to read. + // Empty the queue. + this._queue.length = 0; +}; - If the space parameter is a non-empty string, then that string will - be used for indentation. If the space parameter is a number, then - the indentation will be that many spaces. +/** + * Reset the integration, removing its global variables. + * + * @api private + */ - Example: +exports.reset = function(){ + for (var i = 0; i < this.globals.length; i++) { + window[this.globals[i]] = undefined; + } - text = JSON.stringify(['e', {pluribus: 'unum'}]); - // text is '["e",{"pluribus":"unum"}]' + window.setTimeout = setTimeout; + window.setInterval = setInterval; + 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 ''). + * @param {Function} [callback=noop] A callback, invoked when the tag finishes + * loading. + */ - text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); - // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' +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; } - text = JSON.stringify([new Date()], function (key, value) { - return this[key] instanceof Date ? - 'Date(' + this[key] + ')' : value; - }); - // text is '["Date(---current time---)"]' + // Default arguments + name = name || 'library'; + locals = locals || {}; + locals = this.locals(locals); + var template = this.templates[name]; + if (!template) throw new Error(fmt('template "%s" not defined.', name)); + var attrs = render(template, locals); + callback = callback || noop; + var self = this; + var el; - JSON.parse(text, reviver) - This method parses a JSON text to produce an object or array. - It can throw a SyntaxError exception. + switch (template.type) { + case 'img': + attrs.width = 1; + attrs.height = 1; + el = loadImage(attrs, callback); + break; + case 'script': + el = loadScript(attrs, function(err){ + if (!err) return callback(); + self.debug('error loading "%s" error="%s"', self.name, err); + }); + // TODO: hack until refactoring load-script + delete attrs.src; + each(attrs, function(key, val){ + el.setAttribute(key, val); + }); + break; + case 'iframe': + el = loadIframe(attrs, callback); + break; + default: + // No default case + } - The optional reviver parameter is a function that can filter and - transform the results. It receives each of the keys and values, - and its return value is used instead of the original value. - If it returns what it received, then the structure is not modified. - If it returns undefined then the member is deleted. + return el; +}; - Example: +/** + * Locals for tag templates. + * + * By default it includes a cache buster and all of the options. + * + * @param {Object} [locals] + * @return {Object} + */ - // Parse the text. Values that look like ISO date strings will - // be converted to Date objects. +exports.locals = function(locals){ + locals = locals || {}; + var cache = Math.floor(new Date().getTime() / 3600000); + if (!locals.hasOwnProperty('cache')) locals.cache = cache; + each(this.options, function(key, val){ + if (!locals.hasOwnProperty(key)) locals[key] = val; + }); + return locals; +}; - myData = JSON.parse(text, function (key, value) { - var a; - if (typeof value === 'string') { - a = -/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); - if (a) { - return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], - +a[5], +a[6])); - } - } - return value; - }); +/** + * Simple way to emit ready. + * + * @api public + */ - myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { - var d; - if (typeof value === 'string' && - value.slice(0, 5) === 'Date(' && - value.slice(-1) === ')') { - d = new Date(value.slice(5, -1)); - if (d) { - return d; - } - } - return value; - }); +exports.ready = function(){ + this.emit('ready'); +}; +/** + * Wrap the initialize method in an exists check, so we don't have to do it for + * every single integration. + * + * @api private + */ - This is a reference implementation. You are free to copy, modify, or - redistribute. -*/ +exports._wrapInitialize = function(){ + var initialize = this.initialize; + this.initialize = function(){ + this.debug('initialize'); + this._initialized = true; + var ret = initialize.apply(this, arguments); + this.emit('initialize'); + return ret; + }; -/*jslint evil: true, regexp: true */ + if (this._assumesPageview) this.initialize = after(2, this.initialize); +}; -/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, - call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, - getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, - lastIndex, length, parse, prototype, push, replace, slice, stringify, - test, toJSON, toString, valueOf -*/ +/** + * Wrap the page method to call `initialize` instead if the integration assumes + * a pageview. + * + * @api private + */ +exports._wrapPage = function(){ + var page = this.page; + this.page = function(){ + if (this._assumesPageview && !this._initialized) { + return this.initialize.apply(this, arguments); + } -// Create a JSON object only if one does not already exist. We create the -// methods in a closure to avoid creating global variables. + return page.apply(this, arguments); + }; +}; -(function () { - 'use strict'; +/** + * Wrap the track method to call other ecommerce methods if available depending + * on the `track.event()`. + * + * @api private + */ - var JSON = module.exports = {}; +exports._wrapTrack = function(){ + var t = this.track; + this.track = function(track){ + var event = track.event(); + var called; + var ret; - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; + for (var method in events) { + if (has.call(events, method)) { + var regexp = events[method]; + if (!this[method]) continue; + if (!regexp.test(event)) continue; + ret = this[method].apply(this, arguments); + called = true; + break; + } } - if (typeof Date.prototype.toJSON !== 'function') { + if (!called) ret = t.apply(this, arguments); + return ret; + }; +}; - Date.prototype.toJSON = function () { +/** + * TODO: Document me + * + * @api private + * @param {Object} attrs + * @param {Function} fn + * @return {undefined} + */ - return isFinite(this.valueOf()) - ? this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z' - : null; - }; - - String.prototype.toJSON = - Number.prototype.toJSON = - Boolean.prototype.toJSON = function () { - return this.valueOf(); - }; - } - - var cx, - escapable, - gap, - indent, - meta, - rep; +function loadImage(attrs, fn){ + fn = fn || function(){}; + var img = new Image(); + img.onerror = error(fn, 'failed to load pixel', img); + img.onload = function(){ fn(); }; + img.src = attrs.src; + img.width = 1; + img.height = 1; + return img; +} +/** + * TODO: Document me + * + * @api private + * @param {Function} fn + * @param {string} message + * @param {Element} img + * @return {Function} + */ - function quote(string) { +function error(fn, message, img){ + return function(e){ + e = e || window.event; + var err = new Error(message); + err.event = e; + err.source = img; + fn(err); + }; +} -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. +/** + * Render template + locals into an `attrs` object. + * + * @api private + * @param {Object} template + * @param {Object} locals + * @return {Object} + */ - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' - ? c - : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } +function render(template, locals){ + return foldl(function(attrs, val, key) { + attrs[key] = val.replace(/\{\{\ *(\w+)\ *\}\}/g, function(_, $1){ + return locals[$1]; + }); + return attrs; + }, {}, template.attrs); +} +}, {"emitter":6,"after":8,"each":173,"analytics-events":174,"fmt":175,"foldl":176,"load-iframe":177,"load-script":178,"to-no-case":179,"next-tick":55,"type":180}], +173: [function(require, module, exports) { - function str(key, holder) { +/** + * Module dependencies. + */ -// Produce a string from holder[key]. +try { + var type = require('type'); +} catch (err) { + var type = require('component-type'); +} - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; +var toFunction = require('to-function'); -// If the value has a toJSON method, call it to obtain a replacement value. +/** + * HOP reference. + */ - if (value && typeof value === 'object' && - typeof value.toJSON === 'function') { - value = value.toJSON(key); - } +var has = Object.prototype.hasOwnProperty; -// If we were called with a replacer function, then call the replacer to -// obtain a replacement value. +/** + * Iterate the given `obj` and invoke `fn(val, i)` + * in optional context `ctx`. + * + * @param {String|Array|Object} obj + * @param {Function} fn + * @param {Object} [ctx] + * @api public + */ - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } +module.exports = function(obj, fn, ctx){ + fn = toFunction(fn); + ctx = ctx || this; + switch (type(obj)) { + case 'array': + return array(obj, fn, ctx); + case 'object': + if ('number' == typeof obj.length) return array(obj, fn, ctx); + return object(obj, fn, ctx); + case 'string': + return string(obj, fn, ctx); + } +}; -// What happens next depends on the value's type. +/** + * Iterate string chars. + * + * @param {String} obj + * @param {Function} fn + * @param {Object} ctx + * @api private + */ - switch (typeof value) { - case 'string': - return quote(value); +function string(obj, fn, ctx) { + for (var i = 0; i < obj.length; ++i) { + fn.call(ctx, obj.charAt(i), i); + } +} - case 'number': +/** + * Iterate object keys. + * + * @param {Object} obj + * @param {Function} fn + * @param {Object} ctx + * @api private + */ -// JSON numbers must be finite. Encode non-finite numbers as null. +function object(obj, fn, ctx) { + for (var key in obj) { + if (has.call(obj, key)) { + fn.call(ctx, key, obj[key]); + } + } +} - return isFinite(value) ? String(value) : 'null'; +/** + * Iterate array-ish. + * + * @param {Array|Object} obj + * @param {Function} fn + * @param {Object} ctx + * @api private + */ - case 'boolean': - case 'null': +function array(obj, fn, ctx) { + for (var i = 0; i < obj.length; ++i) { + fn.call(ctx, obj[i], i); + } +} -// If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in -// the remote chance that this gets fixed someday. +}, {"type":180,"component-type":180,"to-function":181}], +180: [function(require, module, exports) { - return String(value); +/** + * toString ref. + */ -// If the type is 'object', we might be dealing with an object or an array or -// null. +var toString = Object.prototype.toString; - case 'object': +/** + * Return the type of `val`. + * + * @param {Mixed} val + * @return {String} + * @api public + */ -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. +module.exports = function(val){ + switch (toString.call(val)) { + case '[object Function]': return 'function'; + case '[object Date]': return 'date'; + case '[object RegExp]': return 'regexp'; + case '[object Arguments]': return 'arguments'; + case '[object Array]': return 'array'; + case '[object String]': return 'string'; + } - if (!value) { - return 'null'; - } + if (val === null) return 'null'; + if (val === undefined) return 'undefined'; + if (val && val.nodeType === 1) return 'element'; + if (val === Object(val)) return 'object'; -// Make an array to hold the partial results of stringifying this object value. + return typeof val; +}; - gap += indent; - partial = []; +}, {}], +181: [function(require, module, exports) { -// Is the value an array? +/** + * Module Dependencies + */ - if (Object.prototype.toString.apply(value) === '[object Array]') { +var expr; +try { + expr = require('props'); +} catch(e) { + expr = require('component-props'); +} -// The value is an array. Stringify every element. Use null as a placeholder -// for non-JSON values. +/** + * Expose `toFunction()`. + */ - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } +module.exports = toFunction; -// Join all of the elements together, separated with commas, and wrap them in -// brackets. +/** + * Convert `obj` to a `Function`. + * + * @param {Mixed} obj + * @return {Function} + * @api private + */ - v = partial.length === 0 - ? '[]' - : gap - ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' - : '[' + partial.join(',') + ']'; - gap = mind; - return v; - } +function toFunction(obj) { + switch ({}.toString.call(obj)) { + case '[object Object]': + return objectToFunction(obj); + case '[object Function]': + return obj; + case '[object String]': + return stringToFunction(obj); + case '[object RegExp]': + return regexpToFunction(obj); + default: + return defaultToFunction(obj); + } +} -// If the replacer is an array, use it to select the members to be stringified. +/** + * Default to strict equality. + * + * @param {Mixed} val + * @return {Function} + * @api private + */ - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { +function defaultToFunction(val) { + return function(obj){ + return val === obj; + }; +} -// Otherwise, iterate through all of the keys in the object. +/** + * Convert `re` to a function. + * + * @param {RegExp} re + * @return {Function} + * @api private + */ - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } +function regexpToFunction(re) { + return function(obj){ + return re.test(obj); + }; +} -// Join all of the member texts together, separated with commas, -// and wrap them in braces. +/** + * Convert property `str` to a function. + * + * @param {String} str + * @return {Function} + * @api private + */ - v = partial.length === 0 - ? '{}' - : gap - ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' - : '{' + partial.join(',') + '}'; - gap = mind; - return v; - } +function stringToFunction(str) { + // immediate such as "> 20" + if (/^ *\W+/.test(str)) return new Function('_', 'return _ ' + str); + + // properties such as "name.first" or "age > 18" or "age > 18 && age < 36" + return new Function('_', 'return ' + get(str)); +} + +/** + * Convert `object` to a function. + * + * @param {Object} object + * @return {Function} + * @api private + */ + +function objectToFunction(obj) { + var match = {}; + for (var key in obj) { + match[key] = typeof obj[key] === 'string' + ? defaultToFunction(obj[key]) + : toFunction(obj[key]); + } + return function(val){ + if (typeof val !== 'object') return false; + for (var key in match) { + if (!(key in val)) return false; + if (!match[key](val[key])) return false; } + return true; + }; +} -// If the JSON object does not yet have a stringify method, give it one. +/** + * Built the getter function. Supports getter style functions + * + * @param {String} str + * @return {String} + * @api private + */ - if (typeof JSON.stringify !== 'function') { - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }; - JSON.stringify = function (value, replacer, space) { +function get(str) { + var props = expr(str); + if (!props.length) return '_.' + str; -// The stringify method takes a value and an optional replacer, and an optional -// space parameter, and returns a JSON text. The replacer can be a function -// that can replace values, or an array of strings that will select the keys. -// A default replacer method can be provided. Use of the space parameter can -// produce text that is more easily readable. + var val, i, prop; + for (i = 0; i < props.length; i++) { + prop = props[i]; + val = '_.' + prop; + val = "('function' == typeof " + val + " ? " + val + "() : " + val + ")"; - var i; - gap = ''; - indent = ''; + // mimic negative lookbehind to avoid problems with nested properties + str = stripNested(prop, str, val); + } -// If the space parameter is a number, make an indent string containing that -// many spaces. + return str; +} - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } +/** + * Mimic negative lookbehind to avoid problems with nested properties. + * + * See: http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript + * + * @param {String} prop + * @param {String} str + * @param {String} val + * @return {String} + * @api private + */ -// If the space parameter is a string, it will be used as the indent string. +function stripNested (prop, str, val) { + return str.replace(new RegExp('(\\.)?' + prop, 'g'), function($0, $1) { + return $1 ? $0 : val; + }); +} - } else if (typeof space === 'string') { - indent = space; - } +}, {"props":73,"component-props":73}], +174: [function(require, module, exports) { -// If there is a replacer, it must be a function or an array. -// Otherwise, throw an error. +module.exports = { + removedProduct: /^[ _]?removed[ _]?product[ _]?$/i, + viewedProduct: /^[ _]?viewed[ _]?product[ _]?$/i, + viewedProductCategory: /^[ _]?viewed[ _]?product[ _]?category[ _]?$/i, + addedProduct: /^[ _]?added[ _]?product[ _]?$/i, + completedOrder: /^[ _]?completed[ _]?order[ _]?$/i, + startedOrder: /^[ _]?started[ _]?order[ _]?$/i, + updatedOrder: /^[ _]?updated[ _]?order[ _]?$/i, + refundedOrder: /^[ _]?refunded?[ _]?order[ _]?$/i, + viewedProductDetails: /^[ _]?viewed[ _]?product[ _]?details?[ _]?$/i, + clickedProduct: /^[ _]?clicked[ _]?product[ _]?$/i, + viewedPromotion: /^[ _]?viewed[ _]?promotion?[ _]?$/i, + clickedPromotion: /^[ _]?clicked[ _]?promotion?[ _]?$/i, + viewedCheckoutStep: /^[ _]?viewed[ _]?checkout[ _]?step[ _]?$/i, + completedCheckoutStep: /^[ _]?completed[ _]?checkout[ _]?step[ _]?$/i +}; - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } +}, {}], +175: [function(require, module, exports) { -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. +/** + * toString. + */ - return str('', {'': value}); - }; - } +var toString = window.JSON + ? JSON.stringify + : function(_){ return String(_); }; +/** + * Export `fmt` + */ -// If the JSON object does not yet have a parse method, give it one. +module.exports = fmt; - if (typeof JSON.parse !== 'function') { - cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - JSON.parse = function (text, reviver) { +/** + * Formatters + */ -// The parse method takes a text and an optional reviver function, and returns -// a JavaScript value if the text is a valid JSON text. +fmt.o = toString; +fmt.s = String; +fmt.d = parseInt; - var j; +/** + * Format the given `str`. + * + * @param {String} str + * @param {...} args + * @return {String} + * @api public + */ - function walk(holder, key) { +function fmt(str){ + var args = [].slice.call(arguments, 1); + var j = 0; -// The walk method is used to recursively walk the resulting structure so -// that modifications can be made. + return str.replace(/%([a-z])/gi, function(_, f){ + return fmt[f] + ? fmt[f](args[j++]) + : _ + f; + }); +} - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - -// Parsing happens in four stages. In the first stage, we replace certain -// Unicode characters with escape sequences. JavaScript handles many characters -// incorrectly, either silently deleting them, or treating them as line endings. - - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - -// In the second stage, we run the text against regular expressions that look -// for non-JSON patterns. We are especially concerned with '()' and 'new' -// because they can cause invocation, and '=' because it can cause mutation. -// But just to be safe, we want to reject all unexpected forms. +}, {}], +176: [function(require, module, exports) { +'use strict'; -// We split the second stage into 4 regexp operations in order to work around -// crippling inefficiencies in IE's and Safari's regexp engines. First we -// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we -// replace all simple value tokens with ']' characters. Third, we delete all -// open brackets that follow a colon or comma or that begin the text. Finally, -// we look to see that the remaining characters are only whitespace or ']' or -// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. +/** + * Module dependencies. + */ - if (/^[\],:{}\s]*$/ - .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') - .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { +// XXX: Hacky fix for Duo not supporting scoped modules +var each; try { each = require('@ndhoule/each'); } catch(e) { each = require('each'); } -// In the third stage we use the eval function to compile the text into a -// JavaScript structure. The '{' operator is subject to a syntactic ambiguity -// in JavaScript: it can begin a block or an object literal. We wrap the text -// in parens to eliminate the ambiguity. +/** + * 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'] + */ - j = eval('(' + text + ')'); +var foldl = function foldl(iterator, accumulator, collection) { + if (typeof iterator !== 'function') { + throw new TypeError('Expected a function but received a ' + typeof iterator); + } -// In the optional fourth stage, we recursively walk the new structure, passing -// each name/value pair to a reviver function for possible transformation. + each(function(val, i, collection) { + accumulator = iterator(accumulator, val, i, collection); + }, collection); - return typeof reviver === 'function' - ? walk({'': j}, '') - : j; - } + return accumulator; +}; -// If the text is not JSON parseable, then a SyntaxError is thrown. +/** + * Exports. + */ - throw new SyntaxError('JSON.parse'); - }; - } -}()); +module.exports = foldl; -}, {}], -33: [function(require, module, exports) { +}, {"each":70}], +177: [function(require, module, exports) { /** * Module dependencies. */ -var integration = require('analytics.js-integration'); -var push = require('global-queue')('_fbq'); -var each = require('each'); +var onload = require('script-onload'); +var tick = require('next-tick'); +var type = require('type'); /** - * HOP + * Expose `loadScript`. + * + * @param {Object} options + * @param {Function} fn + * @api public */ -var has = Object.prototype.hasOwnProperty; +module.exports = function loadIframe(options, fn){ + if (!options) throw new Error('Cant load nothing...'); -/** - * Expose `Facebook` - */ + // Allow for the simplest case, just passing a `src` string. + if ('string' == type(options)) options = { src : options }; -var Facebook = module.exports = integration('Facebook Conversion Tracking') - .global('_fbq') - .option('currency', 'USD') - .tag('') +function check () { + return ( + location.protocol == 'https:' || + location.protocol == 'chrome-extension:' + ); +} +}, {}], +77: [function(require, module, exports) { /** - * Initialize. + * Module dependencies. */ -FullStory.prototype.initialize = function(){ - var self = this; - window._fs_debug = this.options.debug; - window._fs_host = 'www.fullstory.com'; - window._fs_org = this.options.org; +var each = require('each'); +var integration = require('analytics.js-integration'); - (function(m,n,e,t,l,o,g,y){ - g=m[e]=function(a,b){g.q?g.q.push([a,b]):g._api(a,b);};g.q=[]; - // jscs:disable - g.identify=function(i,v){g(l,{uid:i});if(v)g(l,v)};g.setUserVars=function(v){FS(l,v)}; - // jscs:enable - g.setSessionVars=function(v){FS('session',v)};g.setPageVars=function(v){FS('page',v)}; - self.ready(); - self.load(); - })(window,document,'FS','script','user'); -}; +/** + * Expose `AdWords`. + */ + +var AdWords = module.exports = integration('AdWords') + .option('conversionId', '') + .option('remarketing', false) + .tag(''); /** - * Removed product - Enhanced Ecommerce - * - * https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#add-remove-cart + * Initialize Chameleon. * - * @param {Track} track + * @api public */ -GA.prototype.removedProductEnhanced = function(track){ - this.loadEnhancedEcommerce(track); - enhancedEcommerceProductAction(track, 'remove'); - this.pushEnhancedEcommerce(track); +Chameleon.prototype.initialize = function() { + /* eslint-disable */ + (window.chmln={}),names='setup alias track set'.split(' ');for (var i=0;i 0 ? valid.join(', ') : null; -} +var Chartbeat = module.exports = integration('Chartbeat') + .assumesPageview() + .global('_sf_async_config') + .global('_sf_endpt') + .global('pSUPERFLY') + .option('domain', '') + .option('uid', null) + .tag(''); /** - * Mimic negative lookbehind to avoid problems with nested properties. + * Initialize. * - * See: http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript + * http://clicky.com/help/customization * - * @param {String} prop - * @param {String} str - * @param {String} val - * @return {String} - * @api private + * @api public */ -function stripNested (prop, str, val) { - return str.replace(new RegExp('(\\.)?' + prop, 'g'), function($0, $1) { - return $1 ? $0 : val; - }); -} - -}, {"props":120,"component-props":120}], -175: [function(require, module, exports) { +Clicky.prototype.initialize = function() { + var user = this.analytics.user(); + window.clicky_site_ids = window.clicky_site_ids || [this.options.siteId]; + this.identify(new Identify({ + userId: user.id(), + traits: user.traits() + })); + this.load(this.ready); +}; /** - * Parse the given `url`. + * Loaded? * - * @param {String} str - * @return {Object} - * @api public + * @api private + * @return {boolean} */ -exports.parse = function(url){ - var a = document.createElement('a'); - a.href = url; - return { - href: a.href, - host: a.host, - port: a.port, - hash: a.hash, - hostname: a.hostname, - pathname: a.pathname, - protocol: a.protocol, - search: a.search, - query: a.search.slice(1) - } +Clicky.prototype.loaded = function() { + return is.object(window.clicky); }; /** - * Check if `url` is absolute. + * Page. + * + * http://clicky.com/help/customization#/help/custom/manual * - * @param {String} url - * @return {Boolean} * @api public + * @param {Page} page */ -exports.isAbsolute = function(url){ - if (0 == url.indexOf('//')) return true; - if (~url.indexOf('://')) return true; - return false; +Clicky.prototype.page = function(page) { + var properties = page.properties(); + var name = page.fullName(); + window.clicky.log(properties.path, name || properties.title); }; /** - * Check if `url` is relative. + * Identify. * - * @param {String} url - * @return {Boolean} * @api public + * @param {Identify} [id] */ -exports.isRelative = function(url){ - return ! exports.isAbsolute(url); +Clicky.prototype.identify = function(identify) { + window.clicky_custom = window.clicky_custom || {}; + window.clicky_custom.session = window.clicky_custom.session || {}; + var traits = identify.traits(); + + var username = identify.username(); + var email = identify.email(); + var name = identify.name(); + + if (username || email || name) traits.username = username || email || name; + + extend(window.clicky_custom.session, traits); }; /** - * Check if `url` is cross domain. + * Track. + * + * http://clicky.com/help/customization#/help/custom/manual * - * @param {String} url - * @return {Boolean} * @api public + * @param {Track} event */ -exports.isCrossDomain = function(url){ - url = exports.parse(url); - return url.hostname != location.hostname - || url.port != location.port - || url.protocol != location.protocol; +Clicky.prototype.track = function(track) { + window.clicky.goal(track.event(), track.revenue()); }; -}, {}], -40: [function(require, module, exports) { + +}, {"facade":7,"extend":66,"analytics.js-integration":162,"is":16}], +93: [function(require, module, exports) { /** * Module dependencies. */ -var push = require('global-queue')('dataLayer', { wrap: false }); var integration = require('analytics.js-integration'); +var useHttps = require('use-https'); /** - * Expose `GTM`. + * Expose `Comscore` integration. */ -var GTM = module.exports = integration('Google Tag Manager') +var Comscore = module.exports = integration('comScore') .assumesPageview() - .global('dataLayer') - .global('google_tag_manager') - .option('containerId', '') - .option('trackNamedPages', true) - .option('trackCategorizedPages', true) - .tag(''); +}; + +}, {"bind":53,"domify":183,"each":4,"extend":66,"analytics.js-integration":162,"json":57}], +102: [function(require, module, exports) { + +/** + * Module dependencies. + */ + +var each = require('each'); +var integration = require('analytics.js-integration'); +var push = require('global-queue')('_fbq'); + +/** + * Expose `Facebook` */ -Keen.prototype.loaded = function(){ - return !!(window.Keen && window.Keen.prototype.configure); -}; +var Facebook = module.exports = integration('Facebook Conversion Tracking') + .global('_fbq') + .option('currency', 'USD') + .tag(''); /** * Initialize. - * - * https://www.klaviyo.com/docs/getting-started - * - * @param {Object} page */ -Klaviyo.prototype.initialize = function(page){ +FullStory.prototype.initialize = function() { var self = this; - push('account', this.options.apiKey); - this.load(function(){ - tick(self.ready); - }); + window._fs_debug = this.options.debug; + window._fs_host = 'www.fullstory.com'; + window._fs_org = this.options.org; + + /* eslint-disable */ + (function(m,n,e,t,l,o,g,y){ + g=m[e]=function(a,b){g.q?g.q.push([a,b]):g._api(a,b);};g.q=[]; + g.identify=function(i,v){g(l,{uid:i});if(v)g(l,v)};g.setUserVars=function(v){FS(l,v)}; + g.setSessionVars=function(v){FS('session',v)};g.setPageVars=function(v){FS('page',v)}; + self.ready(); + self.load(); + })(window,document,'FS','script','user'); + /* eslint-enable */ }; /** @@ -12394,8 +12503,8 @@ Klaviyo.prototype.initialize = function(page){ * @return {Boolean} */ -Klaviyo.prototype.loaded = function(){ - return !! (window._learnq && window._learnq.push !== Array.prototype.push); +FullStory.prototype.loaded = function() { + return !!window.FS; }; /** @@ -12404,291 +12513,319 @@ Klaviyo.prototype.loaded = function(){ * @param {Identify} identify */ -Klaviyo.prototype.identify = function(identify){ - var traits = identify.traits(aliases); - if (!traits.$id && !traits.$email) return; - push('identify', traits); -}; +FullStory.prototype.identify = function(identify) { + var id = identify.userId() || identify.anonymousId(); + var traits = identify.traits({ name: 'displayName' }); -/** - * Group. - * - * @param {Group} group - */ + var newTraits = foldl(function(results, value, key) { + if (key !== 'id') results[key === 'displayName' || key === 'email' ? key : convert(key, value)] = value; + return results; + }, {}, traits); -Klaviyo.prototype.group = function(group){ - var props = group.properties(); - if (!props.name) return; - push('identify', { $organization: props.name }); + window.FS.identify(String(id), newTraits); }; /** - * Track. - * - * @param {Track} track - */ - -Klaviyo.prototype.track = function(track){ - push('track', track.event(), track.properties({ - revenue: '$value' - })); -}; +* Convert to FullStory format. +* +* @param {string} trait +* @param {*} value +*/ -}, {"analytics.js-integration":90,"global-queue":161,"next-tick":105,"alias":164}], -54: [function(require, module, exports) { +function convert(key, value) { + key = camel(key); + if (is.string(value)) return key + '_str'; + if (isInt(value)) return key + '_int'; + if (isFloat(value)) return key + '_real'; + if (is.date(value)) return key + '_date'; + if (is.boolean(value)) return key + '_bool'; +} /** - * Module dependencies. + * Check if n is a float. */ -var integration = require('analytics.js-integration'); -var clone = require('clone'); -var each = require('each'); -var Identify = require('facade').Identify; -var when = require('when'); -var tick = require('next-tick'); +function isFloat(n) { + return n === +n && n !== (n | 0); +} /** - * Expose `LiveChat` integration. + * Check if n is an integer. */ -var LiveChat = module.exports = integration('LiveChat') - .assumesPageview() - .global('__lc') - .global('__lc_inited') - .global('LC_API') - .global('LC_Invite') - .option('group', 0) - .option('license', '') - .option('listen', false) - .tag('') + .tag('pixel', ''); /** - * Initialize a new `User` with `options`. + * Initialize Wootric. * - * @param {Object} options + * @api public */ -function User(options) { - this.defaults = User.defaults; - this.debug = debug; - Entity.call(this, options); -} +Wootric.prototype.initialize = function() { + // We use this to keep track of the last page that Wootric has tracked to + // ensure we don't accidentally send a duplicate page call + this.lastPageTracked = null; + window.wootricSettings = window.wootricSettings || {}; + window.wootricSettings.account_token = this.options.accountToken; + var self = this; + this.load('library', function() { + self.ready(); + }); +}; /** - * Inherit `Entity` + * Has the Wootric library been loaded yet? + * + * @api private + * @return {boolean} */ -inherit(User, Entity); +Wootric.prototype.loaded = function() { + // We are always ready since we are just setting a global variable in initialize + return !!window.wootric; +}; /** - * 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()); + * Identify a user. * - * // 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()); + * @api public + * @param {Facade} identify */ -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; +Wootric.prototype.identify = function(identify) { + var traits = identify.traits(); + var email = identify.email(); + var createdAt = identify.created(); + var language = traits.language; + + if (createdAt && createdAt.getTime) window.wootricSettings.created_at = createdAt.getTime(); + if (language) window.wootricSettings.language = language; + window.wootricSettings.email = email; + // Set the rest of the traits as properties + window.wootricSettings.properties = omit(['created', 'createdAt', 'email'], traits); + + window.wootric('run'); }; /** - * Set / get / remove anonymousId. + * Page. * - * @param {String} anonymousId - * @return {String|User} + * @api public + * @param {Page} page */ -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; +Wootric.prototype.page = function(page) { + // Only track page if we haven't already tracked it + if (this.lastPageTracked === window.location) { + return; } - // 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; - } + // Set this page as the last page tracked + this.lastPageTracked = window.location; - // empty - anonymousId = uuid(); - store.set('ajs_anonymous_id', anonymousId); - return store.get('ajs_anonymous_id'); + var wootricSettings = window.wootricSettings; + this.load('pixel', { + accountToken: this.options.accountToken, + email: encodeURIComponent(wootricSettings.email), + createdAt: wootricSettings.created_at, + url: encodeURIComponent(page.url()), + cacheBuster: Math.random() + }); }; +}, {"analytics.js-integration":162,"omit":201}], +161: [function(require, module, exports) { + /** - * Remove anonymous id on logout too. + * Module dependencies. */ -User.prototype.logout = function(){ - Entity.prototype.logout.call(this); - this.anonymousId(null); -}; +var bind = require('bind'); +var integration = require('analytics.js-integration'); +var tick = require('next-tick'); +var when = require('when'); /** - * Load saved user `id` or `traits` from storage. + * Expose `Yandex` integration. */ -User.prototype.load = function() { - if (this._loadOldCookie()) return; - Entity.prototype.load.call(this); -}; - +var Yandex = module.exports = integration('Yandex Metrica') + .assumesPageview() + .global('yandex_metrika_callbacks') + .global('Ya') + .option('counterId', null) + .option('clickmap', false) + .option('webvisor', false) + .tag('');Chameleon.prototype.initialize=function(page){var chmln=window.chmln||(window.chmln={}),names="setup alias track set".split(" ");for(var i=0;i');Chartbeat.prototype.initialize=function(page){var self=this;window._sf_async_config=window._sf_async_config||{};window._sf_async_config.useCanonical=true;defaults(window._sf_async_config,this.options);onBody(function(){window._sf_endpt=(new Date).getTime();self.load(self.ready)})};Chartbeat.prototype.loaded=function(){return!!window.pSUPERFLY};Chartbeat.prototype.page=function(page){var category=page.category();if(category)window._sf_async_config.sections=category;var author=page.proxy("properties.author");if(author)window._sf_async_config.authors=author;var props=page.properties();var name=page.fullName();window.pSUPERFLY.virtualPage(props.path,name||props.title)}},{"analytics.js-integration":90,defaults:159,"on-body":131}],159:[function(require,module,exports){module.exports=defaults;function defaults(dest,defaults){for(var prop in defaults){if(!(prop in dest)){dest[prop]=defaults[prop]}}return dest}},{}],23:[function(require,module,exports){var date=require("load-date");var domify=require("domify");var each=require("each");var integration=require("analytics.js-integration");var is=require("is");var useHttps=require("use-https");var onBody=require("on-body");var ClickTale=module.exports=integration("ClickTale").assumesPageview().global("WRInitTime").global("ClickTale").global("ClickTaleSetUID").global("ClickTaleField").global("ClickTaleEvent").option("httpCdnUrl","http://s.clicktale.net/WRe0.js").option("httpsCdnUrl","").option("projectId","").option("recordingRatio",.01).option("partitionId","").tag('');Clicky.prototype.initialize=function(page){var user=this.analytics.user();window.clicky_site_ids=window.clicky_site_ids||[this.options.siteId];this.identify(new Identify({userId:user.id(),traits:user.traits()}));this.load(this.ready)};Clicky.prototype.loaded=function(){return is.object(window.clicky)};Clicky.prototype.page=function(page){var properties=page.properties();var category=page.category();var name=page.fullName();window.clicky.log(properties.path,name||properties.title)};Clicky.prototype.identify=function(identify){window.clicky_custom=window.clicky_custom||{};window.clicky_custom.session=window.clicky_custom.session||{};var traits=identify.traits();var username=identify.username();var email=identify.email();var name=identify.name();if(username||email||name)traits.username=username||email||name;extend(window.clicky_custom.session,traits)};Clicky.prototype.track=function(track){window.clicky.goal(track.event(),track.revenue())}},{facade:134,extend:132,"analytics.js-integration":90,is:93}],25:[function(require,module,exports){var integration=require("analytics.js-integration");var useHttps=require("use-https");var Comscore=module.exports=integration("comScore").assumesPageview().global("_comscore").global("COMSCORE").option("c1","2").option("c2","").tag("http",'")}},{bind:103,domify:121,each:4,extend:132,"analytics.js-integration":90,json:166}],166:[function(require,module,exports){var json=window.JSON||{};var stringify=json.stringify;var parse=json.parse;module.exports=parse&&stringify?JSON:require("json-fallback")},{"json-fallback":167}],167:[function(require,module,exports){(function(){"use strict";var JSON=module.exports={};function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){ -Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()}}var cx,escapable,gap,indent,meta,rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i').mapping("events");Facebook.prototype.initialize=function(page){window._fbq=window._fbq||[];this.load(this.ready);window._fbq.loaded=true};Facebook.prototype.loaded=function(){return!!(window._fbq&&window._fbq.loaded)};Facebook.prototype.page=function(page){var name=page.fullName();this.track(page.track(name))};Facebook.prototype.track=function(track){var event=track.event();var events=this.events(event);var revenue=track.revenue()||0;var self=this;each(events,function(event){push("track",event,{value:String(revenue.toFixed(2)),currency:self.options.currency})})}},{"analytics.js-integration":90,"global-queue":161,each:4}],34:[function(require,module,exports){var push=require("global-queue")("_fxm");var integration=require("analytics.js-integration");var Track=require("facade").Track;var each=require("each");var FoxMetrics=module.exports=integration("FoxMetrics").assumesPageview().global("_fxm").option("appId","").tag('');FullStory.prototype.initialize=function(){var self=this;window._fs_debug=this.options.debug;window._fs_host="www.fullstory.com";window._fs_org=this.options.org;(function(m,n,e,t,l,o,g,y){g=m[e]=function(a,b){g.q?g.q.push([a,b]):g._api(a,b)};g.q=[];g.identify=function(i,v){g(l,{uid:i});if(v)g(l,v)};g.setUserVars=function(v){FS(l,v)};g.setSessionVars=function(v){FS("session",v)};g.setPageVars=function(v){FS("page",v)};self.ready();self.load()})(window,document,"FS","script","user")};FullStory.prototype.loaded=function(){return!!window.FS};FullStory.prototype.identify=function(identify){var id=identify.userId()||identify.anonymousId();var traits=identify.traits({name:"displayName"});var newTraits=foldl(function(results,value,key){if(key!=="id")results[key==="displayName"||key==="email"?key:convert(key,value)]=value;return results},{},traits);window.FS.identify(String(id),newTraits)};function convert(trait,value){trait=camel(trait);if(is.string(value))return trait+="_str";if(isInt(value))return trait+="_int";if(isFloat(value))return trait+="_real";if(is.date(value))return trait+="_date";if(is.boolean(value))return trait+="_bool"}function isFloat(n){return n===+n&&n!==(n|0)}function isInt(n){return n===+n&&n===(n|0)}},{foldl:168,is:93,"to-camel-case":169,"analytics.js-integration":90}],168:[function(require,module,exports){"use strict";var each=require("each");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};module.exports=foldl},{each:170}],170:[function(require,module,exports){"use strict";var keys;try{keys=require("@ndhoule/keys")}catch(e){keys=require("keys")}var objToString=Object.prototype.toString;var isNumber=function isNumber(val){var type=typeof val;return type==="number"||type==="object"&&objToString.call(val)==="[object Number]"};var isArray=typeof Array.isArray==="function"?Array.isArray:function isArray(val){return objToString.call(val)==="[object Array]"};var isArrayLike=function isArrayLike(val){return val!=null&&(isArray(val)||val!=="function"&&isNumber(val.length))};var arrayEach=function arrayEach(iterator,array){for(var i=0;i');Gauges.prototype.initialize=function(page){window._gauges=window._gauges||[];this.load(this.ready)};Gauges.prototype.loaded=function(){return!!(window._gauges&&window._gauges.push!==Array.prototype.push)};Gauges.prototype.page=function(page){push("track")}},{"analytics.js-integration":90,"global-queue":161}],38:[function(require,module,exports){var integration=require("analytics.js-integration");var onBody=require("on-body");var GetSatisfaction=module.exports=integration("Get Satisfaction").assumesPageview().global("GSFN").option("widgetId","").tag('');Chameleon.prototype.initialize=function(){window.chmln={},names="setup alias track set".split(" ");for(var i=0;i');Chartbeat.prototype.initialize=function(){var self=this;window._sf_async_config=window._sf_async_config||{};window._sf_async_config.useCanonical=true;defaults(window._sf_async_config,this.options);onBody(function(){window._sf_endpt=(new Date).getTime();self.load(self.ready)})};Chartbeat.prototype.loaded=function(){return!!window.pSUPERFLY};Chartbeat.prototype.page=function(page){var category=page.category();if(category)window._sf_async_config.sections=category;var author=page.proxy("properties.author");if(author)window._sf_async_config.authors=author;var props=page.properties();var name=page.fullName();window.pSUPERFLY.virtualPage(props.path,name||props.title)}},{defaults:189,"analytics.js-integration":162,"on-body":190}],189:[function(require,module,exports){module.exports=defaults;function defaults(dest,defaults){for(var prop in defaults){if(!(prop in dest)){dest[prop]=defaults[prop]}}return dest}},{}],190:[function(require,module,exports){var each=require("each");var body=false;var callbacks=[];module.exports=function onBody(callback){if(body){call(callback)}else{callbacks.push(callback)}};var interval=setInterval(function(){if(!document.body)return;body=true;each(callbacks,call);clearInterval(interval)},5);function call(callback){callback(document.body)}},{each:173}],91:[function(require,module,exports){var date=require("load-date");var domify=require("domify");var each=require("each");var integration=require("analytics.js-integration");var is=require("is");var onBody=require("on-body");var useHttps=require("use-https");var ClickTale=module.exports=integration("ClickTale").assumesPageview().global("WRInitTime").global("ClickTale").global("ClickTaleSetUID").global("ClickTaleField").global("ClickTaleEvent").option("httpCdnUrl","http://s.clicktale.net/WRe0.js").option("httpsCdnUrl","").option("projectId","").option("recordingRatio",.01).option("partitionId","").tag('');Clicky.prototype.initialize=function(){var user=this.analytics.user();window.clicky_site_ids=window.clicky_site_ids||[this.options.siteId];this.identify(new Identify({userId:user.id(),traits:user.traits()}));this.load(this.ready)};Clicky.prototype.loaded=function(){return is.object(window.clicky)};Clicky.prototype.page=function(page){var properties=page.properties();var name=page.fullName();window.clicky.log(properties.path,name||properties.title)};Clicky.prototype.identify=function(identify){window.clicky_custom=window.clicky_custom||{};window.clicky_custom.session=window.clicky_custom.session||{};var traits=identify.traits();var username=identify.username();var email=identify.email();var name=identify.name();if(username||email||name)traits.username=username||email||name;extend(window.clicky_custom.session,traits)};Clicky.prototype.track=function(track){window.clicky.goal(track.event(),track.revenue())}},{facade:7,extend:66,"analytics.js-integration":162,is:16}],93:[function(require,module,exports){var integration=require("analytics.js-integration");var useHttps=require("use-https");var Comscore=module.exports=integration("comScore").assumesPageview().global("_comscore").global("COMSCORE").option("c1","2").option("c2","").tag("http",'")}},{bind:53,domify:183,each:4,extend:66,"analytics.js-integration":162,json:57}],102:[function(require,module,exports){var each=require("each");var integration=require("analytics.js-integration");var push=require("global-queue")("_fbq");var Facebook=module.exports=integration("Facebook Conversion Tracking").global("_fbq").option("currency","USD").tag('');FullStory.prototype.initialize=function(){var self=this;window._fs_debug=this.options.debug;window._fs_host="www.fullstory.com";window._fs_org=this.options.org;(function(m,n,e,t,l,o,g,y){g=m[e]=function(a,b){g.q?g.q.push([a,b]):g._api(a,b)};g.q=[];g.identify=function(i,v){g(l,{uid:i});if(v)g(l,v)};g.setUserVars=function(v){FS(l,v)};g.setSessionVars=function(v){FS("session",v)};g.setPageVars=function(v){FS("page",v)};self.ready();self.load()})(window,document,"FS","script","user")};FullStory.prototype.loaded=function(){return!!window.FS};FullStory.prototype.identify=function(identify){var id=identify.userId()||identify.anonymousId();var traits=identify.traits({name:"displayName"});var newTraits=foldl(function(results,value,key){if(key!=="id")results[key==="displayName"||key==="email"?key:convert(key,value)]=value;return results},{},traits);window.FS.identify(String(id),newTraits)};function convert(key,value){key=camel(key);if(is.string(value))return key+"_str";if(isInt(value))return key+"_int";if(isFloat(value))return key+"_real";if(is.date(value))return key+"_date";if(is.boolean(value))return key+"_bool"}function isFloat(n){return n===+n&&n!==(n|0)}function isInt(n){return n===+n&&n===(n|0)}},{"to-camel-case":199,foldl:176,"analytics.js-integration":162,is:16}],199:[function(require,module,exports){var toSpace=require("to-space-case");module.exports=toCamelCase;function toCamelCase(string){return toSpace(string).replace(/\s(\w)/g,function(matches,letter){return letter.toUpperCase()})}},{"to-space-case":184}],106:[function(require,module,exports){var integration=require("analytics.js-integration");var push=require("global-queue")("_gauges");var Gauges=module.exports=integration("Gauges").assumesPageview().global("_gauges").option("siteId","").tag('').tag("pixel",'');Wootric.prototype.initialize=function(){this.lastPageTracked=null;window.wootricSettings=window.wootricSettings||{};window.wootricSettings.account_token=this.options.accountToken;var self=this;this.load("library",function(){self.ready()})};Wootric.prototype.loaded=function(){return!!window.wootric};Wootric.prototype.identify=function(identify){var traits=identify.traits();var email=identify.email();var createdAt=identify.created();var language=traits.language;if(createdAt&&createdAt.getTime)window.wootricSettings.created_at=createdAt.getTime();if(language)window.wootricSettings.language=language;window.wootricSettings.email=email;window.wootricSettings.properties=omit(["created","createdAt","email"],traits);window.wootric("run")};Wootric.prototype.page=function(page){if(this.lastPageTracked===window.location){return}this.lastPageTracked=window.location;var wootricSettings=window.wootricSettings;this.load("pixel",{accountToken:this.options.accountToken,email:encodeURIComponent(wootricSettings.email),createdAt:wootricSettings.created_at,url:encodeURIComponent(page.url()),cacheBuster:Math.random()})}},{"analytics.js-integration":162,omit:201}],161:[function(require,module,exports){var bind=require("bind");var integration=require("analytics.js-integration");var tick=require("next-tick");var when=require("when");var Yandex=module.exports=integration("Yandex Metrica").assumesPageview().global("yandex_metrika_callbacks").global("Ya").option("counterId",null).option("clickmap",false).option("webvisor",false).tag('