@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/lib/index.js CHANGED
@@ -1,2337 +1,3100 @@
1
1
  (function (global, factory) {
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());
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
- Copyright (c) Microsoft Corporation.
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
- Permission to use, copy, modify, and/or distribute this software for any
11
- purpose with or without fee is hereby granted.
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
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
14
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
16
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
18
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19
- PERFORMANCE OF THIS SOFTWARE.
20
- ***************************************************************************** */
21
- /* global Reflect, Promise */
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
- var extendStatics = function(d, b) {
24
- extendStatics = Object.setPrototypeOf ||
25
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
26
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
27
- return extendStatics(d, b);
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
- function __extends(d, b) {
31
- if (typeof b !== "function" && b !== null)
32
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
33
- extendStatics(d, b);
34
- function __() { this.constructor = d; }
35
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
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
- var __assign = function() {
39
- __assign = Object.assign || function __assign(t) {
40
- for (var s, i = 1, n = arguments.length; i < n; i++) {
41
- s = arguments[i];
42
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
43
- }
44
- return t;
45
- };
46
- return __assign.apply(this, arguments);
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
- function __spreadArray(to, from) {
50
- for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
51
- to[j] = from[i];
52
- return to;
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
- if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
59
- _typeof = function (obj) {
60
- return typeof obj;
61
- };
62
- } else {
63
- _typeof = function (obj) {
64
- return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
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
- return _typeof(obj);
69
- }
122
+ if (wxVersion) {
123
+ logMsg.push("WeChat " + wxVersion);
124
+ }
70
125
 
71
- var WebTrackingTools =
72
- /** @class */
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
- 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
- */
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
- 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
- */
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
- this.rewriteHistory = function () {
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
- if (!!window.history.pushState) {
198
- history.pushState = historyWrap("pushState");
199
- history.replaceState = historyWrap("replaceState");
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
- this.formatJsonString = function (obj) {
214
- try {
215
- return JSON.stringify(obj, null, " ");
216
- } catch (e) {
217
- return JSON.stringify(obj);
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
- this.nativeForEach = Array.prototype.forEach;
222
- this.slice = Array.prototype.slice;
223
- this.hasOwnProperty = Object.prototype.hasOwnProperty;
224
- this.breaker = {};
225
- /**
226
- * @description 循环遍历
227
- */
229
+ this.each = function (obj, iterator, context) {
230
+ if (obj == null) {
231
+ return false;
232
+ }
228
233
 
229
- this.each = function (obj, iterator, context) {
230
- if (obj == null) {
231
- return false;
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
- 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) {
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
- return true;
253
- };
252
+ return true;
253
+ };
254
254
 
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;
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
- 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++;
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
- return -1;
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
- 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
- };
274
+ this.selector = function (el) {
275
+ var i = el.parentNode && 9 == el.parentNode.nodeType ? -1 : _this.getDomIndex(el);
283
276
 
284
- this.getDomSelector = function (el, arr) {
285
- if (!el || !el.parentNode || !el.parentNode.children) {
286
- return false;
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
- arr = arr && arr.join ? arr : [];
290
- var name = el.nodeName.toLowerCase();
284
+ this.getDomSelector = function (el, arr) {
285
+ if (!el || !el.parentNode || !el.parentNode.children) {
286
+ return false;
287
+ }
291
288
 
292
- if (!el || name === "body" || 1 != el.nodeType) {
293
- arr.unshift("body");
294
- return arr.join(" > ");
295
- }
289
+ arr = arr && arr.join ? arr : [];
290
+ var name = el.nodeName.toLowerCase();
296
291
 
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
- */
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
- for (var i = 0; i < ca.length; i++) {
311
- var c = ca[i];
306
+ this.getCookie = function (name) {
307
+ var nameEQ = name + "=";
308
+ var ca = document.cookie.split(";");
312
309
 
313
- while (c.charAt(0) == " ") {
314
- c = c.substring(1, c.length);
315
- }
310
+ for (var i = 0; i < ca.length; i++) {
311
+ var c = ca[i];
316
312
 
317
- if (c.indexOf(nameEQ) == 0) {
318
- return this._decodeURIComponent(c.substring(nameEQ.length, c.length));
319
- }
313
+ while (c.charAt(0) == " ") {
314
+ c = c.substring(1, c.length);
320
315
  }
321
316
 
322
- return null;
323
- };
324
-
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 : "";
317
+ if (c.indexOf(nameEQ) == 0) {
318
+ return this._decodeURIComponent(c.substring(nameEQ.length, c.length));
319
+ }
320
+ }
333
321
 
334
- if (days !== 0) {
335
- var date = new Date();
322
+ return null;
323
+ };
336
324
 
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);
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
- expires = "; expires=" + date.toUTCString();
344
- }
334
+ if (days !== 0) {
335
+ var date = new Date();
345
336
 
346
- function getValid(data) {
347
- if (data) {
348
- return data;
349
- } else {
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
- var valid_name = "";
355
- var valid_value = "";
356
- var valid_domain = "";
343
+ expires = "; expires=" + date.toUTCString();
344
+ }
357
345
 
358
- if (name) {
359
- valid_name = getValid(name);
346
+ function getValid(data) {
347
+ if (data) {
348
+ return data;
349
+ } else {
350
+ return false;
360
351
  }
352
+ }
361
353
 
362
- if (value) {
363
- valid_value = getValid(value);
364
- }
354
+ var valid_name = "";
355
+ var valid_value = "";
356
+ var valid_domain = "";
365
357
 
366
- if (cdomain) {
367
- valid_domain = getValid(cdomain);
368
- }
358
+ if (name) {
359
+ valid_name = getValid(name);
360
+ }
369
361
 
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
- */
362
+ if (value) {
363
+ valid_value = getValid(value);
364
+ }
379
365
 
366
+ if (cdomain) {
367
+ valid_domain = getValid(cdomain);
368
+ }
380
369
 
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
- */
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
- this.setLocalStorage = function (key, value) {
396
- try {
397
- localStorage.setItem(key, value);
398
- } catch (e) {// 静默失败
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
- this.getTimeStamp = function () {
412
- return new Date().getTime();
413
- };
414
- /**
415
- * 获取 UUID
416
- * @return {string} [UUID唯一值]
417
- */
402
+ this.removeCookie = function (name) {
403
+ _this.setCookie(name, "", -1);
404
+ };
405
+ /**
406
+ * 获取当前时间戳
407
+ * @return {number} [当前时间戳]
408
+ */
418
409
 
419
410
 
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
- */
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
- if (distinctId) return distinctId;
437
- distinctId = _this.uuid();
433
+ this.getDistinctId = function () {
434
+ var distinctId = _this.getCookie("distinctId");
438
435
 
439
- _this.setCookie("distinctId", distinctId);
436
+ if (distinctId) return distinctId;
437
+ distinctId = _this.uuid();
440
438
 
441
- return distinctId;
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
- if (!_this.isObject(obj)) return obj;
457
- var filteredObj = {};
451
+ this.filterSensitiveData = function (obj, sensitiveKeys) {
452
+ if (sensitiveKeys === void 0) {
453
+ sensitiveKeys = ["password", "token", "secret", "key"];
454
+ }
458
455
 
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());
463
- });
456
+ if (!_this.isObject(obj)) return obj;
457
+ var filteredObj = {};
464
458
 
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
- }
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
- return filteredObj;
476
- };
477
- /**
478
- * XSS过滤
479
- * @param str 需要过滤的字符串
480
- * @returns 过滤后的字符串
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, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g, '&#x2F;');
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, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;").replace(/\//g, "&#x2F;");
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
- var queryArr = _href.match(new RegExp("[?&][^?&]+=[^?&]+", "g")) || [];
499
- var paramsObj = {};
495
+ this.getQueryValue = function () {
496
+ var _href = decodeURI(window.location.href);
500
497
 
501
- for (var i = 0; i < queryArr.length; i++) {
502
- var _item = queryArr[i].replace(/\?|\&/, "");
498
+ var queryArr = _href.match(new RegExp("[?&][^?&]+=[^?&]+", "g")) || [];
499
+ var paramsObj = {};
503
500
 
504
- var pair = _item.split("=");
501
+ for (var i = 0; i < queryArr.length; i++) {
502
+ var _item = queryArr[i].replace(/\?|\&/, "");
505
503
 
506
- paramsObj[pair[0]] = pair[1];
507
- }
504
+ var pair = _item.split("=");
508
505
 
509
- return Object.keys(paramsObj).length > 0 ? paramsObj : null;
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
- function getJSON(data) {
523
- if (!data) {
524
- return {};
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
- if (typeof data === 'string') {
528
- try {
529
- return JSON.parse(data);
530
- } catch (e) {
531
- return {};
532
- }
533
- }
522
+ function getJSON(data) {
523
+ if (!data) {
524
+ return {};
525
+ }
534
526
 
535
- if (_typeof(data) === 'object') {
536
- return data;
527
+ if (typeof data === "string") {
528
+ try {
529
+ return JSON.parse(data);
530
+ } catch (e) {
531
+ return {};
537
532
  }
533
+ }
538
534
 
539
- return {};
535
+ if (_typeof(data) === "object") {
536
+ return data;
540
537
  }
541
538
 
542
- var g = _this.xhr(para.cors);
539
+ return {};
540
+ }
543
541
 
544
- if (!g) {
545
- return false;
546
- }
542
+ var g = _this.xhr(para.cors);
547
543
 
548
- if (!para.type) {
549
- para.type = para.data ? "POST" : "GET";
550
- }
544
+ if (!g) {
545
+ return false;
546
+ }
551
547
 
552
- var oldsuccess = para.success;
553
- var olderror = para.error;
554
- var errorTimer;
548
+ if (!para.type) {
549
+ para.type = para.data ? "POST" : "GET";
550
+ }
555
551
 
556
- var abort = function abort() {
557
- try {
558
- if (_this.isObject(g) && g.abort) {
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
- 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;
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
- para.success = function (data) {
576
- oldsuccess && oldsuccess(data);
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
- if (errorTimer) {
579
- clearTimeout(errorTimer);
580
- errorTimer = null;
581
- }
582
- };
575
+ para.success = function (data) {
576
+ oldsuccess && oldsuccess(data);
583
577
 
584
- para.error = function (err, status) {
585
- olderror && olderror(err, status);
578
+ if (errorTimer) {
579
+ clearTimeout(errorTimer);
580
+ errorTimer = null;
581
+ }
582
+ };
586
583
 
587
- if (errorTimer) {
588
- clearTimeout(errorTimer);
589
- errorTimer = null;
590
- }
591
- };
584
+ para.error = function (err, status) {
585
+ olderror && olderror(err, status);
592
586
 
593
- errorTimer = window.setTimeout(function () {
594
- abort();
595
- }, para.timeout);
587
+ if (errorTimer) {
588
+ clearTimeout(errorTimer);
589
+ errorTimer = null;
590
+ }
591
+ };
596
592
 
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);
604
- }
593
+ errorTimer = window.setTimeout(function () {
594
+ abort();
595
+ }, para.timeout);
605
596
 
606
- g.onreadystatechange = null;
607
- g.onload = null;
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
- } catch (e) {
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
- g.open(para.type || "GET", para.url, true);
615
+ g.open(para.type || "GET", para.url, true);
616
616
 
617
- try {
618
- if (para.credentials) {
619
- g.withCredentials = true;
620
- }
617
+ try {
618
+ if (para.credentials) {
619
+ g.withCredentials = true;
620
+ }
621
621
 
622
- if (_this.isObject(para.header)) {
623
- _this.each(para.header, function (v, i) {
624
- g.setRequestHeader && g.setRequestHeader(i, v);
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
- if (para.data) {
629
- if (!para.cors) {
630
- g.setRequestHeader && g.setRequestHeader("X-Requested-With", "XMLHttpRequest");
631
- }
628
+ if (para.data) {
629
+ if (!para.cors) {
630
+ g.setRequestHeader && g.setRequestHeader("X-Requested-With", "XMLHttpRequest");
631
+ }
632
632
 
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");
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
- g.send(para.data || null);
644
- };
643
+ g.send(para.data || null);
644
+ };
645
645
 
646
- this.xhr = function (cors) {
647
- if (cors) {
648
- if (typeof window.XMLHttpRequest !== "undefined" && "withCredentials" in new XMLHttpRequest()) {
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
- 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
- }
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
- this.isSupportBeaconSend = function () {
685
- var supported = false;
681
+ return Sys;
682
+ };
686
683
 
687
- if ((typeof navigator === "undefined" ? "undefined" : _typeof(navigator)) !== "object" || typeof navigator.sendBeacon !== "function") {
688
- return supported;
689
- }
684
+ this.isSupportBeaconSend = function () {
685
+ var supported = false;
690
686
 
691
- var Sys = _this.getUA();
687
+ if ((typeof navigator === "undefined" ? "undefined" : _typeof(navigator)) !== "object" || typeof navigator.sendBeacon !== "function") {
688
+ return supported;
689
+ }
692
690
 
693
- var ua = navigator.userAgent.toLowerCase();
691
+ var Sys = _this.getUA();
694
692
 
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(".");
693
+ var ua = navigator.userAgent.toLowerCase();
700
694
 
701
- if (typeof Sys.safari === "undefined") {
702
- Sys.safari = Number(ver[0]);
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
- if (ver[0] && +ver[0] < 13) {
706
- if (Sys.chrome > 41 || Sys.firefox > 30 || Sys.opera > 25 || Sys.safari > 12) {
707
- supported = true;
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) {
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
- return supported;
719
- };
720
- /**
721
- * 节流函数
722
- * @param func 需要节流的函数
723
- * @param wait 等待时间
724
- * @returns 节流后的函数
725
- */
718
+ return supported;
719
+ };
720
+ /**
721
+ * 节流函数
722
+ * @param func 需要节流的函数
723
+ * @param wait 等待时间
724
+ * @returns 节流后的函数
725
+ */
726
726
 
727
727
 
728
- this.throttle = function (func, wait) {
729
- var timeout = null;
730
- var previous = 0;
731
- return function () {
732
- var args = [];
728
+ this.throttle = function (func, wait) {
729
+ var timeout = null;
730
+ var previous = 0;
731
+ return function () {
732
+ var args = [];
733
733
 
734
- for (var _i = 0; _i < arguments.length; _i++) {
735
- args[_i] = arguments[_i];
736
- }
734
+ for (var _i = 0; _i < arguments.length; _i++) {
735
+ args[_i] = arguments[_i];
736
+ }
737
737
 
738
- var now = Date.now();
739
- var remaining = wait - (now - previous);
738
+ var now = Date.now();
739
+ var remaining = wait - (now - previous);
740
740
 
741
- if (remaining <= 0 || remaining > wait) {
742
- if (timeout) {
743
- clearTimeout(timeout);
744
- timeout = null;
745
- }
741
+ if (remaining <= 0 || remaining > wait) {
742
+ if (timeout) {
743
+ clearTimeout(timeout);
744
+ timeout = null;
745
+ }
746
746
 
747
- previous = now;
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
- } else if (!timeout) {
750
- timeout = window.setTimeout(function () {
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
- * @param func 需要防抖的函数
761
- * @param wait 等待时间
762
- * @returns 防抖后的函数
763
- */
757
+ };
758
+ /**
759
+ * 防抖函数
760
+ * @param func 需要防抖的函数
761
+ * @param wait 等待时间
762
+ * @returns 防抖后的函数
763
+ */
764
764
 
765
765
 
766
- this.debounce = function (func, wait) {
767
- var timeout = null;
768
- return function () {
769
- var args = [];
766
+ this.debounce = function (func, wait) {
767
+ var timeout = null;
768
+ return function () {
769
+ var args = [];
770
770
 
771
- for (var _i = 0; _i < arguments.length; _i++) {
772
- args[_i] = arguments[_i];
773
- }
771
+ for (var _i = 0; _i < arguments.length; _i++) {
772
+ args[_i] = arguments[_i];
773
+ }
774
774
 
775
- if (timeout) {
776
- clearTimeout(timeout);
777
- }
775
+ if (timeout) {
776
+ clearTimeout(timeout);
777
+ }
778
778
 
779
- timeout = window.setTimeout(function () {
780
- func.apply(void 0, args);
781
- }, wait);
782
- };
779
+ timeout = window.setTimeout(function () {
780
+ func.apply(void 0, args);
781
+ }, wait);
783
782
  };
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
- });
804
- } else {
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: "不支持sendBeacon,发送失败!"
807
+ message: "sendBeacon返回false"
814
808
  });
815
809
  }
816
- };
817
- /**
818
- * 获取设备唯一标识
819
- * 基于浏览器指纹技术,通过收集浏览器特征生成唯一ID
820
- * @returns 返回设备唯一标识字符串
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
- this.getDeviceId = function () {
825
- // 获取已存储的设备ID
826
- var storedDeviceId = _this.getCookie('device_id') || _this.getLocalStorage('device_id');
824
+ this.getDeviceId = function () {
825
+ // 获取已存储的设备ID
826
+ var storedDeviceId = _this.getCookie("device_id") || _this.getLocalStorage("device_id");
827
827
 
828
- if (storedDeviceId) {
829
- return storedDeviceId;
830
- } // 收集浏览器指纹信息
828
+ if (storedDeviceId) {
829
+ return storedDeviceId;
830
+ } // 收集浏览器指纹信息
831
831
 
832
832
 
833
- var fingerprint = _this.collectFingerprint(); // 生成设备ID
833
+ var fingerprint = _this.collectFingerprint(); // 生成设备ID
834
834
 
835
835
 
836
- var deviceId = _this.hashFingerprint(fingerprint); // 存储设备ID
836
+ var deviceId = _this.hashFingerprint(fingerprint); // 存储设备ID
837
837
 
838
838
 
839
- _this.setCookie('device_id', deviceId, 365 * 2); // 存储2年
839
+ _this.setCookie("device_id", deviceId, 365 * 2); // 存储2年
840
840
 
841
841
 
842
- _this.setLocalStorage('device_id', deviceId);
842
+ _this.setLocalStorage("device_id", deviceId);
843
843
 
844
- return deviceId;
845
- };
846
- /**
847
- * 收集浏览器指纹信息
848
- * @returns 返回浏览器指纹对象
849
- */
844
+ return deviceId;
845
+ };
846
+ /**
847
+ * 收集浏览器指纹信息
848
+ * @returns 返回浏览器指纹对象
849
+ */
850
850
 
851
851
 
852
- this.collectFingerprint = function () {
853
- var fingerprint = {}; // 1. 用户代理
852
+ this.collectFingerprint = function () {
853
+ var fingerprint = {}; // 1. 用户代理
854
854
 
855
- fingerprint.userAgent = navigator.userAgent; // 2. 屏幕信息
855
+ fingerprint.userAgent = navigator.userAgent; // 2. 屏幕信息
856
856
 
857
- fingerprint.screenWidth = screen.width;
858
- fingerprint.screenHeight = screen.height;
859
- fingerprint.colorDepth = screen.colorDepth;
860
- fingerprint.pixelDepth = screen.pixelDepth; // 3. 时区
857
+ fingerprint.screenWidth = screen.width;
858
+ fingerprint.screenHeight = screen.height;
859
+ fingerprint.colorDepth = screen.colorDepth;
860
+ fingerprint.pixelDepth = screen.pixelDepth; // 3. 时区
861
861
 
862
- fingerprint.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
863
- fingerprint.timezoneOffset = new Date().getTimezoneOffset(); // 4. 语言设置
862
+ fingerprint.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
863
+ fingerprint.timezoneOffset = new Date().getTimezoneOffset(); // 4. 语言设置
864
864
 
865
- fingerprint.language = navigator.language;
866
- fingerprint.languages = Array.from(navigator.languages); // 5. 平台信息
865
+ fingerprint.language = navigator.language;
866
+ fingerprint.languages = Array.from(navigator.languages); // 5. 平台信息
867
867
 
868
- fingerprint.platform = navigator.platform; // 6. WebGL 指纹
868
+ fingerprint.platform = navigator.platform; // 6. WebGL 指纹
869
869
 
870
- fingerprint.webgl = _this.getWebGLFingerprint(); // 7. Canvas 指纹
870
+ fingerprint.webgl = _this.getWebGLFingerprint(); // 7. Canvas 指纹
871
871
 
872
- fingerprint.canvas = _this.getCanvasFingerprint(); // 8. 音频上下文指纹
872
+ fingerprint.canvas = _this.getCanvasFingerprint(); // 8. 音频上下文指纹
873
873
 
874
- fingerprint.audio = _this.getAudioFingerprint(); // 9. 字体检测
874
+ fingerprint.audio = _this.getAudioFingerprint(); // 9. 字体检测
875
875
 
876
- fingerprint.fonts = _this.getFontFingerprint(); // 10. 插件信息
876
+ fingerprint.fonts = _this.getFontFingerprint(); // 10. 插件信息
877
877
 
878
- fingerprint.plugins = _this.getPluginsFingerprint(); // 11. 存储检测
878
+ fingerprint.plugins = _this.getPluginsFingerprint(); // 11. 存储检测
879
879
 
880
- fingerprint.localStorage = _this.hasLocalStorage();
881
- fingerprint.sessionStorage = _this.hasSessionStorage();
882
- fingerprint.indexedDB = _this.hasIndexedDB(); // 12. 硬件信息
880
+ fingerprint.localStorage = _this.hasLocalStorage();
881
+ fingerprint.sessionStorage = _this.hasSessionStorage();
882
+ fingerprint.indexedDB = _this.hasIndexedDB(); // 12. 硬件信息
883
883
 
884
- fingerprint.hardwareConcurrency = navigator.hardwareConcurrency;
885
- fingerprint.deviceMemory = navigator.deviceMemory;
886
- fingerprint.maxTouchPoints = navigator.maxTouchPoints; // 13. 连接信息
884
+ fingerprint.hardwareConcurrency = navigator.hardwareConcurrency;
885
+ fingerprint.deviceMemory = navigator.deviceMemory;
886
+ fingerprint.maxTouchPoints = navigator.maxTouchPoints; // 13. 连接信息
887
887
 
888
- fingerprint.connection = _this.getConnectionFingerprint();
889
- return fingerprint;
890
- };
891
- /**
892
- * 获取WebGL指纹
893
- * @returns WebGL指纹字符串
894
- */
888
+ fingerprint.connection = _this.getConnectionFingerprint();
889
+ return fingerprint;
890
+ };
891
+ /**
892
+ * 获取WebGL指纹
893
+ * @returns WebGL指纹字符串
894
+ */
895
895
 
896
896
 
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
- * @returns Canvas指纹字符串
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
- this.getCanvasFingerprint = function () {
917
- try {
918
- var canvas = document.createElement('canvas');
919
- var ctx = canvas.getContext('2d');
920
- if (!ctx) return 'not-supported'; // 绘制特定图形
921
-
922
- ctx.textBaseline = 'top';
923
- ctx.font = '14px Arial';
924
- ctx.fillStyle = '#f60';
925
- ctx.fillRect(125, 1, 62, 20);
926
- ctx.fillStyle = '#069';
927
- ctx.fillText('Canvas fingerprint', 2, 15);
928
- ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
929
- ctx.fillText('Canvas fingerprint', 4, 17);
930
- return canvas.toDataURL().slice(-50); // 取后50个字符
931
- } catch (e) {
932
- return 'error';
933
- }
934
- };
935
- /**
936
- * 获取音频上下文指纹
937
- * @returns 音频指纹字符串
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
- this.getAudioFingerprint = function () {
942
- try {
943
- var AudioContextClass = window.AudioContext || window.webkitAudioContext;
944
- if (!AudioContextClass) return 'not-supported';
945
- var context = new AudioContextClass();
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
- if (detected) {
1002
- detectedFonts_1.push(font);
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
- this.getPluginsFingerprint = function () {
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
- return plugins.join(';');
1031
- } catch (e) {
1032
- return 'error';
1033
- }
1034
- };
1035
- /**
1036
- * 检测localStorage支持
1037
- * @returns 是否支持localStorage
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
- this.hasIndexedDB = function () {
1074
- return 'indexedDB' in window && indexedDB !== null;
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
- this.getConnectionFingerprint = function () {
1083
- try {
1084
- var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
1085
- if (!connection) return 'not-supported';
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
- this.hashFingerprint = function (fingerprint) {
1099
- // 将指纹对象转换为字符串
1100
- var fingerprintString = JSON.stringify(fingerprint, Object.keys(fingerprint).sort()); // 使用更强大的哈希算法生成ID
1101
-
1102
- var hash1 = 5381; // DJB2哈希算法的初始值
1103
-
1104
- var hash2 = 52711; // 第二个哈希的初始值
1105
-
1106
- for (var i = 0; i < fingerprintString.length; i++) {
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
- var deviceId = 'fp_' + combinedHash.toString(36);
1117
- return deviceId;
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
- * 打印log
1062
+ * 检测IndexedDB支持
1063
+ * @returns 是否支持IndexedDB
1122
1064
  */
1123
1065
 
1124
1066
 
1125
- WebTrackingTools.prototype.printLog = function () {
1126
- var rest = [];
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
- if (this.isObject(rest[0])) {
1133
- rest[0] = this.formatJsonString(rest[0]);
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
- if ((typeof console === "undefined" ? "undefined" : _typeof(console)) === "object" && console.log) {
1137
- try {
1138
- return console.log.apply(console, rest);
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
- * @description 检验是否是对象
1087
+ * 将指纹信息哈希为唯一ID
1088
+ * @param fingerprint 指纹信息
1089
+ * @returns 返回哈希后的设备ID
1146
1090
  */
1147
1091
 
1148
1092
 
1149
- WebTrackingTools.prototype.isObject = function (obj) {
1150
- if (obj == null) {
1151
- return false;
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
- WebTrackingTools.prototype.isUndefined = function (obj) {
1158
- return obj === void 0;
1159
- };
1097
+ var hash1 = 5381; // DJB2哈希算法的初始值
1160
1098
 
1161
- WebTrackingTools.prototype.isString = function (obj) {
1162
- return toString.call(obj) == "[object String]";
1163
- };
1099
+ var hash2 = 52711; // 第二个哈希的初始值
1164
1100
 
1165
- WebTrackingTools.prototype.isDate = function (obj) {
1166
- return toString.call(obj) == "[object Date]";
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
- WebTrackingTools.prototype.isBoolean = function (obj) {
1170
- return toString.call(obj) == "[object Boolean]";
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
- WebTrackingTools.prototype.isElement = function (obj) {
1178
- return !!(obj && obj.nodeType === 1);
1179
- };
1109
+ var hash1Hex = (hash1 >>> 0).toString(16).padStart(8, '0'); // 32位 = 8个十六进制字符
1180
1110
 
1181
- WebTrackingTools.prototype.isFunction = function (f) {
1182
- if (!f) {
1183
- return false;
1184
- }
1111
+ var hash2Hex = (hash2 >>> 0).toString(16).padStart(8, '0'); // 32位 = 8个十六进制字符
1185
1112
 
1186
- var type = toString.call(f);
1187
- return type == "[object Function]" || type == "[object AsyncFunction]";
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
- try {
1194
- JSON.parse(str);
1195
- } catch (e) {
1196
- return false;
1197
- }
1122
+ WebTrackingTools.prototype.printLog = function () {
1123
+ var rest = [];
1198
1124
 
1199
- return true;
1200
- };
1125
+ for (var _i = 0; _i < arguments.length; _i++) {
1126
+ rest[_i] = arguments[_i];
1127
+ }
1201
1128
 
1202
- WebTrackingTools.prototype._decodeURIComponent = function (val) {
1203
- var result = val;
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
- result = decodeURIComponent(val);
1135
+ return console.log.apply(console, rest);
1207
1136
  } catch (e) {
1208
- result = val;
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
- while (domainList.length) {
1225
- urlItems.unshift(domainList.pop());
1226
- var mainHost = urlItems.join(".");
1227
- var cookie = key + "=" + 12345 + ";domain=." + mainHost;
1228
- document.cookie = cookie; //如果cookie存在,则说明域名合法
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
- if (keyR.test(document.cookie)) {
1231
- document.cookie = cookie + ";expires=" + expiredTime;
1232
- return mainHost;
1233
- }
1234
- }
1154
+ WebTrackingTools.prototype.isUndefined = function (obj) {
1155
+ return obj === void 0;
1156
+ };
1235
1157
 
1236
- return domain;
1237
- };
1158
+ WebTrackingTools.prototype.isString = function (obj) {
1159
+ return toString.call(obj) == "[object String]";
1160
+ };
1238
1161
 
1239
- return WebTrackingTools;
1240
- }();
1162
+ WebTrackingTools.prototype.isDate = function (obj) {
1163
+ return toString.call(obj) == "[object Date]";
1164
+ };
1241
1165
 
1242
- var WebTracking =
1243
- /** @class */
1244
- function (_super) {
1245
- __extends(WebTracking, _super);
1166
+ WebTrackingTools.prototype.isBoolean = function (obj) {
1167
+ return toString.call(obj) == "[object Boolean]";
1168
+ };
1246
1169
 
1247
- function WebTracking() {
1248
- var _this = _super.call(this) || this; // 批量发送队列
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
- _this.batchQueue = []; // 批量发送定时器
1178
+ WebTrackingTools.prototype.isFunction = function (f) {
1179
+ if (!f) {
1180
+ return false;
1181
+ }
1252
1182
 
1253
- _this.batchTimer = null; // LocalStorage 存储 key
1183
+ var type = toString.call(f);
1184
+ return type == "[object Function]" || type == "[object AsyncFunction]";
1185
+ };
1254
1186
 
1255
- _this.BATCH_QUEUE_STORAGE_KEY = "web_tracking_batch_queue"; // 是否使用自定义 pageKey(如果为 true,路由变化时不会自动更新 pageKey)
1187
+ WebTrackingTools.prototype.isJSONString = function (str) {
1188
+ if (!this.isString(str)) return false;
1256
1189
 
1257
- _this.useCustomPageKey = false; // 待发送的单个请求队列(用于页面跳转时发送)
1190
+ try {
1191
+ JSON.parse(str);
1192
+ } catch (e) {
1193
+ return false;
1194
+ }
1258
1195
 
1259
- _this.pendingRequests = []; // 页面卸载监听器是否已设置
1196
+ return true;
1197
+ };
1260
1198
 
1261
- _this.isUnloadListenerSetup = false; // LocalStorage 存储 key(待发送请求)
1199
+ WebTrackingTools.prototype._decodeURIComponent = function (val) {
1200
+ var result = val;
1262
1201
 
1263
- _this.PENDING_REQUESTS_STORAGE_KEY = "web_tracking_pending_requests"; // 待发送请求队列最大大小(默认值,可通过配置覆盖)
1202
+ try {
1203
+ result = decodeURIComponent(val);
1204
+ } catch (e) {
1205
+ result = val;
1206
+ }
1264
1207
 
1265
- _this.DEFAULT_PENDING_REQUESTS_MAX_SIZE = 50; // LocalStorage 最大大小限制(4MB)
1208
+ return result;
1209
+ };
1266
1210
 
1267
- _this.MAX_STORAGE_SIZE = 4 * 1024 * 1024; // 用户信息
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
- _this.userInfo = null; // 当前路由
1219
+ urlItems.unshift(domainList.pop()); // 慢慢从后往前测试
1270
1220
 
1271
- _this.currentUrl = ""; // 页面唯一标识,取当前路由 window.location.pathname.replace(/\//g, '_').substr(1)
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
- _this.pageKey = ""; // 设备唯一标识
1227
+ if (keyR.test(document.cookie)) {
1228
+ document.cookie = cookie + ";expires=" + expiredTime;
1229
+ return mainHost;
1230
+ }
1231
+ }
1274
1232
 
1275
- _this.deviceId = ""; // 上传事件描述
1233
+ return domain;
1234
+ };
1276
1235
 
1277
- _this.eventDescMap = {
1278
- PageView: "Web 浏览页面",
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
- _this.init = function (initParams) {
1289
- _this.preset(initParams);
1239
+ var WebTracking =
1240
+ /** @class */
1241
+ function (_super) {
1242
+ __extends(WebTracking, _super);
1290
1243
 
1291
- var pathname = window.location.pathname;
1292
- _this.currentUrl = window.location.href; // 如果传入了自定义 pageKey,使用自定义值,否则自动生成
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
- _this.systemsInfo = _this.getSystemsInfo(initParams.platform); // 获取设备ID
1248
+ _this.batchTimer = null; // LocalStorage 存储 key
1303
1249
 
1304
- _this.deviceId = _this.getDeviceId(); // 如果传入了 userInfo,设置用户信息
1250
+ _this.BATCH_QUEUE_STORAGE_KEY = "web_tracking_batch_queue"; // 是否使用自定义 pageKey(如果为 true,路由变化时不会自动更新 pageKey)
1305
1251
 
1306
- if (initParams.userInfo && _this.isObject(initParams.userInfo)) {
1307
- _this.userInfo = initParams.userInfo;
1308
- }
1252
+ _this.useCustomPageKey = false; // 页面卸载监听器是否已设置
1309
1253
 
1310
- _this.setCookie("retainedStartTime", _this.getTimeStamp()); // 如果启用了批量发送,从 LocalStorage 恢复队列
1254
+ _this.isUnloadListenerSetup = false; // 定时上报页面停留时长的定时器
1311
1255
 
1256
+ _this.pageDurationTimer = null; // LocalStorage 存储 key(待发送请求)
1312
1257
 
1313
- if (_this.initConfig.batchSend) {
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
- _this.restorePendingRequestsFromStorage(); // 无论是否启用批量发送,都需要监听页面卸载事件,确保数据发送
1262
+ _this.MAX_STORAGE_SIZE = 4 * 1024 * 1024; // IntersectionObserver 实例
1319
1263
 
1264
+ _this.exposureObserver = null; // 曝光元素映射
1320
1265
 
1321
- _this.setupBeforeUnloadListener();
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
- _this.each(presetParams, function (val, key) {
1346
- // 跳过 pageKey,因为已经单独处理
1347
- if (key === 'pageKey') return;
1268
+ _this.userInfo = null; // 当前路由
1348
1269
 
1349
- if (_this.initConfig.hasOwnProperty(key)) {
1350
- // TODO:后面加一些校验
1351
- _this.initConfig[key] = val;
1352
- }
1353
- });
1354
- }
1270
+ _this.currentUrl = ""; // 页面唯一标识,取当前路由 window.location.pathname.replace(/\//g, '_').substr(1)
1355
1271
 
1356
- if (!/^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-\(\)]*[\w@?^=%&/~+#-\(\)])?$/.test(_this.initConfig["serverUrl"])) {
1357
- _this.printLog("当前 server_url 为空或不正确,只在控制台打印日志,network 中不会发数据,请配置正确的 server_url!");
1272
+ _this.pageKey = ""; // 设备唯一标识
1358
1273
 
1359
- _this.initConfig["showLog"] = true;
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
- if (!!_this.initConfig["autoTrack"] || !!_this.initConfig["trackPartKeyClick"]) {
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
- _this.login = function (userInfo) {
1377
- if (_this.isObject(userInfo)) _this.userInfo = userInfo;
1378
- };
1379
- /**
1380
- * 获取设备唯一标识
1381
- * @returns 设备唯一标识
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.getDeviceId = function () {
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
- var storedDeviceId = _this.getCookie("device_id") || _this.getLocalStorage("device_id");
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
- _this.deviceId = deviceId;
1412
- return _this.deviceId;
1413
- };
1414
- /**
1415
- * 重置设备ID
1416
- * 清除存储的设备ID并重新生成
1417
- * @returns 新的设备ID
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
- return newDeviceId;
1431
- };
1432
- /**
1433
- * 自定义代码埋点上报
1434
- * @param {object} TrackParams [自定义上报参数]
1435
- * @return {Promise<TrackingResponse>} [回调]
1436
- */
1437
-
1438
-
1439
- _this.track = function (_a) {
1440
- var desc = _a.desc,
1441
- pageKey = _a.pageKey,
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
- _this.listener = function () {
1463
- // 如果启用了全埋点,监听页面浏览事件
1464
- if (!!_this.initConfig.autoTrack) {
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
- _this.addSinglePageEvent(_this.onPageViewCallback);
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
- _this.each(["load", "beforeunload"], function (historyType) {
1472
- _this.addEventListener(window, historyType, _this.onPageViewCallback);
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
- _this.each(["pushState", "replaceState", singlePageEvent], function (historyName) {
1492
- _this.removeEventListener(window, historyName, _this.onPageViewCallback);
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
- _this.removeEventListener(window, "click", _this.onClickCallback); // 清理批量发送定时器
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
- _this.clearBatchTimer();
1504
- };
1505
- /**
1506
- * @description 清理批量发送定时器
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
- _this.clearBatchTimer = function () {
1511
- if (_this.batchTimer !== null) {
1512
- clearTimeout(_this.batchTimer);
1513
- _this.batchTimer = null;
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
- _this.clearBatchQueue = function () {
1522
- _this.batchQueue = [];
1430
+ case 'batchInterval':
1431
+ if (typeof value !== 'number' || value <= 0) {
1432
+ return {
1433
+ valid: false,
1434
+ message: 'batchInterval 必须是大于 0 的数字'
1435
+ };
1436
+ }
1523
1437
 
1524
- _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, "[]");
1438
+ break;
1525
1439
 
1526
- if (_this.initConfig.showLog) {
1527
- _this.printLog("批量队列已清空");
1528
- }
1529
- };
1530
- /**
1531
- * @description 设置自定义页面唯一标识
1532
- * @param pageKey 页面唯一标识,如果传入 null 或空字符串,则恢复自动生成
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
- _this.setPageKey = function (pageKey, autoUpdate) {
1538
- if (autoUpdate === void 0) {
1539
- autoUpdate = false;
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
- if (pageKey === null || pageKey === '') {
1543
- // 恢复自动生成
1544
- _this.useCustomPageKey = false;
1545
- var pathname = window.location.pathname;
1546
- _this.pageKey = pathname.replace(/\//g, "_").substring(1);
1458
+ break;
1547
1459
 
1548
- if (_this.initConfig.showLog) {
1549
- _this.printLog("\u9875\u9762\u6807\u8BC6\u5DF2\u6062\u590D\u81EA\u52A8\u751F\u6210: " + _this.pageKey);
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
- if (_this.initConfig.showLog) {
1556
- _this.printLog("\u9875\u9762\u6807\u8BC6\u5DF2\u8BBE\u7F6E\u4E3A: " + pageKey + ", \u81EA\u52A8\u66F4\u65B0: " + autoUpdate);
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
- _this.getPageKey = function () {
1567
- return _this.pageKey;
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
- _this.onClickCallback = function (e) {
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
- var params = _this.getParams({
1587
- event: "WebClick",
1588
- desc: _this.eventDescMap["WebClick"],
1589
- itemKey: _this.getItemKey(target.dataset.partKey),
1590
- privateParamMap: {
1591
- targetEle: targetEle,
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
- return _this.sendData(params);
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
- _this.onPageViewCallback = function (e) {
1606
- var _a, _b; // 在路由变化前,先发送待发送的数据(避免被取消)
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
- if (_this.pendingRequests.length > 0 || _this.initConfig.batchSend && _this.batchQueue.length > 0) {
1610
- _this.flushPendingData();
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
- var ORGIN = window.location.origin;
1534
+ break;
1614
1535
 
1615
- var params = _this.getParams({
1616
- event: "PageView",
1617
- desc: _this.eventDescMap["PageView"],
1618
- privateParamMap: {
1619
- currentUrl: _this.currentUrl,
1620
- 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
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
- _this.currentUrl = window.location.href; // 如果使用自定义 pageKey,路由变化时不自动更新
1544
+ break;
1625
1545
 
1626
- if (!_this.useCustomPageKey) {
1627
- _this.pageKey = window.location.pathname.replace(/\//g, "_").substring(1);
1628
- }
1546
+ case 'platform':
1547
+ if (typeof value !== 'string') {
1548
+ return {
1549
+ valid: false,
1550
+ message: 'platform 必须是字符串'
1551
+ };
1552
+ }
1629
1553
 
1630
- _this.sendRetained(e.type);
1554
+ break;
1555
+ }
1631
1556
 
1632
- _this.sendData(params);
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
- var filteredUserInfo = _this.filterSensitiveData(_this.userInfo || {});
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
- var filteredUrlParams = _this.filterSensitiveData(_this.getQueryValue() || {}); // 创建私有参数对象
1575
+ _this.getDeviceId = function () {
1576
+ // 如果已有设备ID,直接返回
1577
+ if (_this.deviceId) {
1578
+ return _this.deviceId;
1579
+ } // 获取已存储的设备ID
1654
1580
 
1655
1581
 
1656
- var privateParamMapData = {
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
- if (filteredPrivateParamMap.targetUrl) {
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
- if (filteredPrivateParamMap.elementSelector) {
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
- return {
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.shouldSample = function () {
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
- var batchData = __spreadArray([], _this.batchQueue);
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
- _this.batchQueue = []; // 从 LocalStorage 中移除已发送的数据
1616
+ _this.deviceId = ""; // 重新生成设备ID
1722
1617
 
1723
- _this.saveBatchQueueToStorage(); // 发送批量数据
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
- _this.sendBatchData = function (data) {
1735
- var _a = _this.initConfig,
1736
- serverUrl = _a.serverUrl,
1737
- contentType = _a.contentType,
1738
- showLog = _a.showLog;
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
- if (showLog) {
1741
- _this.printLog("\u6279\u91CF\u53D1\u9001 " + data.length + " \u6761\u6570\u636E");
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
- _this.printLog("\u6279\u91CF\u53D1\u9001\u5931\u8D25: " + err + "\uFF0C\u6570\u636E\u5DF2\u91CD\u65B0\u52A0\u5165\u961F\u5217"); // 将失败的数据重新加入队列,避免数据丢失
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
- _this.saveBatchQueueToStorage();
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.addToBatchQueue = function (params) {
1792
- var _a = _this.initConfig,
1793
- batchInterval = _a.batchInterval,
1794
- batchMaxSize = _a.batchMaxSize;
1689
+ _this.each(["load", "beforeunload"], function (historyType) {
1690
+ _this.removeEventListener(window, historyType, _this.onPageViewCallback);
1691
+ });
1795
1692
 
1796
- _this.batchQueue.push(params); // 保存到 LocalStorage
1693
+ _this.removeEventListener(window, "click", _this.onClickCallback); // 清理批量发送定时器
1797
1694
 
1798
1695
 
1799
- _this.saveBatchQueueToStorage(); // 如果队列达到最大数量,立即发送
1696
+ _this.clearBatchTimer(); // 清理定时上报定时器
1800
1697
 
1801
1698
 
1802
- if (_this.batchQueue.length >= batchMaxSize) {
1803
- _this.flushBatchQueue();
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
- _this.batchTimer = null;
1814
- }, batchInterval);
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
- if (storedQueue) {
1827
- var parsedQueue = JSON.parse(storedQueue);
1730
+ _this.getBatchQueueFromStorage = function () {
1731
+ try {
1732
+ var storedQueue = _this.getLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY);
1828
1733
 
1829
- if (Array.isArray(parsedQueue) && parsedQueue.length > 0) {
1830
- _this.batchQueue = parsedQueue;
1734
+ if (storedQueue) {
1735
+ var parsedQueue = JSON.parse(storedQueue);
1831
1736
 
1832
- if (_this.initConfig.showLog) {
1833
- _this.printLog("\u4ECE LocalStorage \u6062\u590D " + parsedQueue.length + " \u6761\u5F85\u53D1\u9001\u6570\u636E");
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
- var batchMaxSize = _this.initConfig.batchMaxSize;
1745
+ _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, "[]");
1746
+ }
1838
1747
 
1839
- if (_this.batchQueue.length >= batchMaxSize) {
1840
- _this.flushBatchQueue();
1841
- } else {
1842
- // 设置定时发送
1843
- var batchInterval = _this.initConfig.batchInterval;
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
- _this.batchTimer = null;
1850
- }, batchInterval);
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
- _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, "[]");
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
- _this.addToPendingRequests = function (params) {
1869
- _this.pendingRequests.push(params); // 限制队列大小,防止内存溢出
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
- var maxSize = _this.initConfig.pendingRequestsMaxSize || _this.DEFAULT_PENDING_REQUESTS_MAX_SIZE;
1782
+ _this.getPendingRequestsFromStorage = function () {
1783
+ try {
1784
+ var storedRequests = _this.getLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY);
1873
1785
 
1874
- if (_this.pendingRequests.length > maxSize) {
1875
- _this.pendingRequests = _this.pendingRequests.slice(-maxSize);
1786
+ if (storedRequests) {
1787
+ var parsedRequests = JSON.parse(storedRequests);
1876
1788
 
1877
- if (_this.initConfig.showLog) {
1878
- _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");
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
- _this.restorePendingRequestsFromStorage = function () {
1888
- try {
1889
- var storedRequests = _this.getLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY);
1797
+ _this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, "[]");
1798
+ }
1890
1799
 
1891
- if (storedRequests) {
1892
- var parsedRequests = JSON.parse(storedRequests);
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
- if (_this.initConfig.showLog) {
1898
- _this.printLog("\u4ECE LocalStorage \u6062\u590D " + parsedRequests.length + " \u6761\u5F85\u53D1\u9001\u8BF7\u6C42");
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
- if (_this.pendingRequests.length > 0) {
1903
- _this.flushPendingRequests();
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
- _this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, "[]");
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
- _this.isPageUnloading = function () {
1921
- return document.visibilityState === "hidden";
1922
- };
1923
- /**
1924
- * 使用 sendBeacon 发送数据(页面卸载时的备用方案)
1925
- * @param params 数据参数
1926
- * @param serverUrl 服务器地址
1927
- * @param contentType 内容类型
1928
- * @returns 是否发送成功
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
- _this.sendWithBeacon = function (params, serverUrl, contentType) {
1933
- try {
1934
- var blob = new Blob([JSON.stringify(params)], {
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
- return false;
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
- var requestsToSend = __spreadArray([], _this.pendingRequests);
1891
+ _this.onPageViewCallback = function (e) {
1892
+ var _a, _b; // 在路由变化前,先发送待发送的数据(避免被取消)
1958
1893
 
1959
- _this.pendingRequests = []; // 清除 LocalStorage 中的待发送请求
1960
1894
 
1961
- _this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, "[]"); // 尝试发送每个请求(使用 sendData,但如果失败不再添加到 pendingRequests,避免循环)
1895
+ var pendingRequests = _this.getPendingRequestsFromStorage();
1962
1896
 
1897
+ var batchQueue = _this.initConfig.batchSend ? _this.getBatchQueueFromStorage() : [];
1963
1898
 
1964
- requestsToSend.forEach(function (params) {
1965
- // 直接调用 sendData,但不处理失败情况(避免循环)
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
- _this.saveBatchQueueToStorage = function () {
1980
- try {
1981
- var queueString = JSON.stringify(_this.batchQueue); // 检查存储大小,避免超出 LocalStorage 限制(通常 5-10MB)
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
- if (queueString.length > _this.MAX_STORAGE_SIZE) {
1985
- var maxItems = Math.floor(_this.batchQueue.length * 0.8); // 保留 80%
1914
+ _this.currentUrl = window.location.href; // 如果使用自定义 pageKey,路由变化时不自动更新
1986
1915
 
1987
- _this.batchQueue = _this.batchQueue.slice(-maxItems);
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
- _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, JSON.stringify(_this.batchQueue));
1993
- } catch (e) {
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.setupBeforeUnloadListener = function () {
2004
- // 避免重复设置监听器
2005
- if (_this.isUnloadListenerSetup) {
2006
- return;
2007
- }
1927
+ _this.sendRetained(e.type);
2008
1928
 
2009
- _this.isUnloadListenerSetup = true; // 使用 visibilitychange 事件(更可靠,支持页面跳转、切换标签页等场景)
1929
+ _this.sendData(params);
1930
+ };
2010
1931
 
2011
- document.addEventListener("visibilitychange", function () {
2012
- if (_this.isPageUnloading()) {
2013
- _this.flushPendingData();
2014
- }
2015
- }); // 使用 beforeunload 事件作为备用(页面关闭/刷新)
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
- window.addEventListener("beforeunload", function () {
2018
- _this.flushPendingData();
2019
- }); // 使用 pagehide 事件(更可靠,支持移动端)
1944
+ var filteredBusiness = _this.filterSensitiveData(business || {});
2020
1945
 
2021
- window.addEventListener("pagehide", function () {
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
- _this.flushPendingData = function () {
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
- if (_this.pendingRequests.length > 0) {
2040
- allPendingData.push.apply(allPendingData, _this.pendingRequests);
2041
- }
1966
+ }; // 添加其他可能的属性
2042
1967
 
2043
- if (allPendingData.length === 0) {
2044
- return;
2045
- } // 使用 sendBeacon 发送数据(最可靠的方式)
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
- if (navigator.sendBeacon && _this.initConfig.serverUrl) {
2049
- try {
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
- if (sent) {
2060
- // 发送成功,清除所有队列
2061
- _this.batchQueue = [];
2062
- _this.pendingRequests = [];
1980
+ if (filteredPrivateParamMap.elementSelector) {
1981
+ privateParamMapData.elementSelector = filteredPrivateParamMap.elementSelector;
1982
+ }
2063
1983
 
2064
- _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, "[]");
1984
+ if (filteredPrivateParamMap.retainedDuration) {
1985
+ privateParamMapData.retainedDuration = filteredPrivateParamMap.retainedDuration;
1986
+ }
2065
1987
 
2066
- if (_this.initConfig.showLog) {
2067
- _this.printLog("\u9875\u9762\u5378\u8F7D\u65F6\u6210\u529F\u53D1\u9001 " + allPendingData.length + " \u6761\u6570\u636E");
2068
- }
2069
- } else {
2070
- // sendBeacon 返回 false,保存到 LocalStorage(批量模式)
2071
- if (_this.initConfig.batchSend && _this.batchQueue.length > 0) {
2072
- _this.saveBatchQueueToStorage();
2073
- } // 保存 pendingRequests 到 LocalStorage(如果支持)
2074
-
2075
-
2076
- if (_this.pendingRequests.length > 0) {
2077
- try {
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
- if (_this.pendingRequests.length > 0) {
2095
- try {
2096
- _this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, JSON.stringify(_this.pendingRequests));
2097
- } catch (e) {// LocalStorage 可能已满或不可用
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("\u9875\u9762\u5378\u8F7D\u65F6\u53D1\u9001\u6570\u636E\u5931\u8D25: " + e);
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
- // 不支持 sendBeacon,保存到 LocalStorage(批量模式)
2107
- if (_this.initConfig.batchSend && _this.batchQueue.length > 0) {
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
- if (_this.pendingRequests.length > 0) {
2113
- try {
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.sendData = function (params, header) {
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
- return Promise.resolve({
2129
- success: true,
2130
- message: "数据已采样跳过"
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
- if (batchSend) {
2144
- _this.addToBatchQueue(params);
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
- } // 如果使用 sendBeacon 且没有自定义 header
2151
-
2152
-
2153
- if (_this.isSupportBeaconSend() === true && !header && !initHeader) {
2154
- // 检查页面是否即将卸载,如果是,直接使用 sendBeacon 发送,避免被取消
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
- return Promise.resolve({
2605
+ resolve({
2160
2606
  success: true,
2161
- message: "页面卸载时发送成功"
2607
+ message: "页面卸载时使用 sendBeacon 发送成功"
2162
2608
  });
2163
- } else {
2164
- // sendBeacon 返回 false,添加到待发送队列
2165
- _this.addToPendingRequests(params);
2609
+ return;
2610
+ } // sendBeacon 失败,继续使用 XMLHttpRequest
2611
+
2612
+ }
2166
2613
 
2167
- return Promise.resolve({
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
- message: "已添加到待发送队列"
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
- } // 正常情况使用 sendBeacon
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
- return _this.sendBeacon({
2176
- contentType: contentType,
2177
- url: serverUrl,
2178
- data: params
2179
- }).catch(function (err) {
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
- return Promise.resolve({
2184
- success: true,
2185
- message: "sendBeacon 失败,已添加到待发送队列"
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
- } else {
2189
- // 使用 XMLHttpRequest 发送
2190
- return new Promise(function (resolve, reject) {
2191
- // 如果页面即将卸载,也尝试使用 sendBeacon 作为备用
2192
- if (_this.isPageUnloading() && _this.isSupportBeaconSend() && !header && !initHeader) {
2193
- var sent = _this.sendWithBeacon(params, serverUrl, contentType);
2194
-
2195
- if (sent) {
2196
- resolve({
2197
- success: true,
2198
- message: "页面卸载时使用 sendBeacon 发送成功"
2199
- });
2200
- return;
2201
- } // sendBeacon 失败,继续使用 XMLHttpRequest
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
- _this.ajax({
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
- reject({
2235
- success: false,
2236
- message: String(err),
2237
- code: status
2238
- });
2239
- }
2240
- });
2241
- });
2868
+ if (currentElement && currentElement.tagName === 'BODY') {
2869
+ break;
2242
2870
  }
2243
- };
2244
- /**
2245
- * @description 留存时长上报
2246
- * @param type
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
- _this.sendRetained = function (type) {
2251
- var params = _this.getParams({
2252
- event: "PageRetained",
2253
- desc: _this.eventDescMap["PageRetained"]
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
- var retainedStartTime = __time ? +__time : _this.getTimeStamp();
2939
+ _this.observeExposureElements = function () {
2940
+ if (!_this.exposureObserver) {
2941
+ return;
2942
+ }
2260
2943
 
2261
- var retainedData = __assign(__assign({}, params), {
2262
- privateParamMap: __assign(__assign({}, params.privateParamMap), {
2263
- retainedDuration: Math.max(params.requestTime - retainedStartTime, 0)
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.sendData(retainedData);
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
- _this.setCookie("retainedStartTime", _this.getTimeStamp());
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
- _this.getItemKey = function (partkey, pageKey) {
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
- _this.sdkVersion = "1.2.0"; // sdk版本
2290
-
2291
- _this.initConfig = {
2292
- appKey: "",
2293
- platform: undefined,
2294
- showLog: false,
2295
- serverUrl: "",
2296
- autoTrack: false,
2297
- sendTimeout: 3000,
2298
- isTrackSinglePage: false,
2299
- contentType: "application/json",
2300
- business: {},
2301
- header: undefined,
2302
- sampleRate: 1,
2303
- batchSend: false,
2304
- batchInterval: 5000,
2305
- batchMaxSize: 10,
2306
- trackPartKeyClick: false,
2307
- pendingRequestsMaxSize: 50 // 待发送请求队列最大数量(防止内存溢出)
2308
-
2309
- }; // 系统信息
2310
-
2311
- _this.systemsInfo = {};
2312
- return _this;
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
- WebTracking.prototype.addSinglePageEvent = function (callback) {
2321
- var _this = this;
3025
+ _this.stopExposureObserver = function () {
3026
+ if (_this.exposureObserver) {
3027
+ _this.exposureObserver.disconnect();
2322
3028
 
2323
- var historyPushState = window.history.pushState;
2324
- var singlePageEvent = historyPushState ? "popstate" : "hashchange";
2325
- this.each(["pushState", "replaceState", singlePageEvent], function (historyType) {
2326
- _this.addEventListener(window, historyType, callback);
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
- return WebTracking;
2331
- }(WebTrackingTools);
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
- var index = new WebTracking();
3096
+ var index = new WebTracking();
2334
3097
 
2335
- return index;
3098
+ return index;
2336
3099
 
2337
3100
  })));