cryptique-sdk 1.2.17 → 1.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cjs/index.js CHANGED
@@ -5519,104 +5519,157 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5519
5519
 
5520
5520
  /**
5521
5521
  * ElementVisibilityTracker - Emits element_view auto events when elements
5522
- * with IDs or data-track attributes enter the viewport for ≥ 1 second.
5523
- * Tells PMs whether users actually SAW key elements (CTAs, pricing, etc.)
5524
- * vs just didn't click them completely different problems, opposite fixes.
5522
+ * enter the viewport for ≥ 1 second, capturing the actual dwell duration.
5523
+ *
5524
+ * Covers interactive elements (buttons, links, inputs, headings, images) and
5525
+ * any element with an id or data-cq-track attribute. Each viewport entry is
5526
+ * tracked independently — if a user scrolls away and back, both dwells count.
5527
+ * This gives accurate attention data for the heatmap attention overlay.
5525
5528
  */
5526
5529
  startElementVisibilityTracking() {
5527
5530
  try {
5528
5531
  if (typeof IntersectionObserver === 'undefined') return;
5529
5532
 
5530
- // Only observe elements with an id or data-cq-track attribute
5533
+ // Observe interactive/structural elements in addition to id/data-cq-track
5534
+ const TRACKABLE_SELECTOR = [
5535
+ '[id]:not([id=""])', '[data-cq-track]',
5536
+ 'button', 'a[href]', 'input:not([type=hidden])', 'select', 'textarea',
5537
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img[alt]',
5538
+ '[role="button"]', '[role="tab"]', '[role="menuitem"]', '[role="link"]'
5539
+ ].join(', ');
5540
+
5531
5541
  const getTrackableElements = () =>
5532
- document.querySelectorAll('[id]:not([id=""]), [data-cq-track]');
5542
+ Array.from(document.querySelectorAll(TRACKABLE_SELECTOR)).filter(el => {
5543
+ try { return el.getBoundingClientRect().height >= 20; } catch (_) { return true; }
5544
+ });
5545
+
5546
+ // enterTimes: key → { enterTime, el } — tracks elements currently in viewport
5547
+ const enterTimes = new Map();
5548
+ const observedEls = new WeakSet(); // prevents double-observing same DOM node
5533
5549
 
5534
- const viewTimers = new Map(); // elementKey → setTimeout handle
5535
- const reported = new Set(); // elementKey already fired once per page load
5550
+ function elementKey(el) {
5551
+ return `${el.tagName}|${el.id || ''}|${el.getAttribute('data-cq-track') || ''}|${(el.textContent || '').trim().slice(0, 30)}`;
5552
+ }
5553
+
5554
+ function getElementData(el) {
5555
+ return {
5556
+ element_id: el.id || '',
5557
+ element_name: el.getAttribute('name') || el.getAttribute('data-cq-track') || '',
5558
+ element_tag_name: el.tagName || '',
5559
+ element_type: el.type || el.getAttribute('type') || '',
5560
+ element_class: el.className || '',
5561
+ element_text: (el.textContent || '').trim().slice(0, 100)
5562
+ };
5563
+ }
5536
5564
 
5537
5565
  const observer = new IntersectionObserver((entries) => {
5538
5566
  entries.forEach((entry) => {
5539
- const el = entry.target;
5540
- const key = el.id || el.getAttribute('data-cq-track') || el.className;
5541
- if (!key || reported.has(key)) return;
5567
+ const el = entry.target;
5568
+ const key = elementKey(el);
5542
5569
 
5543
5570
  if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
5544
- // Element entered viewport — start 1s timer
5545
- if (!viewTimers.has(key)) {
5546
- const enterTime = Date.now();
5547
- const timer = setTimeout(() => {
5548
- if (reported.has(key)) return;
5549
- reported.add(key);
5550
- EventsManager.trackAutoEvent('element_view', {
5551
- time_in_viewport_ms: Date.now() - enterTime,
5552
- viewport_percent_visible: Math.round(entry.intersectionRatio * 100)
5553
- }, {
5554
- element_id: el.id || '',
5555
- element_name: el.getAttribute('name') || el.getAttribute('data-cq-track') || '',
5556
- element_tag_name: el.tagName || '',
5557
- element_type: el.type || el.getAttribute('type') || '',
5558
- element_class: el.className || '',
5559
- element_text: (el.textContent || '').trim().slice(0, 100)
5560
- });
5561
- viewTimers.delete(key);
5562
- }, 1000);
5563
- viewTimers.set(key, timer);
5571
+ // Element entered viewport — record entry time
5572
+ if (!enterTimes.has(key)) {
5573
+ enterTimes.set(key, { enterTime: Date.now(), el });
5564
5574
  }
5565
5575
  } else {
5566
- // Element left viewport before 1s cancel timer
5567
- if (viewTimers.has(key)) {
5568
- clearTimeout(viewTimers.get(key));
5569
- viewTimers.delete(key);
5576
+ // Element left viewport emit actual dwell if ≥ 1s
5577
+ const record = enterTimes.get(key);
5578
+ if (record) {
5579
+ const dwell = Date.now() - record.enterTime;
5580
+ enterTimes.delete(key);
5581
+ if (dwell >= 1000) {
5582
+ EventsManager.trackAutoEvent('element_view', {
5583
+ time_in_viewport_ms: dwell,
5584
+ viewport_percent_visible: Math.round(entry.intersectionRatio * 100),
5585
+ scroll_y_at_view: window.scrollY
5586
+ }, getElementData(el));
5587
+ }
5570
5588
  }
5571
5589
  }
5572
5590
  });
5573
- }, { threshold: 0.5 });
5591
+ }, { threshold: [0.5] });
5574
5592
 
5575
- // Observe existing elements
5576
- getTrackableElements().forEach(el => observer.observe(el));
5593
+ const observe = (el) => {
5594
+ if (!observedEls.has(el)) { observedEls.add(el); observer.observe(el); }
5595
+ };
5596
+
5597
+ getTrackableElements().forEach(observe);
5577
5598
 
5578
5599
  // Observe elements added to DOM later (SPAs)
5579
5600
  if (typeof MutationObserver !== 'undefined') {
5580
5601
  new MutationObserver(() => {
5581
- getTrackableElements().forEach(el => {
5582
- if (!viewTimers.has(el.id || el.getAttribute('data-cq-track') || el.className)) {
5583
- observer.observe(el);
5584
- }
5585
- });
5602
+ getTrackableElements().forEach(observe);
5586
5603
  }).observe(document.body, { childList: true, subtree: true });
5587
5604
  }
5588
- } catch (error) {
5589
- }
5605
+
5606
+ // On page unload, flush elements still in viewport
5607
+ window.addEventListener('pagehide', () => {
5608
+ enterTimes.forEach(({ enterTime, el }) => {
5609
+ const dwell = Date.now() - enterTime;
5610
+ if (dwell >= 1000) {
5611
+ EventsManager.trackAutoEvent('element_view', {
5612
+ time_in_viewport_ms: dwell,
5613
+ viewport_percent_visible: 100,
5614
+ scroll_y_at_view: window.scrollY
5615
+ }, getElementData(el));
5616
+ }
5617
+ });
5618
+ });
5619
+ } catch (error) {}
5590
5620
  },
