analytics-js-rails 0.11.0.rc1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }})();