cryptique-sdk 1.1.7 → 1.1.9

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
@@ -6063,6 +6063,30 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6063
6063
  }
6064
6064
  }
6065
6065
 
6066
+ // Build auto_event_data for custom events (heatmap/position context when possible)
6067
+ let customAutoEventData = {};
6068
+ if (typeof window !== 'undefined' && window.document) {
6069
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
6070
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
6071
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
6072
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6073
+ customAutoEventData = {
6074
+ scroll_x: scrollX,
6075
+ scroll_y: scrollY,
6076
+ document_height: docHeight,
6077
+ document_width: docWidth
6078
+ };
6079
+ const srcEvent = options.sourceEvent;
6080
+ if (srcEvent && typeof srcEvent === 'object' && (srcEvent.pageX != null || srcEvent.clientX != null)) {
6081
+ customAutoEventData.page_x = srcEvent.pageX != null ? srcEvent.pageX : (srcEvent.clientX + (window.scrollX || window.pageXOffset || 0));
6082
+ customAutoEventData.page_y = srcEvent.pageY != null ? srcEvent.pageY : (srcEvent.clientY + (window.scrollY || window.pageYOffset || 0));
6083
+ customAutoEventData.click_coordinates = {
6084
+ x: srcEvent.clientX != null ? srcEvent.clientX : srcEvent.pageX - (window.scrollX || window.pageXOffset || 0),
6085
+ y: srcEvent.clientY != null ? srcEvent.clientY : srcEvent.pageY - (window.scrollY || window.pageYOffset || 0)
6086
+ };
6087
+ }
6088
+ }
6089
+
6066
6090
  // Prepare event data
