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