ahoy_matey 2.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +112 -37
- data/CONTRIBUTING.md +9 -7
- data/LICENSE.txt +1 -1
- data/README.md +377 -63
- data/app/controllers/ahoy/base_controller.rb +14 -10
- data/app/controllers/ahoy/events_controller.rb +1 -1
- data/app/controllers/ahoy/visits_controller.rb +1 -0
- data/app/jobs/ahoy/geocode_v2_job.rb +3 -4
- data/lib/ahoy.rb +57 -2
- data/lib/ahoy/base_store.rb +32 -3
- data/lib/ahoy/controller.rb +21 -8
- data/lib/ahoy/database_store.rb +33 -16
- data/lib/ahoy/engine.rb +3 -1
- data/lib/ahoy/helper.rb +40 -0
- data/lib/ahoy/model.rb +2 -2
- data/lib/ahoy/query_methods.rb +46 -1
- data/lib/ahoy/tracker.rb +59 -27
- data/lib/ahoy/utils.rb +7 -0
- data/lib/ahoy/version.rb +1 -1
- data/lib/ahoy/visit_properties.rb +73 -37
- data/lib/generators/ahoy/activerecord_generator.rb +17 -26
- data/lib/generators/ahoy/base_generator.rb +1 -1
- data/lib/generators/ahoy/install_generator.rb +1 -1
- data/lib/generators/ahoy/mongoid_generator.rb +1 -5
- data/lib/generators/ahoy/templates/active_record_event_model.rb.tt +10 -0
- data/lib/generators/ahoy/templates/{active_record_migration.rb → active_record_migration.rb.tt} +14 -7
- data/lib/generators/ahoy/templates/active_record_visit_model.rb.tt +6 -0
- data/lib/generators/ahoy/templates/{base_store_initializer.rb → base_store_initializer.rb.tt} +8 -0
- data/lib/generators/ahoy/templates/database_store_initializer.rb.tt +10 -0
- data/lib/generators/ahoy/templates/{mongoid_event_model.rb → mongoid_event_model.rb.tt} +1 -1
- data/lib/generators/ahoy/templates/{mongoid_visit_model.rb → mongoid_visit_model.rb.tt} +9 -7
- data/vendor/assets/javascripts/ahoy.js +539 -552
- metadata +27 -204
- data/.github/ISSUE_TEMPLATE.md +0 -7
- data/.gitignore +0 -17
- data/Gemfile +0 -6
- data/Rakefile +0 -9
- data/ahoy_matey.gemspec +0 -36
- data/docs/Ahoy-2-Upgrade.md +0 -147
- data/docs/Data-Store-Examples.md +0 -240
- data/lib/generators/ahoy/templates/active_record_event_model.rb +0 -10
- data/lib/generators/ahoy/templates/active_record_visit_model.rb +0 -6
- data/lib/generators/ahoy/templates/database_store_initializer.rb +0 -5
- data/test/query_methods/mongoid_test.rb +0 -23
- data/test/query_methods/mysql_json_test.rb +0 -18
- data/test/query_methods/mysql_text_test.rb +0 -19
- data/test/query_methods/postgresql_hstore_test.rb +0 -20
- data/test/query_methods/postgresql_json_test.rb +0 -18
- data/test/query_methods/postgresql_jsonb_test.rb +0 -19
- data/test/query_methods/postgresql_text_test.rb +0 -19
- data/test/test_helper.rb +0 -100
@@ -3,7 +3,7 @@ require "rails/generators"
|
|
3
3
|
module Ahoy
|
4
4
|
module Generators
|
5
5
|
class InstallGenerator < Rails::Generators::Base
|
6
|
-
source_root File.
|
6
|
+
source_root File.join(__dir__, "templates")
|
7
7
|
|
8
8
|
def copy_templates
|
9
9
|
activerecord = defined?(ActiveRecord)
|
@@ -3,7 +3,7 @@ require "rails/generators"
|
|
3
3
|
module Ahoy
|
4
4
|
module Generators
|
5
5
|
class MongoidGenerator < Rails::Generators::Base
|
6
|
-
source_root File.
|
6
|
+
source_root File.join(__dir__, "templates")
|
7
7
|
|
8
8
|
def copy_templates
|
9
9
|
template "database_store_initializer.rb", "config/initializers/ahoy.rb"
|
@@ -11,10 +11,6 @@ module Ahoy
|
|
11
11
|
template "mongoid_event_model.rb", "app/models/ahoy/event.rb"
|
12
12
|
puts "\nAlmost set! Last, run:\n\n rake db:mongoid:create_indexes"
|
13
13
|
end
|
14
|
-
|
15
|
-
def rails5?
|
16
|
-
Rails::VERSION::MAJOR >= 5
|
17
|
-
end
|
18
14
|
end
|
19
15
|
end
|
20
16
|
end
|
data/lib/generators/ahoy/templates/{active_record_migration.rb → active_record_migration.rb.tt}
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
|
2
2
|
def change
|
3
|
-
|
3
|
+
create_table :ahoy_visits do |t|
|
4
4
|
t.string :visit_token
|
5
5
|
t.string :visitor_token
|
6
6
|
|
@@ -15,7 +15,6 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
|
|
15
15
|
t.text :user_agent
|
16
16
|
t.text :referrer
|
17
17
|
t.string :referring_domain
|
18
|
-
t.string :search_keyword
|
19
18
|
t.text :landing_page
|
20
19
|
|
21
20
|
# technology
|
@@ -27,6 +26,8 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
|
|
27
26
|
t.string :country
|
28
27
|
t.string :region
|
29
28
|
t.string :city
|
29
|
+
t.float :latitude
|
30
|
+
t.float :longitude
|
30
31
|
|
31
32
|
# utm parameters
|
32
33
|
t.string :utm_source
|
@@ -35,10 +36,15 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
|
|
35
36
|
t.string :utm_content
|
36
37
|
t.string :utm_campaign
|
37
38
|
|
38
|
-
|
39
|
+
# native apps
|
40
|
+
t.string :app_version
|
41
|
+
t.string :os_version
|
42
|
+
t.string :platform
|
43
|
+
|
44
|
+
t.datetime :started_at
|
39
45
|
end
|
40
46
|
|
41
|
-
add_index :ahoy_visits,
|
47
|
+
add_index :ahoy_visits, :visit_token, unique: true
|
42
48
|
|
43
49
|
create_table :ahoy_events do |t|
|
44
50
|
t.references :visit
|
@@ -46,10 +52,11 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
|
|
46
52
|
|
47
53
|
t.string :name
|
48
54
|
t.<%= properties_type %> :properties
|
49
|
-
t.
|
55
|
+
t.datetime :time
|
50
56
|
end
|
51
57
|
|
52
|
-
add_index :ahoy_events, [:name, :time]<% if properties_type == "jsonb"
|
53
|
-
add_index :ahoy_events,
|
58
|
+
add_index :ahoy_events, [:name, :time]<% if properties_type == "jsonb" %><% if rails52? %>
|
59
|
+
add_index :ahoy_events, :properties, using: :gin, opclass: :jsonb_path_ops<% else %>
|
60
|
+
add_index :ahoy_events, "properties jsonb_path_ops", using: "gin"<% end %><% end %>
|
54
61
|
end
|
55
62
|
end
|
data/lib/generators/ahoy/templates/{base_store_initializer.rb → base_store_initializer.rb.tt}
RENAMED
@@ -15,3 +15,11 @@ class Ahoy::Store < Ahoy::BaseStore
|
|
15
15
|
# !!!
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
# set to true for JavaScript tracking
|
20
|
+
Ahoy.api = false
|
21
|
+
|
22
|
+
# set to true for geocoding
|
23
|
+
# we recommend configuring local geocoding first
|
24
|
+
# see https://github.com/ankane/ahoy#geocoding
|
25
|
+
Ahoy.geocode = false
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Ahoy::Store < Ahoy::DatabaseStore
|
2
|
+
end
|
3
|
+
|
4
|
+
# set to true for JavaScript tracking
|
5
|
+
Ahoy.api = false
|
6
|
+
|
7
|
+
# set to true for geocoding
|
8
|
+
# we recommend configuring local geocoding first
|
9
|
+
# see https://github.com/ankane/ahoy#geocoding
|
10
|
+
Ahoy.geocode = false
|
@@ -3,7 +3,7 @@ class Ahoy::Visit
|
|
3
3
|
|
4
4
|
# associations
|
5
5
|
has_many :events, class_name: "Ahoy::Event"
|
6
|
-
belongs_to :user, index: true
|
6
|
+
belongs_to :user, index: true, optional: true
|
7
7
|
|
8
8
|
# required
|
9
9
|
field :visit_token, type: String
|
@@ -16,23 +16,20 @@ class Ahoy::Visit
|
|
16
16
|
field :ip, type: String
|
17
17
|
field :user_agent, type: String
|
18
18
|
field :referrer, type: String
|
19
|
-
field :landing_page, type: String
|
20
|
-
|
21
|
-
# traffic source
|
22
19
|
field :referring_domain, type: String
|
23
|
-
field :
|
20
|
+
field :landing_page, type: String
|
24
21
|
|
25
22
|
# technology
|
26
23
|
field :browser, type: String
|
27
24
|
field :os, type: String
|
28
25
|
field :device_type, type: String
|
29
|
-
field :screen_height, type: Integer
|
30
|
-
field :screen_width, type: Integer
|
31
26
|
|
32
27
|
# location
|
33
28
|
field :country, type: String
|
34
29
|
field :region, type: String
|
35
30
|
field :city, type: String
|
31
|
+
field :latitude, type: Float
|
32
|
+
field :longitude, type: Float
|
36
33
|
|
37
34
|
# utm parameters
|
38
35
|
field :utm_source, type: String
|
@@ -41,6 +38,11 @@ class Ahoy::Visit
|
|
41
38
|
field :utm_content, type: String
|
42
39
|
field :utm_campaign, type: String
|
43
40
|
|
41
|
+
# native apps
|
42
|
+
field :app_version, type: String
|
43
|
+
field :os_version, type: String
|
44
|
+
field :platform, type: String
|
45
|
+
|
44
46
|
field :started_at, type: Time
|
45
47
|
|
46
48
|
index({visit_token: 1}, {unique: true})
|
@@ -1,628 +1,615 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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];
|
1
|
+
/*
|
2
|
+
* Ahoy.js
|
3
|
+
* Simple, powerful JavaScript analytics
|
4
|
+
* https://github.com/ankane/ahoy.js
|
5
|
+
* v0.3.8
|
6
|
+
* MIT License
|
7
|
+
*/
|
8
|
+
|
9
|
+
(function (global, factory) {
|
10
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
11
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
12
|
+
(global = global || self, global.ahoy = factory());
|
13
|
+
}(this, (function () { 'use strict';
|
14
|
+
|
15
|
+
var isUndefined = function (value) { return value === undefined; };
|
16
|
+
|
17
|
+
var isNull = function (value) { return value === null; };
|
18
|
+
|
19
|
+
var isBoolean = function (value) { return typeof value === 'boolean'; };
|
20
|
+
|
21
|
+
var isObject = function (value) { return value === Object(value); };
|
22
|
+
|
23
|
+
var isArray = function (value) { return Array.isArray(value); };
|
24
|
+
|
25
|
+
var isDate = function (value) { return value instanceof Date; };
|
26
|
+
|
27
|
+
var isBlob = function (value) { return value &&
|
28
|
+
typeof value.size === 'number' &&
|
29
|
+
typeof value.type === 'string' &&
|
30
|
+
typeof value.slice === 'function'; };
|
31
|
+
|
32
|
+
var isFile = function (value) { return isBlob(value) &&
|
33
|
+
typeof value.name === 'string' &&
|
34
|
+
(typeof value.lastModifiedDate === 'object' ||
|
35
|
+
typeof value.lastModified === 'number'); };
|
36
|
+
|
37
|
+
var serialize = function (obj, cfg, fd, pre) {
|
38
|
+
cfg = cfg || {};
|
39
|
+
|
40
|
+
cfg.indices = isUndefined(cfg.indices) ? false : cfg.indices;
|
41
|
+
|
42
|
+
cfg.nullsAsUndefineds = isUndefined(cfg.nullsAsUndefineds)
|
43
|
+
? false
|
44
|
+
: cfg.nullsAsUndefineds;
|
45
|
+
|
46
|
+
cfg.booleansAsIntegers = isUndefined(cfg.booleansAsIntegers)
|
47
|
+
? false
|
48
|
+
: cfg.booleansAsIntegers;
|
49
|
+
|
50
|
+
cfg.allowEmptyArrays = isUndefined(cfg.allowEmptyArrays)
|
51
|
+
? false
|
52
|
+
: cfg.allowEmptyArrays;
|
53
|
+
|
54
|
+
fd = fd || new FormData();
|
55
|
+
|
56
|
+
if (isUndefined(obj)) {
|
57
|
+
return fd;
|
58
|
+
} else if (isNull(obj)) {
|
59
|
+
if (!cfg.nullsAsUndefineds) {
|
60
|
+
fd.append(pre, '');
|
61
|
+
}
|
62
|
+
} else if (isBoolean(obj)) {
|
63
|
+
if (cfg.booleansAsIntegers) {
|
64
|
+
fd.append(pre, obj ? 1 : 0);
|
65
|
+
} else {
|
66
|
+
fd.append(pre, obj);
|
67
|
+
}
|
68
|
+
} else if (isArray(obj)) {
|
69
|
+
if (obj.length) {
|
70
|
+
obj.forEach(function (value, index) {
|
71
|
+
var key = pre + '[' + (cfg.indices ? index : '') + ']';
|
72
|
+
|
73
|
+
serialize(value, cfg, fd, key);
|
74
|
+
});
|
75
|
+
} else if (cfg.allowEmptyArrays) {
|
76
|
+
fd.append(pre + '[]', '');
|
77
|
+
}
|
78
|
+
} else if (isDate(obj)) {
|
79
|
+
fd.append(pre, obj.toISOString());
|
80
|
+
} else if (isObject(obj) && !isFile(obj) && !isBlob(obj)) {
|
81
|
+
Object.keys(obj).forEach(function (prop) {
|
82
|
+
var value = obj[prop];
|
83
|
+
|
84
|
+
if (isArray(value)) {
|
85
|
+
while (prop.length > 2 && prop.lastIndexOf('[]') === prop.length - 2) {
|
86
|
+
prop = prop.substring(0, prop.length - 2);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
var key = pre ? pre + '[' + prop + ']' : prop;
|
91
|
+
|
92
|
+
serialize(value, cfg, fd, key);
|
93
|
+
});
|
94
|
+
} else {
|
95
|
+
fd.append(pre, obj);
|
116
96
|
}
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
var
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
var
|
129
|
-
|
130
|
-
var
|
131
|
-
var
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
}
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
function
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
}
|
157
|
-
var domain = config.cookieDomain || config.domain;
|
158
|
-
if (domain) {
|
159
|
-
cookieDomain = "; domain=" + domain;
|
160
|
-
}
|
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);
|
97
|
+
|
98
|
+
return fd;
|
99
|
+
};
|
100
|
+
|
101
|
+
var index_module = {
|
102
|
+
serialize: serialize,
|
103
|
+
};
|
104
|
+
var index_module_1 = index_module.serialize;
|
105
|
+
|
106
|
+
// https://www.quirksmode.org/js/cookies.html
|
107
|
+
|
108
|
+
var Cookies = {
|
109
|
+
set: function (name, value, ttl, domain) {
|
110
|
+
var expires = "";
|
111
|
+
var cookieDomain = "";
|
112
|
+
if (ttl) {
|
113
|
+
var date = new Date();
|
114
|
+
date.setTime(date.getTime() + (ttl * 60 * 1000));
|
115
|
+
expires = "; expires=" + date.toGMTString();
|
116
|
+
}
|
117
|
+
if (domain) {
|
118
|
+
cookieDomain = "; domain=" + domain;
|
119
|
+
}
|
120
|
+
document.cookie = name + "=" + escape(value) + expires + cookieDomain + "; path=/";
|
121
|
+
},
|
122
|
+
get: function (name) {
|
123
|
+
var i, c;
|
124
|
+
var nameEQ = name + "=";
|
125
|
+
var ca = document.cookie.split(';');
|
126
|
+
for (i = 0; i < ca.length; i++) {
|
127
|
+
c = ca[i];
|
128
|
+
while (c.charAt(0) === ' ') {
|
129
|
+
c = c.substring(1, c.length);
|
130
|
+
}
|
131
|
+
if (c.indexOf(nameEQ) === 0) {
|
132
|
+
return unescape(c.substring(nameEQ.length, c.length));
|
133
|
+
}
|
134
|
+
}
|
135
|
+
return null;
|
173
136
|
}
|
174
|
-
|
175
|
-
|
137
|
+
};
|
138
|
+
|
139
|
+
var config = {
|
140
|
+
urlPrefix: "",
|
141
|
+
visitsUrl: "/ahoy/visits",
|
142
|
+
eventsUrl: "/ahoy/events",
|
143
|
+
page: null,
|
144
|
+
platform: "Web",
|
145
|
+
useBeacon: true,
|
146
|
+
startOnReady: true,
|
147
|
+
trackVisits: true,
|
148
|
+
cookies: true,
|
149
|
+
cookieDomain: null,
|
150
|
+
headers: {},
|
151
|
+
visitParams: {},
|
152
|
+
withCredentials: false,
|
153
|
+
visitDuration: 4 * 60, // default 4 hours
|
154
|
+
visitorDuration: 2 * 365 * 24 * 60 // default 2 years
|
155
|
+
};
|
156
|
+
|
157
|
+
var ahoy = window.ahoy || window.Ahoy || {};
|
158
|
+
|
159
|
+
ahoy.configure = function (options) {
|
160
|
+
for (var key in options) {
|
161
|
+
if (options.hasOwnProperty(key)) {
|
162
|
+
config[key] = options[key];
|
163
|
+
}
|
176
164
|
}
|
165
|
+
};
|
166
|
+
|
167
|
+
// legacy
|
168
|
+
ahoy.configure(ahoy);
|
169
|
+
|
170
|
+
var $ = window.jQuery || window.Zepto || window.$;
|
171
|
+
var visitId, visitorId, track;
|
172
|
+
var isReady = false;
|
173
|
+
var queue = [];
|
174
|
+
var canStringify = typeof(JSON) !== "undefined" && typeof(JSON.stringify) !== "undefined";
|
175
|
+
var eventQueue = [];
|
176
|
+
|
177
|
+
function visitsUrl() {
|
178
|
+
return config.urlPrefix + config.visitsUrl;
|
177
179
|
}
|
178
|
-
return null;
|
179
|
-
}
|
180
180
|
|
181
|
-
function
|
182
|
-
|
183
|
-
}
|
181
|
+
function eventsUrl() {
|
182
|
+
return config.urlPrefix + config.eventsUrl;
|
183
|
+
}
|
184
|
+
|
185
|
+
function isEmpty(obj) {
|
186
|
+
return Object.keys(obj).length === 0;
|
187
|
+
}
|
184
188
|
|
185
|
-
function
|
186
|
-
|
187
|
-
window.console.log(message);
|
189
|
+
function canTrackNow() {
|
190
|
+
return (config.useBeacon || config.trackNow) && isEmpty(config.headers) && canStringify && typeof(window.navigator.sendBeacon) !== "undefined" && !config.withCredentials;
|
188
191
|
}
|
189
|
-
}
|
190
192
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
193
|
+
// cookies
|
194
|
+
|
195
|
+
function setCookie(name, value, ttl) {
|
196
|
+
Cookies.set(name, value, ttl, config.cookieDomain || config.domain);
|
195
197
|
}
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
function ready(callback) {
|
200
|
-
if (isReady) {
|
201
|
-
callback();
|
202
|
-
} else {
|
203
|
-
queue.push(callback);
|
198
|
+
|
199
|
+
function getCookie(name) {
|
200
|
+
return Cookies.get(name);
|
204
201
|
}
|
205
|
-
}
|
206
202
|
|
207
|
-
function
|
208
|
-
|
209
|
-
return element.matches(selector);
|
210
|
-
} else {
|
211
|
-
return element.msMatchesSelector(selector);
|
203
|
+
function destroyCookie(name) {
|
204
|
+
Cookies.set(name, "", -1);
|
212
205
|
}
|
213
|
-
}
|
214
206
|
|
215
|
-
function
|
216
|
-
|
217
|
-
|
218
|
-
callback(e);
|
207
|
+
function log(message) {
|
208
|
+
if (getCookie("ahoy_debug")) {
|
209
|
+
window.console.log(message);
|
219
210
|
}
|
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
|
-
}
|
211
|
+
}
|
236
212
|
|
237
|
-
function
|
238
|
-
|
239
|
-
|
240
|
-
|
213
|
+
function setReady() {
|
214
|
+
var callback;
|
215
|
+
while ((callback = queue.shift())) {
|
216
|
+
callback();
|
217
|
+
}
|
218
|
+
isReady = true;
|
241
219
|
}
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
function csrfToken() {
|
247
|
-
var meta = document.querySelector("meta[name=csrf-token]");
|
248
|
-
return meta && meta.content;
|
249
|
-
}
|
250
|
-
|
251
|
-
function csrfParam() {
|
252
|
-
var meta = document.querySelector("meta[name=csrf-param]");
|
253
|
-
return meta && meta.content;
|
254
|
-
}
|
255
|
-
|
256
|
-
function CSRFProtection(xhr) {
|
257
|
-
var token = csrfToken();
|
258
|
-
if (token) xhr.setRequestHeader("X-CSRF-Token", token);
|
259
|
-
}
|
260
|
-
|
261
|
-
function sendRequest(url, data, success) {
|
262
|
-
if (canStringify) {
|
263
|
-
if ($) {
|
264
|
-
$.ajax({
|
265
|
-
type: "POST",
|
266
|
-
url: url,
|
267
|
-
data: JSON.stringify(data),
|
268
|
-
contentType: "application/json; charset=utf-8",
|
269
|
-
dataType: "json",
|
270
|
-
beforeSend: CSRFProtection,
|
271
|
-
success: success
|
272
|
-
});
|
220
|
+
|
221
|
+
ahoy.ready = function (callback) {
|
222
|
+
if (isReady) {
|
223
|
+
callback();
|
273
224
|
} else {
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
225
|
+
queue.push(callback);
|
226
|
+
}
|
227
|
+
};
|
228
|
+
|
229
|
+
function matchesSelector(element, selector) {
|
230
|
+
var matches = element.matches ||
|
231
|
+
element.matchesSelector ||
|
232
|
+
element.mozMatchesSelector ||
|
233
|
+
element.msMatchesSelector ||
|
234
|
+
element.oMatchesSelector ||
|
235
|
+
element.webkitMatchesSelector;
|
236
|
+
|
237
|
+
if (matches) {
|
238
|
+
if (matches.apply(element, [selector])) {
|
239
|
+
return element;
|
240
|
+
} else if (element.parentElement) {
|
241
|
+
return matchesSelector(element.parentElement, selector)
|
242
|
+
}
|
243
|
+
return null;
|
244
|
+
} else {
|
245
|
+
log("Unable to match");
|
246
|
+
return null;
|
284
247
|
}
|
285
248
|
}
|
286
|
-
}
|
287
249
|
|
288
|
-
function
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
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;
|
307
|
-
}
|
250
|
+
function onEvent(eventName, selector, callback) {
|
251
|
+
document.addEventListener(eventName, function (e) {
|
252
|
+
var matchedElement = matchesSelector(e.target, selector);
|
253
|
+
if (matchedElement) {
|
254
|
+
callback.call(matchedElement, e);
|
308
255
|
}
|
309
|
-
saveEventQueue();
|
310
256
|
});
|
311
|
-
}
|
312
|
-
}
|
313
|
-
|
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
|
-
}
|
326
|
-
|
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
|
-
}
|
257
|
+
}
|
334
258
|
|
335
|
-
|
336
|
-
|
337
|
-
if (
|
338
|
-
|
339
|
-
|
340
|
-
|
259
|
+
// http://beeker.io/jquery-document-ready-equivalent-vanilla-javascript
|
260
|
+
function documentReady(callback) {
|
261
|
+
if (document.readyState === "interactive" || document.readyState === "complete") {
|
262
|
+
setTimeout(callback, 0);
|
263
|
+
} else {
|
264
|
+
document.addEventListener("DOMContentLoaded", callback);
|
341
265
|
}
|
342
266
|
}
|
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
|
-
}
|
356
267
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
268
|
+
// https://stackoverflow.com/a/2117523/1177228
|
269
|
+
function generateId() {
|
270
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
271
|
+
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
272
|
+
return v.toString(16);
|
273
|
+
});
|
274
|
+
}
|
275
|
+
|
276
|
+
function saveEventQueue() {
|
277
|
+
if (config.cookies && canStringify) {
|
278
|
+
setCookie("ahoy_events", JSON.stringify(eventQueue), 1);
|
361
279
|
}
|
362
280
|
}
|
363
281
|
|
364
|
-
|
365
|
-
}
|
282
|
+
// from rails-ujs
|
366
283
|
|
367
|
-
function
|
368
|
-
|
284
|
+
function csrfToken() {
|
285
|
+
var meta = document.querySelector("meta[name=csrf-token]");
|
286
|
+
return meta && meta.content;
|
287
|
+
}
|
288
|
+
|
289
|
+
function csrfParam() {
|
290
|
+
var meta = document.querySelector("meta[name=csrf-param]");
|
291
|
+
return meta && meta.content;
|
292
|
+
}
|
369
293
|
|
370
|
-
|
371
|
-
|
372
|
-
|
294
|
+
function CSRFProtection(xhr) {
|
295
|
+
var token = csrfToken();
|
296
|
+
if (token) { xhr.setRequestHeader("X-CSRF-Token", token); }
|
297
|
+
}
|
373
298
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
299
|
+
function sendRequest(url, data, success) {
|
300
|
+
if (canStringify) {
|
301
|
+
if ($ && $.ajax) {
|
302
|
+
$.ajax({
|
303
|
+
type: "POST",
|
304
|
+
url: url,
|
305
|
+
data: JSON.stringify(data),
|
306
|
+
contentType: "application/json; charset=utf-8",
|
307
|
+
dataType: "json",
|
308
|
+
beforeSend: CSRFProtection,
|
309
|
+
success: success,
|
310
|
+
headers: config.headers,
|
311
|
+
xhrFields: {
|
312
|
+
withCredentials: config.withCredentials
|
313
|
+
}
|
314
|
+
});
|
315
|
+
} else {
|
316
|
+
var xhr = new XMLHttpRequest();
|
317
|
+
xhr.open("POST", url, true);
|
318
|
+
xhr.withCredentials = config.withCredentials;
|
319
|
+
xhr.setRequestHeader("Content-Type", "application/json");
|
320
|
+
for (var header in config.headers) {
|
321
|
+
if (config.headers.hasOwnProperty(header)) {
|
322
|
+
xhr.setRequestHeader(header, config.headers[header]);
|
323
|
+
}
|
324
|
+
}
|
325
|
+
xhr.onload = function() {
|
326
|
+
if (xhr.status === 200) {
|
327
|
+
success();
|
328
|
+
}
|
329
|
+
};
|
330
|
+
CSRFProtection(xhr);
|
331
|
+
xhr.send(JSON.stringify(data));
|
332
|
+
}
|
381
333
|
}
|
334
|
+
}
|
382
335
|
|
383
|
-
|
384
|
-
|
385
|
-
|
336
|
+
function eventData(event) {
|
337
|
+
var data = {
|
338
|
+
events: [event]
|
339
|
+
};
|
340
|
+
if (config.cookies) {
|
341
|
+
data.visit_token = event.visit_token;
|
342
|
+
data.visitor_token = event.visitor_token;
|
386
343
|
}
|
344
|
+
delete event.visit_token;
|
345
|
+
delete event.visitor_token;
|
346
|
+
return data;
|
347
|
+
}
|
387
348
|
|
388
|
-
|
389
|
-
|
390
|
-
|
349
|
+
function trackEvent(event) {
|
350
|
+
ahoy.ready( function () {
|
351
|
+
sendRequest(eventsUrl(), eventData(event), function() {
|
352
|
+
// remove from queue
|
353
|
+
for (var i = 0; i < eventQueue.length; i++) {
|
354
|
+
if (eventQueue[i].id == event.id) {
|
355
|
+
eventQueue.splice(i, 1);
|
356
|
+
break;
|
357
|
+
}
|
358
|
+
}
|
359
|
+
saveEventQueue();
|
360
|
+
});
|
361
|
+
});
|
362
|
+
}
|
391
363
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
364
|
+
function trackEventNow(event) {
|
365
|
+
ahoy.ready( function () {
|
366
|
+
var data = eventData(event);
|
367
|
+
var param = csrfParam();
|
368
|
+
var token = csrfToken();
|
369
|
+
if (param && token) { data[param] = token; }
|
370
|
+
// stringify so we keep the type
|
371
|
+
data.events_json = JSON.stringify(data.events);
|
372
|
+
delete data.events;
|
373
|
+
window.navigator.sendBeacon(eventsUrl(), index_module_1(data));
|
374
|
+
});
|
375
|
+
}
|
396
376
|
|
397
|
-
|
398
|
-
|
399
|
-
|
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;
|
409
|
-
}
|
377
|
+
function page() {
|
378
|
+
return config.page || window.location.pathname;
|
379
|
+
}
|
410
380
|
|
411
|
-
|
381
|
+
function presence(str) {
|
382
|
+
return (str && str.length > 0) ? str : null;
|
383
|
+
}
|
412
384
|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
385
|
+
function cleanObject(obj) {
|
386
|
+
for (var key in obj) {
|
387
|
+
if (obj.hasOwnProperty(key)) {
|
388
|
+
if (obj[key] === null) {
|
389
|
+
delete obj[key];
|
390
|
+
}
|
391
|
+
}
|
417
392
|
}
|
393
|
+
return obj;
|
418
394
|
}
|
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()
|
453
|
-
};
|
454
395
|
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
396
|
+
function eventProperties(e) {
|
397
|
+
return cleanObject({
|
398
|
+
tag: this.tagName.toLowerCase(),
|
399
|
+
id: presence(this.id),
|
400
|
+
"class": presence(this.className),
|
401
|
+
page: page(),
|
402
|
+
section: getClosestSection(this)
|
403
|
+
});
|
404
|
+
}
|
459
405
|
|
460
|
-
|
461
|
-
|
462
|
-
|
406
|
+
function getClosestSection(element) {
|
407
|
+
for ( ; element && element !== document; element = element.parentNode) {
|
408
|
+
if (element.hasAttribute('data-section')) {
|
409
|
+
return element.getAttribute('data-section');
|
410
|
+
}
|
463
411
|
}
|
464
412
|
|
465
|
-
|
466
|
-
|
413
|
+
return null;
|
414
|
+
}
|
467
415
|
|
468
|
-
|
469
|
-
|
416
|
+
function createVisit() {
|
417
|
+
isReady = false;
|
418
|
+
|
419
|
+
visitId = ahoy.getVisitId();
|
420
|
+
visitorId = ahoy.getVisitorId();
|
421
|
+
track = getCookie("ahoy_track");
|
422
|
+
|
423
|
+
if (config.cookies === false || config.trackVisits === false) {
|
424
|
+
log("Visit tracking disabled");
|
425
|
+
setReady();
|
426
|
+
} else if (visitId && visitorId && !track) {
|
427
|
+
// TODO keep visit alive?
|
428
|
+
log("Active visit");
|
429
|
+
setReady();
|
470
430
|
} else {
|
471
|
-
|
472
|
-
|
431
|
+
if (!visitId) {
|
432
|
+
visitId = generateId();
|
433
|
+
setCookie("ahoy_visit", visitId, config.visitDuration);
|
434
|
+
}
|
473
435
|
|
474
|
-
//
|
475
|
-
|
476
|
-
|
477
|
-
}, 1000);
|
478
|
-
}
|
479
|
-
});
|
480
|
-
};
|
436
|
+
// make sure cookies are enabled
|
437
|
+
if (getCookie("ahoy_visit")) {
|
438
|
+
log("Visit started");
|
481
439
|
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
page: page()
|
487
|
-
};
|
440
|
+
if (!visitorId) {
|
441
|
+
visitorId = generateId();
|
442
|
+
setCookie("ahoy_visitor", visitorId, config.visitorDuration);
|
443
|
+
}
|
488
444
|
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
445
|
+
var data = {
|
446
|
+
visit_token: visitId,
|
447
|
+
visitor_token: visitorId,
|
448
|
+
platform: config.platform,
|
449
|
+
landing_page: window.location.href,
|
450
|
+
screen_width: window.screen.width,
|
451
|
+
screen_height: window.screen.height,
|
452
|
+
js: true
|
453
|
+
};
|
454
|
+
|
455
|
+
// referrer
|
456
|
+
if (document.referrer.length > 0) {
|
457
|
+
data.referrer = document.referrer;
|
458
|
+
}
|
459
|
+
|
460
|
+
for (var key in config.visitParams) {
|
461
|
+
if (config.visitParams.hasOwnProperty(key)) {
|
462
|
+
data[key] = config.visitParams[key];
|
463
|
+
}
|
464
|
+
}
|
465
|
+
|
466
|
+
log(data);
|
467
|
+
|
468
|
+
sendRequest(visitsUrl(), data, function () {
|
469
|
+
// wait until successful to destroy
|
470
|
+
destroyCookie("ahoy_track");
|
471
|
+
setReady();
|
472
|
+
});
|
473
|
+
} else {
|
474
|
+
log("Cookies disabled");
|
475
|
+
setReady();
|
493
476
|
}
|
494
477
|
}
|
495
478
|
}
|
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
|
-
};
|
508
|
-
|
509
|
-
ahoy.trackSubmits = function () {
|
510
|
-
onEvent("submit", "form", function (e) {
|
511
|
-
var properties = eventProperties(e);
|
512
|
-
ahoy.track("$submit", properties);
|
513
|
-
});
|
514
|
-
};
|
515
479
|
|
516
|
-
ahoy.
|
517
|
-
|
518
|
-
|
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
|
-
});
|
480
|
+
ahoy.getVisitId = ahoy.getVisitToken = function () {
|
481
|
+
return getCookie("ahoy_visit");
|
482
|
+
};
|
552
483
|
|
553
|
-
|
484
|
+
ahoy.getVisitorId = ahoy.getVisitorToken = function () {
|
485
|
+
return getCookie("ahoy_visitor");
|
486
|
+
};
|
554
487
|
|
555
|
-
|
556
|
-
|
557
|
-
|
488
|
+
ahoy.reset = function () {
|
489
|
+
destroyCookie("ahoy_visit");
|
490
|
+
destroyCookie("ahoy_visitor");
|
491
|
+
destroyCookie("ahoy_events");
|
492
|
+
destroyCookie("ahoy_track");
|
493
|
+
return true;
|
494
|
+
};
|
558
495
|
|
559
|
-
|
496
|
+
ahoy.debug = function (enabled) {
|
497
|
+
if (enabled === false) {
|
498
|
+
destroyCookie("ahoy_debug");
|
499
|
+
} else {
|
500
|
+
setCookie("ahoy_debug", "t", 365 * 24 * 60); // 1 year
|
501
|
+
}
|
502
|
+
return true;
|
503
|
+
};
|
560
504
|
|
505
|
+
ahoy.track = function (name, properties) {
|
506
|
+
// generate unique id
|
507
|
+
var event = {
|
508
|
+
name: name,
|
509
|
+
properties: properties || {},
|
510
|
+
time: (new Date()).getTime() / 1000.0,
|
511
|
+
id: generateId(),
|
512
|
+
js: true
|
513
|
+
};
|
514
|
+
|
515
|
+
ahoy.ready( function () {
|
516
|
+
if (config.cookies && !ahoy.getVisitId()) {
|
517
|
+
createVisit();
|
518
|
+
}
|
561
519
|
|
562
|
-
function
|
563
|
-
|
564
|
-
}
|
520
|
+
ahoy.ready( function () {
|
521
|
+
log(event);
|
565
522
|
|
566
|
-
|
567
|
-
|
568
|
-
}
|
523
|
+
event.visit_token = ahoy.getVisitId();
|
524
|
+
event.visitor_token = ahoy.getVisitorId();
|
569
525
|
|
570
|
-
|
571
|
-
|
572
|
-
}
|
526
|
+
if (canTrackNow()) {
|
527
|
+
trackEventNow(event);
|
528
|
+
} else {
|
529
|
+
eventQueue.push(event);
|
530
|
+
saveEventQueue();
|
573
531
|
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
}
|
532
|
+
// wait in case navigating to reduce duplicate events
|
533
|
+
setTimeout( function () {
|
534
|
+
trackEvent(event);
|
535
|
+
}, 1000);
|
536
|
+
}
|
537
|
+
});
|
538
|
+
});
|
580
539
|
|
581
|
-
|
582
|
-
|
583
|
-
typeof value.lastModified === 'number' &&
|
584
|
-
typeof value.name === 'string'
|
585
|
-
}
|
540
|
+
return true;
|
541
|
+
};
|
586
542
|
|
587
|
-
function
|
588
|
-
|
589
|
-
|
543
|
+
ahoy.trackView = function (additionalProperties) {
|
544
|
+
var properties = {
|
545
|
+
url: window.location.href,
|
546
|
+
title: document.title,
|
547
|
+
page: page()
|
548
|
+
};
|
549
|
+
|
550
|
+
if (additionalProperties) {
|
551
|
+
for(var propName in additionalProperties) {
|
552
|
+
if (additionalProperties.hasOwnProperty(propName)) {
|
553
|
+
properties[propName] = additionalProperties[propName];
|
554
|
+
}
|
555
|
+
}
|
556
|
+
}
|
557
|
+
ahoy.track("$view", properties);
|
558
|
+
};
|
590
559
|
|
591
|
-
function
|
592
|
-
|
560
|
+
ahoy.trackClicks = function () {
|
561
|
+
onEvent("click", "a, button, input[type=submit]", function (e) {
|
562
|
+
var properties = eventProperties.call(this, e);
|
563
|
+
properties.text = properties.tag == "input" ? this.value : (this.textContent || this.innerText || this.innerHTML).replace(/[\s\r\n]+/g, " ").trim();
|
564
|
+
properties.href = this.href;
|
565
|
+
ahoy.track("$click", properties);
|
566
|
+
});
|
567
|
+
};
|
593
568
|
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
569
|
+
ahoy.trackSubmits = function () {
|
570
|
+
onEvent("submit", "form", function (e) {
|
571
|
+
var properties = eventProperties.call(this, e);
|
572
|
+
ahoy.track("$submit", properties);
|
573
|
+
});
|
574
|
+
};
|
599
575
|
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
576
|
+
ahoy.trackChanges = function () {
|
577
|
+
onEvent("change", "input, textarea, select", function (e) {
|
578
|
+
var properties = eventProperties.call(this, e);
|
579
|
+
ahoy.track("$change", properties);
|
580
|
+
});
|
581
|
+
};
|
605
582
|
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
583
|
+
ahoy.trackAll = function() {
|
584
|
+
ahoy.trackView();
|
585
|
+
ahoy.trackClicks();
|
586
|
+
ahoy.trackSubmits();
|
587
|
+
ahoy.trackChanges();
|
588
|
+
};
|
611
589
|
|
612
|
-
|
590
|
+
// push events from queue
|
591
|
+
try {
|
592
|
+
eventQueue = JSON.parse(getCookie("ahoy_events") || "[]");
|
593
|
+
} catch (e) {
|
594
|
+
// do nothing
|
595
|
+
}
|
613
596
|
|
614
|
-
|
615
|
-
|
616
|
-
} else {
|
617
|
-
fd.append(pre, obj)
|
597
|
+
for (var i = 0; i < eventQueue.length; i++) {
|
598
|
+
trackEvent(eventQueue[i]);
|
618
599
|
}
|
619
600
|
|
620
|
-
|
621
|
-
|
601
|
+
ahoy.start = function () {
|
602
|
+
createVisit();
|
622
603
|
|
623
|
-
|
604
|
+
ahoy.start = function () {};
|
605
|
+
};
|
606
|
+
|
607
|
+
documentReady(function() {
|
608
|
+
if (config.startOnReady) {
|
609
|
+
ahoy.start();
|
610
|
+
}
|
611
|
+
});
|
624
612
|
|
613
|
+
return ahoy;
|
625
614
|
|
626
|
-
|
627
|
-
/******/ ])["default"];
|
628
|
-
});
|
615
|
+
})));
|