ahoy_matey 1.6.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +7 -0
  3. data/CHANGELOG.md +22 -0
  4. data/CONTRIBUTING.md +40 -0
  5. data/LICENSE.txt +1 -1
  6. data/README.md +210 -489
  7. data/Rakefile +1 -0
  8. data/ahoy_matey.gemspec +6 -8
  9. data/app/controllers/ahoy/base_controller.rb +2 -6
  10. data/app/controllers/ahoy/events_controller.rb +7 -1
  11. data/app/controllers/ahoy/visits_controller.rb +7 -1
  12. data/app/jobs/ahoy/geocode_job.rb +10 -0
  13. data/app/jobs/ahoy/geocode_v2_job.rb +29 -0
  14. data/config/routes.rb +1 -1
  15. data/docs/Ahoy-2-Upgrade.md +147 -0
  16. data/docs/Data-Store-Examples.md +240 -0
  17. data/lib/ahoy.rb +30 -88
  18. data/lib/ahoy/base_store.rb +72 -0
  19. data/lib/ahoy/controller.rb +4 -10
  20. data/lib/ahoy/database_store.rb +72 -0
  21. data/lib/ahoy/engine.rb +5 -7
  22. data/lib/ahoy/model.rb +4 -26
  23. data/lib/ahoy/{properties.rb → query_methods.rb} +18 -4
  24. data/lib/ahoy/tracker.rb +60 -38
  25. data/lib/ahoy/version.rb +1 -1
  26. data/lib/ahoy/visit_properties.rb +65 -39
  27. data/lib/generators/ahoy/activerecord_generator.rb +58 -0
  28. data/lib/generators/ahoy/base_generator.rb +13 -0
  29. data/lib/generators/ahoy/install_generator.rb +44 -0
  30. data/lib/generators/ahoy/mongoid_generator.rb +20 -0
  31. data/lib/generators/ahoy/templates/active_record_event_model.rb +10 -0
  32. data/lib/generators/ahoy/{stores/templates/active_record_visits_migration.rb → templates/active_record_migration.rb} +19 -21
  33. data/lib/generators/ahoy/templates/active_record_visit_model.rb +6 -0
  34. data/lib/generators/ahoy/templates/base_store_initializer.rb +17 -0
  35. data/lib/generators/ahoy/templates/database_store_initializer.rb +5 -0
  36. data/lib/generators/ahoy/{stores/templates → templates}/mongoid_event_model.rb +4 -2
  37. data/lib/generators/ahoy/{stores/templates → templates}/mongoid_visit_model.rb +7 -3
  38. data/test/query_methods/mongoid_test.rb +23 -0
  39. data/test/{properties → query_methods}/mysql_json_test.rb +1 -1
  40. data/test/{properties → query_methods}/mysql_text_test.rb +1 -1
  41. data/test/{properties → query_methods}/postgresql_hstore_test.rb +1 -1
  42. data/test/{properties → query_methods}/postgresql_json_test.rb +1 -1
  43. data/test/{properties → query_methods}/postgresql_jsonb_test.rb +1 -1
  44. data/test/{properties → query_methods}/postgresql_text_test.rb +1 -1
  45. data/test/test_helper.rb +4 -3
  46. data/vendor/assets/javascripts/ahoy.js +551 -325
  47. metadata +67 -112
  48. data/lib/ahoy/deckhands/location_deckhand.rb +0 -49
  49. data/lib/ahoy/deckhands/request_deckhand.rb +0 -52
  50. data/lib/ahoy/deckhands/technology_deckhand.rb +0 -47
  51. data/lib/ahoy/deckhands/traffic_source_deckhand.rb +0 -22
  52. data/lib/ahoy/deckhands/utm_parameter_deckhand.rb +0 -23
  53. data/lib/ahoy/geocode_job.rb +0 -13
  54. data/lib/ahoy/logger_silencer.rb +0 -75
  55. data/lib/ahoy/stores/active_record_store.rb +0 -61
  56. data/lib/ahoy/stores/active_record_token_store.rb +0 -114
  57. data/lib/ahoy/stores/base_store.rb +0 -88
  58. data/lib/ahoy/stores/bunny_store.rb +0 -33
  59. data/lib/ahoy/stores/fluentd_store.rb +0 -17
  60. data/lib/ahoy/stores/kafka_store.rb +0 -42
  61. data/lib/ahoy/stores/kinesis_firehose_store.rb +0 -42
  62. data/lib/ahoy/stores/log_store.rb +0 -53
  63. data/lib/ahoy/stores/mongoid_store.rb +0 -63
  64. data/lib/ahoy/stores/nats_store.rb +0 -34
  65. data/lib/ahoy/stores/nsq_store.rb +0 -36
  66. data/lib/ahoy/subscribers/active_record.rb +0 -19
  67. data/lib/ahoy/throttle.rb +0 -17
  68. data/lib/generators/ahoy/stores/active_record_events_generator.rb +0 -59
  69. data/lib/generators/ahoy/stores/active_record_generator.rb +0 -16
  70. data/lib/generators/ahoy/stores/active_record_visits_generator.rb +0 -49
  71. data/lib/generators/ahoy/stores/bunny_generator.rb +0 -15
  72. data/lib/generators/ahoy/stores/custom_generator.rb +0 -15
  73. data/lib/generators/ahoy/stores/fluentd_generator.rb +0 -15
  74. data/lib/generators/ahoy/stores/kafka_generator.rb +0 -15
  75. data/lib/generators/ahoy/stores/kinesis_firehose_generator.rb +0 -15
  76. data/lib/generators/ahoy/stores/log_generator.rb +0 -15
  77. data/lib/generators/ahoy/stores/mongoid_events_generator.rb +0 -19
  78. data/lib/generators/ahoy/stores/mongoid_generator.rb +0 -14
  79. data/lib/generators/ahoy/stores/mongoid_visits_generator.rb +0 -27
  80. data/lib/generators/ahoy/stores/nats_generator.rb +0 -15
  81. data/lib/generators/ahoy/stores/nsq_generator.rb +0 -15
  82. data/lib/generators/ahoy/stores/templates/active_record_event_model.rb +0 -12
  83. data/lib/generators/ahoy/stores/templates/active_record_events_migration.rb +0 -20
  84. data/lib/generators/ahoy/stores/templates/active_record_initializer.rb +0 -3
  85. data/lib/generators/ahoy/stores/templates/active_record_visit_model.rb +0 -4
  86. data/lib/generators/ahoy/stores/templates/bunny_initializer.rb +0 -9
  87. data/lib/generators/ahoy/stores/templates/custom_initializer.rb +0 -10
  88. data/lib/generators/ahoy/stores/templates/fluentd_initializer.rb +0 -3
  89. data/lib/generators/ahoy/stores/templates/kafka_initializer.rb +0 -9
  90. data/lib/generators/ahoy/stores/templates/kinesis_firehose_initializer.rb +0 -17
  91. data/lib/generators/ahoy/stores/templates/log_initializer.rb +0 -3
  92. data/lib/generators/ahoy/stores/templates/mongoid_initializer.rb +0 -3
  93. data/lib/generators/ahoy/stores/templates/nats_initializer.rb +0 -9
  94. data/lib/generators/ahoy/stores/templates/nsq_initializer.rb +0 -9
  95. data/test/visit_properties_test.rb +0 -44
