analytics-js-rails 0.11.0.rc1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. data/.travis.yml +10 -0
  2. data/README.md +16 -9
  3. data/analytics-js-rails.gemspec +3 -2
  4. data/app/assets/javascripts/analytics.js +1978 -187
  5. data/app/views/analytics-js/_loader.html.erb +1 -2
  6. data/lib/analytics-js-rails.rb +9 -1
  7. data/lib/analytics-js/engine.rb +3 -0
  8. data/lib/analytics-js/version.rb +1 -1
  9. data/spec/lib/analytics-js-rails_spec.rb +25 -0
  10. data/spec/rails-app-3.2.14/.gitignore +15 -0
  11. data/spec/rails-app-3.2.14/.rspec +1 -0
  12. data/spec/rails-app-3.2.14/Gemfile +23 -0
  13. data/spec/rails-app-3.2.14/README.rdoc +261 -0
  14. data/spec/rails-app-3.2.14/Rakefile +7 -0
  15. data/spec/rails-app-3.2.14/app/assets/images/rails.png +0 -0
  16. data/spec/rails-app-3.2.14/app/assets/javascripts/application.js +15 -0
  17. data/spec/rails-app-3.2.14/app/assets/javascripts/static.js +2 -0
  18. data/spec/rails-app-3.2.14/app/assets/stylesheets/application.css +13 -0
  19. data/spec/rails-app-3.2.14/app/assets/stylesheets/static.css +4 -0
  20. data/spec/rails-app-3.2.14/app/controllers/application_controller.rb +3 -0
  21. data/spec/rails-app-3.2.14/app/controllers/static_controller.rb +4 -0
  22. data/spec/rails-app-3.2.14/app/helpers/application_helper.rb +2 -0
  23. data/spec/rails-app-3.2.14/app/helpers/static_helper.rb +2 -0
  24. data/spec/rails-app-3.2.14/app/mailers/.gitkeep +0 -0
  25. data/spec/rails-app-3.2.14/app/models/.gitkeep +0 -0
  26. data/spec/rails-app-3.2.14/app/views/layouts/application.html.erb +17 -0
  27. data/spec/rails-app-3.2.14/app/views/static/home.html.erb +2 -0
  28. data/spec/rails-app-3.2.14/config.ru +4 -0
  29. data/spec/rails-app-3.2.14/config/application.rb +66 -0
  30. data/spec/rails-app-3.2.14/config/boot.rb +6 -0
  31. data/spec/rails-app-3.2.14/config/database.yml +25 -0
  32. data/spec/rails-app-3.2.14/config/environment.rb +5 -0
  33. data/spec/rails-app-3.2.14/config/environments/development.rb +37 -0
  34. data/spec/rails-app-3.2.14/config/environments/production.rb +67 -0
  35. data/spec/rails-app-3.2.14/config/environments/test.rb +37 -0
  36. data/spec/rails-app-3.2.14/config/initializers/backtrace_silencers.rb +7 -0
  37. data/spec/rails-app-3.2.14/config/initializers/inflections.rb +15 -0
  38. data/spec/rails-app-3.2.14/config/initializers/mime_types.rb +5 -0
  39. data/spec/rails-app-3.2.14/config/initializers/secret_token.rb +7 -0
  40. data/spec/rails-app-3.2.14/config/initializers/session_store.rb +8 -0
  41. data/spec/rails-app-3.2.14/config/initializers/wrap_parameters.rb +14 -0
  42. data/spec/rails-app-3.2.14/config/locales/en.yml +5 -0
  43. data/spec/rails-app-3.2.14/config/routes.rb +12 -0
  44. data/spec/rails-app-3.2.14/db/seeds.rb +7 -0
  45. data/spec/rails-app-3.2.14/lib/assets/.gitkeep +0 -0
  46. data/spec/rails-app-3.2.14/lib/tasks/.gitkeep +0 -0
  47. data/spec/rails-app-3.2.14/log/.gitkeep +0 -0
  48. data/spec/rails-app-3.2.14/public/404.html +26 -0
  49. data/spec/rails-app-3.2.14/public/422.html +26 -0
  50. data/spec/rails-app-3.2.14/public/500.html +25 -0
  51. data/spec/rails-app-3.2.14/public/favicon.ico +0 -0
  52. data/spec/rails-app-3.2.14/public/robots.txt +5 -0
  53. data/spec/rails-app-3.2.14/script/rails +6 -0
  54. data/spec/rails-app-3.2.14/vendor/assets/javascripts/.gitkeep +0 -0
  55. data/spec/rails-app-3.2.14/vendor/assets/stylesheets/.gitkeep +0 -0
  56. data/spec/rails-app-3.2.14/vendor/plugins/.gitkeep +0 -0
  57. data/spec/spec_helper.rb +22 -0
  58. data/spec/views/analytics-js/_loader.html.erb_spec.rb +15 -0
  59. data/spec/views/layouts/application.html.erb_spec.rb +11 -0
  60. metadata +143 -8
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.3"
5
+ - "2.0.0"
6
+ - jruby-18mode # JRuby in 1.8 mode
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+ script: bundle exec rspec spec
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Analytics.js-Rails
2
2
 
