@identityjs/tracker 0.2.12
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/LICENSE +21 -0
- package/README.md +168 -0
- package/dist/identity.min.js +1 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.d.ts +157 -0
- package/dist/index.esm.js +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stevan Andric
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# identity-js
|
|
2
|
+
|
|
3
|
+
Visitor intelligence platform with browser fingerprinting, behavioral analytics, and real-time insights.
|
|
4
|
+
|
|
5
|
+
**Dashboard & Docs: [www.identity-js.com](https://www.identity-js.com)**
|
|
6
|
+
|
|
7
|
+
- **40+ fingerprint signals** — canvas, WebGL, audio, fonts, speech voices, math quirks, screen, CPU, timezone
|
|
8
|
+
- **11 behavioral trackers** — dead clicks, phantom clicks, rage clicks, form abandonment, frustration scoring, reading behavior, input hesitation, error tracking, text copy, session replay
|
|
9
|
+
- **Persistent visitor ID** — survives cookie clears via fingerprint reconciliation
|
|
10
|
+
- **Bot detection** — 0-100 score with reasons, no ML required
|
|
11
|
+
- **Custom events** — `IdentityJS.track('signup', { plan: 'pro' })`
|
|
12
|
+
- **SPA support** — automatic page journey via pushState/popstate
|
|
13
|
+
- **Cookie-free** — no cookies, no consent banners
|
|
14
|
+
- **Zero runtime dependencies** — 29 KB gzipped
|
|
15
|
+
- **Fully managed** — no servers to set up, everything runs on identity-js.com
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
### npm
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @identityjs/tracker
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import IdentityJS from '@identityjs/tracker';
|
|
29
|
+
|
|
30
|
+
const visitor = await IdentityJS.init({
|
|
31
|
+
apiKey: 'pk_live_YOUR_KEY',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
console.log(visitor.visitorId); // e.g. "a3f2b1c4d5e6-k9m2"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Script tag (CDN)
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<script src="https://cdn.jsdelivr.net/npm/@identityjs/tracker/dist/identity.min.js"></script>
|
|
41
|
+
<script>
|
|
42
|
+
IdentityJS.init({
|
|
43
|
+
apiKey: 'pk_live_YOUR_KEY',
|
|
44
|
+
});
|
|
45
|
+
</script>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Get your API key at [www.identity-js.com/dashboard](https://www.identity-js.com/dashboard).
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## API
|
|
53
|
+
|
|
54
|
+
### `IdentityJS.init(options?)`
|
|
55
|
+
|
|
56
|
+
Returns `Promise<VisitorObject>`.
|
|
57
|
+
|
|
58
|
+
| Option | Type | Default | Description |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| `apiKey` | `string` | — | Project API key (`pk_live_...`) from your dashboard |
|
|
61
|
+
| `endpoint` | `string` | `https://www.identity-js.com/api/ping` | API endpoint (no need to change this) |
|
|
62
|
+
| `onReady` | `function` | — | Called with the visitor object once ready |
|
|
63
|
+
| `trackBehavior` | `boolean` | `true` | Track clicks, scrolls, keystrokes |
|
|
64
|
+
| `trackSpaNavigation` | `boolean` | `true` | Auto-track route changes |
|
|
65
|
+
| `sendOnLoad` | `boolean` | `true` | Send fingerprint on page load |
|
|
66
|
+
| `sendOnUnload` | `boolean` | `true` | Send session data on page close |
|
|
67
|
+
|
|
68
|
+
### `VisitorObject`
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
{
|
|
72
|
+
visitorId: string; // Persistent ID (localStorage + cookie)
|
|
73
|
+
sessionId: string; // Per-tab session ID
|
|
74
|
+
isNewVisitor: boolean; // True on first ever visit
|
|
75
|
+
fingerprintHash: string; // Hash of stable signals
|
|
76
|
+
signals: object; // Full fingerprint signal set
|
|
77
|
+
|
|
78
|
+
track(name: string, data?: object): void; // Send custom event
|
|
79
|
+
getBehavior(): object; // Live behavioral snapshot
|
|
80
|
+
flush(): void; // Flush session data immediately
|
|
81
|
+
destroy(): void; // Remove event listeners
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `IdentityJS.track(name, data?)`
|
|
86
|
+
|
|
87
|
+
Queue-safe global track — works even before `init()` resolves.
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
IdentityJS.track('added_to_cart', { productId: 'abc', price: 29.99 });
|
|
91
|
+
|
|
92
|
+
// Works before init too:
|
|
93
|
+
IdentityJS.track('page_loaded');
|
|
94
|
+
IdentityJS.init({ apiKey: 'pk_live_...' }); // queued event flushed after init
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Examples
|
|
100
|
+
|
|
101
|
+
### React
|
|
102
|
+
|
|
103
|
+
```jsx
|
|
104
|
+
import { useEffect } from 'react';
|
|
105
|
+
import IdentityJS from '@identityjs/tracker';
|
|
106
|
+
|
|
107
|
+
function App() {
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
IdentityJS.init({
|
|
110
|
+
apiKey: process.env.REACT_APP_IDENTITY_KEY,
|
|
111
|
+
onReady: (v) => console.log('visitor', v.visitorId),
|
|
112
|
+
});
|
|
113
|
+
}, []);
|
|
114
|
+
|
|
115
|
+
return <div />;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Next.js
|
|
120
|
+
|
|
121
|
+
```js
|
|
122
|
+
// app/layout.js or pages/_app.js
|
|
123
|
+
import IdentityJS from '@identityjs/tracker';
|
|
124
|
+
|
|
125
|
+
if (typeof window !== 'undefined') {
|
|
126
|
+
IdentityJS.init({ apiKey: process.env.NEXT_PUBLIC_IDENTITY_KEY });
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Custom events
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
const visitor = await IdentityJS.init({ apiKey: 'pk_live_...' });
|
|
134
|
+
|
|
135
|
+
document.querySelector('#buy').addEventListener('click', () => {
|
|
136
|
+
visitor.track('purchase_clicked', { plan: 'pro', price: 49 });
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
document.querySelector('form').addEventListener('submit', () => {
|
|
140
|
+
visitor.track('form_submitted', { form: 'signup' });
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## What gets tracked automatically
|
|
147
|
+
|
|
148
|
+
Once you call `init()`, these trackers start automatically:
|
|
149
|
+
|
|
150
|
+
| Tracker | What it detects |
|
|
151
|
+
|---|---|
|
|
152
|
+
| **Fingerprinting** | 40+ browser signals combined into a stable hash |
|
|
153
|
+
| **Dead Clicks** | Clicks on non-interactive elements |
|
|
154
|
+
| **Phantom Clicks** | Clicks on elements that look clickable but aren't |
|
|
155
|
+
| **Rage Clicks** | Rapid frustrated clicking |
|
|
156
|
+
| **Form Abandonment** | Forms started but not submitted |
|
|
157
|
+
| **Input Hesitation** | Time between focusing a field and first keystroke |
|
|
158
|
+
| **Reading Behavior** | Scroll velocity classification (reading/skimming/scanning) |
|
|
159
|
+
| **Frustration Score** | Weighted composite from rage clicks, dead clicks, errors, form abandons |
|
|
160
|
+
| **Error Tracking** | JS exceptions, promise rejections, console errors |
|
|
161
|
+
| **Text Copy** | When users copy text from your page |
|
|
162
|
+
| **Session Replay** | Lightweight event-based session recording |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.IdentityJS=t():e.IdentityJS=t()}(Object("undefined"!=typeof self?self:this),()=>(()=>{"use strict";var e={d:(t,n)=>{for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};function n(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=16777619*t>>>0;return t.toString(16).padStart(8,"0")}function o(){const e=navigator.userAgent||"",t=navigator.userAgentData;let n="unknown",o="unknown",a="unknown",i=!1;if(t){i=t.mobile||!1;const e=(t.brands||[]).filter(e=>!e.brand.includes("Not")&&!e.brand.includes("Chromium"));e.length&&(n=e[0].brand,o=e[0].version),a=t.platform||"unknown"}else/Firefox\/(\d+)/.test(e)?(n="Firefox",o=RegExp.$1):/Edg\/(\d+)/.test(e)?(n="Edge",o=RegExp.$1):/Chrome\/(\d+)/.test(e)?(n="Chrome",o=RegExp.$1):/Safari\/(\d+)/.test(e)&&/Version\/(\d+)/.test(e)?(n="Safari",o=RegExp.$1):/MSIE (\d+)|Trident.*rv:(\d+)/.test(e)&&(n="IE",o=RegExp.$1||RegExp.$2),i=/Mobi|Android|iPhone|iPad/.test(e),/Windows NT/.test(e)?a="Windows":/Mac OS X/.test(e)?a="macOS":/Android/.test(e)?a="Android":/iPhone|iPad/.test(e)?a="iOS":/Linux/.test(e)&&(a="Linux");return{userAgent:e,browserName:n,browserVersion:o,os:a,mobile:i,cpuCores:navigator.hardwareConcurrency||null,deviceMemoryGB:navigator.deviceMemory||null,platform:navigator.platform||null,vendor:navigator.vendor||null,maxTouchPoints:navigator.maxTouchPoints||0}}function a(){try{const e=document.createElement("canvas");e.width=280,e.height=60;const t=e.getContext("2d");t.fillStyle="#f0f0f0",t.fillRect(0,0,e.width,e.height),t.fillStyle="#069",t.font="14px Arial",t.fillText("Browser fingerprint 🌐 ñ 中文 ©",10,20),t.fillStyle="#f60",t.font="bold 13px Times New Roman",t.fillText("!@#$%^&*()_+-=[]{}",10,40),t.beginPath(),t.arc(220,30,18,0,2*Math.PI),t.strokeStyle="rgba(102,204,0,0.7)",t.lineWidth=2,t.stroke();const o=t.createLinearGradient(0,0,280,0);o.addColorStop(0,"rgba(255,0,0,0.3)"),o.addColorStop(1,"rgba(0,0,255,0.3)"),t.fillStyle=o,t.fillRect(0,48,280,12);const a=e.toDataURL("image/png");return{hash:n(a),raw:a.slice(-64)}}catch{return{hash:"unavailable",raw:null}}}function i(){try{const e=document.createElement("canvas"),t=e.getContext("webgl")||e.getContext("experimental-webgl");if(!t)return{supported:!1};const o=t.getExtension("WEBGL_debug_renderer_info"),a=o?t.getParameter(o.UNMASKED_RENDERER_WEBGL):t.getParameter(t.RENDERER),i=o?t.getParameter(o.UNMASKED_VENDOR_WEBGL):t.getParameter(t.VENDOR),r=t.getSupportedExtensions()||[];t.clearColor(.2,.4,.6,1),t.clear(t.COLOR_BUFFER_BIT);const s=new Uint8Array(4);return t.readPixels(0,0,1,1,t.RGBA,t.UNSIGNED_BYTE,s),{supported:!0,renderer:a,vendor:i,version:t.getParameter(t.VERSION),shadingLanguageVersion:t.getParameter(t.SHADING_LANGUAGE_VERSION),extensionCount:r.length,clearColorHash:n(s.join(","))}}catch{return{supported:!1}}}function r(){const e=document.createElement("canvas");e.width=200,e.height=30;const t=e.getContext("2d"),n="monospace",o="mmmmmmmmmmlli";t.font=`16px ${n}`;const a=t.measureText(o).width,i=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Times New Roman","Trebuchet MS","Verdana","Helvetica","Palatino","Garamond","Bookman","Avant Garde","Tahoma","Century Gothic","Calibri","Cambria","Consolas","Segoe UI","Gill Sans","Optima","Futura","Franklin Gothic Medium","Lucida Console","Lucida Sans Unicode","MS Sans Serif","MS Serif","Symbol","Wingdings","San Francisco","Helvetica Neue","Menlo","Monaco","Ubuntu","DejaVu Sans","Liberation Sans","Noto Sans","MS Gothic","SimSun","MingLiU","NSimSun"].filter(e=>(t.font=`16px '${e}', ${n}`,t.measureText(o).width!==a));return{installed:i,count:i.length}}function s(){const e={language:navigator.language||null,languages:navigator.languages?[...navigator.languages]:[],timezone:Intl.DateTimeFormat().resolvedOptions().timeZone||null,timezoneOffset:(new Date).getTimezoneOffset(),cookiesEnabled:navigator.cookieEnabled,doNotTrack:navigator.doNotTrack||window.doNotTrack||null,plugins:[],localStorageAvailable:!1,sessionStorageAvailable:!1,indexedDBAvailable:!1,openDatabaseAvailable:!!window.openDatabase,adBlockDetected:!1};if(navigator.plugins)for(let t=0;t<Math.min(navigator.plugins.length,20);t++)e.plugins.push(navigator.plugins[t].name);try{localStorage.setItem("_t","1"),localStorage.removeItem("_t"),e.localStorageAvailable=!0}catch{}try{sessionStorage.setItem("_t","1"),sessionStorage.removeItem("_t"),e.sessionStorageAvailable=!0}catch{}try{e.indexedDBAvailable=!!window.indexedDB}catch{}try{const t=document.createElement("div");t.className="adsbox ad banner ads",t.style.cssText="position:absolute;left:-9999px;height:1px;",document.body.appendChild(t),e.adBlockDetected=0===t.offsetHeight,document.body.removeChild(t)}catch{}return e}function c(){const e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return{connectionType:e?e.effectiveType:null,downlink:e?e.downlink:null,rtt:e?e.rtt:null,saveData:e?e.saveData:null}}function l(){const e=[Math.acos(.123456789),Math.acosh(1.123456789),Math.asin(.123456789),Math.asinh(.123456789),Math.atan(.123456789),Math.atanh(.123456789),Math.atan2(1.123456789,2.123456789),Math.cbrt(.123456789),Math.cos(.123456789),Math.cosh(.123456789),Math.exp(.123456789),Math.expm1(.123456789),Math.hypot(.123456789,1.123456789),Math.log(.123456789),Math.log1p(.123456789),Math.log2(.123456789),Math.log10(.123456789),Math.sin(.123456789),Math.sinh(.123456789),Math.sqrt(.123456789),Math.tan(.123456789),Math.tanh(.123456789),Math.pow(Math.PI,-100),Math.pow(2,-1022)];return{hash:n(e.map(e=>e.toFixed(20)).join(",")),values:e.map(e=>e.toFixed(15))}}function u(){const e=e=>{try{return window.matchMedia(e).matches}catch{return null}};return{prefersColorScheme:e("(prefers-color-scheme: dark)")?"dark":e("(prefers-color-scheme: light)")?"light":"no-preference",prefersReducedMotion:e("(prefers-reduced-motion: reduce)"),prefersReducedData:e("(prefers-reduced-data: reduce)"),prefersContrast:e("(prefers-contrast: more)")?"more":e("(prefers-contrast: less)")?"less":"none",forcedColors:e("(forced-colors: active)"),hdr:e("(dynamic-range: high)"),pointer:e("(pointer: fine)")?"fine":e("(pointer: coarse)")?"coarse":"none",anyPointer:e("(any-pointer: fine)")?"fine":e("(any-pointer: coarse)")?"coarse":"none",hover:e("(hover: hover)"),anyHover:e("(any-hover: hover)"),invertedColors:e("(inverted-colors: inverted)"),overflowInline:e("(overflow-inline: scroll)"),displayModeStandalone:e("(display-mode: standalone)"),displayModeFullscreen:e("(display-mode: fullscreen)")}}function d(){const e=window.outerWidth-window.innerWidth,t=window.outerHeight-window.innerHeight;let n=e>160||t>160;try{const e=performance.now();performance.now()-e>100&&(n=!0)}catch{}return{likely:n,windowSizeDiff:{width:e,height:t}}}e.d(t,{default:()=>v});const h={mouseSampleInterval:100,maxMouseSamples:50,maxClickSamples:30,maxScrollSamples:30,maxKeySamples:50};const m="__idjs_vid",f="__idjs_sid",g="1.2.0";let p=null,w=[];const v={init:async function(e={}){const{endpoint:t="https://www.identity-js.com/api/ping",apiKey:v=null,onReady:y=null,onLimitReached:b=null,trackBehavior:S=!0,trackSpaNavigation:k=!0,sendOnLoad:C=!0,sendOnUnload:M=!0,transportOptions:x={},eventOptions:E={}}=e;v&&(x.headers={...x.headers||{},"X-API-Key":v}),"function"==typeof b&&(x.onLimitReached=b);const D=S?function(e={}){const t={...h,...e},n={mouseSamples:[],clickSamples:[],scrollSamples:[],keyIntervals:[],touchSamples:[],sessionStart:Date.now(),lastMouseTime:0,lastKeyTime:0,pageVisibilityChanges:0,focusChanges:0,rageclickCount:0,lastClickTime:0,lastClickX:0,lastClickY:0,listeners:[]};function o(e,t,o,a){e.addEventListener(t,o,a||{passive:!0}),n.listeners.push({target:e,type:t,handler:o,opts:a})}function a(e,t,n){e.push(t),e.length>n&&e.shift()}function i(e){const o=Date.now();o-n.lastMouseTime<t.mouseSampleInterval||(n.lastMouseTime=o,a(n.mouseSamples,{x:e.clientX,y:e.clientY,t:o-n.sessionStart},t.maxMouseSamples))}function r(e){const o=Date.now(),i=e.clientX,r=e.clientY,s=Math.abs(i-n.lastClickX),c=Math.abs(r-n.lastClickY);o-n.lastClickTime<600&&s<30&&c<30&&n.rageclickCount++,n.lastClickTime=o,n.lastClickX=i,n.lastClickY=r,a(n.clickSamples,{x:i,y:r,t:o-n.sessionStart,button:e.button},t.maxClickSamples)}let s=null;function c(){s||(s=setTimeout(()=>{s=null,a(n.scrollSamples,{scrollY:Math.round(window.scrollY),t:Date.now()-n.sessionStart},t.maxScrollSamples)},150))}function l(){const e=Date.now();if(n.lastKeyTime>0){const o=e-n.lastKeyTime;o>=20&&o<=3e3&&a(n.keyIntervals,o,t.maxKeySamples)}n.lastKeyTime=e}function u(e){const t=e.touches[0];t&&a(n.touchSamples,{x:Math.round(t.clientX),y:Math.round(t.clientY),t:Date.now()-n.sessionStart},20)}function d(){n.pageVisibilityChanges++}function m(){n.focusChanges++}function f(){n.focusChanges++}return{start:function(){o(document,"mousemove",i),o(document,"click",r),o(window,"scroll",c),o(document,"keydown",l),o(document,"touchstart",u),o(document,"visibilitychange",d),o(window,"focus",m),o(window,"blur",f)},stop:function(){for(const{target:e,type:t,handler:o,opts:a}of n.listeners)e.removeEventListener(t,o,a);n.listeners=[],s&&(clearTimeout(s),s=null)},snapshot:function(){const e=n.mouseSamples,t=n.clickSamples,o=n.keyIntervals;let a=0,i=0;for(let t=1;t<e.length;t++){const n=e[t].x-e[t-1].x,o=e[t].y-e[t-1].y,r=e[t].t-e[t-1].t;a+=Math.sqrt(n*n+o*o),r>0&&(i+=r)}const r=i>0?a/i:null,s=o.length>0?o.reduce((e,t)=>e+t,0)/o.length:null,c=o.length>1?Math.sqrt(o.reduce((e,t)=>e+Math.pow(t-s,2),0)/o.length):null,l=n.scrollSamples.length>0?Math.max(...n.scrollSamples.map(e=>e.scrollY)):0;return{sessionDurationMs:Date.now()-n.sessionStart,mouseSampleCount:e.length,avgMouseVelocityPxMs:r?+r.toFixed(4):null,clickCount:t.length,rageclickCount:n.rageclickCount,scrollEventCount:n.scrollSamples.length,maxScrollDepthPx:l,keyPressCount:o.length,avgKeyIntervalMs:s?+s.toFixed(1):null,stdDevKeyIntervalMs:c?+c.toFixed(1):null,touchEventCount:n.touchSamples.length,pageVisibilityChanges:n.pageVisibilityChanges,focusChanges:n.focusChanges,botSignalScore:(0===e.length?1:0)+(0===o.length?1:0)+(n.rageclickCount>3?1:0)}}}}(E):null;D&&D.start();const T=await async function(){const[e,t,h,m,f,g,p,w]=await Promise.all([new Promise(e=>{try{const t=window.AudioContext||window.webkitAudioContext;if(!t)return e({hash:"unavailable"});const o=new t,a=o.createOscillator(),i=o.createAnalyser(),r=o.createGain(),s=o.createScriptProcessor(4096,1,1);r.gain.value=0,a.type="triangle",a.frequency.value=1e4,a.connect(i),i.connect(s),s.connect(r),r.connect(o.destination);let c="";s.onaudioprocess=t=>{const i=t.inputBuffer.getChannelData(0),r=[];for(let e=0;e<i.length;e+=256)r.push(i[e]);c=n(r.map(e=>e.toFixed(8)).join(",")),a.stop(),o.close(),e({hash:c})},a.start(0),setTimeout(()=>{try{a.stop(),o.close()}catch{}e({hash:c||"timeout"})},1e3)}catch{e({hash:"unavailable"})}}),new Promise(e=>{const t=new Set;try{const n=window.RTCPeerConnection||window.webkitRTCPeerConnection||window.mozRTCPeerConnection;if(!n)return e([]);const o=new n({iceServers:[{urls:"stun:stun.l.google.com:19302"}]});o.createDataChannel(""),o.createOffer().then(e=>o.setLocalDescription(e)).catch(()=>{}),o.onicecandidate=n=>{if(!n||!n.candidate)return o.close(),void e([...t]);const a=n.candidate.candidate.match(/([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{0,4}){2,7})/gi);a&&a.forEach(e=>t.add(e))},setTimeout(()=>{try{o.close()}catch{}e([...t])},2e3)}catch{e([])}}),new Promise(e=>{navigator.storage&&navigator.storage.estimate?navigator.storage.estimate().then(({quota:t})=>{e(t<125829120)}).catch(()=>e(null)):e(null)}),new Promise(e=>{try{if(!window.speechSynthesis)return e({available:!1,voices:[],hash:null});function t(){const e=window.speechSynthesis.getVoices();if(!e.length)return null;const t=e.map(e=>`${e.name}|${e.lang}|${e.localService?"L":"R"}`);return{available:!0,count:t.length,voices:t,hash:n(t.join(";")),langs:[...new Set(e.map(e=>e.lang.split("-")[0]))].sort()}}const o=t();if(o)return e(o);let a=!1;window.speechSynthesis.onvoiceschanged=()=>{a||(a=!0,e(t()||{available:!0,voices:[],hash:null}))},setTimeout(()=>{a||(a=!0,e(t()||{available:!1,voices:[],hash:null}))},1500)}catch{e({available:!1,voices:[],hash:null})}}),new Promise(e=>{try{if(!navigator.mediaDevices||!navigator.mediaDevices.enumerateDevices)return e({available:!1});navigator.mediaDevices.enumerateDevices().then(t=>{const o=t.filter(e=>"videoinput"===e.kind),a=t.filter(e=>"audioinput"===e.kind),i=t.filter(e=>"audiooutput"===e.kind),r=t.map(e=>e.deviceId).join(",");e({available:!0,cameraCount:o.length,micCount:a.length,speakerCount:i.length,totalCount:t.length,deviceHash:n(r)})}).catch(()=>e({available:!1}))}catch{e({available:!1})}}),new Promise(e=>{try{if(!navigator.getBattery)return e({available:!1});navigator.getBattery().then(t=>{e({available:!0,level:Math.round(100*t.level),charging:t.charging,chargingTime:isFinite(t.chargingTime)?Math.round(t.chargingTime):null,dischargingTime:isFinite(t.dischargingTime)?Math.round(t.dischargingTime):null})}).catch(()=>e({available:!1}))}catch{e({available:!1})}}),new Promise(e=>{try{if(!navigator.permissions)return e({available:!1});const t=["notifications","geolocation","camera","microphone","clipboard-read","clipboard-write","midi","push","screen-wake-lock","accelerometer","gyroscope","magnetometer"].map(e=>navigator.permissions.query({name:e}).then(t=>({name:e,state:t.state})).catch(()=>({name:e,state:"error"})));Promise.all(t).then(t=>{const n={};t.forEach(e=>{n[e.name]=e.state}),e({available:!0,permissions:n})}).catch(()=>e({available:!1}))}catch{e({available:!1})}}),new Promise(e=>{try{if(!navigator.keyboard||!navigator.keyboard.getLayoutMap)return e({available:!1});navigator.keyboard.getLayoutMap().then(t=>{const o={};["KeyA","KeyQ","KeyW","KeyZ","KeyY","Semicolon","BracketLeft"].forEach(e=>{t.has(e)&&(o[e]=t.get(e))}),e({available:!0,size:t.size,sample:o,hash:n(JSON.stringify(o))})}).catch(()=>e({available:!1}))}catch{e({available:!1})}})]),v={browserDevice:o(),screen:{screenWidth:screen.width,screenHeight:screen.height,availWidth:screen.availWidth,availHeight:screen.availHeight,colorDepth:screen.colorDepth,pixelDepth:screen.pixelDepth,devicePixelRatio:window.devicePixelRatio||1,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight,orientation:screen.orientation?screen.orientation.type:"unknown"},canvas:a(),webgl:i(),audio:e,fonts:r(),browserConfig:s(),network:c(),webrtcIPs:t,incognitoLikely:h,speechVoices:m,mediaDevices:f,math:l(),cssMedia:u(),battery:g,devTools:d(),permissions:p,keyboardLayout:w,collectedAt:Date.now()},y=[v.canvas.hash,v.webgl.renderer,v.webgl.vendor,v.webgl.extensionCount,v.audio.hash,v.math.hash,v.speechVoices.hash,v.speechVoices.count,v.browserDevice.cpuCores,v.browserDevice.deviceMemoryGB,v.mediaDevices.cameraCount,v.mediaDevices.micCount,v.mediaDevices.deviceHash,v.screen.screenWidth,v.screen.screenHeight,v.screen.colorDepth,v.screen.devicePixelRatio,v.browserConfig.timezone,v.browserConfig.language,v.browserConfig.plugins.join(","),v.fonts.count,v.fonts.installed.slice(0,10).join(","),v.cssMedia.pointer,v.cssMedia.prefersColorScheme,v.keyboardLayout.hash].join("|");return v.fingerprintHash=n(y),v}(),{visitorId:L,isNew:N}=function(e){const t=function(){try{return localStorage.getItem(m)||null}catch{return null}}()||function(){try{const e=document.cookie.match(new RegExp(`${m}=([^;]+)`));return e?e[1]:null}catch{return null}}();if(t)return{visitorId:t,isNew:!1};const n=`${e}-${Math.random().toString(36).slice(2,10)}`;return function(e){try{localStorage.setItem(m,e)}catch{}try{const t=new Date(Date.now()+2592e6).toUTCString();document.cookie=`${m}=${e};expires=${t};path=/;SameSite=Lax`}catch{}}(n),{visitorId:n,isNew:!0}}(T.fingerprintHash),I=function(){try{let e=sessionStorage.getItem(f);return e||(e=n(Date.now()+Math.random().toString()),sessionStorage.setItem(f,e)),e}catch{return n(Date.now()+Math.random().toString())}}(),_={visitorId:L,sessionId:I,isNewVisitor:N,fingerprintHash:T.fingerprintHash,version:g,signals:T};if("function"==typeof y)try{y(_)}catch{}let R=null;function A(e,t={}){R&&R.send({type:"event",name:e,visitorId:L,sessionId:I,data:t,ts:Date.now(),...v?{apiKey:v}:{}})}if(t){R=function(e,t={}){if(!e)throw new Error("[IdentityJS] transport: endpoint is required");const n={headers:{"Content-Type":"application/json",...t.headers||{}},timeout:t.timeout||5e3,onSuccess:t.onSuccess||null,onError:t.onError||null,onLimitReached:t.onLimitReached||null},o=[];let a=null;async function i(t){const o=new AbortController,a=setTimeout(()=>o.abort(),n.timeout);try{const i=await fetch(e,{method:"POST",headers:n.headers,body:JSON.stringify(t),signal:o.signal,keepalive:!0});if(clearTimeout(a),429===i.status){let e={};try{e=await i.json()}catch{}return n.onLimitReached&&n.onLimitReached(e),!1}if(!i.ok)throw new Error(`HTTP ${i.status}`);return n.onSuccess&&n.onSuccess(t),!0}catch(e){throw clearTimeout(a),e}}function r(){if(a||0===o.length)return;const e=Math.min(...o.map(e=>e.nextRetryAt)),t=Math.max(0,e-Date.now());a=setTimeout(s,t)}async function s(){a=null;const e=Date.now(),t=o.filter(t=>t.nextRetryAt<=e);for(const e of t){o.splice(o.indexOf(e),1);try{await i(e.payload)}catch{e.attempts++,e.attempts<3?(e.nextRetryAt=Date.now()+1e3*Math.pow(2,e.attempts-1),o.push(e)):n.onError&&n.onError(e.payload,new Error("Max retries exceeded"))}}o.length>0&&r()}function c(t){if(!function(t){if(!navigator.sendBeacon)return!1;const n=new Blob([JSON.stringify(t)],{type:"application/json"});return navigator.sendBeacon(e,n)}(t))try{const o=new XMLHttpRequest;o.open("POST",e,!1),Object.entries(n.headers).forEach(([e,t])=>o.setRequestHeader(e,t)),o.send(JSON.stringify(t))}catch{}}return{send:async function(e){try{await i(e)}catch{o.push({payload:e,attempts:1,nextRetryAt:Date.now()+1e3}),r()}},flush:c,attachUnloadHandler:function(e){document.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&c(e())},{passive:!0}),window.addEventListener("pagehide",()=>c(e()),{passive:!0})}}}(t,x);const H={};try{const q=new URLSearchParams(location.search);["utm_source","utm_medium","utm_campaign","utm_term","utm_content"].forEach(e=>{const t=q.get(e);t&&(H[e]=t)})}catch{}let O=null;function K(){try{const e=performance.getEntriesByType("navigation")[0],t=performance.getEntriesByType("paint"),n=performance.getEntriesByType("largest-contentful-paint"),o={};e&&(o.dns=Math.round(e.domainLookupEnd-e.domainLookupStart),o.tcp=Math.round(e.connectEnd-e.connectStart),o.ttfb=Math.round(e.responseStart-e.requestStart),o.domLoad=Math.round(e.domContentLoadedEventEnd-e.startTime),o.fullLoad=Math.round(e.loadEventEnd-e.startTime),o.transferSize=e.transferSize||0);const a=t.find(e=>"first-contentful-paint"===e.name);a&&(o.fcp=Math.round(a.startTime)),n&&n.length&&(o.lcp=Math.round(n[n.length-1].startTime)),O=Object.keys(o).length>0?o:null}catch{}}function j(){O&&R&&R.send({type:"event",name:"web_vitals",visitorId:L,sessionId:I,url:location.href,data:O,ts:Date.now(),...v?{apiKey:v}:{}})}"complete"===document.readyState?setTimeout(K,100):window.addEventListener("load",()=>setTimeout(K,100),{once:!0}),setTimeout(j,5e3),C&&R.send({type:"pageview",visitorId:L,sessionId:I,isNewVisitor:N,fingerprintHash:T.fingerprintHash,url:location.href,referrer:document.referrer,title:document.title,signals:T,utm:Object.keys(H).length?H:void 0,ts:Date.now(),...v?{apiKey:v}:{}}),M&&D&&R.attachUnloadHandler(()=>{const e=Date.now();return G&&(F+=e-W,W=e),X(),{type:"session_end",visitorId:L,sessionId:I,url:location.href,behavior:D.snapshot(),activeTimeMs:F,totalTimeMs:e-U,ts:e,...v?{apiKey:v}:{}}});const B=(e,t)=>{A(e,t);try{document.dispatchEvent(new CustomEvent("__idjs_event",{detail:{name:e,data:t}}))}catch{}};try{(function(e){const t=new Set;function n(n){const o=`${n.errorType}:${n.message}:${n.source}:${n.line}`;t.has(o)||(t.add(o),e("console_error",n))}return{start:function(){const e=window.onerror;window.onerror=(t,o,a,i,r)=>(n({errorType:"js_exception",message:String(t).slice(0,500),source:(o||"").replace(location.origin,""),line:a,col:i,stack:r?.stack?.slice(0,1e3)||null,url:location.pathname}),"function"==typeof e&&e(t,o,a,i,r)),window.addEventListener("unhandledrejection",e=>{const t=e.reason;n({errorType:"promise_rejection",message:(t?.message||String(t)).slice(0,500),source:null,line:null,col:null,stack:t?.stack?.slice(0,1e3)||null,url:location.pathname})});const t=console.error.bind(console);console.error=(...e)=>{t(...e),n({errorType:"console_error",message:e.map(e=>e instanceof Error?e.stack||e.message:String(e)).join(" ").slice(0,500),source:null,line:null,col:null,stack:null,url:location.pathname})}}}})(B).start(),function(e){function t(t){if(function(e){let t=e;for(;t&&t!==document.body;){const e=(t.tagName||"").toLowerCase();if(["a","button","input","select","textarea","label","summary"].includes(e))return!0;if(t.onclick||t.getAttribute?.("onclick"))return!0;const n=t.getAttribute?.("role");if("button"===n||"link"===n||"menuitem"===n||"tab"===n)return!0;if(null!=t.getAttribute?.("tabindex")&&"-1"!==t.getAttribute("tabindex"))return!0;try{if("pointer"===window.getComputedStyle(t).cursor)return!0}catch{}t=t.parentElement}return!1}(t.target))return;const n=t.target;e("dead_click",{tag:(n.tagName||"unknown").toLowerCase(),id:n.id||null,className:("string"==typeof n.className?n.className:"").slice(0,100),text:(n.textContent||"").slice(0,80).trim(),x:t.clientX,y:t.clientY,url:location.pathname})}return{start:function(){document.addEventListener("click",t,{passive:!0})}}}(B).start(),function(e){let t=[];function n(n){const o=Date.now(),a=n.clientX,i=n.clientY;t=t.filter(e=>o-e.t<2e3),t.push({x:a,y:i,t:o});const r=t.filter(e=>o-e.t<1e3&&Math.abs(e.x-a)<50&&Math.abs(e.y-i)<50);if(r.length>=3){const o=n.target;e("rage_click",{clickCount:r.length,x:a,y:i,tag:(o.tagName||"unknown").toLowerCase(),id:o.id||null,className:("string"==typeof o.className?o.className:"").slice(0,100),text:(o.textContent||"").slice(0,80).trim(),url:location.pathname}),t=[]}}return{start:function(){document.addEventListener("click",n,{passive:!0})}}}(B).start(),function(e){function t(){const t=(window.getSelection?.()?.toString()||"").trim();t&&e("text_copy",{length:t.length,preview:t.slice(0,120),url:location.pathname})}return{start:function(){document.addEventListener("copy",t,{passive:!0})}}}(B).start(),function(e){const t=new Map;function n(e){return e.id||e.getAttribute("name")||(e.action?new URL(e.action,location.href).pathname:null)||`form-${[...document.forms].indexOf(e)}`}function o(e){const o=(e.target?.tagName||"").toLowerCase();if(!["input","textarea","select"].includes(o))return;const a=e.target.closest("form");if(!a)return;const i=n(a);t.has(i)||t.set(i,{touched:[],submitted:!1});const r=t.get(i),s=e.target.name||e.target.id||e.target.placeholder||o;r.touched.includes(s)||r.touched.push(s)}function a(e){const o=e.target;if(!o)return;const a=n(o);t.has(a)&&(t.get(a).submitted=!0)}function i(){t.forEach((t,n)=>{t.touched.length>0&&!t.submitted&&e("form_abandon",{formId:n,fieldsTouched:t.touched,fieldCount:t.touched.length,lastField:t.touched[t.touched.length-1],url:location.pathname})})}return{start:function(){document.addEventListener("focusin",o,{passive:!0}),document.addEventListener("submit",a,{passive:!0}),window.addEventListener("beforeunload",i)}}}(B).start(),function(e){const t=new Map;function n(n){if(function(e){let t=e;for(;t&&t!==document.body;){const e=(t.tagName||"").toLowerCase();if(["a","button","input","select","textarea","label","summary"].includes(e))return!0;if(t.onclick||t.getAttribute?.("onclick"))return!0;const n=t.getAttribute?.("role");if("button"===n||"link"===n||"menuitem"===n||"tab"===n)return!0;if(null!=t.getAttribute?.("tabindex")&&"-1"!==t.getAttribute("tabindex"))return!0;t=t.parentElement}return!1}(n.target))return;const o=function(e){try{const t=window.getComputedStyle(e);if("pointer"===t.cursor)return"pointer-cursor";if(t.textDecorationLine?.includes("underline")&&"U"!==e.tagName)return"underlined-text";const n=t.color;if(n){const e=n.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e){const[,t,n,o]=e.map(Number);if(o>150&&o>1.5*t&&o>1.2*n)return"link-colored-text"}}const o=(e.tagName||"").toLowerCase();if(("img"===o||"svg"===o)&&e.clientWidth<80&&e.clientHeight<80)return"icon-or-image";if(t.transition&&(t.transition.includes("transform")||t.transition.includes("shadow")||t.transition.includes("background")))return"has-hover-transition";const a=e.parentElement;if(a)for(const t of a.children){if(t===e)continue;const n=(t.tagName||"").toLowerCase();if(["a","button","input"].includes(n))return"near-interactive"}}catch{}return null}(n.target);if(!o)return;const a=n.target,i=(a.tagName||"unknown").toLowerCase(),r=a.id||null,s=(a.textContent||"").slice(0,80).trim(),c=`${i}:${r}:${s}:${o}`,l=Date.now(),u=t.get(c);u&&l-u<3e3||(t.set(c,l),e("phantom_click",{tag:i,id:r,className:("string"==typeof a.className?a.className:"").slice(0,120),text:s,reason:o,x:n.clientX,y:n.clientY,url:location.pathname}))}return{start:function(){document.addEventListener("click",n,{passive:!0})}}}(B).start(),function(e){let t=0,n=0,o=0,a=0,i=0,r=Date.now(),s=-1;function c(){const r=Math.min(100,Math.round(15*t+5*n+10*o+20*a+8*i));r!==s&&(s=r,e("frustration_score",{score:r,rageClicks:t,deadClicks:n,errors:o,formAbandons:a,rapidNav:i,url:location.pathname}))}return{start:function(){document.addEventListener("__idjs_event",e=>{const{name:i}=e.detail||{};"rage_click"===i&&(t++,c()),"dead_click"===i&&(n++,c()),"console_error"===i&&(o++,c()),"form_abandon"===i&&(a++,c())});const e=history.pushState?.bind(history);e&&(history.pushState=function(...t){const n=Date.now();return n-r<3e3&&(i++,c()),r=n,e(...t)}),window.addEventListener("popstate",()=>{const e=Date.now();e-r<3e3&&(i++,c()),r=e}),window.addEventListener("beforeunload",c)}}}(B).start(),function(e){let t=[],n=0,o=Date.now(),a=location.pathname,i=0,r=Date.now(),s=!0,c=null;function l(){c||(c=setTimeout(()=>{c=null;const e=Date.now(),o=function(){const e=document.documentElement.scrollHeight-window.innerHeight;return e>0?Math.round(window.scrollY/e*100):0}();t.push({pct:o,ts:e}),o>n&&(n=o)},250))}function u(){document.hidden?s&&(i+=Date.now()-r,s=!1):(r=Date.now(),s=!0)}function d(){const c=function(){s&&(i+=Date.now()-r);let e=0;if(t.length>=2){let n=0,o=0;for(let e=1;e<t.length;e++){const a=(t[e].ts-t[e-1].ts)/1e3;a>0&&a<5&&(n+=Math.abs(t[e].pct-t[e-1].pct)/a,o++)}e=o>0?Math.round(n/o*10)/10:0}const c=e<15?"reading":e<40?"skimming":"scanning",l=Date.now()-o,u=document.documentElement.scrollHeight;return{url:a,maxScrollPct:n,avgVelocity:e,behavior:c,activeTimeMs:Math.round(i),totalTimeMs:Math.round(l),attentionPct:l>0?Math.round(i/l*100):100,scrollSamples:t.length,contentHeight:u}}();c.totalTimeMs<2e3||e("reading_behavior",c)}function h(){t=[],n=0,o=Date.now(),a=location.pathname,i=0,r=Date.now(),s=!document.hidden}return{start:function(){window.addEventListener("scroll",l,{passive:!0}),document.addEventListener("visibilitychange",u,{passive:!0});const e=history.pushState?.bind(history);e&&(history.pushState=function(...t){d();const n=e(...t);return h(),n}),window.addEventListener("popstate",()=>{d(),h()}),window.addEventListener("beforeunload",d)}}}(B).start(),function(e){const t=new Map;function n(e){return e.id||e.getAttribute("name")||(e.action?new URL(e.action,location.href).pathname:null)||`form-${[...document.forms].indexOf(e)}`}function o(e){return e.name||e.id||e.placeholder||e.getAttribute("aria-label")||e.tagName.toLowerCase()}function a(e){const o=n(e);return t.has(o)||t.set(o,{fields:new Map,submitted:!1}),{id:o,data:t.get(o)}}function i(e){const t=e.target,n=(t?.tagName||"").toLowerCase();if(!["input","textarea","select"].includes(n))return;const i=t.closest("form");if(!i)return;const{data:r}=a(i),s=o(t);if(r.fields.has(s)){const e=r.fields.get(s);e.firstKeyTime||(e.focusTime=Date.now())}else r.fields.set(s,{focusTime:Date.now(),firstKeyTime:null,keyCount:0,deleteCount:0,retypes:0,cleared:!1,hesitationMs:null})}function r(e){const t=e.target,n=(t?.tagName||"").toLowerCase();if(!["input","textarea"].includes(n))return;const i=t.closest("form");if(!i)return;const{data:r}=a(i),s=o(t),c=r.fields.get(s);c&&(c.keyCount++,c.firstKeyTime||(c.firstKeyTime=Date.now(),c.hesitationMs=c.firstKeyTime-c.focusTime),"Backspace"!==e.key&&"Delete"!==e.key||c.deleteCount++,("a"===e.key&&(e.ctrlKey||e.metaKey)||c.deleteCount>3&&t.value?.length<=1)&&(c.cleared||(c.retypes++,c.cleared=!0)),1!==e.key.length||e.ctrlKey||e.metaKey||(c.cleared=!1))}function s(n){const o=t.get(n);if(!o||0===o.fields.size)return;const a=[];o.fields.forEach((e,t)=>{a.push({field:t,hesitationMs:e.hesitationMs,keyCount:e.keyCount,deleteCount:e.deleteCount,retypes:e.retypes})}),e("input_hesitation",{formId:n,fields:a,submitted:o.submitted,url:location.pathname})}function c(e){const o=e.target;if(!o)return;const a=n(o);t.has(a)&&(t.get(a).submitted=!0,s(a),t.delete(a))}function l(){t.forEach((e,t)=>{e.fields.size>0&&!e.submitted&&s(t)})}return{start:function(){document.addEventListener("focusin",i,{passive:!0}),document.addEventListener("keydown",r,{passive:!0}),document.addEventListener("submit",c,{passive:!0}),window.addEventListener("beforeunload",l)}}}(B).start()}catch(J){console.error("[IdentityJS] advanced tracker init failed:",J)}const $=2e4;let V=null,U=Date.now(),G=!document.hidden,F=0,W=Date.now();function Y(){if(!R)return;const e=Date.now();G&&(F+=e-W,W=e),R.send({type:"heartbeat",visitorId:L,sessionId:I,url:location.href,activeTimeMs:F,totalTimeMs:e-U,ts:e,...v?{apiKey:v}:{}})}function z(){V||(V=setInterval(Y,$))}function X(){V&&(clearInterval(V),V=null)}if(document.addEventListener("visibilitychange",()=>{const e=Date.now();document.hidden?(G&&(F+=e-W),G=!1,X(),Y()):(G=!0,W=e,z())},{passive:!0}),z(),k){let Z=Date.now(),Q=location.href,ee=document.title;function te(){const e=location.href,t=document.title;if(e===Q)return;const n=Date.now()-Z;A("pageview",{url:Q,title:ee,durationMs:n,nextUrl:e}),Z=Date.now(),Q=e,ee=t}const ne=history.pushState.bind(history),oe=history.replaceState.bind(history);history.pushState=function(...e){ne(...e),setTimeout(te,0)},history.replaceState=function(...e){oe(...e),setTimeout(te,0)},window.addEventListener("popstate",()=>setTimeout(te,0)),window.addEventListener("hashchange",()=>setTimeout(te,0))}}const P={visitorId:L,sessionId:I,isNewVisitor:N,fingerprintHash:T.fingerprintHash,signals:T,getBehavior:()=>D?D.snapshot():null,track:(e,t={})=>A(e,t),flush:()=>{R&&D&&R.flush({type:"session_end",visitorId:L,sessionId:I,url:location.href,behavior:D.snapshot(),ts:Date.now(),...v?{apiKey:v}:{}})},destroy:()=>{stopHeartbeat(),D&&D.stop()}};return p=P,w.length&&(w.forEach(({name:e,data:t})=>P.track(e,t)),w=[]),P},track:function(e,t={}){p?p.track(e,t):w.push({name:e,data:t})},version:g};return t.default})());
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{"use strict";var e={d:(t,n)=>{for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};function n(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=16777619*t>>>0;return t.toString(16).padStart(8,"0")}function o(){const e=navigator.userAgent||"",t=navigator.userAgentData;let n="unknown",o="unknown",a="unknown",i=!1;if(t){i=t.mobile||!1;const e=(t.brands||[]).filter(e=>!e.brand.includes("Not")&&!e.brand.includes("Chromium"));e.length&&(n=e[0].brand,o=e[0].version),a=t.platform||"unknown"}else/Firefox\/(\d+)/.test(e)?(n="Firefox",o=RegExp.$1):/Edg\/(\d+)/.test(e)?(n="Edge",o=RegExp.$1):/Chrome\/(\d+)/.test(e)?(n="Chrome",o=RegExp.$1):/Safari\/(\d+)/.test(e)&&/Version\/(\d+)/.test(e)?(n="Safari",o=RegExp.$1):/MSIE (\d+)|Trident.*rv:(\d+)/.test(e)&&(n="IE",o=RegExp.$1||RegExp.$2),i=/Mobi|Android|iPhone|iPad/.test(e),/Windows NT/.test(e)?a="Windows":/Mac OS X/.test(e)?a="macOS":/Android/.test(e)?a="Android":/iPhone|iPad/.test(e)?a="iOS":/Linux/.test(e)&&(a="Linux");return{userAgent:e,browserName:n,browserVersion:o,os:a,mobile:i,cpuCores:navigator.hardwareConcurrency||null,deviceMemoryGB:navigator.deviceMemory||null,platform:navigator.platform||null,vendor:navigator.vendor||null,maxTouchPoints:navigator.maxTouchPoints||0}}function a(){try{const e=document.createElement("canvas");e.width=280,e.height=60;const t=e.getContext("2d");t.fillStyle="#f0f0f0",t.fillRect(0,0,e.width,e.height),t.fillStyle="#069",t.font="14px Arial",t.fillText("Browser fingerprint 🌐 ñ 中文 ©",10,20),t.fillStyle="#f60",t.font="bold 13px Times New Roman",t.fillText("!@#$%^&*()_+-=[]{}",10,40),t.beginPath(),t.arc(220,30,18,0,2*Math.PI),t.strokeStyle="rgba(102,204,0,0.7)",t.lineWidth=2,t.stroke();const o=t.createLinearGradient(0,0,280,0);o.addColorStop(0,"rgba(255,0,0,0.3)"),o.addColorStop(1,"rgba(0,0,255,0.3)"),t.fillStyle=o,t.fillRect(0,48,280,12);const a=e.toDataURL("image/png");return{hash:n(a),raw:a.slice(-64)}}catch{return{hash:"unavailable",raw:null}}}function i(){try{const e=document.createElement("canvas"),t=e.getContext("webgl")||e.getContext("experimental-webgl");if(!t)return{supported:!1};const o=t.getExtension("WEBGL_debug_renderer_info"),a=o?t.getParameter(o.UNMASKED_RENDERER_WEBGL):t.getParameter(t.RENDERER),i=o?t.getParameter(o.UNMASKED_VENDOR_WEBGL):t.getParameter(t.VENDOR),r=t.getSupportedExtensions()||[];t.clearColor(.2,.4,.6,1),t.clear(t.COLOR_BUFFER_BIT);const s=new Uint8Array(4);return t.readPixels(0,0,1,1,t.RGBA,t.UNSIGNED_BYTE,s),{supported:!0,renderer:a,vendor:i,version:t.getParameter(t.VERSION),shadingLanguageVersion:t.getParameter(t.SHADING_LANGUAGE_VERSION),extensionCount:r.length,clearColorHash:n(s.join(","))}}catch{return{supported:!1}}}function r(){const e=document.createElement("canvas");e.width=200,e.height=30;const t=e.getContext("2d"),n="monospace",o="mmmmmmmmmmlli";t.font=`16px ${n}`;const a=t.measureText(o).width,i=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Times New Roman","Trebuchet MS","Verdana","Helvetica","Palatino","Garamond","Bookman","Avant Garde","Tahoma","Century Gothic","Calibri","Cambria","Consolas","Segoe UI","Gill Sans","Optima","Futura","Franklin Gothic Medium","Lucida Console","Lucida Sans Unicode","MS Sans Serif","MS Serif","Symbol","Wingdings","San Francisco","Helvetica Neue","Menlo","Monaco","Ubuntu","DejaVu Sans","Liberation Sans","Noto Sans","MS Gothic","SimSun","MingLiU","NSimSun"].filter(e=>(t.font=`16px '${e}', ${n}`,t.measureText(o).width!==a));return{installed:i,count:i.length}}function s(){const e={language:navigator.language||null,languages:navigator.languages?[...navigator.languages]:[],timezone:Intl.DateTimeFormat().resolvedOptions().timeZone||null,timezoneOffset:(new Date).getTimezoneOffset(),cookiesEnabled:navigator.cookieEnabled,doNotTrack:navigator.doNotTrack||window.doNotTrack||null,plugins:[],localStorageAvailable:!1,sessionStorageAvailable:!1,indexedDBAvailable:!1,openDatabaseAvailable:!!window.openDatabase,adBlockDetected:!1};if(navigator.plugins)for(let t=0;t<Math.min(navigator.plugins.length,20);t++)e.plugins.push(navigator.plugins[t].name);try{localStorage.setItem("_t","1"),localStorage.removeItem("_t"),e.localStorageAvailable=!0}catch{}try{sessionStorage.setItem("_t","1"),sessionStorage.removeItem("_t"),e.sessionStorageAvailable=!0}catch{}try{e.indexedDBAvailable=!!window.indexedDB}catch{}try{const t=document.createElement("div");t.className="adsbox ad banner ads",t.style.cssText="position:absolute;left:-9999px;height:1px;",document.body.appendChild(t),e.adBlockDetected=0===t.offsetHeight,document.body.removeChild(t)}catch{}return e}function c(){const e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return{connectionType:e?e.effectiveType:null,downlink:e?e.downlink:null,rtt:e?e.rtt:null,saveData:e?e.saveData:null}}function l(){const e=[Math.acos(.123456789),Math.acosh(1.123456789),Math.asin(.123456789),Math.asinh(.123456789),Math.atan(.123456789),Math.atanh(.123456789),Math.atan2(1.123456789,2.123456789),Math.cbrt(.123456789),Math.cos(.123456789),Math.cosh(.123456789),Math.exp(.123456789),Math.expm1(.123456789),Math.hypot(.123456789,1.123456789),Math.log(.123456789),Math.log1p(.123456789),Math.log2(.123456789),Math.log10(.123456789),Math.sin(.123456789),Math.sinh(.123456789),Math.sqrt(.123456789),Math.tan(.123456789),Math.tanh(.123456789),Math.pow(Math.PI,-100),Math.pow(2,-1022)];return{hash:n(e.map(e=>e.toFixed(20)).join(",")),values:e.map(e=>e.toFixed(15))}}function u(){const e=e=>{try{return window.matchMedia(e).matches}catch{return null}};return{prefersColorScheme:e("(prefers-color-scheme: dark)")?"dark":e("(prefers-color-scheme: light)")?"light":"no-preference",prefersReducedMotion:e("(prefers-reduced-motion: reduce)"),prefersReducedData:e("(prefers-reduced-data: reduce)"),prefersContrast:e("(prefers-contrast: more)")?"more":e("(prefers-contrast: less)")?"less":"none",forcedColors:e("(forced-colors: active)"),hdr:e("(dynamic-range: high)"),pointer:e("(pointer: fine)")?"fine":e("(pointer: coarse)")?"coarse":"none",anyPointer:e("(any-pointer: fine)")?"fine":e("(any-pointer: coarse)")?"coarse":"none",hover:e("(hover: hover)"),anyHover:e("(any-hover: hover)"),invertedColors:e("(inverted-colors: inverted)"),overflowInline:e("(overflow-inline: scroll)"),displayModeStandalone:e("(display-mode: standalone)"),displayModeFullscreen:e("(display-mode: fullscreen)")}}function d(){const e=window.outerWidth-window.innerWidth,t=window.outerHeight-window.innerHeight;let n=e>160||t>160;try{const e=performance.now();performance.now()-e>100&&(n=!0)}catch{}return{likely:n,windowSizeDiff:{width:e,height:t}}}e.r(t),e.d(t,{default:()=>w});const h={mouseSampleInterval:100,maxMouseSamples:50,maxClickSamples:30,maxScrollSamples:30,maxKeySamples:50};const m="__idjs_vid",g="__idjs_sid",f="1.2.0";let p=null,v=[];const w={init:async function(e={}){const{endpoint:t="https://www.identity-js.com/api/ping",apiKey:w=null,onReady:y=null,onLimitReached:b=null,trackBehavior:S=!0,trackSpaNavigation:k=!0,sendOnLoad:C=!0,sendOnUnload:M=!0,transportOptions:x={},eventOptions:E={}}=e;w&&(x.headers={...x.headers||{},"X-API-Key":w}),"function"==typeof b&&(x.onLimitReached=b);const D=S?function(e={}){const t={...h,...e},n={mouseSamples:[],clickSamples:[],scrollSamples:[],keyIntervals:[],touchSamples:[],sessionStart:Date.now(),lastMouseTime:0,lastKeyTime:0,pageVisibilityChanges:0,focusChanges:0,rageclickCount:0,lastClickTime:0,lastClickX:0,lastClickY:0,listeners:[]};function o(e,t,o,a){e.addEventListener(t,o,a||{passive:!0}),n.listeners.push({target:e,type:t,handler:o,opts:a})}function a(e,t,n){e.push(t),e.length>n&&e.shift()}function i(e){const o=Date.now();o-n.lastMouseTime<t.mouseSampleInterval||(n.lastMouseTime=o,a(n.mouseSamples,{x:e.clientX,y:e.clientY,t:o-n.sessionStart},t.maxMouseSamples))}function r(e){const o=Date.now(),i=e.clientX,r=e.clientY,s=Math.abs(i-n.lastClickX),c=Math.abs(r-n.lastClickY);o-n.lastClickTime<600&&s<30&&c<30&&n.rageclickCount++,n.lastClickTime=o,n.lastClickX=i,n.lastClickY=r,a(n.clickSamples,{x:i,y:r,t:o-n.sessionStart,button:e.button},t.maxClickSamples)}let s=null;function c(){s||(s=setTimeout(()=>{s=null,a(n.scrollSamples,{scrollY:Math.round(window.scrollY),t:Date.now()-n.sessionStart},t.maxScrollSamples)},150))}function l(){const e=Date.now();if(n.lastKeyTime>0){const o=e-n.lastKeyTime;o>=20&&o<=3e3&&a(n.keyIntervals,o,t.maxKeySamples)}n.lastKeyTime=e}function u(e){const t=e.touches[0];t&&a(n.touchSamples,{x:Math.round(t.clientX),y:Math.round(t.clientY),t:Date.now()-n.sessionStart},20)}function d(){n.pageVisibilityChanges++}function m(){n.focusChanges++}function g(){n.focusChanges++}return{start:function(){o(document,"mousemove",i),o(document,"click",r),o(window,"scroll",c),o(document,"keydown",l),o(document,"touchstart",u),o(document,"visibilitychange",d),o(window,"focus",m),o(window,"blur",g)},stop:function(){for(const{target:e,type:t,handler:o,opts:a}of n.listeners)e.removeEventListener(t,o,a);n.listeners=[],s&&(clearTimeout(s),s=null)},snapshot:function(){const e=n.mouseSamples,t=n.clickSamples,o=n.keyIntervals;let a=0,i=0;for(let t=1;t<e.length;t++){const n=e[t].x-e[t-1].x,o=e[t].y-e[t-1].y,r=e[t].t-e[t-1].t;a+=Math.sqrt(n*n+o*o),r>0&&(i+=r)}const r=i>0?a/i:null,s=o.length>0?o.reduce((e,t)=>e+t,0)/o.length:null,c=o.length>1?Math.sqrt(o.reduce((e,t)=>e+Math.pow(t-s,2),0)/o.length):null,l=n.scrollSamples.length>0?Math.max(...n.scrollSamples.map(e=>e.scrollY)):0;return{sessionDurationMs:Date.now()-n.sessionStart,mouseSampleCount:e.length,avgMouseVelocityPxMs:r?+r.toFixed(4):null,clickCount:t.length,rageclickCount:n.rageclickCount,scrollEventCount:n.scrollSamples.length,maxScrollDepthPx:l,keyPressCount:o.length,avgKeyIntervalMs:s?+s.toFixed(1):null,stdDevKeyIntervalMs:c?+c.toFixed(1):null,touchEventCount:n.touchSamples.length,pageVisibilityChanges:n.pageVisibilityChanges,focusChanges:n.focusChanges,botSignalScore:(0===e.length?1:0)+(0===o.length?1:0)+(n.rageclickCount>3?1:0)}}}}(E):null;D&&D.start();const T=await async function(){const[e,t,h,m,g,f,p,v]=await Promise.all([new Promise(e=>{try{const t=window.AudioContext||window.webkitAudioContext;if(!t)return e({hash:"unavailable"});const o=new t,a=o.createOscillator(),i=o.createAnalyser(),r=o.createGain(),s=o.createScriptProcessor(4096,1,1);r.gain.value=0,a.type="triangle",a.frequency.value=1e4,a.connect(i),i.connect(s),s.connect(r),r.connect(o.destination);let c="";s.onaudioprocess=t=>{const i=t.inputBuffer.getChannelData(0),r=[];for(let e=0;e<i.length;e+=256)r.push(i[e]);c=n(r.map(e=>e.toFixed(8)).join(",")),a.stop(),o.close(),e({hash:c})},a.start(0),setTimeout(()=>{try{a.stop(),o.close()}catch{}e({hash:c||"timeout"})},1e3)}catch{e({hash:"unavailable"})}}),new Promise(e=>{const t=new Set;try{const n=window.RTCPeerConnection||window.webkitRTCPeerConnection||window.mozRTCPeerConnection;if(!n)return e([]);const o=new n({iceServers:[{urls:"stun:stun.l.google.com:19302"}]});o.createDataChannel(""),o.createOffer().then(e=>o.setLocalDescription(e)).catch(()=>{}),o.onicecandidate=n=>{if(!n||!n.candidate)return o.close(),void e([...t]);const a=n.candidate.candidate.match(/([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{0,4}){2,7})/gi);a&&a.forEach(e=>t.add(e))},setTimeout(()=>{try{o.close()}catch{}e([...t])},2e3)}catch{e([])}}),new Promise(e=>{navigator.storage&&navigator.storage.estimate?navigator.storage.estimate().then(({quota:t})=>{e(t<125829120)}).catch(()=>e(null)):e(null)}),new Promise(e=>{try{if(!window.speechSynthesis)return e({available:!1,voices:[],hash:null});function t(){const e=window.speechSynthesis.getVoices();if(!e.length)return null;const t=e.map(e=>`${e.name}|${e.lang}|${e.localService?"L":"R"}`);return{available:!0,count:t.length,voices:t,hash:n(t.join(";")),langs:[...new Set(e.map(e=>e.lang.split("-")[0]))].sort()}}const o=t();if(o)return e(o);let a=!1;window.speechSynthesis.onvoiceschanged=()=>{a||(a=!0,e(t()||{available:!0,voices:[],hash:null}))},setTimeout(()=>{a||(a=!0,e(t()||{available:!1,voices:[],hash:null}))},1500)}catch{e({available:!1,voices:[],hash:null})}}),new Promise(e=>{try{if(!navigator.mediaDevices||!navigator.mediaDevices.enumerateDevices)return e({available:!1});navigator.mediaDevices.enumerateDevices().then(t=>{const o=t.filter(e=>"videoinput"===e.kind),a=t.filter(e=>"audioinput"===e.kind),i=t.filter(e=>"audiooutput"===e.kind),r=t.map(e=>e.deviceId).join(",");e({available:!0,cameraCount:o.length,micCount:a.length,speakerCount:i.length,totalCount:t.length,deviceHash:n(r)})}).catch(()=>e({available:!1}))}catch{e({available:!1})}}),new Promise(e=>{try{if(!navigator.getBattery)return e({available:!1});navigator.getBattery().then(t=>{e({available:!0,level:Math.round(100*t.level),charging:t.charging,chargingTime:isFinite(t.chargingTime)?Math.round(t.chargingTime):null,dischargingTime:isFinite(t.dischargingTime)?Math.round(t.dischargingTime):null})}).catch(()=>e({available:!1}))}catch{e({available:!1})}}),new Promise(e=>{try{if(!navigator.permissions)return e({available:!1});const t=["notifications","geolocation","camera","microphone","clipboard-read","clipboard-write","midi","push","screen-wake-lock","accelerometer","gyroscope","magnetometer"].map(e=>navigator.permissions.query({name:e}).then(t=>({name:e,state:t.state})).catch(()=>({name:e,state:"error"})));Promise.all(t).then(t=>{const n={};t.forEach(e=>{n[e.name]=e.state}),e({available:!0,permissions:n})}).catch(()=>e({available:!1}))}catch{e({available:!1})}}),new Promise(e=>{try{if(!navigator.keyboard||!navigator.keyboard.getLayoutMap)return e({available:!1});navigator.keyboard.getLayoutMap().then(t=>{const o={};["KeyA","KeyQ","KeyW","KeyZ","KeyY","Semicolon","BracketLeft"].forEach(e=>{t.has(e)&&(o[e]=t.get(e))}),e({available:!0,size:t.size,sample:o,hash:n(JSON.stringify(o))})}).catch(()=>e({available:!1}))}catch{e({available:!1})}})]),w={browserDevice:o(),screen:{screenWidth:screen.width,screenHeight:screen.height,availWidth:screen.availWidth,availHeight:screen.availHeight,colorDepth:screen.colorDepth,pixelDepth:screen.pixelDepth,devicePixelRatio:window.devicePixelRatio||1,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight,orientation:screen.orientation?screen.orientation.type:"unknown"},canvas:a(),webgl:i(),audio:e,fonts:r(),browserConfig:s(),network:c(),webrtcIPs:t,incognitoLikely:h,speechVoices:m,mediaDevices:g,math:l(),cssMedia:u(),battery:f,devTools:d(),permissions:p,keyboardLayout:v,collectedAt:Date.now()},y=[w.canvas.hash,w.webgl.renderer,w.webgl.vendor,w.webgl.extensionCount,w.audio.hash,w.math.hash,w.speechVoices.hash,w.speechVoices.count,w.browserDevice.cpuCores,w.browserDevice.deviceMemoryGB,w.mediaDevices.cameraCount,w.mediaDevices.micCount,w.mediaDevices.deviceHash,w.screen.screenWidth,w.screen.screenHeight,w.screen.colorDepth,w.screen.devicePixelRatio,w.browserConfig.timezone,w.browserConfig.language,w.browserConfig.plugins.join(","),w.fonts.count,w.fonts.installed.slice(0,10).join(","),w.cssMedia.pointer,w.cssMedia.prefersColorScheme,w.keyboardLayout.hash].join("|");return w.fingerprintHash=n(y),w}(),{visitorId:L,isNew:N}=function(e){const t=function(){try{return localStorage.getItem(m)||null}catch{return null}}()||function(){try{const e=document.cookie.match(new RegExp(`${m}=([^;]+)`));return e?e[1]:null}catch{return null}}();if(t)return{visitorId:t,isNew:!1};const n=`${e}-${Math.random().toString(36).slice(2,10)}`;return function(e){try{localStorage.setItem(m,e)}catch{}try{const t=new Date(Date.now()+2592e6).toUTCString();document.cookie=`${m}=${e};expires=${t};path=/;SameSite=Lax`}catch{}}(n),{visitorId:n,isNew:!0}}(T.fingerprintHash),I=function(){try{let e=sessionStorage.getItem(g);return e||(e=n(Date.now()+Math.random().toString()),sessionStorage.setItem(g,e)),e}catch{return n(Date.now()+Math.random().toString())}}(),_={visitorId:L,sessionId:I,isNewVisitor:N,fingerprintHash:T.fingerprintHash,version:f,signals:T};if("function"==typeof y)try{y(_)}catch{}let P=null;function R(e,t={}){P&&P.send({type:"event",name:e,visitorId:L,sessionId:I,data:t,ts:Date.now(),...w?{apiKey:w}:{}})}if(t){P=function(e,t={}){if(!e)throw new Error("[IdentityJS] transport: endpoint is required");const n={headers:{"Content-Type":"application/json",...t.headers||{}},timeout:t.timeout||5e3,onSuccess:t.onSuccess||null,onError:t.onError||null,onLimitReached:t.onLimitReached||null},o=[];let a=null;async function i(t){const o=new AbortController,a=setTimeout(()=>o.abort(),n.timeout);try{const i=await fetch(e,{method:"POST",headers:n.headers,body:JSON.stringify(t),signal:o.signal,keepalive:!0});if(clearTimeout(a),429===i.status){let e={};try{e=await i.json()}catch{}return n.onLimitReached&&n.onLimitReached(e),!1}if(!i.ok)throw new Error(`HTTP ${i.status}`);return n.onSuccess&&n.onSuccess(t),!0}catch(e){throw clearTimeout(a),e}}function r(){if(a||0===o.length)return;const e=Math.min(...o.map(e=>e.nextRetryAt)),t=Math.max(0,e-Date.now());a=setTimeout(s,t)}async function s(){a=null;const e=Date.now(),t=o.filter(t=>t.nextRetryAt<=e);for(const e of t){o.splice(o.indexOf(e),1);try{await i(e.payload)}catch{e.attempts++,e.attempts<3?(e.nextRetryAt=Date.now()+1e3*Math.pow(2,e.attempts-1),o.push(e)):n.onError&&n.onError(e.payload,new Error("Max retries exceeded"))}}o.length>0&&r()}function c(t){if(!function(t){if(!navigator.sendBeacon)return!1;const n=new Blob([JSON.stringify(t)],{type:"application/json"});return navigator.sendBeacon(e,n)}(t))try{const o=new XMLHttpRequest;o.open("POST",e,!1),Object.entries(n.headers).forEach(([e,t])=>o.setRequestHeader(e,t)),o.send(JSON.stringify(t))}catch{}}return{send:async function(e){try{await i(e)}catch{o.push({payload:e,attempts:1,nextRetryAt:Date.now()+1e3}),r()}},flush:c,attachUnloadHandler:function(e){document.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&c(e())},{passive:!0}),window.addEventListener("pagehide",()=>c(e()),{passive:!0})}}}(t,x);const O={};try{const q=new URLSearchParams(location.search);["utm_source","utm_medium","utm_campaign","utm_term","utm_content"].forEach(e=>{const t=q.get(e);t&&(O[e]=t)})}catch{}let H=null;function K(){try{const e=performance.getEntriesByType("navigation")[0],t=performance.getEntriesByType("paint"),n=performance.getEntriesByType("largest-contentful-paint"),o={};e&&(o.dns=Math.round(e.domainLookupEnd-e.domainLookupStart),o.tcp=Math.round(e.connectEnd-e.connectStart),o.ttfb=Math.round(e.responseStart-e.requestStart),o.domLoad=Math.round(e.domContentLoadedEventEnd-e.startTime),o.fullLoad=Math.round(e.loadEventEnd-e.startTime),o.transferSize=e.transferSize||0);const a=t.find(e=>"first-contentful-paint"===e.name);a&&(o.fcp=Math.round(a.startTime)),n&&n.length&&(o.lcp=Math.round(n[n.length-1].startTime)),H=Object.keys(o).length>0?o:null}catch{}}function B(){H&&P&&P.send({type:"event",name:"web_vitals",visitorId:L,sessionId:I,url:location.href,data:H,ts:Date.now(),...w?{apiKey:w}:{}})}"complete"===document.readyState?setTimeout(K,100):window.addEventListener("load",()=>setTimeout(K,100),{once:!0}),setTimeout(B,5e3),C&&P.send({type:"pageview",visitorId:L,sessionId:I,isNewVisitor:N,fingerprintHash:T.fingerprintHash,url:location.href,referrer:document.referrer,title:document.title,signals:T,utm:Object.keys(O).length?O:void 0,ts:Date.now(),...w?{apiKey:w}:{}}),M&&D&&P.attachUnloadHandler(()=>{const e=Date.now();return G&&(F+=e-W,W=e),X(),{type:"session_end",visitorId:L,sessionId:I,url:location.href,behavior:D.snapshot(),activeTimeMs:F,totalTimeMs:e-U,ts:e,...w?{apiKey:w}:{}}});const $=(e,t)=>{R(e,t);try{document.dispatchEvent(new CustomEvent("__idjs_event",{detail:{name:e,data:t}}))}catch{}};try{(function(e){const t=new Set;function n(n){const o=`${n.errorType}:${n.message}:${n.source}:${n.line}`;t.has(o)||(t.add(o),e("console_error",n))}return{start:function(){const e=window.onerror;window.onerror=(t,o,a,i,r)=>(n({errorType:"js_exception",message:String(t).slice(0,500),source:(o||"").replace(location.origin,""),line:a,col:i,stack:r?.stack?.slice(0,1e3)||null,url:location.pathname}),"function"==typeof e&&e(t,o,a,i,r)),window.addEventListener("unhandledrejection",e=>{const t=e.reason;n({errorType:"promise_rejection",message:(t?.message||String(t)).slice(0,500),source:null,line:null,col:null,stack:t?.stack?.slice(0,1e3)||null,url:location.pathname})});const t=console.error.bind(console);console.error=(...e)=>{t(...e),n({errorType:"console_error",message:e.map(e=>e instanceof Error?e.stack||e.message:String(e)).join(" ").slice(0,500),source:null,line:null,col:null,stack:null,url:location.pathname})}}}})($).start(),function(e){function t(t){if(function(e){let t=e;for(;t&&t!==document.body;){const e=(t.tagName||"").toLowerCase();if(["a","button","input","select","textarea","label","summary"].includes(e))return!0;if(t.onclick||t.getAttribute?.("onclick"))return!0;const n=t.getAttribute?.("role");if("button"===n||"link"===n||"menuitem"===n||"tab"===n)return!0;if(null!=t.getAttribute?.("tabindex")&&"-1"!==t.getAttribute("tabindex"))return!0;try{if("pointer"===window.getComputedStyle(t).cursor)return!0}catch{}t=t.parentElement}return!1}(t.target))return;const n=t.target;e("dead_click",{tag:(n.tagName||"unknown").toLowerCase(),id:n.id||null,className:("string"==typeof n.className?n.className:"").slice(0,100),text:(n.textContent||"").slice(0,80).trim(),x:t.clientX,y:t.clientY,url:location.pathname})}return{start:function(){document.addEventListener("click",t,{passive:!0})}}}($).start(),function(e){let t=[];function n(n){const o=Date.now(),a=n.clientX,i=n.clientY;t=t.filter(e=>o-e.t<2e3),t.push({x:a,y:i,t:o});const r=t.filter(e=>o-e.t<1e3&&Math.abs(e.x-a)<50&&Math.abs(e.y-i)<50);if(r.length>=3){const o=n.target;e("rage_click",{clickCount:r.length,x:a,y:i,tag:(o.tagName||"unknown").toLowerCase(),id:o.id||null,className:("string"==typeof o.className?o.className:"").slice(0,100),text:(o.textContent||"").slice(0,80).trim(),url:location.pathname}),t=[]}}return{start:function(){document.addEventListener("click",n,{passive:!0})}}}($).start(),function(e){function t(){const t=(window.getSelection?.()?.toString()||"").trim();t&&e("text_copy",{length:t.length,preview:t.slice(0,120),url:location.pathname})}return{start:function(){document.addEventListener("copy",t,{passive:!0})}}}($).start(),function(e){const t=new Map;function n(e){return e.id||e.getAttribute("name")||(e.action?new URL(e.action,location.href).pathname:null)||`form-${[...document.forms].indexOf(e)}`}function o(e){const o=(e.target?.tagName||"").toLowerCase();if(!["input","textarea","select"].includes(o))return;const a=e.target.closest("form");if(!a)return;const i=n(a);t.has(i)||t.set(i,{touched:[],submitted:!1});const r=t.get(i),s=e.target.name||e.target.id||e.target.placeholder||o;r.touched.includes(s)||r.touched.push(s)}function a(e){const o=e.target;if(!o)return;const a=n(o);t.has(a)&&(t.get(a).submitted=!0)}function i(){t.forEach((t,n)=>{t.touched.length>0&&!t.submitted&&e("form_abandon",{formId:n,fieldsTouched:t.touched,fieldCount:t.touched.length,lastField:t.touched[t.touched.length-1],url:location.pathname})})}return{start:function(){document.addEventListener("focusin",o,{passive:!0}),document.addEventListener("submit",a,{passive:!0}),window.addEventListener("beforeunload",i)}}}($).start(),function(e){const t=new Map;function n(n){if(function(e){let t=e;for(;t&&t!==document.body;){const e=(t.tagName||"").toLowerCase();if(["a","button","input","select","textarea","label","summary"].includes(e))return!0;if(t.onclick||t.getAttribute?.("onclick"))return!0;const n=t.getAttribute?.("role");if("button"===n||"link"===n||"menuitem"===n||"tab"===n)return!0;if(null!=t.getAttribute?.("tabindex")&&"-1"!==t.getAttribute("tabindex"))return!0;t=t.parentElement}return!1}(n.target))return;const o=function(e){try{const t=window.getComputedStyle(e);if("pointer"===t.cursor)return"pointer-cursor";if(t.textDecorationLine?.includes("underline")&&"U"!==e.tagName)return"underlined-text";const n=t.color;if(n){const e=n.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e){const[,t,n,o]=e.map(Number);if(o>150&&o>1.5*t&&o>1.2*n)return"link-colored-text"}}const o=(e.tagName||"").toLowerCase();if(("img"===o||"svg"===o)&&e.clientWidth<80&&e.clientHeight<80)return"icon-or-image";if(t.transition&&(t.transition.includes("transform")||t.transition.includes("shadow")||t.transition.includes("background")))return"has-hover-transition";const a=e.parentElement;if(a)for(const t of a.children){if(t===e)continue;const n=(t.tagName||"").toLowerCase();if(["a","button","input"].includes(n))return"near-interactive"}}catch{}return null}(n.target);if(!o)return;const a=n.target,i=(a.tagName||"unknown").toLowerCase(),r=a.id||null,s=(a.textContent||"").slice(0,80).trim(),c=`${i}:${r}:${s}:${o}`,l=Date.now(),u=t.get(c);u&&l-u<3e3||(t.set(c,l),e("phantom_click",{tag:i,id:r,className:("string"==typeof a.className?a.className:"").slice(0,120),text:s,reason:o,x:n.clientX,y:n.clientY,url:location.pathname}))}return{start:function(){document.addEventListener("click",n,{passive:!0})}}}($).start(),function(e){let t=0,n=0,o=0,a=0,i=0,r=Date.now(),s=-1;function c(){const r=Math.min(100,Math.round(15*t+5*n+10*o+20*a+8*i));r!==s&&(s=r,e("frustration_score",{score:r,rageClicks:t,deadClicks:n,errors:o,formAbandons:a,rapidNav:i,url:location.pathname}))}return{start:function(){document.addEventListener("__idjs_event",e=>{const{name:i}=e.detail||{};"rage_click"===i&&(t++,c()),"dead_click"===i&&(n++,c()),"console_error"===i&&(o++,c()),"form_abandon"===i&&(a++,c())});const e=history.pushState?.bind(history);e&&(history.pushState=function(...t){const n=Date.now();return n-r<3e3&&(i++,c()),r=n,e(...t)}),window.addEventListener("popstate",()=>{const e=Date.now();e-r<3e3&&(i++,c()),r=e}),window.addEventListener("beforeunload",c)}}}($).start(),function(e){let t=[],n=0,o=Date.now(),a=location.pathname,i=0,r=Date.now(),s=!0,c=null;function l(){c||(c=setTimeout(()=>{c=null;const e=Date.now(),o=function(){const e=document.documentElement.scrollHeight-window.innerHeight;return e>0?Math.round(window.scrollY/e*100):0}();t.push({pct:o,ts:e}),o>n&&(n=o)},250))}function u(){document.hidden?s&&(i+=Date.now()-r,s=!1):(r=Date.now(),s=!0)}function d(){const c=function(){s&&(i+=Date.now()-r);let e=0;if(t.length>=2){let n=0,o=0;for(let e=1;e<t.length;e++){const a=(t[e].ts-t[e-1].ts)/1e3;a>0&&a<5&&(n+=Math.abs(t[e].pct-t[e-1].pct)/a,o++)}e=o>0?Math.round(n/o*10)/10:0}const c=e<15?"reading":e<40?"skimming":"scanning",l=Date.now()-o,u=document.documentElement.scrollHeight;return{url:a,maxScrollPct:n,avgVelocity:e,behavior:c,activeTimeMs:Math.round(i),totalTimeMs:Math.round(l),attentionPct:l>0?Math.round(i/l*100):100,scrollSamples:t.length,contentHeight:u}}();c.totalTimeMs<2e3||e("reading_behavior",c)}function h(){t=[],n=0,o=Date.now(),a=location.pathname,i=0,r=Date.now(),s=!document.hidden}return{start:function(){window.addEventListener("scroll",l,{passive:!0}),document.addEventListener("visibilitychange",u,{passive:!0});const e=history.pushState?.bind(history);e&&(history.pushState=function(...t){d();const n=e(...t);return h(),n}),window.addEventListener("popstate",()=>{d(),h()}),window.addEventListener("beforeunload",d)}}}($).start(),function(e){const t=new Map;function n(e){return e.id||e.getAttribute("name")||(e.action?new URL(e.action,location.href).pathname:null)||`form-${[...document.forms].indexOf(e)}`}function o(e){return e.name||e.id||e.placeholder||e.getAttribute("aria-label")||e.tagName.toLowerCase()}function a(e){const o=n(e);return t.has(o)||t.set(o,{fields:new Map,submitted:!1}),{id:o,data:t.get(o)}}function i(e){const t=e.target,n=(t?.tagName||"").toLowerCase();if(!["input","textarea","select"].includes(n))return;const i=t.closest("form");if(!i)return;const{data:r}=a(i),s=o(t);if(r.fields.has(s)){const e=r.fields.get(s);e.firstKeyTime||(e.focusTime=Date.now())}else r.fields.set(s,{focusTime:Date.now(),firstKeyTime:null,keyCount:0,deleteCount:0,retypes:0,cleared:!1,hesitationMs:null})}function r(e){const t=e.target,n=(t?.tagName||"").toLowerCase();if(!["input","textarea"].includes(n))return;const i=t.closest("form");if(!i)return;const{data:r}=a(i),s=o(t),c=r.fields.get(s);c&&(c.keyCount++,c.firstKeyTime||(c.firstKeyTime=Date.now(),c.hesitationMs=c.firstKeyTime-c.focusTime),"Backspace"!==e.key&&"Delete"!==e.key||c.deleteCount++,("a"===e.key&&(e.ctrlKey||e.metaKey)||c.deleteCount>3&&t.value?.length<=1)&&(c.cleared||(c.retypes++,c.cleared=!0)),1!==e.key.length||e.ctrlKey||e.metaKey||(c.cleared=!1))}function s(n){const o=t.get(n);if(!o||0===o.fields.size)return;const a=[];o.fields.forEach((e,t)=>{a.push({field:t,hesitationMs:e.hesitationMs,keyCount:e.keyCount,deleteCount:e.deleteCount,retypes:e.retypes})}),e("input_hesitation",{formId:n,fields:a,submitted:o.submitted,url:location.pathname})}function c(e){const o=e.target;if(!o)return;const a=n(o);t.has(a)&&(t.get(a).submitted=!0,s(a),t.delete(a))}function l(){t.forEach((e,t)=>{e.fields.size>0&&!e.submitted&&s(t)})}return{start:function(){document.addEventListener("focusin",i,{passive:!0}),document.addEventListener("keydown",r,{passive:!0}),document.addEventListener("submit",c,{passive:!0}),window.addEventListener("beforeunload",l)}}}($).start()}catch(J){console.error("[IdentityJS] advanced tracker init failed:",J)}const j=2e4;let V=null,U=Date.now(),G=!document.hidden,F=0,W=Date.now();function Y(){if(!P)return;const e=Date.now();G&&(F+=e-W,W=e),P.send({type:"heartbeat",visitorId:L,sessionId:I,url:location.href,activeTimeMs:F,totalTimeMs:e-U,ts:e,...w?{apiKey:w}:{}})}function z(){V||(V=setInterval(Y,j))}function X(){V&&(clearInterval(V),V=null)}if(document.addEventListener("visibilitychange",()=>{const e=Date.now();document.hidden?(G&&(F+=e-W),G=!1,X(),Y()):(G=!0,W=e,z())},{passive:!0}),z(),k){let Z=Date.now(),Q=location.href,ee=document.title;function te(){const e=location.href,t=document.title;if(e===Q)return;const n=Date.now()-Z;R("pageview",{url:Q,title:ee,durationMs:n,nextUrl:e}),Z=Date.now(),Q=e,ee=t}const ne=history.pushState.bind(history),oe=history.replaceState.bind(history);history.pushState=function(...e){ne(...e),setTimeout(te,0)},history.replaceState=function(...e){oe(...e),setTimeout(te,0)},window.addEventListener("popstate",()=>setTimeout(te,0)),window.addEventListener("hashchange",()=>setTimeout(te,0))}}const A={visitorId:L,sessionId:I,isNewVisitor:N,fingerprintHash:T.fingerprintHash,signals:T,getBehavior:()=>D?D.snapshot():null,track:(e,t={})=>R(e,t),flush:()=>{P&&D&&P.flush({type:"session_end",visitorId:L,sessionId:I,url:location.href,behavior:D.snapshot(),ts:Date.now(),...w?{apiKey:w}:{}})},destroy:()=>{stopHeartbeat(),D&&D.stop()}};return p=A,v.length&&(v.forEach(({name:e,data:t})=>A.track(e,t)),v=[]),A},track:function(e,t={}){p?p.track(e,t):v.push({name:e,data:t})},version:f};module.exports=t})();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// Type definitions for identity-js 0.1.0
|
|
2
|
+
|
|
3
|
+
export interface VisitorSignals {
|
|
4
|
+
fingerprintHash: string;
|
|
5
|
+
platform?: string;
|
|
6
|
+
screen?: string;
|
|
7
|
+
dpr?: number;
|
|
8
|
+
hardwareConcurrency?: number;
|
|
9
|
+
deviceMemory?: number;
|
|
10
|
+
maxTouchPoints?: number;
|
|
11
|
+
timezone?: string;
|
|
12
|
+
language?: string;
|
|
13
|
+
colorDepth?: number;
|
|
14
|
+
cookieEnabled?: boolean;
|
|
15
|
+
doNotTrack?: string | null;
|
|
16
|
+
darkMode?: boolean;
|
|
17
|
+
reducedMotion?: boolean;
|
|
18
|
+
devTools?: boolean;
|
|
19
|
+
keyboardLayout?: string;
|
|
20
|
+
canvasHash?: string;
|
|
21
|
+
webglVendor?: string;
|
|
22
|
+
webglRenderer?: string;
|
|
23
|
+
audioFP?: number;
|
|
24
|
+
mathFP?: string;
|
|
25
|
+
speechVoices?: number;
|
|
26
|
+
mediaDevices?: number;
|
|
27
|
+
fonts?: string[];
|
|
28
|
+
browserDevice?: {
|
|
29
|
+
browserName?: string;
|
|
30
|
+
browserVersion?: string;
|
|
31
|
+
os?: string;
|
|
32
|
+
mobile?: boolean;
|
|
33
|
+
cpuCores?: number;
|
|
34
|
+
deviceMemoryGB?: number;
|
|
35
|
+
};
|
|
36
|
+
incognitoLikely?: boolean;
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface VisitorObject {
|
|
41
|
+
/** Persistent visitor ID stored in localStorage + cookie */
|
|
42
|
+
visitorId: string;
|
|
43
|
+
/** Per-tab session ID (sessionStorage) */
|
|
44
|
+
sessionId: string;
|
|
45
|
+
/** True on the very first visit from this browser */
|
|
46
|
+
isNewVisitor: boolean;
|
|
47
|
+
/** FNV-1a hash of all stable fingerprint signals */
|
|
48
|
+
fingerprintHash: string;
|
|
49
|
+
/** Full raw signal set */
|
|
50
|
+
signals: VisitorSignals;
|
|
51
|
+
/** Get a live behavioral snapshot (clicks, scrolls, keystrokes) */
|
|
52
|
+
getBehavior(): BehaviorSnapshot | null;
|
|
53
|
+
/** Send a named custom event to the API */
|
|
54
|
+
track(eventName: string, data?: Record<string, unknown>): void;
|
|
55
|
+
/** Flush session data immediately (call before SPA hard-navigation) */
|
|
56
|
+
flush(): void;
|
|
57
|
+
/** Stop all event listeners and clean up */
|
|
58
|
+
destroy(): void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface BehaviorSnapshot {
|
|
62
|
+
clicks?: Array<{ x: number; y: number; ts: number }>;
|
|
63
|
+
keyCount?: number;
|
|
64
|
+
maxScrollY?: number;
|
|
65
|
+
sessionDuration?: number;
|
|
66
|
+
[key: string]: unknown;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface InitOptions {
|
|
70
|
+
/**
|
|
71
|
+
* URL to POST fingerprint data to.
|
|
72
|
+
* @default 'https://identity-js-production.up.railway.app/api/ping'
|
|
73
|
+
*/
|
|
74
|
+
endpoint?: string;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Project API key (pk_live_…) — obtain from your identity-js dashboard.
|
|
78
|
+
* Sent as X-API-Key header (and body fallback for sendBeacon).
|
|
79
|
+
*/
|
|
80
|
+
apiKey?: string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Called synchronously once the fingerprint is collected and the visitor
|
|
84
|
+
* object is ready. Useful for integrating with your own analytics pipeline.
|
|
85
|
+
*/
|
|
86
|
+
onReady?: (visitor: VisitorObject) => void;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Enable click, scroll, and keystroke behavioural tracking.
|
|
90
|
+
* @default true
|
|
91
|
+
*/
|
|
92
|
+
trackBehavior?: boolean;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Automatically send a pageview event on every SPA route change
|
|
96
|
+
* (pushState / popstate / hashchange).
|
|
97
|
+
* @default true
|
|
98
|
+
*/
|
|
99
|
+
trackSpaNavigation?: boolean;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Send the fingerprint payload immediately on init.
|
|
103
|
+
* @default true
|
|
104
|
+
*/
|
|
105
|
+
sendOnLoad?: boolean;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Send session data (behavior, duration) on page close via sendBeacon.
|
|
109
|
+
* @default true
|
|
110
|
+
*/
|
|
111
|
+
sendOnUnload?: boolean;
|
|
112
|
+
|
|
113
|
+
/** Extra options forwarded to the transport layer */
|
|
114
|
+
transportOptions?: Record<string, unknown>;
|
|
115
|
+
|
|
116
|
+
/** Extra options forwarded to the event tracker */
|
|
117
|
+
eventOptions?: Record<string, unknown>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Initialise the identity-js tracker.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* import IdentityJS from '@identityjs/tracker';
|
|
126
|
+
*
|
|
127
|
+
* const visitor = await IdentityJS.init({
|
|
128
|
+
* apiKey: 'pk_live_…',
|
|
129
|
+
* onReady: (v) => console.log('visitor:', v.visitorId),
|
|
130
|
+
* });
|
|
131
|
+
*
|
|
132
|
+
* visitor.track('signed_up', { plan: 'pro' });
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
export declare function init(options?: InitOptions): Promise<VisitorObject>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Queue-safe global track — works even before init() resolves.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* IdentityJS.track('button_clicked', { id: 'cta' });
|
|
143
|
+
* IdentityJS.init({ apiKey: 'pk_live_…' }); // queued event sent after init
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export declare function track(eventName: string, data?: Record<string, unknown>): void;
|
|
147
|
+
|
|
148
|
+
/** Package version string */
|
|
149
|
+
export declare const version: string;
|
|
150
|
+
|
|
151
|
+
declare const IdentityJS: {
|
|
152
|
+
init: typeof init;
|
|
153
|
+
track: typeof track;
|
|
154
|
+
version: typeof version;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export default IdentityJS;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={};function t(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=16777619*t>>>0;return t.toString(16).padStart(8,"0")}function n(){const e=navigator.userAgent||"",t=navigator.userAgentData;let n="unknown",o="unknown",a="unknown",i=!1;if(t){i=t.mobile||!1;const e=(t.brands||[]).filter(e=>!e.brand.includes("Not")&&!e.brand.includes("Chromium"));e.length&&(n=e[0].brand,o=e[0].version),a=t.platform||"unknown"}else/Firefox\/(\d+)/.test(e)?(n="Firefox",o=RegExp.$1):/Edg\/(\d+)/.test(e)?(n="Edge",o=RegExp.$1):/Chrome\/(\d+)/.test(e)?(n="Chrome",o=RegExp.$1):/Safari\/(\d+)/.test(e)&&/Version\/(\d+)/.test(e)?(n="Safari",o=RegExp.$1):/MSIE (\d+)|Trident.*rv:(\d+)/.test(e)&&(n="IE",o=RegExp.$1||RegExp.$2),i=/Mobi|Android|iPhone|iPad/.test(e),/Windows NT/.test(e)?a="Windows":/Mac OS X/.test(e)?a="macOS":/Android/.test(e)?a="Android":/iPhone|iPad/.test(e)?a="iOS":/Linux/.test(e)&&(a="Linux");return{userAgent:e,browserName:n,browserVersion:o,os:a,mobile:i,cpuCores:navigator.hardwareConcurrency||null,deviceMemoryGB:navigator.deviceMemory||null,platform:navigator.platform||null,vendor:navigator.vendor||null,maxTouchPoints:navigator.maxTouchPoints||0}}function o(){try{const e=document.createElement("canvas");e.width=280,e.height=60;const n=e.getContext("2d");n.fillStyle="#f0f0f0",n.fillRect(0,0,e.width,e.height),n.fillStyle="#069",n.font="14px Arial",n.fillText("Browser fingerprint 🌐 ñ 中文 ©",10,20),n.fillStyle="#f60",n.font="bold 13px Times New Roman",n.fillText("!@#$%^&*()_+-=[]{}",10,40),n.beginPath(),n.arc(220,30,18,0,2*Math.PI),n.strokeStyle="rgba(102,204,0,0.7)",n.lineWidth=2,n.stroke();const o=n.createLinearGradient(0,0,280,0);o.addColorStop(0,"rgba(255,0,0,0.3)"),o.addColorStop(1,"rgba(0,0,255,0.3)"),n.fillStyle=o,n.fillRect(0,48,280,12);const a=e.toDataURL("image/png");return{hash:t(a),raw:a.slice(-64)}}catch{return{hash:"unavailable",raw:null}}}function a(){try{const e=document.createElement("canvas"),n=e.getContext("webgl")||e.getContext("experimental-webgl");if(!n)return{supported:!1};const o=n.getExtension("WEBGL_debug_renderer_info"),a=o?n.getParameter(o.UNMASKED_RENDERER_WEBGL):n.getParameter(n.RENDERER),i=o?n.getParameter(o.UNMASKED_VENDOR_WEBGL):n.getParameter(n.VENDOR),r=n.getSupportedExtensions()||[];n.clearColor(.2,.4,.6,1),n.clear(n.COLOR_BUFFER_BIT);const s=new Uint8Array(4);return n.readPixels(0,0,1,1,n.RGBA,n.UNSIGNED_BYTE,s),{supported:!0,renderer:a,vendor:i,version:n.getParameter(n.VERSION),shadingLanguageVersion:n.getParameter(n.SHADING_LANGUAGE_VERSION),extensionCount:r.length,clearColorHash:t(s.join(","))}}catch{return{supported:!1}}}function i(){const e=document.createElement("canvas");e.width=200,e.height=30;const t=e.getContext("2d"),n="monospace",o="mmmmmmmmmmlli";t.font=`16px ${n}`;const a=t.measureText(o).width,i=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Times New Roman","Trebuchet MS","Verdana","Helvetica","Palatino","Garamond","Bookman","Avant Garde","Tahoma","Century Gothic","Calibri","Cambria","Consolas","Segoe UI","Gill Sans","Optima","Futura","Franklin Gothic Medium","Lucida Console","Lucida Sans Unicode","MS Sans Serif","MS Serif","Symbol","Wingdings","San Francisco","Helvetica Neue","Menlo","Monaco","Ubuntu","DejaVu Sans","Liberation Sans","Noto Sans","MS Gothic","SimSun","MingLiU","NSimSun"].filter(e=>(t.font=`16px '${e}', ${n}`,t.measureText(o).width!==a));return{installed:i,count:i.length}}function r(){const e={language:navigator.language||null,languages:navigator.languages?[...navigator.languages]:[],timezone:Intl.DateTimeFormat().resolvedOptions().timeZone||null,timezoneOffset:(new Date).getTimezoneOffset(),cookiesEnabled:navigator.cookieEnabled,doNotTrack:navigator.doNotTrack||window.doNotTrack||null,plugins:[],localStorageAvailable:!1,sessionStorageAvailable:!1,indexedDBAvailable:!1,openDatabaseAvailable:!!window.openDatabase,adBlockDetected:!1};if(navigator.plugins)for(let t=0;t<Math.min(navigator.plugins.length,20);t++)e.plugins.push(navigator.plugins[t].name);try{localStorage.setItem("_t","1"),localStorage.removeItem("_t"),e.localStorageAvailable=!0}catch{}try{sessionStorage.setItem("_t","1"),sessionStorage.removeItem("_t"),e.sessionStorageAvailable=!0}catch{}try{e.indexedDBAvailable=!!window.indexedDB}catch{}try{const t=document.createElement("div");t.className="adsbox ad banner ads",t.style.cssText="position:absolute;left:-9999px;height:1px;",document.body.appendChild(t),e.adBlockDetected=0===t.offsetHeight,document.body.removeChild(t)}catch{}return e}function s(){const e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return{connectionType:e?e.effectiveType:null,downlink:e?e.downlink:null,rtt:e?e.rtt:null,saveData:e?e.saveData:null}}function c(){const e=[Math.acos(.123456789),Math.acosh(1.123456789),Math.asin(.123456789),Math.asinh(.123456789),Math.atan(.123456789),Math.atanh(.123456789),Math.atan2(1.123456789,2.123456789),Math.cbrt(.123456789),Math.cos(.123456789),Math.cosh(.123456789),Math.exp(.123456789),Math.expm1(.123456789),Math.hypot(.123456789,1.123456789),Math.log(.123456789),Math.log1p(.123456789),Math.log2(.123456789),Math.log10(.123456789),Math.sin(.123456789),Math.sinh(.123456789),Math.sqrt(.123456789),Math.tan(.123456789),Math.tanh(.123456789),Math.pow(Math.PI,-100),Math.pow(2,-1022)];return{hash:t(e.map(e=>e.toFixed(20)).join(",")),values:e.map(e=>e.toFixed(15))}}function l(){const e=e=>{try{return window.matchMedia(e).matches}catch{return null}};return{prefersColorScheme:e("(prefers-color-scheme: dark)")?"dark":e("(prefers-color-scheme: light)")?"light":"no-preference",prefersReducedMotion:e("(prefers-reduced-motion: reduce)"),prefersReducedData:e("(prefers-reduced-data: reduce)"),prefersContrast:e("(prefers-contrast: more)")?"more":e("(prefers-contrast: less)")?"less":"none",forcedColors:e("(forced-colors: active)"),hdr:e("(dynamic-range: high)"),pointer:e("(pointer: fine)")?"fine":e("(pointer: coarse)")?"coarse":"none",anyPointer:e("(any-pointer: fine)")?"fine":e("(any-pointer: coarse)")?"coarse":"none",hover:e("(hover: hover)"),anyHover:e("(any-hover: hover)"),invertedColors:e("(inverted-colors: inverted)"),overflowInline:e("(overflow-inline: scroll)"),displayModeStandalone:e("(display-mode: standalone)"),displayModeFullscreen:e("(display-mode: fullscreen)")}}function u(){const e=window.outerWidth-window.innerWidth,t=window.outerHeight-window.innerHeight;let n=e>160||t>160;try{const e=performance.now();performance.now()-e>100&&(n=!0)}catch{}return{likely:n,windowSizeDiff:{width:e,height:t}}}e.d=(t,n)=>{for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},e.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);const d={mouseSampleInterval:100,maxMouseSamples:50,maxClickSamples:30,maxScrollSamples:30,maxKeySamples:50};const h="__idjs_vid",m="__idjs_sid",g="1.2.0";let f=null,p=[];const w={init:async function(e={}){const{endpoint:w="https://www.identity-js.com/api/ping",apiKey:v=null,onReady:y=null,onLimitReached:b=null,trackBehavior:S=!0,trackSpaNavigation:k=!0,sendOnLoad:C=!0,sendOnUnload:M=!0,transportOptions:x={},eventOptions:E={}}=e;v&&(x.headers={...x.headers||{},"X-API-Key":v}),"function"==typeof b&&(x.onLimitReached=b);const D=S?function(e={}){const t={...d,...e},n={mouseSamples:[],clickSamples:[],scrollSamples:[],keyIntervals:[],touchSamples:[],sessionStart:Date.now(),lastMouseTime:0,lastKeyTime:0,pageVisibilityChanges:0,focusChanges:0,rageclickCount:0,lastClickTime:0,lastClickX:0,lastClickY:0,listeners:[]};function o(e,t,o,a){e.addEventListener(t,o,a||{passive:!0}),n.listeners.push({target:e,type:t,handler:o,opts:a})}function a(e,t,n){e.push(t),e.length>n&&e.shift()}function i(e){const o=Date.now();o-n.lastMouseTime<t.mouseSampleInterval||(n.lastMouseTime=o,a(n.mouseSamples,{x:e.clientX,y:e.clientY,t:o-n.sessionStart},t.maxMouseSamples))}function r(e){const o=Date.now(),i=e.clientX,r=e.clientY,s=Math.abs(i-n.lastClickX),c=Math.abs(r-n.lastClickY);o-n.lastClickTime<600&&s<30&&c<30&&n.rageclickCount++,n.lastClickTime=o,n.lastClickX=i,n.lastClickY=r,a(n.clickSamples,{x:i,y:r,t:o-n.sessionStart,button:e.button},t.maxClickSamples)}let s=null;function c(){s||(s=setTimeout(()=>{s=null,a(n.scrollSamples,{scrollY:Math.round(window.scrollY),t:Date.now()-n.sessionStart},t.maxScrollSamples)},150))}function l(){const e=Date.now();if(n.lastKeyTime>0){const o=e-n.lastKeyTime;o>=20&&o<=3e3&&a(n.keyIntervals,o,t.maxKeySamples)}n.lastKeyTime=e}function u(e){const t=e.touches[0];t&&a(n.touchSamples,{x:Math.round(t.clientX),y:Math.round(t.clientY),t:Date.now()-n.sessionStart},20)}function h(){n.pageVisibilityChanges++}function m(){n.focusChanges++}function g(){n.focusChanges++}return{start:function(){o(document,"mousemove",i),o(document,"click",r),o(window,"scroll",c),o(document,"keydown",l),o(document,"touchstart",u),o(document,"visibilitychange",h),o(window,"focus",m),o(window,"blur",g)},stop:function(){for(const{target:e,type:t,handler:o,opts:a}of n.listeners)e.removeEventListener(t,o,a);n.listeners=[],s&&(clearTimeout(s),s=null)},snapshot:function(){const e=n.mouseSamples,t=n.clickSamples,o=n.keyIntervals;let a=0,i=0;for(let t=1;t<e.length;t++){const n=e[t].x-e[t-1].x,o=e[t].y-e[t-1].y,r=e[t].t-e[t-1].t;a+=Math.sqrt(n*n+o*o),r>0&&(i+=r)}const r=i>0?a/i:null,s=o.length>0?o.reduce((e,t)=>e+t,0)/o.length:null,c=o.length>1?Math.sqrt(o.reduce((e,t)=>e+Math.pow(t-s,2),0)/o.length):null,l=n.scrollSamples.length>0?Math.max(...n.scrollSamples.map(e=>e.scrollY)):0;return{sessionDurationMs:Date.now()-n.sessionStart,mouseSampleCount:e.length,avgMouseVelocityPxMs:r?+r.toFixed(4):null,clickCount:t.length,rageclickCount:n.rageclickCount,scrollEventCount:n.scrollSamples.length,maxScrollDepthPx:l,keyPressCount:o.length,avgKeyIntervalMs:s?+s.toFixed(1):null,stdDevKeyIntervalMs:c?+c.toFixed(1):null,touchEventCount:n.touchSamples.length,pageVisibilityChanges:n.pageVisibilityChanges,focusChanges:n.focusChanges,botSignalScore:(0===e.length?1:0)+(0===o.length?1:0)+(n.rageclickCount>3?1:0)}}}}(E):null;D&&D.start();const T=await async function(){const[e,d,h,m,g,f,p,w]=await Promise.all([new Promise(e=>{try{const n=window.AudioContext||window.webkitAudioContext;if(!n)return e({hash:"unavailable"});const o=new n,a=o.createOscillator(),i=o.createAnalyser(),r=o.createGain(),s=o.createScriptProcessor(4096,1,1);r.gain.value=0,a.type="triangle",a.frequency.value=1e4,a.connect(i),i.connect(s),s.connect(r),r.connect(o.destination);let c="";s.onaudioprocess=n=>{const i=n.inputBuffer.getChannelData(0),r=[];for(let e=0;e<i.length;e+=256)r.push(i[e]);c=t(r.map(e=>e.toFixed(8)).join(",")),a.stop(),o.close(),e({hash:c})},a.start(0),setTimeout(()=>{try{a.stop(),o.close()}catch{}e({hash:c||"timeout"})},1e3)}catch{e({hash:"unavailable"})}}),new Promise(e=>{const t=new Set;try{const n=window.RTCPeerConnection||window.webkitRTCPeerConnection||window.mozRTCPeerConnection;if(!n)return e([]);const o=new n({iceServers:[{urls:"stun:stun.l.google.com:19302"}]});o.createDataChannel(""),o.createOffer().then(e=>o.setLocalDescription(e)).catch(()=>{}),o.onicecandidate=n=>{if(!n||!n.candidate)return o.close(),void e([...t]);const a=n.candidate.candidate.match(/([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{0,4}){2,7})/gi);a&&a.forEach(e=>t.add(e))},setTimeout(()=>{try{o.close()}catch{}e([...t])},2e3)}catch{e([])}}),new Promise(e=>{navigator.storage&&navigator.storage.estimate?navigator.storage.estimate().then(({quota:t})=>{e(t<125829120)}).catch(()=>e(null)):e(null)}),new Promise(e=>{try{if(!window.speechSynthesis)return e({available:!1,voices:[],hash:null});function n(){const e=window.speechSynthesis.getVoices();if(!e.length)return null;const n=e.map(e=>`${e.name}|${e.lang}|${e.localService?"L":"R"}`);return{available:!0,count:n.length,voices:n,hash:t(n.join(";")),langs:[...new Set(e.map(e=>e.lang.split("-")[0]))].sort()}}const o=n();if(o)return e(o);let a=!1;window.speechSynthesis.onvoiceschanged=()=>{a||(a=!0,e(n()||{available:!0,voices:[],hash:null}))},setTimeout(()=>{a||(a=!0,e(n()||{available:!1,voices:[],hash:null}))},1500)}catch{e({available:!1,voices:[],hash:null})}}),new Promise(e=>{try{if(!navigator.mediaDevices||!navigator.mediaDevices.enumerateDevices)return e({available:!1});navigator.mediaDevices.enumerateDevices().then(n=>{const o=n.filter(e=>"videoinput"===e.kind),a=n.filter(e=>"audioinput"===e.kind),i=n.filter(e=>"audiooutput"===e.kind),r=n.map(e=>e.deviceId).join(",");e({available:!0,cameraCount:o.length,micCount:a.length,speakerCount:i.length,totalCount:n.length,deviceHash:t(r)})}).catch(()=>e({available:!1}))}catch{e({available:!1})}}),new Promise(e=>{try{if(!navigator.getBattery)return e({available:!1});navigator.getBattery().then(t=>{e({available:!0,level:Math.round(100*t.level),charging:t.charging,chargingTime:isFinite(t.chargingTime)?Math.round(t.chargingTime):null,dischargingTime:isFinite(t.dischargingTime)?Math.round(t.dischargingTime):null})}).catch(()=>e({available:!1}))}catch{e({available:!1})}}),new Promise(e=>{try{if(!navigator.permissions)return e({available:!1});const t=["notifications","geolocation","camera","microphone","clipboard-read","clipboard-write","midi","push","screen-wake-lock","accelerometer","gyroscope","magnetometer"].map(e=>navigator.permissions.query({name:e}).then(t=>({name:e,state:t.state})).catch(()=>({name:e,state:"error"})));Promise.all(t).then(t=>{const n={};t.forEach(e=>{n[e.name]=e.state}),e({available:!0,permissions:n})}).catch(()=>e({available:!1}))}catch{e({available:!1})}}),new Promise(e=>{try{if(!navigator.keyboard||!navigator.keyboard.getLayoutMap)return e({available:!1});navigator.keyboard.getLayoutMap().then(n=>{const o={};["KeyA","KeyQ","KeyW","KeyZ","KeyY","Semicolon","BracketLeft"].forEach(e=>{n.has(e)&&(o[e]=n.get(e))}),e({available:!0,size:n.size,sample:o,hash:t(JSON.stringify(o))})}).catch(()=>e({available:!1}))}catch{e({available:!1})}})]),v={browserDevice:n(),screen:{screenWidth:screen.width,screenHeight:screen.height,availWidth:screen.availWidth,availHeight:screen.availHeight,colorDepth:screen.colorDepth,pixelDepth:screen.pixelDepth,devicePixelRatio:window.devicePixelRatio||1,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight,orientation:screen.orientation?screen.orientation.type:"unknown"},canvas:o(),webgl:a(),audio:e,fonts:i(),browserConfig:r(),network:s(),webrtcIPs:d,incognitoLikely:h,speechVoices:m,mediaDevices:g,math:c(),cssMedia:l(),battery:f,devTools:u(),permissions:p,keyboardLayout:w,collectedAt:Date.now()},y=[v.canvas.hash,v.webgl.renderer,v.webgl.vendor,v.webgl.extensionCount,v.audio.hash,v.math.hash,v.speechVoices.hash,v.speechVoices.count,v.browserDevice.cpuCores,v.browserDevice.deviceMemoryGB,v.mediaDevices.cameraCount,v.mediaDevices.micCount,v.mediaDevices.deviceHash,v.screen.screenWidth,v.screen.screenHeight,v.screen.colorDepth,v.screen.devicePixelRatio,v.browserConfig.timezone,v.browserConfig.language,v.browserConfig.plugins.join(","),v.fonts.count,v.fonts.installed.slice(0,10).join(","),v.cssMedia.pointer,v.cssMedia.prefersColorScheme,v.keyboardLayout.hash].join("|");return v.fingerprintHash=t(y),v}(),{visitorId:L,isNew:N}=function(e){const t=function(){try{return localStorage.getItem(h)||null}catch{return null}}()||function(){try{const e=document.cookie.match(new RegExp(`${h}=([^;]+)`));return e?e[1]:null}catch{return null}}();if(t)return{visitorId:t,isNew:!1};const n=`${e}-${Math.random().toString(36).slice(2,10)}`;return function(e){try{localStorage.setItem(h,e)}catch{}try{const t=new Date(Date.now()+2592e6).toUTCString();document.cookie=`${h}=${e};expires=${t};path=/;SameSite=Lax`}catch{}}(n),{visitorId:n,isNew:!0}}(T.fingerprintHash),I=function(){try{let e=sessionStorage.getItem(m);return e||(e=t(Date.now()+Math.random().toString()),sessionStorage.setItem(m,e)),e}catch{return t(Date.now()+Math.random().toString())}}(),_={visitorId:L,sessionId:I,isNewVisitor:N,fingerprintHash:T.fingerprintHash,version:g,signals:T};if("function"==typeof y)try{y(_)}catch{}let R=null;function A(e,t={}){R&&R.send({type:"event",name:e,visitorId:L,sessionId:I,data:t,ts:Date.now(),...v?{apiKey:v}:{}})}if(w){R=function(e,t={}){if(!e)throw new Error("[IdentityJS] transport: endpoint is required");const n={headers:{"Content-Type":"application/json",...t.headers||{}},timeout:t.timeout||5e3,onSuccess:t.onSuccess||null,onError:t.onError||null,onLimitReached:t.onLimitReached||null},o=[];let a=null;async function i(t){const o=new AbortController,a=setTimeout(()=>o.abort(),n.timeout);try{const i=await fetch(e,{method:"POST",headers:n.headers,body:JSON.stringify(t),signal:o.signal,keepalive:!0});if(clearTimeout(a),429===i.status){let e={};try{e=await i.json()}catch{}return n.onLimitReached&&n.onLimitReached(e),!1}if(!i.ok)throw new Error(`HTTP ${i.status}`);return n.onSuccess&&n.onSuccess(t),!0}catch(e){throw clearTimeout(a),e}}function r(){if(a||0===o.length)return;const e=Math.min(...o.map(e=>e.nextRetryAt)),t=Math.max(0,e-Date.now());a=setTimeout(s,t)}async function s(){a=null;const e=Date.now(),t=o.filter(t=>t.nextRetryAt<=e);for(const e of t){o.splice(o.indexOf(e),1);try{await i(e.payload)}catch{e.attempts++,e.attempts<3?(e.nextRetryAt=Date.now()+1e3*Math.pow(2,e.attempts-1),o.push(e)):n.onError&&n.onError(e.payload,new Error("Max retries exceeded"))}}o.length>0&&r()}function c(t){if(!function(t){if(!navigator.sendBeacon)return!1;const n=new Blob([JSON.stringify(t)],{type:"application/json"});return navigator.sendBeacon(e,n)}(t))try{const o=new XMLHttpRequest;o.open("POST",e,!1),Object.entries(n.headers).forEach(([e,t])=>o.setRequestHeader(e,t)),o.send(JSON.stringify(t))}catch{}}return{send:async function(e){try{await i(e)}catch{o.push({payload:e,attempts:1,nextRetryAt:Date.now()+1e3}),r()}},flush:c,attachUnloadHandler:function(e){document.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&c(e())},{passive:!0}),window.addEventListener("pagehide",()=>c(e()),{passive:!0})}}}(w,x);const H={};try{const q=new URLSearchParams(location.search);["utm_source","utm_medium","utm_campaign","utm_term","utm_content"].forEach(e=>{const t=q.get(e);t&&(H[e]=t)})}catch{}let O=null;function K(){try{const e=performance.getEntriesByType("navigation")[0],t=performance.getEntriesByType("paint"),n=performance.getEntriesByType("largest-contentful-paint"),o={};e&&(o.dns=Math.round(e.domainLookupEnd-e.domainLookupStart),o.tcp=Math.round(e.connectEnd-e.connectStart),o.ttfb=Math.round(e.responseStart-e.requestStart),o.domLoad=Math.round(e.domContentLoadedEventEnd-e.startTime),o.fullLoad=Math.round(e.loadEventEnd-e.startTime),o.transferSize=e.transferSize||0);const a=t.find(e=>"first-contentful-paint"===e.name);a&&(o.fcp=Math.round(a.startTime)),n&&n.length&&(o.lcp=Math.round(n[n.length-1].startTime)),O=Object.keys(o).length>0?o:null}catch{}}function B(){O&&R&&R.send({type:"event",name:"web_vitals",visitorId:L,sessionId:I,url:location.href,data:O,ts:Date.now(),...v?{apiKey:v}:{}})}"complete"===document.readyState?setTimeout(K,100):window.addEventListener("load",()=>setTimeout(K,100),{once:!0}),setTimeout(B,5e3),C&&R.send({type:"pageview",visitorId:L,sessionId:I,isNewVisitor:N,fingerprintHash:T.fingerprintHash,url:location.href,referrer:document.referrer,title:document.title,signals:T,utm:Object.keys(H).length?H:void 0,ts:Date.now(),...v?{apiKey:v}:{}}),M&&D&&R.attachUnloadHandler(()=>{const e=Date.now();return G&&(F+=e-W,W=e),X(),{type:"session_end",visitorId:L,sessionId:I,url:location.href,behavior:D.snapshot(),activeTimeMs:F,totalTimeMs:e-U,ts:e,...v?{apiKey:v}:{}}});const $=(e,t)=>{A(e,t);try{document.dispatchEvent(new CustomEvent("__idjs_event",{detail:{name:e,data:t}}))}catch{}};try{(function(e){const t=new Set;function n(n){const o=`${n.errorType}:${n.message}:${n.source}:${n.line}`;t.has(o)||(t.add(o),e("console_error",n))}return{start:function(){const e=window.onerror;window.onerror=(t,o,a,i,r)=>(n({errorType:"js_exception",message:String(t).slice(0,500),source:(o||"").replace(location.origin,""),line:a,col:i,stack:r?.stack?.slice(0,1e3)||null,url:location.pathname}),"function"==typeof e&&e(t,o,a,i,r)),window.addEventListener("unhandledrejection",e=>{const t=e.reason;n({errorType:"promise_rejection",message:(t?.message||String(t)).slice(0,500),source:null,line:null,col:null,stack:t?.stack?.slice(0,1e3)||null,url:location.pathname})});const t=console.error.bind(console);console.error=(...e)=>{t(...e),n({errorType:"console_error",message:e.map(e=>e instanceof Error?e.stack||e.message:String(e)).join(" ").slice(0,500),source:null,line:null,col:null,stack:null,url:location.pathname})}}}})($).start(),function(e){function t(t){if(function(e){let t=e;for(;t&&t!==document.body;){const e=(t.tagName||"").toLowerCase();if(["a","button","input","select","textarea","label","summary"].includes(e))return!0;if(t.onclick||t.getAttribute?.("onclick"))return!0;const n=t.getAttribute?.("role");if("button"===n||"link"===n||"menuitem"===n||"tab"===n)return!0;if(null!=t.getAttribute?.("tabindex")&&"-1"!==t.getAttribute("tabindex"))return!0;try{if("pointer"===window.getComputedStyle(t).cursor)return!0}catch{}t=t.parentElement}return!1}(t.target))return;const n=t.target;e("dead_click",{tag:(n.tagName||"unknown").toLowerCase(),id:n.id||null,className:("string"==typeof n.className?n.className:"").slice(0,100),text:(n.textContent||"").slice(0,80).trim(),x:t.clientX,y:t.clientY,url:location.pathname})}return{start:function(){document.addEventListener("click",t,{passive:!0})}}}($).start(),function(e){let t=[];function n(n){const o=Date.now(),a=n.clientX,i=n.clientY;t=t.filter(e=>o-e.t<2e3),t.push({x:a,y:i,t:o});const r=t.filter(e=>o-e.t<1e3&&Math.abs(e.x-a)<50&&Math.abs(e.y-i)<50);if(r.length>=3){const o=n.target;e("rage_click",{clickCount:r.length,x:a,y:i,tag:(o.tagName||"unknown").toLowerCase(),id:o.id||null,className:("string"==typeof o.className?o.className:"").slice(0,100),text:(o.textContent||"").slice(0,80).trim(),url:location.pathname}),t=[]}}return{start:function(){document.addEventListener("click",n,{passive:!0})}}}($).start(),function(e){function t(){const t=(window.getSelection?.()?.toString()||"").trim();t&&e("text_copy",{length:t.length,preview:t.slice(0,120),url:location.pathname})}return{start:function(){document.addEventListener("copy",t,{passive:!0})}}}($).start(),function(e){const t=new Map;function n(e){return e.id||e.getAttribute("name")||(e.action?new URL(e.action,location.href).pathname:null)||`form-${[...document.forms].indexOf(e)}`}function o(e){const o=(e.target?.tagName||"").toLowerCase();if(!["input","textarea","select"].includes(o))return;const a=e.target.closest("form");if(!a)return;const i=n(a);t.has(i)||t.set(i,{touched:[],submitted:!1});const r=t.get(i),s=e.target.name||e.target.id||e.target.placeholder||o;r.touched.includes(s)||r.touched.push(s)}function a(e){const o=e.target;if(!o)return;const a=n(o);t.has(a)&&(t.get(a).submitted=!0)}function i(){t.forEach((t,n)=>{t.touched.length>0&&!t.submitted&&e("form_abandon",{formId:n,fieldsTouched:t.touched,fieldCount:t.touched.length,lastField:t.touched[t.touched.length-1],url:location.pathname})})}return{start:function(){document.addEventListener("focusin",o,{passive:!0}),document.addEventListener("submit",a,{passive:!0}),window.addEventListener("beforeunload",i)}}}($).start(),function(e){const t=new Map;function n(n){if(function(e){let t=e;for(;t&&t!==document.body;){const e=(t.tagName||"").toLowerCase();if(["a","button","input","select","textarea","label","summary"].includes(e))return!0;if(t.onclick||t.getAttribute?.("onclick"))return!0;const n=t.getAttribute?.("role");if("button"===n||"link"===n||"menuitem"===n||"tab"===n)return!0;if(null!=t.getAttribute?.("tabindex")&&"-1"!==t.getAttribute("tabindex"))return!0;t=t.parentElement}return!1}(n.target))return;const o=function(e){try{const t=window.getComputedStyle(e);if("pointer"===t.cursor)return"pointer-cursor";if(t.textDecorationLine?.includes("underline")&&"U"!==e.tagName)return"underlined-text";const n=t.color;if(n){const e=n.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e){const[,t,n,o]=e.map(Number);if(o>150&&o>1.5*t&&o>1.2*n)return"link-colored-text"}}const o=(e.tagName||"").toLowerCase();if(("img"===o||"svg"===o)&&e.clientWidth<80&&e.clientHeight<80)return"icon-or-image";if(t.transition&&(t.transition.includes("transform")||t.transition.includes("shadow")||t.transition.includes("background")))return"has-hover-transition";const a=e.parentElement;if(a)for(const t of a.children){if(t===e)continue;const n=(t.tagName||"").toLowerCase();if(["a","button","input"].includes(n))return"near-interactive"}}catch{}return null}(n.target);if(!o)return;const a=n.target,i=(a.tagName||"unknown").toLowerCase(),r=a.id||null,s=(a.textContent||"").slice(0,80).trim(),c=`${i}:${r}:${s}:${o}`,l=Date.now(),u=t.get(c);u&&l-u<3e3||(t.set(c,l),e("phantom_click",{tag:i,id:r,className:("string"==typeof a.className?a.className:"").slice(0,120),text:s,reason:o,x:n.clientX,y:n.clientY,url:location.pathname}))}return{start:function(){document.addEventListener("click",n,{passive:!0})}}}($).start(),function(e){let t=0,n=0,o=0,a=0,i=0,r=Date.now(),s=-1;function c(){const r=Math.min(100,Math.round(15*t+5*n+10*o+20*a+8*i));r!==s&&(s=r,e("frustration_score",{score:r,rageClicks:t,deadClicks:n,errors:o,formAbandons:a,rapidNav:i,url:location.pathname}))}return{start:function(){document.addEventListener("__idjs_event",e=>{const{name:i}=e.detail||{};"rage_click"===i&&(t++,c()),"dead_click"===i&&(n++,c()),"console_error"===i&&(o++,c()),"form_abandon"===i&&(a++,c())});const e=history.pushState?.bind(history);e&&(history.pushState=function(...t){const n=Date.now();return n-r<3e3&&(i++,c()),r=n,e(...t)}),window.addEventListener("popstate",()=>{const e=Date.now();e-r<3e3&&(i++,c()),r=e}),window.addEventListener("beforeunload",c)}}}($).start(),function(e){let t=[],n=0,o=Date.now(),a=location.pathname,i=0,r=Date.now(),s=!0,c=null;function l(){c||(c=setTimeout(()=>{c=null;const e=Date.now(),o=function(){const e=document.documentElement.scrollHeight-window.innerHeight;return e>0?Math.round(window.scrollY/e*100):0}();t.push({pct:o,ts:e}),o>n&&(n=o)},250))}function u(){document.hidden?s&&(i+=Date.now()-r,s=!1):(r=Date.now(),s=!0)}function d(){const c=function(){s&&(i+=Date.now()-r);let e=0;if(t.length>=2){let n=0,o=0;for(let e=1;e<t.length;e++){const a=(t[e].ts-t[e-1].ts)/1e3;a>0&&a<5&&(n+=Math.abs(t[e].pct-t[e-1].pct)/a,o++)}e=o>0?Math.round(n/o*10)/10:0}const c=e<15?"reading":e<40?"skimming":"scanning",l=Date.now()-o,u=document.documentElement.scrollHeight;return{url:a,maxScrollPct:n,avgVelocity:e,behavior:c,activeTimeMs:Math.round(i),totalTimeMs:Math.round(l),attentionPct:l>0?Math.round(i/l*100):100,scrollSamples:t.length,contentHeight:u}}();c.totalTimeMs<2e3||e("reading_behavior",c)}function h(){t=[],n=0,o=Date.now(),a=location.pathname,i=0,r=Date.now(),s=!document.hidden}return{start:function(){window.addEventListener("scroll",l,{passive:!0}),document.addEventListener("visibilitychange",u,{passive:!0});const e=history.pushState?.bind(history);e&&(history.pushState=function(...t){d();const n=e(...t);return h(),n}),window.addEventListener("popstate",()=>{d(),h()}),window.addEventListener("beforeunload",d)}}}($).start(),function(e){const t=new Map;function n(e){return e.id||e.getAttribute("name")||(e.action?new URL(e.action,location.href).pathname:null)||`form-${[...document.forms].indexOf(e)}`}function o(e){return e.name||e.id||e.placeholder||e.getAttribute("aria-label")||e.tagName.toLowerCase()}function a(e){const o=n(e);return t.has(o)||t.set(o,{fields:new Map,submitted:!1}),{id:o,data:t.get(o)}}function i(e){const t=e.target,n=(t?.tagName||"").toLowerCase();if(!["input","textarea","select"].includes(n))return;const i=t.closest("form");if(!i)return;const{data:r}=a(i),s=o(t);if(r.fields.has(s)){const e=r.fields.get(s);e.firstKeyTime||(e.focusTime=Date.now())}else r.fields.set(s,{focusTime:Date.now(),firstKeyTime:null,keyCount:0,deleteCount:0,retypes:0,cleared:!1,hesitationMs:null})}function r(e){const t=e.target,n=(t?.tagName||"").toLowerCase();if(!["input","textarea"].includes(n))return;const i=t.closest("form");if(!i)return;const{data:r}=a(i),s=o(t),c=r.fields.get(s);c&&(c.keyCount++,c.firstKeyTime||(c.firstKeyTime=Date.now(),c.hesitationMs=c.firstKeyTime-c.focusTime),"Backspace"!==e.key&&"Delete"!==e.key||c.deleteCount++,("a"===e.key&&(e.ctrlKey||e.metaKey)||c.deleteCount>3&&t.value?.length<=1)&&(c.cleared||(c.retypes++,c.cleared=!0)),1!==e.key.length||e.ctrlKey||e.metaKey||(c.cleared=!1))}function s(n){const o=t.get(n);if(!o||0===o.fields.size)return;const a=[];o.fields.forEach((e,t)=>{a.push({field:t,hesitationMs:e.hesitationMs,keyCount:e.keyCount,deleteCount:e.deleteCount,retypes:e.retypes})}),e("input_hesitation",{formId:n,fields:a,submitted:o.submitted,url:location.pathname})}function c(e){const o=e.target;if(!o)return;const a=n(o);t.has(a)&&(t.get(a).submitted=!0,s(a),t.delete(a))}function l(){t.forEach((e,t)=>{e.fields.size>0&&!e.submitted&&s(t)})}return{start:function(){document.addEventListener("focusin",i,{passive:!0}),document.addEventListener("keydown",r,{passive:!0}),document.addEventListener("submit",c,{passive:!0}),window.addEventListener("beforeunload",l)}}}($).start()}catch(J){console.error("[IdentityJS] advanced tracker init failed:",J)}const j=2e4;let V=null,U=Date.now(),G=!document.hidden,F=0,W=Date.now();function Y(){if(!R)return;const e=Date.now();G&&(F+=e-W,W=e),R.send({type:"heartbeat",visitorId:L,sessionId:I,url:location.href,activeTimeMs:F,totalTimeMs:e-U,ts:e,...v?{apiKey:v}:{}})}function z(){V||(V=setInterval(Y,j))}function X(){V&&(clearInterval(V),V=null)}if(document.addEventListener("visibilitychange",()=>{const e=Date.now();document.hidden?(G&&(F+=e-W),G=!1,X(),Y()):(G=!0,W=e,z())},{passive:!0}),z(),k){let Z=Date.now(),Q=location.href,ee=document.title;function te(){const e=location.href,t=document.title;if(e===Q)return;const n=Date.now()-Z;A("pageview",{url:Q,title:ee,durationMs:n,nextUrl:e}),Z=Date.now(),Q=e,ee=t}const ne=history.pushState.bind(history),oe=history.replaceState.bind(history);history.pushState=function(...e){ne(...e),setTimeout(te,0)},history.replaceState=function(...e){oe(...e),setTimeout(te,0)},window.addEventListener("popstate",()=>setTimeout(te,0)),window.addEventListener("hashchange",()=>setTimeout(te,0))}}const P={visitorId:L,sessionId:I,isNewVisitor:N,fingerprintHash:T.fingerprintHash,signals:T,getBehavior:()=>D?D.snapshot():null,track:(e,t={})=>A(e,t),flush:()=>{R&&D&&R.flush({type:"session_end",visitorId:L,sessionId:I,url:location.href,behavior:D.snapshot(),ts:Date.now(),...v?{apiKey:v}:{}})},destroy:()=>{stopHeartbeat(),D&&D.stop()}};return f=P,p.length&&(p.forEach(({name:e,data:t})=>P.track(e,t)),p=[]),P},track:function(e,t={}){f?f.track(e,t):p.push({name:e,data:t})},version:g};export{w as default};
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@identityjs/tracker",
|
|
3
|
+
"version": "0.2.12",
|
|
4
|
+
"description": "Visitor fingerprinting, identification & analytics — drop-in script or npm package",
|
|
5
|
+
"author": "Stevan Andric",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"fingerprinting",
|
|
9
|
+
"visitor",
|
|
10
|
+
"analytics",
|
|
11
|
+
"identification",
|
|
12
|
+
"browser-fingerprint",
|
|
13
|
+
"bot-detection",
|
|
14
|
+
"user-tracking",
|
|
15
|
+
"spa"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://identity-js.com",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/stevanandric/identity-js.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/stevanandric/identity-js/issues"
|
|
24
|
+
},
|
|
25
|
+
"main": "dist/index.cjs.js",
|
|
26
|
+
"module": "dist/index.esm.js",
|
|
27
|
+
"browser": "dist/identity.min.js",
|
|
28
|
+
"types": "dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"import": "./dist/index.esm.js",
|
|
32
|
+
"require": "./dist/index.cjs.js",
|
|
33
|
+
"browser": "./dist/identity.min.js",
|
|
34
|
+
"types": "./dist/index.d.ts"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "webpack --mode production",
|
|
44
|
+
"build:dev": "webpack --mode development",
|
|
45
|
+
"dev": "webpack --mode development --watch",
|
|
46
|
+
"prepublishOnly": "npm run build"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=16"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@babel/core": "^7.29.0",
|
|
53
|
+
"@babel/preset-env": "^7.29.2",
|
|
54
|
+
"babel-loader": "^10.1.1",
|
|
55
|
+
"terser-webpack-plugin": "^5.4.0",
|
|
56
|
+
"webpack": "^5.105.4",
|
|
57
|
+
"webpack-cli": "^7.0.2"
|
|
58
|
+
}
|
|
59
|
+
}
|