@@ -10,7 +10,7 @@ class MysqlJsonEvent < MysqlBase
10
10
  end
11
11
 
12
12
  class MysqlJsonTest < Minitest::Test
13
- include PropertiesTest
13
+ include QueryMethodsTest
14
14
 
15
15
  def model
16
16
  MysqlJsonEvent
@@ -11,7 +11,7 @@ class MysqlTextEvent < MysqlBase
11
11
  end
12
12
 
13
13
  class MysqlTextTest < Minitest::Test
14
- include PropertiesTest
14
+ include QueryMethodsTest
15
15
 
16
16
  def model
17
17
  MysqlTextEvent
@@ -12,7 +12,7 @@ class PostgresqlHstoreEvent < PostgresqlBase
12
12
  end
13
13
 
14
14
  class PostgresqlHstoreTest < Minitest::Test
15
- include PropertiesTest
15
+ include QueryMethodsTest
16
16
 
17
17
  def model
18
18
  PostgresqlHstoreEvent
@@ -10,7 +10,7 @@ class PostgresqlJsonEvent < PostgresqlBase
10
10
  end
11
11
 
12
12
  class PostgresqlJsonTest < Minitest::Test
13
- include PropertiesTest
13
+ include QueryMethodsTest
14
14
 
15
15
  def model
16
16
  PostgresqlJsonEvent
@@ -11,7 +11,7 @@ class PostgresqlJsonbEvent < PostgresqlBase
11
11
  end
12
12
 
13
13
  class PostgresqlJsonbTest < Minitest::Test
14
- include PropertiesTest
14
+ include QueryMethodsTest
15
15
 
16
16
  def model
17
17
  PostgresqlJsonbEvent
@@ -11,7 +11,7 @@ class PostgresqlTextEvent < PostgresqlBase
11
11
  end
12
12
 
13
13
  class PostgresqlTextTest < Minitest::Test
14
- include PropertiesTest
14
+ include QueryMethodsTest
15
15
 
16
16
  def model
17
17
  PostgresqlTextEvent
data/test/test_helper.rb CHANGED
@@ -3,22 +3,23 @@ Bundler.require(:default)
3
3
  require "minitest/autorun"
4
4
  require "minitest/pride"
5
5
  require "active_record"
6
+ require "mongoid"
6
7
 
7
8
  ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT) if ENV["VERBOSE"]
8
9
 
9
10
  class PostgresqlBase < ActiveRecord::Base
10
- include Ahoy::Properties
11
+ include Ahoy::QueryMethods
11
12
  establish_connection adapter: "postgresql", database: "ahoy_test"
12
13
  self.abstract_class = true
13
14
  end
14
15
 
15
16
  class MysqlBase < ActiveRecord::Base
16
- include Ahoy::Properties
17
+ include Ahoy::QueryMethods
17
18
  establish_connection adapter: "mysql2", username: "root", database: "ahoy_test"
18
19
  self.abstract_class = true
19
20
  end
20
21
 
21
- module PropertiesTest
22
+ module QueryMethodsTest
22
23
  def setup
23
24
  model.delete_all
24
25
  end