5591
5621
 
5592
5622
  /**
5593
5623
  * PageSummaryTracker — collects high-frequency signals in-memory and flushes
5594
5624
  * them as a single 'page_summary' auto-event on page unload.
5595
5625
  *
5596
- * This keeps event counts low (1 per page visit) while capturing:
5597
- * - Mouse movement grid (move_grid)
5598
- * - Hover dwell grid (hover_grid)
5626
+ * High-volume/continuous data lives here (grids, counts) to keep event volume low.
5627
+ * Low-frequency, high-signal moments (exit_intent) are emitted as separate auto-events
5628
+ * but their counters are also included here for correlation.
5629
+ *
5630
+ * Captures:
5631
+ * - Mouse movement grid (move_grid) + hover dwell grid (hover_grid)
5632
+ * - Element-level hover dwells for named/interactive elements (element_dwells)
5599
5633
  * - Tab-switch count and hidden time
5600
- * - Scroll reversal count
5634
+ * - Scroll reversal count + depths at each reversal (scroll_reversal_depths)
5601
5635
  * - Per-field focus dwell times
5636
+ * - Exit intent count + scroll depth at last exit intent
5637
+ * - First interaction time (how long before user engaged)
5638
+ * - Performance: CLS score, long task count/max, INP
5602
5639
  */
