@fle-sdk/event-tracking-web 1.2.0-beta.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +340 -115
- package/lib/index.esm.js +1979 -16
- package/lib/index.esm.min.js +1 -0
- package/lib/index.js +1987 -16
- package/lib/index.min.js +1 -0
- package/lib/types/index.d.ts +132 -87
- package/lib/types/tools.d.ts +209 -113
- package/lib/types/type.d.ts +466 -243
- package/package.json +9 -6
- package/lib/index.ems.js +0 -16
- package/lib/index.ems.js.map +0 -1
- package/lib/index.esm.js.map +0 -1
- package/lib/index.js.map +0 -1
package/lib/index.esm.js
CHANGED
|
@@ -1,16 +1,1979 @@
|
|
|
1
|
-
/*! *****************************************************************************
|
|
2
|
-
Copyright (c) Microsoft Corporation.
|
|
3
|
-
|
|
4
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
5
|
-
purpose with or without fee is hereby granted.
|
|
6
|
-
|
|
7
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
8
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
9
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
10
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
11
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
12
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
13
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
14
|
-
***************************************************************************** */
|
|
15
|
-
var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(t,n)};var t=function(){return(t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},n=new(function(n){function r(){var e=n.call(this)||this;return e.userInfo=null,e.currentUrl="",e.pageKey="",e.eventDescMap={PageView:"Web 浏览页面",WebClick:"Web 元素点击",PageRetained:"Web 页面浏览时长",CustomTrack:"Web 自定义代码上报"},e.init=function(t){e.preset(t);var n=window.location.pathname;e.currentUrl=window.location.href,e.pageKey=n.replace(/\//g,"_").substr(1),e.systemsInfo=e.getSystemsInfo(t.platform),e.setCookie("retainedStartTime",e.getTimeStamp())},e.preset=function(t){t instanceof Object&&e.each(t,(function(t,n){e.initConfig.hasOwnProperty(n)&&(e.initConfig[n]=t)})),/^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-\(\)]*[\w@?^=%&/~+#-\(\)])?$/.test(e.initConfig.serverUrl)||(e.printLog("当前 server_url 为空或不正确,只在控制台打印日志,network 中不会发数据,请配置正确的 server_url!"),e.initConfig.showLog=!0),e.initConfig.autoTrack?e.listener():e.unlistener()},e.login=function(t){e.isObject(t)&&(e.userInfo=t)},e.track=function(t){var n=t.desc,r=t.partkey,i=t.business,o=t.header,a=e.getParams({desc:n,event:"CustomTrack",itemKey:e.getItemKey(r),privateParamMap:{business:i}});return e.sendData(a,o)},e.listener=function(){e.initConfig.isTrackSinglePage&&(e.rewriteHistory(),e.addSinglePageEvent(e.onPageViewCallback)),e.each(["load","beforeunload"],(function(t){e.addEventListener(window,t,e.onPageViewCallback)})),e.addEventListener(window,"click",e.onClickCallback)},e.unlistener=function(){if(e.initConfig.isTrackSinglePage){var t=window.history.pushState?"popstate":"hashchange";e.each(["pushState","replaceState",t],(function(t){e.removeEventListener(window,t,e.onPageViewCallback)}))}e.each(["load","beforeunload"],(function(t){e.removeEventListener(window,t,e.onPageViewCallback)})),e.removeEventListener(window,"click",e.onClickCallback)},e.onClickCallback=function(t){var n,r;if(null===(r=null===(n=null==t?void 0:t.target)||void 0===n?void 0:n.dataset)||void 0===r?void 0:r.partKey){var i=[t.pageX,t.pageY],o=t.target.id,a=t.target.className,s={id:o,nodeName:t.target.nodeName,className:a,position:i},c=e.getParams({event:"WebClick",desc:e.eventDescMap.WebClick,itemKey:e.getItemKey(t.target.dataset.partKey),privateParamMap:{targetEle:s,pointerType:t.pointerType,currentUrl:e.currentUrl,elementSelector:e.getDomSelector(t.target)}});return e.sendData(c)}},e.onPageViewCallback=function(t){var n,r,i=window.location.origin,o=e.getParams({event:"PageView",desc:e.eventDescMap.PageView,privateParamMap:{currentUrl:e.currentUrl,targetUrl:(null===(n=t.arguments)||void 0===n?void 0:n[2])?i+(null===(r=t.arguments)||void 0===r?void 0:r[2]):null}});e.currentUrl=window.location.href,e.pageKey=window.location.pathname.replace(/\//g,"_").substring(1),e.sendRetained(t.type),e.sendData(o)},e.getParams=function(n){var r=n.event,i=n.desc,o=n.privateParamMap,a=void 0===o?{}:o,s=n.itemKey,c=e.initConfig.business,u=window.innerWidth,l=window.innerHeight,d=window.screen.width,p=window.screen.height;return{desc:i,event:r,itemKey:s||e.getItemKey(),requestTime:e.getTimeStamp(),deviceId:e.getDistinctId(),privateParamMap:t(t({},a),{pageWidth:u,pageHeight:l,screenWidth:d,screenHeight:p,sdkVersion:e.sdkVersion,systemsInfo:e.systemsInfo,urlParams:e.getQueryValue(),userInfo:e.userInfo,currentUrl:a.currentUrl||e.currentUrl,business:t(t({},c||{}),a.business||{})})}},e.sendData=function(t,n){var r=e.initConfig,i=r.serverUrl,o=r.sendTimeout,a=r.contentType,s=r.showLog,c=r.header;return s&&e.printLog(t),!0!==e.isSupportBeaconSend()||n||c?new Promise((function(r,s){e.ajax({header:n||c,url:i,type:"POST",data:JSON.stringify(t),contentType:a,credentials:!1,timeout:o,cors:!0,success:function(e){return r(e)},error:function(e){return s(e)}})})):e.sendBeacon({contentType:a,url:i,data:t})},e.sendRetained=function(n){var r=e.getParams({event:"PageRetained",desc:e.eventDescMap.PageRetained});if(["beforeunload","pushState","replaceState","hashchange","popstate"].indexOf(n)>=0){var i=e.getCookie("retainedStartTime"),o=i?+i:e.getTimeStamp(),a=t(t({},r),{privateParamMap:t(t({},r.privateParamMap),{retainedDuration:Math.max(r.requestTime-o,0)})});e.sendData(a),e.setCookie("retainedStartTime",e.getTimeStamp())}},e.getItemKey=function(t){return[e.initConfig.appKey,e.pageKey,t?t.toString():void 0].filter((function(e){return!!e})).reduce((function(e,t){return e+(e.length?".":"")+t}),"")},e.sdkVersion="1.1.0",e.initConfig={appKey:"",platform:void 0,showLog:!1,serverUrl:"",autoTrack:!1,sendTimeout:3e3,isTrackSinglePage:!1,contentType:"application/json",business:{},header:void 0},e.systemsInfo={},e}return function(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}(r,n),r.prototype.addSinglePageEvent=function(e){var t=this,n=window.history.pushState?"popstate":"hashchange";this.each(["pushState","replaceState",n],(function(n){t.addEventListener(window,n,e)}))},r}(function(){function e(){var e=this;this.getSystemsInfo=function(e){var t=navigator.userAgent,n="other",r=[],i={language:navigator.language},o=t.match(/MicroMessenger\/([\d\.]+)/i),a=o&&o[1]?o[1]:null,s=t.match(/(ipod).*\s([\d_]+)/i),c=t.match(/(ipad).*\s([\d_]+)/i),u=t.match(/(iphone)\sos\s([\d_]+)/i),l=t.match(/(android)\s([\d\.]+)/i),d=t.match(/(Windows NT)\s([\d\.]+)/i),p=t.match(/(Mac OS X)\s([\d_]+)/i);r=[],l?(r.push("Android "+l[2]),n="h5"):u?(r.push("iPhone, iOS "+u[2].replace(/_/g,".")),n="h5"):c?(r.push("iPad, iOS "+c[2].replace(/_/g,".")),n="ipad"):s?(r.push("iPod, iOS "+s[2].replace(/_/g,".")),n="h5"):d?(r.push("Windows "+d[2].replace(/_/g,".")),n="pc"):p&&(r.push("Mac, MacOS "+p[2].replace(/_/g,".")),n="pc"),a&&r.push("WeChat "+a),i.client=r.length?r.join(", "):"Unknown",i.platform=e||n;var g=t.toLowerCase().match(/ nettype\/([^ ]+)/g);return g&&g[0]&&(r=[(g=g[0].split("/"))[1]],i.network=r.length?r.join(", "):"Unknown"),i.ua=t,i},this.addEventListener=function(e,t,n){e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent&&e.attachEvent("on"+t,(function(t){return n.call(e,t)}),!1)},this.removeEventListener=function(e,t,n){e.removeEventListener?e.removeEventListener(t,n):e.detachEvent&&e.detachEvent("on"+t,(function(t){return n.call(e,t)}),!0)},this.rewriteHistory=function(){var e=window.history,t=function(e){var t=window.history,n=t[e],r=new Event(e);return function(){var e=n.apply(t,arguments);return r.arguments=arguments,window.dispatchEvent(r),e}};window.history.pushState&&(e.pushState=t("pushState"),e.replaceState=t("replaceState"))},this.isArray=Array.isArray||function(e){return"[object Array]"===toString.call(e)},this.formatJsonString=function(e){try{return JSON.stringify(e,null," ")}catch(t){return JSON.stringify(e)}},this.nativeForEach=Array.prototype.forEach,this.slice=Array.prototype.slice,this.hasOwnProperty=Object.prototype.hasOwnProperty,this.breaker={},this.each=function(t,n,r){if(null==t)return!1;if(e.nativeForEach&&t.forEach===e.nativeForEach)t.forEach(n,r);else if(e.isArray(t)&&t.length===+t.length){for(var i=0,o=t.length;i<o;i++)if(i in t&&n.call(r,t[i],i,t)===e.breaker)return!1}else for(var a in t)if(e.hasOwnProperty.call(t,a)&&n.call(r,t[a],a,t)===e.breaker)return!1},this.getDomIndex=function(e){if(!e.parentNode)return-1;for(var t=0,n=e.tagName,r=e.parentNode.children,i=0;i<r.length;i++)if(r[i].tagName===n){if(e===r[i])return t;t++}return-1},this.selector=function(t){var n=t.parentNode&&9==t.parentNode.nodeType?-1:e.getDomIndex(t);return t.getAttribute&&t.getAttribute("id")&&/^[A-Za-z][-A-Za-z0-9_:.]*$/.test(t.getAttribute("id"))?"#"+t.getAttribute("id"):t.tagName.toLowerCase()+(~n?":nth-of-type("+(n+1)+")":"")},this.getDomSelector=function(t,n){if(!t||!t.parentNode||!t.parentNode.children)return!1;n=n&&n.join?n:[];var r=t.nodeName.toLowerCase();return t&&"body"!==r&&1==t.nodeType?(n.unshift(e.selector(t)),t.getAttribute&&t.getAttribute("id")&&/^[A-Za-z][-A-Za-z0-9_:.]*$/.test(t.getAttribute("id"))?n.join(" > "):e.getDomSelector(t.parentNode,n)):(n.unshift("body"),n.join(" > "))},this.getCookie=function(e){for(var t=e+"=",n=document.cookie.split(";"),r=0;r<n.length;r++){for(var i=n[r];" "==i.charAt(0);)i=i.substring(1,i.length);if(0==i.indexOf(t))return this._decodeURIComponent(i.substring(t.length,i.length))}return null},this.setCookie=function(e,t,n){var r,i="";n=null==n?73e3:n;var o=this.getMainHost();if(r=o?"; domain="+o:"",0!==n){var a=new Date;"s"===String(n).slice(-1)?a.setTime(a.getTime()+1e3*Number(String(n).slice(0,-1))):a.setTime(a.getTime()+24*n*60*60*1e3),i="; expires="+a.toUTCString()}function s(e){return e||!1}var c="",u="",l="";e&&(c=s(e)),t&&(u=s(t)),r&&(l=s(r)),c&&u&&(document.cookie=c+"="+encodeURIComponent(u)+i+"; path=/"+l)},this.removeCookie=function(t){e.setCookie(t,"",-1)},this.getTimeStamp=function(){return(new Date).getTime()},this.uuid=function(){return"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".replace(/[xy]/g,(function(e){var t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)}))},this.getDistinctId=function(){var t=e.getCookie("distinctId");return t||(t=e.uuid(),e.setCookie("distinctId",t),t)},this.getQueryValue=function(){for(var e=decodeURI(window.location.href).match(new RegExp("[?&][^?&]+=[^?&]+","g"))||[],t={},n=0;n<e.length;n++){var r=e[n].replace(/\?|\&/,"").split("=");t[r[0]]=r[1]}return t},this.ajax=function(t){function n(e){if(!e)return"";try{return JSON.parse(e)}catch(e){return{}}}t.timeout=t.timeout||3e4,t.credentials=void 0===t.credentials||t.credentials;var r=e.xhr(t.cors);if(!r)return!1;t.type||(t.type=t.data?"POST":"GET");var i,o=t.success,a=t.error;t.success=function(e){o(e),i&&(clearTimeout(i),i=null)},t.error=function(e){a(e),i&&(clearTimeout(i),i=null)},i=setTimeout((function(){!function(){try{e.isObject(r)&&r.abort&&r.abort()}catch(t){e.printLog(t)}i&&(clearTimeout(i),i=null,t.error&&t.error(),r.onreadystatechange=null,r.onload=null,r.onerror=null)}()}),t.timeout),r.onreadystatechange=function(){try{4==r.readyState&&(r.status>=200&&r.status<300||304==r.status?t.success(n(r.responseText)):t.error(n(r.responseText),r.status),r.onreadystatechange=null,r.onload=null)}catch(e){r.onreadystatechange=null,r.onload=null}},r.open(t.type,t.url,!0);try{t.credentials&&(r.withCredentials=!0),e.isObject(t.header)&&e.each(t.header,(function(e,t){r.setRequestHeader&&r.setRequestHeader(t,e)})),t.data&&(t.cors||r.setRequestHeader&&r.setRequestHeader("X-Requested-With","XMLHttpRequest"),"application/json"===t.contentType?r.setRequestHeader&&r.setRequestHeader("Content-type","application/json; charset=UTF-8"):r.setRequestHeader&&r.setRequestHeader("Content-type","application/x-www-form-urlencoded"))}catch(t){e.printLog(t)}r.send(t.data||null)},this.xhr=function(e){return e?void 0!==window.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest?new XMLHttpRequest:null:void 0!==window.XMLHttpRequest?new XMLHttpRequest:null},this.getUA=function(){var e,t={},n=navigator.userAgent.toLowerCase();return(e=n.match(/opera.([\d.]+)/))?t.opera=Number(e[1].split(".")[0]):(e=n.match(/msie ([\d.]+)/))?t.ie=Number(e[1].split(".")[0]):(e=n.match(/edge.([\d.]+)/))?t.edge=Number(e[1].split(".")[0]):(e=n.match(/firefox\/([\d.]+)/))?t.firefox=Number(e[1].split(".")[0]):(e=n.match(/chrome\/([\d.]+)/))?t.chrome=Number(e[1].split(".")[0]):(e=n.match(/version\/([\d.]+).*safari/))?t.safari=Number(e[1].match(/^\d*.\d*/)):(e=n.match(/trident\/([\d.]+)/))&&(t.ie=11),t},this.isSupportBeaconSend=function(){var t=!1;if("object"!=typeof navigator||"function"!=typeof navigator.sendBeacon)return t;var n=e.getUA(),r=navigator.userAgent.toLowerCase();if(/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)){var i=(r.match(/os [\d._]*/gi)+"").replace(/[^0-9|_.]/gi,"").replace(/_/gi,".").split(".");void 0===n.safari&&(n.safari=i[0]),i[0]&&+i[0]<13?(n.chrome>41||n.firefox>30||n.opera>25||n.safari>12)&&(t=!0):(n.chrome>41||n.firefox>30||n.opera>25||n.safari>11.3)&&(t=!0)}else(n.chrome>38||n.edge>13||n.firefox>30||n.opera>25||n.safari>11)&&(t=!0);return t},this.sendBeacon=function(t){if(!0===e.isSupportBeaconSend()){var n={type:t.contentType},r=new Blob([JSON.stringify(t.data)],n);return navigator.sendBeacon(t.url,r)?Promise.resolve({success:!0}):Promise.reject({success:!1})}return Promise.reject({message:"不支持sendBeacon,发送失败!",success:!1})}}return e.prototype.printLog=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];if(this.isObject(e[0])&&(e[0]=this.formatJsonString(e[0])),"object"==typeof console&&console.log)try{return console.log.apply(console,e)}catch(t){console.log(e[0])}},e.prototype.isObject=function(e){return null!=e&&"[object Object]"==toString.call(e)},e.prototype.isUndefined=function(e){return void 0===e},e.prototype.isString=function(e){return"[object String]"==toString.call(e)},e.prototype.isDate=function(e){return"[object Date]"==toString.call(e)},e.prototype.isBoolean=function(e){return"[object Boolean]"==toString.call(e)},e.prototype.isNumber=function(e){return"[object Number]"==toString.call(e)&&/[\d\.]+/.test(String(e))},e.prototype.isElement=function(e){return!(!e||1!==e.nodeType)},e.prototype.isFunction=function(e){if(!e)return!1;var t=toString.call(e);return"[object Function]"==t||"[object AsyncFunction]"==t},e.prototype.isJSONString=function(e){try{JSON.parse(e)}catch(e){return!1}return!0},e.prototype._decodeURIComponent=function(e){var t=e;try{t=decodeURIComponent(e)}catch(n){t=e}return t},e.prototype.getMainHost=function(){var e="mh_"+Math.random(),t=new RegExp("(^|;)\\s*"+e+"=12345"),n=new Date(0),r=document.domain.split("."),i=[];for(i.unshift(r.pop());r.length;){i.unshift(r.pop());var o=i.join("."),a=e+"=12345;domain=."+o;if(document.cookie=a,t.test(document.cookie))return document.cookie=a+";expires="+n,o}},e}()));export default n;
|
|
16
|
-
|
|
1
|
+
/*! *****************************************************************************
|
|
2
|
+
Copyright (c) Microsoft Corporation.
|
|
3
|
+
|
|
4
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
5
|
+
purpose with or without fee is hereby granted.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
8
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
9
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
10
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
11
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
12
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
13
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
14
|
+
***************************************************************************** */
|
|
15
|
+
/* global Reflect, Promise */
|
|
16
|
+
|
|
17
|
+
var extendStatics = function(d, b) {
|
|
18
|
+
extendStatics = Object.setPrototypeOf ||
|
|
19
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
20
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
21
|
+
return extendStatics(d, b);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function __extends(d, b) {
|
|
25
|
+
if (typeof b !== "function" && b !== null)
|
|
26
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
27
|
+
extendStatics(d, b);
|
|
28
|
+
function __() { this.constructor = d; }
|
|
29
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var __assign = function() {
|
|
33
|
+
__assign = Object.assign || function __assign(t) {
|
|
34
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
35
|
+
s = arguments[i];
|
|
36
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
37
|
+
}
|
|
38
|
+
return t;
|
|
39
|
+
};
|
|
40
|
+
return __assign.apply(this, arguments);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function __spreadArray(to, from) {
|
|
44
|
+
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
|
|
45
|
+
to[j] = from[i];
|
|
46
|
+
return to;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function _typeof(obj) {
|
|
50
|
+
"@babel/helpers - typeof";
|
|
51
|
+
|
|
52
|
+
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
|
|
53
|
+
_typeof = function (obj) {
|
|
54
|
+
return typeof obj;
|
|
55
|
+
};
|
|
56
|
+
} else {
|
|
57
|
+
_typeof = function (obj) {
|
|
58
|
+
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return _typeof(obj);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
var WebTrackingTools =
|
|
66
|
+
/** @class */
|
|
67
|
+
function () {
|
|
68
|
+
function WebTrackingTools() {
|
|
69
|
+
var _this = this;
|
|
70
|
+
/**
|
|
71
|
+
* 系统信息
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
this.getSystemsInfo = function (platform) {
|
|
76
|
+
// print system info
|
|
77
|
+
var ua = navigator.userAgent;
|
|
78
|
+
var language = navigator.language;
|
|
79
|
+
var _platform = "other";
|
|
80
|
+
var logMsg = [];
|
|
81
|
+
var systemsInfo = {
|
|
82
|
+
language: language
|
|
83
|
+
}; // wechat client version
|
|
84
|
+
|
|
85
|
+
var wxVersionMatch = ua.match(/MicroMessenger\/([\d\.]+)/i);
|
|
86
|
+
var wxVersion = wxVersionMatch && wxVersionMatch[1] ? wxVersionMatch[1] : null; // device & system
|
|
87
|
+
|
|
88
|
+
var ipod = ua.match(/(ipod).*\s([\d_]+)/i),
|
|
89
|
+
ipad = ua.match(/(ipad).*\s([\d_]+)/i),
|
|
90
|
+
iphone = ua.match(/(iphone)\sos\s([\d_]+)/i),
|
|
91
|
+
android = ua.match(/(android)\s([\d\.]+)/i),
|
|
92
|
+
windows = ua.match(/(Windows NT)\s([\d\.]+)/i),
|
|
93
|
+
mac = ua.match(/(Mac OS X)\s([\d_]+)/i);
|
|
94
|
+
logMsg = [];
|
|
95
|
+
|
|
96
|
+
if (android) {
|
|
97
|
+
logMsg.push("Android " + android[2]);
|
|
98
|
+
_platform = "h5";
|
|
99
|
+
} else if (iphone) {
|
|
100
|
+
logMsg.push("iPhone, iOS " + iphone[2].replace(/_/g, "."));
|
|
101
|
+
_platform = "h5";
|
|
102
|
+
} else if (ipad) {
|
|
103
|
+
logMsg.push("iPad, iOS " + ipad[2].replace(/_/g, "."));
|
|
104
|
+
_platform = "ipad";
|
|
105
|
+
} else if (ipod) {
|
|
106
|
+
logMsg.push("iPod, iOS " + ipod[2].replace(/_/g, "."));
|
|
107
|
+
_platform = "h5";
|
|
108
|
+
} else if (windows) {
|
|
109
|
+
logMsg.push("Windows " + windows[2].replace(/_/g, "."));
|
|
110
|
+
_platform = "pc";
|
|
111
|
+
} else if (mac) {
|
|
112
|
+
logMsg.push("Mac, MacOS " + mac[2].replace(/_/g, "."));
|
|
113
|
+
_platform = "pc";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (wxVersion) {
|
|
117
|
+
logMsg.push("WeChat " + wxVersion);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
systemsInfo["client"] = logMsg.length ? logMsg.join(", ") : "Unknown";
|
|
121
|
+
systemsInfo["platform"] = platform || _platform; // network type
|
|
122
|
+
|
|
123
|
+
var network = ua.toLowerCase().match(/ nettype\/([^ ]+)/g);
|
|
124
|
+
|
|
125
|
+
if (network && network[0]) {
|
|
126
|
+
// @ts-ignore
|
|
127
|
+
network = network[0].split("/");
|
|
128
|
+
logMsg = [network[1]];
|
|
129
|
+
systemsInfo["network"] = logMsg.length ? logMsg.join(", ") : "Unknown";
|
|
130
|
+
} // User Agent
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
systemsInfo["ua"] = ua;
|
|
134
|
+
return systemsInfo;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* 监听事件
|
|
138
|
+
* @param {Window | Element} target
|
|
139
|
+
* @param {String} type
|
|
140
|
+
* @param {Function} handler
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
this.addEventListener = function (target, type, handler) {
|
|
145
|
+
if (target.addEventListener) {
|
|
146
|
+
target.addEventListener(type, handler, false);
|
|
147
|
+
} else {
|
|
148
|
+
target.attachEvent && target.attachEvent("on" + type, function (event) {
|
|
149
|
+
return handler.call(target, event);
|
|
150
|
+
}, false);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* 移除监听事件
|
|
155
|
+
* @param {Element} target
|
|
156
|
+
* @param {String} type
|
|
157
|
+
* @param {Funtion} handler
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
this.removeEventListener = function (target, type, handler) {
|
|
162
|
+
if (target.removeEventListener) {
|
|
163
|
+
target.removeEventListener(type, handler);
|
|
164
|
+
} else {
|
|
165
|
+
target.detachEvent && target.detachEvent("on" + type, function (event) {
|
|
166
|
+
return handler.call(target, event);
|
|
167
|
+
}, true);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* 重写history[pushState][replaceState]方法
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
this.rewriteHistory = function () {
|
|
176
|
+
var history = window.history;
|
|
177
|
+
|
|
178
|
+
var historyWrap = function historyWrap(type) {
|
|
179
|
+
var history = window.history;
|
|
180
|
+
var orig = history[type];
|
|
181
|
+
var e = new Event(type);
|
|
182
|
+
return function () {
|
|
183
|
+
var rv = orig.apply(history, arguments);
|
|
184
|
+
e.arguments = arguments;
|
|
185
|
+
window.dispatchEvent(e);
|
|
186
|
+
return rv;
|
|
187
|
+
};
|
|
188
|
+
}; // 重写
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
if (!!window.history.pushState) {
|
|
192
|
+
history.pushState = historyWrap("pushState");
|
|
193
|
+
history.replaceState = historyWrap("replaceState");
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
this.isArray = Array.isArray || function (obj) {
|
|
198
|
+
return toString.call(obj) === "[object Array]";
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* 将json序列化成json字符串
|
|
202
|
+
* @param obj
|
|
203
|
+
* @returns {[JsonString]} [json字符串]
|
|
204
|
+
*/
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
this.formatJsonString = function (obj) {
|
|
208
|
+
try {
|
|
209
|
+
return JSON.stringify(obj, null, " ");
|
|
210
|
+
} catch (e) {
|
|
211
|
+
return JSON.stringify(obj);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
this.nativeForEach = Array.prototype.forEach;
|
|
216
|
+
this.slice = Array.prototype.slice;
|
|
217
|
+
this.hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
218
|
+
this.breaker = {};
|
|
219
|
+
/**
|
|
220
|
+
* @description 循环遍历
|
|
221
|
+
*/
|
|
222
|
+
|
|
223
|
+
this.each = function (obj, iterator, context) {
|
|
224
|
+
if (obj == null) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (_this.nativeForEach && obj.forEach === _this.nativeForEach) {
|
|
229
|
+
obj.forEach(iterator, context);
|
|
230
|
+
} else if (_this.isArray(obj) && obj.length === +obj.length) {
|
|
231
|
+
for (var i = 0, l = obj.length; i < l; i++) {
|
|
232
|
+
if (i in obj && iterator.call(context, obj[i], i, obj) === _this.breaker) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
for (var key in obj) {
|
|
238
|
+
if (_this.hasOwnProperty.call(obj, key)) {
|
|
239
|
+
if (iterator.call(context, obj[key], key, obj) === _this.breaker) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return true;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
this.getDomIndex = function (el) {
|
|
250
|
+
if (!el.parentNode) return -1;
|
|
251
|
+
var i = 0;
|
|
252
|
+
var nodeName = el.tagName;
|
|
253
|
+
var list = el.parentNode.children;
|
|
254
|
+
|
|
255
|
+
for (var n = 0; n < list.length; n++) {
|
|
256
|
+
if (list[n].tagName === nodeName) {
|
|
257
|
+
if (el === list[n]) {
|
|
258
|
+
return i;
|
|
259
|
+
} else {
|
|
260
|
+
i++;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return -1;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
this.selector = function (el) {
|
|
269
|
+
var i = el.parentNode && 9 == el.parentNode.nodeType ? -1 : _this.getDomIndex(el);
|
|
270
|
+
|
|
271
|
+
if (el.getAttribute && el.getAttribute("id") && /^[A-Za-z][-A-Za-z0-9_:.]*$/.test(el.getAttribute("id"))) {
|
|
272
|
+
return "#" + el.getAttribute("id");
|
|
273
|
+
} else {
|
|
274
|
+
return el.tagName.toLowerCase() + (~i ? ":nth-of-type(" + (i + 1) + ")" : "");
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
this.getDomSelector = function (el, arr) {
|
|
279
|
+
if (!el || !el.parentNode || !el.parentNode.children) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
arr = arr && arr.join ? arr : [];
|
|
284
|
+
var name = el.nodeName.toLowerCase();
|
|
285
|
+
|
|
286
|
+
if (!el || name === "body" || 1 != el.nodeType) {
|
|
287
|
+
arr.unshift("body");
|
|
288
|
+
return arr.join(" > ");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
arr.unshift(_this.selector(el));
|
|
292
|
+
if (el.getAttribute && el.getAttribute("id") && /^[A-Za-z][-A-Za-z0-9_:.]*$/.test(el.getAttribute("id"))) return arr.join(" > ");
|
|
293
|
+
return _this.getDomSelector(el.parentNode, arr);
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* @description 增强cookie操作
|
|
297
|
+
*/
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
this.getCookie = function (name) {
|
|
301
|
+
var nameEQ = name + "=";
|
|
302
|
+
var ca = document.cookie.split(";");
|
|
303
|
+
|
|
304
|
+
for (var i = 0; i < ca.length; i++) {
|
|
305
|
+
var c = ca[i];
|
|
306
|
+
|
|
307
|
+
while (c.charAt(0) == " ") {
|
|
308
|
+
c = c.substring(1, c.length);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (c.indexOf(nameEQ) == 0) {
|
|
312
|
+
return this._decodeURIComponent(c.substring(nameEQ.length, c.length));
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return null;
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
this.setCookie = function (name, value, days) {
|
|
320
|
+
var cdomain = "",
|
|
321
|
+
expires = "",
|
|
322
|
+
secure = "",
|
|
323
|
+
samesite = "";
|
|
324
|
+
days = days == null ? 73000 : days;
|
|
325
|
+
var domain = this.getMainHost();
|
|
326
|
+
cdomain = domain ? "; domain=" + domain : "";
|
|
327
|
+
|
|
328
|
+
if (days !== 0) {
|
|
329
|
+
var date = new Date();
|
|
330
|
+
|
|
331
|
+
if (String(days).slice(-1) === "s") {
|
|
332
|
+
date.setTime(date.getTime() + Number(String(days).slice(0, -1)) * 1000);
|
|
333
|
+
} else {
|
|
334
|
+
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
expires = "; expires=" + date.toUTCString();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function getValid(data) {
|
|
341
|
+
if (data) {
|
|
342
|
+
return data;
|
|
343
|
+
} else {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
var valid_name = "";
|
|
349
|
+
var valid_value = "";
|
|
350
|
+
var valid_domain = "";
|
|
351
|
+
|
|
352
|
+
if (name) {
|
|
353
|
+
valid_name = getValid(name);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (value) {
|
|
357
|
+
valid_value = getValid(value);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (cdomain) {
|
|
361
|
+
valid_domain = getValid(cdomain);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (valid_name && valid_value) {
|
|
365
|
+
document.cookie = valid_name + "=" + encodeURIComponent(valid_value) + expires + "; path=/" + valid_domain + samesite + secure;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
/**
|
|
369
|
+
* 获取localStorage值
|
|
370
|
+
* @param key 存储键名
|
|
371
|
+
* @returns 存储值
|
|
372
|
+
*/
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
this.getLocalStorage = function (key) {
|
|
376
|
+
try {
|
|
377
|
+
return localStorage.getItem(key);
|
|
378
|
+
} catch (e) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
/**
|
|
383
|
+
* 设置localStorage值
|
|
384
|
+
* @param key 存储键名
|
|
385
|
+
* @param value 存储值
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
this.setLocalStorage = function (key, value) {
|
|
390
|
+
try {
|
|
391
|
+
localStorage.setItem(key, value);
|
|
392
|
+
} catch (e) {// 静默失败
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
this.removeCookie = function (name) {
|
|
397
|
+
_this.setCookie(name, "", -1);
|
|
398
|
+
};
|
|
399
|
+
/**
|
|
400
|
+
* 获取当前时间戳
|
|
401
|
+
* @return {number} [当前时间戳]
|
|
402
|
+
*/
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
this.getTimeStamp = function () {
|
|
406
|
+
return new Date().getTime();
|
|
407
|
+
};
|
|
408
|
+
/**
|
|
409
|
+
* 获取 UUID
|
|
410
|
+
* @return {string} [UUID唯一值]
|
|
411
|
+
*/
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
this.uuid = function () {
|
|
415
|
+
return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
416
|
+
var r = Math.random() * 16 | 0,
|
|
417
|
+
v = c == "x" ? r : r & 0x3 | 0x8;
|
|
418
|
+
return v.toString(16);
|
|
419
|
+
});
|
|
420
|
+
};
|
|
421
|
+
/**
|
|
422
|
+
* 获取设备Id
|
|
423
|
+
* @return {number} [设备Id]
|
|
424
|
+
*/
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
this.getDistinctId = function () {
|
|
428
|
+
var distinctId = _this.getCookie("distinctId");
|
|
429
|
+
|
|
430
|
+
if (distinctId) return distinctId;
|
|
431
|
+
distinctId = _this.uuid();
|
|
432
|
+
|
|
433
|
+
_this.setCookie("distinctId", distinctId);
|
|
434
|
+
|
|
435
|
+
return distinctId;
|
|
436
|
+
};
|
|
437
|
+
/**
|
|
438
|
+
* 敏感字段过滤
|
|
439
|
+
* @param obj 需要过滤的对象
|
|
440
|
+
* @param sensitiveKeys 敏感字段列表
|
|
441
|
+
* @returns 过滤后的对象
|
|
442
|
+
*/
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
this.filterSensitiveData = function (obj, sensitiveKeys) {
|
|
446
|
+
if (sensitiveKeys === void 0) {
|
|
447
|
+
sensitiveKeys = ['password', 'token', 'secret', 'key'];
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (!_this.isObject(obj)) return obj;
|
|
451
|
+
var filteredObj = {};
|
|
452
|
+
|
|
453
|
+
_this.each(obj, function (value, key) {
|
|
454
|
+
// 检查是否是敏感字段
|
|
455
|
+
var isSensitive = sensitiveKeys.some(function (sensitiveKey) {
|
|
456
|
+
return typeof key === 'string' && key.toLowerCase().includes(sensitiveKey.toLowerCase());
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
if (isSensitive) {
|
|
460
|
+
filteredObj[key] = '***';
|
|
461
|
+
} else if (_this.isObject(value)) {
|
|
462
|
+
// 递归过滤嵌套对象
|
|
463
|
+
filteredObj[key] = _this.filterSensitiveData(value, sensitiveKeys);
|
|
464
|
+
} else {
|
|
465
|
+
filteredObj[key] = value;
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
return filteredObj;
|
|
470
|
+
};
|
|
471
|
+
/**
|
|
472
|
+
* XSS过滤
|
|
473
|
+
* @param str 需要过滤的字符串
|
|
474
|
+
* @returns 过滤后的字符串
|
|
475
|
+
*/
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
this.xssFilter = function (str) {
|
|
479
|
+
if (!str) return '';
|
|
480
|
+
if (typeof str !== 'string') return str.toString();
|
|
481
|
+
return str.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/');
|
|
482
|
+
};
|
|
483
|
+
/**
|
|
484
|
+
* 获取url中的参数
|
|
485
|
+
* @param {[string]} name [参数名]
|
|
486
|
+
*/
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
this.getQueryValue = function () {
|
|
490
|
+
var _href = decodeURI(window.location.href);
|
|
491
|
+
|
|
492
|
+
var queryArr = _href.match(new RegExp("[?&][^?&]+=[^?&]+", "g")) || [];
|
|
493
|
+
var paramsObj = {};
|
|
494
|
+
|
|
495
|
+
for (var i = 0; i < queryArr.length; i++) {
|
|
496
|
+
var _item = queryArr[i].replace(/\?|\&/, "");
|
|
497
|
+
|
|
498
|
+
var pair = _item.split("=");
|
|
499
|
+
|
|
500
|
+
paramsObj[pair[0]] = pair[1];
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return Object.keys(paramsObj).length > 0 ? paramsObj : null;
|
|
504
|
+
};
|
|
505
|
+
/**
|
|
506
|
+
* Ajax请求方法
|
|
507
|
+
* @param para Ajax请求参数
|
|
508
|
+
* @returns 是否成功发起请求
|
|
509
|
+
*/
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
this.ajax = function (para) {
|
|
513
|
+
para.timeout = para.timeout || 30000;
|
|
514
|
+
para.credentials = typeof para.credentials === "undefined" ? true : para.credentials;
|
|
515
|
+
|
|
516
|
+
function getJSON(data) {
|
|
517
|
+
if (!data) {
|
|
518
|
+
return {};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (typeof data === 'string') {
|
|
522
|
+
try {
|
|
523
|
+
return JSON.parse(data);
|
|
524
|
+
} catch (e) {
|
|
525
|
+
return {};
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (_typeof(data) === 'object') {
|
|
530
|
+
return data;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return {};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
var g = _this.xhr(para.cors);
|
|
537
|
+
|
|
538
|
+
if (!g) {
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (!para.type) {
|
|
543
|
+
para.type = para.data ? "POST" : "GET";
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
var oldsuccess = para.success;
|
|
547
|
+
var olderror = para.error;
|
|
548
|
+
var errorTimer;
|
|
549
|
+
|
|
550
|
+
var abort = function abort() {
|
|
551
|
+
try {
|
|
552
|
+
if (_this.isObject(g) && g.abort) {
|
|
553
|
+
g.abort();
|
|
554
|
+
}
|
|
555
|
+
} catch (error) {
|
|
556
|
+
_this.printLog(error);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (errorTimer) {
|
|
560
|
+
clearTimeout(errorTimer);
|
|
561
|
+
errorTimer = null;
|
|
562
|
+
para.error && para.error();
|
|
563
|
+
g.onreadystatechange = null;
|
|
564
|
+
g.onload = null;
|
|
565
|
+
g.onerror = null;
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
para.success = function (data) {
|
|
570
|
+
oldsuccess && oldsuccess(data);
|
|
571
|
+
|
|
572
|
+
if (errorTimer) {
|
|
573
|
+
clearTimeout(errorTimer);
|
|
574
|
+
errorTimer = null;
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
para.error = function (err, status) {
|
|
579
|
+
olderror && olderror(err, status);
|
|
580
|
+
|
|
581
|
+
if (errorTimer) {
|
|
582
|
+
clearTimeout(errorTimer);
|
|
583
|
+
errorTimer = null;
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
errorTimer = window.setTimeout(function () {
|
|
588
|
+
abort();
|
|
589
|
+
}, para.timeout);
|
|
590
|
+
|
|
591
|
+
g.onreadystatechange = function () {
|
|
592
|
+
try {
|
|
593
|
+
if (g.readyState == 4) {
|
|
594
|
+
if (g.status >= 200 && g.status < 300 || g.status == 304) {
|
|
595
|
+
para.success && para.success(getJSON(g.responseText));
|
|
596
|
+
} else {
|
|
597
|
+
para.error && para.error(getJSON(g.responseText), g.status);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
g.onreadystatechange = null;
|
|
601
|
+
g.onload = null;
|
|
602
|
+
}
|
|
603
|
+
} catch (e) {
|
|
604
|
+
g.onreadystatechange = null;
|
|
605
|
+
g.onload = null;
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
g.open(para.type || "GET", para.url, true);
|
|
610
|
+
|
|
611
|
+
try {
|
|
612
|
+
if (para.credentials) {
|
|
613
|
+
g.withCredentials = true;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (_this.isObject(para.header)) {
|
|
617
|
+
_this.each(para.header, function (v, i) {
|
|
618
|
+
g.setRequestHeader && g.setRequestHeader(i, v);
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (para.data) {
|
|
623
|
+
if (!para.cors) {
|
|
624
|
+
g.setRequestHeader && g.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (para.contentType === "application/json") {
|
|
628
|
+
g.setRequestHeader && g.setRequestHeader("Content-type", "application/json; charset=UTF-8");
|
|
629
|
+
} else {
|
|
630
|
+
g.setRequestHeader && g.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
} catch (e) {
|
|
634
|
+
_this.printLog(e);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
g.send(para.data || null);
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
this.xhr = function (cors) {
|
|
641
|
+
if (cors) {
|
|
642
|
+
if (typeof window.XMLHttpRequest !== "undefined" && "withCredentials" in new XMLHttpRequest()) {
|
|
643
|
+
return new XMLHttpRequest();
|
|
644
|
+
} else {
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
} else if (typeof window.XMLHttpRequest !== "undefined") {
|
|
648
|
+
return new XMLHttpRequest();
|
|
649
|
+
} else {
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
this.getUA = function () {
|
|
655
|
+
var Sys = {};
|
|
656
|
+
var ua = navigator.userAgent.toLowerCase();
|
|
657
|
+
var s;
|
|
658
|
+
|
|
659
|
+
if (s = ua.match(/opera.([\d.]+)/)) {
|
|
660
|
+
Sys.opera = Number(s[1].split(".")[0]);
|
|
661
|
+
} else if (s = ua.match(/msie ([\d.]+)/)) {
|
|
662
|
+
Sys.ie = Number(s[1].split(".")[0]);
|
|
663
|
+
} else if (s = ua.match(/edge.([\d.]+)/)) {
|
|
664
|
+
Sys.edge = Number(s[1].split(".")[0]);
|
|
665
|
+
} else if (s = ua.match(/firefox\/([\d.]+)/)) {
|
|
666
|
+
Sys.firefox = Number(s[1].split(".")[0]);
|
|
667
|
+
} else if (s = ua.match(/chrome\/([\d.]+)/)) {
|
|
668
|
+
Sys.chrome = Number(s[1].split(".")[0]);
|
|
669
|
+
} else if (s = ua.match(/version\/([\d.]+).*safari/)) {
|
|
670
|
+
Sys.safari = Number(s[1].match(/^\d*.\d*/));
|
|
671
|
+
} else if (s = ua.match(/trident\/([\d.]+)/)) {
|
|
672
|
+
Sys.ie = 11;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return Sys;
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
this.isSupportBeaconSend = function () {
|
|
679
|
+
var supported = false;
|
|
680
|
+
|
|
681
|
+
if ((typeof navigator === "undefined" ? "undefined" : _typeof(navigator)) !== "object" || typeof navigator.sendBeacon !== "function") {
|
|
682
|
+
return supported;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
var Sys = _this.getUA();
|
|
686
|
+
|
|
687
|
+
var ua = navigator.userAgent.toLowerCase();
|
|
688
|
+
|
|
689
|
+
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) {
|
|
690
|
+
var reg = /os [\d._]*/gi;
|
|
691
|
+
var verinfo = ua.match(reg);
|
|
692
|
+
var version = (verinfo + "").replace(/[^0-9|_.]/gi, "").replace(/_/gi, ".");
|
|
693
|
+
var ver = version.split(".");
|
|
694
|
+
|
|
695
|
+
if (typeof Sys.safari === "undefined") {
|
|
696
|
+
Sys.safari = Number(ver[0]);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (ver[0] && +ver[0] < 13) {
|
|
700
|
+
if (Sys.chrome > 41 || Sys.firefox > 30 || Sys.opera > 25 || Sys.safari > 12) {
|
|
701
|
+
supported = true;
|
|
702
|
+
}
|
|
703
|
+
} else if (Sys.chrome > 41 || Sys.firefox > 30 || Sys.opera > 25 || Sys.safari > 11.3) {
|
|
704
|
+
supported = true;
|
|
705
|
+
}
|
|
706
|
+
} else {
|
|
707
|
+
if (Sys.chrome > 38 || Sys.edge > 13 || Sys.firefox > 30 || Sys.opera > 25 || Sys.safari > 11.0) {
|
|
708
|
+
supported = true;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return supported;
|
|
713
|
+
};
|
|
714
|
+
/**
|
|
715
|
+
* 节流函数
|
|
716
|
+
* @param func 需要节流的函数
|
|
717
|
+
* @param wait 等待时间
|
|
718
|
+
* @returns 节流后的函数
|
|
719
|
+
*/
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
this.throttle = function (func, wait) {
|
|
723
|
+
var timeout = null;
|
|
724
|
+
var previous = 0;
|
|
725
|
+
return function () {
|
|
726
|
+
var args = [];
|
|
727
|
+
|
|
728
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
729
|
+
args[_i] = arguments[_i];
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
var now = Date.now();
|
|
733
|
+
var remaining = wait - (now - previous);
|
|
734
|
+
|
|
735
|
+
if (remaining <= 0 || remaining > wait) {
|
|
736
|
+
if (timeout) {
|
|
737
|
+
clearTimeout(timeout);
|
|
738
|
+
timeout = null;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
previous = now;
|
|
742
|
+
func.apply(void 0, args);
|
|
743
|
+
} else if (!timeout) {
|
|
744
|
+
timeout = window.setTimeout(function () {
|
|
745
|
+
previous = Date.now();
|
|
746
|
+
timeout = null;
|
|
747
|
+
func.apply(void 0, args);
|
|
748
|
+
}, remaining);
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
};
|
|
752
|
+
/**
|
|
753
|
+
* 防抖函数
|
|
754
|
+
* @param func 需要防抖的函数
|
|
755
|
+
* @param wait 等待时间
|
|
756
|
+
* @returns 防抖后的函数
|
|
757
|
+
*/
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
this.debounce = function (func, wait) {
|
|
761
|
+
var timeout = null;
|
|
762
|
+
return function () {
|
|
763
|
+
var args = [];
|
|
764
|
+
|
|
765
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
766
|
+
args[_i] = arguments[_i];
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (timeout) {
|
|
770
|
+
clearTimeout(timeout);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
timeout = window.setTimeout(function () {
|
|
774
|
+
func.apply(void 0, args);
|
|
775
|
+
}, wait);
|
|
776
|
+
};
|
|
777
|
+
};
|
|
778
|
+
/**
|
|
779
|
+
* beacon请求
|
|
780
|
+
* @param para SendBeacon参数
|
|
781
|
+
* @returns Promise<TrackingResponse>
|
|
782
|
+
*/
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
this.sendBeacon = function (para) {
|
|
786
|
+
if (_this.isSupportBeaconSend() === true) {
|
|
787
|
+
var headers = {
|
|
788
|
+
type: para.contentType
|
|
789
|
+
};
|
|
790
|
+
var blob = new Blob([JSON.stringify(para.data)], headers);
|
|
791
|
+
var supportType = navigator.sendBeacon(para.url, blob);
|
|
792
|
+
|
|
793
|
+
if (supportType) {
|
|
794
|
+
return Promise.resolve({
|
|
795
|
+
success: true,
|
|
796
|
+
message: "发送成功"
|
|
797
|
+
});
|
|
798
|
+
} else {
|
|
799
|
+
return Promise.reject({
|
|
800
|
+
success: false,
|
|
801
|
+
message: "sendBeacon返回false"
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
} else {
|
|
805
|
+
return Promise.reject({
|
|
806
|
+
success: false,
|
|
807
|
+
message: "不支持sendBeacon,发送失败!"
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
/**
|
|
812
|
+
* 获取设备唯一标识
|
|
813
|
+
* 基于浏览器指纹技术,通过收集浏览器特征生成唯一ID
|
|
814
|
+
* @returns 返回设备唯一标识字符串
|
|
815
|
+
*/
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
this.getDeviceId = function () {
|
|
819
|
+
// 获取已存储的设备ID
|
|
820
|
+
var storedDeviceId = _this.getCookie('device_id') || _this.getLocalStorage('device_id');
|
|
821
|
+
|
|
822
|
+
if (storedDeviceId) {
|
|
823
|
+
return storedDeviceId;
|
|
824
|
+
} // 收集浏览器指纹信息
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
var fingerprint = _this.collectFingerprint(); // 生成设备ID
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
var deviceId = _this.hashFingerprint(fingerprint); // 存储设备ID
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
_this.setCookie('device_id', deviceId, 365 * 2); // 存储2年
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
_this.setLocalStorage('device_id', deviceId);
|
|
837
|
+
|
|
838
|
+
return deviceId;
|
|
839
|
+
};
|
|
840
|
+
/**
|
|
841
|
+
* 收集浏览器指纹信息
|
|
842
|
+
* @returns 返回浏览器指纹对象
|
|
843
|
+
*/
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
this.collectFingerprint = function () {
|
|
847
|
+
var fingerprint = {}; // 1. 用户代理
|
|
848
|
+
|
|
849
|
+
fingerprint.userAgent = navigator.userAgent; // 2. 屏幕信息
|
|
850
|
+
|
|
851
|
+
fingerprint.screenWidth = screen.width;
|
|
852
|
+
fingerprint.screenHeight = screen.height;
|
|
853
|
+
fingerprint.colorDepth = screen.colorDepth;
|
|
854
|
+
fingerprint.pixelDepth = screen.pixelDepth; // 3. 时区
|
|
855
|
+
|
|
856
|
+
fingerprint.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
857
|
+
fingerprint.timezoneOffset = new Date().getTimezoneOffset(); // 4. 语言设置
|
|
858
|
+
|
|
859
|
+
fingerprint.language = navigator.language;
|
|
860
|
+
fingerprint.languages = Array.from(navigator.languages); // 5. 平台信息
|
|
861
|
+
|
|
862
|
+
fingerprint.platform = navigator.platform; // 6. WebGL 指纹
|
|
863
|
+
|
|
864
|
+
fingerprint.webgl = _this.getWebGLFingerprint(); // 7. Canvas 指纹
|
|
865
|
+
|
|
866
|
+
fingerprint.canvas = _this.getCanvasFingerprint(); // 8. 音频上下文指纹
|
|
867
|
+
|
|
868
|
+
fingerprint.audio = _this.getAudioFingerprint(); // 9. 字体检测
|
|
869
|
+
|
|
870
|
+
fingerprint.fonts = _this.getFontFingerprint(); // 10. 插件信息
|
|
871
|
+
|
|
872
|
+
fingerprint.plugins = _this.getPluginsFingerprint(); // 11. 存储检测
|
|
873
|
+
|
|
874
|
+
fingerprint.localStorage = _this.hasLocalStorage();
|
|
875
|
+
fingerprint.sessionStorage = _this.hasSessionStorage();
|
|
876
|
+
fingerprint.indexedDB = _this.hasIndexedDB(); // 12. 硬件信息
|
|
877
|
+
|
|
878
|
+
fingerprint.hardwareConcurrency = navigator.hardwareConcurrency;
|
|
879
|
+
fingerprint.deviceMemory = navigator.deviceMemory;
|
|
880
|
+
fingerprint.maxTouchPoints = navigator.maxTouchPoints; // 13. 连接信息
|
|
881
|
+
|
|
882
|
+
fingerprint.connection = _this.getConnectionFingerprint();
|
|
883
|
+
return fingerprint;
|
|
884
|
+
};
|
|
885
|
+
/**
|
|
886
|
+
* 获取WebGL指纹
|
|
887
|
+
* @returns WebGL指纹字符串
|
|
888
|
+
*/
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
this.getWebGLFingerprint = function () {
|
|
892
|
+
try {
|
|
893
|
+
var canvas = document.createElement('canvas');
|
|
894
|
+
var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
|
895
|
+
if (!gl) return 'not-supported';
|
|
896
|
+
var debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
|
|
897
|
+
var vendor = debugInfo ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : 'unknown';
|
|
898
|
+
var renderer = debugInfo ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : 'unknown';
|
|
899
|
+
return vendor + "|" + renderer;
|
|
900
|
+
} catch (e) {
|
|
901
|
+
return 'error';
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
/**
|
|
905
|
+
* 获取Canvas指纹
|
|
906
|
+
* @returns Canvas指纹字符串
|
|
907
|
+
*/
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
this.getCanvasFingerprint = function () {
|
|
911
|
+
try {
|
|
912
|
+
var canvas = document.createElement('canvas');
|
|
913
|
+
var ctx = canvas.getContext('2d');
|
|
914
|
+
if (!ctx) return 'not-supported'; // 绘制特定图形
|
|
915
|
+
|
|
916
|
+
ctx.textBaseline = 'top';
|
|
917
|
+
ctx.font = '14px Arial';
|
|
918
|
+
ctx.fillStyle = '#f60';
|
|
919
|
+
ctx.fillRect(125, 1, 62, 20);
|
|
920
|
+
ctx.fillStyle = '#069';
|
|
921
|
+
ctx.fillText('Canvas fingerprint', 2, 15);
|
|
922
|
+
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
|
|
923
|
+
ctx.fillText('Canvas fingerprint', 4, 17);
|
|
924
|
+
return canvas.toDataURL().slice(-50); // 取后50个字符
|
|
925
|
+
} catch (e) {
|
|
926
|
+
return 'error';
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
/**
|
|
930
|
+
* 获取音频上下文指纹
|
|
931
|
+
* @returns 音频指纹字符串
|
|
932
|
+
*/
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
this.getAudioFingerprint = function () {
|
|
936
|
+
try {
|
|
937
|
+
var AudioContextClass = window.AudioContext || window.webkitAudioContext;
|
|
938
|
+
if (!AudioContextClass) return 'not-supported';
|
|
939
|
+
var context = new AudioContextClass();
|
|
940
|
+
var oscillator = context.createOscillator();
|
|
941
|
+
var analyser = context.createAnalyser();
|
|
942
|
+
var gain = context.createGain();
|
|
943
|
+
var scriptProcessor = context.createScriptProcessor(4096, 1, 1);
|
|
944
|
+
oscillator.type = 'triangle';
|
|
945
|
+
oscillator.frequency.value = 10000;
|
|
946
|
+
gain.gain.value = 0;
|
|
947
|
+
oscillator.connect(analyser);
|
|
948
|
+
analyser.connect(scriptProcessor);
|
|
949
|
+
scriptProcessor.connect(gain);
|
|
950
|
+
gain.connect(context.destination);
|
|
951
|
+
oscillator.start(0);
|
|
952
|
+
var fingerprint = context.sampleRate + '|' + context.currentTime;
|
|
953
|
+
oscillator.stop();
|
|
954
|
+
context.close();
|
|
955
|
+
return fingerprint;
|
|
956
|
+
} catch (e) {
|
|
957
|
+
return 'error';
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
/**
|
|
961
|
+
* 获取字体指纹
|
|
962
|
+
* @returns 字体指纹字符串
|
|
963
|
+
*/
|
|
964
|
+
|
|
965
|
+
|
|
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;
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
if (detected) {
|
|
996
|
+
detectedFonts_1.push(font);
|
|
997
|
+
}
|
|
998
|
+
});
|
|
999
|
+
return detectedFonts_1.join(',');
|
|
1000
|
+
} catch (e) {
|
|
1001
|
+
return 'error';
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
/**
|
|
1005
|
+
* 获取插件指纹
|
|
1006
|
+
* @returns 插件指纹字符串
|
|
1007
|
+
*/
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
this.getPluginsFingerprint = function () {
|
|
1011
|
+
try {
|
|
1012
|
+
var plugins = [];
|
|
1013
|
+
|
|
1014
|
+
if (navigator.plugins) {
|
|
1015
|
+
for (var i = 0; i < navigator.plugins.length; i++) {
|
|
1016
|
+
var plugin = navigator.plugins[i];
|
|
1017
|
+
|
|
1018
|
+
if (plugin) {
|
|
1019
|
+
plugins.push(plugin.name + "|" + plugin.description + "|" + plugin.filename);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
return plugins.join(';');
|
|
1025
|
+
} catch (e) {
|
|
1026
|
+
return 'error';
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
/**
|
|
1030
|
+
* 检测localStorage支持
|
|
1031
|
+
* @returns 是否支持localStorage
|
|
1032
|
+
*/
|
|
1033
|
+
|
|
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
|
+
*/
|
|
1049
|
+
|
|
1050
|
+
|
|
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
|
+
};
|
|
1061
|
+
/**
|
|
1062
|
+
* 检测IndexedDB支持
|
|
1063
|
+
* @returns 是否支持IndexedDB
|
|
1064
|
+
*/
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
this.hasIndexedDB = function () {
|
|
1068
|
+
return 'indexedDB' in window && indexedDB !== null;
|
|
1069
|
+
};
|
|
1070
|
+
/**
|
|
1071
|
+
* 获取网络连接指纹
|
|
1072
|
+
* @returns 网络连接指纹字符串
|
|
1073
|
+
*/
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
this.getConnectionFingerprint = function () {
|
|
1077
|
+
try {
|
|
1078
|
+
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
|
1079
|
+
if (!connection) return 'not-supported';
|
|
1080
|
+
return connection.effectiveType + "|" + connection.downlink + "|" + connection.rtt;
|
|
1081
|
+
} catch (e) {
|
|
1082
|
+
return 'error';
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
/**
|
|
1086
|
+
* 将指纹信息哈希为唯一ID
|
|
1087
|
+
* @param fingerprint 指纹信息
|
|
1088
|
+
* @returns 返回哈希后的设备ID
|
|
1089
|
+
*/
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
this.hashFingerprint = function (fingerprint) {
|
|
1093
|
+
// 将指纹对象转换为字符串
|
|
1094
|
+
var fingerprintString = JSON.stringify(fingerprint, Object.keys(fingerprint).sort()); // 使用更强大的哈希算法生成ID
|
|
1095
|
+
|
|
1096
|
+
var hash1 = 5381; // DJB2哈希算法的初始值
|
|
1097
|
+
|
|
1098
|
+
var hash2 = 52711; // 第二个哈希的初始值
|
|
1099
|
+
|
|
1100
|
+
for (var i = 0; i < fingerprintString.length; i++) {
|
|
1101
|
+
var char = fingerprintString.charCodeAt(i);
|
|
1102
|
+
hash1 = (hash1 << 5) + hash1 + char; // hash1 * 33 + char
|
|
1103
|
+
|
|
1104
|
+
hash2 = (hash2 << 5) + hash2 + char; // hash2 * 33 + char
|
|
1105
|
+
} // 组合两个哈希值并转换为64位整数
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
var combinedHash = (hash1 >>> 0) * 4096 + (hash2 >>> 0); // 转换为36进制并添加前缀,增加长度和唯一性
|
|
1109
|
+
|
|
1110
|
+
var deviceId = 'fp_' + combinedHash.toString(36);
|
|
1111
|
+
return deviceId;
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* 打印log
|
|
1116
|
+
*/
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
WebTrackingTools.prototype.printLog = function () {
|
|
1120
|
+
var rest = [];
|
|
1121
|
+
|
|
1122
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1123
|
+
rest[_i] = arguments[_i];
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
if (this.isObject(rest[0])) {
|
|
1127
|
+
rest[0] = this.formatJsonString(rest[0]);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
if ((typeof console === "undefined" ? "undefined" : _typeof(console)) === "object" && console.log) {
|
|
1131
|
+
try {
|
|
1132
|
+
return console.log.apply(console, rest);
|
|
1133
|
+
} catch (e) {
|
|
1134
|
+
console.log(rest[0]);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
1138
|
+
/**
|
|
1139
|
+
* @description 检验是否是对象
|
|
1140
|
+
*/
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
WebTrackingTools.prototype.isObject = function (obj) {
|
|
1144
|
+
if (obj == null) {
|
|
1145
|
+
return false;
|
|
1146
|
+
} else {
|
|
1147
|
+
return toString.call(obj) == "[object Object]";
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1151
|
+
WebTrackingTools.prototype.isUndefined = function (obj) {
|
|
1152
|
+
return obj === void 0;
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
WebTrackingTools.prototype.isString = function (obj) {
|
|
1156
|
+
return toString.call(obj) == "[object String]";
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
WebTrackingTools.prototype.isDate = function (obj) {
|
|
1160
|
+
return toString.call(obj) == "[object Date]";
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
WebTrackingTools.prototype.isBoolean = function (obj) {
|
|
1164
|
+
return toString.call(obj) == "[object Boolean]";
|
|
1165
|
+
};
|
|
1166
|
+
|
|
1167
|
+
WebTrackingTools.prototype.isNumber = function (obj) {
|
|
1168
|
+
return toString.call(obj) == "[object Number]" && /[\d\.]+/.test(String(obj));
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
WebTrackingTools.prototype.isElement = function (obj) {
|
|
1172
|
+
return !!(obj && obj.nodeType === 1);
|
|
1173
|
+
};
|
|
1174
|
+
|
|
1175
|
+
WebTrackingTools.prototype.isFunction = function (f) {
|
|
1176
|
+
if (!f) {
|
|
1177
|
+
return false;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
var type = toString.call(f);
|
|
1181
|
+
return type == "[object Function]" || type == "[object AsyncFunction]";
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
WebTrackingTools.prototype.isJSONString = function (str) {
|
|
1185
|
+
if (!this.isString(str)) return false;
|
|
1186
|
+
|
|
1187
|
+
try {
|
|
1188
|
+
JSON.parse(str);
|
|
1189
|
+
} catch (e) {
|
|
1190
|
+
return false;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
return true;
|
|
1194
|
+
};
|
|
1195
|
+
|
|
1196
|
+
WebTrackingTools.prototype._decodeURIComponent = function (val) {
|
|
1197
|
+
var result = val;
|
|
1198
|
+
|
|
1199
|
+
try {
|
|
1200
|
+
result = decodeURIComponent(val);
|
|
1201
|
+
} catch (e) {
|
|
1202
|
+
result = val;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
return result;
|
|
1206
|
+
};
|
|
1207
|
+
|
|
1208
|
+
WebTrackingTools.prototype.getMainHost = function () {
|
|
1209
|
+
var key = "mh_" + Math.random();
|
|
1210
|
+
var keyR = new RegExp("(^|;)\\s*" + key + "=12345");
|
|
1211
|
+
var expiredTime = new Date(0);
|
|
1212
|
+
var domain = document.domain;
|
|
1213
|
+
var domainList = domain.split(".");
|
|
1214
|
+
var urlItems = []; // 主域名一定会有两部分组成
|
|
1215
|
+
|
|
1216
|
+
urlItems.unshift(domainList.pop()); // 慢慢从后往前测试
|
|
1217
|
+
|
|
1218
|
+
while (domainList.length) {
|
|
1219
|
+
urlItems.unshift(domainList.pop());
|
|
1220
|
+
var mainHost = urlItems.join(".");
|
|
1221
|
+
var cookie = key + "=" + 12345 + ";domain=." + mainHost;
|
|
1222
|
+
document.cookie = cookie; //如果cookie存在,则说明域名合法
|
|
1223
|
+
|
|
1224
|
+
if (keyR.test(document.cookie)) {
|
|
1225
|
+
document.cookie = cookie + ";expires=" + expiredTime;
|
|
1226
|
+
return mainHost;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
return domain;
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1233
|
+
return WebTrackingTools;
|
|
1234
|
+
}();
|
|
1235
|
+
|
|
1236
|
+
var WebTracking =
|
|
1237
|
+
/** @class */
|
|
1238
|
+
function (_super) {
|
|
1239
|
+
__extends(WebTracking, _super);
|
|
1240
|
+
|
|
1241
|
+
function WebTracking() {
|
|
1242
|
+
var _this = _super.call(this) || this; // 批量发送队列
|
|
1243
|
+
|
|
1244
|
+
|
|
1245
|
+
_this.batchQueue = []; // 批量发送定时器
|
|
1246
|
+
|
|
1247
|
+
_this.batchTimer = null; // LocalStorage 存储 key
|
|
1248
|
+
|
|
1249
|
+
_this.BATCH_QUEUE_STORAGE_KEY = 'web_tracking_batch_queue'; // 用户信息
|
|
1250
|
+
|
|
1251
|
+
_this.userInfo = null; // 当前路由
|
|
1252
|
+
|
|
1253
|
+
_this.currentUrl = ""; // 页面唯一标识,取当前路由 window.location.pathname.replace(/\//g, '_').substr(1)
|
|
1254
|
+
|
|
1255
|
+
_this.pageKey = ""; // 设备唯一标识
|
|
1256
|
+
|
|
1257
|
+
_this.deviceId = ""; // 上传事件描述
|
|
1258
|
+
|
|
1259
|
+
_this.eventDescMap = {
|
|
1260
|
+
PageView: "Web 浏览页面",
|
|
1261
|
+
WebClick: "Web 元素点击",
|
|
1262
|
+
PageRetained: "Web 页面浏览时长",
|
|
1263
|
+
CustomTrack: "Web 自定义代码上报"
|
|
1264
|
+
};
|
|
1265
|
+
/**
|
|
1266
|
+
* @description 初始化函数
|
|
1267
|
+
* @param {object} InitParams [初始化参数]
|
|
1268
|
+
*/
|
|
1269
|
+
|
|
1270
|
+
_this.init = function (initParams) {
|
|
1271
|
+
_this.preset(initParams);
|
|
1272
|
+
|
|
1273
|
+
var pathname = window.location.pathname;
|
|
1274
|
+
_this.currentUrl = window.location.href;
|
|
1275
|
+
_this.pageKey = pathname.replace(/\//g, "_").substring(1);
|
|
1276
|
+
_this.systemsInfo = _this.getSystemsInfo(initParams.platform); // 获取设备ID
|
|
1277
|
+
|
|
1278
|
+
_this.deviceId = _this.getDeviceId();
|
|
1279
|
+
|
|
1280
|
+
_this.setCookie("retainedStartTime", _this.getTimeStamp()); // 如果启用了批量发送,从 LocalStorage 恢复队列
|
|
1281
|
+
|
|
1282
|
+
|
|
1283
|
+
if (_this.initConfig.batchSend) {
|
|
1284
|
+
_this.restoreBatchQueueFromStorage(); // 监听页面卸载事件,保存队列
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
_this.setupBeforeUnloadListener();
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
/**
|
|
1291
|
+
* TODO: 需要判断有哪些不能被预制的参数
|
|
1292
|
+
* @description 预置参数
|
|
1293
|
+
* @param {object} PresetParams [预置参数]
|
|
1294
|
+
*/
|
|
1295
|
+
|
|
1296
|
+
|
|
1297
|
+
_this.preset = function (presetParams) {
|
|
1298
|
+
if (presetParams instanceof Object) {
|
|
1299
|
+
_this.each(presetParams, function (val, key) {
|
|
1300
|
+
if (_this.initConfig.hasOwnProperty(key)) {
|
|
1301
|
+
// TODO:后面加一些校验
|
|
1302
|
+
_this.initConfig[key] = val;
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
if (!/^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-\(\)]*[\w@?^=%&/~+#-\(\)])?$/.test(_this.initConfig["serverUrl"])) {
|
|
1308
|
+
_this.printLog("当前 server_url 为空或不正确,只在控制台打印日志,network 中不会发数据,请配置正确的 server_url!");
|
|
1309
|
+
|
|
1310
|
+
_this.initConfig["showLog"] = true;
|
|
1311
|
+
} // 如果启用了全埋点
|
|
1312
|
+
|
|
1313
|
+
|
|
1314
|
+
if (!!_this.initConfig["autoTrack"]) {
|
|
1315
|
+
// 启用监听
|
|
1316
|
+
_this.listener();
|
|
1317
|
+
} else {
|
|
1318
|
+
// 取消监听
|
|
1319
|
+
_this.unlistener();
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
/**
|
|
1323
|
+
* 用户登录
|
|
1324
|
+
*/
|
|
1325
|
+
|
|
1326
|
+
|
|
1327
|
+
_this.login = function (userInfo) {
|
|
1328
|
+
if (_this.isObject(userInfo)) _this.userInfo = userInfo;
|
|
1329
|
+
};
|
|
1330
|
+
/**
|
|
1331
|
+
* 获取设备唯一标识
|
|
1332
|
+
* @returns 设备唯一标识
|
|
1333
|
+
*/
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
_this.getDeviceId = function () {
|
|
1337
|
+
// 如果已有设备ID,直接返回
|
|
1338
|
+
if (_this.deviceId) {
|
|
1339
|
+
return _this.deviceId;
|
|
1340
|
+
} // 获取已存储的设备ID
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
var storedDeviceId = _this.getCookie('device_id') || _this.getLocalStorage('device_id');
|
|
1344
|
+
|
|
1345
|
+
if (storedDeviceId) {
|
|
1346
|
+
_this.deviceId = storedDeviceId;
|
|
1347
|
+
return _this.deviceId;
|
|
1348
|
+
} // 收集浏览器指纹信息
|
|
1349
|
+
|
|
1350
|
+
|
|
1351
|
+
var fingerprint = _this.collectFingerprint(); // 生成设备ID
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
var deviceId = _this.hashFingerprint(fingerprint); // 存储设备ID(统一使用2年过期时间,与tools.ts保持一致)
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
_this.setCookie('device_id', deviceId, 365 * 2); // 存储2年
|
|
1358
|
+
|
|
1359
|
+
|
|
1360
|
+
_this.setLocalStorage('device_id', deviceId);
|
|
1361
|
+
|
|
1362
|
+
_this.deviceId = deviceId;
|
|
1363
|
+
return _this.deviceId;
|
|
1364
|
+
};
|
|
1365
|
+
/**
|
|
1366
|
+
* 重置设备ID
|
|
1367
|
+
* 清除存储的设备ID并重新生成
|
|
1368
|
+
* @returns 新的设备ID
|
|
1369
|
+
*/
|
|
1370
|
+
|
|
1371
|
+
|
|
1372
|
+
_this.resetDeviceId = function () {
|
|
1373
|
+
// 清除cookie和localStorage中的设备ID
|
|
1374
|
+
document.cookie = 'device_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
|
|
1375
|
+
localStorage.removeItem('device_id'); // 清除内存中的设备ID
|
|
1376
|
+
|
|
1377
|
+
_this.deviceId = ''; // 重新生成设备ID
|
|
1378
|
+
|
|
1379
|
+
var newDeviceId = _this.getDeviceId();
|
|
1380
|
+
|
|
1381
|
+
return newDeviceId;
|
|
1382
|
+
};
|
|
1383
|
+
/**
|
|
1384
|
+
* 自定义代码埋点上报
|
|
1385
|
+
* @param {object} TrackParams [自定义上报参数]
|
|
1386
|
+
* @return {Promise<TrackingResponse>} [回调]
|
|
1387
|
+
*/
|
|
1388
|
+
|
|
1389
|
+
|
|
1390
|
+
_this.track = function (_a) {
|
|
1391
|
+
var desc = _a.desc,
|
|
1392
|
+
partkey = _a.partkey,
|
|
1393
|
+
business = _a.business,
|
|
1394
|
+
header = _a.header;
|
|
1395
|
+
|
|
1396
|
+
var params = _this.getParams({
|
|
1397
|
+
desc: desc,
|
|
1398
|
+
event: "CustomTrack",
|
|
1399
|
+
itemKey: _this.getItemKey(partkey),
|
|
1400
|
+
privateParamMap: {
|
|
1401
|
+
business: business
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1405
|
+
return _this.sendData(params, header);
|
|
1406
|
+
};
|
|
1407
|
+
/**
|
|
1408
|
+
* @description 监听全埋点事件
|
|
1409
|
+
*/
|
|
1410
|
+
|
|
1411
|
+
|
|
1412
|
+
_this.listener = function () {
|
|
1413
|
+
if (!!_this.initConfig.isTrackSinglePage) {
|
|
1414
|
+
_this.rewriteHistory();
|
|
1415
|
+
|
|
1416
|
+
_this.addSinglePageEvent(_this.onPageViewCallback);
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
_this.each(["load", "beforeunload"], function (historyType) {
|
|
1420
|
+
_this.addEventListener(window, historyType, _this.onPageViewCallback);
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1423
|
+
_this.addEventListener(window, "click", _this.onClickCallback);
|
|
1424
|
+
};
|
|
1425
|
+
/**
|
|
1426
|
+
* @description 取消全埋点事件
|
|
1427
|
+
*/
|
|
1428
|
+
|
|
1429
|
+
|
|
1430
|
+
_this.unlistener = function () {
|
|
1431
|
+
if (!!_this.initConfig.isTrackSinglePage) {
|
|
1432
|
+
var historyPushState = window.history.pushState;
|
|
1433
|
+
var singlePageEvent = !!historyPushState ? "popstate" : "hashchange";
|
|
1434
|
+
|
|
1435
|
+
_this.each(["pushState", "replaceState", singlePageEvent], function (historyName) {
|
|
1436
|
+
_this.removeEventListener(window, historyName, _this.onPageViewCallback);
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
_this.each(["load", "beforeunload"], function (historyType) {
|
|
1441
|
+
_this.removeEventListener(window, historyType, _this.onPageViewCallback);
|
|
1442
|
+
});
|
|
1443
|
+
|
|
1444
|
+
_this.removeEventListener(window, "click", _this.onClickCallback); // 清理批量发送定时器
|
|
1445
|
+
|
|
1446
|
+
|
|
1447
|
+
_this.clearBatchTimer();
|
|
1448
|
+
};
|
|
1449
|
+
/**
|
|
1450
|
+
* @description 清理批量发送定时器
|
|
1451
|
+
*/
|
|
1452
|
+
|
|
1453
|
+
|
|
1454
|
+
_this.clearBatchTimer = function () {
|
|
1455
|
+
if (_this.batchTimer !== null) {
|
|
1456
|
+
clearTimeout(_this.batchTimer);
|
|
1457
|
+
_this.batchTimer = null;
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
/**
|
|
1461
|
+
* @description 清空批量队列(包括 LocalStorage 中的数据)
|
|
1462
|
+
*/
|
|
1463
|
+
|
|
1464
|
+
|
|
1465
|
+
_this.clearBatchQueue = function () {
|
|
1466
|
+
_this.batchQueue = [];
|
|
1467
|
+
|
|
1468
|
+
_this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, '[]');
|
|
1469
|
+
|
|
1470
|
+
if (_this.initConfig.showLog) {
|
|
1471
|
+
_this.printLog('批量队列已清空');
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
|
|
1475
|
+
_this.onClickCallback = function (e) {
|
|
1476
|
+
var _a;
|
|
1477
|
+
|
|
1478
|
+
var target = e.target;
|
|
1479
|
+
if (!((_a = target === null || target === void 0 ? void 0 : target.dataset) === null || _a === void 0 ? void 0 : _a.partKey)) return;
|
|
1480
|
+
var position = [e.pageX, e.pageY];
|
|
1481
|
+
var id = target.id;
|
|
1482
|
+
var className = target.className;
|
|
1483
|
+
var nodeName = target.nodeName;
|
|
1484
|
+
var targetEle = {
|
|
1485
|
+
id: id,
|
|
1486
|
+
nodeName: nodeName,
|
|
1487
|
+
className: className,
|
|
1488
|
+
position: position
|
|
1489
|
+
};
|
|
1490
|
+
|
|
1491
|
+
var params = _this.getParams({
|
|
1492
|
+
event: "WebClick",
|
|
1493
|
+
desc: _this.eventDescMap["WebClick"],
|
|
1494
|
+
itemKey: _this.getItemKey(target.dataset.partKey),
|
|
1495
|
+
privateParamMap: {
|
|
1496
|
+
targetEle: targetEle,
|
|
1497
|
+
pointerType: e.pointerType,
|
|
1498
|
+
currentUrl: _this.currentUrl,
|
|
1499
|
+
elementSelector: _this.getDomSelector(target) || ''
|
|
1500
|
+
}
|
|
1501
|
+
});
|
|
1502
|
+
|
|
1503
|
+
return _this.sendData(params);
|
|
1504
|
+
};
|
|
1505
|
+
/**
|
|
1506
|
+
* @description 路由触发事件
|
|
1507
|
+
*/
|
|
1508
|
+
|
|
1509
|
+
|
|
1510
|
+
_this.onPageViewCallback = function (e) {
|
|
1511
|
+
var _a, _b;
|
|
1512
|
+
|
|
1513
|
+
var ORGIN = window.location.origin;
|
|
1514
|
+
|
|
1515
|
+
var params = _this.getParams({
|
|
1516
|
+
event: "PageView",
|
|
1517
|
+
desc: _this.eventDescMap["PageView"],
|
|
1518
|
+
privateParamMap: {
|
|
1519
|
+
currentUrl: _this.currentUrl,
|
|
1520
|
+
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
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
|
|
1524
|
+
_this.currentUrl = window.location.href;
|
|
1525
|
+
_this.pageKey = window.location.pathname.replace(/\//g, "_").substring(1);
|
|
1526
|
+
|
|
1527
|
+
_this.sendRetained(e.type);
|
|
1528
|
+
|
|
1529
|
+
_this.sendData(params);
|
|
1530
|
+
};
|
|
1531
|
+
|
|
1532
|
+
_this.getParams = function (_a) {
|
|
1533
|
+
var event = _a.event,
|
|
1534
|
+
desc = _a.desc,
|
|
1535
|
+
_b = _a.privateParamMap,
|
|
1536
|
+
privateParamMap = _b === void 0 ? {} : _b,
|
|
1537
|
+
itemKey = _a.itemKey;
|
|
1538
|
+
var business = _this.initConfig.business;
|
|
1539
|
+
var pageWidth = window.innerWidth;
|
|
1540
|
+
var pageHeight = window.innerHeight;
|
|
1541
|
+
var screenWidth = window.screen.width;
|
|
1542
|
+
var screenHeight = window.screen.height; // 过滤敏感数据
|
|
1543
|
+
|
|
1544
|
+
var filteredBusiness = _this.filterSensitiveData(business || {});
|
|
1545
|
+
|
|
1546
|
+
var filteredUserInfo = _this.filterSensitiveData(_this.userInfo || {});
|
|
1547
|
+
|
|
1548
|
+
var filteredPrivateParamMap = _this.filterSensitiveData(privateParamMap || {});
|
|
1549
|
+
|
|
1550
|
+
var filteredUrlParams = _this.filterSensitiveData(_this.getQueryValue() || {}); // 创建私有参数对象
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
var privateParamMapData = {
|
|
1554
|
+
currentUrl: filteredPrivateParamMap.currentUrl || _this.currentUrl,
|
|
1555
|
+
business: Object.assign({}, filteredBusiness, filteredPrivateParamMap.business || {}),
|
|
1556
|
+
pageWidth: pageWidth,
|
|
1557
|
+
pageHeight: pageHeight,
|
|
1558
|
+
screenWidth: screenWidth,
|
|
1559
|
+
screenHeight: screenHeight,
|
|
1560
|
+
sdkVersion: _this.sdkVersion,
|
|
1561
|
+
systemsInfo: _this.systemsInfo,
|
|
1562
|
+
urlParams: filteredUrlParams,
|
|
1563
|
+
userInfo: filteredUserInfo,
|
|
1564
|
+
deviceId: _this.deviceId // 添加设备ID
|
|
1565
|
+
|
|
1566
|
+
}; // 添加其他可能的属性
|
|
1567
|
+
|
|
1568
|
+
if (filteredPrivateParamMap.targetEle) {
|
|
1569
|
+
privateParamMapData.targetEle = filteredPrivateParamMap.targetEle;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
if (filteredPrivateParamMap.targetUrl) {
|
|
1573
|
+
privateParamMapData.targetUrl = filteredPrivateParamMap.targetUrl;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
if (filteredPrivateParamMap.pointerType) {
|
|
1577
|
+
privateParamMapData.pointerType = filteredPrivateParamMap.pointerType;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
if (filteredPrivateParamMap.elementSelector) {
|
|
1581
|
+
privateParamMapData.elementSelector = filteredPrivateParamMap.elementSelector;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
if (filteredPrivateParamMap.retainedDuration) {
|
|
1585
|
+
privateParamMapData.retainedDuration = filteredPrivateParamMap.retainedDuration;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
return {
|
|
1589
|
+
event: event,
|
|
1590
|
+
desc: desc,
|
|
1591
|
+
itemKey: itemKey || _this.getItemKey(),
|
|
1592
|
+
requestTime: _this.getTimeStamp(),
|
|
1593
|
+
privateParamMap: privateParamMapData
|
|
1594
|
+
};
|
|
1595
|
+
};
|
|
1596
|
+
/**
|
|
1597
|
+
* 数据采样判断
|
|
1598
|
+
* @returns 是否应该采样
|
|
1599
|
+
*/
|
|
1600
|
+
|
|
1601
|
+
|
|
1602
|
+
_this.shouldSample = function () {
|
|
1603
|
+
var sampleRate = _this.initConfig.sampleRate;
|
|
1604
|
+
if (sampleRate >= 1) return true;
|
|
1605
|
+
if (sampleRate <= 0) return false;
|
|
1606
|
+
return Math.random() < sampleRate;
|
|
1607
|
+
};
|
|
1608
|
+
/**
|
|
1609
|
+
* 批量发送数据
|
|
1610
|
+
*/
|
|
1611
|
+
|
|
1612
|
+
|
|
1613
|
+
_this.flushBatchQueue = function () {
|
|
1614
|
+
if (_this.batchQueue.length === 0) return;
|
|
1615
|
+
|
|
1616
|
+
var batchData = __spreadArray([], _this.batchQueue);
|
|
1617
|
+
|
|
1618
|
+
_this.batchQueue = []; // 从 LocalStorage 中移除已发送的数据
|
|
1619
|
+
|
|
1620
|
+
_this.saveBatchQueueToStorage(); // 发送批量数据
|
|
1621
|
+
|
|
1622
|
+
|
|
1623
|
+
_this.sendBatchData(batchData);
|
|
1624
|
+
};
|
|
1625
|
+
/**
|
|
1626
|
+
* 发送批量数据
|
|
1627
|
+
* @param data 批量数据
|
|
1628
|
+
*/
|
|
1629
|
+
|
|
1630
|
+
|
|
1631
|
+
_this.sendBatchData = function (data) {
|
|
1632
|
+
var _a = _this.initConfig,
|
|
1633
|
+
serverUrl = _a.serverUrl,
|
|
1634
|
+
contentType = _a.contentType,
|
|
1635
|
+
showLog = _a.showLog;
|
|
1636
|
+
|
|
1637
|
+
if (showLog) {
|
|
1638
|
+
_this.printLog("\u6279\u91CF\u53D1\u9001 " + data.length + " \u6761\u6570\u636E");
|
|
1639
|
+
|
|
1640
|
+
data.forEach(function (item) {
|
|
1641
|
+
return _this.printLog(item);
|
|
1642
|
+
});
|
|
1643
|
+
} // 这里可以根据实际需求决定是逐条发送还是打包发送
|
|
1644
|
+
// 这里选择打包发送
|
|
1645
|
+
|
|
1646
|
+
|
|
1647
|
+
_this.ajax({
|
|
1648
|
+
url: serverUrl,
|
|
1649
|
+
type: "POST",
|
|
1650
|
+
data: JSON.stringify({
|
|
1651
|
+
events: data
|
|
1652
|
+
}),
|
|
1653
|
+
contentType: contentType,
|
|
1654
|
+
credentials: false,
|
|
1655
|
+
timeout: _this.initConfig.sendTimeout,
|
|
1656
|
+
cors: true,
|
|
1657
|
+
success: function success() {
|
|
1658
|
+
// 批量发送成功,数据已从队列中移除,无需额外操作
|
|
1659
|
+
if (_this.initConfig.showLog) {
|
|
1660
|
+
_this.printLog("\u6279\u91CF\u53D1\u9001\u6210\u529F: " + data.length + " \u6761\u6570\u636E");
|
|
1661
|
+
}
|
|
1662
|
+
},
|
|
1663
|
+
error: function error(err) {
|
|
1664
|
+
var _a; // 批量发送失败,重新加入队列以便重试
|
|
1665
|
+
|
|
1666
|
+
|
|
1667
|
+
_this.printLog("\u6279\u91CF\u53D1\u9001\u5931\u8D25: " + err + "\uFF0C\u6570\u636E\u5DF2\u91CD\u65B0\u52A0\u5165\u961F\u5217"); // 将失败的数据重新加入队列,避免数据丢失
|
|
1668
|
+
|
|
1669
|
+
|
|
1670
|
+
(_a = _this.batchQueue).unshift.apply(_a, data); // 限制重试队列大小,避免内存溢出
|
|
1671
|
+
|
|
1672
|
+
|
|
1673
|
+
if (_this.batchQueue.length > _this.initConfig.batchMaxSize * 2) {
|
|
1674
|
+
_this.batchQueue = _this.batchQueue.slice(0, _this.initConfig.batchMaxSize);
|
|
1675
|
+
} // 保存失败的数据到 LocalStorage
|
|
1676
|
+
|
|
1677
|
+
|
|
1678
|
+
_this.saveBatchQueueToStorage();
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
};
|
|
1682
|
+
/**
|
|
1683
|
+
* 添加到批量队列
|
|
1684
|
+
* @param params 数据参数
|
|
1685
|
+
*/
|
|
1686
|
+
|
|
1687
|
+
|
|
1688
|
+
_this.addToBatchQueue = function (params) {
|
|
1689
|
+
var _a = _this.initConfig,
|
|
1690
|
+
batchInterval = _a.batchInterval,
|
|
1691
|
+
batchMaxSize = _a.batchMaxSize;
|
|
1692
|
+
|
|
1693
|
+
_this.batchQueue.push(params); // 保存到 LocalStorage
|
|
1694
|
+
|
|
1695
|
+
|
|
1696
|
+
_this.saveBatchQueueToStorage(); // 如果队列达到最大数量,立即发送
|
|
1697
|
+
|
|
1698
|
+
|
|
1699
|
+
if (_this.batchQueue.length >= batchMaxSize) {
|
|
1700
|
+
_this.flushBatchQueue();
|
|
1701
|
+
|
|
1702
|
+
return;
|
|
1703
|
+
} // 设置定时发送
|
|
1704
|
+
|
|
1705
|
+
|
|
1706
|
+
if (!_this.batchTimer) {
|
|
1707
|
+
_this.batchTimer = window.setTimeout(function () {
|
|
1708
|
+
_this.flushBatchQueue();
|
|
1709
|
+
|
|
1710
|
+
_this.batchTimer = null;
|
|
1711
|
+
}, batchInterval);
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
/**
|
|
1715
|
+
* 从 LocalStorage 恢复批量队列
|
|
1716
|
+
*/
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
_this.restoreBatchQueueFromStorage = function () {
|
|
1720
|
+
try {
|
|
1721
|
+
var storedQueue = _this.getLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY);
|
|
1722
|
+
|
|
1723
|
+
if (storedQueue) {
|
|
1724
|
+
var parsedQueue = JSON.parse(storedQueue);
|
|
1725
|
+
|
|
1726
|
+
if (Array.isArray(parsedQueue) && parsedQueue.length > 0) {
|
|
1727
|
+
_this.batchQueue = parsedQueue;
|
|
1728
|
+
|
|
1729
|
+
if (_this.initConfig.showLog) {
|
|
1730
|
+
_this.printLog("\u4ECE LocalStorage \u6062\u590D " + parsedQueue.length + " \u6761\u5F85\u53D1\u9001\u6570\u636E");
|
|
1731
|
+
} // 恢复后立即尝试发送(如果达到条件)
|
|
1732
|
+
|
|
1733
|
+
|
|
1734
|
+
var batchMaxSize = _this.initConfig.batchMaxSize;
|
|
1735
|
+
|
|
1736
|
+
if (_this.batchQueue.length >= batchMaxSize) {
|
|
1737
|
+
_this.flushBatchQueue();
|
|
1738
|
+
} else {
|
|
1739
|
+
// 设置定时发送
|
|
1740
|
+
var batchInterval = _this.initConfig.batchInterval;
|
|
1741
|
+
|
|
1742
|
+
if (!_this.batchTimer) {
|
|
1743
|
+
_this.batchTimer = window.setTimeout(function () {
|
|
1744
|
+
_this.flushBatchQueue();
|
|
1745
|
+
|
|
1746
|
+
_this.batchTimer = null;
|
|
1747
|
+
}, batchInterval);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
} catch (e) {
|
|
1753
|
+
_this.printLog("\u6062\u590D\u6279\u91CF\u961F\u5217\u5931\u8D25: " + e); // 如果解析失败,清除损坏的数据
|
|
1754
|
+
|
|
1755
|
+
|
|
1756
|
+
_this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, '[]');
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
/**
|
|
1760
|
+
* 保存批量队列到 LocalStorage
|
|
1761
|
+
*/
|
|
1762
|
+
|
|
1763
|
+
|
|
1764
|
+
_this.saveBatchQueueToStorage = function () {
|
|
1765
|
+
try {
|
|
1766
|
+
var queueString = JSON.stringify(_this.batchQueue); // 检查存储大小,避免超出 LocalStorage 限制(通常 5-10MB)
|
|
1767
|
+
// 如果队列过大,只保留最新的数据
|
|
1768
|
+
|
|
1769
|
+
if (queueString.length > 4 * 1024 * 1024) {
|
|
1770
|
+
// 4MB 限制
|
|
1771
|
+
var maxItems = Math.floor(_this.batchQueue.length * 0.8); // 保留 80%
|
|
1772
|
+
|
|
1773
|
+
_this.batchQueue = _this.batchQueue.slice(-maxItems);
|
|
1774
|
+
|
|
1775
|
+
_this.printLog("\u961F\u5217\u8FC7\u5927\uFF0C\u5DF2\u622A\u65AD\u4FDD\u7559\u6700\u65B0 " + maxItems + " \u6761\u6570\u636E");
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
_this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, JSON.stringify(_this.batchQueue));
|
|
1779
|
+
} catch (e) {
|
|
1780
|
+
// LocalStorage 可能已满或不可用
|
|
1781
|
+
_this.printLog("\u4FDD\u5B58\u6279\u91CF\u961F\u5217\u5230 LocalStorage \u5931\u8D25: " + e);
|
|
1782
|
+
}
|
|
1783
|
+
};
|
|
1784
|
+
/**
|
|
1785
|
+
* 设置页面卸载监听器,保存队列
|
|
1786
|
+
*/
|
|
1787
|
+
|
|
1788
|
+
|
|
1789
|
+
_this.setupBeforeUnloadListener = function () {
|
|
1790
|
+
// 使用 visibilitychange 事件(更可靠)
|
|
1791
|
+
document.addEventListener('visibilitychange', function () {
|
|
1792
|
+
if (document.visibilityState === 'hidden' && _this.batchQueue.length > 0) {
|
|
1793
|
+
_this.saveBatchQueueToStorage();
|
|
1794
|
+
}
|
|
1795
|
+
}); // 使用 beforeunload 事件作为备用
|
|
1796
|
+
|
|
1797
|
+
window.addEventListener('beforeunload', function () {
|
|
1798
|
+
if (_this.batchQueue.length > 0) {
|
|
1799
|
+
// 使用 sendBeacon 尝试发送数据(如果支持)
|
|
1800
|
+
if (navigator.sendBeacon && _this.initConfig.serverUrl) {
|
|
1801
|
+
try {
|
|
1802
|
+
var data = JSON.stringify({
|
|
1803
|
+
events: _this.batchQueue
|
|
1804
|
+
});
|
|
1805
|
+
var blob = new Blob([data], {
|
|
1806
|
+
type: 'application/json'
|
|
1807
|
+
});
|
|
1808
|
+
navigator.sendBeacon(_this.initConfig.serverUrl, blob); // 如果 sendBeacon 成功,清除队列
|
|
1809
|
+
|
|
1810
|
+
_this.batchQueue = [];
|
|
1811
|
+
|
|
1812
|
+
_this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, '[]');
|
|
1813
|
+
} catch (e) {
|
|
1814
|
+
// sendBeacon 失败,保存到 LocalStorage
|
|
1815
|
+
_this.saveBatchQueueToStorage();
|
|
1816
|
+
}
|
|
1817
|
+
} else {
|
|
1818
|
+
// 不支持 sendBeacon,保存到 LocalStorage
|
|
1819
|
+
_this.saveBatchQueueToStorage();
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
});
|
|
1823
|
+
};
|
|
1824
|
+
/**
|
|
1825
|
+
* 发送数据通用函数
|
|
1826
|
+
*/
|
|
1827
|
+
|
|
1828
|
+
|
|
1829
|
+
_this.sendData = function (params, header) {
|
|
1830
|
+
// 数据采样判断
|
|
1831
|
+
if (!_this.shouldSample()) {
|
|
1832
|
+
return Promise.resolve({
|
|
1833
|
+
success: true,
|
|
1834
|
+
message: "数据已采样跳过"
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
var _a = _this.initConfig,
|
|
1839
|
+
serverUrl = _a.serverUrl,
|
|
1840
|
+
sendTimeout = _a.sendTimeout,
|
|
1841
|
+
contentType = _a.contentType,
|
|
1842
|
+
showLog = _a.showLog,
|
|
1843
|
+
initHeader = _a.header,
|
|
1844
|
+
batchSend = _a.batchSend;
|
|
1845
|
+
if (!!showLog) _this.printLog(params); // 如果启用批量发送
|
|
1846
|
+
|
|
1847
|
+
if (batchSend) {
|
|
1848
|
+
_this.addToBatchQueue(params);
|
|
1849
|
+
|
|
1850
|
+
return Promise.resolve({
|
|
1851
|
+
success: true,
|
|
1852
|
+
message: "已添加到批量队列"
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
if (_this.isSupportBeaconSend() === true && !header && !initHeader) {
|
|
1857
|
+
return _this.sendBeacon({
|
|
1858
|
+
contentType: contentType,
|
|
1859
|
+
url: serverUrl,
|
|
1860
|
+
data: params
|
|
1861
|
+
});
|
|
1862
|
+
} else {
|
|
1863
|
+
return new Promise(function (resolve, reject) {
|
|
1864
|
+
_this.ajax({
|
|
1865
|
+
header: header || initHeader,
|
|
1866
|
+
url: serverUrl,
|
|
1867
|
+
type: "POST",
|
|
1868
|
+
data: JSON.stringify(params),
|
|
1869
|
+
contentType: contentType,
|
|
1870
|
+
credentials: false,
|
|
1871
|
+
timeout: sendTimeout,
|
|
1872
|
+
cors: true,
|
|
1873
|
+
success: function success(res) {
|
|
1874
|
+
return resolve({
|
|
1875
|
+
success: true,
|
|
1876
|
+
data: res
|
|
1877
|
+
});
|
|
1878
|
+
},
|
|
1879
|
+
error: function error(err, status) {
|
|
1880
|
+
return reject({
|
|
1881
|
+
success: false,
|
|
1882
|
+
message: String(err),
|
|
1883
|
+
code: status
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
});
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
};
|
|
1890
|
+
/**
|
|
1891
|
+
* @description 留存时长上报
|
|
1892
|
+
* @param type
|
|
1893
|
+
*/
|
|
1894
|
+
|
|
1895
|
+
|
|
1896
|
+
_this.sendRetained = function (type) {
|
|
1897
|
+
var params = _this.getParams({
|
|
1898
|
+
event: "PageRetained",
|
|
1899
|
+
desc: _this.eventDescMap["PageRetained"]
|
|
1900
|
+
});
|
|
1901
|
+
|
|
1902
|
+
if (["beforeunload", "pushState", "replaceState", "hashchange", "popstate"].indexOf(type) >= 0) {
|
|
1903
|
+
var __time = _this.getCookie("retainedStartTime");
|
|
1904
|
+
|
|
1905
|
+
var retainedStartTime = __time ? +__time : _this.getTimeStamp();
|
|
1906
|
+
|
|
1907
|
+
var retainedData = __assign(__assign({}, params), {
|
|
1908
|
+
privateParamMap: __assign(__assign({}, params.privateParamMap), {
|
|
1909
|
+
retainedDuration: Math.max(params.requestTime - retainedStartTime, 0)
|
|
1910
|
+
})
|
|
1911
|
+
});
|
|
1912
|
+
|
|
1913
|
+
_this.sendData(retainedData);
|
|
1914
|
+
|
|
1915
|
+
_this.setCookie("retainedStartTime", _this.getTimeStamp());
|
|
1916
|
+
}
|
|
1917
|
+
};
|
|
1918
|
+
/**
|
|
1919
|
+
* @description 获取 itemKey
|
|
1920
|
+
* @param {[string]} partkey [控件/自定义事件的唯一标识]
|
|
1921
|
+
* @return {[string]}
|
|
1922
|
+
*/
|
|
1923
|
+
|
|
1924
|
+
|
|
1925
|
+
_this.getItemKey = function (partkey) {
|
|
1926
|
+
var appKey = _this.initConfig.appKey;
|
|
1927
|
+
var keys = [appKey, _this.pageKey, partkey ? partkey.toString() : undefined].filter(function (key) {
|
|
1928
|
+
return !!key;
|
|
1929
|
+
});
|
|
1930
|
+
return keys.reduce(function (str, key) {
|
|
1931
|
+
return str + ("" + (str.length ? "." : "")) + key;
|
|
1932
|
+
}, "");
|
|
1933
|
+
};
|
|
1934
|
+
|
|
1935
|
+
_this.sdkVersion = "1.2.0"; // sdk版本
|
|
1936
|
+
|
|
1937
|
+
_this.initConfig = {
|
|
1938
|
+
appKey: "",
|
|
1939
|
+
platform: undefined,
|
|
1940
|
+
showLog: false,
|
|
1941
|
+
serverUrl: "",
|
|
1942
|
+
autoTrack: false,
|
|
1943
|
+
sendTimeout: 3000,
|
|
1944
|
+
isTrackSinglePage: false,
|
|
1945
|
+
contentType: "application/json",
|
|
1946
|
+
business: {},
|
|
1947
|
+
header: undefined,
|
|
1948
|
+
sampleRate: 1,
|
|
1949
|
+
batchSend: false,
|
|
1950
|
+
batchInterval: 5000,
|
|
1951
|
+
batchMaxSize: 10 // 批量发送最大数量
|
|
1952
|
+
|
|
1953
|
+
}; // 系统信息
|
|
1954
|
+
|
|
1955
|
+
_this.systemsInfo = {};
|
|
1956
|
+
return _this;
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* @description 添加单页面监听事件
|
|
1960
|
+
* @param callback
|
|
1961
|
+
*/
|
|
1962
|
+
|
|
1963
|
+
|
|
1964
|
+
WebTracking.prototype.addSinglePageEvent = function (callback) {
|
|
1965
|
+
var _this = this;
|
|
1966
|
+
|
|
1967
|
+
var historyPushState = window.history.pushState;
|
|
1968
|
+
var singlePageEvent = historyPushState ? "popstate" : "hashchange";
|
|
1969
|
+
this.each(["pushState", "replaceState", singlePageEvent], function (historyType) {
|
|
1970
|
+
_this.addEventListener(window, historyType, callback);
|
|
1971
|
+
});
|
|
1972
|
+
};
|
|
1973
|
+
|
|
1974
|
+
return WebTracking;
|
|
1975
|
+
}(WebTrackingTools);
|
|
1976
|
+
|
|
1977
|
+
var index = new WebTracking();
|
|
1978
|
+
|
|
1979
|
+
export default index;
|