@fle-sdk/event-tracking-web 1.2.2 → 1.2.4
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.
- package/README.md +366 -2
- package/lib/index.esm.js +1072 -309
- package/lib/index.esm.min.js +1 -1
- package/lib/index.js +2575 -1812
- package/lib/index.min.js +1 -1
- package/lib/types/index.d.ts +87 -11
- package/lib/types/tools.d.ts +2 -0
- package/lib/types/type.d.ts +93 -1
- package/package.json +2 -3
package/lib/index.js
CHANGED
|
@@ -1,2337 +1,3100 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.WebTracking = factory());
|
|
5
5
|
}(this, (function () { 'use strict';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
function _typeof(obj) {
|
|
8
|
+
"@babel/helpers - typeof";
|
|
9
|
+
|
|
10
|
+
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
|
|
11
|
+
_typeof = function (obj) {
|
|
12
|
+
return typeof obj;
|
|
13
|
+
};
|
|
14
|
+
} else {
|
|
15
|
+
_typeof = function (obj) {
|
|
16
|
+
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return _typeof(obj);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/*! *****************************************************************************
|
|
24
|
+
Copyright (c) Microsoft Corporation.
|
|
9
25
|
|
|
10
|
-
|
|
11
|
-
|
|
26
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
27
|
+
purpose with or without fee is hereby granted.
|
|
12
28
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
30
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
31
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
32
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
33
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
34
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
35
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
36
|
+
***************************************************************************** */
|
|
37
|
+
/* global Reflect, Promise */
|
|
22
38
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
var extendStatics = function(d, b) {
|
|
40
|
+
extendStatics = Object.setPrototypeOf ||
|
|
41
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
42
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
43
|
+
return extendStatics(d, b);
|
|
44
|
+
};
|
|
29
45
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
function __extends(d, b) {
|
|
47
|
+
if (typeof b !== "function" && b !== null)
|
|
48
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
49
|
+
extendStatics(d, b);
|
|
50
|
+
function __() { this.constructor = d; }
|
|
51
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
52
|
+
}
|
|
37
53
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
var __assign = function() {
|
|
55
|
+
__assign = Object.assign || function __assign(t) {
|
|
56
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
57
|
+
s = arguments[i];
|
|
58
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
59
|
+
}
|
|
60
|
+
return t;
|
|
61
|
+
};
|
|
62
|
+
return __assign.apply(this, arguments);
|
|
63
|
+
};
|
|
48
64
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
function __spreadArray(to, from) {
|
|
66
|
+
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
|
|
67
|
+
to[j] = from[i];
|
|
68
|
+
return to;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
var WebTrackingTools =
|
|
72
|
+
/** @class */
|
|
73
|
+
function () {
|
|
74
|
+
function WebTrackingTools() {
|
|
75
|
+
var _this = this;
|
|
76
|
+
/**
|
|
77
|
+
* 系统信息
|
|
78
|
+
*/
|
|
54
79
|
|
|
55
|
-
function _typeof(obj) {
|
|
56
|
-
"@babel/helpers - typeof";
|
|
57
80
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
this.getSystemsInfo = function (platform) {
|
|
82
|
+
// print system info
|
|
83
|
+
var ua = navigator.userAgent;
|
|
84
|
+
var language = navigator.language;
|
|
85
|
+
var _platform = "other";
|
|
86
|
+
var logMsg = [];
|
|
87
|
+
var systemsInfo = {
|
|
88
|
+
language: language
|
|
89
|
+
}; // wechat client version
|
|
90
|
+
|
|
91
|
+
var wxVersionMatch = ua.match(/MicroMessenger\/([\d\.]+)/i);
|
|
92
|
+
var wxVersion = wxVersionMatch && wxVersionMatch[1] ? wxVersionMatch[1] : null; // device & system
|
|
93
|
+
|
|
94
|
+
var ipod = ua.match(/(ipod).*\s([\d_]+)/i),
|
|
95
|
+
ipad = ua.match(/(ipad).*\s([\d_]+)/i),
|
|
96
|
+
iphone = ua.match(/(iphone)\sos\s([\d_]+)/i),
|
|
97
|
+
android = ua.match(/(android)\s([\d\.]+)/i),
|
|
98
|
+
windows = ua.match(/(Windows NT)\s([\d\.]+)/i),
|
|
99
|
+
mac = ua.match(/(Mac OS X)\s([\d_]+)/i);
|
|
100
|
+
logMsg = [];
|
|
101
|
+
|
|
102
|
+
if (android) {
|
|
103
|
+
logMsg.push("Android " + android[2]);
|
|
104
|
+
_platform = "h5";
|
|
105
|
+
} else if (iphone) {
|
|
106
|
+
logMsg.push("iPhone, iOS " + iphone[2].replace(/_/g, "."));
|
|
107
|
+
_platform = "h5";
|
|
108
|
+
} else if (ipad) {
|
|
109
|
+
logMsg.push("iPad, iOS " + ipad[2].replace(/_/g, "."));
|
|
110
|
+
_platform = "ipad";
|
|
111
|
+
} else if (ipod) {
|
|
112
|
+
logMsg.push("iPod, iOS " + ipod[2].replace(/_/g, "."));
|
|
113
|
+
_platform = "h5";
|
|
114
|
+
} else if (windows) {
|
|
115
|
+
logMsg.push("Windows " + windows[2].replace(/_/g, "."));
|
|
116
|
+
_platform = "pc";
|
|
117
|
+
} else if (mac) {
|
|
118
|
+
logMsg.push("Mac, MacOS " + mac[2].replace(/_/g, "."));
|
|
119
|
+
_platform = "pc";
|
|
120
|
+
}
|
|
67
121
|
|
|
68
|
-
|
|
69
|
-
|
|
122
|
+
if (wxVersion) {
|
|
123
|
+
logMsg.push("WeChat " + wxVersion);
|
|
124
|
+
}
|
|
70
125
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
function () {
|
|
74
|
-
function WebTrackingTools() {
|
|
75
|
-
var _this = this;
|
|
76
|
-
/**
|
|
77
|
-
* 系统信息
|
|
78
|
-
*/
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this.getSystemsInfo = function (platform) {
|
|
82
|
-
// print system info
|
|
83
|
-
var ua = navigator.userAgent;
|
|
84
|
-
var language = navigator.language;
|
|
85
|
-
var _platform = "other";
|
|
86
|
-
var logMsg = [];
|
|
87
|
-
var systemsInfo = {
|
|
88
|
-
language: language
|
|
89
|
-
}; // wechat client version
|
|
90
|
-
|
|
91
|
-
var wxVersionMatch = ua.match(/MicroMessenger\/([\d\.]+)/i);
|
|
92
|
-
var wxVersion = wxVersionMatch && wxVersionMatch[1] ? wxVersionMatch[1] : null; // device & system
|
|
93
|
-
|
|
94
|
-
var ipod = ua.match(/(ipod).*\s([\d_]+)/i),
|
|
95
|
-
ipad = ua.match(/(ipad).*\s([\d_]+)/i),
|
|
96
|
-
iphone = ua.match(/(iphone)\sos\s([\d_]+)/i),
|
|
97
|
-
android = ua.match(/(android)\s([\d\.]+)/i),
|
|
98
|
-
windows = ua.match(/(Windows NT)\s([\d\.]+)/i),
|
|
99
|
-
mac = ua.match(/(Mac OS X)\s([\d_]+)/i);
|
|
100
|
-
logMsg = [];
|
|
101
|
-
|
|
102
|
-
if (android) {
|
|
103
|
-
logMsg.push("Android " + android[2]);
|
|
104
|
-
_platform = "h5";
|
|
105
|
-
} else if (iphone) {
|
|
106
|
-
logMsg.push("iPhone, iOS " + iphone[2].replace(/_/g, "."));
|
|
107
|
-
_platform = "h5";
|
|
108
|
-
} else if (ipad) {
|
|
109
|
-
logMsg.push("iPad, iOS " + ipad[2].replace(/_/g, "."));
|
|
110
|
-
_platform = "ipad";
|
|
111
|
-
} else if (ipod) {
|
|
112
|
-
logMsg.push("iPod, iOS " + ipod[2].replace(/_/g, "."));
|
|
113
|
-
_platform = "h5";
|
|
114
|
-
} else if (windows) {
|
|
115
|
-
logMsg.push("Windows " + windows[2].replace(/_/g, "."));
|
|
116
|
-
_platform = "pc";
|
|
117
|
-
} else if (mac) {
|
|
118
|
-
logMsg.push("Mac, MacOS " + mac[2].replace(/_/g, "."));
|
|
119
|
-
_platform = "pc";
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (wxVersion) {
|
|
123
|
-
logMsg.push("WeChat " + wxVersion);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
systemsInfo["client"] = logMsg.length ? logMsg.join(", ") : "Unknown";
|
|
127
|
-
systemsInfo["platform"] = platform || _platform; // network type
|
|
128
|
-
|
|
129
|
-
var network = ua.toLowerCase().match(/ nettype\/([^ ]+)/g);
|
|
130
|
-
|
|
131
|
-
if (network && network[0]) {
|
|
132
|
-
// @ts-ignore
|
|
133
|
-
network = network[0].split("/");
|
|
134
|
-
logMsg = [network[1]];
|
|
135
|
-
systemsInfo["network"] = logMsg.length ? logMsg.join(", ") : "Unknown";
|
|
136
|
-
} // User Agent
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
systemsInfo["ua"] = ua;
|
|
140
|
-
return systemsInfo;
|
|
141
|
-
};
|
|
142
|
-
/**
|
|
143
|
-
* 监听事件
|
|
144
|
-
* @param {Window | Element} target
|
|
145
|
-
* @param {String} type
|
|
146
|
-
* @param {Function} handler
|
|
147
|
-
*/
|
|
126
|
+
systemsInfo["client"] = logMsg.length ? logMsg.join(", ") : "Unknown";
|
|
127
|
+
systemsInfo["platform"] = platform || _platform; // network type
|
|
148
128
|
|
|
129
|
+
var network = ua.toLowerCase().match(/ nettype\/([^ ]+)/g);
|
|
149
130
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}, false);
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
/**
|
|
160
|
-
* 移除监听事件
|
|
161
|
-
* @param {Element} target
|
|
162
|
-
* @param {String} type
|
|
163
|
-
* @param {Funtion} handler
|
|
164
|
-
*/
|
|
131
|
+
if (network && network[0]) {
|
|
132
|
+
// @ts-ignore
|
|
133
|
+
network = network[0].split("/");
|
|
134
|
+
logMsg = [network[1]];
|
|
135
|
+
systemsInfo["network"] = logMsg.length ? logMsg.join(", ") : "Unknown";
|
|
136
|
+
} // User Agent
|
|
165
137
|
|
|
166
138
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
139
|
+
systemsInfo["ua"] = ua;
|
|
140
|
+
return systemsInfo;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* 监听事件
|
|
144
|
+
* @param {Window | Element} target
|
|
145
|
+
* @param {String} type
|
|
146
|
+
* @param {Function} handler
|
|
147
|
+
*/
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
this.addEventListener = function (target, type, handler) {
|
|
151
|
+
if (target.addEventListener) {
|
|
152
|
+
target.addEventListener(type, handler, false);
|
|
153
|
+
} else {
|
|
154
|
+
target.attachEvent && target.attachEvent("on" + type, function (event) {
|
|
155
|
+
return handler.call(target, event);
|
|
156
|
+
}, false);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* 移除监听事件
|
|
161
|
+
* @param {Element} target
|
|
162
|
+
* @param {String} type
|
|
163
|
+
* @param {Funtion} handler
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
this.removeEventListener = function (target, type, handler) {
|
|
168
|
+
if (target.removeEventListener) {
|
|
169
|
+
target.removeEventListener(type, handler);
|
|
170
|
+
} else {
|
|
171
|
+
target.detachEvent && target.detachEvent("on" + type, function (event) {
|
|
172
|
+
return handler.call(target, event);
|
|
173
|
+
}, true);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* 重写history[pushState][replaceState]方法
|
|
178
|
+
*/
|
|
179
179
|
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
this.rewriteHistory = function () {
|
|
182
|
+
var history = window.history;
|
|
183
|
+
|
|
184
|
+
var historyWrap = function historyWrap(type) {
|
|
182
185
|
var history = window.history;
|
|
186
|
+
var orig = history[type];
|
|
187
|
+
var e = new Event(type);
|
|
188
|
+
return function () {
|
|
189
|
+
var rv = orig.apply(history, arguments);
|
|
190
|
+
e.arguments = arguments;
|
|
191
|
+
window.dispatchEvent(e);
|
|
192
|
+
return rv;
|
|
193
|
+
};
|
|
194
|
+
}; // 重写
|
|
183
195
|
|
|
184
|
-
var historyWrap = function historyWrap(type) {
|
|
185
|
-
var history = window.history;
|
|
186
|
-
var orig = history[type];
|
|
187
|
-
var e = new Event(type);
|
|
188
|
-
return function () {
|
|
189
|
-
var rv = orig.apply(history, arguments);
|
|
190
|
-
e.arguments = arguments;
|
|
191
|
-
window.dispatchEvent(e);
|
|
192
|
-
return rv;
|
|
193
|
-
};
|
|
194
|
-
}; // 重写
|
|
195
196
|
|
|
197
|
+
if (!!window.history.pushState) {
|
|
198
|
+
history.pushState = historyWrap("pushState");
|
|
199
|
+
history.replaceState = historyWrap("replaceState");
|
|
200
|
+
}
|
|
201
|
+
};
|
|
196
202
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
203
|
+
this.isArray = Array.isArray || function (obj) {
|
|
204
|
+
return toString.call(obj) === "[object Array]";
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* 将json序列化成json字符串
|
|
208
|
+
* @param obj
|
|
209
|
+
* @returns {[JsonString]} [json字符串]
|
|
210
|
+
*/
|
|
202
211
|
|
|
203
|
-
this.isArray = Array.isArray || function (obj) {
|
|
204
|
-
return toString.call(obj) === "[object Array]";
|
|
205
|
-
};
|
|
206
|
-
/**
|
|
207
|
-
* 将json序列化成json字符串
|
|
208
|
-
* @param obj
|
|
209
|
-
* @returns {[JsonString]} [json字符串]
|
|
210
|
-
*/
|
|
211
212
|
|
|
213
|
+
this.formatJsonString = function (obj) {
|
|
214
|
+
try {
|
|
215
|
+
return JSON.stringify(obj, null, " ");
|
|
216
|
+
} catch (e) {
|
|
217
|
+
return JSON.stringify(obj);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
212
220
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
221
|
+
this.nativeForEach = Array.prototype.forEach;
|
|
222
|
+
this.slice = Array.prototype.slice;
|
|
223
|
+
this.hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
224
|
+
this.breaker = {};
|
|
225
|
+
/**
|
|
226
|
+
* @description 循环遍历
|
|
227
|
+
*/
|
|
220
228
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* @description 循环遍历
|
|
227
|
-
*/
|
|
229
|
+
this.each = function (obj, iterator, context) {
|
|
230
|
+
if (obj == null) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
228
233
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
234
|
+
if (_this.nativeForEach && obj.forEach === _this.nativeForEach) {
|
|
235
|
+
obj.forEach(iterator, context);
|
|
236
|
+
} else if (_this.isArray(obj) && obj.length === +obj.length) {
|
|
237
|
+
for (var i = 0, l = obj.length; i < l; i++) {
|
|
238
|
+
if (i in obj && iterator.call(context, obj[i], i, obj) === _this.breaker) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
232
241
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
for (var i = 0, l = obj.length; i < l; i++) {
|
|
238
|
-
if (i in obj && iterator.call(context, obj[i], i, obj) === _this.breaker) {
|
|
242
|
+
} else {
|
|
243
|
+
for (var key in obj) {
|
|
244
|
+
if (_this.hasOwnProperty.call(obj, key)) {
|
|
245
|
+
if (iterator.call(context, obj[key], key, obj) === _this.breaker) {
|
|
239
246
|
return false;
|
|
240
247
|
}
|
|
241
248
|
}
|
|
242
|
-
} else {
|
|
243
|
-
for (var key in obj) {
|
|
244
|
-
if (_this.hasOwnProperty.call(obj, key)) {
|
|
245
|
-
if (iterator.call(context, obj[key], key, obj) === _this.breaker) {
|
|
246
|
-
return false;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
249
|
}
|
|
250
|
+
}
|
|
251
251
|
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
return true;
|
|
253
|
+
};
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
255
|
+
this.getDomIndex = function (el) {
|
|
256
|
+
if (!el.parentNode) return -1;
|
|
257
|
+
var i = 0;
|
|
258
|
+
var nodeName = el.tagName;
|
|
259
|
+
var list = el.parentNode.children;
|
|
260
260
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
261
|
+
for (var n = 0; n < list.length; n++) {
|
|
262
|
+
if (list[n].tagName === nodeName) {
|
|
263
|
+
if (el === list[n]) {
|
|
264
|
+
return i;
|
|
265
|
+
} else {
|
|
266
|
+
i++;
|
|
268
267
|
}
|
|
269
268
|
}
|
|
269
|
+
}
|
|
270
270
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
this.selector = function (el) {
|
|
275
|
-
var i = el.parentNode && 9 == el.parentNode.nodeType ? -1 : _this.getDomIndex(el);
|
|
271
|
+
return -1;
|
|
272
|
+
};
|
|
276
273
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
} else {
|
|
280
|
-
return el.tagName.toLowerCase() + (~i ? ":nth-of-type(" + (i + 1) + ")" : "");
|
|
281
|
-
}
|
|
282
|
-
};
|
|
274
|
+
this.selector = function (el) {
|
|
275
|
+
var i = el.parentNode && 9 == el.parentNode.nodeType ? -1 : _this.getDomIndex(el);
|
|
283
276
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
277
|
+
if (el.getAttribute && el.getAttribute("id") && /^[A-Za-z][-A-Za-z0-9_:.]*$/.test(el.getAttribute("id"))) {
|
|
278
|
+
return "#" + el.getAttribute("id");
|
|
279
|
+
} else {
|
|
280
|
+
return el.tagName.toLowerCase() + (~i ? ":nth-of-type(" + (i + 1) + ")" : "");
|
|
281
|
+
}
|
|
282
|
+
};
|
|
288
283
|
|
|
289
|
-
|
|
290
|
-
|
|
284
|
+
this.getDomSelector = function (el, arr) {
|
|
285
|
+
if (!el || !el.parentNode || !el.parentNode.children) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
291
288
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return arr.join(" > ");
|
|
295
|
-
}
|
|
289
|
+
arr = arr && arr.join ? arr : [];
|
|
290
|
+
var name = el.nodeName.toLowerCase();
|
|
296
291
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
return
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* @description 增强cookie操作
|
|
303
|
-
*/
|
|
292
|
+
if (!el || name === "body" || 1 != el.nodeType) {
|
|
293
|
+
arr.unshift("body");
|
|
294
|
+
return arr.join(" > ");
|
|
295
|
+
}
|
|
304
296
|
|
|
297
|
+
arr.unshift(_this.selector(el));
|
|
298
|
+
if (el.getAttribute && el.getAttribute("id") && /^[A-Za-z][-A-Za-z0-9_:.]*$/.test(el.getAttribute("id"))) return arr.join(" > ");
|
|
299
|
+
return _this.getDomSelector(el.parentNode, arr);
|
|
300
|
+
};
|
|
301
|
+
/**
|
|
302
|
+
* @description 增强cookie操作
|
|
303
|
+
*/
|
|
305
304
|
|
|
306
|
-
this.getCookie = function (name) {
|
|
307
|
-
var nameEQ = name + "=";
|
|
308
|
-
var ca = document.cookie.split(";");
|
|
309
305
|
|
|
310
|
-
|
|
311
|
-
|
|
306
|
+
this.getCookie = function (name) {
|
|
307
|
+
var nameEQ = name + "=";
|
|
308
|
+
var ca = document.cookie.split(";");
|
|
312
309
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
310
|
+
for (var i = 0; i < ca.length; i++) {
|
|
311
|
+
var c = ca[i];
|
|
316
312
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
313
|
+
while (c.charAt(0) == " ") {
|
|
314
|
+
c = c.substring(1, c.length);
|
|
320
315
|
}
|
|
321
316
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
var cdomain = "",
|
|
327
|
-
expires = "",
|
|
328
|
-
secure = "",
|
|
329
|
-
samesite = "";
|
|
330
|
-
days = days == null ? 73000 : days;
|
|
331
|
-
var domain = this.getMainHost();
|
|
332
|
-
cdomain = domain ? "; domain=" + domain : "";
|
|
317
|
+
if (c.indexOf(nameEQ) == 0) {
|
|
318
|
+
return this._decodeURIComponent(c.substring(nameEQ.length, c.length));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
333
321
|
|
|
334
|
-
|
|
335
|
-
|
|
322
|
+
return null;
|
|
323
|
+
};
|
|
336
324
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
325
|
+
this.setCookie = function (name, value, days) {
|
|
326
|
+
var cdomain = "",
|
|
327
|
+
expires = "",
|
|
328
|
+
secure = "",
|
|
329
|
+
samesite = "";
|
|
330
|
+
days = days == null ? 73000 : days;
|
|
331
|
+
var domain = this.getMainHost();
|
|
332
|
+
cdomain = domain ? "; domain=" + domain : "";
|
|
342
333
|
|
|
343
|
-
|
|
344
|
-
|
|
334
|
+
if (days !== 0) {
|
|
335
|
+
var date = new Date();
|
|
345
336
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
337
|
+
if (String(days).slice(-1) === "s") {
|
|
338
|
+
date.setTime(date.getTime() + Number(String(days).slice(0, -1)) * 1000);
|
|
339
|
+
} else {
|
|
340
|
+
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
|
|
352
341
|
}
|
|
353
342
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
var valid_domain = "";
|
|
343
|
+
expires = "; expires=" + date.toUTCString();
|
|
344
|
+
}
|
|
357
345
|
|
|
358
|
-
|
|
359
|
-
|
|
346
|
+
function getValid(data) {
|
|
347
|
+
if (data) {
|
|
348
|
+
return data;
|
|
349
|
+
} else {
|
|
350
|
+
return false;
|
|
360
351
|
}
|
|
352
|
+
}
|
|
361
353
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
354
|
+
var valid_name = "";
|
|
355
|
+
var valid_value = "";
|
|
356
|
+
var valid_domain = "";
|
|
365
357
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
358
|
+
if (name) {
|
|
359
|
+
valid_name = getValid(name);
|
|
360
|
+
}
|
|
369
361
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
};
|
|
374
|
-
/**
|
|
375
|
-
* 获取localStorage值
|
|
376
|
-
* @param key 存储键名
|
|
377
|
-
* @returns 存储值
|
|
378
|
-
*/
|
|
362
|
+
if (value) {
|
|
363
|
+
valid_value = getValid(value);
|
|
364
|
+
}
|
|
379
365
|
|
|
366
|
+
if (cdomain) {
|
|
367
|
+
valid_domain = getValid(cdomain);
|
|
368
|
+
}
|
|
380
369
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
* @param key 存储键名
|
|
391
|
-
* @param value 存储值
|
|
392
|
-
*/
|
|
370
|
+
if (valid_name && valid_value) {
|
|
371
|
+
document.cookie = valid_name + "=" + encodeURIComponent(valid_value) + expires + "; path=/" + valid_domain + samesite + secure;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
/**
|
|
375
|
+
* 获取localStorage值
|
|
376
|
+
* @param key 存储键名
|
|
377
|
+
* @returns 存储值
|
|
378
|
+
*/
|
|
393
379
|
|
|
394
380
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
381
|
+
this.getLocalStorage = function (key) {
|
|
382
|
+
try {
|
|
383
|
+
return localStorage.getItem(key);
|
|
384
|
+
} catch (e) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* 设置localStorage值
|
|
390
|
+
* @param key 存储键名
|
|
391
|
+
* @param value 存储值
|
|
392
|
+
*/
|
|
401
393
|
|
|
402
|
-
this.removeCookie = function (name) {
|
|
403
|
-
_this.setCookie(name, "", -1);
|
|
404
|
-
};
|
|
405
|
-
/**
|
|
406
|
-
* 获取当前时间戳
|
|
407
|
-
* @return {number} [当前时间戳]
|
|
408
|
-
*/
|
|
409
394
|
|
|
395
|
+
this.setLocalStorage = function (key, value) {
|
|
396
|
+
try {
|
|
397
|
+
localStorage.setItem(key, value);
|
|
398
|
+
} catch (e) {// 静默失败
|
|
399
|
+
}
|
|
400
|
+
};
|
|
410
401
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
402
|
+
this.removeCookie = function (name) {
|
|
403
|
+
_this.setCookie(name, "", -1);
|
|
404
|
+
};
|
|
405
|
+
/**
|
|
406
|
+
* 获取当前时间戳
|
|
407
|
+
* @return {number} [当前时间戳]
|
|
408
|
+
*/
|
|
418
409
|
|
|
419
410
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
* 获取设备Id
|
|
429
|
-
* @return {number} [设备Id]
|
|
430
|
-
*/
|
|
411
|
+
this.getTimeStamp = function () {
|
|
412
|
+
return new Date().getTime();
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* 获取 UUID
|
|
416
|
+
* @return {string} [UUID唯一值]
|
|
417
|
+
*/
|
|
418
|
+
|
|
431
419
|
|
|
420
|
+
this.uuid = function () {
|
|
421
|
+
return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
422
|
+
var r = Math.random() * 16 | 0,
|
|
423
|
+
v = c == "x" ? r : r & 0x3 | 0x8;
|
|
424
|
+
return v.toString(16);
|
|
425
|
+
});
|
|
426
|
+
};
|
|
427
|
+
/**
|
|
428
|
+
* 获取设备Id
|
|
429
|
+
* @return {number} [设备Id]
|
|
430
|
+
*/
|
|
432
431
|
|
|
433
|
-
this.getDistinctId = function () {
|
|
434
|
-
var distinctId = _this.getCookie("distinctId");
|
|
435
432
|
|
|
436
|
-
|
|
437
|
-
|
|
433
|
+
this.getDistinctId = function () {
|
|
434
|
+
var distinctId = _this.getCookie("distinctId");
|
|
438
435
|
|
|
439
|
-
|
|
436
|
+
if (distinctId) return distinctId;
|
|
437
|
+
distinctId = _this.uuid();
|
|
440
438
|
|
|
441
|
-
|
|
442
|
-
};
|
|
443
|
-
/**
|
|
444
|
-
* 敏感字段过滤
|
|
445
|
-
* @param obj 需要过滤的对象
|
|
446
|
-
* @param sensitiveKeys 敏感字段列表
|
|
447
|
-
* @returns 过滤后的对象
|
|
448
|
-
*/
|
|
439
|
+
_this.setCookie("distinctId", distinctId);
|
|
449
440
|
|
|
441
|
+
return distinctId;
|
|
442
|
+
};
|
|
443
|
+
/**
|
|
444
|
+
* 敏感字段过滤
|
|
445
|
+
* @param obj 需要过滤的对象
|
|
446
|
+
* @param sensitiveKeys 敏感字段列表
|
|
447
|
+
* @returns 过滤后的对象
|
|
448
|
+
*/
|
|
450
449
|
|
|
451
|
-
this.filterSensitiveData = function (obj, sensitiveKeys) {
|
|
452
|
-
if (sensitiveKeys === void 0) {
|
|
453
|
-
sensitiveKeys = ['password', 'token', 'secret', 'key'];
|
|
454
|
-
}
|
|
455
450
|
|
|
456
|
-
|
|
457
|
-
|
|
451
|
+
this.filterSensitiveData = function (obj, sensitiveKeys) {
|
|
452
|
+
if (sensitiveKeys === void 0) {
|
|
453
|
+
sensitiveKeys = ["password", "token", "secret", "key"];
|
|
454
|
+
}
|
|
458
455
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
var isSensitive = sensitiveKeys.some(function (sensitiveKey) {
|
|
462
|
-
return typeof key === 'string' && key.toLowerCase().includes(sensitiveKey.toLowerCase());
|
|
463
|
-
});
|
|
456
|
+
if (!_this.isObject(obj)) return obj;
|
|
457
|
+
var filteredObj = {};
|
|
464
458
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
filteredObj[key] = _this.filterSensitiveData(value, sensitiveKeys);
|
|
470
|
-
} else {
|
|
471
|
-
filteredObj[key] = value;
|
|
472
|
-
}
|
|
459
|
+
_this.each(obj, function (value, key) {
|
|
460
|
+
// 检查是否是敏感字段
|
|
461
|
+
var isSensitive = sensitiveKeys.some(function (sensitiveKey) {
|
|
462
|
+
return typeof key === "string" && key.toLowerCase().includes(sensitiveKey.toLowerCase());
|
|
473
463
|
});
|
|
474
464
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
465
|
+
if (isSensitive) {
|
|
466
|
+
filteredObj[key] = "***";
|
|
467
|
+
} else if (_this.isObject(value)) {
|
|
468
|
+
// 递归过滤嵌套对象
|
|
469
|
+
filteredObj[key] = _this.filterSensitiveData(value, sensitiveKeys);
|
|
470
|
+
} else {
|
|
471
|
+
filteredObj[key] = value;
|
|
472
|
+
}
|
|
473
|
+
});
|
|
482
474
|
|
|
475
|
+
return filteredObj;
|
|
476
|
+
};
|
|
477
|
+
/**
|
|
478
|
+
* XSS过滤
|
|
479
|
+
* @param str 需要过滤的字符串
|
|
480
|
+
* @returns 过滤后的字符串
|
|
481
|
+
*/
|
|
483
482
|
|
|
484
|
-
this.xssFilter = function (str) {
|
|
485
|
-
if (!str) return '';
|
|
486
|
-
if (typeof str !== 'string') return str.toString();
|
|
487
|
-
return str.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/');
|
|
488
|
-
};
|
|
489
|
-
/**
|
|
490
|
-
* 获取url中的参数
|
|
491
|
-
* @param {[string]} name [参数名]
|
|
492
|
-
*/
|
|
493
483
|
|
|
484
|
+
this.xssFilter = function (str) {
|
|
485
|
+
if (!str) return "";
|
|
486
|
+
if (typeof str !== "string") return str.toString();
|
|
487
|
+
return str.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'").replace(/\//g, "/");
|
|
488
|
+
};
|
|
489
|
+
/**
|
|
490
|
+
* 获取url中的参数
|
|
491
|
+
* @param {[string]} name [参数名]
|
|
492
|
+
*/
|
|
494
493
|
|
|
495
|
-
this.getQueryValue = function () {
|
|
496
|
-
var _href = decodeURI(window.location.href);
|
|
497
494
|
|
|
498
|
-
|
|
499
|
-
|
|
495
|
+
this.getQueryValue = function () {
|
|
496
|
+
var _href = decodeURI(window.location.href);
|
|
500
497
|
|
|
501
|
-
|
|
502
|
-
|
|
498
|
+
var queryArr = _href.match(new RegExp("[?&][^?&]+=[^?&]+", "g")) || [];
|
|
499
|
+
var paramsObj = {};
|
|
503
500
|
|
|
504
|
-
|
|
501
|
+
for (var i = 0; i < queryArr.length; i++) {
|
|
502
|
+
var _item = queryArr[i].replace(/\?|\&/, "");
|
|
505
503
|
|
|
506
|
-
|
|
507
|
-
}
|
|
504
|
+
var pair = _item.split("=");
|
|
508
505
|
|
|
509
|
-
|
|
510
|
-
}
|
|
511
|
-
/**
|
|
512
|
-
* Ajax请求方法
|
|
513
|
-
* @param para Ajax请求参数
|
|
514
|
-
* @returns 是否成功发起请求
|
|
515
|
-
*/
|
|
506
|
+
paramsObj[pair[0]] = pair[1];
|
|
507
|
+
}
|
|
516
508
|
|
|
509
|
+
return Object.keys(paramsObj).length > 0 ? paramsObj : null;
|
|
510
|
+
};
|
|
511
|
+
/**
|
|
512
|
+
* Ajax请求方法
|
|
513
|
+
* @param para Ajax请求参数
|
|
514
|
+
* @returns 是否成功发起请求
|
|
515
|
+
*/
|
|
517
516
|
|
|
518
|
-
this.ajax = function (para) {
|
|
519
|
-
para.timeout = para.timeout || 30000;
|
|
520
|
-
para.credentials = typeof para.credentials === "undefined" ? true : para.credentials;
|
|
521
517
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}
|
|
518
|
+
this.ajax = function (para) {
|
|
519
|
+
para.timeout = para.timeout || 30000;
|
|
520
|
+
para.credentials = typeof para.credentials === "undefined" ? true : para.credentials;
|
|
526
521
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
return {};
|
|
532
|
-
}
|
|
533
|
-
}
|
|
522
|
+
function getJSON(data) {
|
|
523
|
+
if (!data) {
|
|
524
|
+
return {};
|
|
525
|
+
}
|
|
534
526
|
|
|
535
|
-
|
|
536
|
-
|
|
527
|
+
if (typeof data === "string") {
|
|
528
|
+
try {
|
|
529
|
+
return JSON.parse(data);
|
|
530
|
+
} catch (e) {
|
|
531
|
+
return {};
|
|
537
532
|
}
|
|
533
|
+
}
|
|
538
534
|
|
|
539
|
-
|
|
535
|
+
if (_typeof(data) === "object") {
|
|
536
|
+
return data;
|
|
540
537
|
}
|
|
541
538
|
|
|
542
|
-
|
|
539
|
+
return {};
|
|
540
|
+
}
|
|
543
541
|
|
|
544
|
-
|
|
545
|
-
return false;
|
|
546
|
-
}
|
|
542
|
+
var g = _this.xhr(para.cors);
|
|
547
543
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
544
|
+
if (!g) {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
551
547
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
548
|
+
if (!para.type) {
|
|
549
|
+
para.type = para.data ? "POST" : "GET";
|
|
550
|
+
}
|
|
555
551
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
g.abort();
|
|
560
|
-
}
|
|
561
|
-
} catch (error) {
|
|
562
|
-
_this.printLog(error);
|
|
563
|
-
}
|
|
552
|
+
var oldsuccess = para.success;
|
|
553
|
+
var olderror = para.error;
|
|
554
|
+
var errorTimer;
|
|
564
555
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
g.onreadystatechange = null;
|
|
570
|
-
g.onload = null;
|
|
571
|
-
g.onerror = null;
|
|
556
|
+
var abort = function abort() {
|
|
557
|
+
try {
|
|
558
|
+
if (_this.isObject(g) && g.abort) {
|
|
559
|
+
g.abort();
|
|
572
560
|
}
|
|
573
|
-
}
|
|
561
|
+
} catch (error) {
|
|
562
|
+
_this.printLog(error);
|
|
563
|
+
}
|
|
574
564
|
|
|
575
|
-
|
|
576
|
-
|
|
565
|
+
if (errorTimer) {
|
|
566
|
+
clearTimeout(errorTimer);
|
|
567
|
+
errorTimer = null;
|
|
568
|
+
para.error && para.error();
|
|
569
|
+
g.onreadystatechange = null;
|
|
570
|
+
g.onload = null;
|
|
571
|
+
g.onerror = null;
|
|
572
|
+
}
|
|
573
|
+
};
|
|
577
574
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
errorTimer = null;
|
|
581
|
-
}
|
|
582
|
-
};
|
|
575
|
+
para.success = function (data) {
|
|
576
|
+
oldsuccess && oldsuccess(data);
|
|
583
577
|
|
|
584
|
-
|
|
585
|
-
|
|
578
|
+
if (errorTimer) {
|
|
579
|
+
clearTimeout(errorTimer);
|
|
580
|
+
errorTimer = null;
|
|
581
|
+
}
|
|
582
|
+
};
|
|
586
583
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
errorTimer = null;
|
|
590
|
-
}
|
|
591
|
-
};
|
|
584
|
+
para.error = function (err, status) {
|
|
585
|
+
olderror && olderror(err, status);
|
|
592
586
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
587
|
+
if (errorTimer) {
|
|
588
|
+
clearTimeout(errorTimer);
|
|
589
|
+
errorTimer = null;
|
|
590
|
+
}
|
|
591
|
+
};
|
|
596
592
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
if (g.status >= 200 && g.status < 300 || g.status == 304) {
|
|
601
|
-
para.success && para.success(getJSON(g.responseText));
|
|
602
|
-
} else {
|
|
603
|
-
para.error && para.error(getJSON(g.responseText), g.status);
|
|
604
|
-
}
|
|
593
|
+
errorTimer = window.setTimeout(function () {
|
|
594
|
+
abort();
|
|
595
|
+
}, para.timeout);
|
|
605
596
|
|
|
606
|
-
|
|
607
|
-
|
|
597
|
+
g.onreadystatechange = function () {
|
|
598
|
+
try {
|
|
599
|
+
if (g.readyState == 4) {
|
|
600
|
+
if (g.status >= 200 && g.status < 300 || g.status == 304) {
|
|
601
|
+
para.success && para.success(getJSON(g.responseText));
|
|
602
|
+
} else {
|
|
603
|
+
para.error && para.error(getJSON(g.responseText), g.status);
|
|
608
604
|
}
|
|
609
|
-
|
|
605
|
+
|
|
610
606
|
g.onreadystatechange = null;
|
|
611
607
|
g.onload = null;
|
|
612
608
|
}
|
|
613
|
-
}
|
|
609
|
+
} catch (e) {
|
|
610
|
+
g.onreadystatechange = null;
|
|
611
|
+
g.onload = null;
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
614
|
|
|
615
|
-
|
|
615
|
+
g.open(para.type || "GET", para.url, true);
|
|
616
616
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
617
|
+
try {
|
|
618
|
+
if (para.credentials) {
|
|
619
|
+
g.withCredentials = true;
|
|
620
|
+
}
|
|
621
621
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
622
|
+
if (_this.isObject(para.header)) {
|
|
623
|
+
_this.each(para.header, function (v, i) {
|
|
624
|
+
g.setRequestHeader && g.setRequestHeader(i, v);
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
627
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
628
|
+
if (para.data) {
|
|
629
|
+
if (!para.cors) {
|
|
630
|
+
g.setRequestHeader && g.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
|
631
|
+
}
|
|
632
632
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
}
|
|
633
|
+
if (para.contentType === "application/json") {
|
|
634
|
+
g.setRequestHeader && g.setRequestHeader("Content-type", "application/json; charset=UTF-8");
|
|
635
|
+
} else {
|
|
636
|
+
g.setRequestHeader && g.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
|
638
637
|
}
|
|
639
|
-
} catch (e) {
|
|
640
|
-
_this.printLog(e);
|
|
641
638
|
}
|
|
639
|
+
} catch (e) {
|
|
640
|
+
_this.printLog(e);
|
|
641
|
+
}
|
|
642
642
|
|
|
643
|
-
|
|
644
|
-
|
|
643
|
+
g.send(para.data || null);
|
|
644
|
+
};
|
|
645
645
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
return new XMLHttpRequest();
|
|
650
|
-
} else {
|
|
651
|
-
return null;
|
|
652
|
-
}
|
|
653
|
-
} else if (typeof window.XMLHttpRequest !== "undefined") {
|
|
646
|
+
this.xhr = function (cors) {
|
|
647
|
+
if (cors) {
|
|
648
|
+
if (typeof window.XMLHttpRequest !== "undefined" && "withCredentials" in new XMLHttpRequest()) {
|
|
654
649
|
return new XMLHttpRequest();
|
|
655
650
|
} else {
|
|
656
651
|
return null;
|
|
657
652
|
}
|
|
658
|
-
}
|
|
653
|
+
} else if (typeof window.XMLHttpRequest !== "undefined") {
|
|
654
|
+
return new XMLHttpRequest();
|
|
655
|
+
} else {
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
659
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
return Sys;
|
|
682
|
-
};
|
|
660
|
+
this.getUA = function () {
|
|
661
|
+
var Sys = {};
|
|
662
|
+
var ua = navigator.userAgent.toLowerCase();
|
|
663
|
+
var s;
|
|
664
|
+
|
|
665
|
+
if (s = ua.match(/opera.([\d.]+)/)) {
|
|
666
|
+
Sys.opera = Number(s[1].split(".")[0]);
|
|
667
|
+
} else if (s = ua.match(/msie ([\d.]+)/)) {
|
|
668
|
+
Sys.ie = Number(s[1].split(".")[0]);
|
|
669
|
+
} else if (s = ua.match(/edge.([\d.]+)/)) {
|
|
670
|
+
Sys.edge = Number(s[1].split(".")[0]);
|
|
671
|
+
} else if (s = ua.match(/firefox\/([\d.]+)/)) {
|
|
672
|
+
Sys.firefox = Number(s[1].split(".")[0]);
|
|
673
|
+
} else if (s = ua.match(/chrome\/([\d.]+)/)) {
|
|
674
|
+
Sys.chrome = Number(s[1].split(".")[0]);
|
|
675
|
+
} else if (s = ua.match(/version\/([\d.]+).*safari/)) {
|
|
676
|
+
Sys.safari = Number(s[1].match(/^\d*.\d*/));
|
|
677
|
+
} else if (s = ua.match(/trident\/([\d.]+)/)) {
|
|
678
|
+
Sys.ie = 11;
|
|
679
|
+
}
|
|
683
680
|
|
|
684
|
-
|
|
685
|
-
|
|
681
|
+
return Sys;
|
|
682
|
+
};
|
|
686
683
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
}
|
|
684
|
+
this.isSupportBeaconSend = function () {
|
|
685
|
+
var supported = false;
|
|
690
686
|
|
|
691
|
-
|
|
687
|
+
if ((typeof navigator === "undefined" ? "undefined" : _typeof(navigator)) !== "object" || typeof navigator.sendBeacon !== "function") {
|
|
688
|
+
return supported;
|
|
689
|
+
}
|
|
692
690
|
|
|
693
|
-
|
|
691
|
+
var Sys = _this.getUA();
|
|
694
692
|
|
|
695
|
-
|
|
696
|
-
var reg = /os [\d._]*/gi;
|
|
697
|
-
var verinfo = ua.match(reg);
|
|
698
|
-
var version = (verinfo + "").replace(/[^0-9|_.]/gi, "").replace(/_/gi, ".");
|
|
699
|
-
var ver = version.split(".");
|
|
693
|
+
var ua = navigator.userAgent.toLowerCase();
|
|
700
694
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
695
|
+
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) {
|
|
696
|
+
var reg = /os [\d._]*/gi;
|
|
697
|
+
var verinfo = ua.match(reg);
|
|
698
|
+
var version = (verinfo + "").replace(/[^0-9|_.]/gi, "").replace(/_/gi, ".");
|
|
699
|
+
var ver = version.split(".");
|
|
704
700
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
}
|
|
712
|
-
} else {
|
|
713
|
-
if (Sys.chrome > 38 || Sys.edge > 13 || Sys.firefox > 30 || Sys.opera > 25 || Sys.safari > 11.0) {
|
|
701
|
+
if (typeof Sys.safari === "undefined") {
|
|
702
|
+
Sys.safari = Number(ver[0]);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (ver[0] && +ver[0] < 13) {
|
|
706
|
+
if (Sys.chrome > 41 || Sys.firefox > 30 || Sys.opera > 25 || Sys.safari > 12) {
|
|
714
707
|
supported = true;
|
|
715
708
|
}
|
|
709
|
+
} else if (Sys.chrome > 41 || Sys.firefox > 30 || Sys.opera > 25 || Sys.safari > 11.3) {
|
|
710
|
+
supported = true;
|
|
711
|
+
}
|
|
712
|
+
} else {
|
|
713
|
+
if (Sys.chrome > 38 || Sys.edge > 13 || Sys.firefox > 30 || Sys.opera > 25 || Sys.safari > 11.0) {
|
|
714
|
+
supported = true;
|
|
716
715
|
}
|
|
716
|
+
}
|
|
717
717
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
718
|
+
return supported;
|
|
719
|
+
};
|
|
720
|
+
/**
|
|
721
|
+
* 节流函数
|
|
722
|
+
* @param func 需要节流的函数
|
|
723
|
+
* @param wait 等待时间
|
|
724
|
+
* @returns 节流后的函数
|
|
725
|
+
*/
|
|
726
726
|
|
|
727
727
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
728
|
+
this.throttle = function (func, wait) {
|
|
729
|
+
var timeout = null;
|
|
730
|
+
var previous = 0;
|
|
731
|
+
return function () {
|
|
732
|
+
var args = [];
|
|
733
733
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
734
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
735
|
+
args[_i] = arguments[_i];
|
|
736
|
+
}
|
|
737
737
|
|
|
738
|
-
|
|
739
|
-
|
|
738
|
+
var now = Date.now();
|
|
739
|
+
var remaining = wait - (now - previous);
|
|
740
740
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
741
|
+
if (remaining <= 0 || remaining > wait) {
|
|
742
|
+
if (timeout) {
|
|
743
|
+
clearTimeout(timeout);
|
|
744
|
+
timeout = null;
|
|
745
|
+
}
|
|
746
746
|
|
|
747
|
-
|
|
747
|
+
previous = now;
|
|
748
|
+
func.apply(void 0, args);
|
|
749
|
+
} else if (!timeout) {
|
|
750
|
+
timeout = window.setTimeout(function () {
|
|
751
|
+
previous = Date.now();
|
|
752
|
+
timeout = null;
|
|
748
753
|
func.apply(void 0, args);
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
previous = Date.now();
|
|
752
|
-
timeout = null;
|
|
753
|
-
func.apply(void 0, args);
|
|
754
|
-
}, remaining);
|
|
755
|
-
}
|
|
756
|
-
};
|
|
754
|
+
}, remaining);
|
|
755
|
+
}
|
|
757
756
|
};
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
757
|
+
};
|
|
758
|
+
/**
|
|
759
|
+
* 防抖函数
|
|
760
|
+
* @param func 需要防抖的函数
|
|
761
|
+
* @param wait 等待时间
|
|
762
|
+
* @returns 防抖后的函数
|
|
763
|
+
*/
|
|
764
764
|
|
|
765
765
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
766
|
+
this.debounce = function (func, wait) {
|
|
767
|
+
var timeout = null;
|
|
768
|
+
return function () {
|
|
769
|
+
var args = [];
|
|
770
770
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
771
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
772
|
+
args[_i] = arguments[_i];
|
|
773
|
+
}
|
|
774
774
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
775
|
+
if (timeout) {
|
|
776
|
+
clearTimeout(timeout);
|
|
777
|
+
}
|
|
778
778
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
};
|
|
779
|
+
timeout = window.setTimeout(function () {
|
|
780
|
+
func.apply(void 0, args);
|
|
781
|
+
}, wait);
|
|
783
782
|
};
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
}
|
|
805
|
-
return Promise.reject({
|
|
806
|
-
success: false,
|
|
807
|
-
message: "sendBeacon返回false"
|
|
808
|
-
});
|
|
809
|
-
}
|
|
783
|
+
};
|
|
784
|
+
/**
|
|
785
|
+
* beacon请求
|
|
786
|
+
* @param para SendBeacon参数
|
|
787
|
+
* @returns Promise<TrackingResponse>
|
|
788
|
+
*/
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
this.sendBeacon = function (para) {
|
|
792
|
+
if (_this.isSupportBeaconSend() === true) {
|
|
793
|
+
var headers = {
|
|
794
|
+
type: para.contentType
|
|
795
|
+
};
|
|
796
|
+
var blob = new Blob([JSON.stringify(para.data)], headers);
|
|
797
|
+
var supportType = navigator.sendBeacon(para.url, blob);
|
|
798
|
+
|
|
799
|
+
if (supportType) {
|
|
800
|
+
return Promise.resolve({
|
|
801
|
+
success: true,
|
|
802
|
+
message: "发送成功"
|
|
803
|
+
});
|
|
810
804
|
} else {
|
|
811
805
|
return Promise.reject({
|
|
812
806
|
success: false,
|
|
813
|
-
message: "
|
|
807
|
+
message: "sendBeacon返回false"
|
|
814
808
|
});
|
|
815
809
|
}
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
810
|
+
} else {
|
|
811
|
+
return Promise.reject({
|
|
812
|
+
success: false,
|
|
813
|
+
message: "不支持sendBeacon,发送失败!"
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
/**
|
|
818
|
+
* 获取设备唯一标识
|
|
819
|
+
* 基于浏览器指纹技术,通过收集浏览器特征生成唯一ID
|
|
820
|
+
* @returns 返回设备唯一标识字符串
|
|
821
|
+
*/
|
|
822
822
|
|
|
823
823
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
824
|
+
this.getDeviceId = function () {
|
|
825
|
+
// 获取已存储的设备ID
|
|
826
|
+
var storedDeviceId = _this.getCookie("device_id") || _this.getLocalStorage("device_id");
|
|
827
827
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
828
|
+
if (storedDeviceId) {
|
|
829
|
+
return storedDeviceId;
|
|
830
|
+
} // 收集浏览器指纹信息
|
|
831
831
|
|
|
832
832
|
|
|
833
|
-
|
|
833
|
+
var fingerprint = _this.collectFingerprint(); // 生成设备ID
|
|
834
834
|
|
|
835
835
|
|
|
836
|
-
|
|
836
|
+
var deviceId = _this.hashFingerprint(fingerprint); // 存储设备ID
|
|
837
837
|
|
|
838
838
|
|
|
839
|
-
|
|
839
|
+
_this.setCookie("device_id", deviceId, 365 * 2); // 存储2年
|
|
840
840
|
|
|
841
841
|
|
|
842
|
-
|
|
842
|
+
_this.setLocalStorage("device_id", deviceId);
|
|
843
843
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
844
|
+
return deviceId;
|
|
845
|
+
};
|
|
846
|
+
/**
|
|
847
|
+
* 收集浏览器指纹信息
|
|
848
|
+
* @returns 返回浏览器指纹对象
|
|
849
|
+
*/
|
|
850
850
|
|
|
851
851
|
|
|
852
|
-
|
|
853
|
-
|
|
852
|
+
this.collectFingerprint = function () {
|
|
853
|
+
var fingerprint = {}; // 1. 用户代理
|
|
854
854
|
|
|
855
|
-
|
|
855
|
+
fingerprint.userAgent = navigator.userAgent; // 2. 屏幕信息
|
|
856
856
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
857
|
+
fingerprint.screenWidth = screen.width;
|
|
858
|
+
fingerprint.screenHeight = screen.height;
|
|
859
|
+
fingerprint.colorDepth = screen.colorDepth;
|
|
860
|
+
fingerprint.pixelDepth = screen.pixelDepth; // 3. 时区
|
|
861
861
|
|
|
862
|
-
|
|
863
|
-
|
|
862
|
+
fingerprint.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
863
|
+
fingerprint.timezoneOffset = new Date().getTimezoneOffset(); // 4. 语言设置
|
|
864
864
|
|
|
865
|
-
|
|
866
|
-
|
|
865
|
+
fingerprint.language = navigator.language;
|
|
866
|
+
fingerprint.languages = Array.from(navigator.languages); // 5. 平台信息
|
|
867
867
|
|
|
868
|
-
|
|
868
|
+
fingerprint.platform = navigator.platform; // 6. WebGL 指纹
|
|
869
869
|
|
|
870
|
-
|
|
870
|
+
fingerprint.webgl = _this.getWebGLFingerprint(); // 7. Canvas 指纹
|
|
871
871
|
|
|
872
|
-
|
|
872
|
+
fingerprint.canvas = _this.getCanvasFingerprint(); // 8. 音频上下文指纹
|
|
873
873
|
|
|
874
|
-
|
|
874
|
+
fingerprint.audio = _this.getAudioFingerprint(); // 9. 字体检测
|
|
875
875
|
|
|
876
|
-
|
|
876
|
+
fingerprint.fonts = _this.getFontFingerprint(); // 10. 插件信息
|
|
877
877
|
|
|
878
|
-
|
|
878
|
+
fingerprint.plugins = _this.getPluginsFingerprint(); // 11. 存储检测
|
|
879
879
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
880
|
+
fingerprint.localStorage = _this.hasLocalStorage();
|
|
881
|
+
fingerprint.sessionStorage = _this.hasSessionStorage();
|
|
882
|
+
fingerprint.indexedDB = _this.hasIndexedDB(); // 12. 硬件信息
|
|
883
883
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
884
|
+
fingerprint.hardwareConcurrency = navigator.hardwareConcurrency;
|
|
885
|
+
fingerprint.deviceMemory = navigator.deviceMemory;
|
|
886
|
+
fingerprint.maxTouchPoints = navigator.maxTouchPoints; // 13. 连接信息
|
|
887
887
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
888
|
+
fingerprint.connection = _this.getConnectionFingerprint();
|
|
889
|
+
return fingerprint;
|
|
890
|
+
};
|
|
891
|
+
/**
|
|
892
|
+
* 获取WebGL指纹
|
|
893
|
+
* @returns WebGL指纹字符串
|
|
894
|
+
*/
|
|
895
895
|
|
|
896
896
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
897
|
+
this.getWebGLFingerprint = function () {
|
|
898
|
+
try {
|
|
899
|
+
var canvas = document.createElement("canvas");
|
|
900
|
+
var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
|
901
|
+
if (!gl) return "not-supported";
|
|
902
|
+
var debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
|
|
903
|
+
var vendor = debugInfo ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : "unknown";
|
|
904
|
+
var renderer = debugInfo ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : "unknown";
|
|
905
|
+
return vendor + "|" + renderer;
|
|
906
|
+
} catch (e) {
|
|
907
|
+
return "error";
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
/**
|
|
911
|
+
* 获取Canvas指纹
|
|
912
|
+
* 注意:使用固定的尺寸和绘制参数,确保在不同时间生成一致的指纹
|
|
913
|
+
* @returns Canvas指纹字符串
|
|
914
|
+
*/
|
|
914
915
|
|
|
915
916
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
917
|
+
this.getCanvasFingerprint = function () {
|
|
918
|
+
try {
|
|
919
|
+
// 使用固定的 canvas 尺寸,确保一致性
|
|
920
|
+
var canvas = document.createElement("canvas");
|
|
921
|
+
canvas.width = 200;
|
|
922
|
+
canvas.height = 50;
|
|
923
|
+
var ctx = canvas.getContext("2d");
|
|
924
|
+
if (!ctx) return "not-supported"; // 使用固定的绘制参数,确保每次生成一致
|
|
925
|
+
|
|
926
|
+
ctx.textBaseline = "top";
|
|
927
|
+
ctx.font = "14px Arial";
|
|
928
|
+
ctx.fillStyle = "#f60";
|
|
929
|
+
ctx.fillRect(125, 1, 62, 20);
|
|
930
|
+
ctx.fillStyle = "#069";
|
|
931
|
+
ctx.fillText("Canvas fingerprint", 2, 15);
|
|
932
|
+
ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
|
|
933
|
+
ctx.fillText("Canvas fingerprint", 4, 17); // 取后50个字符作为指纹
|
|
934
|
+
|
|
935
|
+
return canvas.toDataURL().slice(-50);
|
|
936
|
+
} catch (e) {
|
|
937
|
+
return "error";
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
/**
|
|
941
|
+
* 获取音频上下文指纹
|
|
942
|
+
* 注意:只使用稳定的 sampleRate,不使用 currentTime(会随时间变化)
|
|
943
|
+
* @returns 音频指纹字符串
|
|
944
|
+
*/
|
|
939
945
|
|
|
940
946
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
var oscillator = context.createOscillator();
|
|
947
|
-
var analyser = context.createAnalyser();
|
|
948
|
-
var gain = context.createGain();
|
|
949
|
-
var scriptProcessor = context.createScriptProcessor(4096, 1, 1);
|
|
950
|
-
oscillator.type = 'triangle';
|
|
951
|
-
oscillator.frequency.value = 10000;
|
|
952
|
-
gain.gain.value = 0;
|
|
953
|
-
oscillator.connect(analyser);
|
|
954
|
-
analyser.connect(scriptProcessor);
|
|
955
|
-
scriptProcessor.connect(gain);
|
|
956
|
-
gain.connect(context.destination);
|
|
957
|
-
oscillator.start(0);
|
|
958
|
-
var fingerprint = context.sampleRate + '|' + context.currentTime;
|
|
959
|
-
oscillator.stop();
|
|
960
|
-
context.close();
|
|
961
|
-
return fingerprint;
|
|
962
|
-
} catch (e) {
|
|
963
|
-
return 'error';
|
|
964
|
-
}
|
|
965
|
-
};
|
|
966
|
-
/**
|
|
967
|
-
* 获取字体指纹
|
|
968
|
-
* @returns 字体指纹字符串
|
|
969
|
-
*/
|
|
947
|
+
this.getAudioFingerprint = function () {
|
|
948
|
+
try {
|
|
949
|
+
var AudioContextClass = window.AudioContext || window.webkitAudioContext;
|
|
950
|
+
if (!AudioContextClass) return "not-supported";
|
|
951
|
+
var context = new AudioContextClass(); // 只使用稳定的 sampleRate,不使用 currentTime(会随时间变化导致指纹不一致)
|
|
970
952
|
|
|
953
|
+
var fingerprint = String(context.sampleRate || 0);
|
|
954
|
+
context.close();
|
|
955
|
+
return fingerprint;
|
|
956
|
+
} catch (e) {
|
|
957
|
+
return "error";
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
/**
|
|
961
|
+
* 获取字体指纹
|
|
962
|
+
* @returns 字体指纹字符串
|
|
963
|
+
*/
|
|
971
964
|
|
|
972
|
-
this.getFontFingerprint = function () {
|
|
973
|
-
try {
|
|
974
|
-
var baseFonts_1 = ['monospace', 'sans-serif', 'serif'];
|
|
975
|
-
var testString_1 = 'mmmmmmmmmmlli';
|
|
976
|
-
var testSize_1 = '72px';
|
|
977
|
-
var canvas = document.createElement('canvas');
|
|
978
|
-
var ctx_1 = canvas.getContext('2d');
|
|
979
|
-
if (!ctx_1) return 'not-supported';
|
|
980
|
-
var detectedFonts_1 = []; // 测试字体列表
|
|
981
|
-
|
|
982
|
-
var fonts = ['Arial', 'Arial Black', 'Comic Sans MS', 'Courier New', 'Georgia', 'Helvetica', 'Impact', 'Times New Roman', 'Trebuchet MS', 'Verdana']; // 获取基准宽度
|
|
983
|
-
|
|
984
|
-
var baseWidths_1 = {};
|
|
985
|
-
baseFonts_1.forEach(function (font) {
|
|
986
|
-
ctx_1.font = testSize_1 + " " + font;
|
|
987
|
-
baseWidths_1[font] = ctx_1.measureText(testString_1).width;
|
|
988
|
-
}); // 测试每个字体
|
|
989
|
-
|
|
990
|
-
fonts.forEach(function (font) {
|
|
991
|
-
var detected = false;
|
|
992
|
-
baseFonts_1.forEach(function (baseFont) {
|
|
993
|
-
ctx_1.font = testSize_1 + " '" + font + "', " + baseFont;
|
|
994
|
-
var width = ctx_1.measureText(testString_1).width;
|
|
995
|
-
|
|
996
|
-
if (width !== baseWidths_1[baseFont]) {
|
|
997
|
-
detected = true;
|
|
998
|
-
}
|
|
999
|
-
});
|
|
1000
965
|
|
|
1001
|
-
|
|
1002
|
-
|
|
966
|
+
this.getFontFingerprint = function () {
|
|
967
|
+
try {
|
|
968
|
+
var baseFonts_1 = ["monospace", "sans-serif", "serif"];
|
|
969
|
+
var testString_1 = "mmmmmmmmmmlli";
|
|
970
|
+
var testSize_1 = "72px";
|
|
971
|
+
var canvas = document.createElement("canvas");
|
|
972
|
+
var ctx_1 = canvas.getContext("2d");
|
|
973
|
+
if (!ctx_1) return "not-supported";
|
|
974
|
+
var detectedFonts_1 = []; // 测试字体列表
|
|
975
|
+
|
|
976
|
+
var fonts = ["Arial", "Arial Black", "Comic Sans MS", "Courier New", "Georgia", "Helvetica", "Impact", "Times New Roman", "Trebuchet MS", "Verdana"]; // 获取基准宽度
|
|
977
|
+
|
|
978
|
+
var baseWidths_1 = {};
|
|
979
|
+
baseFonts_1.forEach(function (font) {
|
|
980
|
+
ctx_1.font = testSize_1 + " " + font;
|
|
981
|
+
baseWidths_1[font] = ctx_1.measureText(testString_1).width;
|
|
982
|
+
}); // 测试每个字体
|
|
983
|
+
|
|
984
|
+
fonts.forEach(function (font) {
|
|
985
|
+
var detected = false;
|
|
986
|
+
baseFonts_1.forEach(function (baseFont) {
|
|
987
|
+
ctx_1.font = testSize_1 + " '" + font + "', " + baseFont;
|
|
988
|
+
var width = ctx_1.measureText(testString_1).width;
|
|
989
|
+
|
|
990
|
+
if (width !== baseWidths_1[baseFont]) {
|
|
991
|
+
detected = true;
|
|
1003
992
|
}
|
|
1004
993
|
});
|
|
1005
|
-
return detectedFonts_1.join(',');
|
|
1006
|
-
} catch (e) {
|
|
1007
|
-
return 'error';
|
|
1008
|
-
}
|
|
1009
|
-
};
|
|
1010
|
-
/**
|
|
1011
|
-
* 获取插件指纹
|
|
1012
|
-
* @returns 插件指纹字符串
|
|
1013
|
-
*/
|
|
1014
994
|
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
try {
|
|
1018
|
-
var plugins = [];
|
|
1019
|
-
|
|
1020
|
-
if (navigator.plugins) {
|
|
1021
|
-
for (var i = 0; i < navigator.plugins.length; i++) {
|
|
1022
|
-
var plugin = navigator.plugins[i];
|
|
1023
|
-
|
|
1024
|
-
if (plugin) {
|
|
1025
|
-
plugins.push(plugin.name + "|" + plugin.description + "|" + plugin.filename);
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
995
|
+
if (detected) {
|
|
996
|
+
detectedFonts_1.push(font);
|
|
1028
997
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
this.hasLocalStorage = function () {
|
|
1042
|
-
try {
|
|
1043
|
-
var test = '__test__';
|
|
1044
|
-
localStorage.setItem(test, test);
|
|
1045
|
-
localStorage.removeItem(test);
|
|
1046
|
-
return true;
|
|
1047
|
-
} catch (e) {
|
|
1048
|
-
return false;
|
|
1049
|
-
}
|
|
1050
|
-
};
|
|
1051
|
-
/**
|
|
1052
|
-
* 检测sessionStorage支持
|
|
1053
|
-
* @returns 是否支持sessionStorage
|
|
1054
|
-
*/
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
this.hasSessionStorage = function () {
|
|
1058
|
-
try {
|
|
1059
|
-
var test = '__test__';
|
|
1060
|
-
sessionStorage.setItem(test, test);
|
|
1061
|
-
sessionStorage.removeItem(test);
|
|
1062
|
-
return true;
|
|
1063
|
-
} catch (e) {
|
|
1064
|
-
return false;
|
|
1065
|
-
}
|
|
1066
|
-
};
|
|
1067
|
-
/**
|
|
1068
|
-
* 检测IndexedDB支持
|
|
1069
|
-
* @returns 是否支持IndexedDB
|
|
1070
|
-
*/
|
|
998
|
+
});
|
|
999
|
+
return detectedFonts_1.join(",");
|
|
1000
|
+
} catch (e) {
|
|
1001
|
+
return "error";
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
/**
|
|
1005
|
+
* 获取插件指纹
|
|
1006
|
+
* @returns 插件指纹字符串
|
|
1007
|
+
*/
|
|
1071
1008
|
|
|
1072
1009
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
/**
|
|
1077
|
-
* 获取网络连接指纹
|
|
1078
|
-
* @returns 网络连接指纹字符串
|
|
1079
|
-
*/
|
|
1010
|
+
this.getPluginsFingerprint = function () {
|
|
1011
|
+
try {
|
|
1012
|
+
var plugins = [];
|
|
1080
1013
|
|
|
1014
|
+
if (navigator.plugins) {
|
|
1015
|
+
for (var i = 0; i < navigator.plugins.length; i++) {
|
|
1016
|
+
var plugin = navigator.plugins[i];
|
|
1081
1017
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
return connection.effectiveType + "|" + connection.downlink + "|" + connection.rtt;
|
|
1087
|
-
} catch (e) {
|
|
1088
|
-
return 'error';
|
|
1018
|
+
if (plugin) {
|
|
1019
|
+
plugins.push(plugin.name + "|" + plugin.description + "|" + plugin.filename);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1089
1022
|
}
|
|
1090
|
-
};
|
|
1091
|
-
/**
|
|
1092
|
-
* 将指纹信息哈希为唯一ID
|
|
1093
|
-
* @param fingerprint 指纹信息
|
|
1094
|
-
* @returns 返回哈希后的设备ID
|
|
1095
|
-
*/
|
|
1096
|
-
|
|
1097
1023
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
var char = fingerprintString.charCodeAt(i);
|
|
1108
|
-
hash1 = (hash1 << 5) + hash1 + char; // hash1 * 33 + char
|
|
1024
|
+
return plugins.join(";");
|
|
1025
|
+
} catch (e) {
|
|
1026
|
+
return "error";
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
/**
|
|
1030
|
+
* 检测localStorage支持
|
|
1031
|
+
* @returns 是否支持localStorage
|
|
1032
|
+
*/
|
|
1109
1033
|
|
|
1110
|
-
hash2 = (hash2 << 5) + hash2 + char; // hash2 * 33 + char
|
|
1111
|
-
} // 组合两个哈希值并转换为64位整数
|
|
1112
1034
|
|
|
1035
|
+
this.hasLocalStorage = function () {
|
|
1036
|
+
try {
|
|
1037
|
+
var test = "__test__";
|
|
1038
|
+
localStorage.setItem(test, test);
|
|
1039
|
+
localStorage.removeItem(test);
|
|
1040
|
+
return true;
|
|
1041
|
+
} catch (e) {
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
/**
|
|
1046
|
+
* 检测sessionStorage支持
|
|
1047
|
+
* @returns 是否支持sessionStorage
|
|
1048
|
+
*/
|
|
1113
1049
|
|
|
1114
|
-
var combinedHash = (hash1 >>> 0) * 4096 + (hash2 >>> 0); // 转换为36进制并添加前缀,增加长度和唯一性
|
|
1115
1050
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1051
|
+
this.hasSessionStorage = function () {
|
|
1052
|
+
try {
|
|
1053
|
+
var test = "__test__";
|
|
1054
|
+
sessionStorage.setItem(test, test);
|
|
1055
|
+
sessionStorage.removeItem(test);
|
|
1056
|
+
return true;
|
|
1057
|
+
} catch (e) {
|
|
1058
|
+
return false;
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1120
1061
|
/**
|
|
1121
|
-
*
|
|
1062
|
+
* 检测IndexedDB支持
|
|
1063
|
+
* @returns 是否支持IndexedDB
|
|
1122
1064
|
*/
|
|
1123
1065
|
|
|
1124
1066
|
|
|
1125
|
-
|
|
1126
|
-
|
|
1067
|
+
this.hasIndexedDB = function () {
|
|
1068
|
+
return "indexedDB" in window && indexedDB !== null;
|
|
1069
|
+
};
|
|
1070
|
+
/**
|
|
1071
|
+
* 获取网络连接指纹
|
|
1072
|
+
* @returns 网络连接指纹字符串
|
|
1073
|
+
*/
|
|
1127
1074
|
|
|
1128
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1129
|
-
rest[_i] = arguments[_i];
|
|
1130
|
-
}
|
|
1131
1075
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1076
|
+
this.getConnectionFingerprint = function () {
|
|
1077
|
+
try {
|
|
1078
|
+
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
|
1079
|
+
if (!connection) return "not-supported"; // 只使用稳定的 effectiveType,不使用 downlink 和 rtt(会随网络状态变化导致指纹不一致)
|
|
1135
1080
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
} catch (e) {
|
|
1140
|
-
console.log(rest[0]);
|
|
1141
|
-
}
|
|
1081
|
+
return connection.effectiveType || "unknown";
|
|
1082
|
+
} catch (e) {
|
|
1083
|
+
return "error";
|
|
1142
1084
|
}
|
|
1143
1085
|
};
|
|
1144
1086
|
/**
|
|
1145
|
-
*
|
|
1087
|
+
* 将指纹信息哈希为唯一ID
|
|
1088
|
+
* @param fingerprint 指纹信息
|
|
1089
|
+
* @returns 返回哈希后的设备ID
|
|
1146
1090
|
*/
|
|
1147
1091
|
|
|
1148
1092
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
} else {
|
|
1153
|
-
return toString.call(obj) == "[object Object]";
|
|
1154
|
-
}
|
|
1155
|
-
};
|
|
1093
|
+
this.hashFingerprint = function (fingerprint) {
|
|
1094
|
+
// 将指纹对象转换为字符串
|
|
1095
|
+
var fingerprintString = JSON.stringify(fingerprint, Object.keys(fingerprint).sort()); // 使用更强大的哈希算法生成ID
|
|
1156
1096
|
|
|
1157
|
-
|
|
1158
|
-
return obj === void 0;
|
|
1159
|
-
};
|
|
1097
|
+
var hash1 = 5381; // DJB2哈希算法的初始值
|
|
1160
1098
|
|
|
1161
|
-
|
|
1162
|
-
return toString.call(obj) == "[object String]";
|
|
1163
|
-
};
|
|
1099
|
+
var hash2 = 52711; // 第二个哈希的初始值
|
|
1164
1100
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1101
|
+
for (var i = 0; i < fingerprintString.length; i++) {
|
|
1102
|
+
var char = fingerprintString.charCodeAt(i);
|
|
1103
|
+
hash1 = (hash1 << 5) + hash1 + char; // hash1 * 33 + char
|
|
1168
1104
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
};
|
|
1105
|
+
hash2 = (hash2 << 5) + hash2 + char; // hash2 * 33 + char
|
|
1106
|
+
} // 将两个32位哈希值转换为十六进制字符串并拼接,保持完整长度
|
|
1172
1107
|
|
|
1173
|
-
WebTrackingTools.prototype.isNumber = function (obj) {
|
|
1174
|
-
return toString.call(obj) == "[object Number]" && /[\d\.]+/.test(String(obj));
|
|
1175
|
-
};
|
|
1176
1108
|
|
|
1177
|
-
|
|
1178
|
-
return !!(obj && obj.nodeType === 1);
|
|
1179
|
-
};
|
|
1109
|
+
var hash1Hex = (hash1 >>> 0).toString(16).padStart(8, '0'); // 32位 = 8个十六进制字符
|
|
1180
1110
|
|
|
1181
|
-
|
|
1182
|
-
if (!f) {
|
|
1183
|
-
return false;
|
|
1184
|
-
}
|
|
1111
|
+
var hash2Hex = (hash2 >>> 0).toString(16).padStart(8, '0'); // 32位 = 8个十六进制字符
|
|
1185
1112
|
|
|
1186
|
-
var
|
|
1187
|
-
return
|
|
1113
|
+
var deviceId = "fp_" + hash1Hex + hash2Hex;
|
|
1114
|
+
return deviceId;
|
|
1188
1115
|
};
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* 打印log
|
|
1119
|
+
*/
|
|
1189
1120
|
|
|
1190
|
-
WebTrackingTools.prototype.isJSONString = function (str) {
|
|
1191
|
-
if (!this.isString(str)) return false;
|
|
1192
1121
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
} catch (e) {
|
|
1196
|
-
return false;
|
|
1197
|
-
}
|
|
1122
|
+
WebTrackingTools.prototype.printLog = function () {
|
|
1123
|
+
var rest = [];
|
|
1198
1124
|
|
|
1199
|
-
|
|
1200
|
-
|
|
1125
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1126
|
+
rest[_i] = arguments[_i];
|
|
1127
|
+
}
|
|
1201
1128
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1129
|
+
if (this.isObject(rest[0])) {
|
|
1130
|
+
rest[0] = this.formatJsonString(rest[0]);
|
|
1131
|
+
}
|
|
1204
1132
|
|
|
1133
|
+
if ((typeof console === "undefined" ? "undefined" : _typeof(console)) === "object" && console.log) {
|
|
1205
1134
|
try {
|
|
1206
|
-
|
|
1135
|
+
return console.log.apply(console, rest);
|
|
1207
1136
|
} catch (e) {
|
|
1208
|
-
|
|
1137
|
+
console.log(rest[0]);
|
|
1209
1138
|
}
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
/**
|
|
1142
|
+
* @description 检验是否是对象
|
|
1143
|
+
*/
|
|
1210
1144
|
|
|
1211
|
-
return result;
|
|
1212
|
-
};
|
|
1213
|
-
|
|
1214
|
-
WebTrackingTools.prototype.getMainHost = function () {
|
|
1215
|
-
var key = "mh_" + Math.random();
|
|
1216
|
-
var keyR = new RegExp("(^|;)\\s*" + key + "=12345");
|
|
1217
|
-
var expiredTime = new Date(0);
|
|
1218
|
-
var domain = document.domain;
|
|
1219
|
-
var domainList = domain.split(".");
|
|
1220
|
-
var urlItems = []; // 主域名一定会有两部分组成
|
|
1221
|
-
|
|
1222
|
-
urlItems.unshift(domainList.pop()); // 慢慢从后往前测试
|
|
1223
1145
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1146
|
+
WebTrackingTools.prototype.isObject = function (obj) {
|
|
1147
|
+
if (obj == null) {
|
|
1148
|
+
return false;
|
|
1149
|
+
} else {
|
|
1150
|
+
return toString.call(obj) == "[object Object]";
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1229
1153
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1154
|
+
WebTrackingTools.prototype.isUndefined = function (obj) {
|
|
1155
|
+
return obj === void 0;
|
|
1156
|
+
};
|
|
1235
1157
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1158
|
+
WebTrackingTools.prototype.isString = function (obj) {
|
|
1159
|
+
return toString.call(obj) == "[object String]";
|
|
1160
|
+
};
|
|
1238
1161
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1162
|
+
WebTrackingTools.prototype.isDate = function (obj) {
|
|
1163
|
+
return toString.call(obj) == "[object Date]";
|
|
1164
|
+
};
|
|
1241
1165
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
__extends(WebTracking, _super);
|
|
1166
|
+
WebTrackingTools.prototype.isBoolean = function (obj) {
|
|
1167
|
+
return toString.call(obj) == "[object Boolean]";
|
|
1168
|
+
};
|
|
1246
1169
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1170
|
+
WebTrackingTools.prototype.isNumber = function (obj) {
|
|
1171
|
+
return toString.call(obj) == "[object Number]" && /[\d\.]+/.test(String(obj));
|
|
1172
|
+
};
|
|
1249
1173
|
|
|
1174
|
+
WebTrackingTools.prototype.isElement = function (obj) {
|
|
1175
|
+
return !!(obj && obj.nodeType === 1);
|
|
1176
|
+
};
|
|
1250
1177
|
|
|
1251
|
-
|
|
1178
|
+
WebTrackingTools.prototype.isFunction = function (f) {
|
|
1179
|
+
if (!f) {
|
|
1180
|
+
return false;
|
|
1181
|
+
}
|
|
1252
1182
|
|
|
1253
|
-
|
|
1183
|
+
var type = toString.call(f);
|
|
1184
|
+
return type == "[object Function]" || type == "[object AsyncFunction]";
|
|
1185
|
+
};
|
|
1254
1186
|
|
|
1255
|
-
|
|
1187
|
+
WebTrackingTools.prototype.isJSONString = function (str) {
|
|
1188
|
+
if (!this.isString(str)) return false;
|
|
1256
1189
|
|
|
1257
|
-
|
|
1190
|
+
try {
|
|
1191
|
+
JSON.parse(str);
|
|
1192
|
+
} catch (e) {
|
|
1193
|
+
return false;
|
|
1194
|
+
}
|
|
1258
1195
|
|
|
1259
|
-
|
|
1196
|
+
return true;
|
|
1197
|
+
};
|
|
1260
1198
|
|
|
1261
|
-
|
|
1199
|
+
WebTrackingTools.prototype._decodeURIComponent = function (val) {
|
|
1200
|
+
var result = val;
|
|
1262
1201
|
|
|
1263
|
-
|
|
1202
|
+
try {
|
|
1203
|
+
result = decodeURIComponent(val);
|
|
1204
|
+
} catch (e) {
|
|
1205
|
+
result = val;
|
|
1206
|
+
}
|
|
1264
1207
|
|
|
1265
|
-
|
|
1208
|
+
return result;
|
|
1209
|
+
};
|
|
1266
1210
|
|
|
1267
|
-
|
|
1211
|
+
WebTrackingTools.prototype.getMainHost = function () {
|
|
1212
|
+
var key = "mh_" + Math.random();
|
|
1213
|
+
var keyR = new RegExp("(^|;)\\s*" + key + "=12345");
|
|
1214
|
+
var expiredTime = new Date(0);
|
|
1215
|
+
var domain = document.domain;
|
|
1216
|
+
var domainList = domain.split(".");
|
|
1217
|
+
var urlItems = []; // 主域名一定会有两部分组成
|
|
1268
1218
|
|
|
1269
|
-
|
|
1219
|
+
urlItems.unshift(domainList.pop()); // 慢慢从后往前测试
|
|
1270
1220
|
|
|
1271
|
-
|
|
1221
|
+
while (domainList.length) {
|
|
1222
|
+
urlItems.unshift(domainList.pop());
|
|
1223
|
+
var mainHost = urlItems.join(".");
|
|
1224
|
+
var cookie = key + "=" + 12345 + ";domain=." + mainHost;
|
|
1225
|
+
document.cookie = cookie; //如果cookie存在,则说明域名合法
|
|
1272
1226
|
|
|
1273
|
-
|
|
1227
|
+
if (keyR.test(document.cookie)) {
|
|
1228
|
+
document.cookie = cookie + ";expires=" + expiredTime;
|
|
1229
|
+
return mainHost;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1274
1232
|
|
|
1275
|
-
|
|
1233
|
+
return domain;
|
|
1234
|
+
};
|
|
1276
1235
|
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
WebClick: "Web 元素点击",
|
|
1280
|
-
PageRetained: "Web 页面浏览时长",
|
|
1281
|
-
CustomTrack: "Web 自定义代码上报"
|
|
1282
|
-
};
|
|
1283
|
-
/**
|
|
1284
|
-
* @description 初始化函数
|
|
1285
|
-
* @param {object} InitParams [初始化参数]
|
|
1286
|
-
*/
|
|
1236
|
+
return WebTrackingTools;
|
|
1237
|
+
}();
|
|
1287
1238
|
|
|
1288
|
-
|
|
1289
|
-
|
|
1239
|
+
var WebTracking =
|
|
1240
|
+
/** @class */
|
|
1241
|
+
function (_super) {
|
|
1242
|
+
__extends(WebTracking, _super);
|
|
1290
1243
|
|
|
1291
|
-
|
|
1292
|
-
|
|
1244
|
+
function WebTracking() {
|
|
1245
|
+
var _this = _super.call(this) || this; // 批量发送定时器
|
|
1293
1246
|
|
|
1294
|
-
if (initParams.pageKey) {
|
|
1295
|
-
_this.pageKey = initParams.pageKey;
|
|
1296
|
-
_this.useCustomPageKey = true;
|
|
1297
|
-
} else {
|
|
1298
|
-
_this.pageKey = pathname.replace(/\//g, "_").substring(1);
|
|
1299
|
-
_this.useCustomPageKey = false;
|
|
1300
|
-
}
|
|
1301
1247
|
|
|
1302
|
-
|
|
1248
|
+
_this.batchTimer = null; // LocalStorage 存储 key
|
|
1303
1249
|
|
|
1304
|
-
|
|
1250
|
+
_this.BATCH_QUEUE_STORAGE_KEY = "web_tracking_batch_queue"; // 是否使用自定义 pageKey(如果为 true,路由变化时不会自动更新 pageKey)
|
|
1305
1251
|
|
|
1306
|
-
|
|
1307
|
-
_this.userInfo = initParams.userInfo;
|
|
1308
|
-
}
|
|
1252
|
+
_this.useCustomPageKey = false; // 页面卸载监听器是否已设置
|
|
1309
1253
|
|
|
1310
|
-
|
|
1254
|
+
_this.isUnloadListenerSetup = false; // 定时上报页面停留时长的定时器
|
|
1311
1255
|
|
|
1256
|
+
_this.pageDurationTimer = null; // LocalStorage 存储 key(待发送请求)
|
|
1312
1257
|
|
|
1313
|
-
|
|
1314
|
-
_this.restoreBatchQueueFromStorage();
|
|
1315
|
-
} // 恢复待发送的单个请求
|
|
1258
|
+
_this.PENDING_REQUESTS_STORAGE_KEY = "web_tracking_pending_requests"; // 待发送请求队列最大大小(默认值,可通过配置覆盖)
|
|
1316
1259
|
|
|
1260
|
+
_this.DEFAULT_PENDING_REQUESTS_MAX_SIZE = 50; // LocalStorage 最大大小限制(4MB)
|
|
1317
1261
|
|
|
1318
|
-
|
|
1262
|
+
_this.MAX_STORAGE_SIZE = 4 * 1024 * 1024; // IntersectionObserver 实例
|
|
1319
1263
|
|
|
1264
|
+
_this.exposureObserver = null; // 曝光元素映射
|
|
1320
1265
|
|
|
1321
|
-
|
|
1322
|
-
};
|
|
1323
|
-
/**
|
|
1324
|
-
* TODO: 需要判断有哪些不能被预制的参数
|
|
1325
|
-
* @description 预置参数
|
|
1326
|
-
* @param {object} PresetParams [预置参数]
|
|
1327
|
-
*/
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
_this.preset = function (presetParams) {
|
|
1331
|
-
if (presetParams instanceof Object) {
|
|
1332
|
-
// 处理 pageKey 特殊逻辑
|
|
1333
|
-
if (presetParams.pageKey !== undefined) {
|
|
1334
|
-
if (presetParams.pageKey === null || presetParams.pageKey === '') {
|
|
1335
|
-
// 恢复自动生成
|
|
1336
|
-
_this.useCustomPageKey = false;
|
|
1337
|
-
var pathname = window.location.pathname;
|
|
1338
|
-
_this.pageKey = pathname.replace(/\//g, "_").substring(1);
|
|
1339
|
-
} else {
|
|
1340
|
-
_this.pageKey = presetParams.pageKey;
|
|
1341
|
-
_this.useCustomPageKey = true;
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1266
|
+
_this.exposureElementsMap = new Map(); // 用户信息
|
|
1344
1267
|
|
|
1345
|
-
|
|
1346
|
-
// 跳过 pageKey,因为已经单独处理
|
|
1347
|
-
if (key === 'pageKey') return;
|
|
1268
|
+
_this.userInfo = null; // 当前路由
|
|
1348
1269
|
|
|
1349
|
-
|
|
1350
|
-
// TODO:后面加一些校验
|
|
1351
|
-
_this.initConfig[key] = val;
|
|
1352
|
-
}
|
|
1353
|
-
});
|
|
1354
|
-
}
|
|
1270
|
+
_this.currentUrl = ""; // 页面唯一标识,取当前路由 window.location.pathname.replace(/\//g, '_').substr(1)
|
|
1355
1271
|
|
|
1356
|
-
|
|
1357
|
-
_this.printLog("当前 server_url 为空或不正确,只在控制台打印日志,network 中不会发数据,请配置正确的 server_url!");
|
|
1272
|
+
_this.pageKey = ""; // 设备唯一标识
|
|
1358
1273
|
|
|
1359
|
-
|
|
1360
|
-
} // 如果启用了全埋点或启用了 data-part-key 点击追踪
|
|
1274
|
+
_this.deviceId = ""; // 上传事件描述
|
|
1361
1275
|
|
|
1276
|
+
_this.eventDescMap = {
|
|
1277
|
+
PageView: "Web 浏览页面",
|
|
1278
|
+
WebClick: "Web 元素点击",
|
|
1279
|
+
PageRetained: "Web 页面浏览时长",
|
|
1280
|
+
CustomTrack: "Web 自定义代码上报",
|
|
1281
|
+
WebExposure: "Web 元素曝光"
|
|
1282
|
+
};
|
|
1283
|
+
/**
|
|
1284
|
+
* @description 初始化函数
|
|
1285
|
+
* @param {object} InitParams [初始化参数]
|
|
1286
|
+
*/
|
|
1362
1287
|
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
_this.listener();
|
|
1366
|
-
} else {
|
|
1367
|
-
// 取消监听
|
|
1368
|
-
_this.unlistener();
|
|
1369
|
-
}
|
|
1370
|
-
};
|
|
1371
|
-
/**
|
|
1372
|
-
* 用户登录
|
|
1373
|
-
*/
|
|
1288
|
+
_this.init = function (initParams) {
|
|
1289
|
+
_this.preset(initParams);
|
|
1374
1290
|
|
|
1291
|
+
var pathname = window.location.pathname;
|
|
1292
|
+
_this.currentUrl = window.location.href; // 如果传入了自定义 pageKey,使用自定义值,否则自动生成
|
|
1375
1293
|
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1294
|
+
if (initParams.pageKey) {
|
|
1295
|
+
_this.pageKey = initParams.pageKey;
|
|
1296
|
+
_this.useCustomPageKey = true;
|
|
1297
|
+
} else {
|
|
1298
|
+
_this.pageKey = pathname.replace(/\//g, "_").substring(1);
|
|
1299
|
+
_this.useCustomPageKey = false;
|
|
1300
|
+
}
|
|
1383
1301
|
|
|
1302
|
+
_this.systemsInfo = _this.getSystemsInfo(initParams.platform); // 获取设备ID
|
|
1384
1303
|
|
|
1385
|
-
_this.
|
|
1386
|
-
// 如果已有设备ID,直接返回
|
|
1387
|
-
if (_this.deviceId) {
|
|
1388
|
-
return _this.deviceId;
|
|
1389
|
-
} // 获取已存储的设备ID
|
|
1304
|
+
_this.deviceId = _this.getDeviceId(); // 如果传入了 userInfo,设置用户信息
|
|
1390
1305
|
|
|
1306
|
+
if (initParams.userInfo && _this.isObject(initParams.userInfo)) {
|
|
1307
|
+
_this.userInfo = initParams.userInfo;
|
|
1308
|
+
}
|
|
1391
1309
|
|
|
1392
|
-
|
|
1310
|
+
_this.setCookie("retainedStartTime", _this.getTimeStamp()); // 如果启用了批量发送,从 LocalStorage 恢复队列
|
|
1393
1311
|
|
|
1394
|
-
if (storedDeviceId) {
|
|
1395
|
-
_this.deviceId = storedDeviceId;
|
|
1396
|
-
return _this.deviceId;
|
|
1397
|
-
} // 收集浏览器指纹信息
|
|
1398
1312
|
|
|
1313
|
+
if (_this.initConfig.batchSend) {
|
|
1314
|
+
_this.restoreBatchQueueFromStorage();
|
|
1315
|
+
} // 恢复待发送的单个请求
|
|
1399
1316
|
|
|
1400
|
-
var fingerprint = _this.collectFingerprint(); // 生成设备ID
|
|
1401
1317
|
|
|
1318
|
+
_this.restorePendingRequestsFromStorage(); // 无论是否启用批量发送,都需要监听页面卸载事件,确保数据发送
|
|
1402
1319
|
|
|
1403
|
-
var deviceId = _this.hashFingerprint(fingerprint); // 存储设备ID(统一使用2年过期时间,与tools.ts保持一致)
|
|
1404
1320
|
|
|
1321
|
+
_this.setupBeforeUnloadListener(); // 如果启用了定时上报,启动定时器
|
|
1405
1322
|
|
|
1406
|
-
_this.setCookie("device_id", deviceId, 365 * 2); // 存储2年
|
|
1407
1323
|
|
|
1324
|
+
if (_this.initConfig.autoTrackPageDurationInterval) {
|
|
1325
|
+
_this.startPageDurationTimer();
|
|
1326
|
+
} // 如果启用了曝光监听,初始化曝光监听
|
|
1408
1327
|
|
|
1409
|
-
_this.setLocalStorage("device_id", deviceId);
|
|
1410
1328
|
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
_this.resetDeviceId = function () {
|
|
1422
|
-
// 清除cookie和localStorage中的设备ID
|
|
1423
|
-
document.cookie = "device_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
|
1424
|
-
localStorage.removeItem("device_id"); // 清除内存中的设备ID
|
|
1425
|
-
|
|
1426
|
-
_this.deviceId = ""; // 重新生成设备ID
|
|
1329
|
+
if (_this.initConfig.autoTrackExposure) {
|
|
1330
|
+
_this.initExposureObserver();
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
/**
|
|
1334
|
+
* @description 预置参数
|
|
1335
|
+
* @param {object} PresetParams [预置参数]
|
|
1336
|
+
*/
|
|
1427
1337
|
|
|
1428
|
-
var newDeviceId = _this.getDeviceId();
|
|
1429
1338
|
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
partkey = _a.partkey,
|
|
1443
|
-
business = _a.business,
|
|
1444
|
-
header = _a.header;
|
|
1445
|
-
|
|
1446
|
-
var params = _this.getParams({
|
|
1447
|
-
desc: desc,
|
|
1448
|
-
event: "CustomTrack",
|
|
1449
|
-
itemKey: _this.getItemKey(partkey, pageKey),
|
|
1450
|
-
privateParamMap: {
|
|
1451
|
-
business: business
|
|
1339
|
+
_this.preset = function (presetParams) {
|
|
1340
|
+
if (presetParams instanceof Object) {
|
|
1341
|
+
// 处理 pageKey 特殊逻辑
|
|
1342
|
+
if (presetParams.pageKey !== undefined) {
|
|
1343
|
+
if (presetParams.pageKey === null || presetParams.pageKey === '') {
|
|
1344
|
+
// 恢复自动生成
|
|
1345
|
+
_this.useCustomPageKey = false;
|
|
1346
|
+
var pathname = window.location.pathname;
|
|
1347
|
+
_this.pageKey = pathname.replace(/\//g, "_").substring(1);
|
|
1348
|
+
} else {
|
|
1349
|
+
_this.pageKey = presetParams.pageKey;
|
|
1350
|
+
_this.useCustomPageKey = true;
|
|
1452
1351
|
}
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
return _this.sendData(params, header);
|
|
1456
|
-
};
|
|
1457
|
-
/**
|
|
1458
|
-
* @description 监听全埋点事件
|
|
1459
|
-
*/
|
|
1352
|
+
}
|
|
1460
1353
|
|
|
1354
|
+
_this.each(presetParams, function (val, key) {
|
|
1355
|
+
// 跳过 pageKey,因为已经单独处理
|
|
1356
|
+
if (key === 'pageKey') return;
|
|
1461
1357
|
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
if (!!_this.initConfig.isTrackSinglePage) {
|
|
1466
|
-
_this.rewriteHistory();
|
|
1358
|
+
if (_this.initConfig.hasOwnProperty(key)) {
|
|
1359
|
+
// 参数验证
|
|
1360
|
+
var validationResult = _this.validateConfigParam(String(key), val);
|
|
1467
1361
|
|
|
1468
|
-
|
|
1362
|
+
if (validationResult.valid) {
|
|
1363
|
+
_this.initConfig[key] = val;
|
|
1364
|
+
} else {
|
|
1365
|
+
_this.printLog("\u914D\u7F6E\u53C2\u6570\u9A8C\u8BC1\u5931\u8D25: " + String(key) + " = " + val + ", \u539F\u56E0: " + validationResult.message);
|
|
1366
|
+
}
|
|
1469
1367
|
}
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1470
1370
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
});
|
|
1474
|
-
} // 如果启用了全埋点或启用了 data-part-key 点击追踪,监听点击事件
|
|
1371
|
+
if (!/^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-\(\)]*[\w@?^=%&/~+#-\(\)])?$/.test(_this.initConfig["serverUrl"])) {
|
|
1372
|
+
_this.printLog("当前 server_url 为空或不正确,只在控制台打印日志,network 中不会发数据,请配置正确的 server_url!");
|
|
1475
1373
|
|
|
1374
|
+
_this.initConfig["showLog"] = true;
|
|
1375
|
+
} // 如果启用了全埋点或启用了 data-part-key 点击追踪
|
|
1476
1376
|
|
|
1477
|
-
if (!!_this.initConfig.autoTrack || !!_this.initConfig.trackPartKeyClick) {
|
|
1478
|
-
_this.addEventListener(window, "click", _this.onClickCallback);
|
|
1479
|
-
}
|
|
1480
|
-
};
|
|
1481
|
-
/**
|
|
1482
|
-
* @description 取消全埋点事件
|
|
1483
|
-
*/
|
|
1484
1377
|
|
|
1378
|
+
if (!!_this.initConfig["autoTrack"] || !!_this.initConfig["trackPartKeyClick"]) {
|
|
1379
|
+
// 启用监听
|
|
1380
|
+
_this.listener();
|
|
1381
|
+
} else {
|
|
1382
|
+
// 取消监听
|
|
1383
|
+
_this.unlistener();
|
|
1384
|
+
} // 处理定时上报配置
|
|
1485
1385
|
|
|
1486
|
-
_this.unlistener = function () {
|
|
1487
|
-
if (!!_this.initConfig.isTrackSinglePage) {
|
|
1488
|
-
var historyPushState = window.history.pushState;
|
|
1489
|
-
var singlePageEvent = !!historyPushState ? "popstate" : "hashchange";
|
|
1490
1386
|
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1387
|
+
if (_this.initConfig.autoTrackPageDurationInterval) {
|
|
1388
|
+
_this.startPageDurationTimer();
|
|
1389
|
+
} else {
|
|
1390
|
+
_this.stopPageDurationTimer();
|
|
1391
|
+
} // 处理曝光监听配置
|
|
1495
1392
|
|
|
1496
|
-
_this.each(["load", "beforeunload"], function (historyType) {
|
|
1497
|
-
_this.removeEventListener(window, historyType, _this.onPageViewCallback);
|
|
1498
|
-
});
|
|
1499
1393
|
|
|
1500
|
-
|
|
1394
|
+
if (_this.initConfig.autoTrackExposure) {
|
|
1395
|
+
_this.initExposureObserver();
|
|
1396
|
+
} else {
|
|
1397
|
+
_this.stopExposureObserver();
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
/**
|
|
1401
|
+
* @description 验证配置参数
|
|
1402
|
+
* @param key 参数名
|
|
1403
|
+
* @param value 参数值
|
|
1404
|
+
* @returns 验证结果
|
|
1405
|
+
*/
|
|
1501
1406
|
|
|
1502
1407
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1408
|
+
_this.validateConfigParam = function (key, value) {
|
|
1409
|
+
switch (key) {
|
|
1410
|
+
case 'sampleRate':
|
|
1411
|
+
if (typeof value !== 'number' || value < 0 || value > 1) {
|
|
1412
|
+
return {
|
|
1413
|
+
valid: false,
|
|
1414
|
+
message: 'sampleRate 必须是 0-1 之间的数字'
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1508
1417
|
|
|
1418
|
+
break;
|
|
1509
1419
|
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
* @description 清空批量队列(包括 LocalStorage 中的数据)
|
|
1518
|
-
*/
|
|
1420
|
+
case 'sendTimeout':
|
|
1421
|
+
if (typeof value !== 'number' || value <= 0) {
|
|
1422
|
+
return {
|
|
1423
|
+
valid: false,
|
|
1424
|
+
message: 'sendTimeout 必须是大于 0 的数字'
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1519
1427
|
|
|
1428
|
+
break;
|
|
1520
1429
|
|
|
1521
|
-
|
|
1522
|
-
|
|
1430
|
+
case 'batchInterval':
|
|
1431
|
+
if (typeof value !== 'number' || value <= 0) {
|
|
1432
|
+
return {
|
|
1433
|
+
valid: false,
|
|
1434
|
+
message: 'batchInterval 必须是大于 0 的数字'
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1523
1437
|
|
|
1524
|
-
|
|
1438
|
+
break;
|
|
1525
1439
|
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
* @param autoUpdate 路由变化时是否自动更新(默认:false,使用自定义值后不再自动更新)
|
|
1534
|
-
*/
|
|
1440
|
+
case 'batchMaxSize':
|
|
1441
|
+
if (typeof value !== 'number' || value <= 0 || !Number.isInteger(value)) {
|
|
1442
|
+
return {
|
|
1443
|
+
valid: false,
|
|
1444
|
+
message: 'batchMaxSize 必须是大于 0 的整数'
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1535
1447
|
|
|
1448
|
+
break;
|
|
1536
1449
|
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1450
|
+
case 'pendingRequestsMaxSize':
|
|
1451
|
+
if (typeof value !== 'number' || value <= 0 || !Number.isInteger(value)) {
|
|
1452
|
+
return {
|
|
1453
|
+
valid: false,
|
|
1454
|
+
message: 'pendingRequestsMaxSize 必须是大于 0 的整数'
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1541
1457
|
|
|
1542
|
-
|
|
1543
|
-
// 恢复自动生成
|
|
1544
|
-
_this.useCustomPageKey = false;
|
|
1545
|
-
var pathname = window.location.pathname;
|
|
1546
|
-
_this.pageKey = pathname.replace(/\//g, "_").substring(1);
|
|
1458
|
+
break;
|
|
1547
1459
|
|
|
1548
|
-
|
|
1549
|
-
|
|
1460
|
+
case 'pageDurationInterval':
|
|
1461
|
+
if (typeof value !== 'number' || value <= 0) {
|
|
1462
|
+
return {
|
|
1463
|
+
valid: false,
|
|
1464
|
+
message: 'pageDurationInterval 必须是大于 0 的数字'
|
|
1465
|
+
};
|
|
1550
1466
|
}
|
|
1551
|
-
} else {
|
|
1552
|
-
_this.pageKey = pageKey;
|
|
1553
|
-
_this.useCustomPageKey = !autoUpdate;
|
|
1554
1467
|
|
|
1555
|
-
|
|
1556
|
-
|
|
1468
|
+
break;
|
|
1469
|
+
|
|
1470
|
+
case 'sendMethod':
|
|
1471
|
+
if (typeof value !== 'string' || !['auto', 'xhr', 'beacon'].includes(value)) {
|
|
1472
|
+
return {
|
|
1473
|
+
valid: false,
|
|
1474
|
+
message: 'sendMethod 必须是 auto、xhr 或 beacon'
|
|
1475
|
+
};
|
|
1557
1476
|
}
|
|
1558
|
-
}
|
|
1559
|
-
};
|
|
1560
|
-
/**
|
|
1561
|
-
* @description 获取当前页面唯一标识
|
|
1562
|
-
* @returns 当前页面唯一标识
|
|
1563
|
-
*/
|
|
1564
1477
|
|
|
1478
|
+
break;
|
|
1565
1479
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1480
|
+
case 'exposureThreshold':
|
|
1481
|
+
if (typeof value !== 'number' || value < 0 || value > 1) {
|
|
1482
|
+
return {
|
|
1483
|
+
valid: false,
|
|
1484
|
+
message: 'exposureThreshold 必须是 0-1 之间的数字'
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1569
1487
|
|
|
1570
|
-
|
|
1571
|
-
var _a;
|
|
1572
|
-
|
|
1573
|
-
var target = e.target;
|
|
1574
|
-
if (!((_a = target === null || target === void 0 ? void 0 : target.dataset) === null || _a === void 0 ? void 0 : _a.partKey)) return;
|
|
1575
|
-
var position = [e.pageX, e.pageY];
|
|
1576
|
-
var id = target.id;
|
|
1577
|
-
var className = target.className;
|
|
1578
|
-
var nodeName = target.nodeName;
|
|
1579
|
-
var targetEle = {
|
|
1580
|
-
id: id,
|
|
1581
|
-
nodeName: nodeName,
|
|
1582
|
-
className: className,
|
|
1583
|
-
position: position
|
|
1584
|
-
};
|
|
1488
|
+
break;
|
|
1585
1489
|
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
pointerType: e.pointerType,
|
|
1593
|
-
currentUrl: _this.currentUrl,
|
|
1594
|
-
elementSelector: _this.getDomSelector(target) || ""
|
|
1490
|
+
case 'exposureTime':
|
|
1491
|
+
if (typeof value !== 'number' || value <= 0) {
|
|
1492
|
+
return {
|
|
1493
|
+
valid: false,
|
|
1494
|
+
message: 'exposureTime 必须是大于 0 的数字'
|
|
1495
|
+
};
|
|
1595
1496
|
}
|
|
1596
|
-
});
|
|
1597
1497
|
|
|
1598
|
-
|
|
1599
|
-
};
|
|
1600
|
-
/**
|
|
1601
|
-
* @description 路由触发事件
|
|
1602
|
-
*/
|
|
1498
|
+
break;
|
|
1603
1499
|
|
|
1500
|
+
case 'exposureNum':
|
|
1501
|
+
if (value !== undefined && (typeof value !== 'number' || value <= 0 || !Number.isInteger(value))) {
|
|
1502
|
+
return {
|
|
1503
|
+
valid: false,
|
|
1504
|
+
message: 'exposureNum 必须是大于 0 的整数或不限制'
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1604
1507
|
|
|
1605
|
-
|
|
1606
|
-
|
|
1508
|
+
break;
|
|
1509
|
+
|
|
1510
|
+
case 'showLog':
|
|
1511
|
+
case 'autoTrack':
|
|
1512
|
+
case 'isTrackSinglePage':
|
|
1513
|
+
case 'batchSend':
|
|
1514
|
+
case 'trackPartKeyClick':
|
|
1515
|
+
case 'autoTrackPageDurationInterval':
|
|
1516
|
+
if (typeof value !== 'boolean') {
|
|
1517
|
+
return {
|
|
1518
|
+
valid: false,
|
|
1519
|
+
message: key + " \u5FC5\u987B\u662F\u5E03\u5C14\u503C"
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1607
1522
|
|
|
1523
|
+
break;
|
|
1608
1524
|
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1525
|
+
case 'business':
|
|
1526
|
+
case 'header':
|
|
1527
|
+
if (value !== null && _typeof(value) !== 'object') {
|
|
1528
|
+
return {
|
|
1529
|
+
valid: false,
|
|
1530
|
+
message: key + " \u5FC5\u987B\u662F\u5BF9\u8C61\u6216 null"
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1612
1533
|
|
|
1613
|
-
|
|
1534
|
+
break;
|
|
1614
1535
|
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1536
|
+
case 'contentType':
|
|
1537
|
+
if (value !== 'application/json' && value !== 'application/x-www-form-urlencoded') {
|
|
1538
|
+
return {
|
|
1539
|
+
valid: false,
|
|
1540
|
+
message: 'contentType 必须是 application/json 或 application/x-www-form-urlencoded'
|
|
1541
|
+
};
|
|
1621
1542
|
}
|
|
1622
|
-
});
|
|
1623
1543
|
|
|
1624
|
-
|
|
1544
|
+
break;
|
|
1625
1545
|
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1546
|
+
case 'platform':
|
|
1547
|
+
if (typeof value !== 'string') {
|
|
1548
|
+
return {
|
|
1549
|
+
valid: false,
|
|
1550
|
+
message: 'platform 必须是字符串'
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1629
1553
|
|
|
1630
|
-
|
|
1554
|
+
break;
|
|
1555
|
+
}
|
|
1631
1556
|
|
|
1632
|
-
|
|
1557
|
+
return {
|
|
1558
|
+
valid: true
|
|
1633
1559
|
};
|
|
1560
|
+
};
|
|
1561
|
+
/**
|
|
1562
|
+
* 用户登录
|
|
1563
|
+
*/
|
|
1634
1564
|
|
|
1635
|
-
_this.getParams = function (_a) {
|
|
1636
|
-
var event = _a.event,
|
|
1637
|
-
desc = _a.desc,
|
|
1638
|
-
_b = _a.privateParamMap,
|
|
1639
|
-
privateParamMap = _b === void 0 ? {} : _b,
|
|
1640
|
-
itemKey = _a.itemKey;
|
|
1641
|
-
var business = _this.initConfig.business;
|
|
1642
|
-
var pageWidth = window.innerWidth;
|
|
1643
|
-
var pageHeight = window.innerHeight;
|
|
1644
|
-
var screenWidth = window.screen.width;
|
|
1645
|
-
var screenHeight = window.screen.height; // 过滤敏感数据
|
|
1646
|
-
|
|
1647
|
-
var filteredBusiness = _this.filterSensitiveData(business || {});
|
|
1648
1565
|
|
|
1649
|
-
|
|
1566
|
+
_this.login = function (userInfo) {
|
|
1567
|
+
if (_this.isObject(userInfo)) _this.userInfo = userInfo;
|
|
1568
|
+
};
|
|
1569
|
+
/**
|
|
1570
|
+
* 获取设备唯一标识
|
|
1571
|
+
* @returns 设备唯一标识
|
|
1572
|
+
*/
|
|
1650
1573
|
|
|
1651
|
-
var filteredPrivateParamMap = _this.filterSensitiveData(privateParamMap || {});
|
|
1652
1574
|
|
|
1653
|
-
|
|
1575
|
+
_this.getDeviceId = function () {
|
|
1576
|
+
// 如果已有设备ID,直接返回
|
|
1577
|
+
if (_this.deviceId) {
|
|
1578
|
+
return _this.deviceId;
|
|
1579
|
+
} // 获取已存储的设备ID
|
|
1654
1580
|
|
|
1655
1581
|
|
|
1656
|
-
|
|
1657
|
-
currentUrl: filteredPrivateParamMap.currentUrl || _this.currentUrl,
|
|
1658
|
-
business: Object.assign({}, filteredBusiness, filteredPrivateParamMap.business || {}),
|
|
1659
|
-
pageWidth: pageWidth,
|
|
1660
|
-
pageHeight: pageHeight,
|
|
1661
|
-
screenWidth: screenWidth,
|
|
1662
|
-
screenHeight: screenHeight,
|
|
1663
|
-
sdkVersion: _this.sdkVersion,
|
|
1664
|
-
systemsInfo: _this.systemsInfo,
|
|
1665
|
-
urlParams: filteredUrlParams,
|
|
1666
|
-
userInfo: filteredUserInfo,
|
|
1667
|
-
deviceId: _this.deviceId // 添加设备ID
|
|
1582
|
+
var storedDeviceId = _this.getCookie("device_id") || _this.getLocalStorage("device_id");
|
|
1668
1583
|
|
|
1669
|
-
|
|
1584
|
+
if (storedDeviceId) {
|
|
1585
|
+
_this.deviceId = storedDeviceId;
|
|
1586
|
+
return _this.deviceId;
|
|
1587
|
+
} // 收集浏览器指纹信息
|
|
1670
1588
|
|
|
1671
|
-
if (filteredPrivateParamMap.targetEle) {
|
|
1672
|
-
privateParamMapData.targetEle = filteredPrivateParamMap.targetEle;
|
|
1673
|
-
}
|
|
1674
1589
|
|
|
1675
|
-
|
|
1676
|
-
privateParamMapData.targetUrl = filteredPrivateParamMap.targetUrl;
|
|
1677
|
-
}
|
|
1590
|
+
var fingerprint = _this.collectFingerprint(); // 生成设备ID
|
|
1678
1591
|
|
|
1679
|
-
if (filteredPrivateParamMap.pointerType) {
|
|
1680
|
-
privateParamMapData.pointerType = filteredPrivateParamMap.pointerType;
|
|
1681
|
-
}
|
|
1682
1592
|
|
|
1683
|
-
|
|
1684
|
-
privateParamMapData.elementSelector = filteredPrivateParamMap.elementSelector;
|
|
1685
|
-
}
|
|
1593
|
+
var deviceId = _this.hashFingerprint(fingerprint); // 存储设备ID(统一使用2年过期时间,与tools.ts保持一致)
|
|
1686
1594
|
|
|
1687
|
-
if (filteredPrivateParamMap.retainedDuration) {
|
|
1688
|
-
privateParamMapData.retainedDuration = filteredPrivateParamMap.retainedDuration;
|
|
1689
|
-
}
|
|
1690
1595
|
|
|
1691
|
-
|
|
1692
|
-
event: event,
|
|
1693
|
-
desc: desc,
|
|
1694
|
-
itemKey: itemKey || _this.getItemKey(),
|
|
1695
|
-
requestTime: _this.getTimeStamp(),
|
|
1696
|
-
privateParamMap: privateParamMapData
|
|
1697
|
-
};
|
|
1698
|
-
};
|
|
1699
|
-
/**
|
|
1700
|
-
* 数据采样判断
|
|
1701
|
-
* @returns 是否应该采样
|
|
1702
|
-
*/
|
|
1596
|
+
_this.setCookie("device_id", deviceId, 365 * 2); // 存储2年
|
|
1703
1597
|
|
|
1704
1598
|
|
|
1705
|
-
_this.
|
|
1706
|
-
var sampleRate = _this.initConfig.sampleRate;
|
|
1707
|
-
if (sampleRate >= 1) return true;
|
|
1708
|
-
if (sampleRate <= 0) return false;
|
|
1709
|
-
return Math.random() < sampleRate;
|
|
1710
|
-
};
|
|
1711
|
-
/**
|
|
1712
|
-
* 批量发送数据
|
|
1713
|
-
*/
|
|
1599
|
+
_this.setLocalStorage("device_id", deviceId);
|
|
1714
1600
|
|
|
1601
|
+
_this.deviceId = deviceId;
|
|
1602
|
+
return _this.deviceId;
|
|
1603
|
+
};
|
|
1604
|
+
/**
|
|
1605
|
+
* 重置设备ID
|
|
1606
|
+
* 清除存储的设备ID并重新生成
|
|
1607
|
+
* @returns 新的设备ID
|
|
1608
|
+
*/
|
|
1715
1609
|
|
|
1716
|
-
_this.flushBatchQueue = function () {
|
|
1717
|
-
if (_this.batchQueue.length === 0) return;
|
|
1718
1610
|
|
|
1719
|
-
|
|
1611
|
+
_this.resetDeviceId = function () {
|
|
1612
|
+
// 清除cookie和localStorage中的设备ID
|
|
1613
|
+
document.cookie = "device_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
|
1614
|
+
localStorage.removeItem("device_id"); // 清除内存中的设备ID
|
|
1720
1615
|
|
|
1721
|
-
|
|
1616
|
+
_this.deviceId = ""; // 重新生成设备ID
|
|
1722
1617
|
|
|
1723
|
-
|
|
1618
|
+
var newDeviceId = _this.getDeviceId();
|
|
1724
1619
|
|
|
1620
|
+
return newDeviceId;
|
|
1621
|
+
};
|
|
1622
|
+
/**
|
|
1623
|
+
* 自定义代码埋点上报
|
|
1624
|
+
* @param {object} TrackParams [自定义上报参数]
|
|
1625
|
+
* @return {Promise<TrackingResponse>} [回调]
|
|
1626
|
+
*/
|
|
1725
1627
|
|
|
1726
|
-
_this.sendBatchData(batchData);
|
|
1727
|
-
};
|
|
1728
|
-
/**
|
|
1729
|
-
* 发送批量数据
|
|
1730
|
-
* @param data 批量数据
|
|
1731
|
-
*/
|
|
1732
1628
|
|
|
1629
|
+
_this.track = function (_a) {
|
|
1630
|
+
var desc = _a.desc,
|
|
1631
|
+
pageKey = _a.pageKey,
|
|
1632
|
+
partkey = _a.partkey,
|
|
1633
|
+
business = _a.business,
|
|
1634
|
+
header = _a.header;
|
|
1733
1635
|
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1636
|
+
var params = _this.getParams({
|
|
1637
|
+
desc: desc,
|
|
1638
|
+
event: "CustomTrack",
|
|
1639
|
+
itemKey: _this.getItemKey(partkey, pageKey),
|
|
1640
|
+
privateParamMap: {
|
|
1641
|
+
business: business
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1739
1644
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1645
|
+
return _this.sendData(params, header);
|
|
1646
|
+
};
|
|
1647
|
+
/**
|
|
1648
|
+
* @description 监听全埋点事件
|
|
1649
|
+
*/
|
|
1742
1650
|
|
|
1743
|
-
data.forEach(function (item) {
|
|
1744
|
-
return _this.printLog(item);
|
|
1745
|
-
});
|
|
1746
|
-
} // 这里可以根据实际需求决定是逐条发送还是打包发送
|
|
1747
|
-
// 这里选择打包发送
|
|
1748
1651
|
|
|
1652
|
+
_this.listener = function () {
|
|
1653
|
+
// 先移除旧的监听器,避免重复绑定
|
|
1654
|
+
_this.unlistener(); // 如果启用了全埋点,监听页面浏览事件
|
|
1749
1655
|
|
|
1750
|
-
_this.ajax({
|
|
1751
|
-
url: serverUrl,
|
|
1752
|
-
type: "POST",
|
|
1753
|
-
data: JSON.stringify({
|
|
1754
|
-
events: data
|
|
1755
|
-
}),
|
|
1756
|
-
contentType: contentType,
|
|
1757
|
-
credentials: false,
|
|
1758
|
-
timeout: _this.initConfig.sendTimeout,
|
|
1759
|
-
cors: true,
|
|
1760
|
-
success: function success() {
|
|
1761
|
-
// 批量发送成功,数据已从队列中移除,无需额外操作
|
|
1762
|
-
if (_this.initConfig.showLog) {
|
|
1763
|
-
_this.printLog("\u6279\u91CF\u53D1\u9001\u6210\u529F: " + data.length + " \u6761\u6570\u636E");
|
|
1764
|
-
}
|
|
1765
|
-
},
|
|
1766
|
-
error: function error(err) {
|
|
1767
|
-
var _a; // 批量发送失败,重新加入队列以便重试
|
|
1768
1656
|
|
|
1657
|
+
if (!!_this.initConfig.autoTrack) {
|
|
1658
|
+
if (!!_this.initConfig.isTrackSinglePage) {
|
|
1659
|
+
_this.rewriteHistory();
|
|
1769
1660
|
|
|
1770
|
-
|
|
1661
|
+
_this.addSinglePageEvent(_this.onPageViewCallback);
|
|
1662
|
+
}
|
|
1771
1663
|
|
|
1664
|
+
_this.each(["load", "beforeunload"], function (historyType) {
|
|
1665
|
+
_this.addEventListener(window, historyType, _this.onPageViewCallback);
|
|
1666
|
+
});
|
|
1667
|
+
} // 如果启用了全埋点或启用了 data-part-key 点击追踪,监听点击事件
|
|
1772
1668
|
|
|
1773
|
-
(_a = _this.batchQueue).unshift.apply(_a, data); // 限制重试队列大小,避免内存溢出
|
|
1774
1669
|
|
|
1670
|
+
if (!!_this.initConfig.autoTrack || !!_this.initConfig.trackPartKeyClick) {
|
|
1671
|
+
_this.addEventListener(window, "click", _this.onClickCallback);
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
/**
|
|
1675
|
+
* @description 取消全埋点事件
|
|
1676
|
+
*/
|
|
1775
1677
|
|
|
1776
|
-
if (_this.batchQueue.length > _this.initConfig.batchMaxSize * 2) {
|
|
1777
|
-
_this.batchQueue = _this.batchQueue.slice(0, _this.initConfig.batchMaxSize);
|
|
1778
|
-
} // 保存失败的数据到 LocalStorage
|
|
1779
1678
|
|
|
1679
|
+
_this.unlistener = function () {
|
|
1680
|
+
if (!!_this.initConfig.isTrackSinglePage) {
|
|
1681
|
+
var historyPushState = window.history.pushState;
|
|
1682
|
+
var singlePageEvent = !!historyPushState ? "popstate" : "hashchange";
|
|
1780
1683
|
|
|
1781
|
-
|
|
1782
|
-
|
|
1684
|
+
_this.each(["pushState", "replaceState", singlePageEvent], function (historyName) {
|
|
1685
|
+
_this.removeEventListener(window, historyName, _this.onPageViewCallback);
|
|
1783
1686
|
});
|
|
1784
|
-
}
|
|
1785
|
-
/**
|
|
1786
|
-
* 添加到批量队列
|
|
1787
|
-
* @param params 数据参数
|
|
1788
|
-
*/
|
|
1789
|
-
|
|
1687
|
+
}
|
|
1790
1688
|
|
|
1791
|
-
_this.
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
batchMaxSize = _a.batchMaxSize;
|
|
1689
|
+
_this.each(["load", "beforeunload"], function (historyType) {
|
|
1690
|
+
_this.removeEventListener(window, historyType, _this.onPageViewCallback);
|
|
1691
|
+
});
|
|
1795
1692
|
|
|
1796
|
-
|
|
1693
|
+
_this.removeEventListener(window, "click", _this.onClickCallback); // 清理批量发送定时器
|
|
1797
1694
|
|
|
1798
1695
|
|
|
1799
|
-
|
|
1696
|
+
_this.clearBatchTimer(); // 清理定时上报定时器
|
|
1800
1697
|
|
|
1801
1698
|
|
|
1802
|
-
|
|
1803
|
-
|
|
1699
|
+
_this.stopPageDurationTimer();
|
|
1700
|
+
};
|
|
1701
|
+
/**
|
|
1702
|
+
* @description 清理批量发送定时器
|
|
1703
|
+
*/
|
|
1804
1704
|
|
|
1805
|
-
return;
|
|
1806
|
-
} // 设置定时发送
|
|
1807
1705
|
|
|
1706
|
+
_this.clearBatchTimer = function () {
|
|
1707
|
+
if (_this.batchTimer !== null) {
|
|
1708
|
+
clearTimeout(_this.batchTimer);
|
|
1709
|
+
_this.batchTimer = null;
|
|
1710
|
+
}
|
|
1711
|
+
};
|
|
1712
|
+
/**
|
|
1713
|
+
* @description 清空批量队列(包括 LocalStorage 中的数据)
|
|
1714
|
+
*/
|
|
1808
1715
|
|
|
1809
|
-
if (!_this.batchTimer) {
|
|
1810
|
-
_this.batchTimer = window.setTimeout(function () {
|
|
1811
|
-
_this.flushBatchQueue();
|
|
1812
1716
|
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
}
|
|
1816
|
-
};
|
|
1817
|
-
/**
|
|
1818
|
-
* 从 LocalStorage 恢复批量队列
|
|
1819
|
-
*/
|
|
1717
|
+
_this.clearBatchQueue = function () {
|
|
1718
|
+
_this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, "[]");
|
|
1820
1719
|
|
|
1720
|
+
if (_this.initConfig.showLog) {
|
|
1721
|
+
_this.printLog("批量队列已清空");
|
|
1722
|
+
}
|
|
1723
|
+
};
|
|
1724
|
+
/**
|
|
1725
|
+
* @description 从 LocalStorage 获取批量队列
|
|
1726
|
+
* @returns 批量队列数组
|
|
1727
|
+
*/
|
|
1821
1728
|
|
|
1822
|
-
_this.restoreBatchQueueFromStorage = function () {
|
|
1823
|
-
try {
|
|
1824
|
-
var storedQueue = _this.getLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY);
|
|
1825
1729
|
|
|
1826
|
-
|
|
1827
|
-
|
|
1730
|
+
_this.getBatchQueueFromStorage = function () {
|
|
1731
|
+
try {
|
|
1732
|
+
var storedQueue = _this.getLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY);
|
|
1828
1733
|
|
|
1829
|
-
|
|
1830
|
-
|
|
1734
|
+
if (storedQueue) {
|
|
1735
|
+
var parsedQueue = JSON.parse(storedQueue);
|
|
1831
1736
|
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1737
|
+
if (Array.isArray(parsedQueue)) {
|
|
1738
|
+
return parsedQueue;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
} catch (e) {
|
|
1742
|
+
_this.printLog("\u8BFB\u53D6\u6279\u91CF\u961F\u5217\u5931\u8D25: " + e); // 如果解析失败,清除损坏的数据
|
|
1835
1743
|
|
|
1836
1744
|
|
|
1837
|
-
|
|
1745
|
+
_this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, "[]");
|
|
1746
|
+
}
|
|
1838
1747
|
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1748
|
+
return [];
|
|
1749
|
+
};
|
|
1750
|
+
/**
|
|
1751
|
+
* @description 保存批量队列到 LocalStorage
|
|
1752
|
+
* @param queue 批量队列数组
|
|
1753
|
+
*/
|
|
1844
1754
|
|
|
1845
|
-
if (!_this.batchTimer) {
|
|
1846
|
-
_this.batchTimer = window.setTimeout(function () {
|
|
1847
|
-
_this.flushBatchQueue();
|
|
1848
1755
|
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
} catch (e) {
|
|
1856
|
-
_this.printLog("\u6062\u590D\u6279\u91CF\u961F\u5217\u5931\u8D25: " + e); // 如果解析失败,清除损坏的数据
|
|
1756
|
+
_this.saveBatchQueueToStorage = function (queue) {
|
|
1757
|
+
try {
|
|
1758
|
+
var queueString = JSON.stringify(queue); // 检查存储大小,避免超出 LocalStorage 限制
|
|
1857
1759
|
|
|
1760
|
+
if (queueString.length > _this.MAX_STORAGE_SIZE) {
|
|
1761
|
+
var maxItems = Math.floor(queue.length * 0.8); // 保留 80%
|
|
1858
1762
|
|
|
1859
|
-
|
|
1860
|
-
}
|
|
1861
|
-
};
|
|
1862
|
-
/**
|
|
1863
|
-
* 添加到待发送请求队列
|
|
1864
|
-
* @param params 数据参数
|
|
1865
|
-
*/
|
|
1763
|
+
var trimmedQueue = queue.slice(-maxItems);
|
|
1866
1764
|
|
|
1765
|
+
_this.printLog("\u961F\u5217\u8FC7\u5927\uFF0C\u5DF2\u622A\u65AD\u4FDD\u7559\u6700\u65B0 " + maxItems + " \u6761\u6570\u636E\uFF08\u9650\u5236: " + _this.MAX_STORAGE_SIZE / 1024 / 1024 + "MB\uFF09");
|
|
1867
1766
|
|
|
1868
|
-
|
|
1869
|
-
|
|
1767
|
+
_this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, JSON.stringify(trimmedQueue));
|
|
1768
|
+
} else {
|
|
1769
|
+
_this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, queueString);
|
|
1770
|
+
}
|
|
1771
|
+
} catch (e) {
|
|
1772
|
+
// LocalStorage 可能已满或不可用
|
|
1773
|
+
_this.printLog("\u4FDD\u5B58\u6279\u91CF\u961F\u5217\u5230 LocalStorage \u5931\u8D25: " + e);
|
|
1774
|
+
}
|
|
1775
|
+
};
|
|
1776
|
+
/**
|
|
1777
|
+
* @description 从 LocalStorage 获取待发送请求队列
|
|
1778
|
+
* @returns 待发送请求队列数组
|
|
1779
|
+
*/
|
|
1870
1780
|
|
|
1871
1781
|
|
|
1872
|
-
|
|
1782
|
+
_this.getPendingRequestsFromStorage = function () {
|
|
1783
|
+
try {
|
|
1784
|
+
var storedRequests = _this.getLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY);
|
|
1873
1785
|
|
|
1874
|
-
if (
|
|
1875
|
-
|
|
1786
|
+
if (storedRequests) {
|
|
1787
|
+
var parsedRequests = JSON.parse(storedRequests);
|
|
1876
1788
|
|
|
1877
|
-
if (
|
|
1878
|
-
|
|
1789
|
+
if (Array.isArray(parsedRequests)) {
|
|
1790
|
+
return parsedRequests;
|
|
1879
1791
|
}
|
|
1880
1792
|
}
|
|
1881
|
-
}
|
|
1882
|
-
|
|
1883
|
-
* 从 LocalStorage 恢复待发送请求
|
|
1884
|
-
*/
|
|
1793
|
+
} catch (e) {
|
|
1794
|
+
_this.printLog("\u8BFB\u53D6\u5F85\u53D1\u9001\u8BF7\u6C42\u5931\u8D25: " + e); // 如果解析失败,清除损坏的数据
|
|
1885
1795
|
|
|
1886
1796
|
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
var storedRequests = _this.getLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY);
|
|
1797
|
+
_this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, "[]");
|
|
1798
|
+
}
|
|
1890
1799
|
|
|
1891
|
-
|
|
1892
|
-
|
|
1800
|
+
return [];
|
|
1801
|
+
};
|
|
1802
|
+
/**
|
|
1803
|
+
* @description 保存待发送请求队列到 LocalStorage
|
|
1804
|
+
* @param requests 待发送请求队列数组
|
|
1805
|
+
*/
|
|
1893
1806
|
|
|
1894
|
-
if (Array.isArray(parsedRequests) && parsedRequests.length > 0) {
|
|
1895
|
-
_this.pendingRequests = parsedRequests;
|
|
1896
1807
|
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1808
|
+
_this.savePendingRequestsToStorage = function (requests) {
|
|
1809
|
+
try {
|
|
1810
|
+
_this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, JSON.stringify(requests));
|
|
1811
|
+
} catch (e) {
|
|
1812
|
+
// LocalStorage 可能已满或不可用
|
|
1813
|
+
_this.printLog("\u4FDD\u5B58\u5F85\u53D1\u9001\u8BF7\u6C42\u5230 LocalStorage \u5931\u8D25: " + e);
|
|
1814
|
+
}
|
|
1815
|
+
};
|
|
1816
|
+
/**
|
|
1817
|
+
* @description 设置自定义页面唯一标识
|
|
1818
|
+
* @param pageKey 页面唯一标识,如果传入 null 或空字符串,则恢复自动生成
|
|
1819
|
+
* @param autoUpdate 路由变化时是否自动更新(默认:false,使用自定义值后不再自动更新)
|
|
1820
|
+
*/
|
|
1900
1821
|
|
|
1901
1822
|
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
}
|
|
1907
|
-
} catch (e) {
|
|
1908
|
-
_this.printLog("\u6062\u590D\u5F85\u53D1\u9001\u8BF7\u6C42\u5931\u8D25: " + e); // 如果解析失败,清除损坏的数据
|
|
1823
|
+
_this.setPageKey = function (pageKey, autoUpdate) {
|
|
1824
|
+
if (autoUpdate === void 0) {
|
|
1825
|
+
autoUpdate = false;
|
|
1826
|
+
}
|
|
1909
1827
|
|
|
1828
|
+
if (pageKey === null || pageKey === '') {
|
|
1829
|
+
// 恢复自动生成
|
|
1830
|
+
_this.useCustomPageKey = false;
|
|
1831
|
+
var pathname = window.location.pathname;
|
|
1832
|
+
_this.pageKey = pathname.replace(/\//g, "_").substring(1);
|
|
1910
1833
|
|
|
1911
|
-
|
|
1834
|
+
if (_this.initConfig.showLog) {
|
|
1835
|
+
_this.printLog("\u9875\u9762\u6807\u8BC6\u5DF2\u6062\u590D\u81EA\u52A8\u751F\u6210: " + _this.pageKey);
|
|
1912
1836
|
}
|
|
1913
|
-
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
* @returns 如果页面即将卸载返回 true,否则返回 false
|
|
1917
|
-
*/
|
|
1918
|
-
|
|
1837
|
+
} else {
|
|
1838
|
+
_this.pageKey = pageKey;
|
|
1839
|
+
_this.useCustomPageKey = !autoUpdate;
|
|
1919
1840
|
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
*/
|
|
1841
|
+
if (_this.initConfig.showLog) {
|
|
1842
|
+
_this.printLog("\u9875\u9762\u6807\u8BC6\u5DF2\u8BBE\u7F6E\u4E3A: " + pageKey + ", \u81EA\u52A8\u66F4\u65B0: " + autoUpdate);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
};
|
|
1846
|
+
/**
|
|
1847
|
+
* @description 获取当前页面唯一标识
|
|
1848
|
+
* @returns 当前页面唯一标识
|
|
1849
|
+
*/
|
|
1930
1850
|
|
|
1931
1851
|
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
type: contentType || "application/json"
|
|
1936
|
-
});
|
|
1937
|
-
return navigator.sendBeacon(serverUrl, blob);
|
|
1938
|
-
} catch (e) {
|
|
1939
|
-
if (_this.initConfig.showLog) {
|
|
1940
|
-
_this.printLog("sendBeacon \u53D1\u9001\u5931\u8D25: " + e);
|
|
1941
|
-
}
|
|
1852
|
+
_this.getPageKey = function () {
|
|
1853
|
+
return _this.pageKey;
|
|
1854
|
+
};
|
|
1942
1855
|
|
|
1943
|
-
|
|
1856
|
+
_this.onClickCallback = function (e) {
|
|
1857
|
+
var _a;
|
|
1858
|
+
|
|
1859
|
+
var target = e.target;
|
|
1860
|
+
if (!((_a = target === null || target === void 0 ? void 0 : target.dataset) === null || _a === void 0 ? void 0 : _a.partKey)) return;
|
|
1861
|
+
var position = [e.pageX, e.pageY];
|
|
1862
|
+
var id = target.id;
|
|
1863
|
+
var className = target.className;
|
|
1864
|
+
var nodeName = target.nodeName;
|
|
1865
|
+
var targetEle = {
|
|
1866
|
+
id: id,
|
|
1867
|
+
nodeName: nodeName,
|
|
1868
|
+
className: className,
|
|
1869
|
+
position: position
|
|
1870
|
+
};
|
|
1871
|
+
|
|
1872
|
+
var params = _this.getParams({
|
|
1873
|
+
event: "WebClick",
|
|
1874
|
+
desc: _this.eventDescMap["WebClick"],
|
|
1875
|
+
itemKey: _this.getItemKey(target.dataset.partKey),
|
|
1876
|
+
privateParamMap: {
|
|
1877
|
+
targetEle: targetEle,
|
|
1878
|
+
pointerType: e.pointerType,
|
|
1879
|
+
currentUrl: _this.currentUrl,
|
|
1880
|
+
elementSelector: _this.getDomSelector(target) || ""
|
|
1944
1881
|
}
|
|
1945
|
-
};
|
|
1946
|
-
/**
|
|
1947
|
-
* 刷新待发送的单个请求(正常情况下的发送)
|
|
1948
|
-
* 注意:这个方法会直接发送,不会再次添加到 pendingRequests,避免循环
|
|
1949
|
-
*/
|
|
1882
|
+
});
|
|
1950
1883
|
|
|
1884
|
+
_this.sendData(params);
|
|
1885
|
+
};
|
|
1886
|
+
/**
|
|
1887
|
+
* @description 路由触发事件
|
|
1888
|
+
*/
|
|
1951
1889
|
|
|
1952
|
-
_this.flushPendingRequests = function () {
|
|
1953
|
-
if (_this.pendingRequests.length === 0) {
|
|
1954
|
-
return;
|
|
1955
|
-
}
|
|
1956
1890
|
|
|
1957
|
-
|
|
1891
|
+
_this.onPageViewCallback = function (e) {
|
|
1892
|
+
var _a, _b; // 在路由变化前,先发送待发送的数据(避免被取消)
|
|
1958
1893
|
|
|
1959
|
-
_this.pendingRequests = []; // 清除 LocalStorage 中的待发送请求
|
|
1960
1894
|
|
|
1961
|
-
|
|
1895
|
+
var pendingRequests = _this.getPendingRequestsFromStorage();
|
|
1962
1896
|
|
|
1897
|
+
var batchQueue = _this.initConfig.batchSend ? _this.getBatchQueueFromStorage() : [];
|
|
1963
1898
|
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
_this.sendData(params).catch(function (err) {
|
|
1968
|
-
if (_this.initConfig.showLog) {
|
|
1969
|
-
_this.printLog("\u5F85\u53D1\u9001\u8BF7\u6C42\u53D1\u9001\u5931\u8D25\uFF08\u4E0D\u518D\u91CD\u8BD5\uFF09: " + err);
|
|
1970
|
-
}
|
|
1971
|
-
});
|
|
1972
|
-
});
|
|
1973
|
-
};
|
|
1974
|
-
/**
|
|
1975
|
-
* 保存批量队列到 LocalStorage
|
|
1976
|
-
*/
|
|
1899
|
+
if (pendingRequests.length > 0 || batchQueue.length > 0) {
|
|
1900
|
+
_this.flushPendingData();
|
|
1901
|
+
}
|
|
1977
1902
|
|
|
1903
|
+
var ORGIN = window.location.origin;
|
|
1978
1904
|
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1905
|
+
var params = _this.getParams({
|
|
1906
|
+
event: "PageView",
|
|
1907
|
+
desc: _this.eventDescMap["PageView"],
|
|
1908
|
+
privateParamMap: {
|
|
1909
|
+
currentUrl: _this.currentUrl,
|
|
1910
|
+
targetUrl: ((_a = e.arguments) === null || _a === void 0 ? void 0 : _a[2]) ? ORGIN + ((_b = e.arguments) === null || _b === void 0 ? void 0 : _b[2]) : null
|
|
1911
|
+
}
|
|
1912
|
+
});
|
|
1983
1913
|
|
|
1984
|
-
|
|
1985
|
-
var maxItems = Math.floor(_this.batchQueue.length * 0.8); // 保留 80%
|
|
1914
|
+
_this.currentUrl = window.location.href; // 如果使用自定义 pageKey,路由变化时不自动更新
|
|
1986
1915
|
|
|
1987
|
-
|
|
1916
|
+
if (!_this.useCustomPageKey) {
|
|
1917
|
+
_this.pageKey = window.location.pathname.replace(/\//g, "_").substring(1);
|
|
1918
|
+
} // 路由变化时,如果启用了定时上报,需要重启定时器
|
|
1988
1919
|
|
|
1989
|
-
_this.printLog("\u961F\u5217\u8FC7\u5927\uFF0C\u5DF2\u622A\u65AD\u4FDD\u7559\u6700\u65B0 " + maxItems + " \u6761\u6570\u636E\uFF08\u9650\u5236: " + _this.MAX_STORAGE_SIZE / 1024 / 1024 + "MB\uFF09");
|
|
1990
|
-
}
|
|
1991
1920
|
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
// LocalStorage 可能已满或不可用
|
|
1995
|
-
_this.printLog("\u4FDD\u5B58\u6279\u91CF\u961F\u5217\u5230 LocalStorage \u5931\u8D25: " + e);
|
|
1996
|
-
}
|
|
1997
|
-
};
|
|
1998
|
-
/**
|
|
1999
|
-
* 设置页面卸载监听器,确保数据发送
|
|
2000
|
-
*/
|
|
1921
|
+
if (_this.initConfig.autoTrackPageDurationInterval) {
|
|
1922
|
+
_this.stopPageDurationTimer();
|
|
2001
1923
|
|
|
1924
|
+
_this.startPageDurationTimer();
|
|
1925
|
+
}
|
|
2002
1926
|
|
|
2003
|
-
_this.
|
|
2004
|
-
// 避免重复设置监听器
|
|
2005
|
-
if (_this.isUnloadListenerSetup) {
|
|
2006
|
-
return;
|
|
2007
|
-
}
|
|
1927
|
+
_this.sendRetained(e.type);
|
|
2008
1928
|
|
|
2009
|
-
|
|
1929
|
+
_this.sendData(params);
|
|
1930
|
+
};
|
|
2010
1931
|
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
1932
|
+
_this.getParams = function (_a) {
|
|
1933
|
+
var event = _a.event,
|
|
1934
|
+
desc = _a.desc,
|
|
1935
|
+
_b = _a.privateParamMap,
|
|
1936
|
+
privateParamMap = _b === void 0 ? {} : _b,
|
|
1937
|
+
itemKey = _a.itemKey;
|
|
1938
|
+
var business = _this.initConfig.business;
|
|
1939
|
+
var pageWidth = window.innerWidth;
|
|
1940
|
+
var pageHeight = window.innerHeight;
|
|
1941
|
+
var screenWidth = window.screen.width;
|
|
1942
|
+
var screenHeight = window.screen.height; // 过滤敏感数据
|
|
2016
1943
|
|
|
2017
|
-
|
|
2018
|
-
_this.flushPendingData();
|
|
2019
|
-
}); // 使用 pagehide 事件(更可靠,支持移动端)
|
|
1944
|
+
var filteredBusiness = _this.filterSensitiveData(business || {});
|
|
2020
1945
|
|
|
2021
|
-
|
|
2022
|
-
_this.flushPendingData();
|
|
2023
|
-
});
|
|
2024
|
-
};
|
|
2025
|
-
/**
|
|
2026
|
-
* 刷新待发送数据(在页面卸载/跳转时调用)
|
|
2027
|
-
*/
|
|
1946
|
+
var filteredUserInfo = _this.filterSensitiveData(_this.userInfo || {});
|
|
2028
1947
|
|
|
1948
|
+
var filteredPrivateParamMap = _this.filterSensitiveData(privateParamMap || {});
|
|
2029
1949
|
|
|
2030
|
-
|
|
2031
|
-
// 收集所有待发送的数据
|
|
2032
|
-
var allPendingData = []; // 如果有批量队列,添加到待发送列表
|
|
1950
|
+
var filteredUrlParams = _this.filterSensitiveData(_this.getQueryValue() || {}); // 创建私有参数对象
|
|
2033
1951
|
|
|
2034
|
-
if (_this.batchQueue.length > 0) {
|
|
2035
|
-
allPendingData.push.apply(allPendingData, _this.batchQueue);
|
|
2036
|
-
} // 如果有待发送的单个请求,也添加到列表
|
|
2037
1952
|
|
|
1953
|
+
var privateParamMapData = {
|
|
1954
|
+
currentUrl: filteredPrivateParamMap.currentUrl || _this.currentUrl,
|
|
1955
|
+
business: Object.assign({}, filteredBusiness, filteredPrivateParamMap.business || {}),
|
|
1956
|
+
pageWidth: pageWidth,
|
|
1957
|
+
pageHeight: pageHeight,
|
|
1958
|
+
screenWidth: screenWidth,
|
|
1959
|
+
screenHeight: screenHeight,
|
|
1960
|
+
sdkVersion: _this.sdkVersion,
|
|
1961
|
+
systemsInfo: _this.systemsInfo,
|
|
1962
|
+
urlParams: filteredUrlParams,
|
|
1963
|
+
userInfo: filteredUserInfo,
|
|
1964
|
+
deviceId: _this.deviceId // 添加设备ID
|
|
2038
1965
|
|
|
2039
|
-
|
|
2040
|
-
allPendingData.push.apply(allPendingData, _this.pendingRequests);
|
|
2041
|
-
}
|
|
1966
|
+
}; // 添加其他可能的属性
|
|
2042
1967
|
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
1968
|
+
if (filteredPrivateParamMap.targetEle) {
|
|
1969
|
+
privateParamMapData.targetEle = filteredPrivateParamMap.targetEle;
|
|
1970
|
+
}
|
|
2046
1971
|
|
|
1972
|
+
if (filteredPrivateParamMap.targetUrl) {
|
|
1973
|
+
privateParamMapData.targetUrl = filteredPrivateParamMap.targetUrl;
|
|
1974
|
+
}
|
|
2047
1975
|
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
var dataToSend = allPendingData.length === 1 ? allPendingData[0] : {
|
|
2052
|
-
events: allPendingData
|
|
2053
|
-
};
|
|
2054
|
-
var blob = new Blob([JSON.stringify(dataToSend)], {
|
|
2055
|
-
type: _this.initConfig.contentType || "application/json"
|
|
2056
|
-
});
|
|
2057
|
-
var sent = navigator.sendBeacon(_this.initConfig.serverUrl, blob);
|
|
1976
|
+
if (filteredPrivateParamMap.pointerType) {
|
|
1977
|
+
privateParamMapData.pointerType = filteredPrivateParamMap.pointerType;
|
|
1978
|
+
}
|
|
2058
1979
|
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
_this.pendingRequests = [];
|
|
1980
|
+
if (filteredPrivateParamMap.elementSelector) {
|
|
1981
|
+
privateParamMapData.elementSelector = filteredPrivateParamMap.elementSelector;
|
|
1982
|
+
}
|
|
2063
1983
|
|
|
2064
|
-
|
|
1984
|
+
if (filteredPrivateParamMap.retainedDuration) {
|
|
1985
|
+
privateParamMapData.retainedDuration = filteredPrivateParamMap.retainedDuration;
|
|
1986
|
+
}
|
|
2065
1987
|
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
_this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, JSON.stringify(_this.pendingRequests));
|
|
2079
|
-
} catch (e) {
|
|
2080
|
-
// LocalStorage 可能已满或不可用
|
|
2081
|
-
if (_this.initConfig.showLog) {
|
|
2082
|
-
_this.printLog("\u4FDD\u5B58\u5F85\u53D1\u9001\u8BF7\u6C42\u5230 LocalStorage \u5931\u8D25: " + e);
|
|
2083
|
-
}
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
}
|
|
2087
|
-
} catch (e) {
|
|
2088
|
-
// sendBeacon 失败,保存到 LocalStorage(批量模式)
|
|
2089
|
-
if (_this.initConfig.batchSend && _this.batchQueue.length > 0) {
|
|
2090
|
-
_this.saveBatchQueueToStorage();
|
|
2091
|
-
} // 保存 pendingRequests 到 LocalStorage(如果支持)
|
|
1988
|
+
return {
|
|
1989
|
+
event: event,
|
|
1990
|
+
desc: desc,
|
|
1991
|
+
itemKey: itemKey || _this.getItemKey(),
|
|
1992
|
+
requestTime: _this.getTimeStamp(),
|
|
1993
|
+
privateParamMap: privateParamMapData
|
|
1994
|
+
};
|
|
1995
|
+
};
|
|
1996
|
+
/**
|
|
1997
|
+
* 数据采样判断
|
|
1998
|
+
* @returns 是否应该采样
|
|
1999
|
+
*/
|
|
2092
2000
|
|
|
2093
2001
|
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2002
|
+
_this.shouldSample = function () {
|
|
2003
|
+
var sampleRate = _this.initConfig.sampleRate;
|
|
2004
|
+
if (sampleRate >= 1) return true;
|
|
2005
|
+
if (sampleRate <= 0) return false;
|
|
2006
|
+
return Math.random() < sampleRate;
|
|
2007
|
+
};
|
|
2008
|
+
/**
|
|
2009
|
+
* 批量发送数据
|
|
2010
|
+
*/
|
|
2011
|
+
|
|
2012
|
+
|
|
2013
|
+
_this.flushBatchQueue = function () {
|
|
2014
|
+
var batchQueue = _this.getBatchQueueFromStorage();
|
|
2015
|
+
|
|
2016
|
+
if (batchQueue.length === 0) return;
|
|
2017
|
+
|
|
2018
|
+
var currentTime = _this.getTimeStamp(); // 过滤出可以发送的数据(未到重试时间的不发送)
|
|
2019
|
+
|
|
2020
|
+
|
|
2021
|
+
var readyToSend = batchQueue.filter(function (item) {
|
|
2022
|
+
if (!item._nextRetryTime) {
|
|
2023
|
+
return true;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
return item._nextRetryTime <= currentTime;
|
|
2027
|
+
});
|
|
2028
|
+
|
|
2029
|
+
if (readyToSend.length === 0) {
|
|
2030
|
+
if (_this.initConfig.showLog) {
|
|
2031
|
+
_this.printLog("\u6279\u91CF\u961F\u5217\u4E2D\u6709 " + batchQueue.length + " \u6761\u6570\u636E\u7B49\u5F85\u91CD\u8BD5");
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
return;
|
|
2035
|
+
} // 从队列中移除已准备发送的数据
|
|
2036
|
+
|
|
2037
|
+
|
|
2038
|
+
var remainingQueue = batchQueue.filter(function (item) {
|
|
2039
|
+
if (!item._nextRetryTime) {
|
|
2040
|
+
return false;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
return item._nextRetryTime > currentTime;
|
|
2044
|
+
}); // 保存剩余的队列
|
|
2045
|
+
|
|
2046
|
+
_this.saveBatchQueueToStorage(remainingQueue); // 发送批量数据
|
|
2047
|
+
|
|
2048
|
+
|
|
2049
|
+
_this.sendBatchData(readyToSend);
|
|
2050
|
+
};
|
|
2051
|
+
/**
|
|
2052
|
+
* 发送批量数据
|
|
2053
|
+
* @param data 批量数据
|
|
2054
|
+
*/
|
|
2055
|
+
|
|
2056
|
+
|
|
2057
|
+
_this.sendBatchData = function (data) {
|
|
2058
|
+
var _a = _this.initConfig,
|
|
2059
|
+
serverUrl = _a.serverUrl,
|
|
2060
|
+
contentType = _a.contentType,
|
|
2061
|
+
showLog = _a.showLog,
|
|
2062
|
+
sendMethod = _a.sendMethod,
|
|
2063
|
+
initHeader = _a.header;
|
|
2064
|
+
|
|
2065
|
+
if (showLog) {
|
|
2066
|
+
_this.printLog("\u6279\u91CF\u53D1\u9001 " + data.length + " \u6761\u6570\u636E");
|
|
2067
|
+
|
|
2068
|
+
data.forEach(function (item) {
|
|
2069
|
+
return _this.printLog(item);
|
|
2070
|
+
});
|
|
2071
|
+
} // 判断是否使用 sendBeacon
|
|
2072
|
+
|
|
2073
|
+
|
|
2074
|
+
var shouldUseBeacon = _this.shouldUseBeacon(sendMethod, undefined, initHeader); // 如果使用 sendBeacon
|
|
2075
|
+
|
|
2076
|
+
|
|
2077
|
+
if (shouldUseBeacon) {
|
|
2078
|
+
try {
|
|
2079
|
+
var blob = new Blob([JSON.stringify(data)], {
|
|
2080
|
+
type: contentType || "application/json"
|
|
2081
|
+
});
|
|
2082
|
+
var sent = navigator.sendBeacon(serverUrl, blob);
|
|
2083
|
+
|
|
2084
|
+
if (sent) {
|
|
2085
|
+
// 发送成功,确保 LocalStorage 已清空
|
|
2086
|
+
_this.saveBatchQueueToStorage([]);
|
|
2087
|
+
|
|
2088
|
+
if (showLog) {
|
|
2089
|
+
_this.printLog("\u6279\u91CF\u53D1\u9001\u6210\u529F: " + data.length + " \u6761\u6570\u636E");
|
|
2099
2090
|
}
|
|
2091
|
+
} else {
|
|
2092
|
+
// sendBeacon 返回 false,重新加入队列以便重试
|
|
2093
|
+
_this.printLog("\u6279\u91CF\u53D1\u9001\u5931\u8D25: sendBeacon \u8FD4\u56DE false\uFF0C\u6570\u636E\u5DF2\u91CD\u65B0\u52A0\u5165\u961F\u5217");
|
|
2094
|
+
|
|
2095
|
+
_this.retryBatchData(data);
|
|
2096
|
+
}
|
|
2097
|
+
} catch (e) {
|
|
2098
|
+
// sendBeacon 失败,重新加入队列以便重试
|
|
2099
|
+
_this.printLog("\u6279\u91CF\u53D1\u9001\u5931\u8D25: " + e + "\uFF0C\u6570\u636E\u5DF2\u91CD\u65B0\u52A0\u5165\u961F\u5217");
|
|
2100
|
+
|
|
2101
|
+
_this.retryBatchData(data);
|
|
2102
|
+
}
|
|
2103
|
+
} else {
|
|
2104
|
+
// 使用 XMLHttpRequest 发送
|
|
2105
|
+
_this.ajax({
|
|
2106
|
+
url: serverUrl,
|
|
2107
|
+
type: "POST",
|
|
2108
|
+
data: JSON.stringify({
|
|
2109
|
+
events: data
|
|
2110
|
+
}),
|
|
2111
|
+
contentType: contentType,
|
|
2112
|
+
credentials: false,
|
|
2113
|
+
timeout: _this.initConfig.sendTimeout,
|
|
2114
|
+
cors: true,
|
|
2115
|
+
success: function success() {
|
|
2116
|
+
// 批量发送成功,确保 LocalStorage 已清空
|
|
2117
|
+
// flushBatchQueue 在发送前已清空 LocalStorage,这里再次确认
|
|
2118
|
+
_this.saveBatchQueueToStorage([]);
|
|
2100
2119
|
|
|
2101
2120
|
if (_this.initConfig.showLog) {
|
|
2102
|
-
_this.printLog("\
|
|
2121
|
+
_this.printLog("\u6279\u91CF\u53D1\u9001\u6210\u529F: " + data.length + " \u6761\u6570\u636E");
|
|
2103
2122
|
}
|
|
2123
|
+
},
|
|
2124
|
+
error: function error(err) {
|
|
2125
|
+
// 批量发送失败,重新加入队列以便重试
|
|
2126
|
+
_this.printLog("\u6279\u91CF\u53D1\u9001\u5931\u8D25: " + err + "\uFF0C\u6570\u636E\u5DF2\u91CD\u65B0\u52A0\u5165\u961F\u5217");
|
|
2127
|
+
|
|
2128
|
+
_this.retryBatchData(data);
|
|
2104
2129
|
}
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
2132
|
+
};
|
|
2133
|
+
/**
|
|
2134
|
+
* @description 批量数据重试逻辑
|
|
2135
|
+
* @param data 批量数据
|
|
2136
|
+
*/
|
|
2137
|
+
|
|
2138
|
+
|
|
2139
|
+
_this.retryBatchData = function (data) {
|
|
2140
|
+
// 获取当前队列
|
|
2141
|
+
var currentQueue = _this.getBatchQueueFromStorage(); // 去重:基于事件类型、itemKey、requestTime 生成唯一键
|
|
2142
|
+
|
|
2143
|
+
|
|
2144
|
+
var getEventKey = function getEventKey(item) {
|
|
2145
|
+
return item.event + "_" + item.itemKey + "_" + item.requestTime;
|
|
2146
|
+
};
|
|
2147
|
+
|
|
2148
|
+
var existingKeys = new Set(currentQueue.map(getEventKey));
|
|
2149
|
+
var maxRetryCount = 3; // 最大重试次数
|
|
2150
|
+
|
|
2151
|
+
var currentTime = _this.getTimeStamp(); // 过滤并更新重试信息
|
|
2152
|
+
|
|
2153
|
+
|
|
2154
|
+
var retryData = data.filter(function (item) {
|
|
2155
|
+
var key = getEventKey(item); // 检查是否已存在
|
|
2156
|
+
|
|
2157
|
+
if (existingKeys.has(key)) {
|
|
2158
|
+
return false;
|
|
2159
|
+
} // 检查重试次数
|
|
2160
|
+
|
|
2161
|
+
|
|
2162
|
+
var retryCount = (item._retryCount || 0) + 1;
|
|
2163
|
+
|
|
2164
|
+
if (retryCount > maxRetryCount) {
|
|
2165
|
+
if (_this.initConfig.showLog) {
|
|
2166
|
+
_this.printLog("\u6570\u636E\u5DF2\u8FBE\u5230\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF0C\u653E\u5F03\u91CD\u8BD5: " + key);
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
return false;
|
|
2170
|
+
} // 更新重试信息
|
|
2171
|
+
|
|
2172
|
+
|
|
2173
|
+
item._retryCount = retryCount; // 指数退避:2^retryCount * 1000ms
|
|
2174
|
+
|
|
2175
|
+
item._nextRetryTime = currentTime + Math.pow(2, retryCount) * 1000;
|
|
2176
|
+
existingKeys.add(key);
|
|
2177
|
+
return true;
|
|
2178
|
+
}); // 将失败的数据重新加入队列(添加到队列前面,优先重试)
|
|
2179
|
+
|
|
2180
|
+
var retryQueue = __spreadArray(__spreadArray([], retryData), currentQueue); // 限制重试队列大小,避免内存溢出
|
|
2181
|
+
|
|
2182
|
+
|
|
2183
|
+
var maxSize = _this.initConfig.batchMaxSize * 2;
|
|
2184
|
+
var trimmedQueue = retryQueue.length > maxSize ? retryQueue.slice(0, maxSize) : retryQueue; // 保存失败的数据到 LocalStorage,确保数据不丢失
|
|
2185
|
+
|
|
2186
|
+
_this.saveBatchQueueToStorage(trimmedQueue);
|
|
2187
|
+
|
|
2188
|
+
if (_this.initConfig.showLog) {
|
|
2189
|
+
_this.printLog("\u5DF2\u5C06 " + retryData.length + " \u6761\u6570\u636E\u52A0\u5165\u91CD\u8BD5\u961F\u5217");
|
|
2190
|
+
}
|
|
2191
|
+
};
|
|
2192
|
+
/**
|
|
2193
|
+
* 添加到批量队列
|
|
2194
|
+
* @param params 数据参数
|
|
2195
|
+
*/
|
|
2196
|
+
|
|
2197
|
+
|
|
2198
|
+
_this.addToBatchQueue = function (params) {
|
|
2199
|
+
var _a = _this.initConfig,
|
|
2200
|
+
batchInterval = _a.batchInterval,
|
|
2201
|
+
batchMaxSize = _a.batchMaxSize; // 数据采样判断(在添加到队列前判断)
|
|
2202
|
+
|
|
2203
|
+
if (!_this.shouldSample()) {
|
|
2204
|
+
if (_this.initConfig.showLog) {
|
|
2205
|
+
_this.printLog("数据已采样跳过(批量模式)");
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
return;
|
|
2209
|
+
} // 从 LocalStorage 获取当前队列
|
|
2210
|
+
|
|
2211
|
+
|
|
2212
|
+
var currentQueue = _this.getBatchQueueFromStorage(); // 添加新数据
|
|
2213
|
+
|
|
2214
|
+
|
|
2215
|
+
currentQueue.push(params); // 保存到 LocalStorage
|
|
2216
|
+
|
|
2217
|
+
_this.saveBatchQueueToStorage(currentQueue); // 如果队列达到最大数量,立即发送
|
|
2218
|
+
|
|
2219
|
+
|
|
2220
|
+
if (currentQueue.length >= batchMaxSize) {
|
|
2221
|
+
_this.flushBatchQueue();
|
|
2222
|
+
|
|
2223
|
+
return;
|
|
2224
|
+
} // 设置定时发送
|
|
2225
|
+
|
|
2226
|
+
|
|
2227
|
+
if (!_this.batchTimer) {
|
|
2228
|
+
_this.batchTimer = window.setTimeout(function () {
|
|
2229
|
+
_this.flushBatchQueue();
|
|
2230
|
+
|
|
2231
|
+
_this.batchTimer = null;
|
|
2232
|
+
}, batchInterval);
|
|
2233
|
+
}
|
|
2234
|
+
};
|
|
2235
|
+
/**
|
|
2236
|
+
* 从 LocalStorage 恢复批量队列
|
|
2237
|
+
*/
|
|
2238
|
+
|
|
2239
|
+
|
|
2240
|
+
_this.restoreBatchQueueFromStorage = function () {
|
|
2241
|
+
var batchQueue = _this.getBatchQueueFromStorage();
|
|
2242
|
+
|
|
2243
|
+
if (batchQueue.length > 0) {
|
|
2244
|
+
if (_this.initConfig.showLog) {
|
|
2245
|
+
_this.printLog("\u4ECE LocalStorage \u6062\u590D " + batchQueue.length + " \u6761\u5F85\u53D1\u9001\u6570\u636E");
|
|
2246
|
+
} // 恢复后立即尝试发送(如果达到条件)
|
|
2247
|
+
|
|
2248
|
+
|
|
2249
|
+
var batchMaxSize = _this.initConfig.batchMaxSize;
|
|
2250
|
+
|
|
2251
|
+
if (batchQueue.length >= batchMaxSize) {
|
|
2252
|
+
_this.flushBatchQueue();
|
|
2105
2253
|
} else {
|
|
2106
|
-
//
|
|
2107
|
-
|
|
2108
|
-
_this.saveBatchQueueToStorage();
|
|
2109
|
-
} // 保存 pendingRequests 到 LocalStorage(如果支持)
|
|
2254
|
+
// 设置定时发送
|
|
2255
|
+
var batchInterval = _this.initConfig.batchInterval;
|
|
2110
2256
|
|
|
2257
|
+
if (!_this.batchTimer) {
|
|
2258
|
+
_this.batchTimer = window.setTimeout(function () {
|
|
2259
|
+
_this.flushBatchQueue();
|
|
2111
2260
|
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
_this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, JSON.stringify(_this.pendingRequests));
|
|
2115
|
-
} catch (e) {// LocalStorage 可能已满或不可用
|
|
2116
|
-
}
|
|
2261
|
+
_this.batchTimer = null;
|
|
2262
|
+
}, batchInterval);
|
|
2117
2263
|
}
|
|
2118
2264
|
}
|
|
2119
|
-
}
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2265
|
+
}
|
|
2266
|
+
};
|
|
2267
|
+
/**
|
|
2268
|
+
* 添加到待发送请求队列
|
|
2269
|
+
* @param params 数据参数
|
|
2270
|
+
*/
|
|
2271
|
+
|
|
2272
|
+
|
|
2273
|
+
_this.addToPendingRequests = function (params) {
|
|
2274
|
+
// 从 LocalStorage 获取当前队列
|
|
2275
|
+
var currentRequests = _this.getPendingRequestsFromStorage(); // 添加新数据
|
|
2276
|
+
|
|
2277
|
+
|
|
2278
|
+
currentRequests.push(params); // 限制队列大小,防止内存溢出
|
|
2279
|
+
|
|
2280
|
+
var maxSize = _this.initConfig.pendingRequestsMaxSize || _this.DEFAULT_PENDING_REQUESTS_MAX_SIZE;
|
|
2281
|
+
|
|
2282
|
+
if (currentRequests.length > maxSize) {
|
|
2283
|
+
var trimmedRequests = currentRequests.slice(-maxSize);
|
|
2284
|
+
|
|
2285
|
+
if (_this.initConfig.showLog) {
|
|
2286
|
+
_this.printLog("\u5F85\u53D1\u9001\u8BF7\u6C42\u961F\u5217\u5DF2\u6EE1\uFF0C\u5DF2\u79FB\u9664\u6700\u65E7\u7684\u6570\u636E\uFF08\u6700\u5927\u9650\u5236: " + maxSize + "\uFF09");
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
_this.savePendingRequestsToStorage(trimmedRequests);
|
|
2290
|
+
} else {
|
|
2291
|
+
_this.savePendingRequestsToStorage(currentRequests);
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
/**
|
|
2295
|
+
* 从 LocalStorage 恢复待发送请求
|
|
2296
|
+
*/
|
|
2297
|
+
|
|
2298
|
+
|
|
2299
|
+
_this.restorePendingRequestsFromStorage = function () {
|
|
2300
|
+
var pendingRequests = _this.getPendingRequestsFromStorage();
|
|
2301
|
+
|
|
2302
|
+
if (pendingRequests.length > 0) {
|
|
2303
|
+
if (_this.initConfig.showLog) {
|
|
2304
|
+
_this.printLog("\u4ECE LocalStorage \u6062\u590D " + pendingRequests.length + " \u6761\u5F85\u53D1\u9001\u8BF7\u6C42");
|
|
2305
|
+
} // 注意:恢复后不立即发送,避免重复
|
|
2306
|
+
// 数据会留在 LocalStorage 中,等待下次正常发送或页面卸载时发送
|
|
2307
|
+
// 这样可以避免与批量队列冲突
|
|
2308
|
+
|
|
2309
|
+
}
|
|
2310
|
+
};
|
|
2311
|
+
/**
|
|
2312
|
+
* 检查页面是否即将卸载
|
|
2313
|
+
* @returns 如果页面即将卸载返回 true,否则返回 false
|
|
2314
|
+
*/
|
|
2315
|
+
|
|
2316
|
+
|
|
2317
|
+
_this.isPageUnloading = function () {
|
|
2318
|
+
return document.visibilityState === "hidden";
|
|
2319
|
+
};
|
|
2320
|
+
/**
|
|
2321
|
+
* 使用 sendBeacon 发送数据(页面卸载时的备用方案)
|
|
2322
|
+
* @param params 数据参数
|
|
2323
|
+
* @param serverUrl 服务器地址
|
|
2324
|
+
* @param contentType 内容类型
|
|
2325
|
+
* @returns 是否发送成功
|
|
2326
|
+
*/
|
|
2327
|
+
|
|
2328
|
+
|
|
2329
|
+
_this.sendWithBeacon = function (params, serverUrl, contentType) {
|
|
2330
|
+
try {
|
|
2331
|
+
var blob = new Blob([JSON.stringify(params)], {
|
|
2332
|
+
type: contentType || "application/json"
|
|
2333
|
+
});
|
|
2334
|
+
return navigator.sendBeacon(serverUrl, blob);
|
|
2335
|
+
} catch (e) {
|
|
2336
|
+
if (_this.initConfig.showLog) {
|
|
2337
|
+
_this.printLog("sendBeacon \u53D1\u9001\u5931\u8D25: " + e);
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
return false;
|
|
2341
|
+
}
|
|
2342
|
+
};
|
|
2343
|
+
/**
|
|
2344
|
+
* 刷新待发送的单个请求(正常情况下的发送)
|
|
2345
|
+
* 注意:这个方法会直接使用 ajax 发送,避免通过 sendData 导致重复
|
|
2346
|
+
*/
|
|
2347
|
+
|
|
2348
|
+
|
|
2349
|
+
_this.flushPendingRequests = function () {
|
|
2350
|
+
var pendingRequests = _this.getPendingRequestsFromStorage();
|
|
2351
|
+
|
|
2352
|
+
if (pendingRequests.length === 0) {
|
|
2353
|
+
return;
|
|
2354
|
+
} // 清除 LocalStorage 中的待发送请求
|
|
2123
2355
|
|
|
2124
2356
|
|
|
2125
|
-
_this.
|
|
2357
|
+
_this.savePendingRequestsToStorage([]); // 直接使用 ajax 发送每个请求,避免通过 sendData 导致重复
|
|
2358
|
+
|
|
2359
|
+
|
|
2360
|
+
var _a = _this.initConfig,
|
|
2361
|
+
serverUrl = _a.serverUrl,
|
|
2362
|
+
sendTimeout = _a.sendTimeout,
|
|
2363
|
+
contentType = _a.contentType,
|
|
2364
|
+
showLog = _a.showLog,
|
|
2365
|
+
initHeader = _a.header;
|
|
2366
|
+
pendingRequests.forEach(function (params) {
|
|
2126
2367
|
// 数据采样判断
|
|
2127
2368
|
if (!_this.shouldSample()) {
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2369
|
+
if (showLog) {
|
|
2370
|
+
_this.printLog("待发送请求已采样跳过");
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
return;
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
if (showLog) {
|
|
2377
|
+
_this.printLog(params);
|
|
2378
|
+
} // 直接使用 ajax 发送
|
|
2379
|
+
|
|
2380
|
+
|
|
2381
|
+
_this.ajax({
|
|
2382
|
+
header: initHeader,
|
|
2383
|
+
url: serverUrl,
|
|
2384
|
+
type: "POST",
|
|
2385
|
+
data: JSON.stringify(params),
|
|
2386
|
+
contentType: contentType,
|
|
2387
|
+
credentials: false,
|
|
2388
|
+
timeout: sendTimeout,
|
|
2389
|
+
cors: true,
|
|
2390
|
+
success: function success() {
|
|
2391
|
+
if (showLog) {
|
|
2392
|
+
_this.printLog("待发送请求发送成功");
|
|
2393
|
+
}
|
|
2394
|
+
},
|
|
2395
|
+
error: function error(err) {
|
|
2396
|
+
if (showLog) {
|
|
2397
|
+
_this.printLog("\u5F85\u53D1\u9001\u8BF7\u6C42\u53D1\u9001\u5931\u8D25\uFF08\u4E0D\u518D\u91CD\u8BD5\uFF09: " + err);
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2401
|
+
});
|
|
2402
|
+
};
|
|
2403
|
+
/**
|
|
2404
|
+
* 设置页面卸载监听器,确保数据发送
|
|
2405
|
+
*/
|
|
2406
|
+
|
|
2407
|
+
|
|
2408
|
+
_this.setupBeforeUnloadListener = function () {
|
|
2409
|
+
// 避免重复设置监听器
|
|
2410
|
+
if (_this.isUnloadListenerSetup) {
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
_this.isUnloadListenerSetup = true; // 使用 visibilitychange 事件(更可靠,支持页面跳转、切换标签页等场景)
|
|
2415
|
+
|
|
2416
|
+
document.addEventListener("visibilitychange", function () {
|
|
2417
|
+
if (_this.isPageUnloading()) {
|
|
2418
|
+
_this.flushPendingData();
|
|
2419
|
+
}
|
|
2420
|
+
}); // 使用 beforeunload 事件作为备用(页面关闭/刷新)
|
|
2421
|
+
|
|
2422
|
+
window.addEventListener("beforeunload", function () {
|
|
2423
|
+
_this.flushPendingData();
|
|
2424
|
+
}); // 使用 pagehide 事件(更可靠,支持移动端)
|
|
2425
|
+
|
|
2426
|
+
window.addEventListener("pagehide", function () {
|
|
2427
|
+
_this.flushPendingData();
|
|
2428
|
+
});
|
|
2429
|
+
}; // 标记是否正在刷新待发送数据,避免重复发送
|
|
2430
|
+
|
|
2431
|
+
|
|
2432
|
+
_this.isFlushingPendingData = false;
|
|
2433
|
+
/**
|
|
2434
|
+
* 刷新待发送数据(在页面卸载/跳转时调用)
|
|
2435
|
+
*/
|
|
2436
|
+
|
|
2437
|
+
_this.flushPendingData = function () {
|
|
2438
|
+
// 如果正在刷新,避免重复执行
|
|
2439
|
+
if (_this.isFlushingPendingData) {
|
|
2440
|
+
return;
|
|
2441
|
+
} // 页面卸载时停止定时器
|
|
2442
|
+
|
|
2443
|
+
|
|
2444
|
+
_this.stopPageDurationTimer(); // 收集所有待发送的数据
|
|
2445
|
+
|
|
2446
|
+
|
|
2447
|
+
var allPendingData = []; // 如果有批量队列,添加到待发送列表
|
|
2448
|
+
|
|
2449
|
+
var batchQueue = _this.getBatchQueueFromStorage();
|
|
2450
|
+
|
|
2451
|
+
if (batchQueue.length > 0) {
|
|
2452
|
+
allPendingData.push.apply(allPendingData, batchQueue);
|
|
2453
|
+
} // 如果有待发送的单个请求,也添加到列表
|
|
2454
|
+
|
|
2455
|
+
|
|
2456
|
+
var pendingRequests = _this.getPendingRequestsFromStorage();
|
|
2457
|
+
|
|
2458
|
+
if (pendingRequests.length > 0) {
|
|
2459
|
+
allPendingData.push.apply(allPendingData, pendingRequests);
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
if (allPendingData.length === 0) {
|
|
2463
|
+
return;
|
|
2464
|
+
} // 标记正在刷新
|
|
2465
|
+
|
|
2466
|
+
|
|
2467
|
+
_this.isFlushingPendingData = true; // 先保存到 LocalStorage,确保数据不丢失(在发送前保存)
|
|
2468
|
+
|
|
2469
|
+
try {
|
|
2470
|
+
if (_this.initConfig.batchSend) {
|
|
2471
|
+
_this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, JSON.stringify(allPendingData));
|
|
2472
|
+
} else {
|
|
2473
|
+
_this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, JSON.stringify(allPendingData));
|
|
2474
|
+
}
|
|
2475
|
+
} catch (e) {
|
|
2476
|
+
if (_this.initConfig.showLog) {
|
|
2477
|
+
_this.printLog("\u4FDD\u5B58\u5F85\u53D1\u9001\u8BF7\u6C42\u5230 LocalStorage \u5931\u8D25: " + e);
|
|
2478
|
+
}
|
|
2479
|
+
} // 使用 sendBeacon 发送数据(最可靠的方式)
|
|
2480
|
+
|
|
2481
|
+
|
|
2482
|
+
if (navigator.sendBeacon && _this.initConfig.serverUrl) {
|
|
2483
|
+
try {
|
|
2484
|
+
// 如果只有一条数据,直接发送;否则批量发送
|
|
2485
|
+
var dataToSend = allPendingData.length === 1 ? allPendingData[0] : allPendingData;
|
|
2486
|
+
var blob = new Blob([JSON.stringify(dataToSend)], {
|
|
2487
|
+
type: _this.initConfig.contentType || "application/json"
|
|
2131
2488
|
});
|
|
2489
|
+
var sent = navigator.sendBeacon(_this.initConfig.serverUrl, blob);
|
|
2490
|
+
|
|
2491
|
+
if (sent) {
|
|
2492
|
+
// 发送成功,清除所有 LocalStorage
|
|
2493
|
+
_this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, "[]");
|
|
2494
|
+
|
|
2495
|
+
_this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, "[]");
|
|
2496
|
+
|
|
2497
|
+
if (_this.initConfig.showLog) {
|
|
2498
|
+
_this.printLog("\u9875\u9762\u5378\u8F7D\u65F6\u6210\u529F\u53D1\u9001 " + allPendingData.length + " \u6761\u6570\u636E");
|
|
2499
|
+
}
|
|
2500
|
+
} else {
|
|
2501
|
+
// sendBeacon 返回 false,数据已在 LocalStorage 中,等待下次恢复
|
|
2502
|
+
if (_this.initConfig.showLog) {
|
|
2503
|
+
_this.printLog("sendBeacon \u8FD4\u56DE false\uFF0C\u6570\u636E\u5DF2\u4FDD\u5B58\u5230 LocalStorage \u7B49\u5F85\u4E0B\u6B21\u6062\u590D");
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
} catch (e) {
|
|
2507
|
+
// sendBeacon 失败,数据已在 LocalStorage 中,等待下次恢复
|
|
2508
|
+
if (_this.initConfig.showLog) {
|
|
2509
|
+
_this.printLog("\u9875\u9762\u5378\u8F7D\u65F6\u53D1\u9001\u6570\u636E\u5931\u8D25: " + e + "\uFF0C\u6570\u636E\u5DF2\u4FDD\u5B58\u5230 LocalStorage");
|
|
2510
|
+
}
|
|
2511
|
+
} finally {
|
|
2512
|
+
// 重置标记
|
|
2513
|
+
_this.isFlushingPendingData = false;
|
|
2132
2514
|
}
|
|
2515
|
+
} else {
|
|
2516
|
+
// 不支持 sendBeacon,数据已在 LocalStorage 中,等待下次恢复
|
|
2517
|
+
if (_this.initConfig.showLog) {
|
|
2518
|
+
_this.printLog("\u4E0D\u652F\u6301 sendBeacon\uFF0C\u6570\u636E\u5DF2\u4FDD\u5B58\u5230 LocalStorage \u7B49\u5F85\u4E0B\u6B21\u6062\u590D");
|
|
2519
|
+
} // 重置标记
|
|
2133
2520
|
|
|
2134
|
-
var _a = _this.initConfig,
|
|
2135
|
-
serverUrl = _a.serverUrl,
|
|
2136
|
-
sendTimeout = _a.sendTimeout,
|
|
2137
|
-
contentType = _a.contentType,
|
|
2138
|
-
showLog = _a.showLog,
|
|
2139
|
-
initHeader = _a.header,
|
|
2140
|
-
batchSend = _a.batchSend;
|
|
2141
|
-
if (!!showLog) _this.printLog(params); // 如果启用批量发送
|
|
2142
2521
|
|
|
2143
|
-
|
|
2144
|
-
|
|
2522
|
+
_this.isFlushingPendingData = false;
|
|
2523
|
+
}
|
|
2524
|
+
};
|
|
2525
|
+
/**
|
|
2526
|
+
* 发送数据通用函数
|
|
2527
|
+
*/
|
|
2528
|
+
|
|
2529
|
+
|
|
2530
|
+
_this.sendData = function (params, header) {
|
|
2531
|
+
// 数据采样判断
|
|
2532
|
+
if (!_this.shouldSample()) {
|
|
2533
|
+
return Promise.resolve({
|
|
2534
|
+
success: true,
|
|
2535
|
+
message: "数据已采样跳过"
|
|
2536
|
+
});
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
var _a = _this.initConfig,
|
|
2540
|
+
serverUrl = _a.serverUrl,
|
|
2541
|
+
sendTimeout = _a.sendTimeout,
|
|
2542
|
+
contentType = _a.contentType,
|
|
2543
|
+
showLog = _a.showLog,
|
|
2544
|
+
initHeader = _a.header,
|
|
2545
|
+
batchSend = _a.batchSend,
|
|
2546
|
+
sendMethod = _a.sendMethod;
|
|
2547
|
+
if (!!showLog) _this.printLog(params); // 如果启用批量发送
|
|
2548
|
+
|
|
2549
|
+
if (batchSend) {
|
|
2550
|
+
_this.addToBatchQueue(params);
|
|
2551
|
+
|
|
2552
|
+
return Promise.resolve({
|
|
2553
|
+
success: true,
|
|
2554
|
+
message: "已添加到批量队列"
|
|
2555
|
+
});
|
|
2556
|
+
} // 判断是否使用 sendBeacon
|
|
2557
|
+
|
|
2558
|
+
|
|
2559
|
+
var shouldUseBeacon = _this.shouldUseBeacon(sendMethod, header, initHeader); // 如果使用 sendBeacon
|
|
2560
|
+
|
|
2561
|
+
|
|
2562
|
+
if (shouldUseBeacon) {
|
|
2563
|
+
// 检查页面是否即将卸载,如果是,直接使用 sendBeacon 发送,避免被取消
|
|
2564
|
+
if (_this.isPageUnloading()) {
|
|
2565
|
+
var sent = _this.sendWithBeacon(params, serverUrl, contentType);
|
|
2566
|
+
|
|
2567
|
+
if (sent) {
|
|
2568
|
+
return Promise.resolve({
|
|
2569
|
+
success: true,
|
|
2570
|
+
message: "页面卸载时发送成功"
|
|
2571
|
+
});
|
|
2572
|
+
} else {
|
|
2573
|
+
// sendBeacon 返回 false,添加到待发送队列
|
|
2574
|
+
_this.addToPendingRequests(params);
|
|
2575
|
+
|
|
2576
|
+
return Promise.resolve({
|
|
2577
|
+
success: true,
|
|
2578
|
+
message: "已添加到待发送队列"
|
|
2579
|
+
});
|
|
2580
|
+
}
|
|
2581
|
+
} // 正常情况使用 sendBeacon
|
|
2582
|
+
|
|
2583
|
+
|
|
2584
|
+
return _this.sendBeacon({
|
|
2585
|
+
contentType: contentType,
|
|
2586
|
+
url: serverUrl,
|
|
2587
|
+
data: params
|
|
2588
|
+
}).catch(function (err) {
|
|
2589
|
+
// sendBeacon 失败,添加到待发送队列,避免数据丢失
|
|
2590
|
+
_this.addToPendingRequests(params);
|
|
2145
2591
|
|
|
2146
2592
|
return Promise.resolve({
|
|
2147
2593
|
success: true,
|
|
2148
|
-
message: "
|
|
2594
|
+
message: "sendBeacon 失败,已添加到待发送队列"
|
|
2149
2595
|
});
|
|
2150
|
-
}
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
//
|
|
2155
|
-
if (_this.isPageUnloading()) {
|
|
2596
|
+
});
|
|
2597
|
+
} else {
|
|
2598
|
+
// 使用 XMLHttpRequest 发送
|
|
2599
|
+
return new Promise(function (resolve, reject) {
|
|
2600
|
+
// 如果页面即将卸载且配置为 auto,尝试使用 sendBeacon 作为备用
|
|
2601
|
+
if (_this.isPageUnloading() && sendMethod === 'auto' && _this.isSupportBeaconSend() && !header && !initHeader) {
|
|
2156
2602
|
var sent = _this.sendWithBeacon(params, serverUrl, contentType);
|
|
2157
2603
|
|
|
2158
2604
|
if (sent) {
|
|
2159
|
-
|
|
2605
|
+
resolve({
|
|
2160
2606
|
success: true,
|
|
2161
|
-
message: "
|
|
2607
|
+
message: "页面卸载时使用 sendBeacon 发送成功"
|
|
2162
2608
|
});
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2609
|
+
return;
|
|
2610
|
+
} // sendBeacon 失败,继续使用 XMLHttpRequest
|
|
2611
|
+
|
|
2612
|
+
}
|
|
2166
2613
|
|
|
2167
|
-
|
|
2614
|
+
_this.ajax({
|
|
2615
|
+
header: header || initHeader,
|
|
2616
|
+
url: serverUrl,
|
|
2617
|
+
type: "POST",
|
|
2618
|
+
data: JSON.stringify(params),
|
|
2619
|
+
contentType: contentType,
|
|
2620
|
+
credentials: false,
|
|
2621
|
+
timeout: sendTimeout,
|
|
2622
|
+
cors: true,
|
|
2623
|
+
success: function success(res) {
|
|
2624
|
+
return resolve({
|
|
2168
2625
|
success: true,
|
|
2169
|
-
|
|
2626
|
+
data: res
|
|
2627
|
+
});
|
|
2628
|
+
},
|
|
2629
|
+
error: function error(err, status) {
|
|
2630
|
+
// 如果请求失败且页面即将卸载且配置为 auto,尝试使用 sendBeacon
|
|
2631
|
+
if (_this.isPageUnloading() && sendMethod === 'auto' && _this.isSupportBeaconSend() && !header && !initHeader) {
|
|
2632
|
+
var sent = _this.sendWithBeacon(params, serverUrl, contentType);
|
|
2633
|
+
|
|
2634
|
+
if (sent) {
|
|
2635
|
+
resolve({
|
|
2636
|
+
success: true,
|
|
2637
|
+
message: "XMLHttpRequest 失败,已使用 sendBeacon 发送"
|
|
2638
|
+
});
|
|
2639
|
+
return;
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
reject({
|
|
2644
|
+
success: false,
|
|
2645
|
+
message: String(err),
|
|
2646
|
+
code: status
|
|
2170
2647
|
});
|
|
2171
2648
|
}
|
|
2172
|
-
}
|
|
2649
|
+
});
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
};
|
|
2653
|
+
/**
|
|
2654
|
+
* @description 判断是否应该使用 sendBeacon
|
|
2655
|
+
* @param sendMethod 配置的发送方式
|
|
2656
|
+
* @param header 自定义 header
|
|
2657
|
+
* @param initHeader 初始化配置的 header
|
|
2658
|
+
* @returns 是否使用 sendBeacon
|
|
2659
|
+
*/
|
|
2173
2660
|
|
|
2174
2661
|
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
// sendBeacon 失败,添加到待发送队列,避免数据丢失
|
|
2181
|
-
_this.addToPendingRequests(params);
|
|
2662
|
+
_this.shouldUseBeacon = function (sendMethod, header, initHeader) {
|
|
2663
|
+
// 如果配置为 xhr,不使用 beacon
|
|
2664
|
+
if (sendMethod === 'xhr') {
|
|
2665
|
+
return false;
|
|
2666
|
+
} // 如果配置为 beacon,检查是否支持
|
|
2182
2667
|
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2668
|
+
|
|
2669
|
+
if (sendMethod === 'beacon') {
|
|
2670
|
+
return _this.isSupportBeaconSend() === true;
|
|
2671
|
+
} // 如果配置为 auto(默认),使用原有逻辑
|
|
2672
|
+
// 只有在支持 sendBeacon 且没有自定义 header 时才使用
|
|
2673
|
+
|
|
2674
|
+
|
|
2675
|
+
return _this.isSupportBeaconSend() === true && !header && !initHeader;
|
|
2676
|
+
};
|
|
2677
|
+
/**
|
|
2678
|
+
* @description 留存时长上报
|
|
2679
|
+
* @param type
|
|
2680
|
+
*/
|
|
2681
|
+
|
|
2682
|
+
|
|
2683
|
+
_this.sendRetained = function (type) {
|
|
2684
|
+
var params = _this.getParams({
|
|
2685
|
+
event: "PageRetained",
|
|
2686
|
+
desc: _this.eventDescMap["PageRetained"]
|
|
2687
|
+
});
|
|
2688
|
+
|
|
2689
|
+
if (["beforeunload", "pushState", "replaceState", "hashchange", "popstate"].indexOf(type) >= 0) {
|
|
2690
|
+
var __time = _this.getCookie("retainedStartTime");
|
|
2691
|
+
|
|
2692
|
+
var retainedStartTime = __time ? +__time : _this.getTimeStamp();
|
|
2693
|
+
|
|
2694
|
+
var retainedData = __assign(__assign({}, params), {
|
|
2695
|
+
privateParamMap: __assign(__assign({}, params.privateParamMap), {
|
|
2696
|
+
retainedDuration: Math.max(params.requestTime - retainedStartTime, 0)
|
|
2697
|
+
})
|
|
2698
|
+
});
|
|
2699
|
+
|
|
2700
|
+
_this.sendData(retainedData);
|
|
2701
|
+
|
|
2702
|
+
_this.setCookie("retainedStartTime", _this.getTimeStamp());
|
|
2703
|
+
}
|
|
2704
|
+
};
|
|
2705
|
+
/**
|
|
2706
|
+
* @description 用户主动上报页面停留时长
|
|
2707
|
+
* @param duration 自定义停留时长(毫秒),如果不传则自动计算从页面加载(或上次调用)到当前的时长
|
|
2708
|
+
* @param options 可选参数,包括自定义描述、业务参数等
|
|
2709
|
+
* @param resetStartTime 是否重置起始时间,默认 true(手动上报后重置,定时上报不重置)
|
|
2710
|
+
* @returns Promise<TrackingResponse> 上报结果
|
|
2711
|
+
*/
|
|
2712
|
+
|
|
2713
|
+
|
|
2714
|
+
_this.trackPageDuration = function (duration, options, resetStartTime) {
|
|
2715
|
+
if (resetStartTime === void 0) {
|
|
2716
|
+
resetStartTime = true;
|
|
2717
|
+
} // 计算停留时长
|
|
2718
|
+
|
|
2719
|
+
|
|
2720
|
+
var retainedDuration;
|
|
2721
|
+
|
|
2722
|
+
if (duration !== undefined && duration !== null) {
|
|
2723
|
+
// 使用自定义时长
|
|
2724
|
+
retainedDuration = Math.max(duration, 0);
|
|
2725
|
+
} else {
|
|
2726
|
+
// 自动计算时长
|
|
2727
|
+
var __time = _this.getCookie("retainedStartTime");
|
|
2728
|
+
|
|
2729
|
+
var retainedStartTime = __time ? +__time : _this.getTimeStamp();
|
|
2730
|
+
|
|
2731
|
+
var currentTime = _this.getTimeStamp();
|
|
2732
|
+
|
|
2733
|
+
retainedDuration = Math.max(currentTime - retainedStartTime, 0);
|
|
2734
|
+
} // 构建参数
|
|
2735
|
+
|
|
2736
|
+
|
|
2737
|
+
var desc = (options === null || options === void 0 ? void 0 : options.desc) || _this.eventDescMap["PageRetained"];
|
|
2738
|
+
var pageKey = (options === null || options === void 0 ? void 0 : options.pageKey) || _this.pageKey;
|
|
2739
|
+
var business = (options === null || options === void 0 ? void 0 : options.business) || {};
|
|
2740
|
+
var header = options === null || options === void 0 ? void 0 : options.header;
|
|
2741
|
+
|
|
2742
|
+
var params = _this.getParams({
|
|
2743
|
+
event: "PageRetained",
|
|
2744
|
+
desc: desc,
|
|
2745
|
+
itemKey: _this.getItemKey(undefined, pageKey),
|
|
2746
|
+
privateParamMap: {
|
|
2747
|
+
business: business,
|
|
2748
|
+
retainedDuration: retainedDuration
|
|
2749
|
+
}
|
|
2750
|
+
}); // 上报数据
|
|
2751
|
+
|
|
2752
|
+
|
|
2753
|
+
var result = _this.sendData(params, header); // 根据 resetStartTime 参数决定是否重置起始时间
|
|
2754
|
+
// 手动上报后重置起始时间,定时上报不重置(累积计算)
|
|
2755
|
+
|
|
2756
|
+
|
|
2757
|
+
if (resetStartTime) {
|
|
2758
|
+
_this.setCookie("retainedStartTime", _this.getTimeStamp());
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
return result;
|
|
2762
|
+
};
|
|
2763
|
+
/**
|
|
2764
|
+
* @description 启动定时上报页面停留时长的定时器
|
|
2765
|
+
*/
|
|
2766
|
+
|
|
2767
|
+
|
|
2768
|
+
_this.startPageDurationTimer = function () {
|
|
2769
|
+
// 先停止现有的定时器(避免重复启动)
|
|
2770
|
+
_this.stopPageDurationTimer();
|
|
2771
|
+
|
|
2772
|
+
var interval = _this.initConfig.pageDurationInterval || 30000; // 检查间隔时间是否有效
|
|
2773
|
+
|
|
2774
|
+
if (interval <= 0) {
|
|
2775
|
+
if (_this.initConfig.showLog) {
|
|
2776
|
+
_this.printLog("定时上报间隔时间无效,已禁用定时上报");
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
return;
|
|
2780
|
+
} // 启动定时器
|
|
2781
|
+
|
|
2782
|
+
|
|
2783
|
+
_this.pageDurationTimer = window.setInterval(function () {
|
|
2784
|
+
// 只在页面可见时上报(避免后台上报)
|
|
2785
|
+
if (document.visibilityState === "visible") {
|
|
2786
|
+
// 定时上报:retainedDuration 直接使用上报间隔时间,数据工程师会清洗数据
|
|
2787
|
+
_this.trackPageDuration(interval, {
|
|
2788
|
+
desc: "定时上报页面停留时长",
|
|
2789
|
+
business: {
|
|
2790
|
+
reportType: "interval",
|
|
2791
|
+
interval: interval
|
|
2792
|
+
}
|
|
2793
|
+
}, true).catch(function (err) {
|
|
2794
|
+
if (_this.initConfig.showLog) {
|
|
2795
|
+
_this.printLog("\u5B9A\u65F6\u4E0A\u62A5\u9875\u9762\u505C\u7559\u65F6\u957F\u5931\u8D25: " + err);
|
|
2796
|
+
}
|
|
2187
2797
|
});
|
|
2188
|
-
}
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2798
|
+
}
|
|
2799
|
+
}, interval);
|
|
2800
|
+
|
|
2801
|
+
if (_this.initConfig.showLog) {
|
|
2802
|
+
_this.printLog("\u5B9A\u65F6\u4E0A\u62A5\u9875\u9762\u505C\u7559\u65F6\u957F\u5DF2\u542F\u52A8\uFF0C\u95F4\u9694: " + interval + "ms");
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
/**
|
|
2806
|
+
* @description 停止定时上报页面停留时长的定时器
|
|
2807
|
+
*/
|
|
2808
|
+
|
|
2809
|
+
|
|
2810
|
+
_this.stopPageDurationTimer = function () {
|
|
2811
|
+
if (_this.pageDurationTimer !== null) {
|
|
2812
|
+
clearInterval(_this.pageDurationTimer);
|
|
2813
|
+
_this.pageDurationTimer = null;
|
|
2814
|
+
|
|
2815
|
+
if (_this.initConfig.showLog) {
|
|
2816
|
+
_this.printLog("定时上报页面停留时长已停止");
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
};
|
|
2820
|
+
/**
|
|
2821
|
+
* @description 获取 itemKey
|
|
2822
|
+
* @param {[string]} partkey [控件/自定义事件的唯一标识]
|
|
2823
|
+
* @return {[string]}
|
|
2824
|
+
*/
|
|
2825
|
+
|
|
2202
2826
|
|
|
2827
|
+
_this.getItemKey = function (partkey, pageKey) {
|
|
2828
|
+
var appKey = _this.initConfig.appKey;
|
|
2829
|
+
var keys = [appKey, (pageKey || _this.pageKey).toString(), partkey ? partkey.toString() : undefined].filter(function (key) {
|
|
2830
|
+
return !!key;
|
|
2831
|
+
});
|
|
2832
|
+
return keys.reduce(function (str, key) {
|
|
2833
|
+
return str + ("" + (str.length ? "." : "")) + key;
|
|
2834
|
+
}, "");
|
|
2835
|
+
};
|
|
2836
|
+
/**
|
|
2837
|
+
* @description 从元素或其祖先节点提取 data-* 属性
|
|
2838
|
+
* @param element 目标元素
|
|
2839
|
+
* @returns 提取的业务参数对象
|
|
2840
|
+
*/
|
|
2841
|
+
|
|
2842
|
+
|
|
2843
|
+
_this.extractDataAttributes = function (element) {
|
|
2844
|
+
var business = {};
|
|
2845
|
+
var currentElement = element;
|
|
2846
|
+
|
|
2847
|
+
while (currentElement) {
|
|
2848
|
+
var attributes = currentElement.attributes;
|
|
2849
|
+
|
|
2850
|
+
for (var i = 0; i < attributes.length; i++) {
|
|
2851
|
+
var attr = attributes[i];
|
|
2852
|
+
var name_1 = attr.name;
|
|
2853
|
+
|
|
2854
|
+
if (name_1.startsWith('data-') && name_1 !== 'data-exposure' && name_1 !== 'data-part-key' && name_1 !== 'data-desc') {
|
|
2855
|
+
var value = attr.value;
|
|
2856
|
+
|
|
2857
|
+
if (value) {
|
|
2858
|
+
var camelCaseKey = name_1.replace(/^data-/, '').split('-').map(function (part, index) {
|
|
2859
|
+
return index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1);
|
|
2860
|
+
}).join('');
|
|
2861
|
+
business[camelCaseKey] = value;
|
|
2203
2862
|
}
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2204
2865
|
|
|
2205
|
-
|
|
2206
|
-
header: header || initHeader,
|
|
2207
|
-
url: serverUrl,
|
|
2208
|
-
type: "POST",
|
|
2209
|
-
data: JSON.stringify(params),
|
|
2210
|
-
contentType: contentType,
|
|
2211
|
-
credentials: false,
|
|
2212
|
-
timeout: sendTimeout,
|
|
2213
|
-
cors: true,
|
|
2214
|
-
success: function success(res) {
|
|
2215
|
-
return resolve({
|
|
2216
|
-
success: true,
|
|
2217
|
-
data: res
|
|
2218
|
-
});
|
|
2219
|
-
},
|
|
2220
|
-
error: function error(err, status) {
|
|
2221
|
-
// 如果请求失败且页面即将卸载,尝试使用 sendBeacon
|
|
2222
|
-
if (_this.isPageUnloading() && _this.isSupportBeaconSend() && !header && !initHeader) {
|
|
2223
|
-
var sent = _this.sendWithBeacon(params, serverUrl, contentType);
|
|
2224
|
-
|
|
2225
|
-
if (sent) {
|
|
2226
|
-
resolve({
|
|
2227
|
-
success: true,
|
|
2228
|
-
message: "XMLHttpRequest 失败,已使用 sendBeacon 发送"
|
|
2229
|
-
});
|
|
2230
|
-
return;
|
|
2231
|
-
}
|
|
2232
|
-
}
|
|
2866
|
+
currentElement = currentElement.parentElement;
|
|
2233
2867
|
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
message: String(err),
|
|
2237
|
-
code: status
|
|
2238
|
-
});
|
|
2239
|
-
}
|
|
2240
|
-
});
|
|
2241
|
-
});
|
|
2868
|
+
if (currentElement && currentElement.tagName === 'BODY') {
|
|
2869
|
+
break;
|
|
2242
2870
|
}
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
return business;
|
|
2874
|
+
};
|
|
2875
|
+
/**
|
|
2876
|
+
* @description 初始化曝光监听
|
|
2877
|
+
*/
|
|
2878
|
+
|
|
2879
|
+
|
|
2880
|
+
_this.initExposureObserver = function () {
|
|
2881
|
+
if (!_this.initConfig.autoTrackExposure) {
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
if (!('IntersectionObserver' in window)) {
|
|
2886
|
+
if (_this.initConfig.showLog) {
|
|
2887
|
+
_this.printLog('当前浏览器不支持 IntersectionObserver,无法启用曝光埋点');
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2893
|
+
var threshold = _this.initConfig.exposureThreshold || 0.5;
|
|
2894
|
+
_this.exposureObserver = new IntersectionObserver(function (entries) {
|
|
2895
|
+
entries.forEach(function (entry) {
|
|
2896
|
+
var element = entry.target;
|
|
2248
2897
|
|
|
2898
|
+
var elementInfo = _this.exposureElementsMap.get(element);
|
|
2249
2899
|
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2900
|
+
if (!elementInfo) {
|
|
2901
|
+
return;
|
|
2902
|
+
}
|
|
2903
|
+
|
|
2904
|
+
var exposureTime = _this.initConfig.exposureTime || 500;
|
|
2905
|
+
|
|
2906
|
+
if (entry.isIntersecting) {
|
|
2907
|
+
elementInfo.isVisible = true;
|
|
2908
|
+
elementInfo.visibleStartTime = _this.getTimeStamp();
|
|
2909
|
+
|
|
2910
|
+
if (elementInfo.exposureTimer) {
|
|
2911
|
+
clearTimeout(elementInfo.exposureTimer);
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
elementInfo.exposureTimer = window.setTimeout(function () {
|
|
2915
|
+
if (elementInfo.isVisible) {
|
|
2916
|
+
_this.reportExposure(element);
|
|
2917
|
+
}
|
|
2918
|
+
}, exposureTime);
|
|
2919
|
+
} else {
|
|
2920
|
+
elementInfo.isVisible = false;
|
|
2921
|
+
|
|
2922
|
+
if (elementInfo.exposureTimer) {
|
|
2923
|
+
clearTimeout(elementInfo.exposureTimer);
|
|
2924
|
+
elementInfo.exposureTimer = null;
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2254
2927
|
});
|
|
2928
|
+
}, {
|
|
2929
|
+
threshold: threshold
|
|
2930
|
+
});
|
|
2931
|
+
|
|
2932
|
+
_this.observeExposureElements();
|
|
2933
|
+
};
|
|
2934
|
+
/**
|
|
2935
|
+
* @description 监听页面上的曝光元素
|
|
2936
|
+
*/
|
|
2255
2937
|
|
|
2256
|
-
if (["beforeunload", "pushState", "replaceState", "hashchange", "popstate"].indexOf(type) >= 0) {
|
|
2257
|
-
var __time = _this.getCookie("retainedStartTime");
|
|
2258
2938
|
|
|
2259
|
-
|
|
2939
|
+
_this.observeExposureElements = function () {
|
|
2940
|
+
if (!_this.exposureObserver) {
|
|
2941
|
+
return;
|
|
2942
|
+
}
|
|
2260
2943
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2944
|
+
var elements = document.querySelectorAll('[data-exposure="true"]');
|
|
2945
|
+
elements.forEach(function (element) {
|
|
2946
|
+
if (!_this.exposureElementsMap.has(element)) {
|
|
2947
|
+
var htmlElement = element;
|
|
2948
|
+
|
|
2949
|
+
_this.exposureElementsMap.set(htmlElement, {
|
|
2950
|
+
element: htmlElement,
|
|
2951
|
+
visibleStartTime: 0,
|
|
2952
|
+
exposureCount: 0,
|
|
2953
|
+
isVisible: false,
|
|
2954
|
+
exposureTimer: null
|
|
2265
2955
|
});
|
|
2266
2956
|
|
|
2267
|
-
_this.
|
|
2957
|
+
_this.exposureObserver.observe(htmlElement);
|
|
2958
|
+
}
|
|
2959
|
+
});
|
|
2960
|
+
|
|
2961
|
+
if (_this.initConfig.showLog && elements.length > 0) {
|
|
2962
|
+
_this.printLog("\u5DF2\u76D1\u542C " + elements.length + " \u4E2A\u66DD\u5149\u5143\u7D20");
|
|
2963
|
+
}
|
|
2964
|
+
};
|
|
2965
|
+
/**
|
|
2966
|
+
* @description 上报曝光事件
|
|
2967
|
+
* @param element 曝光元素
|
|
2968
|
+
*/
|
|
2969
|
+
|
|
2970
|
+
|
|
2971
|
+
_this.reportExposure = function (element) {
|
|
2972
|
+
var elementInfo = _this.exposureElementsMap.get(element);
|
|
2973
|
+
|
|
2974
|
+
if (!elementInfo) {
|
|
2975
|
+
return;
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2978
|
+
var exposureNum = _this.initConfig.exposureNum;
|
|
2268
2979
|
|
|
2269
|
-
|
|
2980
|
+
if (exposureNum !== undefined && elementInfo.exposureCount >= exposureNum) {
|
|
2981
|
+
if (_this.initConfig.showLog) {
|
|
2982
|
+
_this.printLog("\u5143\u7D20\u5DF2\u8FBE\u5230\u6700\u5927\u66DD\u5149\u6B21\u6570\u9650\u5236: " + exposureNum);
|
|
2270
2983
|
}
|
|
2271
|
-
};
|
|
2272
|
-
/**
|
|
2273
|
-
* @description 获取 itemKey
|
|
2274
|
-
* @param {[string]} partkey [控件/自定义事件的唯一标识]
|
|
2275
|
-
* @return {[string]}
|
|
2276
|
-
*/
|
|
2277
2984
|
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2278
2987
|
|
|
2279
|
-
|
|
2280
|
-
var appKey = _this.initConfig.appKey;
|
|
2281
|
-
var keys = [appKey, (pageKey || _this.pageKey).toString(), partkey ? partkey.toString() : undefined].filter(function (key) {
|
|
2282
|
-
return !!key;
|
|
2283
|
-
});
|
|
2284
|
-
return keys.reduce(function (str, key) {
|
|
2285
|
-
return str + ("" + (str.length ? "." : "")) + key;
|
|
2286
|
-
}, "");
|
|
2287
|
-
};
|
|
2988
|
+
var business = _this.extractDataAttributes(element);
|
|
2288
2989
|
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2990
|
+
var desc = element.getAttribute('data-desc') || _this.eventDescMap['WebExposure'];
|
|
2991
|
+
|
|
2992
|
+
var partkey = element.getAttribute('data-part-key') || 'exposure';
|
|
2993
|
+
|
|
2994
|
+
var params = _this.getParams({
|
|
2995
|
+
event: 'WebExposure',
|
|
2996
|
+
desc: desc,
|
|
2997
|
+
itemKey: _this.getItemKey(partkey),
|
|
2998
|
+
privateParamMap: {
|
|
2999
|
+
business: business
|
|
3000
|
+
}
|
|
3001
|
+
});
|
|
3002
|
+
|
|
3003
|
+
_this.sendData(params).then(function () {
|
|
3004
|
+
elementInfo.exposureCount++;
|
|
3005
|
+
|
|
3006
|
+
if (elementInfo.exposureTimer) {
|
|
3007
|
+
clearTimeout(elementInfo.exposureTimer);
|
|
3008
|
+
elementInfo.exposureTimer = null;
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
if (_this.initConfig.showLog) {
|
|
3012
|
+
_this.printLog("\u66DD\u5149\u4E0A\u62A5\u6210\u529F\uFF0C\u5F53\u524D\u66DD\u5149\u6B21\u6570: " + elementInfo.exposureCount);
|
|
3013
|
+
}
|
|
3014
|
+
}).catch(function (err) {
|
|
3015
|
+
if (_this.initConfig.showLog) {
|
|
3016
|
+
_this.printLog("\u66DD\u5149\u4E0A\u62A5\u5931\u8D25: " + err);
|
|
3017
|
+
}
|
|
3018
|
+
});
|
|
3019
|
+
};
|
|
2314
3020
|
/**
|
|
2315
|
-
* @description
|
|
2316
|
-
* @param callback
|
|
3021
|
+
* @description 停止曝光监听
|
|
2317
3022
|
*/
|
|
2318
3023
|
|
|
2319
3024
|
|
|
2320
|
-
|
|
2321
|
-
|
|
3025
|
+
_this.stopExposureObserver = function () {
|
|
3026
|
+
if (_this.exposureObserver) {
|
|
3027
|
+
_this.exposureObserver.disconnect();
|
|
2322
3028
|
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
3029
|
+
_this.exposureObserver = null;
|
|
3030
|
+
|
|
3031
|
+
_this.exposureElementsMap.forEach(function (elementInfo) {
|
|
3032
|
+
if (elementInfo.exposureTimer) {
|
|
3033
|
+
clearTimeout(elementInfo.exposureTimer);
|
|
3034
|
+
}
|
|
3035
|
+
});
|
|
3036
|
+
|
|
3037
|
+
_this.exposureElementsMap.clear();
|
|
3038
|
+
|
|
3039
|
+
if (_this.initConfig.showLog) {
|
|
3040
|
+
_this.printLog('曝光监听已停止');
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
2328
3043
|
};
|
|
2329
3044
|
|
|
2330
|
-
|
|
2331
|
-
|
|
3045
|
+
_this.sdkVersion = "1.2.4"; // sdk版本
|
|
3046
|
+
|
|
3047
|
+
_this.initConfig = {
|
|
3048
|
+
appKey: "",
|
|
3049
|
+
platform: undefined,
|
|
3050
|
+
showLog: false,
|
|
3051
|
+
serverUrl: "",
|
|
3052
|
+
autoTrack: false,
|
|
3053
|
+
sendTimeout: 3000,
|
|
3054
|
+
isTrackSinglePage: false,
|
|
3055
|
+
contentType: "application/json",
|
|
3056
|
+
business: {},
|
|
3057
|
+
header: undefined,
|
|
3058
|
+
sampleRate: 1,
|
|
3059
|
+
batchSend: false,
|
|
3060
|
+
batchInterval: 5000,
|
|
3061
|
+
batchMaxSize: 10,
|
|
3062
|
+
trackPartKeyClick: false,
|
|
3063
|
+
pendingRequestsMaxSize: 50,
|
|
3064
|
+
autoTrackPageDurationInterval: false,
|
|
3065
|
+
pageDurationInterval: 30000,
|
|
3066
|
+
sendMethod: "auto",
|
|
3067
|
+
autoTrackExposure: false,
|
|
3068
|
+
exposureThreshold: 0.5,
|
|
3069
|
+
exposureTime: 500,
|
|
3070
|
+
exposureNum: undefined // 同一元素允许上报的最大曝光次数,不限制
|
|
3071
|
+
|
|
3072
|
+
}; // 系统信息
|
|
3073
|
+
|
|
3074
|
+
_this.systemsInfo = {};
|
|
3075
|
+
return _this;
|
|
3076
|
+
}
|
|
3077
|
+
/**
|
|
3078
|
+
* @description 添加单页面监听事件
|
|
3079
|
+
* @param callback
|
|
3080
|
+
*/
|
|
3081
|
+
|
|
3082
|
+
|
|
3083
|
+
WebTracking.prototype.addSinglePageEvent = function (callback) {
|
|
3084
|
+
var _this = this;
|
|
3085
|
+
|
|
3086
|
+
var historyPushState = window.history.pushState;
|
|
3087
|
+
var singlePageEvent = historyPushState ? "popstate" : "hashchange";
|
|
3088
|
+
this.each(["pushState", "replaceState", singlePageEvent], function (historyType) {
|
|
3089
|
+
_this.addEventListener(window, historyType, callback);
|
|
3090
|
+
});
|
|
3091
|
+
};
|
|
3092
|
+
|
|
3093
|
+
return WebTracking;
|
|
3094
|
+
}(WebTrackingTools);
|
|
2332
3095
|
|
|
2333
|
-
|
|
3096
|
+
var index = new WebTracking();
|
|
2334
3097
|
|
|
2335
|
-
|
|
3098
|
+
return index;
|
|
2336
3099
|
|
|
2337
3100
|
})));
|