5603
5640
  startPageSummaryTracking() {
5604
5641
  try {
5605
- const GRID_SIZE = 40;
5606
- // In-memory accumulators — never sent to the server individually
5607
- const moveGrid = new Map(); // cellKey → move count
5608
- const hoverGrid = new Map(); // cellKey → total dwell_ms
5609
- const fieldDwells = []; // [{field_id, field_label, field_type, dwell_ms, was_filled}]
5610
-
5611
- let tabSwitches = 0;
5612
- let totalTabHiddenMs = 0;
5613
- let tabHiddenTime = null;
5614
- let scrollReversals = 0;
5642
+ const GRID_SIZE = 60;
5615
5643
 
5616
- // Track last significant scroll position for reversal detection
5644
+ // In-memory accumulators never sent to the server individually
5645
+ const moveGrid = new Map(); // cellKey → move count
5646
+ const hoverGrid = new Map(); // cellKey → total dwell_ms
5647
+ const fieldDwells = []; // [{field_id, field_label, field_type, dwell_ms, was_filled}]
5648
+ const elementDwellsMap = new Map(); // elemKey → {tag, id, text, dwell_ms}
5649
+ const scrollReversalDepths = []; // scroll depth % at each reversal
5650
+
5651
+ let tabSwitches = 0;
5652
+ let totalTabHiddenMs = 0;
5653
+ let tabHiddenTime = null;
5654
+ let scrollReversals = 0;
5655
+ let exitIntentCount = 0;
5656
+ let exitIntentLastScrollDepth = 0;
5657
+ let firstInteractionMs = null;
5658
+ let clsScore = 0;
5659
+ let longTasksCount = 0;
5660
+ let maxLongTaskMs = 0;
5661
+ let inpMs = 0;
5662
+
5663
+ const pageLoadTime = Date.now();
5617
5664
  let lastSigScrollY = window.scrollY;
5618
5665
  let lastSigDir = 0; // 1=down, -1=up
5619
5666
 
5667
+ // ── Helpers
5668
+ function getScrollDepth() {
5669
+ const maxScroll = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight) - window.innerHeight;
5670
+ return maxScroll <= 0 ? 100 : Math.round((window.scrollY / maxScroll) * 100);
5671
+ }
5672
+
5620
5673
  function getGridCell(pageX, pageY) {
5621
5674
  const docW = Math.max(1, document.documentElement.scrollWidth || document.body.scrollWidth || window.innerWidth);
5622
5675
  const docH = Math.max(1, document.documentElement.scrollHeight || document.body.scrollHeight || window.innerHeight);
@@ -5625,6 +5678,39 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5625
5678
  return col * GRID_SIZE + row;
5626
5679
  }
5627
5680
 
5681
+ // ── PerformanceObserver: CLS, Long Tasks, INP (best-effort, not all browsers)
5682
+ try {
5683
+ new PerformanceObserver(list => {
5684
+ for (const e of list.getEntries()) {
5685
+ if (!e.hadRecentInput) clsScore = Math.round((clsScore + e.value) * 1000) / 1000;
5686
+ }
5687
+ }).observe({ type: 'layout-shift', buffered: true });
5688
+ } catch (_) {}
5689
+ try {
5690
+ new PerformanceObserver(list => {
5691
+ for (const e of list.getEntries()) {
5692
+ longTasksCount++;
5693
+ if (e.duration > maxLongTaskMs) maxLongTaskMs = Math.round(e.duration);
5694
+ }
5695
+ }).observe({ type: 'longtask', buffered: true });
5696
+ } catch (_) {}
5697
+ try {
5698
+ new PerformanceObserver(list => {
5699
+ for (const e of list.getEntries()) {
5700
+ if (e.duration > inpMs) inpMs = Math.round(e.duration);
5701
+ }
5702
+ }).observe({ type: 'event', durationThreshold: 40, buffered: true });
5703
+ } catch (_) {}
5704
+
5705
+ // ── First interaction: time from page load to first meaningful engagement
5706
+ const markFirstInteraction = () => {
5707
+ if (firstInteractionMs === null) firstInteractionMs = Date.now() - pageLoadTime;
5708
+ };
5709
+ document.addEventListener('click', markFirstInteraction, { once: true, passive: true });
5710
+ document.addEventListener('keydown', markFirstInteraction, { once: true, passive: true });
5711
+ document.addEventListener('touchstart', markFirstInteraction, { once: true, passive: true });
5712
+ window.addEventListener('scroll', markFirstInteraction, { once: true, passive: true });
5713
+
5628
5714
  // ── Mouse movement → move_grid (throttled to max 1 sample / 100 ms)
5629
5715
  let moveThrottle = false;
5630
5716
  document.addEventListener('mousemove', (e) => {
@@ -5635,7 +5721,8 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5635
5721
  moveGrid.set(key, (moveGrid.get(key) || 0) + 1);
5636
5722
  }, { passive: true });
5637
5723
 
5638
- // ── Hover dwell → hover_grid (track all elements, filter < 100 ms)
5724
+ // ── Hover dwell → hover_grid + element_dwells for named/interactive elements
5725
+ const ELEMENT_DWELL_TAGS = new Set(['BUTTON', 'A', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'INPUT', 'SELECT', 'TEXTAREA', 'IMG', 'LABEL']);
5639
5726
  const hoverStarts = new Map(); // element → enterTime
5640
5727
  document.addEventListener('mouseover', (e) => {
5641
5728
  if (e.target && e.target !== document.documentElement) {
@@ -5654,9 +5741,81 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5654
5741
  const cy = rect.top + rect.height / 2 + window.scrollY;
5655
5742
  const key = getGridCell(cx, cy);
5656
5743
  hoverGrid.set(key, (hoverGrid.get(key) || 0) + dwell);
5744
+
5745
+ // Element-level dwell: track named/interactive elements hovered ≥ 300ms
5746
+ if (dwell >= 300 && (target.id || ELEMENT_DWELL_TAGS.has(target.tagName))) {
5747
+ const elemKey = `${target.tagName}|${target.id || ''}|${(target.textContent || '').trim().slice(0, 30)}`;
5748
+ const existing = elementDwellsMap.get(elemKey);
5749
+ if (existing) {
5750
+ existing.dwell_ms += dwell;
5751
+ } else {
5752
+ elementDwellsMap.set(elemKey, {
5753
+ tag: target.tagName.toLowerCase(),
5754
+ id: target.id || '',
5755
+ text: (target.textContent || '').trim().slice(0, 50),
5756
+ dwell_ms: dwell
5757
+ });
5758
+ }
5759
+ }
5657
5760
  } catch (_) {}
5658
5761
  }, { passive: true });
5659
5762
 
5763
+ // ── Viewport visibility → viewport_element_dwells (replaces individual element_view events)
5764
+ const viewportDwellsMap = new Map(); // elemKey → {tag,id,cls,text,dwell_ms,x,y,w,h}
5765
+ const vpEnterTimes = new Map(); // elemKey → {enterTime,x,y,w,h}
5766
+ try {
5767
+ if (typeof IntersectionObserver !== 'undefined') {
5768
+ const VP_SELECTOR = [
5769
+ '[id]:not([id=""])', '[data-cq-track]',
5770
+ 'button', 'a[href]', 'input:not([type=hidden])', 'select', 'textarea',
5771
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img[alt]',
5772
+ '[role="button"]', '[role="tab"]', '[role="menuitem"]', '[role="link"]'
5773
+ ].join(', ');
5774
+ const vpKey = el => `${el.tagName}|${el.id || ''}|${(el.textContent || '').trim().slice(0, 30)}`;
5775
+ const vpObserver = new IntersectionObserver(entries => {
5776
+ entries.forEach(entry => {
5777
+ const el = entry.target;
5778
+ const key = vpKey(el);
5779
+ if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
5780
+ if (!vpEnterTimes.has(key)) {
5781
+ const r = el.getBoundingClientRect();
5782
+ vpEnterTimes.set(key, {
5783
+ enterTime: Date.now(),
5784
+ x: Math.round(r.left + window.scrollX),
5785
+ y: Math.round(r.top + window.scrollY),
5786
+ w: Math.round(r.width),
5787
+ h: Math.round(r.height),
5788
+ });
5789
+ }
5790
+ } else {
5791
+ const rec = vpEnterTimes.get(key);
5792
+ if (rec) {
5793
+ const dwell = Date.now() - rec.enterTime;
5794
+ vpEnterTimes.delete(key);
5795
+ if (dwell >= 1000) {
5796
+ const ex = viewportDwellsMap.get(key);
5797
+ if (ex) { ex.dwell_ms += dwell; }
5798
+ else {
5799
+ viewportDwellsMap.set(key, {
5800
+ tag: el.tagName.toLowerCase(),
5801
+ id: el.id || '',
5802
+ cls: el.className ? String(el.className).trim().slice(0, 60) : '',
5803
+ text: (el.textContent || '').trim().slice(0, 60),
5804
+ dwell_ms: dwell,
5805
+ x: rec.x, y: rec.y, w: rec.w, h: rec.h,
5806
+ });
5807
+ }
5808
+ }
5809
+ }
5810
+ }
5811
+ });
5812
+ }, { threshold: [0.5] });
5813
+ document.querySelectorAll(VP_SELECTOR).forEach(el => {
5814
+ try { if (el.getBoundingClientRect().height >= 20) vpObserver.observe(el); } catch (_) {}
5815
+ });
5816
+ }
5817
+ } catch (_) {}
5818
+
5660
5819
  // ── Tab visibility → tab_switches + total_tab_hidden_ms