@@ -1,153 +1,266 @@
1
- /*
2
- * Ahoy.js
3
- * Simple, powerful JavaScript analytics
4
- * https://github.com/ankane/ahoy.js
5
- * v0.2.1
6
- * MIT License
7
- */
8
-
9
- /*jslint browser: true, indent: 2, plusplus: true, vars: true */
10
-
11
- (function (window) {
12
- "use strict";
13
-
14
- var config = {
15
- urlPrefix: "",
16
- visitsUrl: "/ahoy/visits",
17
- eventsUrl: "/ahoy/events",
18
- cookieDomain: null,
19
- page: null,
20
- platform: "Web",
21
- useBeacon: false,
22
- startOnReady: true
23
- };
24
-
25
- var ahoy = window.ahoy || window.Ahoy || {};
26
-
27
- ahoy.configure = function (options) {
28
- for (var key in options) {
29
- if (options.hasOwnProperty(key)) {
30
- config[key] = options[key];
31
- }
1
+ (function webpackUniversalModuleDefinition(root, factory) {
2
+ if(typeof exports === 'object' && typeof module === 'object')
3
+ module.exports = factory();
4
+ else if(typeof define === 'function' && define.amd)
5
+ define([], factory);
6
+ else if(typeof exports === 'object')
7
+ exports["ahoy"] = factory();
8
+ else
9
+ root["ahoy"] = factory();
10
+ })(typeof self !== 'undefined' ? self : this, function() {
11
+ return /******/ (function(modules) { // webpackBootstrap
12
+ /******/ // The module cache
13
+ /******/ var installedModules = {};
14
+ /******/
15
+ /******/ // The require function
16
+ /******/ function __webpack_require__(moduleId) {
17
+ /******/
18
+ /******/ // Check if module is in cache
19
+ /******/ if(installedModules[moduleId]) {
20
+ /******/ return installedModules[moduleId].exports;
21
+ /******/ }
22
+ /******/ // Create a new module (and put it into the cache)
23
+ /******/ var module = installedModules[moduleId] = {
24
+ /******/ i: moduleId,
25
+ /******/ l: false,
26
+ /******/ exports: {}
27
+ /******/ };
28
+ /******/
29
+ /******/ // Execute the module function
30
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31
+ /******/
32
+ /******/ // Flag the module as loaded
33
+ /******/ module.l = true;
34
+ /******/
35
+ /******/ // Return the exports of the module
36
+ /******/ return module.exports;
37
+ /******/ }
38
+ /******/
39
+ /******/
40
+ /******/ // expose the modules object (__webpack_modules__)
41
+ /******/ __webpack_require__.m = modules;
42
+ /******/
43
+ /******/ // expose the module cache
44
+ /******/ __webpack_require__.c = installedModules;
45
+ /******/
46
+ /******/ // define getter function for harmony exports
47
+ /******/ __webpack_require__.d = function(exports, name, getter) {
48
+ /******/ if(!__webpack_require__.o(exports, name)) {
49
+ /******/ Object.defineProperty(exports, name, {
50
+ /******/ configurable: false,
51
+ /******/ enumerable: true,
52
+ /******/ get: getter
53
+ /******/ });
54
+ /******/ }
55
+ /******/ };
56
+ /******/
57
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
58
+ /******/ __webpack_require__.n = function(module) {
59
+ /******/ var getter = module && module.__esModule ?
60
+ /******/ function getDefault() { return module['default']; } :
61
+ /******/ function getModuleExports() { return module; };
62
+ /******/ __webpack_require__.d(getter, 'a', getter);
63
+ /******/ return getter;
64
+ /******/ };
65
+ /******/
66
+ /******/ // Object.prototype.hasOwnProperty.call
67
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
68
+ /******/
69
+ /******/ // __webpack_public_path__
70
+ /******/ __webpack_require__.p = "";
71
+ /******/
72
+ /******/ // Load entry module and return exports
73
+ /******/ return __webpack_require__(__webpack_require__.s = 0);
74
+ /******/ })
75
+ /************************************************************************/
76
+ /******/ ([
77
+ /* 0 */
78
+ /***/ (function(module, exports, __webpack_require__) {
79
+
80
+ "use strict";
81
+
82
+
83
+ Object.defineProperty(exports, "__esModule", {
84
+ value: true
85
+ });
86
+
87
+ var _objectToFormdata = __webpack_require__(1);
88
+
89
+ var _objectToFormdata2 = _interopRequireDefault(_objectToFormdata);
90
+
91
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
92
+
93
+ var config = {
94
+ urlPrefix: "",
95
+ visitsUrl: "/ahoy/visits",
96
+ eventsUrl: "/ahoy/events",
97
+ cookieDomain: null,
98
+ page: null,
99
+ platform: "Web",
100
+ useBeacon: true,
101
+ startOnReady: true
102
+ }; /*
103
+ * Ahoy.js
104
+ * Simple, powerful JavaScript analytics
105
+ * https://github.com/ankane/ahoy.js
106
+ * v0.3.0
107
+ * MIT License
108
+ */
109
+
110
+ var ahoy = window.ahoy || window.Ahoy || {};
111
+
112
+ ahoy.configure = function (options) {
113
+ for (var key in options) {
114
+ if (options.hasOwnProperty(key)) {
115
+ config[key] = options[key];
32
116
  }
33
- };
34
-
35
- // legacy
36
- ahoy.configure(ahoy);
37
-
38
- var $ = window.jQuery || window.Zepto || window.$;
39
- var visitId, visitorId, track;
40
- var visitTtl = 4 * 60; // 4 hours
41
- var visitorTtl = 2 * 365 * 24 * 60; // 2 years
42
- var isReady = false;
43
- var queue = [];
44
- var canStringify = typeof(JSON) !== "undefined" && typeof(JSON.stringify) !== "undefined";
45
- var eventQueue = [];
46
-
47
- function visitsUrl() {
48
- return config.urlPrefix + config.visitsUrl;
49
117
  }
50
-
51
- function eventsUrl() {
52
- return config.urlPrefix + config.eventsUrl;
118
+ };
119
+
120
+ // legacy
121
+ ahoy.configure(ahoy);
122
+
123
+ var $ = window.jQuery || window.Zepto || window.$;
124
+ var visitId = void 0,
125
+ visitorId = void 0,
126
+ track = void 0;
127
+ var visitTtl = 4 * 60; // 4 hours
128
+ var visitorTtl = 2 * 365 * 24 * 60; // 2 years
129
+ var isReady = false;
130
+ var queue = [];
131
+ var canStringify = typeof JSON !== "undefined" && typeof JSON.stringify !== "undefined";
132
+ var eventQueue = [];
133
+
134
+ function visitsUrl() {
135
+ return config.urlPrefix + config.visitsUrl;
136
+ }
137
+
138
+ function eventsUrl() {
139
+ return config.urlPrefix + config.eventsUrl;
140
+ }
141
+
142
+ function canTrackNow() {
143
+ return (config.useBeacon || config.trackNow) && canStringify && typeof window.navigator.sendBeacon !== "undefined";
144
+ }
145
+
146
+ // cookies
147
+
148
+ // http://www.quirksmode.org/js/cookies.html
149
+ function setCookie(name, value, ttl) {
150
+ var expires = "";
151
+ var cookieDomain = "";
152
+ if (ttl) {
153
+ var date = new Date();
154
+ date.setTime(date.getTime() + ttl * 60 * 1000);
155
+ expires = "; expires=" + date.toGMTString();
53
156
  }
54
-
55
- function canTrackNow() {
56
- return (config.useBeacon || config.trackNow) && canStringify && typeof(window.navigator.sendBeacon) !== "undefined";
157
+ var domain = config.cookieDomain || config.domain;
158
+ if (domain) {
159
+ cookieDomain = "; domain=" + domain;
57
160
  }
58
-
59
- // cookies
60
-
61
- // http://www.quirksmode.org/js/cookies.html
62
- function setCookie(name, value, ttl) {
63
- var expires = "";
64
- var cookieDomain = "";
65
- if (ttl) {
66
- var date = new Date();
67
- date.setTime(date.getTime() + (ttl * 60 * 1000));
68
- expires = "; expires=" + date.toGMTString();
161
+ document.cookie = name + "=" + escape(value) + expires + cookieDomain + "; path=/";
162
+ }
163
+
164
+ function getCookie(name) {
165
+ var i = void 0,
166
+ c = void 0;
167
+ var nameEQ = name + "=";
168
+ var ca = document.cookie.split(';');
169
+ for (i = 0; i < ca.length; i++) {
170
+ c = ca[i];
171
+ while (c.charAt(0) === ' ') {
172
+ c = c.substring(1, c.length);
69
173
  }
70
- var domain = config.cookieDomain || config.domain;
71
- if (domain) {
72
- cookieDomain = "; domain=" + domain;
174
+ if (c.indexOf(nameEQ) === 0) {
175
+ return unescape(c.substring(nameEQ.length, c.length));
73
176
  }
74
- document.cookie = name + "=" + escape(value) + expires + cookieDomain + "; path=/";
75
177
  }
178
+ return null;
179
+ }
76
180
 
77
- function getCookie(name) {
78
- var i, c;
79
- var nameEQ = name + "=";
80
- var ca = document.cookie.split(';');
81
- for (i = 0; i < ca.length; i++) {
82
- c = ca[i];
83
- while (c.charAt(0) === ' ') {
84
- c = c.substring(1, c.length);
85
- }
86
- if (c.indexOf(nameEQ) === 0) {
87
- return unescape(c.substring(nameEQ.length, c.length));
88
- }
89
- }
90
- return null;
91
- }
181
+ function destroyCookie(name) {
182
+ setCookie(name, "", -1);
183
+ }
92
184
 
93
- function destroyCookie(name) {
94
- setCookie(name, "", -1);
185
+ function log(message) {
186
+ if (getCookie("ahoy_debug")) {
187
+ window.console.log(message);
95
188
  }
189
+ }
96
190
 
97
- function log(message) {
98
- if (getCookie("ahoy_debug")) {
99
- window.console.log(message);
100
- }
191
+ function setReady() {
192
+ var callback = void 0;
193
+ while (callback = queue.shift()) {
194
+ callback();
195
+ }
196
+ isReady = true;
197
+ }
198
+
199
+ function ready(callback) {
200
+ if (isReady) {
201
+ callback();
202
+ } else {
203
+ queue.push(callback);
101
204
  }
205
+ }
102
206
 
103
- function setReady() {
104
- var callback;
105
- while (callback = queue.shift()) {
106
- callback();
107
- }
108
- isReady = true;
207
+ function matchesSelector(element, selector) {
208
+ if (element.matches) {
209
+ return element.matches(selector);
210
+ } else {
211
+ return element.msMatchesSelector(selector);
109
212
  }
213
+ }
110
214
 
111
- function ready(callback) {
112
- if (isReady) {
113
- callback();
114
- } else {
115
- queue.push(callback);
215
+ function onEvent(eventName, selector, callback) {
216
+ document.addEventListener(eventName, function (e) {
217
+ if (matchesSelector(e.target, selector)) {
218
+ callback(e);
116
219
  }
117
- }
220
+ });
221
+ }
222
+
223
+ // http://beeker.io/jquery-document-ready-equivalent-vanilla-javascript
224
+ function documentReady(callback) {
225
+ document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback);
226
+ }
227
+
228
+ // http://stackoverflow.com/a/2117523/1177228
229
+ function generateId() {
230
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
231
+ var r = Math.random() * 16 | 0,
232
+ v = c == 'x' ? r : r & 0x3 | 0x8;
233
+ return v.toString(16);
234
+ });
235
+ }
118
236
 
119
- // http://stackoverflow.com/a/2117523/1177228
120
- function generateId() {
121
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
122
- var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
123
- return v.toString(16);
124
- });
237
+ function saveEventQueue() {
238
+ // TODO add stringify method for IE 7 and under
239
+ if (canStringify) {
240
+ setCookie("ahoy_events", JSON.stringify(eventQueue), 1);
125
241
  }
242
+ }
126
243
 
127
- function saveEventQueue() {
128
- // TODO add stringify method for IE 7 and under
129
- if (canStringify) {
130
- setCookie("ahoy_events", JSON.stringify(eventQueue), 1);
131
- }
132
- }
244
+ // from rails-ujs
133
245
 
134
- // from jquery-ujs
246
+ function csrfToken() {
247
+ var meta = document.querySelector("meta[name=csrf-token]");
248
+ return meta && meta.content;
249
+ }
135
250
 
136
- function csrfToken() {
137
- return $("meta[name=csrf-token]").attr("content");
138
- }
251
+ function csrfParam() {
252
+ var meta = document.querySelector("meta[name=csrf-param]");
253
+ return meta && meta.content;
254
+ }
139
255
 
140
- function csrfParam() {
141
- return $("meta[name=csrf-param]").attr("content");
142
- }
256
+ function CSRFProtection(xhr) {
257
+ var token = csrfToken();
258
+ if (token) xhr.setRequestHeader("X-CSRF-Token", token);
259
+ }
143
260
 
144
- function CSRFProtection(xhr) {
145
- var token = csrfToken();
146
- if (token) xhr.setRequestHeader("X-CSRF-Token", token);
147
- }
148
-
149
- function sendRequest(url, data, success) {
150
- if (canStringify) {
261
+ function sendRequest(url, data, success) {
262
+ if (canStringify) {
263
+ if ($) {
151
264
  $.ajax({
152
265
  type: "POST",
153
266
  url: url,
@@ -157,246 +270,359 @@
157
270
  beforeSend: CSRFProtection,
158
271
  success: success
159
272
  });
273
+ } else {
274
+ var xhr = new XMLHttpRequest();
275
+ xhr.open("POST", url, true);
276
+ xhr.setRequestHeader("Content-Type", "application/json");
277
+ xhr.onload = function () {
278
+ if (xhr.status === 200) {
279
+ success();
280
+ }
281
+ };
282
+ CSRFProtection(xhr);
283
+ xhr.send(JSON.stringify(data));
160
284
  }
161
285
  }
286
+ }
162
287
 
163
- function eventData(event) {
164
- var data = {
165
- events: [event],
166
- visit_token: event.visit_token,
167
- visitor_token: event.visitor_token
168
- };
169
- delete event.visit_token;
170
- delete event.visitor_token;
171
- return data;
172
- }
173
-
174
- function trackEvent(event) {
175
- ready( function () {
176
- sendRequest(eventsUrl(), eventData(event), function() {
177
- // remove from queue
178
- for (var i = 0; i < eventQueue.length; i++) {
179
- if (eventQueue[i].id == event.id) {
180
- eventQueue.splice(i, 1);
181
- break;
182
- }
288
+ function eventData(event) {
289
+ var data = {
290
+ events: [event],
291
+ visit_token: event.visit_token,
292
+ visitor_token: event.visitor_token
293
+ };
294
+ delete event.visit_token;
295
+ delete event.visitor_token;
296
+ return data;
297
+ }
298
+
299
+ function trackEvent(event) {
300
+ ready(function () {
301
+ sendRequest(eventsUrl(), eventData(event), function () {
302
+ // remove from queue
303
+ for (var i = 0; i < eventQueue.length; i++) {
304
+ if (eventQueue[i].id == event.id) {
305
+ eventQueue.splice(i, 1);
306
+ break;
183
307
  }
184
- saveEventQueue();
185
- });
308
+ }
309
+ saveEventQueue();
186
310
  });
187
- }
311
+ });
312
+ }
188
313
 
189
- function trackEventNow(event) {
190
- ready( function () {
191
- var data = eventData(event);
192
- var param = csrfParam();
193
- var token = csrfToken();
194
- if (param && token) data[param] = token;
195
- var payload = new Blob([JSON.stringify(data)], {type : "application/json; charset=utf-8"});
196
- navigator.sendBeacon(eventsUrl(), payload);
197
- });
198
- }
314
+ function trackEventNow(event) {
315
+ ready(function () {
316
+ var data = eventData(event);
317
+ var param = csrfParam();
318
+ var token = csrfToken();
319
+ if (param && token) data[param] = token;
320
+ // stringify so we keep the type
321
+ data.events_json = JSON.stringify(data.events);
322
+ delete data.events;
323
+ window.navigator.sendBeacon(eventsUrl(), (0, _objectToFormdata2.default)(data));
324
+ });
325
+ }
199
326
 
200
- function page() {
201
- return config.page || window.location.pathname;
202
- }
327
+ function page() {
328
+ return config.page || window.location.pathname;
329
+ }
330
+
331
+ function presence(str) {
332
+ return str && str.length > 0 ? str : null;
333
+ }
203
334
 
204
- function eventProperties(e) {
205
- var $target = $(e.currentTarget);
206
- return {
207
- tag: $target.get(0).tagName.toLowerCase(),
208
- id: $target.attr("id"),
209
- "class": $target.attr("class"),
210
- page: page(),
211
- section: $target.closest("*[data-section]").attr("data-section")
212
- };
335
+ function cleanObject(obj) {
336
+ for (var key in obj) {
337
+ if (obj.hasOwnProperty(key)) {
338
+ if (obj[key] === null) {
339
+ delete obj[key];
340
+ }
341
+ }
213
342
  }
343
+ return obj;
344
+ }
345
+
346
+ function eventProperties(e) {
347
+ var target = e.target;
348
+ return cleanObject({
349
+ tag: target.tagName.toLowerCase(),
350
+ id: presence(target.id),
351
+ "class": presence(target.className),
352
+ page: page(),
353
+ section: getClosestSection(target)
354
+ });
355
+ }
214
356
 
215
- function createVisit() {
216
- isReady = false;
357
+ function getClosestSection(element) {
358
+ for (; element && element !== document; element = element.parentNode) {
359
+ if (element.hasAttribute('data-section')) {
360
+ return element.getAttribute('data-section');
361
+ }
362
+ }
217
363
 
218
- visitId = ahoy.getVisitId();
219
- visitorId = ahoy.getVisitorId();
220
- track = getCookie("ahoy_track");
364
+ return null;
365
+ }
221
366
 
222
- if (visitId && visitorId && !track) {
223
- // TODO keep visit alive?
224
- log("Active visit");
225
- setReady();
226
- } else {
227
- if (track) {
228
- destroyCookie("ahoy_track");
229
- }
367
+ function createVisit() {
368
+ isReady = false;
230
369
 
231
- if (!visitId) {
232
- visitId = generateId();
233
- setCookie("ahoy_visit", visitId, visitTtl);
234
- }
370
+ visitId = ahoy.getVisitId();
371
+ visitorId = ahoy.getVisitorId();
372
+ track = getCookie("ahoy_track");
235
373
 
236
- // make sure cookies are enabled
237
- if (getCookie("ahoy_visit")) {
238
- log("Visit started");
374
+ if (visitId && visitorId && !track) {
375
+ // TODO keep visit alive?
376
+ log("Active visit");
377
+ setReady();
378
+ } else {
379
+ if (track) {
380
+ destroyCookie("ahoy_track");
381
+ }
239
382
 
240
- if (!visitorId) {
241
- visitorId = generateId();
242
- setCookie("ahoy_visitor", visitorId, visitorTtl);
243
- }
383
+ if (!visitId) {
384
+ visitId = generateId();
385
+ setCookie("ahoy_visit", visitId, visitTtl);
386
+ }
244
387
 
245
- var data = {
246
- visit_token: visitId,
247
- visitor_token: visitorId,
248
- platform: config.platform,
249
- landing_page: window.location.href,
250
- screen_width: window.screen.width,
251
- screen_height: window.screen.height
252
- };
253
-
254
- // referrer
255
- if (document.referrer.length > 0) {
256
- data.referrer = document.referrer;
257
- }
388
+ // make sure cookies are enabled
389
+ if (getCookie("ahoy_visit")) {
390
+ log("Visit started");
258
391
 
259
- log(data);
392
+ if (!visitorId) {
393
+ visitorId = generateId();
394
+ setCookie("ahoy_visitor", visitorId, visitorTtl);
395
+ }
260
396
 
261
- sendRequest(visitsUrl(), data, setReady);
262
- } else {
263
- log("Cookies disabled");
264
- setReady();
397
+ var data = {
398
+ visit_token: visitId,
399
+ visitor_token: visitorId,
400
+ platform: config.platform,
401
+ landing_page: window.location.href,
402
+ screen_width: window.screen.width,
403
+ screen_height: window.screen.height
404
+ };
405
+
406
+ // referrer
407
+ if (document.referrer.length > 0) {
408
+ data.referrer = document.referrer;
265
409
  }
410
+
411
+ log(data);
412
+
413
+ sendRequest(visitsUrl(), data, setReady);
414
+ } else {
415
+ log("Cookies disabled");
416
+ setReady();
266
417
  }
267
418
  }
268
-
269
- ahoy.getVisitId = ahoy.getVisitToken = function () {
270
- return getCookie("ahoy_visit");
419
+ }
420
+
421
+ ahoy.getVisitId = ahoy.getVisitToken = function () {
422
+ return getCookie("ahoy_visit");
423
+ };
424
+
425
+ ahoy.getVisitorId = ahoy.getVisitorToken = function () {
426
+ return getCookie("ahoy_visitor");
427
+ };
428
+
429
+ ahoy.reset = function () {
430
+ destroyCookie("ahoy_visit");
431
+ destroyCookie("ahoy_visitor");
432
+ destroyCookie("ahoy_events");
433
+ destroyCookie("ahoy_track");
434
+ return true;
435
+ };
436
+
437
+ ahoy.debug = function (enabled) {
438
+ if (enabled === false) {
439
+ destroyCookie("ahoy_debug");
440
+ } else {
441
+ setCookie("ahoy_debug", "t", 365 * 24 * 60); // 1 year
442
+ }
443
+ return true;
444
+ };
445
+
446
+ ahoy.track = function (name, properties) {
447
+ // generate unique id
448
+ var event = {
449
+ name: name,
450
+ properties: properties || {},
451
+ time: new Date().getTime() / 1000.0,
452
+ id: generateId()
271
453
  };
272
454
 
273
- ahoy.getVisitorId = ahoy.getVisitorToken = function () {
274
- return getCookie("ahoy_visitor");
275
- };
455
+ // wait for createVisit to log
456
+ documentReady(function () {
457
+ log(event);
458
+ });
276
459
 
277
- ahoy.reset = function () {
278
- destroyCookie("ahoy_visit");
279
- destroyCookie("ahoy_visitor");
280
- destroyCookie("ahoy_events");
281
- destroyCookie("ahoy_track");
282
- return true;
283
- };
460
+ ready(function () {
461
+ if (!ahoy.getVisitId()) {
462
+ createVisit();
463
+ }
464
+
465
+ event.visit_token = ahoy.getVisitId();
466
+ event.visitor_token = ahoy.getVisitorId();
284
467
 
285
- ahoy.debug = function (enabled) {
286
- if (enabled === false) {
287
- destroyCookie("ahoy_debug");
468
+ if (canTrackNow()) {
469
+ trackEventNow(event);
288
470
  } else {
289
- setCookie("ahoy_debug", "t", 365 * 24 * 60); // 1 year
471
+ eventQueue.push(event);
472
+ saveEventQueue();
473
+
474
+ // wait in case navigating to reduce duplicate events
475
+ setTimeout(function () {
476
+ trackEvent(event);
477
+ }, 1000);
290
478
  }
291
- return true;
292
- };
479
+ });
480
+ };
293
481
 
294
- ahoy.track = function (name, properties) {
295
- // generate unique id
296
- var event = {
297
- id: generateId(),
298
- name: name,
299
- properties: properties || {},
300
- time: (new Date()).getTime() / 1000.0
301
- };
302
-
303
- // wait for createVisit to log
304
- $( function () {
305
- log(event);
306
- });
482
+ ahoy.trackView = function (additionalProperties) {
483
+ var properties = {
484
+ url: window.location.href,
485
+ title: document.title,
486
+ page: page()
487
+ };
307
488
 
308
- ready( function () {
309
- if (!ahoy.getVisitId()) {
310
- createVisit();
489
+ if (additionalProperties) {
490
+ for (var propName in additionalProperties) {
491
+ if (additionalProperties.hasOwnProperty(propName)) {
492
+ properties[propName] = additionalProperties[propName];
311
493
  }
494
+ }
495
+ }
496
+ ahoy.track("$view", properties);
497
+ };
498
+
499
+ ahoy.trackClicks = function () {
500
+ onEvent("click", "a, button, input[type=submit]", function (e) {
501
+ var target = e.target;
502
+ var properties = eventProperties(e);
503
+ properties.text = properties.tag == "input" ? target.value : (target.textContent || target.innerText || target.innerHTML).replace(/[\s\r\n]+/g, " ").trim();
504
+ properties.href = target.href;
505
+ ahoy.track("$click", properties);
506
+ });
507
+ };
312
508
 
313
- event.visit_token = ahoy.getVisitId();
314
- event.visitor_token = ahoy.getVisitorId();
509
+ ahoy.trackSubmits = function () {
510
+ onEvent("submit", "form", function (e) {
511
+ var properties = eventProperties(e);
512
+ ahoy.track("$submit", properties);
513
+ });
514
+ };
315
515
 
316
- if (canTrackNow()) {
317
- trackEventNow(event);
318
- } else {
319
- eventQueue.push(event);
320
- saveEventQueue();
516
+ ahoy.trackChanges = function () {
517
+ onEvent("change", "input, textarea, select", function (e) {
518
+ var properties = eventProperties(e);
519
+ ahoy.track("$change", properties);
520
+ });
521
+ };
522
+
523
+ ahoy.trackAll = function () {
524
+ ahoy.trackView();
525
+ ahoy.trackClicks();
526
+ ahoy.trackSubmits();
527
+ ahoy.trackChanges();
528
+ };
529
+
530
+ // push events from queue
531
+ try {
532
+ eventQueue = JSON.parse(getCookie("ahoy_events") || "[]");
533
+ } catch (e) {
534
+ // do nothing
535
+ }
536
+
537
+ for (var i = 0; i < eventQueue.length; i++) {
538
+ trackEvent(eventQueue[i]);
539
+ }
540
+
541
+ ahoy.start = function () {
542
+ createVisit();
543
+
544
+ ahoy.start = function () {};
545
+ };
546
+
547
+ documentReady(function () {
548
+ if (config.startOnReady) {
549
+ ahoy.start();
550
+ }
551
+ });
321
552
 
322
- // wait in case navigating to reduce duplicate events
323
- setTimeout( function () {
324
- trackEvent(event);
325
- }, 1000);
326
- }
327
- });
328
- };
553
+ exports.default = ahoy;
329
554
 
330
- ahoy.trackView = function (additionalProperties) {
331
- var properties = {
332
- url: window.location.href,
333
- title: document.title,
334
- page: page()
335
- };
336
-
337
- if (additionalProperties) {
338
- for(var propName in additionalProperties) {
339
- if (additionalProperties.hasOwnProperty(propName)) {
340
- properties[propName] = additionalProperties[propName];
341
- }
342
- }
343
- }
344
- ahoy.track("$view", properties);
345
- };
555
+ /***/ }),
556
+ /* 1 */
557
+ /***/ (function(module, exports, __webpack_require__) {
346
558
 
347
- ahoy.trackClicks = function () {
348
- $(document).on("click", "a, button, input[type=submit]", function (e) {
349
- var $target = $(e.currentTarget);
350
- var properties = eventProperties(e);
351
- properties.text = properties.tag == "input" ? $target.val() : $.trim($target.text().replace(/[\s\r\n]+/g, " "));
352
- properties.href = $target.attr("href");
353
- ahoy.track("$click", properties);
354
- });
355
- };
559
+ "use strict";
356
560
 
357
- ahoy.trackSubmits = function () {
358
- $(document).on("submit", "form", function (e) {
359
- var properties = eventProperties(e);
360
- ahoy.track("$submit", properties);
361
- });
362
- };
363
561
 
364
- ahoy.trackChanges = function () {
365
- $(document).on("change", "input, textarea, select", function (e) {
366
- var properties = eventProperties(e);
367
- ahoy.track("$change", properties);
368
- });
369
- };
562
+ function isUndefined (value) {
563
+ return value === undefined
564
+ }
370
565
 
371
- ahoy.trackAll = function() {
372
- ahoy.trackView();
373
- ahoy.trackClicks();
374
- ahoy.trackSubmits();
375
- ahoy.trackChanges();
376
- };
566
+ function isObject (value) {
567
+ return value === Object(value)
568
+ }
377
569
 
378
- // push events from queue
379
- try {
380
- eventQueue = JSON.parse(getCookie("ahoy_events") || "[]");
381
- } catch (e) {
382
- // do nothing
383
- }
570
+ function isArray (value) {
571
+ return Array.isArray(value)
572
+ }
384
573
 
385
- for (var i = 0; i < eventQueue.length; i++) {
386
- trackEvent(eventQueue[i]);
574
+ function isBlob (value) {
575
+ return value != null &&
576
+ typeof value.size === 'number' &&
577
+ typeof value.type === 'string' &&
578
+ typeof value.slice === 'function'
579
+ }
580
+
581
+ function isFile (value) {
582
+ return isBlob(value) &&
583
+ typeof value.lastModified === 'number' &&
584
+ typeof value.name === 'string'
585
+ }
586
+
587
+ function isDate (value) {
588
+ return value instanceof Date
589
+ }
590
+
591
+ function objectToFormData (obj, fd, pre) {
592
+ fd = fd || new FormData()
593
+
594
+ if (isUndefined(obj)) {
595
+ return fd
596
+ } else if (isArray(obj)) {
597
+ obj.forEach(function (value) {
598
+ var key = pre + '[]'
599
+
600
+ objectToFormData(value, fd, key)
601
+ })
602
+ } else if (isObject(obj) && !isFile(obj) && !isDate(obj)) {
603
+ Object.keys(obj).forEach(function (prop) {
604
+ var value = obj[prop]
605
+
606
+ if (isArray(value)) {
607
+ while (prop.length > 2 && prop.lastIndexOf('[]') === prop.length - 2) {
608
+ prop = prop.substring(0, prop.length - 2)
609
+ }
610
+ }
611
+
612
+ var key = pre ? (pre + '[' + prop + ']') : prop
613
+
614
+ objectToFormData(value, fd, key)
615
+ })
616
+ } else {
617
+ fd.append(pre, obj)
387
618
  }
388
619
 
389
- ahoy.start = function () {
390
- createVisit();
620
+ return fd
621
+ }
391
622
 
392
- ahoy.start = function () {};
393
- };
623
+ module.exports = objectToFormData
394
624
 
395
- $( function () {
396
- if (config.startOnReady) {
397
- ahoy.start();
398
- }
399
- });
400
625
 
401
- window.ahoy = ahoy;
402
- }(window));
626
+ /***/ })
627
+ /******/ ])["default"];
628
+ });