Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Newer
Older
100644 20420 lines (16784 sloc) 439 KB
1
(function umd(require){
2
if (typeof exports === 'object') {
3
module.exports = require('1');
4
} else if (typeof define === 'function' && (define.amd || define.cmd)) {
5
define(function(){ return require('1'); });
6
} else {
7
this['analytics'] = require('1');
8
}
9
})((function outer(modules, cache, entries){
10
11
/**
12
* Global
13
*/
14
15
var global = (function(){ return this; })();
16
17
/**
18
* Require `name`.
19
*
20
* @param {String} name
21
* @api public
22
*/
23
24
function require(name){
25
if (cache[name]) return cache[name].exports;
26
if (modules[name]) return call(name, require);
27
throw new Error('cannot find module "' + name + '"');
28
}
29
30
/**
31
* Call module `id` and cache it.
32
*
33
* @param {Number} id
34
* @param {Function} require
35
* @return {Function}
36
* @api private
37
*/
38
39
function call(id, require){
40
var m = cache[id] = { exports: {} };
41
var mod = modules[id];
42
var name = mod[2];
43
var fn = mod[0];
44
var threw = true;
45
46
try {
47
fn.call(m.exports, function(req){
48
var dep = modules[id][1][req];
49
return require(dep || req);
50
}, m, m.exports, outer, modules, cache, entries);
51
threw = false;
52
} finally {
53
if (threw) {
54
delete cache[id];
55
} else if (name) {
56
// expose as 'name'.
57
cache[name] = cache[id];
58
}
59
}
60
61
return cache[id].exports;
62
}
63
64
/**
65
* Require all entries exposing them on global if needed.
66
*/
67
68
for (var id in entries) {
69
if (entries[id]) {
70
global[entries[id]] = require(id);
71
} else {
72
require(id);
73
}
74
}
75
76
/**
77
* Duo flag.
78
*/
79
80
require.duo = true;
81
82
/**
83
* Expose cache.
84
*/
85
86
require.cache = cache;
87
88
/**
89
* Expose modules
90
*/
91
92
require.modules = modules;
93
94
/**
95
* Return newest require.
96
*/
97
98
return require;
99
})({
100
1: [function(require, module, exports) {
101
102
/**
103
* Analytics.js
104
*
105
* (C) 2015 Segment.io Inc.
106
*/
107
108
var analytics = require('segmentio/analytics.js-core');
109
var Integrations = require('./integrations');
110
var each = require('each');
111
112
/**
Jul 1, 2015
113
* Expose the `analytics` singleton.
114
*/
115
116
module.exports = exports = analytics;
117
118
/**
119
* Expose require.
120
*/
121
122
analytics.require = require;
123
124
/**
125
* Expose `VERSION`.
126
*/
127
128
exports.VERSION = require('../bower.json').version;
129
130
/**
131
* Add integrations.
132
*/
133
134
each(Integrations, function(name, Integration) {
135
analytics.use(Integration);
136
});
137
138
}, {"segmentio/analytics.js-core":2,"./integrations":3,"each":4,"../bower.json":5}],
139
2: [function(require, module, exports) {
140
141
/**
142
* Analytics.js
143
*
144
* (C) 2013 Segment.io Inc.
145
*/
146
147
var Analytics = require('./analytics');
148
149
/**
150
* Expose the `analytics` singleton.
151
*/
152
153
var analytics = module.exports = exports = new Analytics();
154
155
/**
156
* Expose require
157
*/
158
159
analytics.require = require;
160
161
/**
162
* Expose `VERSION`.
163
*/
164
165
exports.VERSION = require('../bower.json').version;
166
167
}, {"./analytics":6,"../bower.json":7}],
168
6: [function(require, module, exports) {
169
170
/**
171
* Module dependencies.
172
*/
173
174
var _analytics = window.analytics;
175
var Emitter = require('emitter');
176
var Facade = require('facade');
177
var after = require('after');
178
var bind = require('bind');
179
var callback = require('callback');
180
var clone = require('clone');
181
var cookie = require('./cookie');
182
var debug = require('debug');
183
var defaults = require('defaults');
184
var each = require('each');
185
var group = require('./group');
186
var is = require('is');
187
var isMeta = require('is-meta');
188
var keys = require('object').keys;
189
var memory = require('./memory');
190
var normalize = require('./normalize');
191
var on = require('event').bind;
192
var pageDefaults = require('./pageDefaults');
193
var pick = require('pick');
194
var prevent = require('prevent');
195
var querystring = require('querystring');
196
var size = require('object').length;
197
var store = require('./store');
198
var user = require('./user');
199
var Alias = Facade.Alias;
200
var Group = Facade.Group;
201
var Identify = Facade.Identify;
202
var Page = Facade.Page;
203
var Track = Facade.Track;
204
205
/**
207
*/
208
209
exports = module.exports = Analytics;
210
211
/**
213
*/
214
215
exports.cookie = cookie;
216
exports.store = store;
217
exports.memory = memory;
218
219
/**
220
* Initialize a new `Analytics` instance.
221
*/
222
223
function Analytics() {
224
this._options({});
225
this.Integrations = {};
226
this._integrations = {};
227
this._readied = false;
228
this._timeout = 300;
229
// XXX: BACKWARDS COMPATIBILITY
230
this._user = user;
231
this.log = debug('analytics.js');
232
bind.all(this);
233
234
var self = this;
235
this.on('initialize', function(settings, options){
236
if (options.initialPageview) self.page();
237
self._parseQuery();
238
});
239
}
240
241
/**
243
*/
244
245
Emitter(Analytics.prototype);
246
247
/**
249
*
250
* @param {Function} plugin
251
* @return {Analytics}
252
*/
253
254
Analytics.prototype.use = function(plugin) {
255
plugin(this);
256
return this;
257
};
258
259
/**
260
* Define a new `Integration`.
261
*
262
* @param {Function} Integration
263
* @return {Analytics}
264
*/
265
266
Analytics.prototype.addIntegration = function(Integration) {
267
var name = Integration.prototype.name;
268
if (!name) throw new TypeError('attempted to add an invalid integration');
269
this.Integrations[name] = Integration;
270
return this;
271
};
272
273
/**
274
* Initialize with the given integration `settings` and `options`.
275
*
276
* Aliased to `init` for convenience.
277
*
278
* @param {Object} [settings={}]
279
* @param {Object} [options={}]
280
* @return {Analytics}
281
*/
282
283
Analytics.prototype.init = Analytics.prototype.initialize = function(settings, options) {
284
settings = settings || {};
285
options = options || {};
286
287
this._options(options);
288
this._readied = false;
289
290
// clean unknown integrations from settings
291
var self = this;
292
each(settings, function(name) {
293
var Integration = self.Integrations[name];
294
if (!Integration) delete settings[name];
295
});
296
297
// add integrations
298
each(settings, function(name, opts) {
299
var Integration = self.Integrations[name];
300
var integration = new Integration(clone(opts));
301
self.log('initialize %o - %o', name, opts);
302
self.add(integration);
303
});
304
305
var integrations = this._integrations;
306
307
// load user now that options are set
308
user.load();
309
group.load();
310
311
// make ready callback
312
var ready = after(size(integrations), function() {
313
self._readied = true;
314
self.emit('ready');
315
});
316
317
// initialize integrations, passing ready
318
each(integrations, function(name, integration) {
319
if (options.initialPageview && integration.options.initialPageview === false) {
320
integration.page = after(2, integration.page);
321
}
322
323
integration.analytics = self;
324
integration.once('ready', ready);
325
integration.initialize();
326
});
327
328
// backwards compat with angular plugin.
329
// TODO: remove
330
this.initialized = true;
332
this.emit('initialize', settings, options);
333
return this;
334
};
336
/**
337
* Set the user's `id`.
338
*
339
* @param {Mixed} id
340
*/
341
342
Analytics.prototype.setAnonymousId = function(id){
343
this.user().anonymousId(id);
344
return this;
345
};
346
347
/**
349
*
350
* @param {Integration} integration
351
*/
352
353
Analytics.prototype.add = function(integration){
354
this._integrations[integration.name] = integration;
355
return this;
356
};
357
358
/**
359
* Identify a user by optional `id` and `traits`.
360
*
361
* @param {string} [id=user.id()] User ID.
362
* @param {Object} [traits=null] User traits.
363
* @param {Object} [options=null]
364
* @param {Function} [fn]
365
* @return {Analytics}
366
*/
367
368
Analytics.prototype.identify = function(id, traits, options, fn) {
369
// Argument reshuffling.
370
/* eslint-disable no-unused-expressions, no-sequences */
371
if (is.fn(options)) fn = options, options = null;
372
if (is.fn(traits)) fn = traits, options = null, traits = null;
373
if (is.object(id)) options = traits, traits = id, id = user.id();
374
/* eslint-enable no-unused-expressions, no-sequences */
375
376
// clone traits before we manipulate so we don't do anything uncouth, and take
377
// from `user` so that we carryover anonymous traits
378
user.identify(id, traits);
379
380
var msg = this.normalize({
381
options: options,
382
traits: user.traits(),
383
userId: user.id()
384
});
385
386
this._invoke('identify', new Identify(msg));
387
388
// emit
389
this.emit('identify', id, traits, options);
390
this._callback(fn);
391
return this;
392
};
393
394
/**
395
* Return the current user.
396
*
398
*/
399
400
Analytics.prototype.user = function() {
401
return user;
402
};
403
404
/**
405
* Identify a group by optional `id` and `traits`. Or, if no arguments are
406
* supplied, return the current group.
407
*
408
* @param {string} [id=group.id()] Group ID.
409
* @param {Object} [traits=null] Group traits.
410
* @param {Object} [options=null]
411
* @param {Function} [fn]
412
* @return {Analytics|Object}
413
*/
414
415
Analytics.prototype.group = function(id, traits, options, fn) {
416
/* eslint-disable no-unused-expressions, no-sequences */
417
if (!arguments.length) return group;
418
if (is.fn(options)) fn = options, options = null;
419
if (is.fn(traits)) fn = traits, options = null, traits = null;
420
if (is.object(id)) options = traits, traits = id, id = group.id();
421
/* eslint-enable no-unused-expressions, no-sequences */
422
423
424
// grab from group again to make sure we're taking from the source
425
group.identify(id, traits);
426
427
var msg = this.normalize({
428
options: options,
429
traits: group.traits(),
430
groupId: group.id()
431
});
432
433
this._invoke('group', new Group(msg));
435
this.emit('group', id, traits, options);
436
this._callback(fn);
437
return this;
438
};
440
/**
441
* Track an `event` that a user has triggered with optional `properties`.
442
*
443
* @param {string} event
444
* @param {Object} [properties=null]
445
* @param {Object} [options=null]
446
* @param {Function} [fn]
447
* @return {Analytics}
448
*/
449
450
Analytics.prototype.track = function(event, properties, options, fn) {
451
// Argument reshuffling.
452
/* eslint-disable no-unused-expressions, no-sequences */
453
if (is.fn(options)) fn = options, options = null;
454
if (is.fn(properties)) fn = properties, options = null, properties = null;
455
/* eslint-enable no-unused-expressions, no-sequences */
457
// figure out if the event is archived.
458
var plan = this.options.plan || {};
459
var events = plan.track || {};
460
461
// normalize
462
var msg = this.normalize({
463
properties: properties,
464
options: options,
465
event: event
466
});
467
468
// plan.
469
plan = events[event];
470
if (plan) {
471
this.log('plan %o - %o', event, plan);
472
if (plan.enabled === false) return this._callback(fn);
473
defaults(msg.integrations, plan.integrations || {});
474
}
475
476
this._invoke('track', new Track(msg));
477
478
this.emit('track', event, properties, options);
479
this._callback(fn);
480
return this;
481
};
482
483
/**
484
* Helper method to track an outbound link that would normally navigate away
485
* from the page before the analytics calls were sent.
486
*
487
* BACKWARDS COMPATIBILITY: aliased to `trackClick`.
488
*
489
* @param {Element|Array} links
490
* @param {string|Function} event
491
* @param {Object|Function} properties (optional)
492
* @return {Analytics}
493
*/
494
495
Analytics.prototype.trackClick = Analytics.prototype.trackLink = function(links, event, properties) {
496
if (!links) return this;
497
// always arrays, handles jquery
498
if (is.element(links)) links = [links];
499
500
var self = this;
501
each(links, function(el) {
502
if (!is.element(el)) throw new TypeError('Must pass HTMLElement to `analytics.trackLink`.');
503
on(el, 'click', function(e) {
504
var ev = is.fn(event) ? event(el) : event;
505
var props = is.fn(properties) ? properties(el) : properties;
506
var href = el.getAttribute('href')
507
|| el.getAttributeNS('http://www.w3.org/1999/xlink', 'href')
508
|| el.getAttribute('xlink:href');
509
511
512
if (href && el.target !== '_blank' && !isMeta(e)) {
513
prevent(e);
514
self._callback(function() {
515
window.location.href = href;
516
});
517
}
518
});
519
});
520
523
524
/**
525
* Helper method to track an outbound form that would normally navigate away
526
* from the page before the analytics calls were sent.
527
*
528
* BACKWARDS COMPATIBILITY: aliased to `trackSubmit`.
529
*
530
* @param {Element|Array} forms
531
* @param {string|Function} event
532
* @param {Object|Function} properties (optional)
533
* @return {Analytics}
534
*/
535
536
Analytics.prototype.trackSubmit = Analytics.prototype.trackForm = function(forms, event, properties) {
537
if (!forms) return this;
538
// always arrays, handles jquery
539
if (is.element(forms)) forms = [forms];
540
541
var self = this;
542
each(forms, function(el) {
543
if (!is.element(el)) throw new TypeError('Must pass HTMLElement to `analytics.trackForm`.');
544
function handler(e) {
545
prevent(e);
546
547
var ev = is.fn(event) ? event(el) : event;
548
var props = is.fn(properties) ? properties(el) : properties;
549
self.track(ev, props);
550
551
self._callback(function() {
552
el.submit();
553
});
554
}
555
556
// Support the events happening through jQuery or Zepto instead of through
557
// the normal DOM API, because `el.submit` doesn't bubble up events...
558
var $ = window.jQuery || window.Zepto;
559
if ($) {
560
$(el).submit(handler);
561
} else {
562
on(el, 'submit', handler);
563
}
564
});
565
566
return this;
567
};
568
569
/**
570
* Trigger a pageview, labeling the current page with an optional `category`,
571
* `name` and `properties`.
572
*
573
* @param {string} [category]
574
* @param {string} [name]
575
* @param {Object|string} [properties] (or path)
576
* @param {Object} [options]
577
* @param {Function} [fn]
578
* @return {Analytics}
579
*/
580
581
Analytics.prototype.page = function(category, name, properties, options, fn) {
582
// Argument reshuffling.
583
/* eslint-disable no-unused-expressions, no-sequences */
584
if (is.fn(options)) fn = options, options = null;
585
if (is.fn(properties)) fn = properties, options = properties = null;
586
if (is.fn(name)) fn = name, options = properties = name = null;
587
if (is.object(category)) options = name, properties = category, name = category = null;
588
if (is.object(name)) options = properties, properties = name, name = null;
589
if (is.string(category) && !is.string(name)) name = category, category = null;
590
/* eslint-enable no-unused-expressions, no-sequences */
591
592
properties = clone(properties) || {};
593
if (name) properties.name = name;
594
if (category) properties.category = category;
595
596
// Ensure properties has baseline spec properties.
597
// TODO: Eventually move these entirely to `options.context.page`
598
var defs = pageDefaults();
599
defaults(properties, defs);
600
601
// Mirror user overrides to `options.context.page` (but exclude custom properties)
602
// (Any page defaults get applied in `this.normalize` for consistency.)
603
// Weird, yeah--moving special props to `context.page` will fix this in the long term.
604
var overrides = pick(keys(defs), properties);
605
if (!is.empty(overrides)) {
606
options = options || {};
607
options.context = options.context || {};
608
options.context.page = overrides;
609
}
611
var msg = this.normalize({
612
properties: properties,
613
category: category,
614
options: options,
615
name: name
616
});
617
618
this._invoke('page', new Page(msg));
619
620
this.emit('page', category, name, properties, options);
621
this._callback(fn);
622
return this;
623
};
624
625
/**
626
* FIXME: BACKWARDS COMPATIBILITY: convert an old `pageview` to a `page` call.
627
*
628
* @param {string} [url]
629
* @return {Analytics}
630
* @api private
631
*/
632
633
Analytics.prototype.pageview = function(url) {
634
var properties = {};
635
if (url) properties.path = url;
636
this.page(properties);
637
return this;
638
};
639
640
/**
641
* Merge two previously unassociated user identities.
643
* @param {string} to
644
* @param {string} from (optional)
645
* @param {Object} options (optional)
646
* @param {Function} fn (optional)
647
* @return {Analytics}
648
*/
649
650
Analytics.prototype.alias = function(to, from, options, fn) {
651
// Argument reshuffling.
652
/* eslint-disable no-unused-expressions, no-sequences */
653
if (is.fn(options)) fn = options, options = null;
654
if (is.fn(from)) fn = from, options = null, from = null;
655
if (is.object(from)) options = from, from = null;
656
/* eslint-enable no-unused-expressions, no-sequences */
658
var msg = this.normalize({
659
options: options,
660
previousId: from,
661
userId: to
662
});
663
664
this._invoke('alias', new Alias(msg));
665
666
this.emit('alias', to, from, options);
667
this._callback(fn);
668
return this;
669
};
670
671
/**
672
* Register a `fn` to be fired when all the analytics services are ready.
673
*
674
* @param {Function} fn
676
*/
677
678
Analytics.prototype.ready = function(fn) {
679
if (is.fn(fn)) {
680
if (this._readied) {
681
callback.async(fn);
682
} else {
683
this.once('ready', fn);
684
}
685
}
686
return this;
687
};
688
689
/**
690
* Set the `timeout` (in milliseconds) used for callbacks.
691
*
692
* @param {Number} timeout
693
*/
694
695
Analytics.prototype.timeout = function(timeout) {
696
this._timeout = timeout;
697
};
699
/**
700
* Enable or disable debug.
701
*
702
* @param {string|boolean} str
703
*/
705
Analytics.prototype.debug = function(str){
706
if (!arguments.length || str) {
707
debug.enable('analytics:' + (str || '*'));
708
} else {
709
debug.disable();
712
713
/**
714
* Apply options.
715
*
716
* @param {Object} options
717
* @return {Analytics}
718
* @api private
719
*/
720
721
Analytics.prototype._options = function(options) {
722
options = options || {};
723
this.options = options;
724
cookie.options(options.cookie);
725
store.options(options.localStorage);
726
user.options(options.user);
727
group.options(options.group);
728
return this;
729
};
730
731
/**
732
* Callback a `fn` after our defined timeout period.
733
*
734
* @param {Function} fn
735
* @return {Analytics}
736
* @api private
737
*/
738
739
Analytics.prototype._callback = function(fn) {
740
callback.async(fn, this._timeout);
741
return this;
742
};
743
744
/**
745
* Call `method` with `facade` on all enabled integrations.
746
*
747
* @param {string} method
748
* @param {Facade} facade
749
* @return {Analytics}
750
* @api private
751
*/
752
753
Analytics.prototype._invoke = function(method, facade) {
754
this.emit('invoke', facade);
755
756
each(this._integrations, function(name, integration) {
757
if (!facade.enabled(name)) return;
758
integration.invoke.call(integration, method, facade);
759
});
760
763
764
/**
765
* Push `args`.
766
*
767
* @param {Array} args
768
* @api private
769
*/
770
771
Analytics.prototype.push = function(args){
772
var method = args.shift();
773
if (!this[method]) return;
774
this[method].apply(this, args);
775
};
776
777
/**
778
* Reset group and user traits and id's.
779
*
780
* @api public
781
*/
782
783
Analytics.prototype.reset = function(){
784
this.user().logout();
785
this.group().logout();
786
};
787
788
/**
789
* Parse the query string for callable methods.
790
*
791
* @return {Analytics}
792
* @api private
793
*/
794
795
Analytics.prototype._parseQuery = function() {
796
// Identify and track any `ajs_uid` and `ajs_event` parameters in the URL.
797
var q = querystring.parse(window.location.search);
798
if (q.ajs_uid) this.identify(q.ajs_uid);
799
if (q.ajs_event) this.track(q.ajs_event);
800
if (q.ajs_aid) user.anonymousId(q.ajs_aid);
801
return this;
802
};
803
804
/**
805
* Normalize the given `msg`.
806
*
807
* @param {Object} msg
808
* @return {Object}
809
*/
810
811
Analytics.prototype.normalize = function(msg){
812
msg = normalize(msg, keys(this._integrations));
813
if (msg.anonymousId) user.anonymousId(msg.anonymousId);
814
msg.anonymousId = user.anonymousId();
815
816
// Ensure all outgoing requests include page data in their contexts.
817
msg.context.page = defaults(msg.context.page || {}, pageDefaults());
818
819
return msg;
820
};
821
822
/**
824
*/
825
826
Analytics.prototype.noConflict = function(){
827
window.analytics = _analytics;
828
return this;
829
};
830
831
832
}, {"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}],
833
8: [function(require, module, exports) {
834
835
/**
837
*/
838
839
var index = require('indexof');
840
841
/**
843
*/
844
845
module.exports = Emitter;
846
847
/**
848
* Initialize a new `Emitter`.