5661
5820
  document.addEventListener('visibilitychange', () => {
5662
5821
  if (document.hidden) {
@@ -5668,14 +5827,15 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5668
5827
  }
5669
5828
  });
5670
5829
 
5671
- // ── Scroll reversal detection (direction change > 200 px)
5830
+ // ── Scroll reversal detection (direction change 200 px) + depth recording
5672
5831
  window.addEventListener('scroll', () => {
5673
5832
  const curr = window.scrollY;
5674
5833
  const diff = curr - lastSigScrollY;
5675
- if (Math.abs(diff) < 50) return; // ignore tiny movements
5676
- const dir = diff > 0 ? 1 : -1;
5834
+ if (Math.abs(diff) < 50) return;
5835
+ const dir = diff > 0 ? 1 : -1;
5677
5836
  if (lastSigDir !== 0 && dir !== lastSigDir && Math.abs(curr - lastSigScrollY) >= 200) {
5678
5837
  scrollReversals++;
5838
+ scrollReversalDepths.push(getScrollDepth());
5679
5839
  }
5680
5840
  if (Math.abs(diff) >= 50) { lastSigDir = dir; lastSigScrollY = curr; }
5681
5841
  }, { passive: true });
@@ -5693,7 +5853,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5693
5853
  if (!t || !fieldFocusTimes.has(t)) return;
