UNPKG

7.28 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
5Object.defineProperty(exports, "__esModule", {
6 value: true
7});
8exports["default"] = void 0;
9
10var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
11
12var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
13
14var isLeftClickEvent = function isLeftClickEvent(event) {
15 return event.button === 0;
16};
17
18var isModifiedEvent = function isModifiedEvent(event) {
19 return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
20};
21
22var trackMap = {
23 select: ['focus', 'blur'],
24 textarea: ['focus', 'blur'],
25 input: ['focus', 'blur'],
26 "default": ['click']
27};
28
29var isValidEventTypeOnTarget = function isValidEventTypeOnTarget(event) {
30 return (trackMap[event.target.nodeName.toLowerCase()] || trackMap["default"]).indexOf(event.type) > -1;
31};
32
33var isPluginEnabled = function isPluginEnabled(plugin) {
34 return typeof plugin.isEnabled === 'function' ? plugin.isEnabled() : plugin.isEnabled;
35};
36
37var camelCase = function camelCase(str) {
38 return str.replace(/-([a-z\d])/gi, function (match, _char) {
39 return _char.toUpperCase();
40 });
41};
42/**
43 * Polyfill for [`Event.composedPath()`][1].
44 * https://gist.github.com/kleinfreund/e9787d73776c0e3750dcfcdc89f100ec
45 */
46
47
48var getComposedPath = function getComposedPath(node) {
49 var parent;
50
51 if (node.parentNode) {
52 parent = node.parentNode;
53 } else if (node.host) {
54 parent = node.host;
55 } else if (node.defaultView) {
56 parent = node.defaultView;
57 }
58
59 if (parent !== undefined) {
60 return [node].concat(getComposedPath(parent));
61 }
62
63 return [node];
64};
65
66var AvAnalytics = function AvAnalytics(plugins) {
67 var _this = this;
68
69 var promise = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Promise;
70 var pageTracking = arguments.length > 2 ? arguments[2] : undefined;
71 var autoTrack = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
72 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
73 (0, _classCallCheck2["default"])(this, AvAnalytics);
74
75 this.startAutoTrack = function () {
76 document.body.addEventListener('click', _this.handleEvent, true);
77 document.body.addEventListener('focus', _this.handleEvent, true);
78 document.body.addEventListener('blur', _this.handleEvent, true);
79 };
80
81 this.stopAutoTrack = function () {
82 document.body.removeEventListener('click', _this.handleEvent, true);
83 document.body.removeEventListener('focus', _this.handleEvent, true);
84 document.body.removeEventListener('blur', _this.handleEvent, true);
85 };
86
87 this.handleEvent = function (event) {
88 if (_this.invalidEvent(event)) {
89 return;
90 }
91
92 var target = event.target || event.srcElement;
93 var path = getComposedPath(event.target);
94 var analyticAttrs = {};
95
96 if (_this.recursive) {
97 // Reverse the array so we pull attributes from top down
98 path.reverse().forEach(function (pth) {
99 var attrs = _this.getAnalyticAttrs(pth);
100
101 analyticAttrs = (0, _objectSpread2["default"])({}, analyticAttrs, {}, attrs); // To consider using the element it has to have analytics attrs
102
103 if (Object.keys(attrs).length > 0) {
104 analyticAttrs.elemId = pth.getAttribute('id') || pth.getAttribute('name');
105 }
106 });
107 } else {
108 analyticAttrs = _this.getAnalyticAttrs(target);
109 }
110
111 if (!Object.keys(analyticAttrs).length > 0 || _this.recursive && !analyticAttrs.action || analyticAttrs.action !== event.type) {
112 return;
113 }
114
115 analyticAttrs.action = analyticAttrs.action || event.type;
116 analyticAttrs.event = event.type;
117 analyticAttrs.elemId = analyticAttrs.elemId || target.getAttribute('id') || target.getAttribute('name');
118
119 _this.trackEvent(analyticAttrs);
120 };
121
122 this.invalidEvent = function (event) {
123 return isModifiedEvent(event) || event.type === 'click' && !isLeftClickEvent(event) || !isValidEventTypeOnTarget(event);
124 };
125
126 this.getAnalyticAttrs = function (elem) {
127 if (!elem.attributes) {
128 return {};
129 }
130
131 var attrs = elem.attributes;
132 var analyticAttrs = {};
133
134 if (elem.nodeType === 1) {
135 for (var i = attrs.length - 1; i >= 0; i--) {
136 var name = attrs[i].name;
137
138 if (name.indexOf("".concat(_this.attributePrefix, "-")) === 0) {
139 var camelName = camelCase(name.slice(_this.attributePrefix.length + 1));
140 analyticAttrs[camelName] = elem.getAttribute(name);
141 }
142 }
143 }
144
145 return analyticAttrs;
146 };
147
148 this.startPageTracking = function () {
149 if (!_this.pageListener) {
150 _this.pageListener = _this.trackPageView;
151 window.addEventListener('hashchange', _this.pageListener, false);
152 }
153 };
154
155 this.stopPageTracking = function () {
156 if (_this.pageListener) {
157 window.removeEventListener('hashchange', _this.pageListener, false);
158 delete _this.pageListener;
159 }
160 };
161
162 this.init = function () {
163 _this.setPageTracking();
164
165 _this.plugins.forEach(function (plugin) {
166 if (isPluginEnabled(plugin) && typeof plugin.init === 'function') {
167 plugin.init();
168 }
169 });
170 };
171
172 this.setPageTracking = function (value) {
173 // eslint-disable-next-line eqeqeq
174 if (value != undefined) {
175 _this.pageTracking = !!value;
176 }
177
178 var canPageTrack = typeof _this.startPageTracking === 'function' && typeof _this.stopPageTracking === 'function';
179
180 if (canPageTrack && _this.pageTracking !== _this.isPageTracking) {
181 if (_this.pageTracking) {
182 _this.startPageTracking();
183 } else {
184 _this.stopPageTracking();
185 }
186
187 _this.isPageTracking = _this.pageTracking;
188 }
189 };
190
191 this.trackEvent = function (properties) {
192 var promises = [];
193 properties.url = properties.url || window.location.href || 'N/A';
194
195 _this.plugins.forEach(function (plugin) {
196 var props = (0, _objectSpread2["default"])({}, properties);
197
198 if (isPluginEnabled(plugin) && typeof plugin.trackEvent === 'function') {
199 promises.push(plugin.trackEvent(props));
200 }
201 });
202
203 return _this.Promise.all(promises);
204 };
205
206 this.trackPageView = function (url) {
207 // hashchanges are an object so we want to grab the new url from it
208 if (typeof url === 'object') {
209 url = url.newURL;
210 }
211
212 url = url || window.location.href;
213 var promises = [];
214
215 _this.plugins.forEach(function (plugin) {
216 if (isPluginEnabled(plugin) && typeof plugin.trackPageView === 'function') {
217 promises.push(plugin.trackPageView(url));
218 }
219 });
220
221 return _this.Promise.all(promises);
222 };
223
224 // if plugins or promise are undefined,
225 // or if either is skipped and pageTracking boolean is used in their place
226 if (!plugins || !promise) {
227 throw new Error('[plugins], and [promise] must be defined');
228 }
229
230 this.plugins = Array.isArray(plugins) ? plugins : [plugins];
231 this.pageTracking = !!pageTracking;
232 this.Promise = promise;
233 this.recursive = !!options.recursive;
234 this.attributePrefix = options.attributePrefix || 'data-analytics';
235 this.isPageTracking = false;
236 this.hasInit = false;
237
238 if (autoTrack) {
239 this.startAutoTrack();
240 }
241};
242
243exports["default"] = AvAnalytics;
\No newline at end of file