6067
6091
  const eventData = {
6068
6092
  event_type: 'custom',
@@ -6072,6 +6096,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6072
6096
  user_id: userId,
6073
6097
  // Only include user-provided properties in custom_properties (filtered)
6074
6098
  custom_properties: filteredProperties,
6099
+ auto_event_data: customAutoEventData,
6075
6100
  // Page context and screen/viewport data go in page_data (not custom_properties)
6076
6101
  page_data: {
6077
6102
  current_url: window.location.href,
@@ -6886,7 +6911,18 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6886
6911
  if (element === lastClickElement && now - lastClickTime < 1000) {
6887
6912
  clickCount++;
6888
6913
  if (clickCount >= 3) {
6914
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
6915
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
6916
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
6917
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6889
6918
  EventsManager.trackAutoEvent('rage_click', {
6919
+ click_coordinates: { x: event.clientX, y: event.clientY },
6920
+ page_x: event.pageX,
6921
+ page_y: event.pageY,
6922
+ scroll_x: scrollX,
6923
+ scroll_y: scrollY,
6924
+ document_height: docHeight,
6925
+ document_width: docWidth,
6890
6926
  click_count: clickCount,
6891
6927
  time_span: now - lastClickTime,
6892
6928
  element_area: element.offsetWidth * element.offsetHeight,
@@ -6903,11 +6939,15 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6903
6939
  const isInteractive = isInteractiveElement(element);
6904
6940
 
6905
6941
  if (!isInteractive) {
6906
- // Capture coordinates before setTimeout
6942
+ // Capture coordinates and page context before setTimeout (for heatmaps)
6907
6943
  const clickX = event.clientX;
6908
6944
  const clickY = event.clientY;
6909
6945
  const clickElement = element;
6910
-
6946
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
6947
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
6948
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
6949
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6950
+
6911
6951
  // Mark this click as potentially dead
6912
6952
  const clickId = `${now}_${Math.random().toString(36).substr(2, 9)}`;
6913
6953
  pendingDeadClicks.set(clickId, {
@@ -6917,7 +6957,13 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6917
6957
  timestamp: now,
6918
6958
  url: window.location.href,
6919
6959
  clickX,
6920
- clickY
6960
+ clickY,
6961
+ page_x: event.pageX,
6962
+ page_y: event.pageY,
6963
+ scroll_x: scrollX,
6964
+ scroll_y: scrollY,
6965
+ document_height: docHeight,
6966
+ document_width: docWidth
6921
6967
  });
6922
6968
 
6923
6969
  // Check after 1 second if navigation occurred or if it's still a dead click
@@ -6931,6 +6977,12 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6931
6977
 
6932
6978
  EventsManager.trackAutoEvent('dead_click', {
6933
6979
  click_coordinates: { x: pendingClick.clickX, y: pendingClick.clickY },
6980
+ page_x: pendingClick.page_x,
6981
+ page_y: pendingClick.page_y,
6982
+ scroll_x: pendingClick.scroll_x,
6983
+ scroll_y: pendingClick.scroll_y,
6984
+ document_height: pendingClick.document_height,
6985
+ document_width: pendingClick.document_width,
6934
6986
  element_area: clickElement.offsetWidth * clickElement.offsetHeight,
6935
6987
  element_category: pendingClick.elementCategory,
6936
6988
  element_has_onclick: !!clickElement.onclick,
@@ -6946,9 +6998,19 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6946
6998
  }, 1000);
6947
6999
  }
6948
7000
 
6949
- // Track regular click with enhanced data
7001
+ // Track regular click with enhanced data (viewport + page-relative for heatmaps)
7002
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7003
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7004
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7005
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6950
7006
  EventsManager.trackAutoEvent('element_click', {
6951
7007
  click_coordinates: { x: event.clientX, y: event.clientY },
7008
+ page_x: event.pageX,
7009
+ page_y: event.pageY,
7010
+ scroll_x: scrollX,
7011
+ scroll_y: scrollY,
7012
+ document_height: docHeight,
7013
+ document_width: docWidth,
6952
7014
  double_click: event.detail === 2,
6953
7015
  element_category: elementCategory
6954
7016
  }, elementData).catch(err => {
@@ -6973,15 +7035,22 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6973
7035
  clearTimeout(scrollTimeout);
6974
7036
 
6975
7037
  scrollTimeout = setTimeout(() => {
6976
- const scrollDepth = Math.round((window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100);
6977
-
7038
+ const maxScroll = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight) - window.innerHeight;
7039
+ const scrollDepth = maxScroll <= 0 ? 100 : Math.round((window.scrollY / maxScroll) * 100);
7040
+
6978
7041
  if (scrollDepth > maxScrollDepth) {
6979
7042
  maxScrollDepth = scrollDepth;
6980
-
7043
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7044
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7045
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6981
7046
  EventsManager.trackAutoEvent('page_scroll', {
6982
7047
  scroll_depth: scrollDepth,
6983
7048
  max_scroll_reached: maxScrollDepth,
6984
- scroll_position: window.scrollY
7049
+ scroll_position: window.scrollY,
7050
+ scroll_x: scrollX,
7051
+ scroll_y: window.scrollY,
7052
+ document_height: docHeight,
7053
+ document_width: docWidth
6985
7054
  }).catch(err => {
6986
7055
  console.error('❌ [AutoEvents] Failed to track page_scroll:', err);
6987
7056
  });
@@ -6999,10 +7068,24 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6999
7068
  document.addEventListener('submit', (event) => {
7000
7069
  const form = event.target;
7001
7070
  if (form.tagName === 'FORM') {
7071
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7072
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7073
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7074
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7075
+ const rect = form.getBoundingClientRect();
7076
+ const pageX = rect.left + scrollX;
7077
+ const pageY = rect.top + scrollY;
7002
7078
  EventsManager.trackAutoEvent('form_submit', {
7003
7079
  form_id: form.id || null,
7004
7080
  form_action: form.action || null,
7005
- form_method: form.method || 'get'
7081
+ form_method: form.method || 'get',
7082
+ scroll_x: scrollX,
7083
+ scroll_y: scrollY,
7084
+ document_height: docHeight,
7085
+ document_width: docWidth,
7086
+ page_x: pageX,
7087
+ page_y: pageY,
7088
+ click_coordinates: { x: event.clientX != null ? event.clientX : rect.left + rect.width / 2, y: event.clientY != null ? event.clientY : rect.top + rect.height / 2 }
7006
7089
  }, {
7007
7090
  element_tag_name: 'form',
7008
7091
  element_id: form.id || null,
@@ -7011,8 +7094,8 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
7011
7094
  element_type: form.method || 'get', // FIX: Added element_type (form method)
7012
7095
  element_text: form.textContent?.trim().substring(0, 100) || null, // FIX: Added element_text
7013
7096
  element_position: { // FIX: Added element_position
7014
- x: event.clientX || 0,
7015
- y: event.clientY || 0,
7097
+ x: event.clientX || rect.left,
7098
+ y: event.clientY || rect.top,
7016
7099
  width: form.offsetWidth || 0,
7017
7100
  height: form.offsetHeight || 0
7018
7101
  }
@@ -7024,10 +7107,24 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
7024
7107
  document.addEventListener('focus', (event) => {
7025
7108
  const element = event.target;
7026
7109
  if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA' || element.tagName === 'SELECT') {
7110
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7111
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7112
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7113
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7114
+ const rect = element.getBoundingClientRect();
7115
+ const pageX = rect.left + scrollX;
7116
+ const pageY = rect.top + scrollY;
7027
7117
  EventsManager.trackAutoEvent('form_focus', {
7028
7118
  field_name: element.name || null,
7029
7119
  field_type: element.type || null,
7030
- field_id: element.id || null
7120
+ field_id: element.id || null,
7121
+ scroll_x: scrollX,
7122
+ scroll_y: scrollY,
7123
+ document_height: docHeight,
7124
+ document_width: docWidth,
7125
+ page_x: pageX,
7126
+ page_y: pageY,
7127
+ click_coordinates: { x: event.clientX != null ? event.clientX : rect.left + rect.width / 2, y: event.clientY != null ? event.clientY : rect.top + rect.height / 2 }
7031
7128
  }, {
7032
7129
  element_tag_name: element.tagName.toLowerCase(),
7033
7130
  element_id: element.id || null,
@@ -7036,8 +7133,8 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
7036
7133
  element_type: element.type || null,
7037
7134
  element_text: element.value ? element.value.toString().trim().substring(0, 100) : null, // FIX: Added element_text (value for form fields)
7038
7135
  element_position: { // FIX: Added element_position
7039
- x: event.clientX || 0,
7040
- y: event.clientY || 0,
7136
+ x: event.clientX != null ? event.clientX : rect.left,
7137
+ y: event.clientY != null ? event.clientY : rect.top,
7041
7138
  width: element.offsetWidth || 0,
7042
7139
  height: element.offsetHeight || 0
7043
7140
  }
@@ -7050,23 +7147,54 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
7050
7147
  * Setup media tracking
7051
7148
  */
7052
7149
  setupMediaTracking() {
7150
+ function getPageContext() {
7151
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7152
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7153
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7154
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7155
+ return { scrollX, scrollY, docHeight, docWidth };
7156
+ }
7053
7157
  ['video', 'audio'].forEach(mediaType => {
7054
7158
  document.addEventListener('play', (event) => {
7055
7159
  if (event.target.tagName.toLowerCase() === mediaType) {
7160
+ const el = event.target;
7161
+ const { scrollX, scrollY, docHeight, docWidth } = getPageContext();
7162
+ const rect = el.getBoundingClientRect();
7163
+ const pageX = rect.left + scrollX;
7164
+ const pageY = rect.top + scrollY;
7056
7165
  EventsManager.trackAutoEvent('media_play', {
7057
7166
  media_type: mediaType,
7058
- media_src: event.target.src || null,
7059
- media_duration: event.target.duration || null
7167
+ media_src: el.src || null,
7168
+ media_duration: el.duration || null,
7169
+ scroll_x: scrollX,
7170
+ scroll_y: scrollY,
7171
+ document_height: docHeight,
7172
+ document_width: docWidth,
7173
+ page_x: pageX,
7174
+ page_y: pageY,
7175
+ click_coordinates: { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }
7060
7176
  });
7061
7177
  }
7062
7178
  }, true);
7063
7179
 
7064
7180
  document.addEventListener('pause', (event) => {
7065
7181
  if (event.target.tagName.toLowerCase() === mediaType) {
7182
+ const el = event.target;
7183
+ const { scrollX, scrollY, docHeight, docWidth } = getPageContext();
7184
+ const rect = el.getBoundingClientRect();
7185
+ const pageX = rect.left + scrollX;
7186
+ const pageY = rect.top + scrollY;
7066
7187
  EventsManager.trackAutoEvent('media_pause', {
7067
7188
  media_type: mediaType,
7068
- media_current_time: event.target.currentTime || null,
7069
- media_duration: event.target.duration || null
7189
+ media_current_time: el.currentTime || null,
7190
+ media_duration: el.duration || null,
7191
+ scroll_x: scrollX,
7192
+ scroll_y: scrollY,
7193
+ document_height: docHeight,
7194
+ document_width: docWidth,
7195
+ page_x: pageX,
7196
+ page_y: pageY,
7197
+ click_coordinates: { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }
7070
7198
  });
7071
7199
  }
7072
7200
  }, true);
@@ -7148,11 +7276,33 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
7148
7276
  focus_offset: selection.focusOffset || null
7149
7277
  };
7150
7278
 
7151
- // Get page context
7279
+ // Get page context and selection position for heatmaps
7280
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7281
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7282
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7283
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7284
+ let pageX = null;
7285
+ let pageY = null;
7286
+ if (range) {
7287
+ try {
7288
+ const selRect = range.getBoundingClientRect();
7289
+ if (selRect.width > 0 || selRect.height > 0) {
7290
+ pageX = selRect.left + scrollX;
7291
+ pageY = selRect.top + scrollY;
7292
+ }
7293
+ } catch (e) { /* getBoundingClientRect can throw in some edge cases */ }
7294
+ }
7152
7295
  const pageData = {
7153
7296
  page_url: window.location.href,
7154
7297
  page_title: document.title,
7155
- page_path: window.location.pathname
7298
+ page_path: window.location.pathname,
7299
+ scroll_x: scrollX,
7300
+ scroll_y: scrollY,
7301
+ document_height: docHeight,
7302
+ document_width: docWidth,
7303
+ page_x: pageX,
7304
+ page_y: pageY,
7305
+ click_coordinates: pageX != null && pageY != null ? { x: pageX - scrollX, y: pageY - scrollY } : null
7156
7306
  };
7157
7307
 
7158
7308
  EventsManager.trackAutoEvent('text_selection', {
package/lib/esm/index.js CHANGED
@@ -6061,6 +6061,30 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6061
6061
  }
6062
6062
  }
6063
6063
 
6064
+ // Build auto_event_data for custom events (heatmap/position context when possible)
6065
+ let customAutoEventData = {};
6066
+ if (typeof window !== 'undefined' && window.document) {
6067
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
6068
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
6069
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
6070
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6071
+ customAutoEventData = {
6072
+ scroll_x: scrollX,
6073
+ scroll_y: scrollY,
6074
+ document_height: docHeight,
6075
+ document_width: docWidth
6076
+ };
6077
+ const srcEvent = options.sourceEvent;
6078
+ if (srcEvent && typeof srcEvent === 'object' && (srcEvent.pageX != null || srcEvent.clientX != null)) {
6079
+ customAutoEventData.page_x = srcEvent.pageX != null ? srcEvent.pageX : (srcEvent.clientX + (window.scrollX || window.pageXOffset || 0));
6080
+ customAutoEventData.page_y = srcEvent.pageY != null ? srcEvent.pageY : (srcEvent.clientY + (window.scrollY || window.pageYOffset || 0));
6081
+ customAutoEventData.click_coordinates = {
6082
+ x: srcEvent.clientX != null ? srcEvent.clientX : srcEvent.pageX - (window.scrollX || window.pageXOffset || 0),
6083
+ y: srcEvent.clientY != null ? srcEvent.clientY : srcEvent.pageY - (window.scrollY || window.pageYOffset || 0)
6084
+ };
6085
+ }
6086
+ }
6087
+
6064
6088
  // Prepare event data
6065
6089
  const eventData = {
6066
6090
  event_type: 'custom',
@@ -6070,6 +6094,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6070
6094
  user_id: userId,
6071
6095
  // Only include user-provided properties in custom_properties (filtered)
6072
6096
  custom_properties: filteredProperties,
6097
+ auto_event_data: customAutoEventData,
6073
6098
  // Page context and screen/viewport data go in page_data (not custom_properties)
6074
6099
  page_data: {
6075
6100
  current_url: window.location.href,
@@ -6884,7 +6909,18 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6884
6909
  if (element === lastClickElement && now - lastClickTime < 1000) {
6885
6910
  clickCount++;
6886
6911
  if (clickCount >= 3) {
6912
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
6913
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
6914
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
6915
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6887
6916
  EventsManager.trackAutoEvent('rage_click', {
6917
+ click_coordinates: { x: event.clientX, y: event.clientY },
6918
+ page_x: event.pageX,
6919
+ page_y: event.pageY,
6920
+ scroll_x: scrollX,
6921
+ scroll_y: scrollY,
6922
+ document_height: docHeight,
6923
+ document_width: docWidth,
6888
6924
  click_count: clickCount,
6889
6925
  time_span: now - lastClickTime,
6890
6926
  element_area: element.offsetWidth * element.offsetHeight,
@@ -6901,11 +6937,15 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6901
6937
  const isInteractive = isInteractiveElement(element);
6902
6938
 
6903
6939
  if (!isInteractive) {
6904
- // Capture coordinates before setTimeout
6940
+ // Capture coordinates and page context before setTimeout (for heatmaps)
6905
6941
  const clickX = event.clientX;
6906
6942
  const clickY = event.clientY;
6907
6943
  const clickElement = element;
6908
-
6944
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
6945
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
6946
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
6947
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6948
+
6909
6949
  // Mark this click as potentially dead
6910
6950
  const clickId = `${now}_${Math.random().toString(36).substr(2, 9)}`;
6911
6951
  pendingDeadClicks.set(clickId, {
@@ -6915,7 +6955,13 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6915
6955
  timestamp: now,
6916
6956
  url: window.location.href,
6917
6957
  clickX,
6918
- clickY
6958
+ clickY,
6959
+ page_x: event.pageX,
6960
+ page_y: event.pageY,
6961
+ scroll_x: scrollX,
6962
+ scroll_y: scrollY,
6963
+ document_height: docHeight,
6964
+ document_width: docWidth
6919
6965
  });
6920
6966
 
6921
6967
  // Check after 1 second if navigation occurred or if it's still a dead click
@@ -6929,6 +6975,12 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6929
6975
 
6930
6976
  EventsManager.trackAutoEvent('dead_click', {
6931
6977
  click_coordinates: { x: pendingClick.clickX, y: pendingClick.clickY },
6978
+ page_x: pendingClick.page_x,
6979
+ page_y: pendingClick.page_y,
6980
+ scroll_x: pendingClick.scroll_x,
6981
+ scroll_y: pendingClick.scroll_y,
6982
+ document_height: pendingClick.document_height,
6983
+ document_width: pendingClick.document_width,
6932
6984
  element_area: clickElement.offsetWidth * clickElement.offsetHeight,
6933
6985
  element_category: pendingClick.elementCategory,
6934
6986
  element_has_onclick: !!clickElement.onclick,
@@ -6944,9 +6996,19 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6944
6996
  }, 1000);
6945
6997
  }
6946
6998
 
6947
- // Track regular click with enhanced data
6999
+ // Track regular click with enhanced data (viewport + page-relative for heatmaps)
7000
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7001
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7002
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7003
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6948
7004
  EventsManager.trackAutoEvent('element_click', {
6949
7005
  click_coordinates: { x: event.clientX, y: event.clientY },
7006
+ page_x: event.pageX,
7007
+ page_y: event.pageY,
7008
+ scroll_x: scrollX,
7009
+ scroll_y: scrollY,
7010
+ document_height: docHeight,
7011
+ document_width: docWidth,
6950
7012
  double_click: event.detail === 2,
6951
7013
  element_category: elementCategory
6952
7014
  }, elementData).catch(err => {
@@ -6971,15 +7033,22 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6971
7033
  clearTimeout(scrollTimeout);
6972
7034
 
6973
7035
  scrollTimeout = setTimeout(() => {
6974
- const scrollDepth = Math.round((window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100);
6975
-
7036
+ const maxScroll = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight) - window.innerHeight;
7037
+ const scrollDepth = maxScroll <= 0 ? 100 : Math.round((window.scrollY / maxScroll) * 100);
7038
+
6976
7039
  if (scrollDepth > maxScrollDepth) {
6977
7040
  maxScrollDepth = scrollDepth;
6978
-
7041
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7042
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7043
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6979
7044
  EventsManager.trackAutoEvent('page_scroll', {
6980
7045
  scroll_depth: scrollDepth,
6981
7046
  max_scroll_reached: maxScrollDepth,
6982
- scroll_position: window.scrollY
7047
+ scroll_position: window.scrollY,
7048
+ scroll_x: scrollX,
7049
+ scroll_y: window.scrollY,
7050
+ document_height: docHeight,
7051
+ document_width: docWidth
6983
7052
  }).catch(err => {
6984
7053
  console.error('❌ [AutoEvents] Failed to track page_scroll:', err);
6985
7054
  });
@@ -6997,10 +7066,24 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
6997
7066
  document.addEventListener('submit', (event) => {
6998
7067
  const form = event.target;
6999
7068
  if (form.tagName === 'FORM') {
7069
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7070
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7071
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7072
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7073
+ const rect = form.getBoundingClientRect();
7074
+ const pageX = rect.left + scrollX;
7075
+ const pageY = rect.top + scrollY;
7000
7076
  EventsManager.trackAutoEvent('form_submit', {
7001
7077
  form_id: form.id || null,
7002
7078
  form_action: form.action || null,
7003
- form_method: form.method || 'get'
7079
+ form_method: form.method || 'get',
7080
+ scroll_x: scrollX,
7081
+ scroll_y: scrollY,
7082
+ document_height: docHeight,
7083
+ document_width: docWidth,
7084
+ page_x: pageX,
7085
+ page_y: pageY,
7086
+ click_coordinates: { x: event.clientX != null ? event.clientX : rect.left + rect.width / 2, y: event.clientY != null ? event.clientY : rect.top + rect.height / 2 }
7004
7087
  }, {
7005
7088
  element_tag_name: 'form',
7006
7089
  element_id: form.id || null,
@@ -7009,8 +7092,8 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
7009
7092
  element_type: form.method || 'get', // FIX: Added element_type (form method)
7010
7093
  element_text: form.textContent?.trim().substring(0, 100) || null, // FIX: Added element_text
7011
7094
  element_position: { // FIX: Added element_position
7012
- x: event.clientX || 0,
7013
- y: event.clientY || 0,
7095
+ x: event.clientX || rect.left,
7096
+ y: event.clientY || rect.top,
7014
7097
  width: form.offsetWidth || 0,
7015
7098
  height: form.offsetHeight || 0
7016
7099
  }
@@ -7022,10 +7105,24 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
7022
7105
  document.addEventListener('focus', (event) => {
7023
7106
  const element = event.target;
7024
7107
  if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA' || element.tagName === 'SELECT') {
7108
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7109
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7110
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7111
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7112
+ const rect = element.getBoundingClientRect();
7113
+ const pageX = rect.left + scrollX;
7114
+ const pageY = rect.top + scrollY;
7025
7115
  EventsManager.trackAutoEvent('form_focus', {
7026
7116
  field_name: element.name || null,
7027
7117
  field_type: element.type || null,
7028
- field_id: element.id || null
7118
+ field_id: element.id || null,
7119
+ scroll_x: scrollX,
7120
+ scroll_y: scrollY,
7121
+ document_height: docHeight,
7122
+ document_width: docWidth,
7123
+ page_x: pageX,
7124
+ page_y: pageY,
7125
+ click_coordinates: { x: event.clientX != null ? event.clientX : rect.left + rect.width / 2, y: event.clientY != null ? event.clientY : rect.top + rect.height / 2 }
7029
7126
  }, {
7030
7127
  element_tag_name: element.tagName.toLowerCase(),
7031
7128
  element_id: element.id || null,
@@ -7034,8 +7131,8 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
7034
7131
  element_type: element.type || null,
7035
7132
  element_text: element.value ? element.value.toString().trim().substring(0, 100) : null, // FIX: Added element_text (value for form fields)
7036
7133
  element_position: { // FIX: Added element_position
7037
- x: event.clientX || 0,
7038
- y: event.clientY || 0,
7134
+ x: event.clientX != null ? event.clientX : rect.left,
7135
+ y: event.clientY != null ? event.clientY : rect.top,
7039
7136
  width: element.offsetWidth || 0,
7040
7137
  height: element.offsetHeight || 0
7041
7138
  }
@@ -7048,23 +7145,54 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
7048
7145
  * Setup media tracking
7049
7146
  */
7050
7147
  setupMediaTracking() {
7148
+ function getPageContext() {
7149
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7150
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7151
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7152
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7153
+ return { scrollX, scrollY, docHeight, docWidth };
7154
+ }
7051
7155
  ['video', 'audio'].forEach(mediaType => {
7052
7156
  document.addEventListener('play', (event) => {
7053
7157
  if (event.target.tagName.toLowerCase() === mediaType) {
7158
+ const el = event.target;
7159
+ const { scrollX, scrollY, docHeight, docWidth } = getPageContext();
7160
+ const rect = el.getBoundingClientRect();
7161
+ const pageX = rect.left + scrollX;
7162
+ const pageY = rect.top + scrollY;
7054
7163
  EventsManager.trackAutoEvent('media_play', {
7055
7164
  media_type: mediaType,
7056
- media_src: event.target.src || null,
7057
- media_duration: event.target.duration || null
7165
+ media_src: el.src || null,
7166
+ media_duration: el.duration || null,
7167
+ scroll_x: scrollX,
7168
+ scroll_y: scrollY,
7169
+ document_height: docHeight,
7170
+ document_width: docWidth,
7171
+ page_x: pageX,
7172
+ page_y: pageY,
7173
+ click_coordinates: { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }
7058
7174
  });
7059
7175
  }
7060
7176
  }, true);
7061
7177
 
7062
7178
  document.addEventListener('pause', (event) => {
7063
7179
  if (event.target.tagName.toLowerCase() === mediaType) {
7180
+ const el = event.target;
7181
+ const { scrollX, scrollY, docHeight, docWidth } = getPageContext();
7182
+ const rect = el.getBoundingClientRect();
7183
+ const pageX = rect.left + scrollX;
7184
+ const pageY = rect.top + scrollY;
7064
7185
  EventsManager.trackAutoEvent('media_pause', {
7065
7186
  media_type: mediaType,
7066
- media_current_time: event.target.currentTime || null,
7067
- media_duration: event.target.duration || null
7187
+ media_current_time: el.currentTime || null,
7188
+ media_duration: el.duration || null,
7189
+ scroll_x: scrollX,
7190
+ scroll_y: scrollY,
7191
+ document_height: docHeight,
7192
+ document_width: docWidth,
7193
+ page_x: pageX,
7194
+ page_y: pageY,
7195
+ click_coordinates: { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }
7068
7196
  });
7069
7197
  }
7070
7198
  }, true);
@@ -7146,11 +7274,33 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
7146
7274
  focus_offset: selection.focusOffset || null
7147
7275
  };
7148
7276
 
7149
- // Get page context
7277
+ // Get page context and selection position for heatmaps
7278
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7279
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7280
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7281
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7282
+ let pageX = null;
7283
+ let pageY = null;
7284
+ if (range) {
7285
+ try {
7286
+ const selRect = range.getBoundingClientRect();
7287
+ if (selRect.width > 0 || selRect.height > 0) {
7288
+ pageX = selRect.left + scrollX;
7289
+ pageY = selRect.top + scrollY;
7290
+ }
7291
+ } catch (e) { /* getBoundingClientRect can throw in some edge cases */ }
7292
+ }
7150
7293
  const pageData = {
7151
7294
  page_url: window.location.href,
7152
7295
  page_title: document.title,
7153
- page_path: window.location.pathname
7296
+ page_path: window.location.pathname,
7297
+ scroll_x: scrollX,
7298
+ scroll_y: scrollY,
7299
+ document_height: docHeight,
7300
+ document_width: docWidth,
7301
+ page_x: pageX,
7302
+ page_y: pageY,
7303
+ click_coordinates: pageX != null && pageY != null ? { x: pageX - scrollX, y: pageY - scrollY } : null
7154
7304
  };
7155
7305
 
7156
7306
  EventsManager.trackAutoEvent('text_selection', {
package/lib/umd/index.js CHANGED
@@ -6067,6 +6067,30 @@
6067
6067
  }
6068
6068
  }
6069
6069
 
6070
+ // Build auto_event_data for custom events (heatmap/position context when possible)
6071
+ let customAutoEventData = {};
6072
+ if (typeof window !== 'undefined' && window.document) {
6073
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
6074
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
6075
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
6076
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6077
+ customAutoEventData = {
6078
+ scroll_x: scrollX,
6079
+ scroll_y: scrollY,
6080
+ document_height: docHeight,
6081
+ document_width: docWidth
6082
+ };
6083
+ const srcEvent = options.sourceEvent;
6084
+ if (srcEvent && typeof srcEvent === 'object' && (srcEvent.pageX != null || srcEvent.clientX != null)) {
6085
+ customAutoEventData.page_x = srcEvent.pageX != null ? srcEvent.pageX : (srcEvent.clientX + (window.scrollX || window.pageXOffset || 0));
6086
+ customAutoEventData.page_y = srcEvent.pageY != null ? srcEvent.pageY : (srcEvent.clientY + (window.scrollY || window.pageYOffset || 0));
6087
+ customAutoEventData.click_coordinates = {
6088
+ x: srcEvent.clientX != null ? srcEvent.clientX : srcEvent.pageX - (window.scrollX || window.pageXOffset || 0),
6089
+ y: srcEvent.clientY != null ? srcEvent.clientY : srcEvent.pageY - (window.scrollY || window.pageYOffset || 0)
6090
+ };
6091
+ }
6092
+ }
6093
+
6070
6094
  // Prepare event data
6071
6095
  const eventData = {
6072
6096
  event_type: 'custom',
@@ -6076,6 +6100,7 @@
6076
6100
  user_id: userId,
6077
6101
  // Only include user-provided properties in custom_properties (filtered)
6078
6102
  custom_properties: filteredProperties,
6103
+ auto_event_data: customAutoEventData,
6079
6104
  // Page context and screen/viewport data go in page_data (not custom_properties)
6080
6105
  page_data: {
6081
6106
  current_url: window.location.href,
@@ -6890,7 +6915,18 @@
6890
6915
  if (element === lastClickElement && now - lastClickTime < 1000) {
6891
6916
  clickCount++;
6892
6917
  if (clickCount >= 3) {
6918
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
6919
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
6920
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
6921
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6893
6922
  EventsManager.trackAutoEvent('rage_click', {
6923
+ click_coordinates: { x: event.clientX, y: event.clientY },
6924
+ page_x: event.pageX,
6925
+ page_y: event.pageY,
6926
+ scroll_x: scrollX,
6927
+ scroll_y: scrollY,
6928
+ document_height: docHeight,
6929
+ document_width: docWidth,
6894
6930
  click_count: clickCount,
6895
6931
  time_span: now - lastClickTime,
6896
6932
  element_area: element.offsetWidth * element.offsetHeight,
@@ -6907,11 +6943,15 @@
6907
6943
  const isInteractive = isInteractiveElement(element);
6908
6944
 
6909
6945
  if (!isInteractive) {
6910
- // Capture coordinates before setTimeout
6946
+ // Capture coordinates and page context before setTimeout (for heatmaps)
6911
6947
  const clickX = event.clientX;
6912
6948
  const clickY = event.clientY;
6913
6949
  const clickElement = element;
6914
-
6950
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
6951
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
6952
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
6953
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6954
+
6915
6955
  // Mark this click as potentially dead
6916
6956
  const clickId = `${now}_${Math.random().toString(36).substr(2, 9)}`;
6917
6957
  pendingDeadClicks.set(clickId, {
@@ -6921,7 +6961,13 @@
6921
6961
  timestamp: now,
6922
6962
  url: window.location.href,
6923
6963
  clickX,
6924
- clickY
6964
+ clickY,
6965
+ page_x: event.pageX,
6966
+ page_y: event.pageY,
6967
+ scroll_x: scrollX,
6968
+ scroll_y: scrollY,
6969
+ document_height: docHeight,
6970
+ document_width: docWidth
6925
6971
  });
6926
6972
 
6927
6973
  // Check after 1 second if navigation occurred or if it's still a dead click
@@ -6935,6 +6981,12 @@
6935
6981
 
6936
6982
  EventsManager.trackAutoEvent('dead_click', {
6937
6983
  click_coordinates: { x: pendingClick.clickX, y: pendingClick.clickY },
6984
+ page_x: pendingClick.page_x,
6985
+ page_y: pendingClick.page_y,
6986
+ scroll_x: pendingClick.scroll_x,
6987
+ scroll_y: pendingClick.scroll_y,
6988
+ document_height: pendingClick.document_height,
6989
+ document_width: pendingClick.document_width,
6938
6990
  element_area: clickElement.offsetWidth * clickElement.offsetHeight,
6939
6991
  element_category: pendingClick.elementCategory,
6940
6992
  element_has_onclick: !!clickElement.onclick,
@@ -6950,9 +7002,19 @@
6950
7002
  }, 1000);
6951
7003
  }
6952
7004
 
6953
- // Track regular click with enhanced data
7005
+ // Track regular click with enhanced data (viewport + page-relative for heatmaps)
7006
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7007
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7008
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7009
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6954
7010
  EventsManager.trackAutoEvent('element_click', {
6955
7011
  click_coordinates: { x: event.clientX, y: event.clientY },
7012
+ page_x: event.pageX,
7013
+ page_y: event.pageY,
7014
+ scroll_x: scrollX,
7015
+ scroll_y: scrollY,
7016
+ document_height: docHeight,
7017
+ document_width: docWidth,
6956
7018
  double_click: event.detail === 2,
6957
7019
  element_category: elementCategory
6958
7020
  }, elementData).catch(err => {
@@ -6977,15 +7039,22 @@
6977
7039
  clearTimeout(scrollTimeout);
6978
7040
 
6979
7041
  scrollTimeout = setTimeout(() => {
6980
- const scrollDepth = Math.round((window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100);
6981
-
7042
+ const maxScroll = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight) - window.innerHeight;
7043
+ const scrollDepth = maxScroll <= 0 ? 100 : Math.round((window.scrollY / maxScroll) * 100);
7044
+
6982
7045
  if (scrollDepth > maxScrollDepth) {
6983
7046
  maxScrollDepth = scrollDepth;
6984
-
7047
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7048
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7049
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
6985
7050
  EventsManager.trackAutoEvent('page_scroll', {
6986
7051
  scroll_depth: scrollDepth,
6987
7052
  max_scroll_reached: maxScrollDepth,
6988
- scroll_position: window.scrollY
7053
+ scroll_position: window.scrollY,
7054
+ scroll_x: scrollX,
7055
+ scroll_y: window.scrollY,
7056
+ document_height: docHeight,
7057
+ document_width: docWidth
6989
7058
  }).catch(err => {
6990
7059
  console.error('❌ [AutoEvents] Failed to track page_scroll:', err);
6991
7060
  });
@@ -7003,10 +7072,24 @@
7003
7072
  document.addEventListener('submit', (event) => {
7004
7073
  const form = event.target;
7005
7074
  if (form.tagName === 'FORM') {
7075
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7076
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7077
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7078
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7079
+ const rect = form.getBoundingClientRect();
7080
+ const pageX = rect.left + scrollX;
7081
+ const pageY = rect.top + scrollY;
7006
7082
  EventsManager.trackAutoEvent('form_submit', {
7007
7083
  form_id: form.id || null,
7008
7084
  form_action: form.action || null,
7009
- form_method: form.method || 'get'
7085
+ form_method: form.method || 'get',
7086
+ scroll_x: scrollX,
7087
+ scroll_y: scrollY,
7088
+ document_height: docHeight,
7089
+ document_width: docWidth,
7090
+ page_x: pageX,
7091
+ page_y: pageY,
7092
+ click_coordinates: { x: event.clientX != null ? event.clientX : rect.left + rect.width / 2, y: event.clientY != null ? event.clientY : rect.top + rect.height / 2 }
7010
7093
  }, {
7011
7094
  element_tag_name: 'form',
7012
7095
  element_id: form.id || null,
@@ -7015,8 +7098,8 @@
7015
7098
  element_type: form.method || 'get', // FIX: Added element_type (form method)
7016
7099
  element_text: form.textContent?.trim().substring(0, 100) || null, // FIX: Added element_text
7017
7100
  element_position: { // FIX: Added element_position
7018
- x: event.clientX || 0,
7019
- y: event.clientY || 0,
7101
+ x: event.clientX || rect.left,
7102
+ y: event.clientY || rect.top,
7020
7103
  width: form.offsetWidth || 0,
7021
7104
  height: form.offsetHeight || 0
7022
7105
  }
@@ -7028,10 +7111,24 @@
7028
7111
  document.addEventListener('focus', (event) => {
7029
7112
  const element = event.target;
7030
7113
  if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA' || element.tagName === 'SELECT') {
7114
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7115
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7116
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7117
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7118
+ const rect = element.getBoundingClientRect();
7119
+ const pageX = rect.left + scrollX;
7120
+ const pageY = rect.top + scrollY;
7031
7121
  EventsManager.trackAutoEvent('form_focus', {
7032
7122
  field_name: element.name || null,
7033
7123
  field_type: element.type || null,
7034
- field_id: element.id || null
7124
+ field_id: element.id || null,
7125
+ scroll_x: scrollX,
7126
+ scroll_y: scrollY,
7127
+ document_height: docHeight,
7128
+ document_width: docWidth,
7129
+ page_x: pageX,
7130
+ page_y: pageY,
7131
+ click_coordinates: { x: event.clientX != null ? event.clientX : rect.left + rect.width / 2, y: event.clientY != null ? event.clientY : rect.top + rect.height / 2 }
7035
7132
  }, {
7036
7133
  element_tag_name: element.tagName.toLowerCase(),
7037
7134
  element_id: element.id || null,
@@ -7040,8 +7137,8 @@
7040
7137
  element_type: element.type || null,
7041
7138
  element_text: element.value ? element.value.toString().trim().substring(0, 100) : null, // FIX: Added element_text (value for form fields)
7042
7139
  element_position: { // FIX: Added element_position
7043
- x: event.clientX || 0,
7044
- y: event.clientY || 0,
7140
+ x: event.clientX != null ? event.clientX : rect.left,
7141
+ y: event.clientY != null ? event.clientY : rect.top,
7045
7142
  width: element.offsetWidth || 0,
7046
7143
  height: element.offsetHeight || 0
7047
7144
  }
@@ -7054,23 +7151,54 @@
7054
7151
  * Setup media tracking
7055
7152
  */
7056
7153
  setupMediaTracking() {
7154
+ function getPageContext() {
7155
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7156
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7157
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7158
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7159
+ return { scrollX, scrollY, docHeight, docWidth };
7160
+ }
7057
7161
  ['video', 'audio'].forEach(mediaType => {
7058
7162
  document.addEventListener('play', (event) => {
7059
7163
  if (event.target.tagName.toLowerCase() === mediaType) {
7164
+ const el = event.target;
7165
+ const { scrollX, scrollY, docHeight, docWidth } = getPageContext();
7166
+ const rect = el.getBoundingClientRect();
7167
+ const pageX = rect.left + scrollX;
7168
+ const pageY = rect.top + scrollY;
7060
7169
  EventsManager.trackAutoEvent('media_play', {
7061
7170
  media_type: mediaType,
7062
- media_src: event.target.src || null,
7063
- media_duration: event.target.duration || null
7171
+ media_src: el.src || null,
7172
+ media_duration: el.duration || null,
7173
+ scroll_x: scrollX,
7174
+ scroll_y: scrollY,
7175
+ document_height: docHeight,
7176
+ document_width: docWidth,
7177
+ page_x: pageX,
7178
+ page_y: pageY,
7179
+ click_coordinates: { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }
7064
7180
  });
7065
7181
  }
7066
7182
  }, true);
7067
7183
 
7068
7184
  document.addEventListener('pause', (event) => {
7069
7185
  if (event.target.tagName.toLowerCase() === mediaType) {
7186
+ const el = event.target;
7187
+ const { scrollX, scrollY, docHeight, docWidth } = getPageContext();
7188
+ const rect = el.getBoundingClientRect();
7189
+ const pageX = rect.left + scrollX;
7190
+ const pageY = rect.top + scrollY;
7070
7191
  EventsManager.trackAutoEvent('media_pause', {
7071
7192
  media_type: mediaType,
7072
- media_current_time: event.target.currentTime || null,
7073
- media_duration: event.target.duration || null
7193
+ media_current_time: el.currentTime || null,
7194
+ media_duration: el.duration || null,
7195
+ scroll_x: scrollX,
7196
+ scroll_y: scrollY,
7197
+ document_height: docHeight,
7198
+ document_width: docWidth,
7199
+ page_x: pageX,
7200
+ page_y: pageY,
7201
+ click_coordinates: { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }
7074
7202
  });
7075
7203
  }
7076
7204
  }, true);
@@ -7152,11 +7280,33 @@
7152
7280
  focus_offset: selection.focusOffset || null
7153
7281
  };
7154
7282
 
7155
- // Get page context
7283
+ // Get page context and selection position for heatmaps
7284
+ const scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
7285
+ const scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
7286
+ const docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
7287
+ const docWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
7288
+ let pageX = null;
7289
+ let pageY = null;
7290
+ if (range) {
7291
+ try {
7292
+ const selRect = range.getBoundingClientRect();
7293
+ if (selRect.width > 0 || selRect.height > 0) {
7294
+ pageX = selRect.left + scrollX;
7295
+ pageY = selRect.top + scrollY;
7296
+ }
7297
+ } catch (e) { /* getBoundingClientRect can throw in some edge cases */ }
7298
+ }
7156
7299
  const pageData = {
7157
7300
  page_url: window.location.href,
7158
7301
  page_title: document.title,
7159
- page_path: window.location.pathname
7302
+ page_path: window.location.pathname,
7303
+ scroll_x: scrollX,
7304
+ scroll_y: scrollY,
7305
+ document_height: docHeight,
7306
+ document_width: docWidth,
7307
+ page_x: pageX,
7308
+ page_y: pageY,
7309
+ click_coordinates: pageX != null && pageY != null ? { x: pageX - scrollX, y: pageY - scrollY } : null
7160
7310
  };
7161
7311
 
7162
7312
  EventsManager.trackAutoEvent('text_selection', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cryptique-sdk",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "type": "module",
5
5
  "description": "Cryptique Analytics SDK - Comprehensive web analytics and user tracking for modern web applications",
6
6
  "main": "lib/cjs/index.js",