5694
5854
  const dwell = Date.now() - fieldFocusTimes.get(t);
5695
5855
  fieldFocusTimes.delete(t);
5696
- if (dwell < 100) return; // skip accidental focus
5856
+ if (dwell < 100) return;
5697
5857
  const label = t.id
5698
5858
  ? (document.querySelector(`label[for="${CSS.escape(t.id)}"]`)?.textContent?.trim() || t.placeholder || t.name || t.id)
5699
5859
  : (t.placeholder || t.name || t.type || '');
@@ -5706,6 +5866,26 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5706
5866
  });
5707
5867
  }, true);
5708
5868
 
5869
+ // ── Exit intent: mouse leaving viewport toward the top (user about to navigate away)
5870
+ // Also fires as a standalone auto-event for direct querying.
5871
+ let lastExitIntentTime = 0;
5872
+ document.addEventListener('mouseleave', (e) => {
5873
+ if (e.clientY > 0 || e.relatedTarget !== null) return; // only top-edge exits
5874
+ const now = Date.now();
5875
+ if (now - lastExitIntentTime < 2000) return; // debounce: max once per 2s
5876
+ lastExitIntentTime = now;
5877
+ exitIntentCount++;
5878
+ exitIntentLastScrollDepth = getScrollDepth();
5879
+ EventsManager.trackAutoEvent('exit_intent', {
5880
+ scroll_depth: exitIntentLastScrollDepth,
5881
+ time_on_page_ms: now - pageLoadTime,
5882
+ scroll_y: window.scrollY,
5883
+ document_height: Math.min(
5884
+ document.documentElement.scrollHeight || document.body.scrollHeight, 25000
5885
+ )
5886
+ }).catch(() => {});
5887
+ });
5888
+
5709
5889
  // ── Serialise grid Map to [[col, row, value], ...] sparse array
