@privatrak/tracker 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -0
- package/README.md +48 -15
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,109 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
## [1.2.0] - 2026-10-05
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- The Element Picker shows feature rules with an "or" group ("any of"
|
|
20
|
+
conditions) on the page like any other rule. Their button in **Show rules and
|
|
21
|
+
tags on this page** says **View** instead of **Edit** and opens the rule
|
|
22
|
+
read-only, because such a rule can only be edited in the dashboard.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- The Element Picker creates a rule in one step. Pick an element, give it a
|
|
27
|
+
name and press **Create rule**: the rule is saved right away, and **Undo**
|
|
28
|
+
takes it back for a few seconds. The separate step for choosing conditions
|
|
29
|
+
is gone. To count only some of the matching elements, open **Narrow it
|
|
30
|
+
down** and limit the rule to the element's text or to the current page. The
|
|
31
|
+
picker shows how many elements on the page the rule matches ("Matches 3
|
|
32
|
+
elements on this page") as you go.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- The Element Picker understands pages with IDs in the address. On a page like
|
|
37
|
+
`/orders/123` it limits a rule to `/orders/:id`, the address the tracker
|
|
38
|
+
records, and it shows rules saved for `/orders/:id` on every such page.
|
|
39
|
+
Before, a rule limited to the page never counted anything there.
|
|
40
|
+
|
|
41
|
+
- Element Picker labels no longer cover each other. When several rules or tags
|
|
42
|
+
name elements that sit side by side, or the same element, each label finds
|
|
43
|
+
its own spot next to its element and stays inside the window.
|
|
44
|
+
|
|
45
|
+
- A trait longer than 64 characters, or more than 50 traits, no longer loses
|
|
46
|
+
the events they were sent with. The tracker now leaves out only the traits
|
|
47
|
+
over the limits (and empty ones, or values that are not text), keeps the
|
|
48
|
+
first 50, and names what it left out in a warning in the browser console.
|
|
49
|
+
Before, the whole batch of events was refused without a message.
|
|
50
|
+
|
|
51
|
+
- When `init()` is called without an `apiKey`, the console message now also
|
|
52
|
+
says that later `init()` calls are ignored. A later `init()` call, which is
|
|
53
|
+
ignored, now says in its warning when the first call had no key and tracking
|
|
54
|
+
is therefore off for the page load.
|
|
55
|
+
|
|
56
|
+
## [1.1.1] - 2026-10-04
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- The exact selector (`element_selector`) no longer records the position of
|
|
61
|
+
`<body>`'s direct children, so it no longer changes with what else sits in
|
|
62
|
+
`<body>`, such as browser extensions, menus, dialogs and toasts:
|
|
63
|
+
`body > div:nth-of-type(3) > aside` is now `body > div > aside`. Selectors
|
|
64
|
+
longer than 200 characters are cut from the front after that position is
|
|
65
|
+
dropped. The generalized selector (`element_selector_generalized`) is now
|
|
66
|
+
always sent, also when it is the same as the exact one.
|
|
67
|
+
|
|
68
|
+
- Clicks on buttons that open a menu as they are pressed, such as the "⋯"
|
|
69
|
+
actions button of a shadcn or Radix `DropdownMenu`, a `Select`, or a modal
|
|
70
|
+
`Popover`, are now counted. Those widgets make the browser send the click to
|
|
71
|
+
the page itself rather than the button, so they were missing from click
|
|
72
|
+
counts, features and click problems. A press dragged off the button and
|
|
73
|
+
released elsewhere still does not count.
|
|
74
|
+
|
|
75
|
+
- A page whose events briefly hit the API's rate limit, which is shared by
|
|
76
|
+
everyone behind the same office or mobile network address, no longer stops
|
|
77
|
+
tracking for the rest of the visit and loses what it had collected. It now
|
|
78
|
+
keeps the events and tries again after a short wait, 1 to 2 seconds at
|
|
79
|
+
first and growing to at most a minute while the API keeps refusing, then
|
|
80
|
+
sends them once the API accepts them again. That includes events sent as
|
|
81
|
+
the visitor switches tabs or minimises the window, which used to be lost
|
|
82
|
+
if refused. The same applies when the API is briefly unavailable. Tracking
|
|
83
|
+
is still switched off, as before, when a project is over its plan's monthly
|
|
84
|
+
event limit. A batch also never carries more than the 100 events the API
|
|
85
|
+
accepts at once.
|
|
86
|
+
|
|
87
|
+
- Links in single-page apps are now checked for dead and slow clicks. Next.js
|
|
88
|
+
`<Link>`, React Router, Vue Router, SvelteKit and similar routers render an
|
|
89
|
+
ordinary link but change the view themselves instead of letting the browser
|
|
90
|
+
load a new page, and every click on such a link used to be taken for leaving
|
|
91
|
+
the page, so it could never be dead or slow. Now a link is only treated that
|
|
92
|
+
way when the browser actually follows it. A router link whose new address
|
|
93
|
+
appears more than a second after the click sends a `slow_click`, and one
|
|
94
|
+
that does nothing at all sends a `dead_click`. Only the new address, or a
|
|
95
|
+
change inside the link itself such as a loading spinner, counts as the
|
|
96
|
+
link's answer: a menu or a mobile sidebar closing around the link while the
|
|
97
|
+
visitor waits for the page does not hide the wait, and a link in a menu
|
|
98
|
+
that closes and never leads anywhere is a `dead_click`. Routers built on
|
|
99
|
+
the browser's Navigation API, which change the address at once, are judged
|
|
100
|
+
by when the new view finishes loading. Links the browser
|
|
101
|
+
follows, including ones opened in a new tab or with a modifier key and
|
|
102
|
+
downloads, are unchanged and are never dead or slow, and so is a link to the
|
|
103
|
+
page that is already open, such as the current item in a navigation menu,
|
|
104
|
+
where seeing nothing new is what the visitor expects.
|
|
105
|
+
|
|
106
|
+
- Page views are now recorded for single-page apps whose router is built on
|
|
107
|
+
the browser's Navigation API, which change the address without
|
|
108
|
+
`history.pushState`. They used to record only the first page. Each change of
|
|
109
|
+
address still counts once, including Back and Forward and routers that use
|
|
110
|
+
`pushState`.
|
|
111
|
+
|
|
112
|
+
- Changes in the page's `<head>` that visitors never see (scripts, the tab's
|
|
113
|
+
title, metadata, icons, preloads) no longer count as a click doing
|
|
114
|
+
something, so a click whose only effect was one of those is now a
|
|
115
|
+
`dead_click`, as it should be. Stylesheets still count: a theme switch that
|
|
116
|
+
swaps one changes the whole page.
|
|
117
|
+
|
|
15
118
|
## [1.1.0] - 2026-10-03
|
|
16
119
|
|
|
17
120
|
### Added
|
package/README.md
CHANGED
|
@@ -131,7 +131,8 @@ tracking; nothing else is affected.
|
|
|
131
131
|
|
|
132
132
|
Starts the tracker: applies the configuration, records the first pageview, and
|
|
133
133
|
attaches the delegated `click` and `submit` listeners plus the History API
|
|
134
|
-
patches
|
|
134
|
+
patches (and, where the browser has it, a Navigation API listener) that detect
|
|
135
|
+
SPA navigation. Calling it twice logs a warning and does
|
|
135
136
|
nothing the second time. Returns immediately; the first batch leaves once the
|
|
136
137
|
project's settings have loaded.
|
|
137
138
|
|
|
@@ -150,8 +151,9 @@ track('invite-sent', { role: 'editor' });
|
|
|
150
151
|
Labels the current session with developer-chosen strings — `['paid', 'trial']`,
|
|
151
152
|
`['plan:pro']`, a feature-flag bucket — which every subsequent event carries and
|
|
152
153
|
which the dashboard can filter and break down by. Traits are your own labels,
|
|
153
|
-
not personal data: at most 50 per batch, 64 characters each.
|
|
154
|
-
|
|
154
|
+
not personal data: at most 50 per batch, 64 characters each. A trait over
|
|
155
|
+
those limits is left out, with a warning in the browser console, and the rest
|
|
156
|
+
are sent as usual. Call it whenever what you know about the session changes.
|
|
155
157
|
|
|
156
158
|
### `version: string`
|
|
157
159
|
|
|
@@ -166,17 +168,34 @@ as types.
|
|
|
166
168
|
|
|
167
169
|
## What gets captured
|
|
168
170
|
|
|
169
|
-
**Pageview** — on load and on every SPA navigation
|
|
170
|
-
`replaceState`, `popstate
|
|
171
|
+
**Pageview** — on load and on every SPA navigation: `pushState`,
|
|
172
|
+
`replaceState`, `popstate`, and, in browsers with the Navigation API, a router
|
|
173
|
+
that intercepts `navigate` instead of calling `pushState`. Each change of
|
|
174
|
+
address is recorded once, however many of those report it, and navigations to
|
|
175
|
+
the same URL are not recorded twice.
|
|
171
176
|
Fields: `page_url`, `referrer`, `timestamp`.
|
|
172
177
|
|
|
173
178
|
**Click** — on anything in `autocaptureElements`, or anything carrying
|
|
174
179
|
`data-track`, via a single delegated listener on `document`. A click on a
|
|
175
180
|
non-interactive child walks up to `walkDepth` ancestors to find the real target.
|
|
181
|
+
A button that opens a modal menu as it is pressed — a shadcn/Radix
|
|
182
|
+
`DropdownMenu`, `Select` or modal `Popover` trigger — makes the browser send
|
|
183
|
+
its click to the page itself rather than the button; that click is counted on
|
|
184
|
+
the button, as long as the pointer was released on it. A press dragged off the
|
|
185
|
+
button and released elsewhere is not counted.
|
|
176
186
|
Fields: `element_tag`, `element_selector`, `element_selector_generalized`,
|
|
177
187
|
`element_text` (truncated), `data_track`, `data_track_attrs`, plus `page_url`
|
|
178
188
|
and `timestamp`.
|
|
179
189
|
|
|
190
|
+
`element_selector` is the element's exact path from `<body>`, each level as its
|
|
191
|
+
tag, `[type=…]` and `[role=…]`, and its position among same-tagged siblings
|
|
192
|
+
(`:nth-of-type(N)`) when it has any — except directly under `<body>`, where
|
|
193
|
+
menus, toasts and browser extensions come and go, so the same element always
|
|
194
|
+
gets the same selector: `body > div > aside > a:nth-of-type(2)`. A path longer
|
|
195
|
+
than 200 characters is cut from the front, after that position is dropped.
|
|
196
|
+
`element_selector_generalized` is the same path without any positions, and is
|
|
197
|
+
always sent, even when it is the same as `element_selector`.
|
|
198
|
+
|
|
180
199
|
**Form submit** — on submit, with the form's own identity and labels. Field
|
|
181
200
|
values are never read. Fields: `element_tag`, `element_selector`,
|
|
182
201
|
`element_selector_generalized`, `data_track`, `data_track_attrs`, `page_url`,
|
|
@@ -196,20 +215,34 @@ and fields, and for elements that only look clickable — a `cursor: pointer`
|
|
|
196
215
|
element, an image, an icon, underlined text — which never send a click of their
|
|
197
216
|
own. Clicks on plain text or empty space, clicks that select text, and clicks
|
|
198
217
|
on canvas, video, audio, form fields and editable content are ignored, and
|
|
199
|
-
anything ambiguous counts as something having happened.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
click
|
|
218
|
+
anything ambiguous counts as something having happened. A link the browser
|
|
219
|
+
follows to another page is never dead, and neither is a link to the page
|
|
220
|
+
already shown (the active item of a navigation). Any other link a single-page
|
|
221
|
+
app's router handles itself (Next.js `<Link>`, React Router, Vue Router,
|
|
222
|
+
SvelteKit and the like, which cancel the browser's navigation and change the
|
|
223
|
+
view in place) is judged by its new address: slow if it appears more than a
|
|
224
|
+
second after the click, dead if it never does. A change inside the link
|
|
225
|
+
itself, such as a loading spinner, counts as its answer too. A menu or drawer
|
|
226
|
+
closing around the link does not: if the link went away with it, no new
|
|
227
|
+
address came and nothing new appeared (such as a dialog), the click is dead.
|
|
228
|
+
If the link is still on screen and something else on the page changed, the
|
|
229
|
+
click is not reported. Routers built on the browser's Navigation API, which
|
|
230
|
+
let the address change at once, are judged by when the new view finishes
|
|
231
|
+
loading instead. If the visitor clicks or types
|
|
232
|
+
somewhere else, scrolls, or leaves the page before the five seconds are up,
|
|
233
|
+
the click is decided there and then: dead if nothing had happened for over a
|
|
234
|
+
second, and otherwise not. Parts of the page that were already changing on
|
|
235
|
+
their own before the click, such as a carousel, a clock or a list that
|
|
236
|
+
refreshes itself, are not taken for the click's effect. Same fields as a
|
|
237
|
+
click.
|
|
206
238
|
|
|
207
239
|
**Slow click** (`slow_click`) — a click that did work, but whose first visible
|
|
208
240
|
effect came more than a second after it, while the visitor was still waiting.
|
|
209
241
|
Only effects the click plausibly caused count: a change to the clicked element,
|
|
210
|
-
to something inside it or close around it, a URL change
|
|
211
|
-
scroll, or a dialog or popover
|
|
212
|
-
|
|
242
|
+
to something inside it or close around it, a URL change (such as a router link
|
|
243
|
+
whose new view arrives late), focus moving, a scroll, or a dialog or popover
|
|
244
|
+
opening. Anything else changing on the page makes the click neither slow nor
|
|
245
|
+
dead. `response_ms` is the time from the click
|
|
213
246
|
to that first effect. Same fields as a click.
|
|
214
247
|
|
|
215
248
|
**Error click** (`error_click`) — a click followed, within the same five
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var config={flush_interval_ms:5e3,max_queue_size:10,walk_depth:3,max_text_length:100,autocapture_pageviews:!0,autocapture:!0,autocapture_elements:["a","button","input[type=submit]","[role=button]","[role=link]","[role=menuitem]","[role=menuitemcheckbox]","[role=menuitemradio]","[role=option]","[role=tab]","[role=switch]","[role=checkbox]","[role=radio]","[role=combobox]"],pii_url_params:["email","token","key","password","secret"],normalize_path_ids:!0,pii_path_patterns:[],excluded_urls:[],capture_dead_clicks:!0,capture_rage_clicks:!0,capture_error_clicks:!0,capture_slow_clicks:!0,dead_click_window_ms:1e3,slow_click_threshold_ms:1e3,dead_click_wait_ms:5e3,rage_click_threshold:3,rage_click_window_ms:1e3},apiBase="",apiKey="",configLoaded=!1,trackingDisabled=!1,traits=[];function setApiBase(value){apiBase=value}function setApiKey(value){apiKey=value}function setConfigLoaded(value){configLoaded=value}function setTrackingDisabled(value){trackingDisabled=value}function setTraits(value){traits=value}function applyServerConfig(server){if(typeof server!="object"||server===null)return;let s=server;if(typeof s.flush_interval_ms=="number"&&(config.flush_interval_ms=s.flush_interval_ms),typeof s.max_queue_size=="number"&&(config.max_queue_size=s.max_queue_size),typeof s.walk_depth=="number"&&(config.walk_depth=s.walk_depth),typeof s.max_text_length=="number"&&(config.max_text_length=s.max_text_length),typeof s.autocapture_pageviews=="boolean"&&(config.autocapture_pageviews=s.autocapture_pageviews),typeof s.autocapture=="boolean"&&(config.autocapture=s.autocapture),Array.isArray(s.autocapture_elements)&&(config.autocapture_elements=s.autocapture_elements),Array.isArray(s.pii_url_params)&&(config.pii_url_params=s.pii_url_params),typeof s.normalize_path_ids=="boolean"&&(config.normalize_path_ids=s.normalize_path_ids),Array.isArray(s.pii_path_patterns)&&(config.pii_path_patterns=s.pii_path_patterns),Array.isArray(s.excluded_urls)&&(config.excluded_urls=s.excluded_urls),typeof s.capture_dead_clicks=="boolean"&&(config.capture_dead_clicks=s.capture_dead_clicks),typeof s.capture_rage_clicks=="boolean"&&(config.capture_rage_clicks=s.capture_rage_clicks),typeof s.capture_error_clicks=="boolean"&&(config.capture_error_clicks=s.capture_error_clicks),typeof s.capture_slow_clicks=="boolean"&&(config.capture_slow_clicks=s.capture_slow_clicks),typeof s.dead_click_window_ms=="number"){let w=s.dead_click_window_ms;config.dead_click_window_ms=w,config.slow_click_threshold_ms=w,config.dead_click_wait_ms=Math.max(w,5e3)}typeof s.slow_click_threshold_ms=="number"&&(config.slow_click_threshold_ms=s.slow_click_threshold_ms),typeof s.dead_click_wait_ms=="number"&&(config.dead_click_wait_ms=s.dead_click_wait_ms),typeof s.rage_click_threshold=="number"&&(config.rage_click_threshold=s.rage_click_threshold),typeof s.rage_click_window_ms=="number"&&(config.rage_click_window_ms=s.rage_click_window_ms)}var CONFIG_FETCH_ATTEMPTS=5,CONFIG_RETRY_DELAYS_MS=[500,1e3,2e3,4e3],CONFIG_FETCH_TIMEOUT_MS=3e3;function isRetryableStatus(status){return status===408||status===429||status>=500}function delay(ms){return new Promise(resolve2=>setTimeout(resolve2,ms))}async function attemptConfigFetch(){let controller=new AbortController,timer2=setTimeout(()=>{controller.abort()},CONFIG_FETCH_TIMEOUT_MS);try{let response=await fetch(`${apiBase}/api/config`,{headers:{"X-API-Key":apiKey},credentials:"omit",signal:controller.signal});if(!response.ok)return isRetryableStatus(response.status)?{kind:"retry",status:response.status}:{kind:"permanent",status:response.status};let data=await response.json();return applyServerConfig(data),{kind:"ok"}}catch{return{kind:"retry"}}finally{clearTimeout(timer2)}}async function fetchConfig(){let status;for(let attempt=0;attempt<CONFIG_FETCH_ATTEMPTS;attempt++){let outcome=await attemptConfigFetch();if(outcome.kind==="ok")return{loaded:!0,attempts:attempt+1};if(outcome.kind==="permanent")return{loaded:!1,attempts:attempt+1,status:outcome.status};status=outcome.status;let wait=CONFIG_RETRY_DELAYS_MS[attempt];if(wait===void 0)break;await delay(wait)}return{loaded:!1,attempts:CONFIG_FETCH_ATTEMPTS,...status!==void 0&&{status}}}var CONFIG_FAILURE_REASONS={401:"the API key is missing or not valid",403:"this site's origin is not in the project's allowed domains"};function describeConfigFailure(result){let attempts=result.attempts===1?"1 attempt":`${String(result.attempts)} attempts`;if(result.status===void 0)return`no response after ${attempts} (network error or timeout)`;let reason=CONFIG_FAILURE_REASONS[result.status],http=`HTTP ${String(result.status)}`;return reason===void 0?`${http} after ${attempts}`:`${http} after ${attempts} (${reason})`}function applyInitConfig(initConfig){initConfig.apiKey!==void 0&&(apiKey=initConfig.apiKey),initConfig.apiHost!==void 0&&(apiBase=initConfig.apiHost),initConfig.flushIntervalMs!==void 0&&(config.flush_interval_ms=initConfig.flushIntervalMs),initConfig.maxQueueSize!==void 0&&(config.max_queue_size=initConfig.maxQueueSize),initConfig.walkDepth!==void 0&&(config.walk_depth=initConfig.walkDepth),initConfig.maxTextLength!==void 0&&(config.max_text_length=initConfig.maxTextLength),initConfig.autocapturePageviews!==void 0&&(config.autocapture_pageviews=initConfig.autocapturePageviews),initConfig.autocapture!==void 0&&(config.autocapture=initConfig.autocapture),initConfig.autocaptureElements!==void 0&&(config.autocapture_elements=initConfig.autocaptureElements),initConfig.piiUrlParams!==void 0&&(config.pii_url_params=initConfig.piiUrlParams),typeof initConfig.normalizePathIds=="boolean"&&(config.normalize_path_ids=initConfig.normalizePathIds),Array.isArray(initConfig.piiPathPatterns)&&(config.pii_path_patterns=initConfig.piiPathPatterns),initConfig.excludedUrls!==void 0&&(config.excluded_urls=initConfig.excludedUrls),initConfig.traits!==void 0&&(traits=initConfig.traits)}var ID_PATTERNS=[/^\d+$/,/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,/^[0-9a-f]{8,}$/i];function isIdSegment(segment){return ID_PATTERNS.some(p=>p.test(segment))}function buildPiiPattern(){if(config.pii_url_params.length===0)return null;let escaped=config.pii_url_params.map(p=>p.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp("("+escaped.join("|")+")","i")}function buildPiiPathPattern(){if(config.pii_path_patterns.length===0)return null;try{return new RegExp("^("+config.pii_path_patterns.join("|")+")$","i")}catch{return null}}function sanitizeUrl(url){try{let u=new URL(url),segments=u.pathname.split("/"),piiPathRegex=buildPiiPathPattern(),pathChanged=!1;for(let i=0;i<segments.length;i++){let seg=segments[i];if(seg){if(config.normalize_path_ids&&isIdSegment(seg)){segments[i]=":id",pathChanged=!0;continue}piiPathRegex?.test(seg)&&(segments[i]=":redacted",pathChanged=!0)}}pathChanged&&(u.pathname=segments.join("/"));let paramPattern=buildPiiPattern();if(paramPattern){let toRedact=[];u.searchParams.forEach((_v,k)=>{paramPattern.test(k)&&toRedact.push(k)});for(let k of toRedact)u.searchParams.set(k,":redacted")}return u.toString()}catch{return url}}function matchGlob(pattern,url){try{let path=new URL(url).pathname,escaped=pattern.replace(/[.+^${}()|[\]\\]/g,"\\$&");return new RegExp("^"+escaped.replace(/\*/g,".*")+"$").test(path)}catch{return!1}}function isExcludedUrl(url){return config.excluded_urls.some(pattern=>matchGlob(pattern,url))}function disableTracking(queue2){setTrackingDisabled(!0),queue2.length=0,config.autocapture_pageviews=!1,config.autocapture=!1}function extractText(el){let raw=el.textContent;if(!raw)return;let trimmed=raw.trim().replace(/\s+/g," ");if(trimmed.length!==0)return trimmed.length>config.max_text_length?trimmed.slice(0,config.max_text_length):trimmed}function extractDataTrack(el){let current=el;for(let i=0;i<=config.walk_depth&¤t;i++){let trackValue=current.getAttribute("data-track");if(trackValue!==null){let attrs={};for(let attr of current.attributes)if(attr.name.startsWith("data-track-")){let key=attr.name.slice(11);attrs[key]=attr.value}return{dataTrack:trackValue||void 0,dataTrackAttrs:Object.keys(attrs).length>0?attrs:void 0}}current=current.parentElement}return{dataTrack:void 0,dataTrackAttrs:void 0}}function getSegment(el){let seg=el.tagName.toLowerCase();el instanceof HTMLInputElement&&el.type&&(seg+='[type="'+CSS.escape(el.type)+'"]');let role=el.getAttribute("role");role&&(seg+='[role="'+CSS.escape(role)+'"]');let parent=el.parentElement;if(parent){let tag=el.tagName,siblings=parent.children,sameTag=0,idx=0;for(let i=0;i<siblings.length;i++){let sib=siblings[i];sib&&sib.tagName===tag&&(sameTag++,sib===el&&(idx=sameTag))}sameTag>1&&(seg+=":nth-of-type("+String(idx)+")")}return seg}function computeSelector(target){let parts=[],el=target;for(;el&&el!==document.documentElement;)parts.unshift(getSegment(el)),el=el.parentElement;let result=parts.join(" > ");for(;result.length>200&&parts.length>1;)parts.shift(),result=parts.join(" > ");return result}function getGenericSegment(el){let seg=el.tagName.toLowerCase();el instanceof HTMLInputElement&&el.type&&(seg+='[type="'+CSS.escape(el.type)+'"]');let role=el.getAttribute("role");return role&&(seg+='[role="'+CSS.escape(role)+'"]'),seg}function computeGenericSelector(target){let parts=[],el=target;for(;el&&el!==document.documentElement;)parts.unshift(getGenericSegment(el)),el=el.parentElement;let result=parts.join(" > ");for(;result.length>200&&parts.length>1;)parts.shift(),result=parts.join(" > ");let specific=computeSelector(target);return result===specific?null:result}function createPageviewEvent(referrer){return{event_type:"pageview",page_url:window.location.href,referrer:referrer===""?void 0:referrer,timestamp:new Date().toISOString()}}function createClickEvent(el){return createElementEvent(el,"click")}function createElementEvent(el,type){let{dataTrack,dataTrackAttrs}=extractDataTrack(el);return{event_type:type,page_url:window.location.href,element_tag:el.tagName.toLowerCase(),element_selector:computeSelector(el),element_selector_generalized:computeGenericSelector(el)??void 0,element_text:extractText(el),data_track:dataTrack,data_track_attrs:dataTrackAttrs,timestamp:new Date().toISOString()}}function createFormSubmitEvent(form){let{dataTrack,dataTrackAttrs}=extractDataTrack(form);return{event_type:"form_submit",page_url:window.location.href,element_tag:form.tagName.toLowerCase(),element_selector:computeSelector(form),element_selector_generalized:computeGenericSelector(form)??void 0,data_track:dataTrack,data_track_attrs:dataTrackAttrs,timestamp:new Date().toISOString()}}var pickerActive=!1,pickerDashboardOrigin=null;function setPickerActive(value){pickerActive=value}function setPickerDashboardOrigin(value){pickerDashboardOrigin=value}var REJECTION_REASONS={401:"the API key is missing or not valid",403:"this site's origin is not in the project's allowed domains",429:"the project is over its event limit"},queue=[],lastUrl="",isFlushing=!1;function setLastUrl(value){lastUrl=value}function sanitizeEvent(event){let pageUrl=sanitizeUrl(event.page_url),referrer=event.referrer===void 0?void 0:sanitizeUrl(event.referrer);return pageUrl===event.page_url&&referrer===event.referrer?event:{...event,page_url:pageUrl,...referrer!==void 0&&{referrer}}}function prepareEvents(events){return filterEvents(events.map(sanitizeEvent))}function filterEvents(events){return events.filter(event=>!(isExcludedUrl(event.page_url)||event.event_type==="pageview"&&!config.autocapture_pageviews||(event.event_type==="click"||event.event_type==="form_submit")&&!config.autocapture&&!event.data_track||event.event_type==="dead_click"&&!(config.autocapture&&config.capture_dead_clicks)||event.event_type==="rage_click"&&!(config.autocapture&&config.capture_rage_clicks)||event.event_type==="error_click"&&!(config.autocapture&&config.capture_error_clicks)||event.event_type==="slow_click"&&!(config.autocapture&&config.capture_slow_clicks)))}function enqueue(event){trackingDisabled||pickerActive||apiKey&&(queue.length>=100||(queue.push(event),configLoaded&&queue.length>=config.max_queue_size&&flush()))}async function flush(){if(trackingDisabled||pickerActive||!configLoaded||isFlushing||queue.length===0)return;isFlushing=!0;let events=prepareEvents(queue.splice(0));if(events.length===0){isFlushing=!1;return}let body={events,...traits.length>0&&{traits}},headers={"Content-Type":"application/json"};apiKey&&(headers["X-API-Key"]=apiKey);try{let response=await fetch(`${apiBase}/api/events`,{method:"POST",headers,body:JSON.stringify(body),keepalive:!0,credentials:"omit"});if(!response.ok){let status=response.status;status===401||status===403||status===429?(console.warn(`[tracker] The API rejected events with HTTP ${String(status)} \u2014 ${REJECTION_REASONS[status]??"the request was refused"}. Tracking is now off for this page.`),disableTracking(queue)):status>=500&&queue.unshift(...events)}}catch{queue.unshift(...events)}finally{isFlushing=!1}}function flushBeacon(){if(trackingDisabled||!configLoaded||queue.length===0)return;let events=prepareEvents(queue.splice(0));if(events.length===0)return;let body={events,...traits.length>0&&{traits}};apiKey&&(body.api_key=apiKey);let headers={"Content-Type":"application/json"};apiKey&&(headers["X-API-Key"]=apiKey),fetch(`${apiBase}/api/events`,{method:"POST",headers,body:JSON.stringify(body),keepalive:!0,credentials:"omit"}).catch(()=>{})}function isSensitive(el){if(el instanceof HTMLInputElement){let t=el.type.toLowerCase();if(t==="password"||t==="hidden")return!0}let ac=el.getAttribute("autocomplete");return!!(ac&&ac.toLowerCase().startsWith("cc-"))}function isInteractive(el){if(el.hasAttribute("data-track"))return!0;if(!config.autocapture)return!1;for(let selector of config.autocapture_elements)try{if(el.matches(selector))return!0}catch{}return!1}function findInteractiveAncestor(target){let el=target;for(let i=0;i<=config.walk_depth&⪙i++){if(isInteractive(el))return el;el=el.parentElement}return null}function isPickerTarget(el){return isInteractive(el)||el.matches("form")}function findPickerTarget(target){let el=target;for(let i=0;i<=config.walk_depth&⪙i++){if(isPickerTarget(el))return el;el=el.parentElement}return null}function trackableElements(){let selectors=["[data-track]"];config.autocapture&&selectors.push("form",...config.autocapture_elements);let seen2=new Set;for(let selector of selectors){let found;try{found=document.querySelectorAll(selector)}catch{continue}for(let el of found)seen2.add(el)}let result=[];for(let el of seen2)isSensitive(el)||result.push(el);return result}var PICKER_HANDOFF_KEY="__stPickerHandoff";var PICKER_FILE="tracker-picker.js",picker=null,loading=!1,failed=!1,pendingOptions=null,pickerHostApi={setActive:setPickerActive,setDashboardOrigin:setPickerDashboardOrigin,clearQueue:()=>{queue.length=0},computeSelector,computeGenericSelector,extractText,extractDataTrack,findPickerTarget,trackableElements,sanitizeUrl};function pickerScriptUrl(){for(let script of document.querySelectorAll("script[src]")){let src=script.getAttribute("src");if(src?.includes("tracker.js"))try{return new URL(PICKER_FILE,new URL(src,document.baseURI)).toString()}catch{}}return apiBase?apiBase.replace(/\/+$/,"")+"/"+PICKER_FILE:"/"+PICKER_FILE}function registerPicker(module){if(picker=module,loading=!1,pendingOptions){let options=pendingOptions;pendingOptions=null,activate(module,options)}}function activate(module,options){module.activate({...options,dashboardOrigin:pickerDashboardOrigin})}function abandonLoad(url){loading=!1,failed=!0,pendingOptions=null,setPickerActive(!1),console.warn(`[tracker] Could not load the Element Picker from ${url}. Tracking is unaffected.`)}function loadPicker(){let url=pickerScriptUrl(),script=document.createElement("script");script.src=url,script.async=!0,script[PICKER_HANDOFF_KEY]={host:pickerHostApi,register:registerPicker},script.addEventListener("load",()=>{script.remove(),picker||abandonLoad(url)}),script.addEventListener("error",()=>{script.remove(),abandonLoad(url)}),document.head.appendChild(script)}function requestPicker(options){if(picker){activate(picker,options??{});return}failed||(setPickerActive(!0),queue.length=0,pendingOptions=options??{},!loading&&(loading=!0,loadPicker()))}function onPickerNavigation(){picker?.onNavigation()}function onPickerWindowMessage(e){if(picker){picker.handleMessage(e);return}if(typeof e.data!="object"||e.data===null)return;let data=e.data;data.type==="st-picker-activate"&&(pickerDashboardOrigin&&e.origin!==pickerDashboardOrigin||pickerActive||!window.opener||requestPicker(data.mode==="click-map"?{mode:"click-map"}:void 0))}function parsePickerHash(hash){let m=/^#st-picker=([^&]+)(?:&(.*))?$/.exec(hash),raw=m?.[1];if(!m||!raw)return null;let origin;try{origin=decodeURIComponent(raw)}catch{return null}let mode="rules";for(let pair of(m[2]??"").split("&"))pair==="mode=click-map"&&(mode="click-map");return{origin,mode}}function findLooksClickable(target,depth,parent,look2){let el=target;for(let i=0;i<=depth&⪙i++){let f=look2(el);if(f&12)return null;if(f&1){let top=el,up=parent(el);for(;up;i++){let g=look2(up);if(!(g&1))return top;if(g&8||i>=depth)return null;top=up,up=parent(up)}return top}if(f&2)return el;el=parent(el)}return null}function walkUp(target,depth,parent,pred){let el=target;for(let i=0;i<=depth&⪙i++){if(pred(el))return el;el=parent(el)}return null}function leavesDocument(href,page){try{let u=new URL(href,page);if(u.protocol==="javascript:")return!1;if(!href.includes("#"))return!0;let p=new URL(page);return u.hash="",p.hash="",u.href!==p.href}catch{return!0}}function elementKey(ev){return JSON.stringify([ev.element_selector_generalized??null,ev.element_text??null,ev.element_tag??null])}var MAX_REPORTED=100;function claim(seen2,key){return seen2.has(key)||seen2.size>=MAX_REPORTED?!1:(seen2.add(key),!0)}function nextBurst(burst2,key,first,now,windowMs){return burst2?.key===key&&now-burst2.last<=windowMs?(burst2.count++,burst2.last=now,burst2):{key,first,count:1,last:now}}var MAX_CLICK_COUNT=1e3;function rageCount(count,threshold,selected=!1){return count>=Math.max(selected?4:3,threshold)?Math.min(count,MAX_CLICK_COUNT):null}function selectionIsEffect(current,made,touchesMade=!1){return current!==null&&!touchesMade&&!(made&&made.anchor===current.anchor&&made.anchorOffset===current.anchorOffset&&made.focus===current.focus&&made.focusOffset===current.focusOffset)}var MAX_PENDING=8;function openWindow(pending2,seen2,p){if(seen2.every(s=>s.has(p.key))||pending2.length>=MAX_PENDING)return!1;for(let q of pending2)if(q.key===p.key)return!1;return pending2.push(p),!0}var IGNORE=0,LIVE=1,CAUSED=2,SUSPECT=3;function classify(related,noisy2,global=!1,ticking=!1){return noisy2?related?LIVE:IGNORE:related||global?CAUSED:ticking?SUSPECT:LIVE}function isRelated(node,el,depth,parent,contains2){return contains2(el,node)||walkUp(el,depth,parent,n=>n===node)!==null}var NOISE_GAP_MS=6e3,NOISE_REPEAT_MS=250;function noteStreak(s,t){return s?(t-s.last>NOISE_GAP_MS?(s.start=s.beatAt=t,s.since=1/0,s.beats=1):(s.since===1/0&&t-s.start>=NOISE_REPEAT_MS&&(s.since=t),t-s.beatAt>=NOISE_REPEAT_MS&&(s.beats++,s.beatAt=t)),s.last=t,s):{start:t,last:t,since:1/0,beats:1,beatAt:t}}function noisyAt(s,at){return s!==void 0&&s.since<at}function tickingAt(s,at,t){return s!==void 0&&s.start<at&&t-s.last<=NOISE_GAP_MS}var MAX_DOUBTS=8;function addDoubt(doubts,node,t,beats){for(let d of doubts)if(d.node===node)return!0;return doubts.length>=MAX_DOUBTS?!1:(doubts.push({node,t,beats}),!0)}function judgeDoubts(doubts,at,now,streakOf2){let open=!1;for(let d of doubts){let s=streakOf2(d.node);if(!s||s.start>=at||now-s.last>NOISE_GAP_MS)return"live";s.beats-d.beats<2&&(open=!0)}return open?"open":"noise"}var NONE=0,DROP=1,SLOW=2,DOUBT=3;function effectOn(p,kind,t){return kind===IGNORE?NONE:kind===SUSPECT?DOUBT:kind===LIVE||t<p.slowAt||t>=p.due?DROP:SLOW}function moveOn(pending2,now,el){for(let p of pending2)p.el!==el&&!p.verdict&&(p.due=Math.max(p.slowAt,Math.min(p.due,now)))}var MAX_RESPONSE_MS=6e4;function responseMs(at,t){return Math.min(MAX_RESPONSE_MS,Math.max(0,Math.round(t-at)))}function errorEventCounts(trusted,rejection){return trusted||rejection}var ERROR_NAME=/^[A-Za-z_$][A-Za-z0-9_$.]{0,63}$/;function errorType(err,rejection){if(!(err instanceof Error))return rejection?"UnhandledRejection":"Error";let name;try{name=err.name}catch{return"Error"}return typeof name=="string"&&ERROR_NAME.test(name)?name:"Error"}function ambientError(last,at,windowMs){return last!==void 0&&last<at&&at-last<=windowMs}function takeSettled(pending2,now,final,streakOf2){let out=[];for(let i=0;i<pending2.length;i++){let p=pending2[i];if(!p)continue;if(!p.verdict){if(p.due>now)continue;p.verdict="dead"}let j=judgeDoubts(p.doubts,p.at,now,streakOf2);if(j==="open"&&!final){p.due<=now&&(p.due=now+NOISE_REPEAT_MS);continue}pending2.splice(i--,1),j==="noise"&&out.push(p)}return out}function nextDue(pending2){let next=1/0;for(let p of pending2)p.due<next&&(next=p.due);return next}function holdsClick(prev,key,now,windowMs,detail){return detail!==0&&prev?.key===key&&now-prev.at<=windowMs}var MAX_HELD=32;function openHeld(held2,el,at,due,live2){if(held2.length>=MAX_HELD)return null;let h={ev:null,el,at,due,live:live2};return held2.push(h),h}function attachHeld(held2,h,ev){let i=held2.indexOf(h);return i<0?!1:h.live?(held2.splice(i,1),!1):(h.ev=ev,!0)}function releaseHeld(held2,did=()=>!0){let out=[];for(let i=0;i<held2.length;i++){let h=held2[i];!h||h.live||!did(h)||(h.ev!==null?(out.push(h.ev),held2.splice(i--,1)):h.live=!0)}return out}function takeDueHeld(held2,now){let out=[];for(let i=0;i<held2.length;i++){let h=held2[i];h&&h.due<=now&&(h.ev!==null&&!h.live&&out.push(h.ev),held2.splice(i--,1))}return out}function heldWaiting(held2){for(let h of held2)if(!h.live)return!0;return!1}var PRESS_MS=5e3,STALL_MS=250,MAX_ERROR_TYPES=32;function newSeen(){return{dead:new Set,rage:new Set,slow:new Set,error:new Set}}var seen=newSeen(),pending=[],burst=null,lastKey="",madeSel=null,madeEl=null,lastClick=null,held=[],holding=null,holdTarget=null,errClick=null,errorTimes=new Map,streaks=new WeakMap,pressTarget=null,pressEl=null,pressAt=0,pressUntil=0,pressEffect=!1,pressDoubts=[],inTask=!1,observer=null,observing=!1,listening=!1,timer,timerDue=0,live=()=>LIVE,caused=()=>CAUSED;function parentOf(el){return el.parentElement}function nearParent(n){let p=n.parentNode;return p instanceof Element&&p!==document.body&&p!==document.documentElement?p:null}function contains(outer,inner){return outer.contains(inner)}function noisy(node,at){return walkUp(node,config.walk_depth,nearParent,n=>noisyAt(streaks.get(n),at))!==null}function tickingNode(node,at,t){let found=null,n=node;for(let i=0;i<=config.walk_depth&&n;i++)tickingAt(streaks.get(n),at,t)&&(found=n),n=nearParent(n);return found}function streakOf(n){return streaks.get(n)}function note(node,t){let s=streaks.get(node),n=noteStreak(s,t);s||streaks.set(node,n)}function isDisabled(el){return el.matches(":is(button,input,select,textarea):disabled")||el.getAttribute("aria-disabled")==="true"&&isInteractive(el)}function isEditable(el){return el instanceof HTMLInputElement?!/^(button|submit|image)$/.test(el.type):/^(TEXTAREA|SELECT)$/.test(el.tagName)||el.isContentEditable}function look(el){if(/^(canvas|video|audio|input|textarea|select)$/i.test(el.tagName)||isEditable(el))return 4;let s=getComputedStyle(el),f=s.cursor==="pointer"?1:0;return el===document.body||el===document.documentElement?f|8:el.tagName==="IMG"||el.tagName==="svg"||s.textDecorationLine.includes("underline")?f|2:f}function active(){return!pickerActive&&!trackingDisabled&&config.autocapture&&(config.capture_dead_clicks||config.capture_rage_clicks||config.capture_slow_clicks||config.capture_error_clicks)}function rolling(){return active()&&(config.capture_dead_clicks||config.capture_slow_clicks)}function idle(){return pending.length===0&&(pressEffect||!pressTarget)&&!heldWaiting(held)}function resolve(target){if(target.closest("#st-picker-host"))return null;let control=findInteractiveAncestor(target);if(control)return isSensitive(control)||isEditable(control)?null:control;let t=target;for(;t instanceof SVGElement&&t.ownerSVGElement;)t=t.ownerSVGElement;let el=findLooksClickable(t,config.walk_depth,parentOf,look);return el&&!isSensitive(el)?el:null}function selection(){let s=getSelection();return s&&!s.isCollapsed?{anchor:s.anchorNode,anchorOffset:s.anchorOffset,focus:s.focusNode,focusOffset:s.focusOffset}:null}function selectionEffect(){let s=getSelection();return selectionIsEffect(selection(),madeSel,!!madeEl&&!!s&&s.containsNode(madeEl,!0))}function startTask(){inTask||(inTask=!0,setTimeout(()=>{inTask=!1},0))}function report(el,now,detail,dead,target,doubts,disabled=!1){let ev=createElementEvent(el,"dead_click"),key=elementKey(ev),rage=detail!==0,threshold=config.slow_click_threshold_ms,wait=Math.max(threshold,config.dead_click_wait_ms),multi=detail>=2&&key===lastKey;if(madeSel=multi?selection():null,madeEl=multi?el:null,lastKey=key,holding=null,holdTarget=target,rage&&(holdsClick(lastClick,key,now,config.rage_click_window_ms,detail)&&(holding=openHeld(held,el,now,now+threshold,!dead)),lastClick={key,at:now}),rage&&config.capture_rage_clicks){let next=nextBurst(burst,key,ev,now,config.rage_click_window_ms);next!==burst&&endBurst(),burst=next;let s=getSelection();next.selected=!!s&&!s.isCollapsed&&s.containsNode(el,!0)}errClick=config.capture_error_clicks&&!seen.error.has(key)?{key,ev,el,at:now,due:now+wait}:null;let link=el.closest("a[href]");if(dead&&!(link&&leavesDocument(link.getAttribute("href")??"",location.href))&&!el.closest("label")){let kinds=[];config.capture_dead_clicks&&kinds.push(seen.dead),config.capture_slow_clicks&&!disabled&&kinds.push(seen.slow);let slowAt=now+threshold;openWindow(pending,kinds,{key,ev,el,at:now,slowAt,due:disabled?slowAt:now+wait,doubts:doubts.slice(),seen})}sync()}function endBurst(){let b=burst;burst=null;let n=b&&rageCount(b.count,config.rage_click_threshold,b.selected);b&&n&&claim(seen.rage,b.key)&&enqueue({...b.first,event_type:"rage_click",click_count:n})}function happen(kindAt,suspect){let t=Date.now(),doubt=(doubts,at)=>{let n=suspect?.(at),s=n?streaks.get(n):void 0;return!!n&&!!s&&addDoubt(doubts,n,t,s.beats)};if(pressTarget&&!pressEffect){let k=inTask||!pressEl?LIVE:kindAt(pressEl,pressAt);k!==IGNORE&&!(k===SUSPECT&&doubt(pressDoubts,pressAt))&&(pressEffect=!0)}for(let i=0;i<pending.length;i++){let p=pending[i];if(!p||p.verdict)continue;let r=effectOn(p,inTask?LIVE:kindAt(p.el,p.at),t);r===NONE||r===DOUBT&&doubt(p.doubts,p.at)||(r===SLOW?(p.verdict="slow",p.answeredAt=t):pending.splice(i--,1))}let did=h=>{let k=inTask?LIVE:kindAt(h.el,h.at);return k!==IGNORE&&k!==SUSPECT};for(let ev of releaseHeld(held,did))enqueue(ev)}function markLive(){idle()||(happen(live),sync())}function markCaused(){idle()||(happen(caused),sync())}function markHistory(changed){changed?markCaused():markLive()}function settle(now,final){for(let p of takeSettled(pending,now,final,streakOf))p.verdict==="slow"?config.capture_slow_clicks&&claim(p.seen.slow,p.key)&&enqueue({...p.ev,event_type:"slow_click",response_ms:responseMs(p.at,p.answeredAt??now)}):config.capture_dead_clicks&&claim(p.seen.dead,p.key)&&enqueue(p.ev)}function movedOn(now,el,errors){if(errors&&errClick&&errClick.el!==el&&(errClick=null),pending.length===0)return;let records=observer?.takeRecords();records?.length&&onMutations(records),selectionEffect()&&happen(live),moveOn(pending,now,el),settle(now,!1)}function onMutations(records){let t=Date.now();for(let r of records)note(r.target,t);for(let r of records){if(idle())break;let node=r.target;happen(r.attributeName==="open"&&/^(DIALOG|DETAILS)$/.test(node.nodeName)?caused:(el,at)=>classify(isRelated(node,el,config.walk_depth,nearParent,contains),noisy(node,at),!1,tickingNode(node,at,t)!==null),at=>tickingNode(node,at,t))}sync()}function holdClick(e,ev){let h=holding;return holding=null,!!h&&e.isTrusted&&e.detail!==0&&e.target===holdTarget&&attachHeld(held,h,ev)}function onFocusIn(e){let f=e.target;pressTarget&&f instanceof Node&&f.contains(pressTarget)||markCaused()}function onScroll(e){let node=e.target instanceof Node?e.target:document;note(node,Date.now()),!idle()&&(happen((el,at)=>classify(node.contains(el),noisyAt(streaks.get(node),at),!0)),sync())}function onKeyDown(e){if(!e.isTrusted||pending.length===0&&!errClick)return;let t=e.target;movedOn(Date.now(),(e.key==="Enter"||e.key===" ")&&t instanceof Element?t:null,!0),sync()}function onWheel(e){!e.isTrusted||pending.length===0||(movedOn(Date.now(),null,!1),sync())}function onLeave(){movedOn(Date.now(),null,!1),markLive(),sync()}function noteError(type){let now=Date.now(),last=errorTimes.get(type);last===void 0&&errorTimes.size>=MAX_ERROR_TYPES&&errorTimes.clear(),errorTimes.set(type,now);let c=errClick;!c||now>c.due||!active()||!config.capture_error_clicks||!inTask&&ambientError(last,c.at,NOISE_GAP_MS)||(errClick=null,claim(seen.error,c.key)&&enqueue({...c.ev,event_type:"error_click",error_type:type}))}function onError(e){e instanceof ErrorEvent&&errorEventCounts(e.isTrusted,!1)&¬eError(errorType(e.error,!1))}function onRejection(e){errorEventCounts(e.isTrusted,!0)&¬eError(errorType(e.reason,!0))}function sync(){settle(Date.now(),!1);let watch=!idle(),observe=watch||rolling();observe!==observing&&(observe?(typeof MutationObserver=="function"&&(observer??=new MutationObserver(onMutations)),observer?.observe(document,{subtree:!0,childList:!0,attributes:!0,characterData:!0})):observer?.disconnect(),observing=observe),watch!==listening&&(listening=watch,watch?window.addEventListener("beforeunload",onLeave):window.removeEventListener("beforeunload",onLeave));let next=Math.min(nextDue(pending),nextDue(held),burst?burst.last+config.rage_click_window_ms+1:1/0,pressTarget?pressUntil:1/0);clearTimeout(timer),timer=void 0,next<1/0&&(timerDue=next,timer=setTimeout(tick,Math.max(0,next-Date.now())))}function tick(){let now=Date.now(),late=now-timerDue>STALL_MS,records=observer?.takeRecords();records?.length&&onMutations(records),(late||selectionEffect())&&happen(live),settle(now,!1);for(let ev of takeDueHeld(held,now))active()||enqueue(ev);burst&&now-burst.last>config.rage_click_window_ms&&endBurst(),pressTarget&&now>=pressUntil&&(pressTarget=pressEl=null),sync()}function onPointerDown(e){if(!e.isTrusted)return;let target=e.target,now=Date.now();if(!(target instanceof Element)||e.button!==0||!active()){movedOn(now,null,!0),sync();return}let disabled=walkUp(target,config.walk_depth,parentOf,isDisabled),el=disabled??resolve(target);if(movedOn(now,el,!0),startTask(),holding=null,pressTarget=target,pressEffect=!1,pressDoubts=[],pressAt=now,pressUntil=now+PRESS_MS,disabled){pressEl=null,pressEffect=!0,!isSensitive(disabled)&&!target.closest("#st-picker-host")?report(disabled,now,1,!0,target,[],!0):sync();return}pressEl=el,pressEl||(pressTarget=null),sync()}function onClick(e){if(!e.isTrusted||!active())return;let target=e.target;if(!(target instanceof Element))return;startTask();let press=pressTarget,effect=press!==null&&pressEffect,el=press===target?pressEl:resolve(target);pressTarget=pressEl=null;let multi=e.detail>=2;el&&!walkUp(target,config.walk_depth,parentOf,isDisabled)&&(isInteractive(el)||multi||getSelection()?.isCollapsed!==!1)?report(el,Date.now(),e.detail,!effect,target,press?pressDoubts:[]):sync()}function onFrustrationHide(){onLeave(),settle(Date.now(),!0),endBurst()}function resetFrustration(){movedOn(Date.now(),null,!1),markLive(),endBurst(),lastClick=null,lastKey="",madeSel=null,madeEl=null,seen=newSeen()}function startFrustration(){let capture={capture:!0,passive:!0};window.addEventListener("pointerdown",onPointerDown,capture),window.addEventListener("click",onClick,capture),window.addEventListener("focusin",onFocusIn,capture),window.addEventListener("keydown",onKeyDown,capture),window.addEventListener("wheel",onWheel,capture),window.addEventListener("error",onError),window.addEventListener("unhandledrejection",onRejection);for(let type of["blur","pagehide","popstate","hashchange"])window.addEventListener(type,onLeave);document.addEventListener("scroll",onScroll,capture),document.addEventListener("toggle",markCaused,capture);for(let type of["submit","invalid","change","play","copy"])document.addEventListener(type,markLive,capture);sync()}function onClickCapture(e){if(pickerActive)return;let target=e.target;if(!(target instanceof Element))return;let interactive=findInteractiveAncestor(target);if(!interactive||isSensitive(interactive))return;let ev=createClickEvent(interactive);holdClick(e,ev)||enqueue(ev)}function onSubmitCapture(e){if(pickerActive)return;let form=e.target;form instanceof HTMLFormElement&&(isSensitive(form)||!config.autocapture&&!extractDataTrack(form).dataTrack||enqueue(createFormSubmitEvent(form)))}function onSpaNavigation(){if(pickerActive){onPickerNavigation();return}if(!config.autocapture_pageviews)return;let currentUrl=window.location.href;if(currentUrl===lastUrl)return;let referrer=lastUrl;setLastUrl(currentUrl),resetFrustration(),enqueue(createPageviewEvent(referrer))}function patchHistory(){let origPushState=history.pushState.bind(history),origReplaceState=history.replaceState.bind(history);history.pushState=function(...args){let before=location.href;origPushState(...args),markHistory(location.href!==before),onSpaNavigation()},history.replaceState=function(...args){let before=location.href;origReplaceState(...args),markHistory(location.href!==before),onSpaNavigation()}}var version="1.1.0",initialized=!1,storedInitConfig={};function init(initConfig){if(typeof window>"u")return;if(initialized){console.warn("[tracker] Already initialized. Ignoring duplicate init() call.");return}if(initialized=!0,storedInitConfig=initConfig??{},storedInitConfig.apiKey!==void 0&&setApiKey(storedInitConfig.apiKey),storedInitConfig.apiHost!==void 0&&setApiBase(storedInitConfig.apiHost),!apiKey){console.error("[tracker] Missing apiKey. Events will not be sent.");return}applyInitConfig(storedInitConfig),setLastUrl(window.location.href),config.autocapture_pageviews&&enqueue(createPageviewEvent(document.referrer)),patchHistory(),window.addEventListener("popstate",onSpaNavigation),document.addEventListener("click",onClickCapture,{capture:!0}),document.addEventListener("submit",onSubmitCapture,{capture:!0}),startFrustration(),window.addEventListener("message",onPickerWindowMessage);let pickerHash=parsePickerHash(window.location.hash);if(pickerHash){let{origin,mode}=pickerHash;setPickerDashboardOrigin(origin);let clickMap=mode==="click-map";try{sessionStorage.setItem("st-picker",JSON.stringify({active:!0,origin,...clickMap&&{mode}}))}catch{}history.replaceState(null,"",window.location.pathname+window.location.search),window.opener&&requestPicker(clickMap?{mode}:void 0)}else try{let stored=sessionStorage.getItem("st-picker");if(stored&&window.opener){let parsed=JSON.parse(stored);if(typeof parsed=="object"&&parsed!==null&&"active"in parsed&&"origin"in parsed){let obj=parsed;if(obj.active&&typeof obj.origin=="string"){setPickerDashboardOrigin(obj.origin);let storedScan="scanEnabled"in obj&&obj.scanEnabled===!0,storedNav="navigateMode"in obj&&obj.navigateMode===!0,rawMode="selectorMode"in obj?obj.selectorMode:null;requestPicker({scanEnabled:storedScan,navigateMode:storedNav,selectorMode:rawMode==="generic"||rawMode==="exact"?rawMode:null,..."mode"in obj&&obj.mode==="click-map"&&{mode:"click-map"}})}}}}catch{}document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&(onFrustrationHide(),flushBeacon())}),fetchConfig().then(result=>{if(!result.loaded){failClosed(result);return}applyInitConfig(storedInitConfig),!trackingDisabled&&(setConfigLoaded(!0),setInterval(()=>{flush()},config.flush_interval_ms),flush())})}function failClosed(result){console.warn(`[tracker] Could not load this project's settings from ${apiBase}/api/config \u2014 ${describeConfigFailure(result)}. No events were sent from this page load: without those settings the tracker cannot apply the project's URL redaction rules, and sending unredacted URLs would be worse than sending nothing.`),disableTracking(queue)}function track(name,attrs){typeof window>"u"||trackingDisabled||name&&enqueue({event_type:"custom",page_url:window.location.href,data_track:name,data_track_attrs:attrs,timestamp:new Date().toISOString()})}function setTraits2(newTraits){typeof window>"u"||setTraits(newTraits)}export{init,setTraits2 as setTraits,track,version};
|
|
1
|
+
function codePointLength(value){let n=0;for(let _ of value)n++;return n}function quote(value){let shown="",n=0;for(let ch of value){if(n===20)return`${JSON.stringify(shown)}\u2026`;shown+=ch,n++}return JSON.stringify(shown)}function describeNonString(value){return typeof value=="number"||typeof value=="boolean"?String(value):value===null?"null":`a value of type ${typeof value}`}function cleanTraits(value){if(!Array.isArray(value))return{traits:[],dropped:[`${describeNonString(value)} (not a list of traits)`]};let traits2=[],dropped=[],seen2=new Set,overLimit=[];for(let raw of value){if(typeof raw!="string"){dropped.push(`${describeNonString(raw)} (not a string)`);continue}let trait=raw.trim();if(trait===""){dropped.push(`${JSON.stringify(raw)} (empty)`);continue}if(codePointLength(trait)>64){dropped.push(`${quote(trait)} (longer than ${String(64)} characters)`);continue}seen2.has(trait)||(seen2.add(trait),traits2.length<50?traits2.push(trait):overLimit.push(quote(trait)))}if(overLimit.length>0){let extra=overLimit.length-10,names=overLimit.slice(0,10).join(", "),tail=extra>0?` and ${String(extra)} more`:"";dropped.push(`${names}${tail} (over the limit of ${String(50)} traits)`)}return{traits:traits2,dropped}}function describeDroppedTraits(source,dropped){let named=dropped.slice(0,10).join("; "),rest=dropped.length-10,more=rest>0?`; and ${String(rest)} more`:"";return`[tracker] ${source} left out traits the API would refuse: ${named}${more}. The other traits are sent as usual.`}function acceptTraits(value,source){let{traits:traits2,dropped}=cleanTraits(value);return dropped.length>0&&console.warn(describeDroppedTraits(source,dropped)),traits2}var LEGACY_DEAD_CLICK_WAIT_MS=5e3,MAX_QUEUE_CAP=100,config={flush_interval_ms:5e3,max_queue_size:10,walk_depth:3,max_text_length:100,autocapture_pageviews:!0,autocapture:!0,autocapture_elements:["a","button","input[type=submit]","[role=button]","[role=link]","[role=menuitem]","[role=menuitemcheckbox]","[role=menuitemradio]","[role=option]","[role=tab]","[role=switch]","[role=checkbox]","[role=radio]","[role=combobox]"],pii_url_params:["email","token","key","password","secret"],normalize_path_ids:!0,pii_path_patterns:[],excluded_urls:[],capture_dead_clicks:!0,capture_rage_clicks:!0,capture_error_clicks:!0,capture_slow_clicks:!0,dead_click_window_ms:1e3,slow_click_threshold_ms:1e3,dead_click_wait_ms:5e3,rage_click_threshold:3,rage_click_window_ms:1e3},apiBase="",apiKey="",configLoaded=!1,trackingDisabled=!1,traits=[];function setApiBase(value){apiBase=value}function setApiKey(value){apiKey=value}function setConfigLoaded(value){configLoaded=value}function setTrackingDisabled(value){trackingDisabled=value}function setTraits(value,source="setTraits()"){traits=acceptTraits(value,source)}function applyServerConfig(server){if(typeof server!="object"||server===null)return;let s=server;if(typeof s.flush_interval_ms=="number"&&(config.flush_interval_ms=s.flush_interval_ms),typeof s.max_queue_size=="number"&&(config.max_queue_size=s.max_queue_size),typeof s.walk_depth=="number"&&(config.walk_depth=s.walk_depth),typeof s.max_text_length=="number"&&(config.max_text_length=s.max_text_length),typeof s.autocapture_pageviews=="boolean"&&(config.autocapture_pageviews=s.autocapture_pageviews),typeof s.autocapture=="boolean"&&(config.autocapture=s.autocapture),Array.isArray(s.autocapture_elements)&&(config.autocapture_elements=s.autocapture_elements),Array.isArray(s.pii_url_params)&&(config.pii_url_params=s.pii_url_params),typeof s.normalize_path_ids=="boolean"&&(config.normalize_path_ids=s.normalize_path_ids),Array.isArray(s.pii_path_patterns)&&(config.pii_path_patterns=s.pii_path_patterns),Array.isArray(s.excluded_urls)&&(config.excluded_urls=s.excluded_urls),typeof s.capture_dead_clicks=="boolean"&&(config.capture_dead_clicks=s.capture_dead_clicks),typeof s.capture_rage_clicks=="boolean"&&(config.capture_rage_clicks=s.capture_rage_clicks),typeof s.capture_error_clicks=="boolean"&&(config.capture_error_clicks=s.capture_error_clicks),typeof s.capture_slow_clicks=="boolean"&&(config.capture_slow_clicks=s.capture_slow_clicks),typeof s.dead_click_window_ms=="number"){let w=s.dead_click_window_ms;config.dead_click_window_ms=w,config.slow_click_threshold_ms=w,config.dead_click_wait_ms=Math.max(w,LEGACY_DEAD_CLICK_WAIT_MS)}typeof s.slow_click_threshold_ms=="number"&&(config.slow_click_threshold_ms=s.slow_click_threshold_ms),typeof s.dead_click_wait_ms=="number"&&(config.dead_click_wait_ms=s.dead_click_wait_ms),typeof s.rage_click_threshold=="number"&&(config.rage_click_threshold=s.rage_click_threshold),typeof s.rage_click_window_ms=="number"&&(config.rage_click_window_ms=s.rage_click_window_ms)}var CONFIG_FETCH_ATTEMPTS=5,CONFIG_RETRY_DELAYS_MS=[500,1e3,2e3,4e3],CONFIG_FETCH_TIMEOUT_MS=3e3;function isRetryableStatus(status){return status===408||status===429||status>=500}function delay(ms){return new Promise(resolve2=>setTimeout(resolve2,ms))}async function attemptConfigFetch(){let controller=new AbortController,timer2=setTimeout(()=>{controller.abort()},CONFIG_FETCH_TIMEOUT_MS);try{let response=await fetch(`${apiBase}/api/config`,{headers:{"X-API-Key":apiKey},credentials:"omit",signal:controller.signal});if(!response.ok)return isRetryableStatus(response.status)?{kind:"retry",status:response.status}:{kind:"permanent",status:response.status};let data=await response.json();return applyServerConfig(data),{kind:"ok"}}catch{return{kind:"retry"}}finally{clearTimeout(timer2)}}async function fetchConfig(){let status;for(let attempt=0;attempt<CONFIG_FETCH_ATTEMPTS;attempt++){let outcome=await attemptConfigFetch();if(outcome.kind==="ok")return{loaded:!0,attempts:attempt+1};if(outcome.kind==="permanent")return{loaded:!1,attempts:attempt+1,status:outcome.status};status=outcome.status;let wait=CONFIG_RETRY_DELAYS_MS[attempt];if(wait===void 0)break;await delay(wait)}return{loaded:!1,attempts:CONFIG_FETCH_ATTEMPTS,...status!==void 0&&{status}}}var CONFIG_FAILURE_REASONS={401:"the API key is missing or not valid",403:"this site's origin is not in the project's allowed domains"};function describeConfigFailure(result){let attempts=result.attempts===1?"1 attempt":`${String(result.attempts)} attempts`;if(result.status===void 0)return`no response after ${attempts} (network error or timeout)`;let reason=CONFIG_FAILURE_REASONS[result.status],http=`HTTP ${String(result.status)}`;return reason===void 0?`${http} after ${attempts}`:`${http} after ${attempts} (${reason})`}function applyInitConfig(initConfig){initConfig.apiKey!==void 0&&(apiKey=initConfig.apiKey),initConfig.apiHost!==void 0&&(apiBase=initConfig.apiHost),initConfig.flushIntervalMs!==void 0&&(config.flush_interval_ms=initConfig.flushIntervalMs),initConfig.maxQueueSize!==void 0&&(config.max_queue_size=initConfig.maxQueueSize),initConfig.walkDepth!==void 0&&(config.walk_depth=initConfig.walkDepth),initConfig.maxTextLength!==void 0&&(config.max_text_length=initConfig.maxTextLength),initConfig.autocapturePageviews!==void 0&&(config.autocapture_pageviews=initConfig.autocapturePageviews),initConfig.autocapture!==void 0&&(config.autocapture=initConfig.autocapture),initConfig.autocaptureElements!==void 0&&(config.autocapture_elements=initConfig.autocaptureElements),initConfig.piiUrlParams!==void 0&&(config.pii_url_params=initConfig.piiUrlParams),typeof initConfig.normalizePathIds=="boolean"&&(config.normalize_path_ids=initConfig.normalizePathIds),Array.isArray(initConfig.piiPathPatterns)&&(config.pii_path_patterns=initConfig.piiPathPatterns),initConfig.excludedUrls!==void 0&&(config.excluded_urls=initConfig.excludedUrls),initConfig.traits!==void 0&&setTraits(initConfig.traits,"The traits setting")}var ID_PATTERNS=[/^\d+$/,/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,/^[0-9a-f]{8,}$/i];function isIdSegment(segment){return ID_PATTERNS.some(p=>p.test(segment))}function buildPiiPattern(){if(config.pii_url_params.length===0)return null;let escaped=config.pii_url_params.map(p=>p.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp("("+escaped.join("|")+")","i")}function buildPiiPathPattern(){if(config.pii_path_patterns.length===0)return null;try{return new RegExp("^("+config.pii_path_patterns.join("|")+")$","i")}catch{return null}}function sanitizeUrl(url){try{let u=new URL(url),segments=u.pathname.split("/"),piiPathRegex=buildPiiPathPattern(),pathChanged=!1;for(let i=0;i<segments.length;i++){let seg=segments[i];if(seg){if(config.normalize_path_ids&&isIdSegment(seg)){segments[i]=":id",pathChanged=!0;continue}piiPathRegex?.test(seg)&&(segments[i]=":redacted",pathChanged=!0)}}pathChanged&&(u.pathname=segments.join("/"));let paramPattern=buildPiiPattern();if(paramPattern){let toRedact=[];u.searchParams.forEach((_v,k)=>{paramPattern.test(k)&&toRedact.push(k)});for(let k of toRedact)u.searchParams.set(k,":redacted")}return u.toString()}catch{return url}}function matchGlob(pattern,url){try{let path=new URL(url).pathname,escaped=pattern.replace(/[.+^${}()|[\]\\]/g,"\\$&");return new RegExp("^"+escaped.replace(/\*/g,".*")+"$").test(path)}catch{return!1}}function isExcludedUrl(url){return config.excluded_urls.some(pattern=>matchGlob(pattern,url))}function disableTracking(queue2){setTrackingDisabled(!0),queue2.length=0,config.autocapture_pageviews=!1,config.autocapture=!1}function extractText(el){let raw=el.textContent;if(!raw)return;let trimmed=raw.trim().replace(/\s+/g," ");if(trimmed.length!==0)return trimmed.length>config.max_text_length?trimmed.slice(0,config.max_text_length):trimmed}function extractDataTrack(el){let current=el;for(let i=0;i<=config.walk_depth&¤t;i++){let trackValue=current.getAttribute("data-track");if(trackValue!==null){let attrs={};for(let attr of current.attributes)if(attr.name.startsWith("data-track-")){let key=attr.name.slice(11);attrs[key]=attr.value}return{dataTrack:trackValue||void 0,dataTrackAttrs:Object.keys(attrs).length>0?attrs:void 0}}current=current.parentElement}return{dataTrack:void 0,dataTrackAttrs:void 0}}function getSegment(el){let seg=el.tagName.toLowerCase();el instanceof HTMLInputElement&&el.type&&(seg+='[type="'+CSS.escape(el.type)+'"]');let role=el.getAttribute("role");role&&(seg+='[role="'+CSS.escape(role)+'"]');let parent=el.parentElement;if(parent){let tag=el.tagName,siblings=parent.children,sameTag=0,idx=0;for(let i=0;i<siblings.length;i++){let sib=siblings[i];sib&&sib.tagName===tag&&(sameTag++,sib===el&&(idx=sameTag))}sameTag>1&&(seg+=":nth-of-type("+String(idx)+")")}return seg}var BODY="body > ",SEPARATOR=" > ",NTH_OF_TYPE=":nth-of-type(";function normalizeSelector(s){if(!s.startsWith(BODY))return s;let rest=s.slice(BODY.length),i=rest.indexOf(SEPARATOR),first=i>=0?rest.slice(0,i):rest,tail=i>=0?rest.slice(i):"";return BODY+withoutPosition(first)+tail}function withoutPosition(seg){if(!seg.endsWith(")"))return seg;let open=seg.lastIndexOf(NTH_OF_TYPE);if(open<0)return seg;let index=seg.slice(open+NTH_OF_TYPE.length,seg.length-1);if(index==="")return seg;for(let k=0;k<index.length;k++){let c=index[k];if(c===void 0||c<"0"||c>"9")return seg}return seg.slice(0,open)}function capped(parts){let result=parts.join(SEPARATOR);for(;result.length>200&&parts.length>1;)parts.shift(),result=parts.join(SEPARATOR);return result}function computeSelector(target){let parts=[],el=target;for(;el&&el!==document.documentElement;)parts.unshift(getSegment(el)),el=el.parentElement;return capped(normalizeSelector(parts.join(SEPARATOR)).split(SEPARATOR))}function getGenericSegment(el){let seg=el.tagName.toLowerCase();el instanceof HTMLInputElement&&el.type&&(seg+='[type="'+CSS.escape(el.type)+'"]');let role=el.getAttribute("role");return role&&(seg+='[role="'+CSS.escape(role)+'"]'),seg}function computeGenericSelector(target){let parts=[],el=target;for(;el&&el!==document.documentElement;)parts.unshift(getGenericSegment(el)),el=el.parentElement;return capped(parts)}function createPageviewEvent(referrer){return{event_type:"pageview",page_url:window.location.href,referrer:referrer===""?void 0:referrer,timestamp:new Date().toISOString()}}function createClickEvent(el){return createElementEvent(el,"click")}function createElementEvent(el,type){let{dataTrack,dataTrackAttrs}=extractDataTrack(el);return{event_type:type,page_url:window.location.href,element_tag:el.tagName.toLowerCase(),element_selector:computeSelector(el),element_selector_generalized:computeGenericSelector(el),element_text:extractText(el),data_track:dataTrack,data_track_attrs:dataTrackAttrs,timestamp:new Date().toISOString()}}function createFormSubmitEvent(form){let{dataTrack,dataTrackAttrs}=extractDataTrack(form);return{event_type:"form_submit",page_url:window.location.href,element_tag:form.tagName.toLowerCase(),element_selector:computeSelector(form),element_selector_generalized:computeGenericSelector(form),data_track:dataTrack,data_track_attrs:dataTrackAttrs,timestamp:new Date().toISOString()}}var pickerActive=!1,pickerDashboardOrigin=null;function setPickerActive(value){pickerActive=value}function setPickerDashboardOrigin(value){pickerDashboardOrigin=value}function backoffMs(attempt,random){let base=Math.min(6e4,2e3*2**Math.max(0,attempt-1));return Math.round(base/2+base/2*Math.min(1,Math.max(0,random)))}function retryAfterMs(value,now){if(value===null)return null;let v=value.trim(),ms;if(/^\d+$/.test(v))ms=Number(v)*1e3;else{let at=Date.parse(v);if(Number.isNaN(at))return null;ms=at-now}return Math.min(6e5,Math.max(0,ms))}function waitMs(attempt,retryAfter,random){return retryAfter===null?backoffMs(attempt,random):Math.max(retryAfter,backoffMs(1,random))}var REJECTION_REASONS={401:"the API key is missing or not valid",403:"this site's origin is not in the project's allowed domains",429:"the project is over its plan's monthly event limit"},MONTHLY_LIMIT="limit_events_per_month",MAX_BATCH=100,backoff={until:0,attempts:0,noted:!1},queue=[],lastUrl="",isFlushing=!1;function setLastUrl(value){lastUrl=value}function sanitizeEvent(event){let pageUrl=sanitizeUrl(event.page_url),referrer=event.referrer===void 0?void 0:sanitizeUrl(event.referrer);return pageUrl===event.page_url&&referrer===event.referrer?event:{...event,page_url:pageUrl,...referrer!==void 0&&{referrer}}}function prepareEvents(events){return filterEvents(events.map(sanitizeEvent))}function filterEvents(events){return events.filter(event=>!(isExcludedUrl(event.page_url)||event.event_type==="pageview"&&!config.autocapture_pageviews||(event.event_type==="click"||event.event_type==="form_submit")&&!config.autocapture&&!event.data_track||event.event_type==="dead_click"&&!(config.autocapture&&config.capture_dead_clicks)||event.event_type==="rage_click"&&!(config.autocapture&&config.capture_rage_clicks)||event.event_type==="error_click"&&!(config.autocapture&&config.capture_error_clicks)||event.event_type==="slow_click"&&!(config.autocapture&&config.capture_slow_clicks)))}function enqueue(event){trackingDisabled||pickerActive||apiKey&&(queue.length>=MAX_QUEUE_CAP||(queue.push(event),configLoaded&&queue.length>=config.max_queue_size&&flush()))}var flushing=null;async function flush(){if(trackingDisabled||pickerActive||!configLoaded||isFlushing||queue.length===0||Date.now()<backoff.until)return;isFlushing=!0;let events=prepareEvents(queue.splice(0,MAX_BATCH));if(events.length===0){isFlushing=!1;return}let sent=send(events,!1,null);flushing=sent;try{await sent}finally{flushing=null,isFlushing=!1}}async function send(events,beacon,before){let body={events,...traits.length>0&&{traits}};beacon&&apiKey&&(body.api_key=apiKey);let headers={"Content-Type":"application/json"};apiKey&&(headers["X-API-Key"]=apiKey);let response;try{response=await fetch(`${apiBase}/api/events`,{method:"POST",headers,body:JSON.stringify(body),keepalive:!0,credentials:"omit"})}catch{return putBack(events,null,before)}if(response.ok)return backoff.until=0,backoff.attempts=0,0;let status=response.status;return status===401||status===403||status===429&&await errorCode(response)===MONTHLY_LIMIT?(trackingDisabled||console.warn(`[tracker] The API rejected events with HTTP ${String(status)} \u2014 ${REJECTION_REASONS[status]??"the request was refused"}. Tracking is now off for this page.`),disableTracking(queue),0):status===429||status>=500?(status===429&&!backoff.noted&&(backoff.noted=!0,console.info("[tracker] The API asked to slow down (HTTP 429). Events are kept and sent again shortly.")),putBack(events,response,before)):0}async function errorCode(response){try{let body=await response.json(),code=typeof body=="object"&&body!==null?body.error_code:void 0;return typeof code=="string"?code:void 0}catch{return}}async function putBack(events,response,before){let at=before?await before:0;return trackingDisabled?0:(queue.splice(Math.min(at,queue.length),0,...events),queue.length>MAX_QUEUE_CAP&&(queue.length=MAX_QUEUE_CAP),backOff(response),events.length)}function backOff(response){backoff.attempts++;let now=Date.now(),told=response?retryAfterMs(response.headers.get("Retry-After"),now):null;backoff.until=now+waitMs(backoff.attempts,told,Math.random())}function flushBeacon(){if(trackingDisabled||!configLoaded||queue.length===0)return;let events=prepareEvents(queue.splice(0,MAX_BATCH));events.length!==0&&send(events,!0,flushing)}function isSensitive(el){if(el instanceof HTMLInputElement){let t=el.type.toLowerCase();if(t==="password"||t==="hidden")return!0}let ac=el.getAttribute("autocomplete");return!!(ac&&ac.toLowerCase().startsWith("cc-"))}function isInteractive(el){if(el.hasAttribute("data-track"))return!0;if(!config.autocapture)return!1;for(let selector of config.autocapture_elements)try{if(el.matches(selector))return!0}catch{}return!1}function findInteractiveAncestor(target){let el=target;for(let i=0;i<=config.walk_depth&⪙i++){if(isInteractive(el))return el;el=el.parentElement}return null}function isPickerTarget(el){return isInteractive(el)||el.matches("form")}function findPickerTarget(target){let el=target;for(let i=0;i<=config.walk_depth&⪙i++){if(isPickerTarget(el))return el;el=el.parentElement}return null}function trackableElements(){let selectors=["[data-track]"];config.autocapture&&selectors.push("form",...config.autocapture_elements);let seen2=new Set;for(let selector of selectors){let found;try{found=document.querySelectorAll(selector)}catch{continue}for(let el of found)seen2.add(el)}let result=[];for(let el of seen2)isSensitive(el)||result.push(el);return result}var PICKER_HANDOFF_KEY="__stPickerHandoff";var PICKER_FILE="tracker-picker.js",picker=null,loading=!1,failed=!1,pendingOptions=null,pickerHostApi={setActive:setPickerActive,setDashboardOrigin:setPickerDashboardOrigin,clearQueue:()=>{queue.length=0},computeSelector,computeGenericSelector,extractText,extractDataTrack,findPickerTarget,trackableElements,sanitizeUrl};function pickerScriptUrl(){for(let script of document.querySelectorAll("script[src]")){let src=script.getAttribute("src");if(src?.includes("tracker.js"))try{return new URL(PICKER_FILE,new URL(src,document.baseURI)).toString()}catch{}}return apiBase?apiBase.replace(/\/+$/,"")+"/"+PICKER_FILE:"/"+PICKER_FILE}function registerPicker(module){if(picker=module,loading=!1,pendingOptions){let options=pendingOptions;pendingOptions=null,activate(module,options)}}function activate(module,options){module.activate({...options,dashboardOrigin:pickerDashboardOrigin})}function abandonLoad(url){loading=!1,failed=!0,pendingOptions=null,setPickerActive(!1),console.warn(`[tracker] Could not load the Element Picker from ${url}. Tracking is unaffected.`)}function loadPicker(){let url=pickerScriptUrl(),script=document.createElement("script");script.src=url,script.async=!0,script[PICKER_HANDOFF_KEY]={host:pickerHostApi,register:registerPicker},script.addEventListener("load",()=>{script.remove(),picker||abandonLoad(url)}),script.addEventListener("error",()=>{script.remove(),abandonLoad(url)}),document.head.appendChild(script)}function requestPicker(options){if(picker){activate(picker,options??{});return}failed||(setPickerActive(!0),queue.length=0,pendingOptions=options??{},!loading&&(loading=!0,loadPicker()))}function onPickerNavigation(){picker?.onNavigation()}function onPickerWindowMessage(e){if(picker){picker.handleMessage(e);return}if(typeof e.data!="object"||e.data===null)return;let data=e.data;data.type==="st-picker-activate"&&(pickerDashboardOrigin&&e.origin!==pickerDashboardOrigin||pickerActive||!window.opener||requestPicker(data.mode==="click-map"?{mode:"click-map"}:void 0))}function parsePickerHash(hash){let m=/^#st-picker=([^&]+)(?:&(.*))?$/.exec(hash),raw=m?.[1];if(!m||!raw)return null;let origin;try{origin=decodeURIComponent(raw)}catch{return null}let mode="rules";for(let pair of(m[2]??"").split("&"))pair==="mode=click-map"&&(mode="click-map");return{origin,mode}}var PRESS_MS=5e3,press=null;function disabled(el){return el.matches(":is(button,input,select,textarea):disabled")||el.getAttribute("aria-disabled")==="true"}function notePress(e){if(press=null,!e.isTrusted||e.button!==0)return;let target=e.target;if(!(target instanceof Element))return;let control=findInteractiveAncestor(target);!control||isSensitive(control)||disabled(control)||(press={id:e.pointerId,control,rect:control.getBoundingClientRect(),at:Date.now(),click:null,target:null})}function inside(r,x,y){return x>=r.left&&x<=r.right&&y>=r.top&&y<=r.bottom}function clickTarget(e){let target=e.target,p=press;if(!p||!e.isTrusted||e.detail===0)return target;if(p.click===e)return p.target??target;if(p.click!==null||(p.click=e,!(target instanceof Element)||findInteractiveAncestor(target)!==null||Date.now()-p.at>PRESS_MS||!p.control.isConnected))return target;let id=e.pointerId;if(typeof id=="number"&&id!==-1&&id!==p.id)return target;let x=e.clientX,y=e.clientY;return!inside(p.rect,x,y)&&!inside(p.control.getBoundingClientRect(),x,y)?target:(p.target=p.control,p.control)}function findLooksClickable(target,depth,parent,look2){let el=target;for(let i=0;i<=depth&⪙i++){let f=look2(el);if(f&12)return null;if(f&1){let top=el,up=parent(el);for(;up;i++){let g=look2(up);if(!(g&1))return top;if(g&8||i>=depth)return null;top=up,up=parent(up)}return top}if(f&2)return el;el=parent(el)}return null}function walkUp(target,depth,parent,pred){let el=target;for(let i=0;i<=depth&⪙i++){if(pred(el))return el;el=parent(el)}return null}function leavesDocument(href,page){try{let u=new URL(href,page);if(u.protocol==="javascript:")return!1;if(!href.includes("#"))return!0;let p=new URL(page);return u.hash="",p.hash="",u.href!==p.href}catch{return!0}}function reloadsDocument(href,page){try{let u=new URL(href,page);if(u.protocol==="javascript:")return!1;let p=new URL(page);return u.hash="",p.hash="",u.href===p.href}catch{return!1}}var UNDECIDED=0,FOLLOWED=1,CANCELLED=2,INTERCEPTED=3;function linkFate(dispatching,prevented){return prevented?CANCELLED:dispatching?UNDECIDED:FOLLOWED}function navFate(dispatching,done,inFlight){return dispatching?UNDECIDED:done?FOLLOWED:inFlight?INTERCEPTED:FOLLOWED}function abortMeans(navigationType,userInitiated){return navigationType==="traverse"||userInitiated?"moved-on":"live"}function decideLinks(pending2,fate){for(let i=0;i<pending2.length;i++){let p=pending2[i];if(p?.link===void 0)continue;let f=fate(p);f===FOLLOWED?pending2.splice(i--,1):f!==UNDECIDED&&delete p.link}}function elementKey(ev){return JSON.stringify([ev.element_selector_generalized??null,ev.element_text??null,ev.element_tag??null])}var MAX_REPORTED=100;function claim(seen2,key){return seen2.has(key)||seen2.size>=MAX_REPORTED?!1:(seen2.add(key),!0)}function nextBurst(burst2,key,first,now,windowMs){return burst2?.key===key&&now-burst2.last<=windowMs?(burst2.count++,burst2.last=now,burst2):{key,first,count:1,last:now}}var MAX_CLICK_COUNT=1e3;function rageCount(count,threshold,selected=!1){return count>=Math.max(selected?4:3,threshold)?Math.min(count,MAX_CLICK_COUNT):null}function selectionIsEffect(current,made,touchesMade=!1){return current!==null&&!touchesMade&&!(made&&made.anchor===current.anchor&&made.anchorOffset===current.anchorOffset&&made.focus===current.focus&&made.focusOffset===current.focusOffset)}var MAX_PENDING=8;function openWindow(pending2,seen2,p){if(seen2.every(s=>s.has(p.key))||pending2.length>=MAX_PENDING)return!1;for(let q of pending2)if(q.key===p.key)return!1;return pending2.push(p),!0}var IGNORE=0,LIVE=1,CAUSED=2,SUSPECT=3;function classify(related,noisy2,global=!1,ticking=!1){return noisy2?related?LIVE:IGNORE:related||global?CAUSED:ticking?SUSPECT:LIVE}function isRelated(node,el,depth,parent,contains2){return contains2(el,node)||walkUp(el,depth,parent,n=>n===node)!==null}function inHead(node,head,contains2){return head!==null&&contains2(head,node)}var DRAWS_NOTHING=/^(script|title|meta|base|noscript|template)$/;function drawsNothing(tag,rel){return tag==="link"?!/(^|\s)stylesheet(\s|$)/i.test(rel??""):DRAWS_NOTHING.test(tag)}function unseenMutation(type,target,attribute,added,removed,read){let silent=el=>{let tag=el===null?null:read.tag(el);return el!==null&&tag!==null&&drawsNothing(tag,read.rel(el))};if(type==="attributes"&&attribute==="rel"&&read.tag(target)==="link")return!1;if(type!=="childList")return silent(read.tag(target)===null?read.parent(target):target);for(let nodes of[added,removed])for(let i=0;i<nodes.length;i++){let n=nodes[i];if(read.tag(n)!==null?!silent(n):!read.blank(n)&&!silent(target))return!1}return!0}var NOISE_GAP_MS=6e3,NOISE_REPEAT_MS=250;function noteStreak(s,t){return s?(t-s.last>NOISE_GAP_MS?(s.start=s.beatAt=t,s.since=1/0,s.beats=1):(s.since===1/0&&t-s.start>=NOISE_REPEAT_MS&&(s.since=t),t-s.beatAt>=NOISE_REPEAT_MS&&(s.beats++,s.beatAt=t)),s.last=t,s):{start:t,last:t,since:1/0,beats:1,beatAt:t}}function noisyAt(s,at){return s!==void 0&&s.since<at}function tickingAt(s,at,t){return s!==void 0&&s.start<at&&t-s.last<=NOISE_GAP_MS}var MAX_DOUBTS=8;function addDoubt(doubts,node,t,beats){for(let d of doubts)if(d.node===node)return!0;return doubts.length>=MAX_DOUBTS?!1:(doubts.push({node,t,beats}),!0)}function judgeDoubts(doubts,at,now,streakOf2){let open=!1;for(let d of doubts){let s=streakOf2(d.node);if(!s||s.start>=at||now-s.last>NOISE_GAP_MS)return"live";s.beats-d.beats<2&&(open=!0)}return open?"open":"noise"}var NONE=0,DROP=1,SLOW=2,DOUBT=3;function effectOn(p,kind,t){return kind===IGNORE?NONE:kind===SUSPECT?DOUBT:kind===LIVE||t<p.slowAt||t>=p.due?DROP:SLOW}function effectOnRoute(p,answers,kind,t){return kind===IGNORE?NONE:!answers||kind===SUSPECT?(p.stirred=!0,NONE):effectOn(p,kind,t)===SLOW?SLOW:DROP}function moveOn(pending2,now,el){for(let p of pending2)p.el!==el&&!p.verdict&&(p.due=Math.max(p.slowAt,Math.min(p.due,now)))}var MAX_RESPONSE_MS=6e4;function responseMs(at,t){return Math.min(MAX_RESPONSE_MS,Math.max(0,Math.round(t-at)))}function errorEventCounts(trusted,rejection){return trusted||rejection}var ERROR_NAME=/^[A-Za-z_$][A-Za-z0-9_$.]{0,63}$/;function errorType(err,rejection){if(!(err instanceof Error))return rejection?"UnhandledRejection":"Error";let name;try{name=err.name}catch{return"Error"}return typeof name=="string"&&ERROR_NAME.test(name)?name:"Error"}function ambientError(last,at,windowMs){return last!==void 0&&last<at&&at-last<=windowMs}function takeSettled(pending2,now,final,streakOf2,gone2=()=>!1){let out=[];for(let i=0;i<pending2.length;i++){let p=pending2[i];if(!p)continue;if(!p.verdict){if(p.due>now)continue;if(p.stirred&&(p.appeared||!gone2(p))){pending2.splice(i--,1);continue}p.verdict="dead"}let j=judgeDoubts(p.doubts,p.at,now,streakOf2);if(j==="open"&&!final){p.due<=now&&(p.due=now+NOISE_REPEAT_MS);continue}pending2.splice(i--,1),j==="noise"&&out.push(p)}return out}function nextDue(pending2){let next=1/0;for(let p of pending2)p.due<next&&(next=p.due);return next}function holdsClick(prev,key,now,windowMs,detail){return detail!==0&&prev?.key===key&&now-prev.at<=windowMs}var MAX_HELD=32;function openHeld(held2,el,at,due,live2){if(held2.length>=MAX_HELD)return null;let h={ev:null,el,at,due,live:live2};return held2.push(h),h}function attachHeld(held2,h,ev){let i=held2.indexOf(h);return i<0?!1:h.live?(held2.splice(i,1),!1):(h.ev=ev,!0)}function releaseHeld(held2,did=()=>!0){let out=[];for(let i=0;i<held2.length;i++){let h=held2[i];!h||h.live||!did(h)||(h.ev!==null?(out.push(h.ev),held2.splice(i--,1)):h.live=!0)}return out}function takeDueHeld(held2,now){let out=[];for(let i=0;i<held2.length;i++){let h=held2[i];h&&h.due<=now&&(h.ev!==null&&!h.live&&out.push(h.ev),held2.splice(i--,1))}return out}function heldWaiting(held2){for(let h of held2)if(!h.live)return!0;return!1}var PRESS_MS2=5e3,STALL_MS=250,MAX_ERROR_TYPES=32;function newSeen(){return{dead:new Set,rage:new Set,slow:new Set,error:new Set}}var seen=newSeen(),nav=null,aborted=null,pending=[],burst=null,lastKey="",madeSel=null,madeEl=null,lastClick=null,held=[],holding=null,holdTarget=null,errClick=null,errorTimes=new Map,streaks=new WeakMap,pressTarget=null,pressEl=null,pressAt=0,pressUntil=0,pressEffect=!1,pressDoubts=[],inTask=!1,observer=null,observing=!1,listening=!1,timer,timerDue=0,live=()=>LIVE,caused=()=>CAUSED,notNav=p=>!p.nav;function parentOf(el){return el.parentElement}function nearParent(n){let p=n.parentNode;return p instanceof Element&&p!==document.body&&p!==document.documentElement?p:null}function contains(outer,inner){return outer.contains(inner)}function noisy(node,at){return walkUp(node,config.walk_depth,nearParent,n=>noisyAt(streaks.get(n),at))!==null}function tickingNode(node,at,t){let found=null,n=node;for(let i=0;i<=config.walk_depth&&n;i++)tickingAt(streaks.get(n),at,t)&&(found=n),n=nearParent(n);return found}function streakOf(n){return streaks.get(n)}function note(node,t){let s=streaks.get(node),n=noteStreak(s,t);s||streaks.set(node,n)}function isDisabled(el){return el.matches(":is(button,input,select,textarea):disabled")||el.getAttribute("aria-disabled")==="true"&&isInteractive(el)}function isEditable(el){return el instanceof HTMLInputElement?!/^(button|submit|image)$/.test(el.type):/^(TEXTAREA|SELECT)$/.test(el.tagName)||el.isContentEditable}function look(el){if(/^(canvas|video|audio|input|textarea|select)$/i.test(el.tagName)||isEditable(el))return 4;let s=getComputedStyle(el),f=s.cursor==="pointer"?1:0;return el===document.body||el===document.documentElement?f|8:el.tagName==="IMG"||el.tagName==="svg"||s.textDecorationLine.includes("underline")?f|2:f}function active(){return!pickerActive&&!trackingDisabled&&config.autocapture&&(config.capture_dead_clicks||config.capture_rage_clicks||config.capture_slow_clicks||config.capture_error_clicks)}function rolling(){return active()&&(config.capture_dead_clicks||config.capture_slow_clicks)}function idle(links=!0){return(links?pending.length===0:pending.every(p=>p.link))&&(pressEffect||!pressTarget)&&!heldWaiting(held)}function fateOf(p){let e=p.link;if(!e)return FOLLOWED;let f=linkFate(e.eventPhase!==0,e.defaultPrevented);if(f!==FOLLOWED||!p.nav)return f;let n=navigation();return navFate(p.nav.e.eventPhase!==0,p.nav.done,!!n&&n.transition!==null)}function navigation(){return window.navigation??null}function gone(p){let a=p.route;if(!a)return!1;if(!a.isConnected)return!0;if(a.getClientRects().length>0)return!1;if(getComputedStyle(a).display!=="contents")return!0;for(let i=0;i<a.children.length;i++)if((a.children[i]?.getClientRects().length??0)>0)return!1;return!0}function decide(){decideLinks(pending,fateOf)}function resolve(target){if(target.closest("#st-picker-host"))return null;let control=findInteractiveAncestor(target);if(control)return isSensitive(control)||isEditable(control)?null:control;let t=target;for(;t instanceof SVGElement&&t.ownerSVGElement;)t=t.ownerSVGElement;let el=findLooksClickable(t,config.walk_depth,parentOf,look);return el&&!isSensitive(el)?el:null}function selection(){let s=getSelection();return s&&!s.isCollapsed?{anchor:s.anchorNode,anchorOffset:s.anchorOffset,focus:s.focusNode,focusOffset:s.focusOffset}:null}function selectionEffect(){let s=getSelection();return selectionIsEffect(selection(),madeSel,!!madeEl&&!!s&&s.containsNode(madeEl,!0))}function startTask(){inTask||(inTask=!0,setTimeout(()=>{inTask=!1},0))}function report(el,now,detail,dead,target,doubts,disabled2=!1,click){let ev=createElementEvent(el,"dead_click"),key=elementKey(ev),rage=detail!==0,threshold=config.slow_click_threshold_ms,wait=Math.max(threshold,config.dead_click_wait_ms),multi=detail>=2&&key===lastKey;if(madeSel=multi?selection():null,madeEl=multi?el:null,lastKey=key,holding=null,holdTarget=target,rage&&(holdsClick(lastClick,key,now,config.rage_click_window_ms,detail)&&(holding=openHeld(held,el,now,now+threshold,!dead)),lastClick={key,at:now}),rage&&config.capture_rage_clicks){let next=nextBurst(burst,key,ev,now,config.rage_click_window_ms);next!==burst&&endBurst(),burst=next;let s=getSelection();next.selected=!!s&&!s.isCollapsed&&s.containsNode(el,!0)}errClick=config.capture_error_clicks&&!seen.error.has(key)?{key,ev,el,at:now,due:now+wait}:null;let link=el.closest("a[href]"),href=link?.getAttribute("href")??"",leaves=!!link&&leavesDocument(href,location.href),reloads=leaves&&reloadsDocument(href,location.href),route=leaves&&click&&!reloads?link:null;if((dead||route)&&!(leaves&&(!click||reloads))&&!el.closest("label")){let kinds=[];config.capture_dead_clicks&&kinds.push(seen.dead),config.capture_slow_clicks&&!disabled2&&kinds.push(seen.slow);let slowAt=now+threshold,p={key,ev,el,at:now,slowAt,due:disabled2?slowAt:now+wait,doubts:route?[]:doubts.slice(),seen};route&&(p.link=click,p.route=route,(!dead||doubts.length>0)&&(p.stirred=!0)),openWindow(pending,kinds,p)&&route&&setTimeout(sync,0)}sync()}function endBurst(){let b=burst;burst=null;let n=b&&rageCount(b.count,config.rage_click_threshold,b.selected);b&&n&&claim(seen.rage,b.key)&&enqueue({...b.first,event_type:"rage_click",click_count:n})}function happen(kindAt,suspect,answers,appears=!1){let t=Date.now();decide();let doubt=(doubts,at)=>{let n=suspect?.(at),s=n?streaks.get(n):void 0;return!!n&&!!s&&addDoubt(doubts,n,t,s.beats)};if(pressTarget&&!pressEffect){let k=inTask||!pressEl?LIVE:kindAt(pressEl,pressAt);k!==IGNORE&&!(k===SUSPECT&&doubt(pressDoubts,pressAt))&&(pressEffect=!0)}for(let i=0;i<pending.length;i++){let p=pending[i];if(!p||p.verdict)continue;if(p.route){let k=inTask?LIVE:kindAt(p.el,p.at),r2=effectOnRoute(p,answers?.(p)??!1,k,t);r2===NONE&&appears&&k!==IGNORE&&(p.appeared=!0),r2===SLOW?(p.verdict="slow",p.answeredAt=t):r2!==NONE&&pending.splice(i--,1);continue}let r=effectOn(p,inTask?LIVE:kindAt(p.el,p.at),t);r===NONE||r===DOUBT&&doubt(p.doubts,p.at)||(r===SLOW?(p.verdict="slow",p.answeredAt=t):pending.splice(i--,1))}let did=h=>{let k=inTask?LIVE:kindAt(h.el,h.at);return k!==IGNORE&&k!==SUSPECT};for(let ev of releaseHeld(held,did))enqueue(ev)}function markLive(){idle()||(happen(live),sync())}function markCaused(){idle()||(happen(caused),sync())}function onToggle(e){idle()||(happen(caused,void 0,void 0,e.newState!=="closed"),sync())}function markHistory(changed){changed?idle()||(happen(caused,void 0,notNav),sync()):markLive()}function settle(now,final){decide();for(let p of takeSettled(pending,now,final,streakOf,gone))p.verdict==="slow"?config.capture_slow_clicks&&claim(p.seen.slow,p.key)&&enqueue({...p.ev,event_type:"slow_click",response_ms:responseMs(p.at,p.answeredAt??now)}):config.capture_dead_clicks&&claim(p.seen.dead,p.key)&&enqueue(p.ev)}function movedOn(now,el,errors){if(errors&&errClick&&errClick.el!==el&&(errClick=null),pending.length===0)return;let records=observer?.takeRecords();records?.length&&onMutations(records),selectionEffect()&&happen(live),moveOn(pending,now,el),settle(now,!1)}var reader={tag:n=>n instanceof Element?n.tagName.toLowerCase():null,rel:n=>n instanceof Element?n.getAttribute("rel"):null,blank:n=>n.nodeType===8||!n.textContent?.trim(),parent:n=>n.parentNode};function unseen(r,head){return inHead(r.target,head,contains)&&unseenMutation(r.type,r.target,r.attributeName,r.addedNodes,r.removedNodes,reader)}function adds(r,head){if(r.type!=="childList"||inHead(r.target,head,contains))return!1;for(let i=0;i<r.addedNodes.length;i++){let n=r.addedNodes[i];if(n&&(n instanceof Element||!reader.blank(n)))return!0}return!1}function onMutations(records){let t=Date.now(),head=document.head,shown=records;for(let r of records)if(unseen(r,head)){shown=records.filter(q=>!unseen(q,head));break}for(let r of shown)note(r.target,t);for(let r of shown){if(idle())break;let node=r.target,dialog=r.attributeName==="open"&&/^(DIALOG|DETAILS)$/.test(node.nodeName);happen(dialog?caused:(el,at)=>classify(isRelated(node,el,config.walk_depth,nearParent,contains),noisy(node,at),!1,tickingNode(node,at,t)!==null),at=>tickingNode(node,at,t),p=>!!p.route&&p.route.contains(node)&&(node!==p.route||r.type!=="attributes"||r.attributeName==="aria-busy"),dialog&&node instanceof Element&&node.hasAttribute("open")||adds(r,head))}sync()}function holdClick(e,ev){let h=holding;return holding=null,!!h&&e.isTrusted&&e.detail!==0&&clickTarget(e)===holdTarget&&attachHeld(held,h,ev)}function onFocusIn(e){let f=e.target;pressTarget&&f instanceof Node&&f.contains(pressTarget)||markCaused()}function onScroll(e){let node=e.target instanceof Node?e.target:document;note(node,Date.now()),!idle()&&(happen((el,at)=>classify(node.contains(el),noisyAt(streaks.get(node),at),!0)),sync())}function onKeyDown(e){if(!e.isTrusted||pending.length===0&&!errClick)return;let t=e.target;movedOn(Date.now(),(e.key==="Enter"||e.key===" ")&&t instanceof Element?t:null,!0),sync()}function onWheel(e){!e.isTrusted||pending.length===0||(movedOn(Date.now(),null,!1),sync())}function onLeave(){movedOn(Date.now(),null,!1),markLive(),sync()}function onNavigate(e){let prev=aborted??(nav&&!nav.done?nav:null);if(aborted=null,prev){prev.done=!0;let ne=e;abortMeans(ne.navigationType??"",ne.userInitiated===!0)==="moved-on"?navMovedOn(prev):navAnswer(prev,!1)}let n={e,done:!1};nav=n;for(let p of pending){let c=p.link;c&&c.eventPhase===0&&!c.defaultPrevented&&!p.nav&&(p.nav=n)}}function navDone(ok){let n=nav;!n||n.done||(n.done=!0,navAnswer(n,ok))}function onNavigateError(e){let n=nav,err=e.error;if(!(typeof err=="object"&&err!==null&&err.name==="AbortError")||!n||n.done){navDone(!1);return}n.done=!0,aborted=n,setTimeout(()=>{aborted===n&&(aborted=null,navAnswer(n,!1))},0)}function navMovedOn(n){decide();let now=Date.now();for(let p of pending)p.nav===n&&!p.link&&!p.verdict&&(p.due=Math.max(p.slowAt,Math.min(p.due,now)));sync()}function navAnswer(n,ok){if(pending.length===0)return;let t=Date.now();decide();for(let i=0;i<pending.length;i++){let p=pending[i];if(!p||p.nav!==n||p.link||p.verdict)continue;let r=effectOnRoute(p,!0,ok?CAUSED:LIVE,t);r===SLOW?(p.verdict="slow",p.answeredAt=t):r!==NONE&&pending.splice(i--,1)}sync()}function noteError(type){let now=Date.now(),last=errorTimes.get(type);last===void 0&&errorTimes.size>=MAX_ERROR_TYPES&&errorTimes.clear(),errorTimes.set(type,now);let c=errClick;!c||now>c.due||!active()||!config.capture_error_clicks||!inTask&&ambientError(last,c.at,NOISE_GAP_MS)||(errClick=null,claim(seen.error,c.key)&&enqueue({...c.ev,event_type:"error_click",error_type:type}))}function onError(e){e instanceof ErrorEvent&&errorEventCounts(e.isTrusted,!1)&¬eError(errorType(e.error,!1))}function onRejection(e){errorEventCounts(e.isTrusted,!0)&¬eError(errorType(e.reason,!0))}function sync(){settle(Date.now(),!1);let watch=!idle(),observe=watch||rolling();observe!==observing&&(observe?(typeof MutationObserver=="function"&&(observer??=new MutationObserver(onMutations)),observer?.observe(document,{subtree:!0,childList:!0,attributes:!0,characterData:!0})):observer?.disconnect(),observing=observe);let leave=watch&&!idle(!1);leave!==listening&&(listening=leave,leave?window.addEventListener("beforeunload",onLeave):window.removeEventListener("beforeunload",onLeave));let next=Math.min(nextDue(pending),nextDue(held),burst?burst.last+config.rage_click_window_ms+1:1/0,pressTarget?pressUntil:1/0);clearTimeout(timer),timer=void 0,next<1/0&&(timerDue=next,timer=setTimeout(tick,Math.max(0,next-Date.now())))}function tick(){let now=Date.now(),late=now-timerDue>STALL_MS,records=observer?.takeRecords();records?.length&&onMutations(records),(late||selectionEffect())&&happen(live),settle(now,!1);for(let ev of takeDueHeld(held,now))active()||enqueue(ev);burst&&now-burst.last>config.rage_click_window_ms&&endBurst(),pressTarget&&now>=pressUntil&&(pressTarget=pressEl=null),sync()}function onPointerDown(e){if(!e.isTrusted)return;let target=e.target,now=Date.now();if(!(target instanceof Element)||e.button!==0||!active()){movedOn(now,null,!0),sync();return}let disabled2=walkUp(target,config.walk_depth,parentOf,isDisabled),el=disabled2??resolve(target);if(movedOn(now,el,!0),startTask(),holding=null,pressTarget=target,pressEffect=!1,pressDoubts=[],pressAt=now,pressUntil=now+PRESS_MS2,disabled2){pressEl=null,pressEffect=!0,!isSensitive(disabled2)&&!target.closest("#st-picker-host")?report(disabled2,now,1,!0,target,[],!0):sync();return}pressEl=el,pressEl||(pressTarget=null),sync()}function onClick(e){if(!e.isTrusted||!active())return;let target=clickTarget(e);if(!(target instanceof Element))return;startTask();let press2=pressTarget,effect=press2!==null&&pressEffect,el=press2===target?pressEl:resolve(target);pressTarget=pressEl=null;let multi=e.detail>=2;el&&!walkUp(target,config.walk_depth,parentOf,isDisabled)&&(isInteractive(el)||multi||getSelection()?.isCollapsed!==!1)?report(el,Date.now(),e.detail,!effect,target,press2?pressDoubts:[],!1,e):sync()}function onFrustrationHide(){onLeave(),settle(Date.now(),!0),endBurst()}function resetFrustration(){let own=nav&&!nav.done?nav:null,waiting=own?pending.filter(p=>p.nav===own):[];for(let p of waiting)pending.splice(pending.indexOf(p),1);movedOn(Date.now(),null,!1),markLive();for(let p of waiting)pending.push(p);waiting.length>0&&sync(),endBurst(),lastClick=null,lastKey="",madeSel=null,madeEl=null,seen=newSeen()}function startFrustration(){let capture={capture:!0,passive:!0};window.addEventListener("pointerdown",onPointerDown,capture),window.addEventListener("click",onClick,capture),window.addEventListener("focusin",onFocusIn,capture),window.addEventListener("keydown",onKeyDown,capture),window.addEventListener("wheel",onWheel,capture),window.addEventListener("error",onError),window.addEventListener("unhandledrejection",onRejection);for(let type of["blur","pagehide","popstate","hashchange"])window.addEventListener(type,onLeave);let n=navigation();n&&(n.addEventListener("navigate",onNavigate),n.addEventListener("navigatesuccess",()=>{navDone(!0)}),n.addEventListener("navigateerror",onNavigateError)),document.addEventListener("scroll",onScroll,capture),document.addEventListener("toggle",onToggle,capture);for(let type of["submit","invalid","change","play","copy"])document.addEventListener(type,markLive,capture);sync()}function onClickCapture(e){if(pickerActive)return;let target=clickTarget(e);if(!(target instanceof Element))return;let interactive=findInteractiveAncestor(target);if(!interactive||isSensitive(interactive))return;let ev=createClickEvent(interactive);holdClick(e,ev)||enqueue(ev)}function onSubmitCapture(e){if(pickerActive)return;let form=e.target;form instanceof HTMLFormElement&&(isSensitive(form)||!config.autocapture&&!extractDataTrack(form).dataTrack||enqueue(createFormSubmitEvent(form)))}function onSpaNavigation(){if(pickerActive){onPickerNavigation();return}if(!config.autocapture_pageviews)return;let currentUrl=window.location.href;if(currentUrl===lastUrl)return;let referrer=lastUrl;setLastUrl(currentUrl),resetFrustration(),enqueue(createPageviewEvent(referrer))}var inHistoryCall=!1;function patchHistory(){let origPushState=history.pushState.bind(history),origReplaceState=history.replaceState.bind(history);history.pushState=function(...args){let before=location.href;inHistoryCall=!0;try{origPushState(...args)}finally{inHistoryCall=!1}markHistory(location.href!==before),onSpaNavigation()},history.replaceState=function(...args){let before=location.href;inHistoryCall=!0;try{origReplaceState(...args)}finally{inHistoryCall=!1}markHistory(location.href!==before),onSpaNavigation()}}function listenNavigation(){window.navigation?.addEventListener("currententrychange",()=>{inHistoryCall||onSpaNavigation()})}var version="1.2.0",initialized=!1,storedInitConfig={};function init(initConfig){if(typeof window>"u")return;if(initialized){console.warn("[tracker] Already initialized. Ignoring duplicate init() call."+(apiKey?"":" The first init() call had no apiKey, so tracking stays off for this page load."));return}if(initialized=!0,storedInitConfig=initConfig??{},storedInitConfig.apiKey!==void 0&&setApiKey(storedInitConfig.apiKey),storedInitConfig.apiHost!==void 0&&setApiBase(storedInitConfig.apiHost),!apiKey){console.error("[tracker] Missing apiKey. Events will not be sent. Later init() calls are ignored, so call init() once, with the key.");return}storedInitConfig.traits!==void 0&&(storedInitConfig={...storedInitConfig,traits:acceptTraits(storedInitConfig.traits,"The traits setting")}),applyInitConfig(storedInitConfig),setLastUrl(window.location.href),config.autocapture_pageviews&&enqueue(createPageviewEvent(document.referrer)),patchHistory(),window.addEventListener("popstate",onSpaNavigation),listenNavigation(),window.addEventListener("pointerdown",notePress,{capture:!0,passive:!0}),document.addEventListener("click",onClickCapture,{capture:!0}),document.addEventListener("submit",onSubmitCapture,{capture:!0}),startFrustration(),window.addEventListener("message",onPickerWindowMessage);let pickerHash=parsePickerHash(window.location.hash);if(pickerHash){let{origin,mode}=pickerHash;setPickerDashboardOrigin(origin);let clickMap=mode==="click-map";try{sessionStorage.setItem("st-picker",JSON.stringify({active:!0,origin,...clickMap&&{mode}}))}catch{}history.replaceState(null,"",window.location.pathname+window.location.search),window.opener&&requestPicker(clickMap?{mode}:void 0)}else try{let stored=sessionStorage.getItem("st-picker");if(stored&&window.opener){let parsed=JSON.parse(stored);if(typeof parsed=="object"&&parsed!==null&&"active"in parsed&&"origin"in parsed){let obj=parsed;if(obj.active&&typeof obj.origin=="string"){setPickerDashboardOrigin(obj.origin);let storedScan="scanEnabled"in obj&&obj.scanEnabled===!0,storedNav="navigateMode"in obj&&obj.navigateMode===!0,rawMode="selectorMode"in obj?obj.selectorMode:null;requestPicker({scanEnabled:storedScan,navigateMode:storedNav,selectorMode:rawMode==="generic"||rawMode==="exact"?rawMode:null,..."mode"in obj&&obj.mode==="click-map"&&{mode:"click-map"}})}}}}catch{}document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&(onFrustrationHide(),flushBeacon())}),fetchConfig().then(result=>{if(!result.loaded){failClosed(result);return}applyInitConfig(storedInitConfig),!trackingDisabled&&(setConfigLoaded(!0),setInterval(()=>{flush()},config.flush_interval_ms),flush())})}function failClosed(result){console.warn(`[tracker] Could not load this project's settings from ${apiBase}/api/config \u2014 ${describeConfigFailure(result)}. No events were sent from this page load: without those settings the tracker cannot apply the project's URL redaction rules, and sending unredacted URLs would be worse than sending nothing.`),disableTracking(queue)}function track(name,attrs){typeof window>"u"||trackingDisabled||name&&enqueue({event_type:"custom",page_url:window.location.href,data_track:name,data_track_attrs:attrs,timestamp:new Date().toISOString()})}function setTraits2(newTraits){typeof window>"u"||setTraits(newTraits)}export{init,setTraits2 as setTraits,track,version};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@privatrak/tracker",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Cookieless, privacy-first analytics tracker: auto-captures pageviews, clicks and form submissions with no cookies, no localStorage, no PII and no consent banner.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"analytics",
|