Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
6bcd304ead
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
20420 lines (16784 sloc) 439 KB
(function umd(require){
if (typeof exports === 'object') {
module.exports = require('1');
} else if (typeof define === 'function' && (define.amd || define.cmd)) {
define(function(){ return require('1'); });
} else {
this['analytics'] = require('1');
}
})((function outer(modules, cache, entries){
/**
* Global
*/
var global = (function(){ return this; })();
/**
* Require `name`.
*
* @param {String} name
* @api public
*/
function require(name){
if (cache[name]) return cache[name].exports;
if (modules[name]) return call(name, require);
throw new Error('cannot find module "' + name + '"');
}
/**
* Call module `id` and cache it.
*
* @param {Number} id
* @param {Function} require
* @return {Function}
* @api private
*/
function call(id, require){
var m = cache[id] = { exports: {} };
var mod = modules[id];
var name = mod[2];
var fn = mod[0];
var threw = true;
try {
fn.call(m.exports, function(req){
var dep = modules[id][1][req];
return require(dep || req);
}, m, m.exports, outer, modules, cache, entries);
threw = false;
} finally {
if (threw) {
delete cache[id];
} else if (name) {
// expose as 'name'.
cache[name] = cache[id];
}
}
return cache[id].exports;
}
/**
* Require all entries exposing them on global if needed.
*/
for (var id in entries) {
if (entries[id]) {
global[entries[id]] = require(id);
} else {
require(id);
}
}
/**
* Duo flag.
*/
require.duo = true;
/**
* Expose cache.
*/
require.cache = cache;
/**
* Expose modules
*/
require.modules = modules;
/**
* Return newest require.
*/
return require;
})({
1: [function(require, module, exports) {
/**
* Analytics.js
*
* (C) 2015 Segment.io Inc.
*/
var analytics = require('segmentio/analytics.js-core');
var Integrations = require('./integrations');
var each = require('each');
/**
* Expose the `analytics` singleton.
*/
module.exports = exports = analytics;
/**
* Expose require.
*/
analytics.require = require;
/**
* Expose `VERSION`.
*/
exports.VERSION = require('../bower.json').version;
/**
* Add integrations.
*/
each(Integrations, function(name, Integration) {
analytics.use(Integration);
});
}, {"segmentio/analytics.js-core":2,"./integrations":3,"each":4,"../bower.json":5}],
2: [function(require, module, exports) {
/**
* Analytics.js
*
* (C) 2013 Segment.io Inc.
*/
var Analytics = require('./analytics');
/**
* Expose the `analytics` singleton.
*/
var analytics = module.exports = exports = new Analytics();
/**
* Expose require
*/
analytics.require = require;
/**
* Expose `VERSION`.
*/
exports.VERSION = require('../bower.json').version;
}, {"./analytics":6,"../bower.json":7}],
6: [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 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 `Analytics`.
*/
exports = module.exports = Analytics;
/**
* Expose storage.
*/
exports.cookie = cookie;
exports.store = store;
exports.memory = memory;
/**
* Initialize a new `Analytics` instance.
*/
function Analytics() {
this._options({});
this.Integrations = {};
this._integrations = {};
this._readied = false;
this._timeout = 300;
// XXX: BACKWARDS COMPATIBILITY
this._user = user;
this.log = debug('analytics.js');
bind.all(this);
var self = this;
this.on('initialize', function(settings, options){
if (options.initialPageview) self.page();
self._parseQuery();
});
}
/**
* Event Emitter.
*/
Emitter(Analytics.prototype);
/**
* Use a `plugin`.
*
* @param {Function} plugin
* @return {Analytics}
*/
Analytics.prototype.use = function(plugin) {
plugin(this);
return this;
};
/**
* Define a new `Integration`.
*
* @param {Function} Integration
* @return {Analytics}
*/
Analytics.prototype.addIntegration = function(Integration) {
var name = Integration.prototype.name;
if (!name) throw new TypeError('attempted to add an invalid integration');
this.Integrations[name] = Integration;
return this;
};
/**
* Initialize with the given integration `settings` and `options`.
*
* Aliased to `init` for convenience.
*
* @param {Object} [settings={}]
* @param {Object} [options={}]
* @return {Analytics}
*/
Analytics.prototype.init = Analytics.prototype.initialize = function(settings, options) {
settings = settings || {};
options = options || {};
this._options(options);
this._readied = false;
// clean unknown integrations from settings
var self = this;
each(settings, function(name) {
var Integration = self.Integrations[name];
if (!Integration) delete settings[name];
});
// 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);
});
var integrations = this._integrations;
// load user now that options are set
user.load();
group.load();
// make ready callback
var ready = after(size(integrations), function() {
self._readied = true;
self.emit('ready');
});
// initialize integrations, passing ready
each(integrations, function(name, integration) {
if (options.initialPageview && integration.options.initialPageview === false) {
integration.page = after(2, integration.page);
}
integration.analytics = self;
integration.once('ready', ready);
integration.initialize();
});
// backwards compat with angular plugin.
// TODO: remove
this.initialized = true;
this.emit('initialize', settings, options);
return this;
};
/**
* Set the user's `id`.
*
* @param {Mixed} id
*/
Analytics.prototype.setAnonymousId = function(id){
this.user().anonymousId(id);
return this;
};
/**
* Add an integration.
*
* @param {Integration} integration
*/
Analytics.prototype.add = function(integration){
this._integrations[integration.name] = integration;
return this;
};
/**
* Identify a user by optional `id` and `traits`.
*
* @param {string} [id=user.id()] User ID.
* @param {Object} [traits=null] User traits.
* @param {Object} [options=null]
* @param {Function} [fn]
* @return {Analytics}
*/
Analytics.prototype.identify = function(id, traits, options, fn) {
// Argument reshuffling.
/* eslint-disable no-unused-expressions, no-sequences */
if (is.fn(options)) fn = options, options = null;
if (is.fn(traits)) fn = traits, options = null, traits = null;
if (is.object(id)) options = traits, traits = id, id = user.id();
/* eslint-enable no-unused-expressions, no-sequences */
// clone traits before we manipulate so we don't do anything uncouth, and take
// from `user` so that we carryover anonymous traits
user.identify(id, traits);
var msg = this.normalize({
options: options,
traits: user.traits(),
userId: user.id()
});
this._invoke('identify', new Identify(msg));
// emit
this.emit('identify', id, traits, options);
this._callback(fn);
return this;
};
/**
* Return the current user.
*
* @return {Object}
*/
Analytics.prototype.user = function() {
return user;
};
/**
* Identify a group by optional `id` and `traits`. Or, if no arguments are
* supplied, return the current group.
*
* @param {string} [id=group.id()] Group ID.
* @param {Object} [traits=null] Group traits.
* @param {Object} [options=null]
* @param {Function} [fn]
* @return {Analytics|Object}
*/
Analytics.prototype.group = function(id, traits, options, fn) {
/* eslint-disable no-unused-expressions, no-sequences */
if (!arguments.length) return group;
if (is.fn(options)) fn = options, options = null;
if (is.fn(traits)) fn = traits, options = null, traits = null;
if (is.object(id)) options = traits, traits = id, id = group.id();
/* eslint-enable no-unused-expressions, no-sequences */
// grab from group again to make sure we're taking from the source
group.identify(id, traits);
var msg = this.normalize({
options: options,
traits: group.traits(),
groupId: group.id()
});
this._invoke('group', new Group(msg));
this.emit('group', id, traits, options);
this._callback(fn);
return this;
};
/**
* Track an `event` that a user has triggered with optional `properties`.
*
* @param {string} event
* @param {Object} [properties=null]
* @param {Object} [options=null]
* @param {Function} [fn]
* @return {Analytics}
*/
Analytics.prototype.track = function(event, properties, options, fn) {
// Argument reshuffling.
/* eslint-disable no-unused-expressions, no-sequences */
if (is.fn(options)) fn = options, options = null;
if (is.fn(properties)) fn = properties, options = null, properties = null;
/* eslint-enable no-unused-expressions, no-sequences */
// figure out if the event is archived.
var plan = this.options.plan || {};
var events = plan.track || {};
// 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) return this._callback(fn);
defaults(msg.integrations, plan.integrations || {});
}
this._invoke('track', new Track(msg));
this.emit('track', event, properties, options);
this._callback(fn);
return this;
};
/**
* Helper method to track an outbound link that would normally navigate away
* from the page before the analytics calls were sent.
*
* BACKWARDS COMPATIBILITY: aliased to `trackClick`.
*
* @param {Element|Array} links
* @param {string|Function} event
* @param {Object|Function} properties (optional)
* @return {Analytics}
*/
Analytics.prototype.trackClick = Analytics.prototype.trackLink = function(links, event, properties) {
if (!links) return this;
// always arrays, handles jquery
if (is.element(links)) links = [links];
var self = this;
each(links, function(el) {
if (!is.element(el)) throw new TypeError('Must pass HTMLElement to `analytics.trackLink`.');
on(el, 'click', function(e) {
var ev = is.fn(event) ? event(el) : event;
var props = is.fn(properties) ? properties(el) : properties;
var href = el.getAttribute('href')
|| el.getAttributeNS('http://www.w3.org/1999/xlink', 'href')
|| el.getAttribute('xlink:href');
self.track(ev, props);
if (href && el.target !== '_blank' && !isMeta(e)) {
prevent(e);
self._callback(function() {
window.location.href = href;
});
}
});
});
return this;
};
/**
* Helper method to track an outbound form that would normally navigate away
* from the page before the analytics calls were sent.
*
* BACKWARDS COMPATIBILITY: aliased to `trackSubmit`.
*
* @param {Element|Array} forms
* @param {string|Function} event
* @param {Object|Function} properties (optional)
* @return {Analytics}
*/
Analytics.prototype.trackSubmit = Analytics.prototype.trackForm = function(forms, event, properties) {
if (!forms) return this;
// always arrays, handles jquery
if (is.element(forms)) forms = [forms];
var self = this;
each(forms, function(el) {
if (!is.element(el)) throw new TypeError('Must pass HTMLElement to `analytics.trackForm`.');
function handler(e) {
prevent(e);
var ev = is.fn(event) ? event(el) : event;
var props = is.fn(properties) ? properties(el) : properties;
self.track(ev, props);
self._callback(function() {
el.submit();
});
}
// Support the events happening through jQuery or Zepto instead of through
// the normal DOM API, because `el.submit` doesn't bubble up events...
var $ = window.jQuery || window.Zepto;
if ($) {
$(el).submit(handler);
} else {
on(el, 'submit', handler);
}
});
return this;
};
/**
* Trigger a pageview, labeling the current page with an optional `category`,
* `name` and `properties`.
*
* @param {string} [category]
* @param {string} [name]
* @param {Object|string} [properties] (or path)
* @param {Object} [options]
* @param {Function} [fn]
* @return {Analytics}
*/
Analytics.prototype.page = function(category, name, properties, options, fn) {
// Argument reshuffling.
/* eslint-disable no-unused-expressions, no-sequences */
if (is.fn(options)) fn = options, options = null;
if (is.fn(properties)) fn = properties, options = properties = null;
if (is.fn(name)) fn = name, options = properties = name = null;
if (is.object(category)) options = name, properties = category, name = category = null;
if (is.object(name)) options = properties, properties = name, name = null;
if (is.string(category) && !is.string(name)) name = category, category = null;
/* eslint-enable no-unused-expressions, no-sequences */
properties = clone(properties) || {};
if (name) properties.name = name;
if (category) properties.category = category;
// Ensure properties has baseline spec properties.
// TODO: Eventually move these entirely to `options.context.page`
var defs = pageDefaults();
defaults(properties, defs);
// Mirror user overrides to `options.context.page` (but exclude custom properties)
// (Any page defaults get applied in `this.normalize` for consistency.)
// Weird, yeah--moving special props to `context.page` will fix this in the long term.
var overrides = pick(keys(defs), properties);
if (!is.empty(overrides)) {
options = options || {};
options.context = options.context || {};
options.context.page = overrides;
}
var msg = this.normalize({
properties: properties,
category: category,
options: options,
name: name
});
this._invoke('page', new Page(msg));
this.emit('page', category, name, properties, options);
this._callback(fn);
return this;
};
/**
* FIXME: BACKWARDS COMPATIBILITY: convert an old `pageview` to a `page` call.
*
* @param {string} [url]
* @return {Analytics}
* @api private
*/
Analytics.prototype.pageview = function(url) {
var properties = {};
if (url) properties.path = url;
this.page(properties);
return this;
};
/**
* Merge two previously unassociated user identities.
*
* @param {string} to
* @param {string} from (optional)
* @param {Object} options (optional)
* @param {Function} fn (optional)
* @return {Analytics}
*/
Analytics.prototype.alias = function(to, from, options, fn) {
// Argument reshuffling.
/* eslint-disable no-unused-expressions, no-sequences */
if (is.fn(options)) fn = options, options = null;
if (is.fn(from)) fn = from, options = null, from = null;
if (is.object(from)) options = from, from = null;
/* eslint-enable no-unused-expressions, no-sequences */
var msg = this.normalize({
options: options,
previousId: from,
userId: to
});
this._invoke('alias', new Alias(msg));
this.emit('alias', to, from, options);
this._callback(fn);
return this;
};
/**
* Register a `fn` to be fired when all the analytics services are ready.
*
* @param {Function} fn
* @return {Analytics}
*/
Analytics.prototype.ready = function(fn) {
if (is.fn(fn)) {
if (this._readied) {
callback.async(fn);
} else {
this.once('ready', fn);
}
}
return this;
};
/**
* Set the `timeout` (in milliseconds) used for callbacks.
*
* @param {Number} timeout
*/
Analytics.prototype.timeout = function(timeout) {
this._timeout = timeout;
};
/**
* Enable or disable debug.
*
* @param {string|boolean} str
*/
Analytics.prototype.debug = function(str){
if (!arguments.length || str) {
debug.enable('analytics:' + (str || '*'));
} else {
debug.disable();
}
};
/**
* Apply options.
*
* @param {Object} options
* @return {Analytics}
* @api private
*/
Analytics.prototype._options = function(options) {
options = options || {};
this.options = options;
cookie.options(options.cookie);
store.options(options.localStorage);
user.options(options.user);
group.options(options.group);
return this;
};
/**
* Callback a `fn` after our defined timeout period.
*
* @param {Function} fn
* @return {Analytics}
* @api private
*/
Analytics.prototype._callback = function(fn) {
callback.async(fn, this._timeout);
return this;
};
/**
* Call `method` with `facade` on all enabled integrations.
*
* @param {string} method
* @param {Facade} facade
* @return {Analytics}
* @api private
*/
Analytics.prototype._invoke = function(method, facade) {
this.emit('invoke', facade);
each(this._integrations, function(name, integration) {
if (!facade.enabled(name)) return;
integration.invoke.call(integration, method, facade);
});
return this;
};
/**
* Push `args`.
*
* @param {Array} args
* @api private
*/
Analytics.prototype.push = function(args){
var method = args.shift();
if (!this[method]) return;
this[method].apply(this, args);
};
/**
* Reset group and user traits and id's.
*
* @api public
*/
Analytics.prototype.reset = function(){
this.user().logout();
this.group().logout();
};
/**
* Parse the query string for callable methods.
*
* @return {Analytics}
* @api private
*/
Analytics.prototype._parseQuery = function() {
// Identify and track any `ajs_uid` and `ajs_event` parameters in the URL.
var q = querystring.parse(window.location.search);
if (q.ajs_uid) this.identify(q.ajs_uid);
if (q.ajs_event) this.track(q.ajs_event);
if (q.ajs_aid) user.anonymousId(q.ajs_aid);
return this;
};
/**
* Normalize the given `msg`.
*
* @param {Object} msg
* @return {Object}
*/
Analytics.prototype.normalize = function(msg){
msg = normalize(msg, keys(this._integrations));
if (msg.anonymousId) user.anonymousId(msg.anonymousId);
msg.anonymousId = user.anonymousId();
// Ensure all outgoing requests include page data in their contexts.
msg.context.page = defaults(msg.context.page || {}, pageDefaults());
return msg;
};
/**
* No conflict support.
*/
Analytics.prototype.noConflict = function(){
window.analytics = _analytics;
return this;
};
}, {"emitter":8,"facade":9,"after":10,"bind":11,"callback":12,"clone":13,"./cookie":14,"debug":15,"defaults":16,"each":4,"./group":17,"is":18,"is-meta":19,"object":20,"./memory":21,"./normalize":22,"event":23,"./pageDefaults":24,"pick":25,"prevent":26,"querystring":27,"./store":28,"./user":29}],
8: [function(require, module, exports) {
/**
* Module dependencies.
*/
var index = require('indexof');
/**
* Expose `Emitter`.
*/
module.exports = Emitter;
/**
* Initialize a new `Emitter`.
*
* @api public
*/
function Emitter(obj) {
if (obj) return mixin(obj);
};
/**
* Mixin the emitter properties.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
function mixin(obj) {
for (var key in Emitter.prototype) {
obj[key] = Emitter.prototype[key];
}
return obj;
}
/**
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
(this._callbacks[event] = this._callbacks[event] || [])
.push(fn);
return this;
};
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.once = function(event, fn){
var self = this;
this._callbacks = this._callbacks || {};
function on() {
self.off(event, on);
fn.apply(this, arguments);
}
fn._off = on;
this.on(event, on);
return this;
};
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
// all
if (0 == arguments.length) {
this._callbacks = {};
return this;
}
// specific event
var callbacks = this._callbacks[event];
if (!callbacks) return this;
// remove all handlers
if (1 == arguments.length) {
delete this._callbacks[event];
return this;
}
// remove specific handler
var i = index(callbacks, fn._off || fn);
if (~i) callbacks.splice(i, 1);
return this;
};
/**
* Emit `event` with the given args.
*
* @param {String} event
* @param {Mixed} ...
* @return {Emitter}
*/
Emitter.prototype.emit = function(event){
this._callbacks = this._callbacks || {};
var args = [].slice.call(arguments, 1)
, callbacks = this._callbacks[event];
if (callbacks) {
callbacks = callbacks.slice(0);
for (var i = 0, len = callbacks.length; i < len; ++i) {
callbacks[i].apply(this, args);
}
}
return this;
};
/**
* Return array of callbacks for `event`.
*
* @param {String} event
* @return {Array}
* @api public
*/
Emitter.prototype.listeners = function(event){
this._callbacks = this._callbacks || {};
return this._callbacks[event] || [];
};
/**
* Check if this emitter has `event` handlers.
*
* @param {String} event
* @return {Boolean}
* @api public
*/
Emitter.prototype.hasListeners = function(event){
return !! this.listeners(event).length;
};
}, {"indexof":30}],
30: [function(require, module, exports) {
module.exports = function(arr, obj){
if (arr.indexOf) return arr.indexOf(obj);
for (var i = 0; i < arr.length; ++i) {
if (arr[i] === obj) return i;
}
return -1;
};
}, {}],
9: [function(require, module, exports) {
var Facade = require('./facade');
/**
* Expose `Facade` facade.
*/
module.exports = Facade;
/**
* Expose specific-method facades.
*/
Facade.Alias = require('./alias');
Facade.Group = require('./group');
Facade.Identify = require('./identify');
Facade.Track = require('./track');
Facade.Page = require('./page');
Facade.Screen = require('./screen');
}, {"./facade":31,"./alias":32,"./group":33,"./identify":34,"./track":35,"./page":36,"./screen":37}],
31: [function(require, module, exports) {
var traverse = require('isodate-traverse');
var isEnabled = require('./is-enabled');
var clone = require('./utils').clone;
var type = require('./utils').type;
var address = require('./address');
var objCase = require('obj-case');
var newDate = require('new-date');
/**
* Expose `Facade`.
*/
module.exports = Facade;
/**
* Initialize a new `Facade` with an `obj` of arguments.
*
* @param {Object} obj
*/
function Facade (obj) {
obj = clone(obj);
if (!obj.hasOwnProperty('timestamp')) obj.timestamp = new Date();
else obj.timestamp = newDate(obj.timestamp);
traverse(obj);
this.obj = obj;
}
/**
* Mixin address traits.
*/
address(Facade.prototype);
/**
* Return a proxy function for a `field` that will attempt to first use methods,
* and fallback to accessing the underlying object directly. You can specify
* deeply nested fields too like:
*
* this.proxy('options.Librato');
*
* @param {String} field
*/
Facade.prototype.proxy = function (field) {
var fields = field.split('.');
field = fields.shift();
// Call a function at the beginning to take advantage of facaded fields
var obj = this[field] || this.field(field);
if (!obj) return obj;
if (typeof obj === 'function') obj = obj.call(this) || {};
if (fields.length === 0) return transform(obj);
obj = objCase(obj, fields.join('.'));
return transform(obj);
};
/**
* Directly access a specific `field` from the underlying object, returning a
* clone so outsiders don't mess with stuff.
*
* @param {String} field
* @return {Mixed}
*/
Facade.prototype.field = function (field) {
var obj = this.obj[field];
return transform(obj);
};
/**
* Utility method to always proxy a particular `field`. You can specify deeply
* nested fields too like:
*
* Facade.proxy('options.Librato');
*
* @param {String} field
* @return {Function}
*/
Facade.proxy = function (field) {
return function () {
return this.proxy(field);
};
};
/**
* Utility method to directly access a `field`.
*
* @param {String} field
* @return {Function}
*/
Facade.field = function (field) {
return function () {
return this.field(field);
};
};
/**
* Proxy multiple `path`.
*
* @param {String} path
* @return {Array}
*/
Facade.multi = function(path){
return function(){
var multi = this.proxy(path + 's');
if ('array' == type(multi)) return multi;
var one = this.proxy(path);
if (one) one = [clone(one)];
return one || [];
};
};
/**
* Proxy one `path`.
*
* @param {String} path
* @return {Mixed}
*/
Facade.one = function(path){
return function(){
var one = this.proxy(path);
if (one) return one;
var multi = this.proxy(path + 's');
if ('array' == type(multi)) return multi[0];
};
};
/**
* Get the basic json object of this facade.
*
* @return {Object}
*/
Facade.prototype.json = function () {
var ret = clone(this.obj);
if (this.type) ret.type = this.type();
return ret;
};
/**
* Get the options of a call (formerly called "context"). If you pass an
* integration name, it will get the options for that specific integration, or
* undefined if the integration is not enabled.
*
* @param {String} integration (optional)
* @return {Object or Null}
*/
Facade.prototype.context =
Facade.prototype.options = function (integration) {
var options = clone(this.obj.options || this.obj.context) || {};
if (!integration) return clone(options);
if (!this.enabled(integration)) return;
var integrations = this.integrations();
var value = integrations[integration] || objCase(integrations, integration);
if ('boolean' == typeof value) value = {};
return value || {};
};
/**
* Check whether an integration is enabled.
*
* @param {String} integration
* @return {Boolean}
*/
Facade.prototype.enabled = function (integration) {
var allEnabled = this.proxy('options.providers.all');
if (typeof allEnabled !== 'boolean') allEnabled = this.proxy('options.all');
if (typeof allEnabled !== 'boolean') allEnabled = this.proxy('integrations.all');
if (typeof allEnabled !== 'boolean') allEnabled = true;
var enabled = allEnabled && isEnabled(integration);
var options = this.integrations();
// If the integration is explicitly enabled or disabled, use that
// First, check options.providers for backwards compatibility
if (options.providers && options.providers.hasOwnProperty(integration)) {
enabled = options.providers[integration];
}
// Next, check for the integration's existence in 'options' to enable it.
// If the settings are a boolean, use that, otherwise it should be enabled.
if (options.hasOwnProperty(integration)) {
var settings = options[integration];
if (typeof settings === 'boolean') {
enabled = settings;
} else {
enabled = true;
}
}
return enabled ? true : false;
};
/**
* Get all `integration` options.
*
* @param {String} integration
* @return {Object}
* @api private
*/
Facade.prototype.integrations = function(){
return this.obj.integrations
|| this.proxy('options.providers')
|| this.options();
};
/**
* Check whether the user is active.
*
* @return {Boolean}
*/
Facade.prototype.active = function () {
var active = this.proxy('options.active');
if (active === null || active === undefined) active = true;
return active;
};
/**
* Get `sessionId / anonymousId`.
*
* @return {Mixed}
* @api public
*/
Facade.prototype.sessionId =
Facade.prototype.anonymousId = function(){
return this.field('anonymousId')
|| this.field('sessionId');
};
/**
* Get `groupId` from `context.groupId`.
*
* @return {String}
* @api public
*/
Facade.prototype.groupId = Facade.proxy('options.groupId');
/**
* Get the call's "super properties" which are just traits that have been
* passed in as if from an identify call.
*
* @param {Object} aliases
* @return {Object}
*/
Facade.prototype.traits = function (aliases) {
var ret = this.proxy('options.traits') || {};
var id = this.userId();
aliases = aliases || {};
if (id) ret.id = id;
for (var alias in aliases) {
var value = null == this[alias]
? this.proxy('options.traits.' + alias)
: this[alias]();
if (null == value) continue;
ret[aliases[alias]] = value;
delete ret[alias];
}
return ret;
};
/**
* Add a convenient way to get the library name and version
*/
Facade.prototype.library = function(){
var library = this.proxy('options.library');
if (!library) return { name: 'unknown', version: null };
if (typeof library === 'string') return { name: library, version: null };
return library;
};
/**
* Setup some basic proxies.
*/
Facade.prototype.userId = Facade.field('userId');
Facade.prototype.channel = Facade.field('channel');
Facade.prototype.timestamp = Facade.field('timestamp');
Facade.prototype.userAgent = Facade.proxy('options.userAgent');
Facade.prototype.ip = Facade.proxy('options.ip');
/**
* Return the cloned and traversed object
*
* @param {Mixed} obj
* @return {Mixed}
*/
function transform(obj){
var cloned = clone(obj);
return cloned;
}
}, {"isodate-traverse":38,"./is-enabled":39,"./utils":40,"./address":41,"obj-case":42,"new-date":43}],
38: [function(require, module, exports) {
var is = require('is');
var isodate = require('isodate');
var each;
try {
each = require('each');
} catch (err) {
each = require('each-component');
}
/**
* Expose `traverse`.
*/
module.exports = traverse;
/**
* Traverse an object or array, and return a clone with all ISO strings parsed
* into Date objects.
*
* @param {Object} obj
* @return {Object}
*/
function traverse (input, strict) {
if (strict === undefined) strict = true;
if (is.object(input)) return object(input, strict);
if (is.array(input)) return array(input, strict);
return input;
}
/**
* Object traverser.
*
* @param {Object} obj
* @param {Boolean} strict
* @return {Object}
*/
function object (obj, strict) {
each(obj, function (key, val) {
if (isodate.is(val, strict)) {
obj[key] = isodate.parse(val);
} else if (is.object(val) || is.array(val)) {
traverse(val, strict);
}
});
return obj;
}
/**
* Array traverser.
*
* @param {Array} arr
* @param {Boolean} strict
* @return {Array}
*/
function array (arr, strict) {
each(arr, function (val, x) {
if (is.object(val)) {
traverse(val, strict);
} else if (isodate.is(val, strict)) {
arr[x] = isodate.parse(val);
}
});
return arr;
}
}, {"is":44,"isodate":45,"each":4}],
44: [function(require, module, exports) {
var isEmpty = require('is-empty');
try {
var typeOf = require('type');
} catch (e) {
var typeOf = require('component-type');
}
/**
* Types.
*/
var types = [
'arguments',
'array',
'boolean',
'date',
'element',
'function',
'null',
'number',
'object',
'regexp',
'string',
'undefined'
];
/**
* Expose type checkers.
*
* @param {Mixed} value
* @return {Boolean}
*/
for (var i = 0, type; type = types[i]; i++) exports[type] = generate(type);
/**
* Add alias for `function` for old browsers.
*/
exports.fn = exports['function'];
/**
* Expose `empty` check.
*/
exports.empty = isEmpty;
/**
* Expose `nan` check.
*/
exports.nan = function (val) {
return exports.number(val) && val != val;
};
/**
* Generate a type checker.
*
* @param {String} type
* @return {Function}
*/
function generate (type) {
return function (value) {
return type === typeOf(value);
};
}
}, {"is-empty":46,"type":47,"component-type":47}],
46: [function(require, module, exports) {
/**
* Expose `isEmpty`.
*/
module.exports = isEmpty;
/**
* Has.
*/
var has = Object.prototype.hasOwnProperty;
/**
* Test whether a value is "empty".
*
* @param {Mixed} val
* @return {Boolean}
*/
function isEmpty (val) {
if (null == val) return true;
if ('number' == typeof val) return 0 === val;
if (undefined !== val.length) return 0 === val.length;
for (var key in val) if (has.call(val, key)) return false;
return true;
}
}, {}],
47: [function(require, module, exports) {
/**
* toString ref.
*/
var toString = Object.prototype.toString;
/**
* Return the type of `val`.
*
* @param {Mixed} val
* @return {String}
* @api public
*/
module.exports = function(val){
switch (toString.call(val)) {
case '[object Date]': return 'date';
case '[object RegExp]': return 'regexp';
case '[object Arguments]': return 'arguments';
case '[object Array]': return 'array';
case '[object Error]': return 'error';
}
if (val === null) return 'null';
if (val === undefined) return 'undefined';
if (val !== val) return 'nan';
if (val && val.nodeType === 1) return 'element';
val = val.valueOf
? val.valueOf()
: Object.prototype.valueOf.apply(val)
return typeof val;
};
}, {}],
45: [function(require, module, exports) {
/**
* Matcher, slightly modified from:
*
* https://github.com/csnover/js-iso8601/blob/lax/iso8601.js
*/
var matcher = /^(\d{4})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:([ T])(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/;
/**
* Convert an ISO date string to a date. Fallback to native `Date.parse`.
*
* https://github.com/csnover/js-iso8601/blob/lax/iso8601.js
*
* @param {String} iso
* @return {Date}
*/
exports.parse = function (iso) {
var numericKeys = [1, 5, 6, 7, 11, 12];
var arr = matcher.exec(iso);
var offset = 0;
// fallback to native parsing
if (!arr) return new Date(iso);
// remove undefined values
for (var i = 0, val; val = numericKeys[i]; i++) {
arr[val] = parseInt(arr[val], 10) || 0;
}
// allow undefined days and months
arr[2] = parseInt(arr[2], 10) || 1;
arr[3] = parseInt(arr[3], 10) || 1;
// month is 0-11
arr[2]--;
// allow abitrary sub-second precision
arr[8] = arr[8]
? (arr[8] + '00').substring(0, 3)
: 0;
// apply timezone if one exists
if (arr[4] == ' ') {
offset = new Date().getTimezoneOffset();
} else if (arr[9] !== 'Z' && arr[10]) {
offset = arr[11] * 60 + arr[12];
if ('+' == arr[10]) offset = 0 - offset;
}
var millis = Date.UTC(arr[1], arr[2], arr[3], arr[5], arr[6] + offset, arr[7], arr[8]);
return new Date(millis);
};
/**
* Checks whether a `string` is an ISO date string. `strict` mode requires that
* the date string at least have a year, month and date.
*
* @param {String} string
* @param {Boolean} strict
* @return {Boolean}
*/
exports.is = function (string, strict) {
if (strict && false === /^\d{4}-\d{2}-\d{2}/.test(string)) return false;
return matcher.test(string);
};
}, {}],
4: [function(require, module, exports) {
/**
* Module dependencies.
*/
var type = require('type');
/**
* HOP reference.
*/
var has = Object.prototype.hasOwnProperty;
/**
* Iterate the given `obj` and invoke `fn(val, i)`.
*
* @param {String|Array|Object} obj
* @param {Function} fn
* @api public
*/
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);
}
};
/**
* Iterate string chars.
*
* @param {String} obj
* @param {Function} fn
* @api private
*/
function string(obj, fn) {
for (var i = 0; i < obj.length; ++i) {
fn(obj.charAt(i), i);
}
}
/**
* Iterate object keys.
*
* @param {Object} obj
* @param {Function} fn
* @api private
*/
function object(obj, fn) {
for (var key in obj) {
if (has.call(obj, key)) {
fn(key, obj[key]);
}
}
}
/**
* Iterate array-ish.
*
* @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":47}],
39: [function(require, module, exports) {
/**
* A few integrations are disabled by default. They must be explicitly
* enabled by setting options[Provider] = true.
*/
var disabled = {
Salesforce: true
};
/**
* Check whether an integration should be enabled by default.
*
* @param {String} integration
* @return {Boolean}
*/
module.exports = function (integration) {
return ! disabled[integration];
};
}, {}],
40: [function(require, module, exports) {
/**
* TODO: use component symlink, everywhere ?
*/
try {
exports.inherit = require('inherit');
exports.clone = require('clone');
exports.type = require('type');
} catch (e) {
exports.inherit = require('inherit-component');
exports.clone = require('clone-component');
exports.type = require('type-component');
}
}, {"inherit":48,"clone":49,"type":47}],
48: [function(require, module, exports) {
module.exports = function(a, b){
var fn = function(){};
fn.prototype = b.prototype;
a.prototype = new fn;
a.prototype.constructor = a;
};
}, {}],
49: [function(require, module, exports) {
/**
* Module dependencies.
*/
var type;
try {
type = require('component-type');
} catch (_) {
type = require('type');
}
/**
* Module exports.
*/
module.exports = clone;
/**
* Clones objects.
*
* @param {Mixed} any object
* @api public
*/
function clone(obj){
switch (type(obj)) {
case 'object':
var copy = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = clone(obj[key]);
}
}
return copy;
case 'array':
var copy = new Array(obj.length);
for (var i = 0, l = obj.length; i < l; i++) {
copy[i] = clone(obj[i]);
}
return copy;
case '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);
case 'date':
return new Date(obj.getTime());
default: // string, number, boolean, …
return obj;
}
}
}, {"component-type":47,"type":47}],
41: [function(require, module, exports) {
/**
* Module dependencies.
*/
var get = require('obj-case');
/**
* Add address getters to `proto`.
*
* @param {Function} proto
*/
module.exports = function(proto){
proto.zip = trait('postalCode', 'zip');
proto.country = trait('country');
proto.street = trait('street');
proto.state = trait('state');
proto.city = trait('city');
function trait(a, b){
return function(){
var traits = this.traits();
var props = this.properties ? this.properties() : {};
return get(traits, 'address.' + a)
|| get(traits, a)
|| (b ? get(traits, 'address.' + b) : null)
|| (b ? get(traits, b) : null)
|| get(props, 'address.' + a)
|| get(props, a)
|| (b ? get(props, 'address.' + b) : null)
|| (b ? get(props, b) : null);
};
}
};
}, {"obj-case":42}],
42: [function(require, module, exports) {
var identity = function(_){ return _; };
/**
* Module exports, export
*/
module.exports = multiple(find);
module.exports.find = module.exports;
/**
* Export the replacement function, return the modified object
*/
module.exports.replace = function (obj, key, val, options) {
multiple(replace).call(this, obj, key, val, options);
return obj;
};
/**
* Export the delete function, return the modified object
*/
module.exports.del = function (obj, key, options) {
multiple(del).call(this, obj, key, null, options);
return obj;
};
/**
* Compose applying the function to a nested key
*/
function multiple (fn) {
return function (obj, path, val, options) {
var normalize = options && isFunction(options.normalizer) ? options.normalizer : defaultNormalize;
path = normalize(path);
var key;
var finished = false;
while (!finished) loop();
function loop() {
for (key in obj) {
var normalizedKey = normalize(key);
if (0 === path.indexOf(normalizedKey)) {
var temp = path.substr(normalizedKey.length);
if (temp.charAt(0) === '.' || temp.length === 0) {
path = temp.substr(1);
var child = obj[key];
// we're at the end and there is nothing.
if (null == child) {
finished = true;
return;
}
// we're at the end and there is something.
if (!path.length) {
finished = true;
return;
}
// step into child
obj = child;
// but we're done here
return;
}
}
}
key = undefined;
// if we found no matching properties
// on the current object, there's no match.
finished = true;
}
if (!key) return;
if (null == obj) return obj;
// the `obj` and `key` is one above the leaf object and key, so
// start object: { a: { 'b.c': 10 } }
// end object: { 'b.c': 10 }
// end key: 'b.c'
// this way, you can do `obj[key]` and get `10`.
return fn(obj, key, val);
};
}
/**
* Find an object by its key
*
* find({ first_name : 'Calvin' }, 'firstName')
*/
function find (obj, key) {
if (obj.hasOwnProperty(key)) return obj[key];
}
/**
* Delete a value for a given key
*
* del({ a : 'b', x : 'y' }, 'X' }) -> { a : 'b' }
*/
function del (obj, key) {
if (obj.hasOwnProperty(key)) delete obj[key];
return obj;
}
/**
* Replace an objects existing value with a new one
*
* replace({ a : 'b' }, 'a', 'c') -> { a : 'c' }
*/
function replace (obj, key, val) {
if (obj.hasOwnProperty(key)) obj[key] = val;
return obj;
}
/**
* Normalize a `dot.separated.path`.
*
* A.HELL(!*&#(!)O_WOR LD.bar => ahelloworldbar
*
* @param {String} path
* @return {String}
*/
function defaultNormalize(path) {
return path.replace(/[^a-zA-Z0-9\.]+/g, '').toLowerCase();
}
/**
* Check if a value is a function.
*
* @param {*} val
* @return {boolean} Returns `true` if `val` is a function, otherwise `false`.
*/
function isFunction(val) {
return typeof val === 'function';
}
}, {}],
43: [function(require, module, exports) {
var is = require('is');
var isodate = require('isodate');
var milliseconds = require('./milliseconds');
var seconds = require('./seconds');
/**
* Returns a new Javascript Date object, allowing a variety of extra input types
* over the native Date constructor.
*
* @param {Date|String|Number} val
*/
module.exports = function newDate (val) {
if (is.date(val)) return val;
if (is.number(val)) return new Date(toMs(val));
// date strings
if (isodate.is(val)) return isodate.parse(val);
if (milliseconds.is(val)) return milliseconds.parse(val);
if (seconds.is(val)) return seconds.parse(val);
// fallback to Date.parse
return new Date(val);
};
/**
* If the number passed val is seconds from the epoch, turn it into milliseconds.
* Milliseconds would be greater than 31557600000 (December 31, 1970).
*
* @param {Number} num
*/
function toMs (num) {
if (num < 31557600000) return num * 1000;
return num;
}
}, {"is":50,"isodate":45,"./milliseconds":51,"./seconds":52}],
50: [function(require, module, exports) {
var isEmpty = require('is-empty')
, typeOf = require('type');
/**
* Types.
*/
var types = [
'arguments',
'array',
'boolean',
'date',
'element',
'function',
'null',
'number',
'object',
'regexp',
'string',
'undefined'
];
/**
* Expose type checkers.
*
* @param {Mixed} value
* @return {Boolean}
*/
for (var i = 0, type; type = types[i]; i++) exports[type] = generate(type);
/**
* Add alias for `function` for old browsers.
*/
exports.fn = exports['function'];
/**
* Expose `empty` check.
*/
exports.empty = isEmpty;
/**
* Expose `nan` check.
*/
exports.nan = function (val) {
return exports.number(val) && val != val;
};
/**
* Generate a type checker.
*
* @param {String} type
* @return {Function}
*/
function generate (type) {
return function (value) {
return type === typeOf(value);
};
}
}, {"is-empty":46,"type":47}],
51: [function(require, module, exports) {
/**
* Matcher.
*/
var matcher = /\d{13}/;
/**
* Check whether a string is a millisecond date string.
*
* @param {String} string
* @return {Boolean}
*/
exports.is = function (string) {
return matcher.test(string);
};
/**
* Convert a millisecond string to a date.
*
* @param {String} millis
* @return {Date}
*/
exports.parse = function (millis) {
millis = parseInt(millis, 10);
return new Date(millis);
};
}, {}],
52: [function(require, module, exports) {
/**
* Matcher.
*/
var matcher = /\d{10}/;
/**
* Check whether a string is a second date string.
*
* @param {String} string
* @return {Boolean}
*/
exports.is = function (string) {
return matcher.test(string);
};
/**
* Convert a second string to a date.
*
* @param {String} seconds
* @return {Date}
*/
exports.parse = function (seconds) {
var millis = parseInt(seconds, 10) * 1000;
return new Date(millis);
};
}, {}],
32: [function(require, module, exports) {
/**
* Module dependencies.
*/
var inherit = require('./utils').inherit;
var Facade = require('./facade');
/**
* Expose `Alias` facade.
*/
module.exports = Alias;
/**
* Initialize a new `Alias` facade with a `dictionary` of arguments.
*
* @param {Object} dictionary
* @property {String} from
* @property {String} to
* @property {Object} options
*/
function Alias (dictionary) {
Facade.call(this, dictionary);
}
/**
* Inherit from `Facade`.
*/
inherit(Alias, Facade);
/**
* Return type of facade.
*
* @return {String}
*/
Alias.prototype.type =
Alias.prototype.action = function () {
return 'alias';
};
/**
* Get `previousId`.
*
* @return {Mixed}
* @api public
*/
Alias.prototype.from =
Alias.prototype.previousId = function(){
return this.field('previousId')
|| this.field('from');
};
/**
* Get `userId`.
*
* @return {String}
* @api public
*/
Alias.prototype.to =
Alias.prototype.userId = function(){
return this.field('userId')
|| this.field('to');
};
}, {"./utils":40,"./facade":31}],
33: [function(require, module, exports) {
/**
* Module dependencies.
*/
var inherit = require('./utils').inherit;
var address = require('./address');
var isEmail = require('is-email');
var newDate = require('new-date');
var Facade = require('./facade');
/**
* Expose `Group` facade.
*/
module.exports = Group;
/**
* Initialize a new `Group` facade with a `dictionary` of arguments.
*
* @param {Object} dictionary
* @param {String} userId
* @param {String} groupId
* @param {Object} properties
* @param {Object} options
*/
function Group (dictionary) {
Facade.call(this, dictionary);
}
/**
* Inherit from `Facade`
*/
inherit(Group, Facade);
/**
* Get the facade's action.
*/
Group.prototype.type =
Group.prototype.action = function () {
return 'group';
};
/**
* Setup some basic proxies.
*/
Group.prototype.groupId = Facade.field('groupId');
/**
* Get created or createdAt.
*
* @return {Date}
*/
Group.prototype.created = function(){
var created = this.proxy('traits.createdAt')
|| this.proxy('traits.created')
|| this.proxy('properties.createdAt')
|| this.proxy('properties.created');
if (created) return newDate(created);
};
/**
* Get the group's email, falling back to the group ID if it's a valid email.
*
* @return {String}
*/
Group.prototype.email = function () {
var email = this.proxy('traits.email');
if (email) return email;
var groupId = this.groupId();
if (isEmail(groupId)) return groupId;
};
/**
* Get the group's traits.
*
* @param {Object} aliases
* @return {Object}
*/
Group.prototype.traits = function (aliases) {
var ret = this.properties();
var id = this.groupId();
aliases = aliases || {};
if (id) ret.id = id;
for (var alias in aliases) {
var value = null == this[alias]
? this.proxy('traits.' + alias)
: this[alias]();
if (null == value) continue;
ret[aliases[alias]] = value;
delete ret[alias];
}
return ret;
};
/**
* Special traits.
*/
Group.prototype.name = Facade.proxy('traits.name');
Group.prototype.industry = Facade.proxy('traits.industry');
Group.prototype.employees = Facade.proxy('traits.employees');
/**
* Get traits or properties.
*
* TODO: remove me
*
* @return {Object}
*/
Group.prototype.properties = function(){
return this.field('traits')
|| this.field('properties')
|| {};
};
}, {"./utils":40,"./address":41,"is-email":53,"new-date":43,"./facade":31}],
53: [function(require, module, exports) {
/**
* Expose `isEmail`.
*/
module.exports = isEmail;
/**
* Email address matcher.
*/
var matcher = /.+\@.+\..+/;
/**
* Loosely validate an email address.
*
* @param {String} string
* @return {Boolean}
*/
function isEmail (string) {
return matcher.test(string);
}
}, {}],
34: [function(require, module, exports) {
var address = require('./address');
var Facade = require('./facade');
var isEmail = require('is-email');
var newDate = require('new-date');
var utils = require('./utils');
var get = require('obj-case');
var trim = require('trim');
var inherit = utils.inherit;
var clone = utils.clone;
var type = utils.type;
/**
* Expose `Idenfity` facade.
*/
module.exports = Identify;
/**
* Initialize a new `Identify` facade with a `dictionary` of arguments.
*
* @param {Object} dictionary
* @param {String} userId
* @param {String} sessionId
* @param {Object} traits
* @param {Object} options
*/
function Identify (dictionary) {
Facade.call(this, dictionary);
}
/**
* Inherit from `Facade`.
*/
inherit(Identify, Facade);
/**
* Get the facade's action.
*/
Identify.prototype.type =
Identify.prototype.action = function () {
return 'identify';
};
/**
* Get the user's traits.
*
* @param {Object} aliases
* @return {Object}
*/
Identify.prototype.traits = function (aliases) {
var ret = this.field('traits') || {};
var id = this.userId();
aliases = aliases || {};
if (id) ret.id = id;
for (var alias in aliases) {
var value = null == this[alias]
? this.proxy('traits.' + alias)
: this[alias]();
if (null == value) continue;
ret[aliases[alias]] = value;
if (alias !== aliases[alias]) delete ret[alias];
}
return ret;
};
/**
* Get the user's email, falling back to their user ID if it's a valid email.
*
* @return {String}
*/
Identify.prototype.email = function () {
var email = this.proxy('traits.email');
if (email) return email;
var userId = this.userId();
if (isEmail(userId)) return userId;
};
/**
* Get the user's created date, optionally looking for `createdAt` since lots of
* people do that instead.
*
* @return {Date or Undefined}
*/
Identify.prototype.created = function () {
var created = this.proxy('traits.created') || this.proxy('traits.createdAt');
if (created) return newDate(created);
};
/**
* Get the company created date.
*
* @return {Date or undefined}
*/
Identify.prototype.companyCreated = function(){
var created = this.proxy('traits.company.created')
|| this.proxy('traits.company.createdAt');
if (created) return newDate(created);
};
/**
* Get the user's name, optionally combining a first and last name if that's all
* that was provided.
*
* @return {String or Undefined}
*/
Identify.prototype.name = function () {
var name = this.proxy('traits.name');
if (typeof name === 'string') return trim(name);
var firstName = this.firstName();
var lastName = this.lastName();
if (firstName && lastName) return trim(firstName + ' ' + lastName);
};
/**
* Get the user's first name, optionally splitting it out of a single name if
* that's all that was provided.
*
* @return {String or Undefined}
*/
Identify.prototype.firstName = function () {
var firstName = this.proxy('traits.firstName');
if (typeof firstName === 'string') return trim(firstName);
var name = this.proxy('traits.name');
if (typeof name === 'string') return trim(name).split(' ')[0];
};
/**
* Get the user's last name, optionally splitting it out of a single name if
* that's all that was provided.
*
* @return {String or Undefined}
*/
Identify.prototype.lastName = function () {
var lastName = this.proxy('traits.lastName');
if (typeof lastName === 'string') return trim(lastName);
var name = this.proxy('traits.name');