5710
5890
  function serializeGrid(grid) {
5711
5891
  const out = [];
@@ -5722,17 +5902,52 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5722
5902
  let summarySent = false;
5723
5903
  function firePageSummary() {
5724
5904
  if (summarySent) return; // only fire once per page lifecycle
5725
- if (moveGrid.size === 0 && hoverGrid.size === 0 && fieldDwells.length === 0
5726
- && tabSwitches === 0 && scrollReversals === 0) return;
5905
+ const hasData = moveGrid.size > 0 || hoverGrid.size > 0 || fieldDwells.length > 0
5906
+ || tabSwitches > 0 || scrollReversals > 0 || exitIntentCount > 0
5907
+ || firstInteractionMs !== null || clsScore > 0 || longTasksCount > 0;
5908
+ if (!hasData) return;
5727
5909
  summarySent = true;
5728
5910
  try {
5911
+ // Flush still-visible elements into viewportDwellsMap before summarising
5912
+ vpEnterTimes.forEach((rec, key) => {
5913
+ const dwell = Date.now() - rec.enterTime;
5914
+ if (dwell < 1000) return;
5915
+ const ex = viewportDwellsMap.get(key);
5916
+ if (ex) { ex.dwell_ms += dwell; }
5917
+ else {
5918
+ const parts = key.split('|');
5919
+ viewportDwellsMap.set(key, { tag: (parts[0] || '').toLowerCase(), id: parts[1] || '', cls: '', text: parts[2] || '', dwell_ms: dwell, x: rec.x, y: rec.y, w: rec.w, h: rec.h });
5920
+ }
5921
+ });
5922
+ // Serialize viewport_element_dwells: filter ≥ 1000ms, sort desc, cap at 25
5923
+ const viewportElementDwells = Array.from(viewportDwellsMap.values())
5924
+ .filter(e => e.dwell_ms >= 1000)
5925
+ .sort((a, b) => b.dwell_ms - a.dwell_ms)
5926
+ .slice(0, 25);
5927
+
5928
+ // Serialize element_dwells: filter ≥ 300ms, sort desc, cap at 20
5929
+ const elementDwells = Array.from(elementDwellsMap.values())
5930
+ .filter(e => e.dwell_ms >= 300)
5931
+ .sort((a, b) => b.dwell_ms - a.dwell_ms)
5932
+ .slice(0, 20);
5933
+
5729
5934
  EventsManager.trackAutoEvent('page_summary', {
5730
- scroll_reversals: scrollReversals,
5731
- tab_switches: tabSwitches,
5732
- total_tab_hidden_ms: totalTabHiddenMs,
5733
- move_grid: serializeGrid(moveGrid),
5734
- hover_grid: serializeGrid(hoverGrid),
5735
- field_dwells: fieldDwells,
5935
+ scroll_reversals: scrollReversals,
5936
+ scroll_reversal_depths: scrollReversalDepths,
5937
+ tab_switches: tabSwitches,
5938
+ total_tab_hidden_ms: totalTabHiddenMs,
5939
+ move_grid: serializeGrid(moveGrid),
5940
+ hover_grid: serializeGrid(hoverGrid),
5941
+ field_dwells: fieldDwells,
5942
+ element_dwells: elementDwells,
5943
+ viewport_element_dwells: viewportElementDwells,
5944
+ first_interaction_ms: firstInteractionMs,
5945
+ exit_intent_count: exitIntentCount,
5946
+ exit_intent_last_scroll_depth: exitIntentLastScrollDepth,
5947
+ cls_score: clsScore,
5948
+ long_tasks_count: longTasksCount,
5949
+ max_long_task_ms: maxLongTaskMs,
5950
+ inp_ms: inpMs,
5736
5951
  document_height: document.documentElement.scrollHeight || document.body.scrollHeight || 0,
5737
5952
  document_width: document.documentElement.scrollWidth || document.body.scrollWidth || 0,
5738
5953
  viewport_width: window.innerWidth,
@@ -5772,7 +5987,8 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
5772
5987
  this.startNetworkTracking();
5773
5988
  this.startAdvancedFormTracking();
5774
5989
  this.startFormAbandonmentTracking();
5775
- this.startElementVisibilityTracking();
5990
+ // element_view events replaced by viewport_element_dwells inside page_summary
5991
+ // this.startElementVisibilityTracking();
5776
5992
  this.startPageSummaryTracking();
5777
5993
  }
5778
5994
  };