3
- A self-hosted copy of [Analytics.js](https://github.com/segmentio/analytics.js), a wrapper for web analytics services, for the Ruby on Rails 3.1+ asset pipeline.
3
+ This gem packages a self-hosted copy of [Analytics.js](https://github.com/segmentio/analytics.js), a wrapper for web analytics services, for use with the Ruby on Rails 3.1+ asset pipeline.
4
4
 
5
- Service A is good at X and Service B is good at Y, unfortunate but true. Use one common interface to standardize and modularize analytics setup across services.
5
+ The unfortunate truch: Analytics Service A is good at X, bad at Y, Analytics Service B is good at Y, bad at X and Z. Or maybe Marketing likes Service A, but Engineering needs Service B. Whatever. Analytics.js offers one common interface to standardize and modularize analytics setup across services - now easily in rails.
6
6
 
7
7
  ## Installation
8
8
 
@@ -20,26 +20,33 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- Add this line to your application.html.erb, for example:
23
+ Render the loader partial on all pages where you want Analytics.js to load. Typically this is done in application.html.erb, immediately after the opening <body> tag. For example:
24
24
 
25
- <%= render 'analytics-js/loader', providers: {
26
- 'Google Analytics' => 'google_analytics_key',
27
- 'KISSmetrics' => 'kissmetrics_key'
28
- } %>
25
+ <%= render 'analytics-js/loader',
26
+ user_identifier: current_user.id,
27
+ providers: {
28
+ 'Google Analytics' => 'google_analytics_key',
29
+ 'KISSmetrics' => 'kissmetrics_key'
30
+ } %>
29
31
 
30
32
  Of course, keys should probably be stored in config files, not hard coded.
31
33
 
34
+ Make sure that you ```root :to => "controller#action"``` in ```config/routes.rb```.
35
+
32
36
  Add to your ```config/environments/production.rb```:
33
37
 
34
38
  config.assets.precompile += %w( analytics.js )
35
39
 
36
-
37
- You can now use rickshaw in your app.
40
+ You can now use Analytics.js in your app.
38
41
 
39
42
  ## Version
40
43
 
41
44
  The version of this gem reflects the Analytics.js version.
42
45
 
46
+ ## Thanks
47
+
48
+ To the folks at [segment.io](http://segment.io) for all their work on Analytics.js - to a more panoptical world!
49
+
43
50
  ## Contributing
44
51
 
45
52
  Yes, please. Pull requests are very welcome. If it's not tested, we will not pull it.
@@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "railties", ">= 3.1.0"
21
+ spec.add_dependency "railties", ">= 3.1.0", "<4.0.0"
22
22
  spec.add_development_dependency "bundler", ">= 1.0.0"
23
23
  spec.add_development_dependency "rake"
24
- spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "rails", ">= 3.1.0", "<4.0.0"
25
+ spec.add_development_dependency "rspec-rails"
25
26
  end
@@ -1591,10 +1591,10 @@ require.register("segmentio-store.js/store.js", function(exports, require, modul
1591
1591
  var json = require('json')
1592
1592
  , store = {}
1593
1593
  , win = window
1594
- , doc = win.document
1595
- , localStorageName = 'localStorage'
1596
- , namespace = '__storejs__'
1597
- , storage;
1594
+ , doc = win.document
1595
+ , localStorageName = 'localStorage'
1596
+ , namespace = '__storejs__'
1597
+ , storage;
1598
1598
 
1599
1599
  store.disabled = false
1600
1600
  store.set = function(key, value) {}
@@ -1602,139 +1602,139 @@ store.get = function(key) {}
1602
1602
  store.remove = function(key) {}
1603
1603
  store.clear = function() {}
1604
1604
  store.transact = function(key, defaultVal, transactionFn) {
1605
- var val = store.get(key)
1606
- if (transactionFn == null) {
1607
- transactionFn = defaultVal
1608
- defaultVal = null
1609
- }
1610
- if (typeof val == 'undefined') { val = defaultVal || {} }
1611
- transactionFn(val)
1612
- store.set(key, val)
1605
+ var val = store.get(key)
1606
+ if (transactionFn == null) {
1607
+ transactionFn = defaultVal
1608
+ defaultVal = null
1609
+ }
1610
+ if (typeof val == 'undefined') { val = defaultVal || {} }
1611
+ transactionFn(val)
1612
+ store.set(key, val)
1613
1613
  }
1614
1614
  store.getAll = function() {}
1615
1615
 
1616
1616
  store.serialize = function(value) {
1617
- return json.stringify(value)
1617
+ return json.stringify(value)
1618
1618
  }
1619
1619
  store.deserialize = function(value) {
1620
- if (typeof value != 'string') { return undefined }
1621
- try { return json.parse(value) }
1622
- catch(e) { return value || undefined }
1620
+ if (typeof value != 'string') { return undefined }
1621
+ try { return json.parse(value) }
1622
+ catch(e) { return value || undefined }
1623
1623
  }
1624
1624
 
1625
1625
  // Functions to encapsulate questionable FireFox 3.6.13 behavior
1626
1626
  // when about.config::dom.storage.enabled === false
1627
1627
  // See https://github.com/marcuswestin/store.js/issues#issue/13
1628
1628
  function isLocalStorageNameSupported() {
1629
- try { return (localStorageName in win && win[localStorageName]) }
1630
- catch(err) { return false }
1629
+ try { return (localStorageName in win && win[localStorageName]) }
1630
+ catch(err) { return false }
1631
1631
  }
1632
1632
 
1633
1633
  if (isLocalStorageNameSupported()) {
1634
- storage = win[localStorageName]
1635
- store.set = function(key, val) {
1636
- if (val === undefined) { return store.remove(key) }
1637
- storage.setItem(key, store.serialize(val))
1638
- return val
1639
- }
1640
- store.get = function(key) { return store.deserialize(storage.getItem(key)) }
1641
- store.remove = function(key) { storage.removeItem(key) }
1642
- store.clear = function() { storage.clear() }
1643
- store.getAll = function() {
1644
- var ret = {}
1645
- for (var i=0; i<storage.length; ++i) {
1646
- var key = storage.key(i)
1647
- ret[key] = store.get(key)
1648
- }
1649
- return ret
1650
- }
1634
+ storage = win[localStorageName]
1635
+ store.set = function(key, val) {
1636
+ if (val === undefined) { return store.remove(key) }
1637
+ storage.setItem(key, store.serialize(val))
1638
+ return val
1639
+ }
1640
+ store.get = function(key) { return store.deserialize(storage.getItem(key)) }
1641
+ store.remove = function(key) { storage.removeItem(key) }
1642
+ store.clear = function() { storage.clear() }
1643
+ store.getAll = function() {
1644
+ var ret = {}
1645
+ for (var i=0; i<storage.length; ++i) {
1646
+ var key = storage.key(i)
1647
+ ret[key] = store.get(key)
1648
+ }
1649
+ return ret
1650
+ }
1651
1651
  } else if (doc.documentElement.addBehavior) {
1652
- var storageOwner,
1653
- storageContainer
1654
- // Since #userData storage applies only to specific paths, we need to
1655
- // somehow link our data to a specific path. We choose /favicon.ico
1656
- // as a pretty safe option, since all browsers already make a request to
1657
- // this URL anyway and being a 404 will not hurt us here. We wrap an
1658
- // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
1659
- // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
1660
- // since the iframe access rules appear to allow direct access and
1661
- // manipulation of the document element, even for a 404 page. This
1662
- // document can be used instead of the current document (which would
1663
- // have been limited to the current path) to perform #userData storage.
1664
- try {
1665
- storageContainer = new ActiveXObject('htmlfile')
1666
- storageContainer.open()
1667
- storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
1668
- storageContainer.close()
1669
- storageOwner = storageContainer.w.frames[0].document
1670
- storage = storageOwner.createElement('div')
1671
- } catch(e) {
1672
- // somehow ActiveXObject instantiation failed (perhaps some special
1673
- // security settings or otherwse), fall back to per-path storage
1674
- storage = doc.createElement('div')
1675
- storageOwner = doc.body
1676
- }
1677
- function withIEStorage(storeFunction) {
1678
- return function() {
1679
- var args = Array.prototype.slice.call(arguments, 0)
1680
- args.unshift(storage)
1681
- // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
1682
- // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
1683
- storageOwner.appendChild(storage)
1684
- storage.addBehavior('#default#userData')
1685
- storage.load(localStorageName)
1686
- var result = storeFunction.apply(store, args)
1687
- storageOwner.removeChild(storage)
1688
- return result
1689
- }
1690
- }
1691
-
1692
- // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
1693
- var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
1694
- function ieKeyFix(key) {
1695
- return key.replace(forbiddenCharsRegex, '___')
1696
- }
1697
- store.set = withIEStorage(function(storage, key, val) {
1698
- key = ieKeyFix(key)
1699
- if (val === undefined) { return store.remove(key) }
1700
- storage.setAttribute(key, store.serialize(val))
1701
- storage.save(localStorageName)
1702
- return val
1703
- })
1704
- store.get = withIEStorage(function(storage, key) {
1705
- key = ieKeyFix(key)
1706
- return store.deserialize(storage.getAttribute(key))
1707
- })
1708
- store.remove = withIEStorage(function(storage, key) {
1709
- key = ieKeyFix(key)
1710
- storage.removeAttribute(key)
1711
- storage.save(localStorageName)
1712
- })
1713
- store.clear = withIEStorage(function(storage) {
1714
- var attributes = storage.XMLDocument.documentElement.attributes
1715
- storage.load(localStorageName)
1716
- for (var i=0, attr; attr=attributes[i]; i++) {
1717
- storage.removeAttribute(attr.name)
1718
- }
1719
- storage.save(localStorageName)
1720
- })
1721
- store.getAll = withIEStorage(function(storage) {
1722
- var attributes = storage.XMLDocument.documentElement.attributes
1723
- var ret = {}
1724
- for (var i=0, attr; attr=attributes[i]; ++i) {
1725
- var key = ieKeyFix(attr.name)
1726
- ret[attr.name] = store.deserialize(storage.getAttribute(key))
1727
- }
1728
- return ret
1729
- })
1652
+ var storageOwner,
1653
+ storageContainer
1654
+ // Since #userData storage applies only to specific paths, we need to
1655
+ // somehow link our data to a specific path. We choose /favicon.ico
1656
+ // as a pretty safe option, since all browsers already make a request to
1657
+ // this URL anyway and being a 404 will not hurt us here. We wrap an
1658
+ // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
1659
+ // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
1660
+ // since the iframe access rules appear to allow direct access and
1661
+ // manipulation of the document element, even for a 404 page. This
1662
+ // document can be used instead of the current document (which would
1663
+ // have been limited to the current path) to perform #userData storage.
1664
+ try {
1665
+ storageContainer = new ActiveXObject('htmlfile')
1666
+ storageContainer.open()
1667
+ storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
1668
+ storageContainer.close()
1669
+ storageOwner = storageContainer.w.frames[0].document
1670
+ storage = storageOwner.createElement('div')
1671
+ } catch(e) {
1672
+ // somehow ActiveXObject instantiation failed (perhaps some special
1673
+ // security settings or otherwse), fall back to per-path storage
1674
+ storage = doc.createElement('div')
1675
+ storageOwner = doc.body
1676
+ }
1677
+ function withIEStorage(storeFunction) {
1678
+ return function() {
1679
+ var args = Array.prototype.slice.call(arguments, 0)
1680
+ args.unshift(storage)
1681
+ // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
1682
+ // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
1683
+ storageOwner.appendChild(storage)
1684
+ storage.addBehavior('#default#userData')
1685
+ storage.load(localStorageName)
1686
+ var result = storeFunction.apply(store, args)
1687
+ storageOwner.removeChild(storage)
1688
+ return result
1689
+ }
1690
+ }
1691
+
1692
+ // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
1693
+ var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
1694
+ function ieKeyFix(key) {
1695
+ return key.replace(forbiddenCharsRegex, '___')
1696
+ }
1697
+ store.set = withIEStorage(function(storage, key, val) {
1698
+ key = ieKeyFix(key)
1699
+ if (val === undefined) { return store.remove(key) }
1700
+ storage.setAttribute(key, store.serialize(val))
1701
+ storage.save(localStorageName)
1702
+ return val
1703
+ })
1704
+ store.get = withIEStorage(function(storage, key) {
1705
+ key = ieKeyFix(key)
1706
+ return store.deserialize(storage.getAttribute(key))
1707
+ })
1708
+ store.remove = withIEStorage(function(storage, key) {
1709
+ key = ieKeyFix(key)
1710
+ storage.removeAttribute(key)
1711
+ storage.save(localStorageName)
1712
+ })
1713
+ store.clear = withIEStorage(function(storage) {
1714
+ var attributes = storage.XMLDocument.documentElement.attributes
1715
+ storage.load(localStorageName)
1716
+ for (var i=0, attr; attr=attributes[i]; i++) {
1717
+ storage.removeAttribute(attr.name)
1718
+ }
1719
+ storage.save(localStorageName)
1720
+ })
1721
+ store.getAll = withIEStorage(function(storage) {
1722
+ var attributes = storage.XMLDocument.documentElement.attributes
1723
+ var ret = {}
1724
+ for (var i=0, attr; attr=attributes[i]; ++i) {
1725
+ var key = ieKeyFix(attr.name)
1726
+ ret[attr.name] = store.deserialize(storage.getAttribute(key))
1727
+ }
1728
+ return ret
1729
+ })
1730
1730
  }
1731
1731
 
1732
1732
  try {
1733
- store.set(namespace, namespace)
1734
- if (store.get(namespace) != namespace) { store.disabled = true }
1735
- store.remove(namespace)
1733
+ store.set(namespace, namespace)
1734
+ if (store.get(namespace) != namespace) { store.disabled = true }
1735
+ store.remove(namespace)
1736
1736
  } catch(e) {
1737
- store.disabled = true
1737
+ store.disabled = true
1738
1738
  }
1739
1739
  store.enabled = !store.disabled
1740
1740
 
@@ -3570,75 +3570,75 @@ module.exports = Provider.extend({
3570
3570
  });
3571
3571
  });
3572
3572
  require.register("analytics/src/providers/foxmetrics.js", function(exports, require, module){
3573
- // http://foxmetrics.com/documentation/apijavascript
3574
-
3575
- var Provider = require('../provider')
3576
- , load = require('load-script');
3577
-
3578
-
3579
- module.exports = Provider.extend({
3580
-
3581
- name : 'FoxMetrics',
3582
-
3583
- key : 'appId',
3584
-
3585
- defaults : {
3586
- appId : null
3587
- },
3588
-
3589
- initialize : function (options, ready) {
3590
- var _fxm = window._fxm || {};
3591
- window._fxm = _fxm.events || [];
3592
- load('//d35tca7vmefkrc.cloudfront.net/scripts/' + options.appId + '.js');
3593
-
3594
- // FoxMetrics makes a queue, so it's ready immediately.
3595
- ready();
3596
- },
3597
-
3598
- identify : function (userId, traits) {
3599
- // A `userId` is required for profile updates.
3600
- if (!userId) return;
3601
-
3602
- // FoxMetrics needs the first and last name seperately. Fallback to
3603
- // splitting the `name` trait if we don't have what we need.
3604
- var firstName = traits.firstName
3605
- , lastName = traits.lastName;
3606
-
3607
- if (!firstName && traits.name) firstName = traits.name.split(' ')[0];
3608
- if (!lastName && traits.name) lastName = traits.name.split(' ')[1];
3609
-
3610
- window._fxm.push([
3611
- '_fxm.visitor.profile',
3612
- userId, // user id
3613
- firstName, // first name
3614
- lastName, // last name
3615
- traits.email, // email
3616
- traits.address, // address
3617
- undefined, // social
3618
- undefined, // partners
3619
- traits // attributes
3620
- ]);
3621
- },
3622
-
3623
- track : function (event, properties) {
3624
- window._fxm.push([
3625
- event, // event name
3626
- properties.category, // category
3627
- properties // properties
3628
- ]);
3629
- },
3630
-
3631
- pageview : function (url) {
3632
- window._fxm.push([
3633
- '_fxm.pages.view',
3634
- undefined, // title
3635
- undefined, // name
3636
- undefined, // category
3637
- url, // url
3638
- undefined // referrer
3639
- ]);
3640
- }
3641
-
3573
+ // http://foxmetrics.com/documentation/apijavascript
3574
+
3575
+ var Provider = require('../provider')
3576
+ , load = require('load-script');
3577
+
3578
+
3579
+ module.exports = Provider.extend({
3580
+
3581
+ name : 'FoxMetrics',
3582
+
3583
+ key : 'appId',
3584
+
3585
+ defaults : {
3586
+ appId : null
3587
+ },
3588
+
3589
+ initialize : function (options, ready) {
3590
+ var _fxm = window._fxm || {};
3591
+ window._fxm = _fxm.events || [];
3592
+ load('//d35tca7vmefkrc.cloudfront.net/scripts/' + options.appId + '.js');
3593
+
3594
+ // FoxMetrics makes a queue, so it's ready immediately.
3595
+ ready();
3596
+ },
3597
+
3598
+ identify : function (userId, traits) {
3599
+ // A `userId` is required for profile updates.
3600
+ if (!userId) return;
3601
+
3602
+ // FoxMetrics needs the first and last name seperately. Fallback to
3603
+ // splitting the `name` trait if we don't have what we need.
3604
+ var firstName = traits.firstName
3605
+ , lastName = traits.lastName;
3606
+
3607
+ if (!firstName && traits.name) firstName = traits.name.split(' ')[0];
3608
+ if (!lastName && traits.name) lastName = traits.name.split(' ')[1];
3609
+
3610
+ window._fxm.push([
3611
+ '_fxm.visitor.profile',
3612
+ userId, // user id
3613
+ firstName, // first name
3614
+ lastName, // last name
3615
+ traits.email, // email
3616
+ traits.address, // address
3617
+ undefined, // social
3618
+ undefined, // partners
3619
+ traits // attributes
3620
+ ]);
3621
+ },
3622
+
3623
+ track : function (event, properties) {
3624
+ window._fxm.push([
3625
+ event, // event name
3626
+ properties.category, // category
3627
+ properties // properties
3628
+ ]);
3629
+ },
3630
+
3631
+ pageview : function (url) {
3632
+ window._fxm.push([
3633
+ '_fxm.pages.view',
3634
+ undefined, // title
3635
+ undefined, // name
3636
+ undefined, // category
3637
+ url, // url
3638
+ undefined // referrer
3639
+ ]);
3640
+ }
3641
+
3642
3642
  });
3643
3643
  });
3644
3644
  require.register("analytics/src/providers/gauges.js", function(exports, require, module){
@@ -3784,4 +3784,1795 @@ module.exports = Provider.extend({
3784
3784
  }
3785
3785
  if (options.initialPageview) {
3786
3786
  var path, canon = canonical();
3787
- if (canon)
3787
+ if (canon) path = url.parse(canon).pathname;
3788
+ this.pageview(path);
3789
+ }
3790
+
3791
+ // URLs change if DoubleClick is on. Even though Google Analytics makes a
3792
+ // queue, the `_gat` object isn't available until the library loads.
3793
+ if (options.doubleClick) {
3794
+ load('//stats.g.doubleclick.net/dc.js', ready);
3795
+ } else {
3796
+ load({
3797
+ http : 'http://www.google-analytics.com/ga.js',
3798
+ https : 'https://ssl.google-analytics.com/ga.js'
3799
+ }, ready);
3800
+ }
3801
+ },
3802
+
3803
+ initializeUniversal: function (options, ready) {
3804
+
3805
+ // GA-universal lets you set your own queue name
3806
+ var global = this.global = 'ga';
3807
+
3808
+ // and needs to know about this queue name in this special object
3809
+ // so that future plugins can also operate on the object
3810
+ window['GoogleAnalyticsObject'] = global;
3811
+
3812
+ // setup the global variable
3813
+ window[global] = window[global] || function () {
3814
+ (window[global].q = window[global].q || []).push(arguments);
3815
+ };
3816
+
3817
+ // GA also needs to know the current time (all from their snippet)
3818
+ window[global].l = 1 * new Date();
3819
+
3820
+ var createOpts = {};
3821
+
3822
+ // Apply a bunch of optional settings.
3823
+ if (options.domain)
3824
+ createOpts.cookieDomain = options.domain || 'none';
3825
+ if (type(options.siteSpeedSampleRate) === 'number')
3826
+ createOpts.siteSpeedSampleRate = options.siteSpeedSampleRate;
3827
+ if (options.anonymizeIp)
3828
+ ga('set', 'anonymizeIp', true);
3829
+
3830
+ ga('create', options.trackingId, createOpts);
3831
+
3832
+ if (options.initialPageview) {
3833
+ var path, canon = canonical();
3834
+ if (canon) path = url.parse(canon).pathname;
3835
+ this.pageview(path);
3836
+ }
3837
+
3838
+ load('//www.google-analytics.com/analytics.js');
3839
+
3840
+ // Google makes a queue so it's ready immediately.
3841
+ ready();
3842
+ },
3843
+
3844
+ track : function (event, properties) {
3845
+ properties || (properties = {});
3846
+
3847
+ var value;
3848
+
3849
+ // Since value is a common property name, ensure it is a number and Google
3850
+ // requires that it be an integer.
3851
+ if (type(properties.value) === 'number') value = Math.round(properties.value);
3852
+
3853
+ // Try to check for a `category` and `label`. A `category` is required,
3854
+ // so if it's not there we use `'All'` as a default. We can safely push
3855
+ // undefined if the special properties don't exist. Try using revenue
3856
+ // first, but fall back to a generic `value` as well.
3857
+ if (this.options.universalClient) {
3858
+ var opts = {};
3859
+ if (properties.noninteraction) opts.nonInteraction = properties.noninteraction;
3860
+ window[this.global](
3861
+ 'send',
3862
+ 'event',
3863
+ properties.category || 'All',
3864
+ event,
3865
+ properties.label,
3866
+ Math.round(properties.revenue) || value,
3867
+ opts
3868
+ );
3869
+ } else {
3870
+ window._gaq.push([
3871
+ '_trackEvent',
3872
+ properties.category || 'All',
3873
+ event,
3874
+ properties.label,
3875
+ Math.round(properties.revenue) || value,
3876
+ properties.noninteraction
3877
+ ]);
3878
+ }
3879
+ },
3880
+
3881
+ pageview : function (url) {
3882
+ if (this.options.universalClient) {
3883
+ window[this.global]('send', 'pageview', url);
3884
+ } else {
3885
+ window._gaq.push(['_trackPageview', url]);
3886
+ }
3887
+ }
3888
+
3889
+ });
3890
+ });
3891
+ require.register("analytics/src/providers/gosquared.js", function(exports, require, module){
3892
+ // http://www.gosquared.com/support
3893
+ // https://www.gosquared.com/customer/portal/articles/612063-tracker-functions
3894
+
3895
+ var Provider = require('../provider')
3896
+ , user = require('../user')
3897
+ , load = require('load-script')
3898
+ , onBody = require('on-body');
3899
+
3900
+
3901
+ module.exports = Provider.extend({
3902
+
3903
+ name : 'GoSquared',
3904
+
3905
+ key : 'siteToken',
3906
+
3907
+ defaults : {
3908
+ siteToken : null
3909
+ },
3910
+
3911
+ initialize : function (options, ready) {
3912
+ // GoSquared assumes a body in their script, so we need this wrapper.
3913
+ onBody(function () {
3914
+ var GoSquared = window.GoSquared = {};
3915
+ GoSquared.acct = options.siteToken;
3916
+ GoSquared.q = [];
3917
+ window._gstc_lt =+ (new Date());
3918
+
3919
+ GoSquared.VisitorName = user.id();
3920
+ GoSquared.Visitor = user.traits();
3921
+
3922
+ load('//d1l6p2sc9645hc.cloudfront.net/tracker.js');
3923
+
3924
+ // GoSquared makes a queue, so it's ready immediately.
3925
+ ready();
3926
+ });
3927
+ },
3928
+
3929
+ identify : function (userId, traits) {
3930
+ // TODO figure out if this will actually work. Seems like GoSquared will
3931
+ // never know these values are updated.
3932
+ if (userId) window.GoSquared.UserName = userId;
3933
+ if (traits) window.GoSquared.Visitor = traits;
3934
+ },
3935
+
3936
+ track : function (event, properties) {
3937
+ // GoSquared sets a `gs_evt_name` property with a value of the event
3938
+ // name, so it relies on properties being an object.
3939
+ window.GoSquared.q.push(['TrackEvent', event, properties || {}]);
3940
+ },
3941
+
3942
+ pageview : function (url) {
3943
+ window.GoSquared.q.push(['TrackView', url]);
3944
+ }
3945
+
3946
+ });
3947
+ });
3948
+ require.register("analytics/src/providers/heap.js", function(exports, require, module){
3949
+ // https://heapanalytics.com/docs
3950
+
3951
+ var Provider = require('../provider')
3952
+ , load = require('load-script');
3953
+
3954
+ module.exports = Provider.extend({
3955
+
3956
+ name : 'Heap',
3957
+
3958
+ key : 'apiKey',
3959
+
3960
+ defaults : {
3961
+ apiKey : null
3962
+ },
3963
+
3964
+ initialize : function (options, ready) {
3965
+ window.heap=window.heap||[];window.heap.load=function(a){window._heapid=a;var b=document.createElement("script");b.type="text/javascript",b.async=!0,b.src=("https:"===document.location.protocol?"https:":"http:")+"//d36lvucg9kzous.cloudfront.net";var c=document.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c);var d=function(a){return function(){heap.push([a].concat(Array.prototype.slice.call(arguments,0)))}},e=["identify","track"];for(var f=0;f<e.length;f++)heap[e[f]]=d(e[f])};
3966
+ window.heap.load(options.apiKey);
3967
+
3968
+ // heap creates its own queue, so we're ready right away
3969
+ ready();
3970
+ },
3971
+
3972
+ identify : function (userId, traits) {
3973
+ window.heap.identify(traits);
3974
+ },
3975
+
3976
+ track : function (event, properties) {
3977
+ window.heap.track(event, properties);
3978
+ }
3979
+
3980
+ });
3981
+ });
3982
+ require.register("analytics/src/providers/hittail.js", function(exports, require, module){
3983
+ // http://www.hittail.com
3984
+
3985
+ var Provider = require('../provider')
3986
+ , load = require('load-script');
3987
+
3988
+
3989
+ module.exports = Provider.extend({
3990
+
3991
+ name : 'HitTail',
3992
+
3993
+ key : 'siteId',
3994
+
3995
+ defaults : {
3996
+ siteId : null
3997
+ },
3998
+
3999
+ initialize : function (options, ready) {
4000
+ load('//' + options.siteId + '.hittail.com/mlt.js', ready);
4001
+ }
4002
+
4003
+ });
4004
+ });
4005
+ require.register("analytics/src/providers/hubspot.js", function(exports, require, module){
4006
+ // http://hubspot.clarify-it.com/d/4m62hl
4007
+
4008
+ var Provider = require('../provider')
4009
+ , isEmail = require('is-email')
4010
+ , load = require('load-script');
4011
+
4012
+
4013
+ module.exports = Provider.extend({
4014
+
4015
+ name : 'HubSpot',
4016
+
4017
+ key : 'portalId',
4018
+
4019
+ defaults : {
4020
+ portalId : null
4021
+ },
4022
+
4023
+ initialize : function (options, ready) {
4024
+ // HubSpot checks in their snippet to make sure another script with
4025
+ // `hs-analytics` isn't already in the DOM. Seems excessive, but who knows
4026
+ // if there's weird deprecation going on :p
4027
+ if (!document.getElementById('hs-analytics')) {
4028
+ window._hsq = window._hsq || [];
4029
+ var script = load('https://js.hubspot.com/analytics/' + (Math.ceil(new Date()/300000)*300000) + '/' + options.portalId + '.js');
4030
+ script.id = 'hs-analytics';
4031
+ }
4032
+
4033
+ // HubSpot makes a queue, so it's ready immediately.
4034
+ ready();
4035
+ },
4036
+
4037
+ // HubSpot does not use a userId, but the email address is required on
4038
+ // the traits object.
4039
+ identify : function (userId, traits) {
4040
+ window._hsq.push(["identify", traits]);
4041
+ },
4042
+
4043
+ // Event Tracking is available to HubSpot Enterprise customers only. In
4044
+ // addition to adding any unique event name, you can also use the id of an
4045
+ // existing custom event as the event variable.
4046
+ track : function (event, properties) {
4047
+ window._hsq.push(["trackEvent", event, properties]);
4048
+ },
4049
+
4050
+ // HubSpot doesn't support passing in a custom URL.
4051
+ pageview : function (url) {
4052
+ window._hsq.push(['_trackPageview']);
4053
+ }
4054
+
4055
+ });
4056
+ });
4057
+ require.register("analytics/src/providers/index.js", function(exports, require, module){
4058
+ module.exports = [
4059
+ require('./adroll'),
4060
+ require('./amplitude'),
4061
+ require('./bitdeli'),
4062
+ require('./bugherd'),
4063
+ require('./chartbeat'),
4064
+ require('./clicktale'),
4065
+ require('./clicky'),
4066
+ require('./comscore'),
4067
+ require('./crazyegg'),
4068
+ require('./customerio'),
4069
+ require('./errorception'),
4070
+ require('./foxmetrics'),
4071
+ require('./gauges'),
4072
+ require('./get-satisfaction'),
4073
+ require('./google-analytics'),
4074
+ require('./gosquared'),
4075
+ require('./heap'),
4076
+ require('./hittail'),
4077
+ require('./hubspot'),
4078
+ require('./improvely'),
4079
+ require('./intercom'),
4080
+ require('./keen-io'),
4081
+ require('./kissmetrics'),
4082
+ require('./klaviyo'),
4083
+ require('./livechat'),
4084
+ require('./lytics'),
4085
+ require('./mixpanel'),
4086
+ require('./olark'),
4087
+ require('./optimizely'),
4088
+ require('./perfect-audience'),
4089
+ require('./pingdom'),
4090
+ require('./preact'),
4091
+ require('./qualaroo'),
4092
+ require('./quantcast'),
4093
+ require('./sentry'),
4094
+ require('./snapengage'),
4095
+ require('./usercycle'),
4096
+ require('./userfox'),
4097
+ require('./uservoice'),
4098
+ require('./vero'),
4099
+ require('./visual-website-optimizer'),
4100
+ require('./woopra')
4101
+ ];
4102
+
4103
+ });
4104
+ require.register("analytics/src/providers/improvely.js", function(exports, require, module){
4105
+ // http://www.improvely.com/docs/landing-page-code
4106
+ // http://www.improvely.com/docs/conversion-code
4107
+ // http://www.improvely.com/docs/labeling-visitors
4108
+
4109
+ var Provider = require('../provider')
4110
+ , alias = require('alias')
4111
+ , load = require('load-script');
4112
+
4113
+
4114
+ module.exports = Provider.extend({
4115
+
4116
+ name : 'Improvely',
4117
+
4118
+ defaults : {
4119
+ // Improvely requires two options: `domain` and `projectId`.
4120
+ domain : null,
4121
+ projectId : null
4122
+ },
4123
+
4124
+ initialize : function (options, ready) {
4125
+ window._improvely = window._improvely || [];
4126
+ window.improvely = window.improvely || {
4127
+ init : function (e, t) { window._improvely.push(["init", e, t]); },
4128
+ goal : function (e) { window._improvely.push(["goal", e]); },
4129
+ label : function (e) { window._improvely.push(["label", e]); }
4130
+ };
4131
+
4132
+ load('//' + options.domain + '.iljmp.com/improvely.js');
4133
+ window.improvely.init(options.domain, options.projectId);
4134
+
4135
+ // Improvely creates a queue, so it's ready immediately.
4136
+ ready();
4137
+ },
4138
+
4139
+ identify : function (userId, traits) {
4140
+ if (userId) window.improvely.label(userId);
4141
+ },
4142
+
4143
+ track : function (event, properties) {
4144
+ // Improvely calls `revenue` `amount`, and puts the `event` in properties as
4145
+ // the `type`.
4146
+ properties || (properties = {});
4147
+ properties.type = event;
4148
+ alias(properties, { 'revenue' : 'amount' });
4149
+ window.improvely.goal(properties);
4150
+ }
4151
+
4152
+ });
4153
+
4154
+ });
4155
+ require.register("analytics/src/providers/intercom.js", function(exports, require, module){
4156
+ // http://docs.intercom.io/
4157
+ // http://docs.intercom.io/#IntercomJS
4158
+
4159
+ var Provider = require('../provider')
4160
+ , extend = require('extend')
4161
+ , load = require('load-script')
4162
+ , isEmail = require('is-email');
4163
+
4164
+
4165
+ module.exports = Provider.extend({
4166
+
4167
+ name : 'Intercom',
4168
+
4169
+ // Whether Intercom has already been booted or not. Intercom becomes booted
4170
+ // after Intercom('boot', ...) has been called on the first identify.
4171
+ booted : false,
4172
+
4173
+ key : 'appId',
4174
+
4175
+ defaults : {
4176
+ // Intercom's required key.
4177
+ appId : null,
4178
+ // An optional setting to display the Intercom inbox widget.
4179
+ activator : null,
4180
+ // Whether to show the count of messages for the inbox widget.
4181
+ counter : true
4182
+ },
4183
+
4184
+ initialize : function (options, ready) {
4185
+ load('https://static.intercomcdn.com/intercom.v1.js', ready);
4186
+ },
4187
+
4188
+ identify : function (userId, traits, options) {
4189
+ // Don't do anything if we just have traits the first time.
4190
+ if (!this.booted && !userId) return;
4191
+
4192
+ // Intercom specific settings. BACKWARDS COMPATIBILITY: we need to check for
4193
+ // the lowercase variant as well.
4194
+ options || (options = {});
4195
+ var Intercom = options.Intercom || options.intercom || {};
4196
+ traits.increments = Intercom.increments;
4197
+ traits.user_hash = Intercom.userHash || Intercom.user_hash;
4198
+
4199
+ // They need `created_at` as a Unix timestamp (seconds).
4200
+ if (traits.created) {
4201
+ traits.created_at = Math.floor(traits.created/1000);
4202
+ delete traits.created;
4203
+ }
4204
+
4205
+ // Convert a `company`'s `created` date.
4206
+ if (traits.company && traits.company.created) {
4207
+ traits.company.created_at = Math.floor(traits.company.created/1000);
4208
+ delete traits.company.created;
4209
+ }
4210
+
4211
+ // Optionally add the inbox widget.
4212
+ if (this.options.activator) {
4213
+ traits.widget = {
4214
+ activator : this.options.activator,
4215
+ use_counter : this.options.counter
4216
+ };
4217
+ }
4218
+
4219
+ // If this is the first time we've identified, `boot` instead of `update`
4220
+ // and add our one-time boot settings.
4221
+ if (this.booted) {
4222
+ window.Intercom('update', traits);
4223
+ } else {
4224
+ extend(traits, {
4225
+ app_id : this.options.appId,
4226
+ user_id : userId
4227
+ });
4228
+ window.Intercom('boot', traits);
4229
+ }
4230
+
4231
+ // Set the booted state, so that we know to call 'update' next time.
4232
+ this.booted = true;
4233
+ },
4234
+
4235
+ // Intercom doesn't have a separate `group` method, but they take a
4236
+ // `companies` trait for the user.
4237
+ group : function (groupId, properties, options) {
4238
+ properties.id = groupId;
4239
+ window.Intercom('update', { company : properties });
4240
+ }
4241
+
4242
+ });
4243
+
4244
+ });
4245
+ require.register("analytics/src/providers/keen-io.js", function(exports, require, module){
4246
+ // https://keen.io/docs/
4247
+
4248
+ var Provider = require('../provider')
4249
+ , load = require('load-script');
4250
+
4251
+
4252
+ module.exports = Provider.extend({
4253
+
4254
+ name : 'Keen IO',
4255
+
4256
+ defaults : {
4257
+ // The Project ID is **required**.
4258
+ projectId : null,
4259
+ // The Write Key is **required** to send events.
4260
+ writeKey : null,
4261
+ // The Read Key is optional, only if you want to "do analysis".
4262
+ readKey : null,
4263
+ // Whether or not to pass pageviews on to Keen IO.
4264
+ pageview : true,
4265
+ // Whether or not to track an initial pageview on `initialize`.
4266
+ initialPageview : true
4267
+ },
4268
+
4269
+ initialize : function (options, ready) {
4270
+ window.Keen = window.Keen||{configure:function(e){this._cf=e},addEvent:function(e,t,n,i){this._eq=this._eq||[],this._eq.push([e,t,n,i])},setGlobalProperties:function(e){this._gp=e},onChartsReady:function(e){this._ocrq=this._ocrq||[],this._ocrq.push(e)}};
4271
+ window.Keen.configure({
4272
+ projectId : options.projectId,
4273
+ writeKey : options.writeKey,
4274
+ readKey : options.readKey
4275
+ });
4276
+
4277
+ load('//dc8na2hxrj29i.cloudfront.net/code/keen-2.1.0-min.js');
4278
+
4279
+ if (options.initialPageview) this.pageview();
4280
+
4281
+ // Keen IO defines all their functions in the snippet, so they're ready.
4282
+ ready();
4283
+ },
4284
+
4285
+ identify : function (userId, traits) {
4286
+ // Use Keen IO global properties to include `userId` and `traits` on
4287
+ // every event sent to Keen IO.
4288
+ var globalUserProps = {};
4289
+ if (userId) globalUserProps.userId = userId;
4290
+ if (traits) globalUserProps.traits = traits;
4291
+ if (userId || traits) {
4292
+ window.Keen.setGlobalProperties(function(eventCollection) {
4293
+ return { user: globalUserProps };
4294
+ });
4295
+ }
4296
+ },
4297
+
4298
+ track : function (event, properties) {
4299
+ window.Keen.addEvent(event, properties);
4300
+ },
4301
+
4302
+ pageview : function (url) {
4303
+ if (!this.options.pageview) return;
4304
+
4305
+ var properties = {
4306
+ url : url || document.location.href,
4307
+ name : document.title
4308
+ };
4309
+
4310
+ this.track('Loaded a Page', properties);
4311
+ }
4312
+
4313
+ });
4314
+ });
4315
+ require.register("analytics/src/providers/kissmetrics.js", function(exports, require, module){
4316
+ // http://support.kissmetrics.com/apis/javascript
4317
+
4318
+ var Provider = require('../provider')
4319
+ , alias = require('alias')
4320
+ , load = require('load-script');
4321
+
4322
+
4323
+ module.exports = Provider.extend({
4324
+
4325
+ name : 'KISSmetrics',
4326
+
4327
+ key : 'apiKey',
4328
+
4329
+ defaults : {
4330
+ apiKey : null
4331
+ },
4332
+
4333
+ initialize : function (options, ready) {
4334
+ window._kmq = window._kmq || [];
4335
+ load('//i.kissmetrics.com/i.js');
4336
+ load('//doug1izaerwt3.cloudfront.net/' + options.apiKey + '.1.js');
4337
+
4338
+ // KISSmetrics creates a queue, so it's ready immediately.
4339
+ ready();
4340
+ },
4341
+
4342
+ // KISSmetrics uses two separate methods: `identify` for storing the
4343
+ // `userId`, and `set` for storing `traits`.
4344
+ identify : function (userId, traits) {
4345
+ if (userId) window._kmq.push(['identify', userId]);
4346
+ if (traits) window._kmq.push(['set', traits]);
4347
+ },
4348
+
4349
+ track : function (event, properties) {
4350
+ // KISSmetrics handles revenue with the `'Billing Amount'` property by
4351
+ // default, although it's changeable in the interface.
4352
+ if (properties) {
4353
+ alias(properties, {
4354
+ 'revenue' : 'Billing Amount'
4355
+ });
4356
+ }
4357
+
4358
+ window._kmq.push(['record', event, properties]);
4359
+ },
4360
+
4361
+ // Although undocumented, KISSmetrics actually supports not passing a second
4362
+ // ID, in which case it uses the currenty identified user's ID.
4363
+ alias : function (newId, originalId) {
4364
+ window._kmq.push(['alias', newId, originalId]);
4365
+ }
4366
+
4367
+ });
4368
+ });
4369
+ require.register("analytics/src/providers/klaviyo.js", function(exports, require, module){
4370
+ // https://www.klaviyo.com/docs
4371
+
4372
+ var Provider = require('../provider')
4373
+ , load = require('load-script');
4374
+
4375
+
4376
+ module.exports = Provider.extend({
4377
+
4378
+ name : 'Klaviyo',
4379
+
4380
+ key : 'apiKey',
4381
+
4382
+ defaults : {
4383
+ apiKey : null
4384
+ },
4385
+
4386
+ initialize : function (options, ready) {
4387
+ window._learnq = window._learnq || [];
4388
+ window._learnq.push(['account', options.apiKey]);
4389
+ load('//a.klaviyo.com/media/js/learnmarklet.js');
4390
+
4391
+ // Klaviyo creats a queue, so it's ready immediately.
4392
+ ready();
4393
+ },
4394
+
4395
+ identify : function (userId, traits) {
4396
+ // Klaviyo requires a `userId` and takes the it on the traits object itself.
4397
+ if (!userId) return;
4398
+ traits.$id = userId;
4399
+ window._learnq.push(['identify', traits]);
4400
+ },
4401
+
4402
+ track : function (event, properties) {
4403
+ window._learnq.push(['track', event, properties]);
4404
+ }
4405
+
4406
+ });
4407
+ });
4408
+ require.register("analytics/src/providers/livechat.js", function(exports, require, module){
4409
+ // http://www.livechatinc.com/api/javascript-api
4410
+
4411
+ var Provider = require('../provider')
4412
+ , each = require('each')
4413
+ , load = require('load-script');
4414
+
4415
+
4416
+ module.exports = Provider.extend({
4417
+
4418
+ name : 'LiveChat',
4419
+
4420
+ key : 'license',
4421
+
4422
+ defaults : {
4423
+ license : null
4424
+ },
4425
+
4426
+ initialize : function (options, ready) {
4427
+ window.__lc = { license : options.license };
4428
+ load('//cdn.livechatinc.com/tracking.js', ready);
4429
+ },
4430
+
4431
+ // LiveChat isn't an analytics service, but we can use the `userId` and
4432
+ // `traits` to tag the user with their real name in the chat console.
4433
+ identify : function (userId, traits) {
4434
+ // In case the LiveChat library hasn't loaded yet.
4435
+ if (!window.LC_API) return;
4436
+
4437
+ // LiveChat takes them in an array format.
4438
+ var variables = [];
4439
+
4440
+ if (userId) variables.push({ name: 'User ID', value: userId });
4441
+ if (traits) {
4442
+ each(traits, function (key, value) {
4443
+ variables.push({
4444
+ name : key,
4445
+ value : value
4446
+ });
4447
+ });
4448
+ }
4449
+
4450
+ window.LC_API.set_custom_variables(variables);
4451
+ }
4452
+
4453
+ });
4454
+ });
4455
+ require.register("analytics/src/providers/lytics.js", function(exports, require, module){
4456
+ // Lytics
4457
+ // --------
4458
+ // [Documentation](http://developer.lytics.io/doc#jstag),
4459
+
4460
+ var Provider = require('../provider')
4461
+ , load = require('load-script');
4462
+
4463
+
4464
+ module.exports = Provider.extend({
4465
+
4466
+ name : 'Lytics',
4467
+
4468
+ key : 'cid',
4469
+
4470
+ defaults : {
4471
+ cid: null
4472
+ },
4473
+
4474
+ initialize : function (options, ready) {
4475
+ window.jstag = (function () {
4476
+ var t={_q:[],_c:{cid:options.cid,url:'//c.lytics.io'},ts:(new Date()).getTime()};
4477
+ t.send=function(){
4478
+ this._q.push(["ready","send",Array.prototype.slice.call(arguments)]);
4479
+ return this;
4480
+ };
4481
+ return t;
4482
+ })();
4483
+
4484
+ load('//c.lytics.io/static/io.min.js');
4485
+
4486
+ ready();
4487
+ },
4488
+
4489
+ identify: function (userId, traits) {
4490
+ traits._uid = userId;
4491
+ window.jstag.send(traits);
4492
+ },
4493
+
4494
+ track: function (event, properties) {
4495
+ properties._e = event;
4496
+ window.jstag.send(properties);
4497
+ },
4498
+
4499
+ pageview: function (url) {
4500
+ window.jstag.send();
4501
+ }
4502
+
4503
+ });
4504
+ });
4505
+ require.register("analytics/src/providers/mixpanel.js", function(exports, require, module){
4506
+ // https://mixpanel.com/docs/integration-libraries/javascript
4507
+ // https://mixpanel.com/docs/people-analytics/javascript
4508
+ // https://mixpanel.com/docs/integration-libraries/javascript-full-api
4509
+
4510
+ var Provider = require('../provider')
4511
+ , alias = require('alias')
4512
+ , isEmail = require('is-email')
4513
+ , load = require('load-script');
4514
+
4515
+
4516
+ module.exports = Provider.extend({
4517
+
4518
+ name : 'Mixpanel',
4519
+
4520
+ key : 'token',
4521
+
4522
+ defaults : {
4523
+ // Whether to call `mixpanel.nameTag` on `identify`.
4524
+ nameTag : true,
4525
+ // Whether to use Mixpanel's People API.
4526
+ people : false,
4527
+ // The Mixpanel API token for your account.
4528
+ token : null,
4529
+ // Whether to track pageviews to Mixpanel.
4530
+ pageview : false,
4531
+ // Whether to track an initial pageview on initialize.
4532
+ initialPageview : false,
4533
+ // A custom cookie name to use
4534
+ cookieName : null
4535
+ },
4536
+
4537
+ initialize : function (options, ready) {
4538
+ (function (c, a) {
4539
+ window.mixpanel = a;
4540
+ var b, d, h, e;
4541
+ a._i = [];
4542
+ a.init = function (b, c, f) {
4543
+ function d(a, b) {
4544
+ var c = b.split('.');
4545
+ 2 == c.length && (a = a[c[0]], b = c[1]);
4546
+ a[b] = function () {
4547
+ a.push([b].concat(Array.prototype.slice.call(arguments, 0)));
4548
+ };
4549
+ }
4550
+ var g = a;
4551
+ 'undefined' !== typeof f ? g = a[f] = [] : f = 'mixpanel';
4552
+ g.people = g.people || [];
4553
+ h = ['disable', 'track', 'track_pageview', 'track_links', 'track_forms', 'register', 'register_once', 'unregister', 'identify', 'alias', 'name_tag', 'set_config', 'people.set', 'people.increment', 'people.track_charge', 'people.append'];
4554
+ for (e = 0; e < h.length; e++) d(g, h[e]);
4555
+ a._i.push([b, c, f]);
4556
+ };
4557
+ a.__SV = 1.2;
4558
+ // Modification to the snippet: call ready whenever the library has
4559
+ // fully loaded.
4560
+ load('//cdn.mxpnl.com/libs/mixpanel-2.2.min.js', ready);
4561
+ })(document, window.mixpanel || []);
4562
+
4563
+ // Mixpanel only accepts snake_case options
4564
+ options.cookie_name = options.cookieName;
4565
+
4566
+ // Pass options directly to `init` as the second argument.
4567
+ window.mixpanel.init(options.token, options);
4568
+
4569
+ if (options.initialPageview) this.pageview();
4570
+ },
4571
+
4572
+ identify : function (userId, traits) {
4573
+ // Alias the traits' keys with dollar signs for Mixpanel's API.
4574
+ alias(traits, {
4575
+ 'created' : '$created',
4576
+ 'email' : '$email',
4577
+ 'firstName' : '$first_name',
4578
+ 'lastName' : '$last_name',
4579
+ 'lastSeen' : '$last_seen',
4580
+ 'name' : '$name',
4581
+ 'username' : '$username',
4582
+ 'phone' : '$phone'
4583
+ });
4584
+
4585
+ // Finally, call all of the identify equivalents. Verify certain calls
4586
+ // against options to make sure they're enabled.
4587
+ if (userId) {
4588
+ window.mixpanel.identify(userId);
4589
+ if (this.options.nameTag) window.mixpanel.name_tag(traits && traits.$email || userId);
4590
+ }
4591
+ if (traits) {
4592
+ window.mixpanel.register(traits);
4593
+ if (this.options.people) window.mixpanel.people.set(traits);
4594
+ }
4595
+ },
4596
+
4597
+ track : function (event, properties) {
4598
+ window.mixpanel.track(event, properties);
4599
+
4600
+ // Mixpanel handles revenue with a `transaction` call in their People
4601
+ // feature. So if we're using people, record a transcation.
4602
+ if (properties && properties.revenue && this.options.people) {
4603
+ window.mixpanel.people.track_charge(properties.revenue);
4604
+ }
4605
+ },
4606
+
4607
+ // Mixpanel doesn't actually track the pageviews, but they do show up in the
4608
+ // Mixpanel stream.
4609
+ pageview : function (url) {
4610
+ window.mixpanel.track_pageview(url);
4611
+
4612
+ // If they don't want pageviews tracked, leave now.
4613
+ if (!this.options.pageview) return;
4614
+
4615
+ var properties = {
4616
+ url : url || document.location.href,
4617
+ name : document.title
4618
+ };
4619
+
4620
+ this.track('Loaded a Page', properties);
4621
+ },
4622
+
4623
+ // Although undocumented, Mixpanel actually supports the `originalId`. It
4624
+ // just usually defaults to the current user's `distinct_id`.
4625
+ alias : function (newId, originalId) {
4626
+
4627
+ if(window.mixpanel.get_distinct_id &&
4628
+ window.mixpanel.get_distinct_id() === newId) return;
4629
+
4630
+ // HACK: internal mixpanel API to ensure we don't overwrite.
4631
+ if(window.mixpanel.get_property &&
4632
+ window.mixpanel.get_property('$people_distinct_id') === newId) return;
4633
+
4634
+ window.mixpanel.alias(newId, originalId);
4635
+ }
4636
+
4637
+ });
4638
+ });
4639
+ require.register("analytics/src/providers/olark.js", function(exports, require, module){
4640
+ // http://www.olark.com/documentation
4641
+
4642
+ var Provider = require('../provider')
4643
+ , isEmail = require('is-email');
4644
+
4645
+
4646
+ module.exports = Provider.extend({
4647
+
4648
+ name : 'Olark',
4649
+
4650
+ key : 'siteId',
4651
+
4652
+ chatting : false,
4653
+
4654
+ defaults : {
4655
+ siteId : null,
4656
+ // Whether to use the user's name or email in the Olark chat console.
4657
+ identify : true,
4658
+ // Whether to log pageviews to the Olark chat console.
4659
+ track : false,
4660
+ // Whether to log pageviews to the Olark chat console.
4661
+ pageview : true
4662
+ },
4663
+
4664
+ initialize : function (options, ready) {
4665
+ window.olark||(function(c){var f=window,d=document,l=f.location.protocol=="https:"?"https:":"http:",z=c.name,r="load";var nt=function(){f[z]=function(){(a.s=a.s||[]).push(arguments)};var a=f[z]._={},q=c.methods.length;while(q--){(function(n){f[z][n]=function(){f[z]("call",n,arguments)}})(c.methods[q])}a.l=c.loader;a.i=nt;a.p={0:+new Date};a.P=function(u){a.p[u]=new Date-a.p[0]};function s(){a.P(r);f[z](r)}f.addEventListener?f.addEventListener(r,s,false):f.attachEvent("on"+r,s);var ld=function(){function p(hd){hd="head";return["<",hd,"></",hd,"><",i,' onl' + 'oad="var d=',g,";d.getElementsByTagName('head')[0].",j,"(d.",h,"('script')).",k,"='",l,"//",a.l,"'",'"',"></",i,">"].join("")}var i="body",m=d[i];if(!m){return setTimeout(ld,100)}a.P(1);var j="appendChild",h="createElement",k="src",n=d[h]("div"),v=n[j](d[h](z)),b=d[h]("iframe"),g="document",e="domain",o;n.style.display="none";m.insertBefore(n,m.firstChild).id=z;b.frameBorder="0";b.id=z+"-loader";if(/MSIE[ ]+6/.test(navigator.userAgent)){b.src="javascript:false"}b.allowTransparency="true";v[j](b);try{b.contentWindow[g].open()}catch(w){c[e]=d[e];o="javascript:var d="+g+".open();d.domain='"+d.domain+"';";b[k]=o+"void(0);"}try{var t=b.contentWindow[g];t.write(p());t.close()}catch(x){b[k]=o+'d.write("'+p().replace(/"/g,String.fromCharCode(92)+'"')+'");d.close();'}a.P(2)};ld()};nt()})({loader: "static.olark.com/jsclient/loader0.js",name:"olark",methods:["configure","extend","declare","identify"]});
4666
+ window.olark.identify(options.siteId);
4667
+
4668
+ // Set up event handlers for chat box open and close so that
4669
+ // we know whether a conversation is active. If it is active,
4670
+ // then we'll send track and pageview information.
4671
+ var self = this;
4672
+ window.olark('api.box.onExpand', function () { self.chatting = true; });
4673
+ window.olark('api.box.onShrink', function () { self.chatting = false; });
4674
+
4675
+ // Olark creates it's method in the snippet, so it's ready immediately.
4676
+ ready();
4677
+ },
4678
+
4679
+ // Update traits about the user in Olark to make the operator's life easier.
4680
+ identify : function (userId, traits) {
4681
+ if (!this.options.identify) return;
4682
+
4683
+ var email = traits.email
4684
+ , name = traits.name || traits.firstName
4685
+ , phone = traits.phone
4686
+ , nickname = name || email || userId;
4687
+
4688
+ // If we have a name and an email, add the email too to be more helpful.
4689
+ if (name && email) nickname += ' ('+email+')';
4690
+
4691
+ // Call all of Olark's settings APIs.
4692
+ window.olark('api.visitor.updateCustomFields', traits);
4693
+ if (email) window.olark('api.visitor.updateEmailAddress', { emailAddress : email });
4694
+ if (name) window.olark('api.visitor.updateFullName', { fullName : name });
4695
+ if (phone) window.olark('api.visitor.updatePhoneNumber', { phoneNumber : phone });
4696
+ if (nickname) window.olark('api.chat.updateVisitorNickname', { snippet : nickname });
4697
+ },
4698
+
4699
+ // Log events the user triggers to the chat console, if you so desire it.
4700
+ track : function (event, properties) {
4701
+ if (!this.options.track || !this.chatting) return;
4702
+
4703
+ // To stay consistent with olark's default messages, it's all lowercase.
4704
+ window.olark('api.chat.sendNotificationToOperator', {
4705
+ body : 'visitor triggered "'+event+'"'
4706
+ });
4707
+ },
4708
+
4709
+ // Mimic the functionality Olark has for normal pageviews with pseudo-
4710
+ // pageviews, telling the operator when a visitor changes pages.
4711
+ pageview : function (url) {
4712
+ if (!this.options.pageview || !this.chatting) return;
4713
+
4714
+ // To stay consistent with olark's default messages, it's all lowercase.
4715
+ window.olark('api.chat.sendNotificationToOperator', {
4716
+ body : 'looking at ' + window.location.href
4717
+ });
4718
+ }
4719
+
4720
+ });
4721
+ });
4722
+ require.register("analytics/src/providers/optimizely.js", function(exports, require, module){
4723
+ // https://www.optimizely.com/docs/api
4724
+
4725
+ var each = require('each')
4726
+ , nextTick = require('next-tick')
4727
+ , Provider = require('../provider');
4728
+
4729
+
4730
+ module.exports = Provider.extend({
4731
+
4732
+ name : 'Optimizely',
4733
+
4734
+ defaults : {
4735
+ // Whether to replay variations into other enabled integrations as traits.
4736
+ variations : true
4737
+ },
4738
+
4739
+ initialize : function (options, ready, analytics) {
4740
+ // Create the `optimizely` object in case it doesn't exist already.
4741
+ // https://www.optimizely.com/docs/api#function-calls
4742
+ window.optimizely = window.optimizely || [];
4743
+
4744
+ // If the `variations` option is true, replay our variations on the next
4745
+ // tick to wait for the entire library to be ready for replays.
4746
+ if (options.variations) {
4747
+ var self = this;
4748
+ nextTick(function () { self.replay(); });
4749
+ }
4750
+
4751
+ // Optimizely should be on the page already, so it's always ready.
4752
+ ready();
4753
+ },
4754
+
4755
+ track : function (event, properties) {
4756
+ // Optimizely takes revenue as cents, not dollars.
4757
+ if (properties && properties.revenue) properties.revenue = properties.revenue * 100;
4758
+
4759
+ window.optimizely.push(['trackEvent', event, properties]);
4760
+ },
4761
+
4762
+ replay : function () {
4763
+ // Make sure we have access to Optimizely's `data` dictionary.
4764
+ var data = window.optimizely.data;
4765
+ if (!data) return;
4766
+
4767
+ // Grab a few pieces of data we'll need for replaying.
4768
+ var experiments = data.experiments
4769
+ , variationNamesMap = data.state.variationNamesMap;
4770
+
4771
+ // Create our traits object to add variations to.
4772
+ var traits = {};
4773
+
4774
+ // Loop through all the experiement the user has been assigned a variation
4775
+ // for and add them to our traits.
4776
+ each(variationNamesMap, function (experimentId, variation) {
4777
+ traits['Experiment: ' + experiments[experimentId].name] = variation;
4778
+ });
4779
+
4780
+ this.analytics.identify(traits);
4781
+ }
4782
+
4783
+ });
4784
+ });
4785
+ require.register("analytics/src/providers/perfect-audience.js", function(exports, require, module){
4786
+ // https://www.perfectaudience.com/docs#javascript_api_autoopen
4787
+
4788
+ var Provider = require('../provider')
4789
+ , load = require('load-script');
4790
+
4791
+
4792
+ module.exports = Provider.extend({
4793
+
4794
+ name : 'Perfect Audience',
4795
+
4796
+ key : 'siteId',
4797
+
4798
+ defaults : {
4799
+ siteId : null
4800
+ },
4801
+
4802
+ initialize : function (options, ready) {
4803
+ window._pa || (window._pa = {});
4804
+ load('//tag.perfectaudience.com/serve/' + options.siteId + '.js', ready);
4805
+ },
4806
+
4807
+ track : function (event, properties) {
4808
+ window._pa.track(event, properties);
4809
+ }
4810
+
4811
+ });
4812
+ });
4813
+ require.register("analytics/src/providers/pingdom.js", function(exports, require, module){
4814
+ var date = require('load-date')
4815
+ , Provider = require('../provider')
4816
+ , load = require('load-script');
4817
+
4818
+
4819
+ module.exports = Provider.extend({
4820
+
4821
+ name : 'Pingdom',
4822
+
4823
+ key : 'id',
4824
+
4825
+ defaults : {
4826
+ id : null
4827
+ },
4828
+
4829
+ initialize : function (options, ready) {
4830
+
4831
+ window._prum = [
4832
+ ['id', options.id],
4833
+ ['mark', 'firstbyte', date.getTime()]
4834
+ ];
4835
+
4836
+ // We've replaced the original snippet loader with our own load method.
4837
+ load('//rum-static.pingdom.net/prum.min.js', ready);
4838
+ }
4839
+
4840
+ });
4841
+ });
4842
+ require.register("analytics/src/providers/preact.js", function(exports, require, module){
4843
+ // http://www.preact.io/api/javascript
4844
+
4845
+ var Provider = require('../provider')
4846
+ , isEmail = require('is-email')
4847
+ , load = require('load-script');
4848
+
4849
+ module.exports = Provider.extend({
4850
+
4851
+ name : 'Preact',
4852
+
4853
+ key : 'projectCode',
4854
+
4855
+ defaults : {
4856
+ projectCode : null
4857
+ },
4858
+
4859
+ initialize : function (options, ready) {
4860
+ var _lnq = window._lnq = window._lnq || [];
4861
+ _lnq.push(["_setCode", options.projectCode]);
4862
+
4863
+ load('//d2bbvl6dq48fa6.cloudfront.net/js/ln-2.4.min.js');
4864
+ ready();
4865
+ },
4866
+
4867
+ identify : function (userId, traits) {
4868
+ // Don't do anything if we just have traits. Preact requires a `userId`.
4869
+ if (!userId) return;
4870
+
4871
+ // Swap the `created` trait to the `created_at` that Preact needs
4872
+ // and convert it from milliseconds to seconds.
4873
+ if (traits.created) {
4874
+ traits.created_at = Math.floor(traits.created/1000);
4875
+ delete traits.created;
4876
+ }
4877
+
4878
+ window._lnq.push(['_setPersonData', {
4879
+ name : traits.name,
4880
+ email : traits.email,
4881
+ uid : userId,
4882
+ properties : traits
4883
+ }]);
4884
+ },
4885
+
4886
+ group : function (groupId, properties) {
4887
+ if (!groupId) return;
4888
+ properties.id = groupId;
4889
+ window._lnq.push(['_setAccount', properties]);
4890
+ },
4891
+
4892
+ track : function (event, properties) {
4893
+ properties || (properties = {});
4894
+
4895
+ // Preact takes a few special properties, and the rest in `extras`. So first
4896
+ // convert and remove the special ones from `properties`.
4897
+ var special = { name : event };
4898
+
4899
+ // They take `revenue` in cents.
4900
+ if (properties.revenue) {
4901
+ special.revenue = properties.revenue * 100;
4902
+ delete properties.revenue;
4903
+ }
4904
+
4905
+ if (properties.note) {
4906
+ special.note = properties.note;
4907
+ delete properties.note;
4908
+ }
4909
+
4910
+ window._lnq.push(['_logEvent', special, properties]);
4911
+ }
4912
+
4913
+ });
4914
+ });
4915
+ require.register("analytics/src/providers/qualaroo.js", function(exports, require, module){
4916
+ // http://help.qualaroo.com/customer/portal/articles/731085-identify-survey-nudge-takers
4917
+ // http://help.qualaroo.com/customer/portal/articles/731091-set-additional-user-properties
4918
+
4919
+ var Provider = require('../provider')
4920
+ , isEmail = require('is-email')
4921
+ , load = require('load-script');
4922
+
4923
+
4924
+ module.exports = Provider.extend({
4925
+
4926
+ name : 'Qualaroo',
4927
+
4928
+ defaults : {
4929
+ // Qualaroo has two required options.
4930
+ customerId : null,
4931
+ siteToken : null,
4932
+ // Whether to record traits when a user triggers an event. This can be
4933
+ // useful for sending targetted questionnaries.
4934
+ track : false
4935
+ },
4936
+
4937
+ // Qualaroo's script has two options in its URL.
4938
+ initialize : function (options, ready) {
4939
+ window._kiq = window._kiq || [];
4940
+ load('//s3.amazonaws.com/ki.js/' + options.customerId + '/' + options.siteToken + '.js');
4941
+
4942
+ // Qualaroo creates a queue, so it's ready immediately.
4943
+ ready();
4944
+ },
4945
+
4946
+ // Qualaroo uses two separate methods: `identify` for storing the `userId`,
4947
+ // and `set` for storing `traits`.
4948
+ identify : function (userId, traits) {
4949
+ var identity = traits.email || userId;
4950
+ if (identity) window._kiq.push(['identify', identity]);
4951
+ if (traits) window._kiq.push(['set', traits]);
4952
+ },
4953
+
4954
+ // Qualaroo doesn't have `track` method yet, but to allow the users to do
4955
+ // targetted questionnaires we can set name-value pairs on the user properties
4956
+ // that apply to the current visit.
4957
+ track : function (event, properties) {
4958
+ if (!this.options.track) return;
4959
+
4960
+ // Create a name-value pair that will be pretty unique. For an event like
4961
+ // 'Loaded a Page' this will make it 'Triggered: Loaded a Page'.
4962
+ var traits = {};
4963
+ traits['Triggered: ' + event] = true;
4964
+
4965
+ // Fire a normal identify, with traits only.
4966
+ this.identify(null, traits);
4967
+ }
4968
+
4969
+ });
4970
+ });
4971
+ require.register("analytics/src/providers/quantcast.js", function(exports, require, module){
4972
+ // https://www.quantcast.com/learning-center/guides/using-the-quantcast-asynchronous-tag/
4973
+
4974
+ var Provider = require('../provider')
4975
+ , load = require('load-script');
4976
+
4977
+
4978
+ module.exports = Provider.extend({
4979
+
4980
+ name : 'Quantcast',
4981
+
4982
+ key : 'pCode',
4983
+
4984
+ defaults : {
4985
+ pCode : null
4986
+ },
4987
+
4988
+ initialize : function (options, ready) {
4989
+ window._qevents = window._qevents || [];
4990
+ window._qevents.push({ qacct: options.pCode });
4991
+ load({
4992
+ http : 'http://edge.quantserve.com/quant.js',
4993
+ https : 'https://secure.quantserve.com/quant.js'
4994
+ }, ready);
4995
+ }
4996
+
4997
+ });
4998
+ });
4999
+ require.register("analytics/src/providers/sentry.js", function(exports, require, module){
5000
+ // http://raven-js.readthedocs.org/en/latest/config/index.html
5001
+
5002
+ var Provider = require('../provider')
5003
+ , load = require('load-script');
5004
+
5005
+
5006
+ module.exports = Provider.extend({
5007
+
5008
+ name : 'Sentry',
5009
+
5010
+ key : 'config',
5011
+
5012
+ defaults : {
5013
+ config : null
5014
+ },
5015
+
5016
+ initialize : function (options, ready) {
5017
+ load('//d3nslu0hdya83q.cloudfront.net/dist/1.0/raven.min.js', function () {
5018
+ // For now, Raven basically requires `install` to be called.
5019
+ // https://github.com/getsentry/raven-js/blob/master/src/raven.js#L87
5020
+ window.Raven.config(options.config).install();
5021
+ ready();
5022
+ });
5023
+ },
5024
+
5025
+ identify : function (userId, traits) {
5026
+ traits.id = userId;
5027
+ window.Raven.setUser(traits);
5028
+ },
5029
+
5030
+ // Raven will automatically use `captureMessage` if the error is a string.
5031
+ log : function (error, properties) {
5032
+ window.Raven.captureException(error, properties);
5033
+ }
5034
+
5035
+ });
5036
+ });
5037
+ require.register("analytics/src/providers/snapengage.js", function(exports, require, module){
5038
+ // http://help.snapengage.com/installation-guide-getting-started-in-a-snap/
5039
+
5040
+ var Provider = require('../provider')
5041
+ , isEmail = require('is-email')
5042
+ , load = require('load-script');
5043
+
5044
+
5045
+ module.exports = Provider.extend({
5046
+
5047
+ name : 'SnapEngage',
5048
+
5049
+ key : 'apiKey',
5050
+
5051
+ defaults : {
5052
+ apiKey : null
5053
+ },
5054
+
5055
+ initialize : function (options, ready) {
5056
+ load('//commondatastorage.googleapis.com/code.snapengage.com/js/' + options.apiKey + '.js', ready);
5057
+ },
5058
+
5059
+ // Set the email in the chat window if we have it.
5060
+ identify : function (userId, traits, options) {
5061
+ if (!traits.email) return;
5062
+ window.SnapABug.setUserEmail(traits.email);
5063
+ }
5064
+
5065
+ });
5066
+ });
5067
+ require.register("analytics/src/providers/usercycle.js", function(exports, require, module){
5068
+ // http://docs.usercycle.com/javascript_api
5069
+
5070
+ var Provider = require('../provider')
5071
+ , load = require('load-script')
5072
+ , user = require('../user');
5073
+
5074
+
5075
+ module.exports = Provider.extend({
5076
+
5077
+ name : 'USERcycle',
5078
+
5079
+ key : 'key',
5080
+
5081
+ defaults : {
5082
+ key : null
5083
+ },
5084
+
5085
+ initialize : function (options, ready) {
5086
+ window._uc = window._uc || [];
5087
+ window._uc.push(['_key', options.key]);
5088
+ load('//api.usercycle.com/javascripts/track.js');
5089
+
5090
+ // USERcycle makes a queue, so it's ready immediately.
5091
+ ready();
5092
+ },
5093
+
5094
+ identify : function (userId, traits) {
5095
+ if (userId) window._uc.push(['uid', userId]);
5096
+
5097
+ // USERcycle has a special "hidden" event that is used just for retention measurement.
5098
+ // Lukas suggested on 6/4/2013 that we send traits on that event, since they use the
5099
+ // the latest value of every event property as a "trait"
5100
+ window._uc.push(['action', 'came_back', traits]);
5101
+ },
5102
+
5103
+ track : function (event, properties) {
5104
+ window._uc.push(['action', event, properties]);
5105
+ }
5106
+
5107
+ });
5108
+ });
5109
+ require.register("analytics/src/providers/userfox.js", function(exports, require, module){
5110
+ // https://www.userfox.com/docs/
5111
+
5112
+ var Provider = require('../provider')
5113
+ , extend = require('extend')
5114
+ , load = require('load-script')
5115
+ , isEmail = require('is-email');
5116
+
5117
+
5118
+ module.exports = Provider.extend({
5119
+
5120
+ name : 'userfox',
5121
+
5122
+ key : 'clientId',
5123
+
5124
+ defaults : {
5125
+ // userfox's required key.
5126
+ clientId : null
5127
+ },
5128
+
5129
+ initialize : function (options, ready) {
5130
+ window._ufq = window._ufq || [];
5131
+ load('//d2y71mjhnajxcg.cloudfront.net/js/userfox-stable.js');
5132
+
5133
+ // userfox creates its own queue, so we're ready right away.
5134
+ ready();
5135
+ },
5136
+
5137
+ identify : function (userId, traits) {
5138
+ if (!traits.email) return;
5139
+
5140
+ // Initialize the library with the email now that we have it.
5141
+ window._ufq.push(['init', {
5142
+ clientId : this.options.clientId,
5143
+ email : traits.email
5144
+ }]);
5145
+
5146
+ // Record traits to "track" if we have the required signup date `created`.
5147
+ // userfox takes `signup_date` as a string of seconds since the epoch.
5148
+ if (traits.created) {
5149
+ traits.signup_date = (traits.created.getTime() / 1000).toString();
5150
+ delete traits.created;
5151
+ window._ufq.push(['track', traits]);
5152
+ }
5153
+ }
5154
+
5155
+ });
5156
+
5157
+ });
5158
+ require.register("analytics/src/providers/uservoice.js", function(exports, require, module){
5159
+ // http://feedback.uservoice.com/knowledgebase/articles/225-how-do-i-pass-custom-data-through-the-widget-and-i
5160
+
5161
+ var Provider = require('../provider')
5162
+ , load = require('load-script')
5163
+ , alias = require('alias')
5164
+ , clone = require('clone');
5165
+
5166
+
5167
+ module.exports = Provider.extend({
5168
+
5169
+ name : 'UserVoice',
5170
+
5171
+ defaults : {
5172
+ // These first two options are required.
5173
+ widgetId : null,
5174
+ forumId : null,
5175
+ // Should we show the tab automatically?
5176
+ showTab : true,
5177
+ // There's tons of options for the tab.
5178
+ mode : 'full',
5179
+ primaryColor : '#cc6d00',
5180
+ linkColor : '#007dbf',
5181
+ defaultMode : 'support',
5182
+ tabLabel : 'Feedback & Support',
5183
+ tabColor : '#cc6d00',
5184
+ tabPosition : 'middle-right',
5185
+ tabInverted : false
5186
+ },
5187
+
5188
+ initialize : function (options, ready) {
5189
+ window.UserVoice = window.UserVoice || [];
5190
+ load('//widget.uservoice.com/' + options.widgetId + '.js', ready);
5191
+
5192
+ var optionsClone = clone(options);
5193
+ alias(optionsClone, {
5194
+ 'forumId' : 'forum_id',
5195
+ 'primaryColor' : 'primary_color',
5196
+ 'linkColor' : 'link_color',
5197
+ 'defaultMode' : 'default_mode',
5198
+ 'tabLabel' : 'tab_label',
5199
+ 'tabColor' : 'tab_color',
5200
+ 'tabPosition' : 'tab_position',
5201
+ 'tabInverted' : 'tab_inverted'
5202
+ });
5203
+
5204
+ // If we don't automatically show the tab, let them show it via
5205
+ // javascript. This is the default name for the function in their snippet.
5206
+ window.showClassicWidget = function (showWhat) {
5207
+ window.UserVoice.push([showWhat || 'showLightbox', 'classic_widget', optionsClone]);
5208
+ };
5209
+
5210
+ // If we *do* automatically show the tab, get on with it!
5211
+ if (options.showTab) {
5212
+ window.showClassicWidget('showTab');
5213
+ }
5214
+ },
5215
+
5216
+ identify : function (userId, traits) {
5217
+ // Pull the ID into traits.
5218
+ traits.id = userId;
5219
+ window.UserVoice.push(['setCustomFields', traits]);
5220
+ }
5221
+
5222
+ });
5223
+ });
5224
+ require.register("analytics/src/providers/vero.js", function(exports, require, module){
5225
+ // https://github.com/getvero/vero-api/blob/master/sections/js.md
5226
+
5227
+ var Provider = require('../provider')
5228
+ , isEmail = require('is-email')
5229
+ , load = require('load-script');
5230
+
5231
+
5232
+ module.exports = Provider.extend({
5233
+
5234
+ name : 'Vero',
5235
+
5236
+ key : 'apiKey',
5237
+
5238
+ defaults : {
5239
+ apiKey : null
5240
+ },
5241
+
5242
+ initialize : function (options, ready) {
5243
+ window._veroq = window._veroq || [];
5244
+ window._veroq.push(['init', { api_key: options.apiKey }]);
5245
+ load('//d3qxef4rp70elm.cloudfront.net/m.js');
5246
+
5247
+ // Vero creates a queue, so it's ready immediately.
5248
+ ready();
5249
+ },
5250
+
5251
+ identify : function (userId, traits) {
5252
+ // Don't do anything if we just have traits, because Vero
5253
+ // requires a `userId`.
5254
+ if (!userId || !traits.email) return;
5255
+
5256
+ // Vero takes the `userId` as part of the traits object.
5257
+ traits.id = userId;
5258
+
5259
+ window._veroq.push(['user', traits]);
5260
+ },
5261
+
5262
+ track : function (event, properties) {
5263
+ window._veroq.push(['track', event, properties]);
5264
+ }
5265
+
5266
+ });
5267
+ });
5268
+ require.register("analytics/src/providers/visual-website-optimizer.js", function(exports, require, module){
5269
+ // http://v2.visualwebsiteoptimizer.com/tools/get_tracking_code.php
5270
+ // http://visualwebsiteoptimizer.com/knowledge/integration-of-vwo-with-kissmetrics/
5271
+
5272
+ var each = require('each')
5273
+ , inherit = require('inherit')
5274
+ , nextTick = require('next-tick')
5275
+ , Provider = require('../provider');
5276
+
5277
+
5278
+ /**
5279
+ * Expose `VWO`.
5280
+ */
5281
+
5282
+ module.exports = VWO;
5283
+
5284
+
5285
+ /**
5286
+ * `VWO` inherits from the generic `Provider`.
5287
+ */
5288
+
5289
+ function VWO () {
5290
+ Provider.apply(this, arguments);
5291
+ }
5292
+
5293
+ inherit(VWO, Provider);
5294
+
5295
+
5296
+ /**
5297
+ * Name.
5298
+ */
5299
+
5300
+ VWO.prototype.name = 'Visual Website Optimizer';
5301
+
5302
+
5303
+ /**
5304
+ * Default options.
5305
+ */
5306
+
5307
+ VWO.prototype.defaults = {
5308
+ // Whether to replay variations into other integrations as traits.
5309
+ replay : true
5310
+ };
5311
+
5312
+
5313
+ /**
5314
+ * Initialize.
5315
+ */
5316
+
5317
+ VWO.prototype.initialize = function (options, ready) {
5318
+ if (options.replay) this.replay();
5319
+ ready();
5320
+ };
5321
+
5322
+
5323
+ /**
5324
+ * Replay the experiments the user has seen as traits to all other integrations.
5325
+ * Wait for the next tick to replay so that the `analytics` object and all of
5326
+ * the integrations are fully initialized.
5327
+ */
5328
+
5329
+ VWO.prototype.replay = function () {
5330
+ var analytics = this.analytics;
5331
+ nextTick(function () {
5332
+ experiments(function (err, traits) {
5333
+ if (traits) analytics.identify(traits);
5334
+ });
5335
+ });
5336
+ };
5337
+
5338
+
5339
+ /**
5340
+ * Get dictionary of experiment keys and variations.
5341
+ * http://visualwebsiteoptimizer.com/knowledge/integration-of-vwo-with-kissmetrics/
5342
+ *
5343
+ * @param {Function} callback Called with `err, experiments`.
5344
+ * @return {Object} Dictionary of experiments and variations.
5345
+ */
5346
+
5347
+ function experiments (callback) {
5348
+ enqueue(function () {
5349
+ var data = {};
5350
+ var ids = window._vwo_exp_ids;
5351
+ if (!ids) return callback();
5352
+ each(ids, function (id) {
5353
+ var name = variation(id);
5354
+ if (name) data['Experiment: ' + id] = name;
5355
+ });
5356
+ callback(null, data);
5357
+ });
5358
+ }
5359
+
5360
+
5361
+ /**
5362
+ * Add a function to the VWO queue, creating one if it doesn't exist.
5363
+ *
5364
+ * @param {Function} fn Function to enqueue.
5365
+ */
5366
+
5367
+ function enqueue (fn) {
5368
+ window._vis_opt_queue || (window._vis_opt_queue = []);
5369
+ window._vis_opt_queue.push(fn);
5370
+ }
5371
+
5372
+
5373
+ /**
5374
+ * Get the chosen variation's name from an experiment `id`.
5375
+ * http://visualwebsiteoptimizer.com/knowledge/integration-of-vwo-with-kissmetrics/
5376
+ *
5377
+ * @param {String} id ID of the experiment to read.
5378
+ * @return {String} Variation name.
5379
+ */
5380
+
5381
+ function variation (id) {
5382
+ var experiments = window._vwo_exp;
5383
+ if (!experiments) return null;
5384
+ var experiment = experiments[id];
5385
+ var variationId = experiment.combination_chosen;
5386
+ return variationId ? experiment.comb_n[variationId] : null;
5387
+ }
5388
+ });
5389
+ require.register("analytics/src/providers/woopra.js", function(exports, require, module){
5390
+ // http://www.woopra.com/docs/setup/javascript-tracking/
5391
+
5392
+ var Provider = require('../provider')
5393
+ , each = require('each')
5394
+ , extend = require('extend')
5395
+ , isEmail = require('is-email')
5396
+ , load = require('load-script')
5397
+ , type = require('type')
5398
+ , user = require('../user');
5399
+
5400
+
5401
+ module.exports = Provider.extend({
5402
+
5403
+ name : 'Woopra',
5404
+
5405
+ key : 'domain',
5406
+
5407
+ defaults : {
5408
+ domain : null
5409
+ },
5410
+
5411
+ initialize : function (options, ready) {
5412
+ // Woopra gives us a nice ready callback.
5413
+ var self = this;
5414
+
5415
+ window.woopraReady = function (tracker) {
5416
+ tracker.setDomain(self.options.domain);
5417
+ tracker.setIdleTimeout(300000);
5418
+
5419
+ var userId = user.id()
5420
+ , traits = user.traits();
5421
+
5422
+ addTraits(userId, traits, tracker);
5423
+ tracker.track();
5424
+
5425
+ ready();
5426
+ return false;
5427
+ };
5428
+
5429
+ load('//static.woopra.com/js/woopra.js');
5430
+ },
5431
+
5432
+ identify : function (userId, traits) {
5433
+ // We aren't guaranteed a tracker.
5434
+ if (!window.woopraTracker) return;
5435
+ addTraits(userId, traits, window.woopraTracker);
5436
+ },
5437
+
5438
+ track : function (event, properties) {
5439
+ // We aren't guaranteed a tracker.
5440
+ if (!window.woopraTracker) return;
5441
+
5442
+ // Woopra takes its `event` as the `name` key.
5443
+ properties || (properties = {});
5444
+ properties.name = event;
5445
+
5446
+ window.woopraTracker.pushEvent(properties);
5447
+ }
5448
+
5449
+ });
5450
+
5451
+
5452
+ /**
5453
+ * Convenience function for updating the userId and traits.
5454
+ *
5455
+ * @param {String} userId The user's ID.
5456
+ * @param {Object} traits The user's traits.
5457
+ * @param {Tracker} tracker The Woopra tracker object.
5458
+ */
5459
+
5460
+ function addTraits (userId, traits, tracker) {
5461
+ // Move a `userId` into `traits`.
5462
+ if (userId) traits.id = userId;
5463
+ each(traits, function (key, value) {
5464
+ // Woopra seems to only support strings as trait values.
5465
+ if ('string' === type(value)) tracker.addVisitorProperty(key, value);
5466
+ });
5467
+ }
5468
+ });
5469
+ require.alias("avetisk-defaults/index.js", "analytics/deps/defaults/index.js");
5470
+ require.alias("avetisk-defaults/index.js", "defaults/index.js");
5471
+
5472
+ require.alias("component-clone/index.js", "analytics/deps/clone/index.js");
5473
+ require.alias("component-clone/index.js", "clone/index.js");
5474
+ require.alias("component-type/index.js", "component-clone/deps/type/index.js");
5475
+
5476
+ require.alias("component-cookie/index.js", "analytics/deps/cookie/index.js");
5477
+ require.alias("component-cookie/index.js", "cookie/index.js");
5478
+
5479
+ require.alias("component-each/index.js", "analytics/deps/each/index.js");
5480
+ require.alias("component-each/index.js", "each/index.js");
5481
+ require.alias("component-type/index.js", "component-each/deps/type/index.js");
5482
+
5483
+ require.alias("component-event/index.js", "analytics/deps/event/index.js");
5484
+ require.alias("component-event/index.js", "event/index.js");
5485
+
5486
+ require.alias("component-inherit/index.js", "analytics/deps/inherit/index.js");
5487
+ require.alias("component-inherit/index.js", "inherit/index.js");
5488
+
5489
+ require.alias("component-object/index.js", "analytics/deps/object/index.js");
5490
+ require.alias("component-object/index.js", "object/index.js");
5491
+
5492
+ require.alias("component-querystring/index.js", "analytics/deps/querystring/index.js");
5493
+ require.alias("component-querystring/index.js", "querystring/index.js");
5494
+ require.alias("component-trim/index.js", "component-querystring/deps/trim/index.js");
5495
+
5496
+ require.alias("component-type/index.js", "analytics/deps/type/index.js");
5497
+ require.alias("component-type/index.js", "type/index.js");
5498
+
5499
+ require.alias("component-url/index.js", "analytics/deps/url/index.js");
5500
+ require.alias("component-url/index.js", "url/index.js");
5501
+
5502
+ require.alias("segmentio-after/index.js", "analytics/deps/after/index.js");
5503
+ require.alias("segmentio-after/index.js", "after/index.js");
5504
+
5505
+ require.alias("segmentio-alias/index.js", "analytics/deps/alias/index.js");
5506
+ require.alias("segmentio-alias/index.js", "alias/index.js");
5507
+
5508
+ require.alias("segmentio-bind-all/index.js", "analytics/deps/bind-all/index.js");
5509
+ require.alias("segmentio-bind-all/index.js", "analytics/deps/bind-all/index.js");
5510
+ require.alias("segmentio-bind-all/index.js", "bind-all/index.js");
5511
+ require.alias("component-bind/index.js", "segmentio-bind-all/deps/bind/index.js");
5512
+
5513
+ require.alias("component-type/index.js", "segmentio-bind-all/deps/type/index.js");
5514
+
5515
+ require.alias("segmentio-bind-all/index.js", "segmentio-bind-all/index.js");
5516
+
5517
+ require.alias("segmentio-canonical/index.js", "analytics/deps/canonical/index.js");
5518
+ require.alias("segmentio-canonical/index.js", "canonical/index.js");
5519
+
5520
+ require.alias("segmentio-extend/index.js", "analytics/deps/extend/index.js");
5521
+ require.alias("segmentio-extend/index.js", "extend/index.js");
5522
+
5523
+ require.alias("segmentio-is-email/index.js", "analytics/deps/is-email/index.js");
5524
+ require.alias("segmentio-is-email/index.js", "is-email/index.js");
5525
+
5526
+ require.alias("segmentio-is-meta/index.js", "analytics/deps/is-meta/index.js");
5527
+ require.alias("segmentio-is-meta/index.js", "is-meta/index.js");
5528
+
5529
+ require.alias("segmentio-json/index.js", "analytics/deps/json/index.js");
5530
+ require.alias("segmentio-json/index.js", "json/index.js");
5531
+ require.alias("component-json-fallback/index.js", "segmentio-json/deps/json-fallback/index.js");
5532
+
5533
+ require.alias("segmentio-load-date/index.js", "analytics/deps/load-date/index.js");
5534
+ require.alias("segmentio-load-date/index.js", "load-date/index.js");
5535
+
5536
+ require.alias("segmentio-load-script/index.js", "analytics/deps/load-script/index.js");
5537
+ require.alias("segmentio-load-script/index.js", "load-script/index.js");
5538
+ require.alias("component-type/index.js", "segmentio-load-script/deps/type/index.js");
5539
+
5540
+ require.alias("segmentio-new-date/index.js", "analytics/deps/new-date/index.js");
5541
+ require.alias("segmentio-new-date/index.js", "new-date/index.js");
5542
+ require.alias("segmentio-type/index.js", "segmentio-new-date/deps/type/index.js");
5543
+
5544
+ require.alias("segmentio-on-body/index.js", "analytics/deps/on-body/index.js");
5545
+ require.alias("segmentio-on-body/index.js", "on-body/index.js");
5546
+ require.alias("component-each/index.js", "segmentio-on-body/deps/each/index.js");
5547
+ require.alias("component-type/index.js", "component-each/deps/type/index.js");
5548
+
5549
+ require.alias("segmentio-store.js/store.js", "analytics/deps/store/store.js");
5550
+ require.alias("segmentio-store.js/store.js", "analytics/deps/store/index.js");
5551
+ require.alias("segmentio-store.js/store.js", "store/index.js");
5552
+ require.alias("segmentio-json/index.js", "segmentio-store.js/deps/json/index.js");
5553
+ require.alias("component-json-fallback/index.js", "segmentio-json/deps/json-fallback/index.js");
5554
+
5555
+ require.alias("segmentio-store.js/store.js", "segmentio-store.js/index.js");
5556
+
5557
+ require.alias("segmentio-top-domain/index.js", "analytics/deps/top-domain/index.js");
5558
+ require.alias("segmentio-top-domain/index.js", "analytics/deps/top-domain/index.js");
5559
+ require.alias("segmentio-top-domain/index.js", "top-domain/index.js");
5560
+ require.alias("component-url/index.js", "segmentio-top-domain/deps/url/index.js");
5561
+
5562
+ require.alias("segmentio-top-domain/index.js", "segmentio-top-domain/index.js");
5563
+
5564
+ require.alias("timoxley-next-tick/index.js", "analytics/deps/next-tick/index.js");
5565
+ require.alias("timoxley-next-tick/index.js", "next-tick/index.js");
5566
+
5567
+ require.alias("yields-prevent/index.js", "analytics/deps/prevent/index.js");
5568
+ require.alias("yields-prevent/index.js", "prevent/index.js");
5569
+
5570
+ require.alias("analytics/src/index.js", "analytics/index.js");
5571
+
5572
+ if (typeof exports == "object") {
5573
+ module.exports = require("analytics");
5574
+ } else if (typeof define == "function" && define.amd) {
5575
+ define(function(){ return require("analytics"); });
5576
+ } else {
5577
+ this["analytics"] = require("analytics");
5578
+ }})();