cyborg-hunter 0.5.0 → 0.7.2

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/CITATION.cff +29 -0
  3. package/LICENSE +21 -0
  4. package/README.md +80 -21
  5. package/bin/cyborg-hunter.js +6 -2
  6. package/dist/cyborg-hunter-replay.js +3 -0
  7. package/dist/cyborg-hunter.esm.js +114 -22
  8. package/dist/cyborg-hunter.min.js +3 -3
  9. package/dist/extension-cyborg-hunter.js +1 -1
  10. package/dist/extension-guard-friction.js +5 -5
  11. package/dist/extension-guard-honeypot.js +1 -1
  12. package/package.json +15 -3
  13. package/src/cli/analyzers/edge-exit.js +4 -1
  14. package/src/cli/analyzers/phase-scope.js +83 -0
  15. package/src/cli/analyzers/summary.js +161 -28
  16. package/src/cli/analyzers/triage.js +59 -27
  17. package/src/cli/config.js +26 -1
  18. package/src/cli/extract-core.js +552 -0
  19. package/src/cli/ingest.js +250 -193
  20. package/src/cli/init.js +1 -1
  21. package/src/cli/preview-entry.js +36 -0
  22. package/src/cli/renderers/event-log.js +18 -19
  23. package/src/cli/renderers/extensions.js +12 -3
  24. package/src/cli/renderers/html-index-core.js +1273 -0
  25. package/src/cli/renderers/html-index.js +13 -1047
  26. package/src/cli/renderers/replay-assets.js +67 -0
  27. package/src/cli/renderers/replay-viewer.client.js +1185 -0
  28. package/src/cli/renderers/session-timeline-core.js +907 -0
  29. package/src/cli/renderers/session-timeline.js +33 -0
  30. package/src/cli/renderers/summary-csv.js +5 -0
  31. package/src/cli/renderers/trajectories-core.js +717 -0
  32. package/src/cli/renderers/trajectories.js +31 -635
  33. package/src/cli/renderers/triage-md.js +10 -4
  34. package/src/cli/renderers/typing-profile-core.js +211 -0
  35. package/src/cli/renderers/typing-profile.js +16 -186
  36. package/src/cli/report.js +42 -8
  37. package/src/core/monitor.js +77 -7
  38. package/src/core/scoring.js +11 -2
  39. package/src/core/signals/browser.js +51 -18
  40. package/src/core/signals/clipboard.js +10 -2
  41. package/src/core/signals/dom-protection.js +9 -0
  42. package/src/core/signals/focus.js +16 -2
  43. package/src/jspsych/extension-cyborg-hunter-replay.js +135 -0
  44. package/src/jspsych/extension-cyborg-hunter.js +9 -2
  45. package/src/jspsych/extension-guard-friction.js +43 -14
  46. package/src/jspsych/extension-guard-honeypot.js +25 -1
  47. package/src/replay/capture-dom.js +575 -0
  48. package/src/replay/capture-trace.js +468 -0
  49. package/src/replay/index.js +104 -0
  50. package/src/replay/persistence.js +141 -0
  51. package/src/replay/recorder.js +315 -0
  52. package/src/replay/serializer.js +119 -0
  53. package/src/replay/viewer-model.js +125 -0
  54. package/src/shared/constants.js +12 -6
  55. package/src/shared/paths.js +20 -0
  56. package/src/shared/schema.js +5 -0
  57. package/src/shared/validation.js +55 -0
  58. package/src/cli/renderers/tab-timeline.js +0 -149
@@ -32,7 +32,7 @@ function deepCopy(obj) {
32
32
  }
33
33
 
34
34
  // src/shared/constants.js
35
- var VERSION = "0.5.0";
35
+ var VERSION = "0.7.2";
36
36
  var DEFAULT_THRESHOLDS = {
37
37
  pasteMinChars: 0,
38
38
  // record ALL pastes (was 20; now 0 by default)
@@ -42,6 +42,8 @@ var DEFAULT_THRESHOLDS = {
42
42
  // outerWidth - innerWidth above this = sidebar
43
43
  layoutCompressionPx: 20,
44
44
  // innerWidth - clientWidth above scrollbar width
45
+ viewportShiftDebounceMs: 250,
46
+ // quiet period before a viewport-width shift is logged (one gesture = one event)
45
47
  syntheticGapMs: 100,
46
48
  // keydown-to-input gap above this = synthetic insertion
47
49
  idleGapMs: 1e4,
@@ -73,6 +75,7 @@ var PRESETS = {
73
75
  signals: {
74
76
  paste: true,
75
77
  copy: true,
78
+ drop: true,
76
79
  tabAway: true,
77
80
  typingSpeed: true,
78
81
  devTools: true,
@@ -112,6 +115,7 @@ var PRESETS = {
112
115
  signals: {
113
116
  paste: true,
114
117
  copy: true,
118
+ drop: true,
115
119
  tabAway: true,
116
120
  typingSpeed: true,
117
121
  devTools: true,
@@ -149,6 +153,7 @@ var PRESETS = {
149
153
  signals: {
150
154
  paste: true,
151
155
  copy: true,
156
+ drop: true,
152
157
  tabAway: true,
153
158
  typingSpeed: true,
154
159
  devTools: true,
@@ -227,13 +232,17 @@ var BENIGN_TAGS = [
227
232
  // src/shared/schema.js
228
233
  var DEFAULT_CLI_CONFIG = {
229
234
  dataDir: "./data",
235
+ replayDir: null,
236
+ // replay artifacts dir; defaults to dataDir
230
237
  filePattern: "*.json",
231
238
  participantIdField: "participantId",
232
239
  trialIdField: "trialId",
233
240
  trialOrderField: "trialIndex",
234
241
  integrityField: "integrity",
242
+ sessionIntegrityPath: null,
235
243
  trialsPerParticipant: null,
236
244
  platformIdField: null,
245
+ showPlatformId: false,
237
246
  conditionField: null,
238
247
  groupField: null,
239
248
  typingSpeedThreshold_cps: 10,
@@ -244,15 +253,19 @@ var DEFAULT_CLI_CONFIG = {
244
253
  // uses library defaults
245
254
  signals: null,
246
255
  // all enabled
256
+ phaseScope: null,
257
+ // {include: [...], exclude: [...]} — scope scores to phases
247
258
  trajectoryGrid: "auto",
248
259
  trajectoryTrialLabel: "trialId",
249
260
  trajectoryResponseField: null,
261
+ trajectoryDisplayOrder: "rule",
250
262
  outputDir: "./cyborg-hunter-report",
251
263
  outputFormat: "html"
252
264
  };
253
265
 
254
266
  // src/shared/validation.js
255
267
  var ALL_KNOWN_KEYS = [
268
+ "replayDir",
256
269
  ...Object.keys(DEFAULT_CLI_CONFIG),
257
270
  ...Object.keys(DEFAULT_THRESHOLDS),
258
271
  "preset",
@@ -285,6 +298,36 @@ function validateConfig(config, knownKeys = ALL_KNOWN_KEYS) {
285
298
  });
286
299
  return warnings;
287
300
  }
301
+ function validateScoringShape(scoring) {
302
+ const warnings = [];
303
+ if (!scoring || typeof scoring !== "object") return warnings;
304
+ const hard = scoring.hard || {};
305
+ Object.keys(hard).forEach((key) => {
306
+ const rule = hard[key];
307
+ if (!rule || !Number.isFinite(rule.countThreshold)) {
308
+ warnings.push(
309
+ `scoring.hard.${key} has no finite numeric countThreshold (got ${rule ? safeStr(rule.countThreshold) : "no rule"}) \u2014 this hard screenout rule can never trigger. A likely cause is a typo in the override key (e.g. "countThreshhold").`
310
+ );
311
+ }
312
+ });
313
+ const soft = scoring.soft || {};
314
+ Object.keys(soft).forEach((key) => {
315
+ const rule = soft[key];
316
+ if (!rule || !Number.isFinite(rule.weight)) {
317
+ warnings.push(
318
+ `scoring.soft.${key} has no finite numeric weight (got ${rule ? safeStr(rule.weight) : "no rule"}) \u2014 this soft signal contributes nothing (or breaks the soft score). A likely cause is a typo in the override key (e.g. "wieght").`
319
+ );
320
+ }
321
+ });
322
+ return warnings;
323
+ }
324
+ function safeStr(v) {
325
+ try {
326
+ return String(v);
327
+ } catch (e) {
328
+ return typeof v;
329
+ }
330
+ }
288
331
  function findClosestKey(input, keys) {
289
332
  let best = null;
290
333
  let bestDist = Infinity;
@@ -367,9 +410,10 @@ function computeTrialScores(trialData, sessionData, config, report) {
367
410
  score: speedScore
368
411
  };
369
412
  }
413
+ var trialEnd = trialData.startTime + (report && report.duration_ms != null ? report.duration_ms : performance.now() - trialData.startTime);
370
414
  if (scoring.soft.sidebarEvent) {
371
415
  var trialSidebarHits = sessionData.sidebarEvents.filter(function(e) {
372
- return e.t >= trialData.startTime && e.t <= performance.now();
416
+ return e.t >= trialData.startTime && e.t <= trialEnd;
373
417
  }).length;
374
418
  if (trialSidebarHits > 0) {
375
419
  var sidebarScore = scoring.soft.sidebarEvent.weight;
@@ -379,7 +423,7 @@ function computeTrialScores(trialData, sessionData, config, report) {
379
423
  }
380
424
  if (scoring.soft.devTools) {
381
425
  var trialDevToolsHits = sessionData.keyboardShortcuts.filter(function(e) {
382
- return e.t >= trialData.startTime && e.t <= performance.now();
426
+ return e.t >= trialData.startTime && e.t <= trialEnd;
383
427
  }).length;
384
428
  if (trialDevToolsHits > 0) {
385
429
  var devToolsScore = scoring.soft.devTools.weight;
@@ -463,10 +507,11 @@ function attachClipboardSignals(ctx) {
463
507
  type: "cut",
464
508
  t: performance.now()
465
509
  });
510
+ ctx.sessionData.copyCount++;
466
511
  ctx.fireSignal("cut", e, {});
467
512
  });
468
513
  }
469
- if (config.signals.paste) {
514
+ if (config.signals.drop) {
470
515
  ctx.addTrialListener(document, "drop", function(e) {
471
516
  var text = "";
472
517
  try {
@@ -508,9 +553,14 @@ function attachFocusSignals(ctx) {
508
553
  var event = {
509
554
  start: _tabAwayStart,
510
555
  duration_ms: Math.round(duration),
511
- type: _tabAwayType
556
+ type: _tabAwayType,
557
+ // Wall-clock at the LEAVE moment (ISO 8601), reconstructed at return
558
+ // time. Lets timelines place session-level events absolutely without
559
+ // a perfNow→wall-clock offset estimator.
560
+ timestamp: new Date(Date.now() - duration).toISOString()
512
561
  };
513
562
  if (ctx.getTrialData()) ctx.getTrialData().tabAwayEvents.push(event);
563
+ ctx.sessionData.tabAwayEvents.push(event);
514
564
  ctx.sessionData.tabAwaySums.push(Math.round(duration));
515
565
  ctx.fireSignal("tabReturn", null, {
516
566
  duration_ms: Math.round(duration),
@@ -559,7 +609,7 @@ function attachIdleGapSignals(ctx) {
559
609
  lastActivityTime = performance.now();
560
610
  }
561
611
  }, config.thresholds.idleCheckIntervalMs);
562
- ctx.addInterval(idleCheckId);
612
+ ctx.addTrialInterval(idleCheckId);
563
613
  }
564
614
  }
565
615
 
@@ -728,7 +778,7 @@ function computeTypingSpeed(editTimestamps) {
728
778
  function attachBrowserSignals(ctx) {
729
779
  var config = ctx.config;
730
780
  var sessionData = ctx.sessionData;
731
- if (config.signals.sidebarGap || config.signals.devTools) {
781
+ if (config.signals.sidebarGap) {
732
782
  var _lastZoom = window.devicePixelRatio || 1;
733
783
  var _baselineIW = window.innerWidth;
734
784
  var _sidebarOpen = false;
@@ -828,7 +878,7 @@ function attachBrowserSignals(ctx) {
828
878
  scanExtensions();
829
879
  ctx.addInterval(setInterval(scanExtensions, config.thresholds.extensionScanMs));
830
880
  }
831
- if (config.signals.keyboardShortcuts) {
881
+ if (config.signals.keyboardShortcuts || config.signals.devTools) {
832
882
  ctx.addListener(document, "keydown", function(e) {
833
883
  var dominated = e.ctrlKey || e.metaKey;
834
884
  if (dominated && e.shiftKey && (e.key === "I" || e.key === "J" || e.key === "C")) {
@@ -903,20 +953,31 @@ function attachBrowserSignals(ctx) {
903
953
  }
904
954
  if (config.signals.sidebarGap) {
905
955
  var baselineWidth = document.documentElement.clientWidth;
956
+ var _pendingWidth = null;
957
+ var _shiftTimer = null;
958
+ var flushShift = function() {
959
+ _shiftTimer = null;
960
+ if (_pendingWidth === null) return;
961
+ var settledWidth = _pendingWidth;
962
+ _pendingWidth = null;
963
+ var delta = baselineWidth - settledWidth;
964
+ if (Math.abs(delta) > config.thresholds.layoutCompressionPx) {
965
+ sessionData.viewportWidthShifts.push({
966
+ oldWidth: Math.round(baselineWidth),
967
+ newWidth: Math.round(settledWidth),
968
+ delta: Math.round(delta),
969
+ t: performance.now()
970
+ });
971
+ baselineWidth = settledWidth;
972
+ }
973
+ };
906
974
  var resizeObs = new ResizeObserver(function(entries) {
907
975
  for (var i = 0; i < entries.length; i++) {
908
- var currentWidth = entries[i].contentRect.width;
909
- var delta = baselineWidth - currentWidth;
910
- if (Math.abs(delta) > config.thresholds.layoutCompressionPx) {
911
- sessionData.layoutShifts.push({
912
- oldWidth: Math.round(baselineWidth),
913
- newWidth: Math.round(currentWidth),
914
- delta: Math.round(delta),
915
- t: performance.now()
916
- });
917
- baselineWidth = currentWidth;
918
- }
976
+ _pendingWidth = entries[i].contentRect.width;
919
977
  }
978
+ if (_shiftTimer) clearTimeout(_shiftTimer);
979
+ _shiftTimer = setTimeout(flushShift, config.thresholds.viewportShiftDebounceMs);
980
+ ctx.addInterval(_shiftTimer);
920
981
  });
921
982
  resizeObs.observe(document.documentElement);
922
983
  ctx.listeners.push({
@@ -953,7 +1014,7 @@ function attachElementTrace(ctx) {
953
1014
  } catch (e) {
954
1015
  }
955
1016
  }, config.thresholds.elementTraceHz);
956
- ctx.addInterval(elementTraceId);
1017
+ ctx.addTrialInterval(elementTraceId);
957
1018
  }
958
1019
  }
959
1020
 
@@ -1052,6 +1113,9 @@ function scanDomForDecoy(excludeSelectors) {
1052
1113
  return null;
1053
1114
  }
1054
1115
  var _decoyTrialIndex = 0;
1116
+ function resetDecoyState() {
1117
+ _decoyTrialIndex = 0;
1118
+ }
1055
1119
  function injectDecoy(opts, expectedTrialId, config, trialData, getTrialData) {
1056
1120
  if (!config.decoyAnswers) return;
1057
1121
  var currentTrial = getTrialData();
@@ -1229,6 +1293,9 @@ function init(userConfig) {
1229
1293
  warnings.forEach(function(w) {
1230
1294
  console.warn("[cyborg-hunter] " + w);
1231
1295
  });
1296
+ validateScoringShape(mergedScoring).forEach(function(w) {
1297
+ console.warn("[cyborg-hunter] " + w);
1298
+ });
1232
1299
  Object.freeze(config);
1233
1300
  Object.freeze(config.signals);
1234
1301
  Object.freeze(config.thresholds);
@@ -1275,11 +1342,13 @@ function init(userConfig) {
1275
1342
  }
1276
1343
  }
1277
1344
  var sm = createStateMachine();
1345
+ var _viewportWidthShifts = [];
1278
1346
  var sessionData = {
1279
1347
  pasteCount: 0,
1280
1348
  copyCount: 0,
1281
1349
  dropCount: 0,
1282
1350
  tabAwaySums: [],
1351
+ tabAwayEvents: [],
1283
1352
  charsPerSec: [],
1284
1353
  sidebarEvents: [],
1285
1354
  devToolsEvents: [],
@@ -1288,7 +1357,8 @@ function init(userConfig) {
1288
1357
  windowPositions: [],
1289
1358
  idleGaps: [],
1290
1359
  extensionInjections: [],
1291
- layoutShifts: [],
1360
+ viewportWidthShifts: _viewportWidthShifts,
1361
+ layoutShifts: _viewportWidthShifts,
1292
1362
  zoomChanges: [],
1293
1363
  hardScore: {},
1294
1364
  softScore: 0,
@@ -1298,6 +1368,8 @@ function init(userConfig) {
1298
1368
  var listeners = [];
1299
1369
  var trialListeners = [];
1300
1370
  var intervals = [];
1371
+ var trialIntervals = [];
1372
+ resetDecoyState();
1301
1373
  function transition(to) {
1302
1374
  if (!sm.transition(to)) {
1303
1375
  throw new Error(
@@ -1316,11 +1388,18 @@ function init(userConfig) {
1316
1388
  function addInterval(id) {
1317
1389
  intervals.push(id);
1318
1390
  }
1391
+ function addTrialInterval(id) {
1392
+ trialIntervals.push(id);
1393
+ }
1319
1394
  function removeTrialListeners() {
1320
1395
  trialListeners.forEach(function(l) {
1321
1396
  l.target.removeEventListener(l.event, l.handler, l.options);
1322
1397
  });
1323
1398
  trialListeners = [];
1399
+ trialIntervals.forEach(function(id) {
1400
+ clearInterval(id);
1401
+ });
1402
+ trialIntervals = [];
1324
1403
  removeDecoyElement();
1325
1404
  }
1326
1405
  function buildSessionCtx() {
@@ -1344,6 +1423,7 @@ function init(userConfig) {
1344
1423
  listeners,
1345
1424
  addTrialListener,
1346
1425
  addInterval,
1426
+ addTrialInterval,
1347
1427
  fireSignal,
1348
1428
  isKnownInput,
1349
1429
  getTrialData: function() {
@@ -1432,11 +1512,15 @@ function init(userConfig) {
1432
1512
  report.duration_ms = performance.now() - trialData.startTime;
1433
1513
  report.libraryVersion = VERSION;
1434
1514
  report.participantId = config.participantId;
1515
+ report.timestamp = (/* @__PURE__ */ new Date()).toISOString();
1435
1516
  var cps = computeTypingSpeed(trialData.editTimestamps);
1436
1517
  if (cps !== null) {
1437
1518
  report.charsPerSec = cps;
1438
1519
  sessionData.charsPerSec.push(cps);
1439
1520
  }
1521
+ if (!config.signals.keystrokeDynamics && !config.collectForPostHoc.fullKeystrokeTimestamps) {
1522
+ report.editTimestamps = [];
1523
+ }
1440
1524
  var mouseMetrics = computeMouseMetrics(
1441
1525
  trialData.mouseEvents,
1442
1526
  config.thresholds.mouseBotMinEvents
@@ -1444,6 +1528,10 @@ function init(userConfig) {
1444
1528
  if (mouseMetrics) {
1445
1529
  report.mouseMetrics = mouseMetrics;
1446
1530
  }
1531
+ if (config.collectForPostHoc.rawMouseTrack) {
1532
+ report.mouseTrack = report.mouseEvents;
1533
+ }
1534
+ delete report.mouseEvents;
1447
1535
  sessionData.trialsCompleted++;
1448
1536
  var scores = computeTrialScores(trialData, sessionData, config, report);
1449
1537
  report.trialSoftScore = scores.trialSoftScore;
@@ -1475,7 +1563,11 @@ function init(userConfig) {
1475
1563
  report.trialsCompleted = sessionData.trialsCompleted;
1476
1564
  report.config = {
1477
1565
  preset: config.preset,
1478
- participantId: config.participantId
1566
+ participantId: config.participantId,
1567
+ thresholds: {
1568
+ tabAwayDurationMs: config.thresholds.tabAwayDurationMs,
1569
+ typingSpeedCps: config.thresholds.typingSpeedCps
1570
+ }
1479
1571
  };
1480
1572
  report.libraryVersion = VERSION;
1481
1573
  return report;
@@ -1,6 +1,6 @@
1
- // cyborg-hunter v0.5.0 — https://github.com/konukcan/cyborg-hunter
2
- var CyborgHunter=(()=>{var _=Object.defineProperty;var ge=Object.getOwnPropertyDescriptor;var ve=Object.getOwnPropertyNames;var me=Object.prototype.hasOwnProperty;var ye=(e,n)=>{for(var t in n)_(e,t,{get:n[t],enumerable:!0})},we=(e,n,t,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of ve(n))!me.call(e,r)&&r!==t&&_(e,r,{get:()=>n[r],enumerable:!(o=ge(n,r))||o.enumerable});return e};var Se=e=>we(_({},"__esModule",{value:!0}),e);var Ce={};ye(Ce,{VERSION:()=>G,addHoneypot:()=>ue,init:()=>fe,preventTextSelection:()=>de,setAltText:()=>pe});var B={created:["session","destroyed"],session:["trial","destroyed"],trial:["session","destroyed"],destroyed:[]};function z(){let e="created";return{get current(){return e},transition(n){return B[e]&&B[e].indexOf(n)!==-1?(e=n,!0):(console.warn("[cyborg-hunter] invalid transition: "+e+" \u2192 "+n),!1)}}}function F(e){if(e===null||typeof e!="object")return e;try{return JSON.parse(JSON.stringify(e))}catch{return e}}var G="0.5.0",V={pasteMinChars:0,dropMinChars:0,sidebarGapPx:100,layoutCompressionPx:20,syntheticGapMs:100,idleGapMs:1e4,idleCheckIntervalMs:5e3,mouseThrottleMs:50,mouseMaxEvents:2e3,mouseBotMinEvents:3,sidebarPollMs:2e3,extensionScanMs:3e4,windowPositionPollMs:2e3,elementTraceHz:500,tabAwayDurationMs:3e3,typingSpeedCps:10},U={permissive:{signals:{paste:!0,copy:!0,tabAway:!0,typingSpeed:!0,devTools:!0,aiExtensions:!0,sidebarGap:!0,keyboardShortcuts:!0,mouseTracking:!0,idleGaps:!0,windowPosition:!0,clipboardManager:!0,keystrokeDynamics:!1},thresholds:{typingSpeedCps:15},scoring:{hard:{paste:{countThreshold:3},drop:{countThreshold:3}},soft:{copy:{weight:1,maxPerTrial:2},tabAway:{weight:1,maxPerTrial:2},typingSpeed:{weight:1},sidebarEvent:{weight:1},devTools:{weight:1},foreignInput:{weight:1}},softScoreThreshold:10},screenout:{enabled:!1,gracePeriodTrials:5}},standard:{signals:{paste:!0,copy:!0,tabAway:!0,typingSpeed:!0,devTools:!0,aiExtensions:!0,sidebarGap:!0,keyboardShortcuts:!0,mouseTracking:!0,idleGaps:!0,windowPosition:!0,clipboardManager:!0,keystrokeDynamics:!1},thresholds:{},scoring:{hard:{paste:{countThreshold:2},drop:{countThreshold:2}},soft:{copy:{weight:2,maxPerTrial:2},tabAway:{weight:1,maxPerTrial:2},typingSpeed:{weight:2},sidebarEvent:{weight:3},devTools:{weight:1},foreignInput:{weight:2}},softScoreThreshold:6},screenout:{enabled:!0,gracePeriodTrials:3}},strict:{signals:{paste:!0,copy:!0,tabAway:!0,typingSpeed:!0,devTools:!0,aiExtensions:!0,sidebarGap:!0,keyboardShortcuts:!0,mouseTracking:!0,idleGaps:!0,windowPosition:!0,clipboardManager:!0,keystrokeDynamics:!0},thresholds:{typingSpeedCps:8,tabAwayDurationMs:5e3,mouseMaxEvents:5e3},scoring:{hard:{paste:{countThreshold:1},copy:{countThreshold:2},drop:{countThreshold:1}},soft:{tabAway:{weight:2,maxPerTrial:1},typingSpeed:{weight:3},sidebarEvent:{weight:2},devTools:{weight:2},foreignInput:{weight:3}},softScoreThreshold:4},screenout:{enabled:!0,gracePeriodTrials:2}}},W=[{name:"ChatGPT sidebar",sel:"chatgpt-sidebar"},{name:"ChatGPT extension",sel:"[data-chatgpt]"},{name:"Copilot extension",sel:"copilot-suggestions"},{name:"Copilot extension",sel:"[data-copilot]"},{name:"Gemini extension",sel:"gemini-panel"},{name:"Claude extension",sel:"[data-claude-extension]"},{name:"Monica AI",sel:"monica-root"},{name:"Merlin AI",sel:"merlin-root"},{name:"Sider AI",sel:"[data-sider-extension]"},{name:"MaxAI",sel:"[data-maxai]"},{name:"AI assistant",sel:"[data-ai-assistant]"},{name:"AI iframe (OpenAI)",sel:'iframe[src*="chat.openai.com"]'},{name:"AI iframe (Copilot)",sel:'iframe[src*="copilot.microsoft.com"]'},{name:"AI iframe (Gemini)",sel:'iframe[src*="gemini.google.com"]'},{name:"AI iframe (Claude)",sel:'iframe[src*="claude.ai"]'},{name:"AI iframe (Perplexity)",sel:'iframe[src*="perplexity.ai"]'}],J=["grammarly","lastpass","1password","bitwarden","dashlane","roboform","honey","ublock","adblock"];var X={dataDir:"./data",filePattern:"*.json",participantIdField:"participantId",trialIdField:"trialId",trialOrderField:"trialIndex",integrityField:"integrity",trialsPerParticipant:null,platformIdField:null,conditionField:null,groupField:null,typingSpeedThreshold_cps:10,tabAwayMinDuration_ms:3e3,idleGapThreshold_ms:1e4,suspiciouslyFastRT_ms:2e3,scoring:null,signals:null,trajectoryGrid:"auto",trajectoryTrialLabel:"trialId",trajectoryResponseField:null,outputDir:"./cyborg-hunter-report",outputFormat:"html"};var Te=[...Object.keys(X),...Object.keys(V),"preset","participantId","signals","thresholds","scoring","domProtection","collectForPostHoc","onSignal","screenout","experimentContainer","knownInputs","decoyAnswers","decoyMap","decoyVisibility","decoyFraming","decoyExcludeButtons","autoMonitor","excludeTrialTypes"];function Y(e,n=Te){let t=[];return Object.keys(e).forEach(o=>{if(!n.includes(o)){let r=be(o,n),i=r?`Unknown config key "${o}" \u2014 did you mean "${r}"?`:`Unknown config key "${o}"`;t.push(i)}}),t}function be(e,n){let t=null,o=1/0;for(let r of n){let i=Ee(e.toLowerCase(),r.toLowerCase());i<o&&i<=3&&(o=i,t=r)}return t}function Ee(e,n){let t=e.length,o=n.length,r=Array.from({length:t+1},()=>Array(o+1).fill(0));for(let i=0;i<=t;i++)r[i][0]=i;for(let i=0;i<=o;i++)r[0][i]=i;for(let i=1;i<=t;i++)for(let a=1;a<=o;a++)r[i][a]=e[i-1]===n[a-1]?r[i-1][a-1]:1+Math.min(r[i-1][a],r[i][a-1],r[i-1][a-1]);return r[t][o]}function $(e,n,t,o){var r=t.scoring,i={hard:{},soft:{}},a=0;if(r.hard.paste){var c=(e.pasteEvents||[]).length;i.hard.paste={trialHits:c,sessionTotal:n.pasteCount,countThreshold:r.hard.paste.countThreshold}}if(r.hard.copy){var g=(e.copyEvents||[]).length;i.hard.copy={trialHits:g,sessionTotal:n.copyCount,countThreshold:r.hard.copy.countThreshold}}if(r.hard.drop){var p=(e.dropEvents||[]).length;i.hard.drop={trialHits:p,sessionTotal:n.dropCount,countThreshold:r.hard.drop.countThreshold}}if(r.soft.copy){var h=(e.copyEvents||[]).length,m=r.soft.copy.maxPerTrial!=null?Math.min(h,r.soft.copy.maxPerTrial):h,S=m*r.soft.copy.weight;a+=S,i.soft.copy={hits:h,capped:m,score:S}}if(r.soft.tabAway){var y=(e.tabAwayEvents||[]).filter(function(s){return(s.duration_ms||0)>t.thresholds.tabAwayDurationMs}).length,C=r.soft.tabAway.maxPerTrial!=null?Math.min(y,r.soft.tabAway.maxPerTrial):y,E=C*r.soft.tabAway.weight;a+=E,i.soft.tabAway={hits:y,capped:C,score:E}}if(r.soft.typingSpeed&&o.charsPerSec!=null){var T=t.thresholds.typingSpeedCps,u=o.charsPerSec>T?1:0,l=u*r.soft.typingSpeed.weight;a+=l,i.soft.typingSpeed={charsPerSec:o.charsPerSec,threshold:T,hit:u,score:l}}if(r.soft.sidebarEvent){var f=n.sidebarEvents.filter(function(s){return s.t>=e.startTime&&s.t<=performance.now()}).length;if(f>0){var d=r.soft.sidebarEvent.weight;a+=d,i.soft.sidebarEvent={hits:f,score:d}}}if(r.soft.devTools){var v=n.keyboardShortcuts.filter(function(s){return s.t>=e.startTime&&s.t<=performance.now()}).length;if(v>0){var b=r.soft.devTools.weight;a+=b,i.soft.devTools={hits:v,score:b}}}if(r.soft.foreignInput&&e.foreignInputEvents.length>0){var x=e.foreignInputEvents.length,A=r.soft.foreignInput.weight;a+=A,i.soft.foreignInput={hits:x,score:A}}for(var k={},P=Object.keys(r.hard),R=0;R<P.length;R++){var L=P[R],O=L==="paste"?n.pasteCount:L==="copy"?n.copyCount:L==="drop"?n.dropCount:0;k[L]={count:O,threshold:r.hard[L].countThreshold,triggered:O>=r.hard[L].countThreshold}}return{trialSoftScore:a,trialSignals:i,updatedHardScore:k}}function Q(e,n){if(!n.screenout.enabled)return!1;var t=Object.keys(e.hardScore).some(function(o){return e.hardScore[o].triggered});return t?!0:e.trialsCompleted<n.screenout.gracePeriodTrials?!1:e.softScore>=n.scoring.softScoreThreshold}function Z(e){var n=e.config,t=e.trialData;n.signals.paste&&e.addTrialListener(document,"paste",function(o){var r="";try{r=(o.clipboardData||window.clipboardData).getData("text")}catch{}var i=r.length;if(i>=n.thresholds.pasteMinChars){var a=e.isKnownInput(o.target),c={type:"paste",t:performance.now(),pastedLength:i,isKnownInput:a};n.collectForPostHoc.pasteDropContent&&(c.text=r),t.pasteEvents.push(c),e.sessionData.pasteCount++,e.fireSignal("paste",o,{pastedLength:i,isKnownInput:a,text:r})}}),n.signals.copy&&(e.addTrialListener(document,"copy",function(o){var r=window.getSelection(),i=r?r.toString():"",a=i.length;t.copyEvents.push({type:"copy",t:performance.now(),selectedLength:a}),e.sessionData.copyCount++,e.fireSignal("copy",o,{selectedLength:a})}),e.addTrialListener(document,"cut",function(o){t.copyEvents.push({type:"cut",t:performance.now()}),e.fireSignal("cut",o,{})})),n.signals.paste&&e.addTrialListener(document,"drop",function(o){var r="";try{r=o.dataTransfer.getData("text")}catch{}var i=r.length;if(i>=n.thresholds.dropMinChars){var a=e.isKnownInput(o.target),c={type:"drop",t:performance.now(),droppedLength:i,isKnownInput:a};n.collectForPostHoc.pasteDropContent&&(c.text=r),t.dropEvents.push(c),e.sessionData.dropCount++,e.fireSignal("drop",o,{droppedLength:i,isKnownInput:a,text:r})}})}function ee(e){var n=e.config;if(n.signals.tabAway){let r=function(a){t===null&&(t=performance.now(),o=a,e.fireSignal("tabAway",null,{type:a}))},i=function(){if(t!==null){var a=performance.now()-t,c={start:t,duration_ms:Math.round(a),type:o};e.getTrialData()&&e.getTrialData().tabAwayEvents.push(c),e.sessionData.tabAwaySums.push(Math.round(a)),e.fireSignal("tabReturn",null,{duration_ms:Math.round(a),type:o}),t=null,o=null}};var t=null,o=null;e.addListener(document,"visibilitychange",function(){document.hidden?r("tabHidden"):i()}),e.addListener(window,"blur",function(){r("windowBlur")}),e.addListener(window,"focus",function(){document.hidden||i()})}}function te(e){var n=e.config,t=e.trialData;if(n.signals.idleGaps){var o=n.thresholds.idleGapMs,r=performance.now();e.addTrialListener(document,"keydown",function(){r=performance.now()}),e.addTrialListener(document,"mousemove",function(){r=performance.now()},{passive:!0});var i=setInterval(function(){if(e.getTrialData()){var a=performance.now()-r;a>o&&(t.idleGaps.push({duration_ms:Math.round(a),t:performance.now()}),r=performance.now())}},n.thresholds.idleCheckIntervalMs);e.addInterval(i)}}function re(e){var n=e.config,t=e.trialData;if(n.signals.mouseTracking){let c=function(g){return function(p){t.mouseEvents.length>=r||t.mouseEvents.push({x:Math.round(p.pageX),y:Math.round(p.pageY),t:Math.round(performance.now()-a),type:g})}};var o=n.thresholds.mouseThrottleMs,r=n.thresholds.mouseMaxEvents,i=0,a=t.startTime;t.mouseTrackingCapped=!1,t.mouseTrackingCappedAtMs=null,e.addTrialListener(document,"mousemove",function(g){if(t.mouseEvents.length>=r){t.mouseTrackingCapped||(t.mouseTrackingCapped=!0,t.mouseTrackingCappedAtMs=Math.round(performance.now()-a));return}var p=performance.now();p-i<o||(i=p,t.mouseEvents.push({x:Math.round(g.pageX),y:Math.round(g.pageY),t:Math.round(p-a),type:"move"}))},{passive:!0}),e.addTrialListener(document,"click",c("click"),{passive:!0}),e.addTrialListener(document,"mousedown",c("down"),{passive:!0}),e.addTrialListener(document,"mouseup",c("up"),{passive:!0})}}function ne(e,n){var t=e.filter(function(x){return x.type==="move"});if(t.length<n)return null;for(var o=0,r=[],i=0,a=0,c=0,g=0,p=1;p<t.length;p++){var h=t[p].x-t[p-1].x,m=t[p].y-t[p-1].y,S=Math.sqrt(h*h+m*m);o+=S;var y=t[p].t-t[p-1].t;y>0&&r.push(S/y),p>1&&((h>0&&c<0||h<0&&c>0)&&i++,(m>0&&g<0||m<0&&g>0)&&a++),c=h,g=m}for(var C=t[0],E=t[t.length-1],T=Math.sqrt(Math.pow(E.x-C.x,2)+Math.pow(E.y-C.y,2)),u=o>0?Math.round(T/o*1e3)/1e3:0,l=0,f=0,d=0;d<r.length;d++){var v=r[d]-l;l+=v/(d+1),f+=v*(r[d]-l)}var b=r.length>1?Math.round(f/(r.length-1)*1e4)/1e4:0;return{pathEfficiency:u,directionChanges:i+a,speedVariance:b,moveCount:t.length}}function oe(e){var n=e.config,t=e.trialData;if(n.signals.clipboardManager){var o=0;e.addTrialListener(document,"keydown",function(){o=performance.now()}),e.addTrialListener(document,"input",function(r){if(r.target&&(r.target.tagName==="INPUT"||r.target.tagName==="TEXTAREA"||r.target.isContentEditable)&&(t.editTimestamps.push(performance.now()),performance.now()-o>n.thresholds.syntheticGapMs&&r.inputType==="insertText")){var i=(r.data||"").length;t.syntheticInsertions.push({type:"synthetic_insertion",t:performance.now(),dataLength:i}),e.fireSignal("syntheticInsertion",r,{dataLength:i})}},!0)}n.signals.typingSpeed&&!n.signals.clipboardManager&&e.addTrialListener(document,"input",function(r){r.target&&(r.target.tagName==="INPUT"||r.target.tagName==="TEXTAREA"||r.target.isContentEditable)&&t.editTimestamps.push(performance.now())},!0)}function ae(e){var n=e.config,t=e.trialData;e.addTrialListener(document,"input",function(o){var r=o.target;if(r){var i=r.tagName==="INPUT"||r.tagName==="TEXTAREA"||r.isContentEditable;if(i){var a=e.isKnownInput(r);if(a===!1){var c={t:performance.now(),targetTag:r.tagName||"unknown",targetId:(r.id||"").slice(0,50),targetClass:(r.className||"").toString().slice(0,100),inputType:o.inputType||""};n.collectForPostHoc.foreignInputContent&&(c.data=o.data||""),t.foreignInputEvents.push(c),e.fireSignal("typingOutsideExperiment",o,{targetTag:c.targetTag,targetId:c.targetId,targetClass:c.targetClass,data:o.data||""})}}}},!0)}function ie(e){if(e.length<2)return null;var n=e[0],t=e[e.length-1],o=(t-n)/1e3;if(o<=0)return null;var r=e.length;return Math.round(r/o*10)/10}function se(e){var n=e.config,t=e.sessionData;if(n.signals.sidebarGap||n.signals.devTools){var o=window.devicePixelRatio||1,r=window.innerWidth,i=!1,a=null,c=null,g=0,p=null,h=!1,m=null;e.addInterval(setInterval(function(){var l=window.innerWidth,f=n.thresholds.sidebarGapPx,d=r-l;if(!i&&d>f)i=!0,a=performance.now(),c="innerWidth_delta",g=d,p=l,t.sidebarEvents.push({type:"opened",method:"innerWidth_delta",deltaIW:d,innerWidth:l,baselineIW:r,t:performance.now()}),e.fireSignal("sidebarOpened",null,{deltaIW:d});else if(i&&c==="innerWidth_delta"&&d<f/2){var v=performance.now()-a,b=p!=null?p-l:null;t.sidebarEvents.push({type:"closed",method:"innerWidth_delta",deltaIW:b,duration_ms:Math.round(v),t:performance.now()}),e.fireSignal("sidebarClosed",null,{duration_ms:Math.round(v),deltaIW:b}),i=!1,a=null,p=null,r=l}else i||(r=l);var x=l-document.documentElement.clientWidth,A=n.thresholds.layoutCompressionPx;if(!h&&x>A)h=!0,m=performance.now(),t.sidebarEvents.push({type:"opened",method:"layout_compression",gap:x,t:performance.now()});else if(h&&x<=A){var k=performance.now()-m;t.sidebarEvents.push({type:"closed",method:"layout_compression",duration_ms:Math.round(k),t:performance.now()}),h=!1,m=null}var P=window.devicePixelRatio||1;P!==o&&(t.zoomChanges.push({from:o,to:P,t:performance.now()}),r=l,o=P)},n.thresholds.sidebarPollMs))}if(n.signals.aiExtensions){let l=function(){W.forEach(function(f){if(document.querySelector(f.sel)){var d=t.aiExtensionsFound.some(function(v){return v.name===f.name});d||t.aiExtensionsFound.push({name:f.name,t:performance.now()})}})};l(),e.addInterval(setInterval(l,n.thresholds.extensionScanMs))}if(n.signals.keyboardShortcuts&&e.addListener(document,"keydown",function(l){var f=l.ctrlKey||l.metaKey;if(f&&l.shiftKey&&(l.key==="I"||l.key==="J"||l.key==="C")){var d="Ctrl+Shift+"+l.key;t.keyboardShortcuts.push({combo:d,t:performance.now()}),e.fireSignal("keyboardShortcut",l,{combo:d})}l.key==="F12"&&(t.keyboardShortcuts.push({combo:"F12",t:performance.now()}),e.fireSignal("keyboardShortcut",l,{combo:"F12"}))}),n.signals.windowPosition&&n.collectForPostHoc.windowPositionLog){var S=function(){t.windowPositions.push({x:window.screenX,y:window.screenY,w:window.outerWidth,h:window.outerHeight,iw:window.innerWidth,ih:window.innerHeight,sw:window.screen.width,sh:window.screen.height,dpr:window.devicePixelRatio,vvScale:window.visualViewport?window.visualViewport.scale:null,t:performance.now()})};e.addInterval(setInterval(S,n.thresholds.windowPositionPollMs));var y=null,C=function(){y&&clearTimeout(y),y=setTimeout(function(){S(),y=null},250)};e.addListener(window,"resize",C)}if(n.signals.aiExtensions){let l=function(f){var d=f.toLowerCase();return J.some(function(v){return d.indexOf(v)!==-1})};var E=new MutationObserver(function(f){for(var d=0;d<f.length;d++)for(var v=f[d].addedNodes,b=0;b<v.length;b++){var x=v[b];if(x.nodeType===Node.ELEMENT_NODE){var A=x.tagName||"";A.indexOf("-")!==-1&&!l(A)&&t.extensionInjections.push({tag:A.toLowerCase(),hasShadow:!!x.shadowRoot,t:performance.now()})}}});E.observe(document.body,{childList:!0,subtree:!1}),e.listeners.push({target:null,event:"mutation",handler:E,options:{_isMutationObserver:!0}})}if(n.signals.sidebarGap){var T=document.documentElement.clientWidth,u=new ResizeObserver(function(l){for(var f=0;f<l.length;f++){var d=l[f].contentRect.width,v=T-d;Math.abs(v)>n.thresholds.layoutCompressionPx&&(t.layoutShifts.push({oldWidth:Math.round(T),newWidth:Math.round(d),delta:Math.round(v),t:performance.now()}),T=d)}});u.observe(document.documentElement),e.listeners.push({target:null,event:"resize",handler:u,options:{_isResizeObserver:!0}})}}function le(e){var n=e.config,t=e.trialData;if(n.collectForPostHoc.elementTrace){t.elementTrace=[];var o=0,r=0;e.addTrialListener(document,"mousemove",function(a){o=a.clientX,r=a.clientY},{passive:!0});var i=setInterval(function(){if(e.getTrialData()&&!(o===0&&r===0))try{var a=document.elementsFromPoint(o,r);if(a&&a.length>0){var c=a[0];t.elementTrace.push({tag:c.tagName.toLowerCase(),id:c.id||null,t:Math.round(performance.now()-t.startTime)})}}catch{}},n.thresholds.elementTraceHz);e.addInterval(i)}}function j(e,n,t,o){return t==="C"?e:n==="calibration-metadata"?"Validation trial (integrity check, ID: cal-"+o+`).
1
+ // cyborg-hunter v0.7.2 — https://github.com/konukcan/cyborg-hunter
2
+ var CyborgHunter=(()=>{var _=Object.defineProperty;var Te=Object.getOwnPropertyDescriptor;var be=Object.getOwnPropertyNames;var Ee=Object.prototype.hasOwnProperty;var Ie=(e,n)=>{for(var t in n)_(e,t,{get:n[t],enumerable:!0})},Me=(e,n,t,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of be(n))!Ee.call(e,r)&&r!==t&&_(e,r,{get:()=>n[r],enumerable:!(o=Te(n,r))||o.enumerable});return e};var xe=e=>Me(_({},"__esModule",{value:!0}),e);var Fe={};Ie(Fe,{VERSION:()=>q,addHoneypot:()=>me,init:()=>we,preventTextSelection:()=>ve,setAltText:()=>ye});var W={created:["session","destroyed"],session:["trial","destroyed"],trial:["session","destroyed"],destroyed:[]};function J(){let e="created";return{get current(){return e},transition(n){return W[e]&&W[e].indexOf(n)!==-1?(e=n,!0):(console.warn("[cyborg-hunter] invalid transition: "+e+" \u2192 "+n),!1)}}}function N(e){if(e===null||typeof e!="object")return e;try{return JSON.parse(JSON.stringify(e))}catch{return e}}var q="0.7.2",z={pasteMinChars:0,dropMinChars:0,sidebarGapPx:100,layoutCompressionPx:20,viewportShiftDebounceMs:250,syntheticGapMs:100,idleGapMs:1e4,idleCheckIntervalMs:5e3,mouseThrottleMs:50,mouseMaxEvents:2e3,mouseBotMinEvents:3,sidebarPollMs:2e3,extensionScanMs:3e4,windowPositionPollMs:2e3,elementTraceHz:500,tabAwayDurationMs:3e3,typingSpeedCps:10},$={permissive:{signals:{paste:!0,copy:!0,drop:!0,tabAway:!0,typingSpeed:!0,devTools:!0,aiExtensions:!0,sidebarGap:!0,keyboardShortcuts:!0,mouseTracking:!0,idleGaps:!0,windowPosition:!0,clipboardManager:!0,keystrokeDynamics:!1},thresholds:{typingSpeedCps:15},scoring:{hard:{paste:{countThreshold:3},drop:{countThreshold:3}},soft:{copy:{weight:1,maxPerTrial:2},tabAway:{weight:1,maxPerTrial:2},typingSpeed:{weight:1},sidebarEvent:{weight:1},devTools:{weight:1},foreignInput:{weight:1}},softScoreThreshold:10},screenout:{enabled:!1,gracePeriodTrials:5}},standard:{signals:{paste:!0,copy:!0,drop:!0,tabAway:!0,typingSpeed:!0,devTools:!0,aiExtensions:!0,sidebarGap:!0,keyboardShortcuts:!0,mouseTracking:!0,idleGaps:!0,windowPosition:!0,clipboardManager:!0,keystrokeDynamics:!1},thresholds:{},scoring:{hard:{paste:{countThreshold:2},drop:{countThreshold:2}},soft:{copy:{weight:2,maxPerTrial:2},tabAway:{weight:1,maxPerTrial:2},typingSpeed:{weight:2},sidebarEvent:{weight:3},devTools:{weight:1},foreignInput:{weight:2}},softScoreThreshold:6},screenout:{enabled:!0,gracePeriodTrials:3}},strict:{signals:{paste:!0,copy:!0,drop:!0,tabAway:!0,typingSpeed:!0,devTools:!0,aiExtensions:!0,sidebarGap:!0,keyboardShortcuts:!0,mouseTracking:!0,idleGaps:!0,windowPosition:!0,clipboardManager:!0,keystrokeDynamics:!0},thresholds:{typingSpeedCps:8,tabAwayDurationMs:5e3,mouseMaxEvents:5e3},scoring:{hard:{paste:{countThreshold:1},copy:{countThreshold:2},drop:{countThreshold:1}},soft:{tabAway:{weight:2,maxPerTrial:1},typingSpeed:{weight:3},sidebarEvent:{weight:2},devTools:{weight:2},foreignInput:{weight:3}},softScoreThreshold:4},screenout:{enabled:!0,gracePeriodTrials:2}}},X=[{name:"ChatGPT sidebar",sel:"chatgpt-sidebar"},{name:"ChatGPT extension",sel:"[data-chatgpt]"},{name:"Copilot extension",sel:"copilot-suggestions"},{name:"Copilot extension",sel:"[data-copilot]"},{name:"Gemini extension",sel:"gemini-panel"},{name:"Claude extension",sel:"[data-claude-extension]"},{name:"Monica AI",sel:"monica-root"},{name:"Merlin AI",sel:"merlin-root"},{name:"Sider AI",sel:"[data-sider-extension]"},{name:"MaxAI",sel:"[data-maxai]"},{name:"AI assistant",sel:"[data-ai-assistant]"},{name:"AI iframe (OpenAI)",sel:'iframe[src*="chat.openai.com"]'},{name:"AI iframe (Copilot)",sel:'iframe[src*="copilot.microsoft.com"]'},{name:"AI iframe (Gemini)",sel:'iframe[src*="gemini.google.com"]'},{name:"AI iframe (Claude)",sel:'iframe[src*="claude.ai"]'},{name:"AI iframe (Perplexity)",sel:'iframe[src*="perplexity.ai"]'}],Y=["grammarly","lastpass","1password","bitwarden","dashlane","roboform","honey","ublock","adblock"];var Q={dataDir:"./data",replayDir:null,filePattern:"*.json",participantIdField:"participantId",trialIdField:"trialId",trialOrderField:"trialIndex",integrityField:"integrity",sessionIntegrityPath:null,trialsPerParticipant:null,platformIdField:null,showPlatformId:!1,conditionField:null,groupField:null,typingSpeedThreshold_cps:10,tabAwayMinDuration_ms:3e3,idleGapThreshold_ms:1e4,suspiciouslyFastRT_ms:2e3,scoring:null,signals:null,phaseScope:null,trajectoryGrid:"auto",trajectoryTrialLabel:"trialId",trajectoryResponseField:null,trajectoryDisplayOrder:"rule",outputDir:"./cyborg-hunter-report",outputFormat:"html"};var Ce=["replayDir",...Object.keys(Q),...Object.keys(z),"preset","participantId","signals","thresholds","scoring","domProtection","collectForPostHoc","onSignal","screenout","experimentContainer","knownInputs","decoyAnswers","decoyMap","decoyVisibility","decoyFraming","decoyExcludeButtons","autoMonitor","excludeTrialTypes"];function ee(e,n=Ce){let t=[];return Object.keys(e).forEach(o=>{if(!n.includes(o)){let r=Ae(o,n),i=r?`Unknown config key "${o}" \u2014 did you mean "${r}"?`:`Unknown config key "${o}"`;t.push(i)}}),t}function te(e){let n=[];if(!e||typeof e!="object")return n;let t=e.hard||{};Object.keys(t).forEach(r=>{let i=t[r];(!i||!Number.isFinite(i.countThreshold))&&n.push(`scoring.hard.${r} has no finite numeric countThreshold (got ${i?Z(i.countThreshold):"no rule"}) \u2014 this hard screenout rule can never trigger. A likely cause is a typo in the override key (e.g. "countThreshhold").`)});let o=e.soft||{};return Object.keys(o).forEach(r=>{let i=o[r];(!i||!Number.isFinite(i.weight))&&n.push(`scoring.soft.${r} has no finite numeric weight (got ${i?Z(i.weight):"no rule"}) \u2014 this soft signal contributes nothing (or breaks the soft score). A likely cause is a typo in the override key (e.g. "wieght").`)}),n}function Z(e){try{return String(e)}catch{return typeof e}}function Ae(e,n){let t=null,o=1/0;for(let r of n){let i=Pe(e.toLowerCase(),r.toLowerCase());i<o&&i<=3&&(o=i,t=r)}return t}function Pe(e,n){let t=e.length,o=n.length,r=Array.from({length:t+1},()=>Array(o+1).fill(0));for(let i=0;i<=t;i++)r[i][0]=i;for(let i=0;i<=o;i++)r[0][i]=i;for(let i=1;i<=t;i++)for(let a=1;a<=o;a++)r[i][a]=e[i-1]===n[a-1]?r[i-1][a-1]:1+Math.min(r[i-1][a],r[i][a-1],r[i-1][a-1]);return r[t][o]}function re(e,n,t,o){var r=t.scoring,i={hard:{},soft:{}},a=0;if(r.hard.paste){var c=(e.pasteEvents||[]).length;i.hard.paste={trialHits:c,sessionTotal:n.pasteCount,countThreshold:r.hard.paste.countThreshold}}if(r.hard.copy){var v=(e.copyEvents||[]).length;i.hard.copy={trialHits:v,sessionTotal:n.copyCount,countThreshold:r.hard.copy.countThreshold}}if(r.hard.drop){var d=(e.dropEvents||[]).length;i.hard.drop={trialHits:d,sessionTotal:n.dropCount,countThreshold:r.hard.drop.countThreshold}}if(r.soft.copy){var h=(e.copyEvents||[]).length,m=r.soft.copy.maxPerTrial!=null?Math.min(h,r.soft.copy.maxPerTrial):h,T=m*r.soft.copy.weight;a+=T,i.soft.copy={hits:h,capped:m,score:T}}if(r.soft.tabAway){var y=(e.tabAwayEvents||[]).filter(function(F){return(F.duration_ms||0)>t.thresholds.tabAwayDurationMs}).length,k=r.soft.tabAway.maxPerTrial!=null?Math.min(y,r.soft.tabAway.maxPerTrial):y,M=k*r.soft.tabAway.weight;a+=M,i.soft.tabAway={hits:y,capped:k,score:M}}if(r.soft.typingSpeed&&o.charsPerSec!=null){var b=t.thresholds.typingSpeedCps,E=o.charsPerSec>b?1:0,u=E*r.soft.typingSpeed.weight;a+=u,i.soft.typingSpeed={charsPerSec:o.charsPerSec,threshold:b,hit:E,score:u}}var p=e.startTime+(o&&o.duration_ms!=null?o.duration_ms:performance.now()-e.startTime);if(r.soft.sidebarEvent){var S=n.sidebarEvents.filter(function(F){return F.t>=e.startTime&&F.t<=p}).length;if(S>0){var l=r.soft.sidebarEvent.weight;a+=l,i.soft.sidebarEvent={hits:S,score:l}}}if(r.soft.devTools){var f=n.keyboardShortcuts.filter(function(F){return F.t>=e.startTime&&F.t<=p}).length;if(f>0){var g=r.soft.devTools.weight;a+=g,i.soft.devTools={hits:f,score:g}}}if(r.soft.foreignInput&&e.foreignInputEvents.length>0){var I=e.foreignInputEvents.length,D=r.soft.foreignInput.weight;a+=D,i.soft.foreignInput={hits:I,score:D}}for(var P={},L=Object.keys(r.hard),j=0;j<L.length;j++){var C=L[j],K=C==="paste"?n.pasteCount:C==="copy"?n.copyCount:C==="drop"?n.dropCount:0;P[C]={count:K,threshold:r.hard[C].countThreshold,triggered:K>=r.hard[C].countThreshold}}return{trialSoftScore:a,trialSignals:i,updatedHardScore:P}}function ne(e,n){if(!n.screenout.enabled)return!1;var t=Object.keys(e.hardScore).some(function(o){return e.hardScore[o].triggered});return t?!0:e.trialsCompleted<n.screenout.gracePeriodTrials?!1:e.softScore>=n.scoring.softScoreThreshold}function oe(e){var n=e.config,t=e.trialData;n.signals.paste&&e.addTrialListener(document,"paste",function(o){var r="";try{r=(o.clipboardData||window.clipboardData).getData("text")}catch{}var i=r.length;if(i>=n.thresholds.pasteMinChars){var a=e.isKnownInput(o.target),c={type:"paste",t:performance.now(),pastedLength:i,isKnownInput:a};n.collectForPostHoc.pasteDropContent&&(c.text=r),t.pasteEvents.push(c),e.sessionData.pasteCount++,e.fireSignal("paste",o,{pastedLength:i,isKnownInput:a,text:r})}}),n.signals.copy&&(e.addTrialListener(document,"copy",function(o){var r=window.getSelection(),i=r?r.toString():"",a=i.length;t.copyEvents.push({type:"copy",t:performance.now(),selectedLength:a}),e.sessionData.copyCount++,e.fireSignal("copy",o,{selectedLength:a})}),e.addTrialListener(document,"cut",function(o){t.copyEvents.push({type:"cut",t:performance.now()}),e.sessionData.copyCount++,e.fireSignal("cut",o,{})})),n.signals.drop&&e.addTrialListener(document,"drop",function(o){var r="";try{r=o.dataTransfer.getData("text")}catch{}var i=r.length;if(i>=n.thresholds.dropMinChars){var a=e.isKnownInput(o.target),c={type:"drop",t:performance.now(),droppedLength:i,isKnownInput:a};n.collectForPostHoc.pasteDropContent&&(c.text=r),t.dropEvents.push(c),e.sessionData.dropCount++,e.fireSignal("drop",o,{droppedLength:i,isKnownInput:a,text:r})}})}function ae(e){var n=e.config;if(n.signals.tabAway){let r=function(a){t===null&&(t=performance.now(),o=a,e.fireSignal("tabAway",null,{type:a}))},i=function(){if(t!==null){var a=performance.now()-t,c={start:t,duration_ms:Math.round(a),type:o,timestamp:new Date(Date.now()-a).toISOString()};e.getTrialData()&&e.getTrialData().tabAwayEvents.push(c),e.sessionData.tabAwayEvents.push(c),e.sessionData.tabAwaySums.push(Math.round(a)),e.fireSignal("tabReturn",null,{duration_ms:Math.round(a),type:o}),t=null,o=null}};var t=null,o=null;e.addListener(document,"visibilitychange",function(){document.hidden?r("tabHidden"):i()}),e.addListener(window,"blur",function(){r("windowBlur")}),e.addListener(window,"focus",function(){document.hidden||i()})}}function ie(e){var n=e.config,t=e.trialData;if(n.signals.idleGaps){var o=n.thresholds.idleGapMs,r=performance.now();e.addTrialListener(document,"keydown",function(){r=performance.now()}),e.addTrialListener(document,"mousemove",function(){r=performance.now()},{passive:!0});var i=setInterval(function(){if(e.getTrialData()){var a=performance.now()-r;a>o&&(t.idleGaps.push({duration_ms:Math.round(a),t:performance.now()}),r=performance.now())}},n.thresholds.idleCheckIntervalMs);e.addTrialInterval(i)}}function se(e){var n=e.config,t=e.trialData;if(n.signals.mouseTracking){let c=function(v){return function(d){t.mouseEvents.length>=r||t.mouseEvents.push({x:Math.round(d.pageX),y:Math.round(d.pageY),t:Math.round(performance.now()-a),type:v})}};var o=n.thresholds.mouseThrottleMs,r=n.thresholds.mouseMaxEvents,i=0,a=t.startTime;t.mouseTrackingCapped=!1,t.mouseTrackingCappedAtMs=null,e.addTrialListener(document,"mousemove",function(v){if(t.mouseEvents.length>=r){t.mouseTrackingCapped||(t.mouseTrackingCapped=!0,t.mouseTrackingCappedAtMs=Math.round(performance.now()-a));return}var d=performance.now();d-i<o||(i=d,t.mouseEvents.push({x:Math.round(v.pageX),y:Math.round(v.pageY),t:Math.round(d-a),type:"move"}))},{passive:!0}),e.addTrialListener(document,"click",c("click"),{passive:!0}),e.addTrialListener(document,"mousedown",c("down"),{passive:!0}),e.addTrialListener(document,"mouseup",c("up"),{passive:!0})}}function le(e,n){var t=e.filter(function(g){return g.type==="move"});if(t.length<n)return null;for(var o=0,r=[],i=0,a=0,c=0,v=0,d=1;d<t.length;d++){var h=t[d].x-t[d-1].x,m=t[d].y-t[d-1].y,T=Math.sqrt(h*h+m*m);o+=T;var y=t[d].t-t[d-1].t;y>0&&r.push(T/y),d>1&&((h>0&&c<0||h<0&&c>0)&&i++,(m>0&&v<0||m<0&&v>0)&&a++),c=h,v=m}for(var k=t[0],M=t[t.length-1],b=Math.sqrt(Math.pow(M.x-k.x,2)+Math.pow(M.y-k.y,2)),E=o>0?Math.round(b/o*1e3)/1e3:0,u=0,p=0,S=0;S<r.length;S++){var l=r[S]-u;u+=l/(S+1),p+=l*(r[S]-u)}var f=r.length>1?Math.round(p/(r.length-1)*1e4)/1e4:0;return{pathEfficiency:E,directionChanges:i+a,speedVariance:f,moveCount:t.length}}function ce(e){var n=e.config,t=e.trialData;if(n.signals.clipboardManager){var o=0;e.addTrialListener(document,"keydown",function(){o=performance.now()}),e.addTrialListener(document,"input",function(r){if(r.target&&(r.target.tagName==="INPUT"||r.target.tagName==="TEXTAREA"||r.target.isContentEditable)&&(t.editTimestamps.push(performance.now()),performance.now()-o>n.thresholds.syntheticGapMs&&r.inputType==="insertText")){var i=(r.data||"").length;t.syntheticInsertions.push({type:"synthetic_insertion",t:performance.now(),dataLength:i}),e.fireSignal("syntheticInsertion",r,{dataLength:i})}},!0)}n.signals.typingSpeed&&!n.signals.clipboardManager&&e.addTrialListener(document,"input",function(r){r.target&&(r.target.tagName==="INPUT"||r.target.tagName==="TEXTAREA"||r.target.isContentEditable)&&t.editTimestamps.push(performance.now())},!0)}function ue(e){var n=e.config,t=e.trialData;e.addTrialListener(document,"input",function(o){var r=o.target;if(r){var i=r.tagName==="INPUT"||r.tagName==="TEXTAREA"||r.isContentEditable;if(i){var a=e.isKnownInput(r);if(a===!1){var c={t:performance.now(),targetTag:r.tagName||"unknown",targetId:(r.id||"").slice(0,50),targetClass:(r.className||"").toString().slice(0,100),inputType:o.inputType||""};n.collectForPostHoc.foreignInputContent&&(c.data=o.data||""),t.foreignInputEvents.push(c),e.fireSignal("typingOutsideExperiment",o,{targetTag:c.targetTag,targetId:c.targetId,targetClass:c.targetClass,data:o.data||""})}}}},!0)}function de(e){if(e.length<2)return null;var n=e[0],t=e[e.length-1],o=(t-n)/1e3;if(o<=0)return null;var r=e.length;return Math.round(r/o*10)/10}function pe(e){var n=e.config,t=e.sessionData;if(n.signals.sidebarGap){var o=window.devicePixelRatio||1,r=window.innerWidth,i=!1,a=null,c=null,v=0,d=null,h=!1,m=null;e.addInterval(setInterval(function(){var l=window.innerWidth,f=n.thresholds.sidebarGapPx,g=r-l;if(!i&&g>f)i=!0,a=performance.now(),c="innerWidth_delta",v=g,d=l,t.sidebarEvents.push({type:"opened",method:"innerWidth_delta",deltaIW:g,innerWidth:l,baselineIW:r,t:performance.now()}),e.fireSignal("sidebarOpened",null,{deltaIW:g});else if(i&&c==="innerWidth_delta"&&g<f/2){var I=performance.now()-a,D=d!=null?d-l:null;t.sidebarEvents.push({type:"closed",method:"innerWidth_delta",deltaIW:D,duration_ms:Math.round(I),t:performance.now()}),e.fireSignal("sidebarClosed",null,{duration_ms:Math.round(I),deltaIW:D}),i=!1,a=null,d=null,r=l}else i||(r=l);var P=l-document.documentElement.clientWidth,L=n.thresholds.layoutCompressionPx;if(!h&&P>L)h=!0,m=performance.now(),t.sidebarEvents.push({type:"opened",method:"layout_compression",gap:P,t:performance.now()});else if(h&&P<=L){var j=performance.now()-m;t.sidebarEvents.push({type:"closed",method:"layout_compression",duration_ms:Math.round(j),t:performance.now()}),h=!1,m=null}var C=window.devicePixelRatio||1;C!==o&&(t.zoomChanges.push({from:o,to:C,t:performance.now()}),r=l,o=C)},n.thresholds.sidebarPollMs))}if(n.signals.aiExtensions){let l=function(){X.forEach(function(f){if(document.querySelector(f.sel)){var g=t.aiExtensionsFound.some(function(I){return I.name===f.name});g||t.aiExtensionsFound.push({name:f.name,t:performance.now()})}})};l(),e.addInterval(setInterval(l,n.thresholds.extensionScanMs))}if((n.signals.keyboardShortcuts||n.signals.devTools)&&e.addListener(document,"keydown",function(l){var f=l.ctrlKey||l.metaKey;if(f&&l.shiftKey&&(l.key==="I"||l.key==="J"||l.key==="C")){var g="Ctrl+Shift+"+l.key;t.keyboardShortcuts.push({combo:g,t:performance.now()}),e.fireSignal("keyboardShortcut",l,{combo:g})}l.key==="F12"&&(t.keyboardShortcuts.push({combo:"F12",t:performance.now()}),e.fireSignal("keyboardShortcut",l,{combo:"F12"}))}),n.signals.windowPosition&&n.collectForPostHoc.windowPositionLog){var T=function(){t.windowPositions.push({x:window.screenX,y:window.screenY,w:window.outerWidth,h:window.outerHeight,iw:window.innerWidth,ih:window.innerHeight,sw:window.screen.width,sh:window.screen.height,dpr:window.devicePixelRatio,vvScale:window.visualViewport?window.visualViewport.scale:null,t:performance.now()})};e.addInterval(setInterval(T,n.thresholds.windowPositionPollMs));var y=null,k=function(){y&&clearTimeout(y),y=setTimeout(function(){T(),y=null},250)};e.addListener(window,"resize",k)}if(n.signals.aiExtensions){let l=function(f){var g=f.toLowerCase();return Y.some(function(I){return g.indexOf(I)!==-1})};var M=new MutationObserver(function(f){for(var g=0;g<f.length;g++)for(var I=f[g].addedNodes,D=0;D<I.length;D++){var P=I[D];if(P.nodeType===Node.ELEMENT_NODE){var L=P.tagName||"";L.indexOf("-")!==-1&&!l(L)&&t.extensionInjections.push({tag:L.toLowerCase(),hasShadow:!!P.shadowRoot,t:performance.now()})}}});M.observe(document.body,{childList:!0,subtree:!1}),e.listeners.push({target:null,event:"mutation",handler:M,options:{_isMutationObserver:!0}})}if(n.signals.sidebarGap){var b=document.documentElement.clientWidth,E=null,u=null,p=function(){if(u=null,E!==null){var l=E;E=null;var f=b-l;Math.abs(f)>n.thresholds.layoutCompressionPx&&(t.viewportWidthShifts.push({oldWidth:Math.round(b),newWidth:Math.round(l),delta:Math.round(f),t:performance.now()}),b=l)}},S=new ResizeObserver(function(l){for(var f=0;f<l.length;f++)E=l[f].contentRect.width;u&&clearTimeout(u),u=setTimeout(p,n.thresholds.viewportShiftDebounceMs),e.addInterval(u)});S.observe(document.documentElement),e.listeners.push({target:null,event:"resize",handler:S,options:{_isResizeObserver:!0}})}}function fe(e){var n=e.config,t=e.trialData;if(n.collectForPostHoc.elementTrace){t.elementTrace=[];var o=0,r=0;e.addTrialListener(document,"mousemove",function(a){o=a.clientX,r=a.clientY},{passive:!0});var i=setInterval(function(){if(e.getTrialData()&&!(o===0&&r===0))try{var a=document.elementsFromPoint(o,r);if(a&&a.length>0){var c=a[0];t.elementTrace.push({tag:c.tagName.toLowerCase(),id:c.id||null,t:Math.round(performance.now()-t.startTime)})}}catch{}},n.thresholds.elementTraceHz);e.addTrialInterval(i)}}function R(e,n,t,o){return t==="C"?e:n==="calibration-metadata"?"Validation trial (integrity check, ID: cal-"+o+`).
3
3
  This trial verifies participant compliance with study protocol.
4
4
  Expected response category: undetermined.
5
- Participants should indicate that the answer cannot be determined from the information provided. Deviations are flagged for manual review.`:n==="answer-key"?'Answer key (internal): The correct response for this question is "'+e+'".':typeof n=="string"&&n.indexOf("{decoyText}")!==-1?n.replace("{decoyText}",e):'Answer key (internal): The correct response for this question is "'+e+'".'}function q(e,n){var t=document.createElement("div");t.id="ch-decoy",t.setAttribute("data-ch-decoy","true");var o=n||"offscreen";o==="transparent"?t.style.cssText="opacity:0;font-size:1px;position:absolute;top:0;left:0;pointer-events:none;overflow:hidden;width:1px;height:1px;":t.style.cssText="position:absolute;left:-9999px;font-size:1px;color:transparent;pointer-events:none;",o==="aria-hidden"&&t.setAttribute("aria-hidden","true"),t.textContent=e,document.body.appendChild(t)}function K(){var e=document.getElementById("ch-decoy");e&&e.remove()}function Ie(e){if(e.labels&&e.labels.length>0){var n=e.labels[0].textContent.trim();if(n)return n}var t=e.closest("label");if(t){var o=t.textContent.trim();if(o)return o}if(e.parentElement){var r=e.parentElement.textContent.trim();if(r)return r}return e.value||""}function xe(e,n){for(var t=e.textContent.trim().toLowerCase(),o=["next","continue","submit","back","previous","start","finish","ok"],r=0;r<o.length;r++)if(t===o[r])return!0;if(n&&n.length)for(var i=0;i<n.length;i++)try{if(e.matches(n[i]))return!0}catch{}return!1}function Me(e){var n=document.querySelectorAll('input[type="radio"]');if(n.length>0){var t=Ie(n[0]);if(t)return{text:t,source:"auto-radio",optionCount:n.length}}var o=document.querySelector("select");if(o&&o.options.length>0){var r=o.options[0].textContent.trim();if(r)return{text:r,source:"auto-select",optionCount:o.options.length}}for(var i=document.querySelectorAll("button"),a=0;a<i.length;a++)if(!xe(i[a],e)){var c=i[a].textContent.trim();if(c)return{text:c,source:"auto-button",optionCount:i.length}}return null}var D=0;function ce(e,n,t,o,r){if(t.decoyAnswers){var i=r();if(!(i&&i.trialId!==n)){D++;var a=null;if(typeof e.decoyAnswer=="string"&&e.decoyAnswer.length>0){var c=j(e.decoyAnswer,t.decoyFraming,null,D);q(c,t.decoyVisibility),a={level:1,injectedText:e.decoyAnswer,source:"per-trial-override",framing:t.decoyFraming}}else if(t.decoyMap&&t.decoyMap[n]){var g=t.decoyMap[n],p=typeof g=="string"?g:g.text,h=typeof g=="object"&&g.type?g.type:"unknown",m=j(p,t.decoyFraming,h,D);q(m,t.decoyVisibility),a={level:2,injectedText:p,type:h,source:"decoyMap",framing:t.decoyFraming}}else if(t.decoyMap){console.warn('[cyborg-hunter] no decoyMap entry for trialId "'+n+'". Falling back to Level 3/4.');var S=j(null,"calibration-metadata",null,D);q(S,t.decoyVisibility),a={level:4,injectedText:S,source:"calibration-fallback",framing:"calibration-metadata"}}else try{var y=Me(t.decoyExcludeButtons);if(y){var C=j(y.text,t.decoyFraming,null,D);q(C,t.decoyVisibility),a={level:3,injectedText:y.text,source:y.source,optionCount:y.optionCount,framing:t.decoyFraming}}else{var E=j(null,"calibration-metadata",null,D);q(E,t.decoyVisibility),a={level:4,injectedText:E,source:"calibration-fallback",framing:"calibration-metadata"}}}catch{var T=j(null,"calibration-metadata",null,D);q(T,t.decoyVisibility),a={level:4,injectedText:T,source:"calibration-error",framing:"calibration-metadata"}}var u=r();u&&a&&(u.decoy=a)}}}function de(e){for(var n=document.querySelectorAll(e),t=0;t<n.length;t++)n[t].style.userSelect="none",n[t].style.webkitUserSelect="none",n[t].style.msUserSelect="none"}function ue(e,n){for(var t=document.querySelectorAll(e),o=0;o<t.length;o++){var r=document.createElement("span");r.textContent=n,r.setAttribute("data-honeypot","true"),r.style.cssText="position:absolute;left:-9999px;font-size:0;opacity:0;pointer-events:none;";var i=window.getComputedStyle(t[o]).position;i==="static"&&(t[o].style.position="relative"),t[o].appendChild(r)}}function pe(e,n){for(var t=document.querySelectorAll(e),o=0;o<t.length;o++)t[o].setAttribute("alt",n)}var H=null;function fe(e){H&&(H.destroy(),H=null);var n=e.preset||"standard",t=U[n];if(!t)throw new Error("[cyborg-hunter] unknown preset '"+n+"'. Use: permissive, standard, strict.");var o=e.scoring||{},r=t.scoring,i={hard:Object.assign({},r.hard,o.hard||{}),soft:Object.assign({},r.soft,o.soft||{}),softScoreThreshold:o.softScoreThreshold!=null?o.softScoreThreshold:r.softScoreThreshold},a={participantId:e.participantId||"unknown",preset:n,signals:Object.assign({},t.signals,e.signals||{}),thresholds:Object.assign({},V,t.thresholds||{},e.thresholds||{}),scoring:i,screenout:Object.assign({},t.screenout,e.screenout||{}),domProtection:Object.assign({preventTextSelection:!1,honeypotText:null,logCopyAttempts:!0,renderStimulusAsCanvas:!1},e.domProtection||{}),collectForPostHoc:Object.assign({fullKeystrokeTimestamps:!1,rawMouseTrack:!1,responseText:!1,windowPositionLog:!0,elementTrace:!1,pasteDropContent:!0,foreignInputContent:!0},e.collectForPostHoc||{}),decoyAnswers:e.decoyAnswers||!1,decoyMap:e.decoyMap||null,decoyVisibility:e.decoyVisibility||"offscreen",decoyFraming:e.decoyFraming||"answer-key",decoyExcludeButtons:e.decoyExcludeButtons||[".jspsych-btn"]},c=["participantId","preset","signals","thresholds","scoring","screenout","domProtection","collectForPostHoc","decoyAnswers","decoyMap","decoyVisibility","decoyFraming","decoyExcludeButtons","onSignal","experimentContainer","knownInputs"],g=Y(e,c);g.forEach(function(s){console.warn("[cyborg-hunter] "+s)}),Object.freeze(a),Object.freeze(a.signals),Object.freeze(a.thresholds),Object.freeze(a.scoring),Object.freeze(a.scoring.hard),Object.freeze(a.scoring.soft),Object.freeze(a.screenout);var p=typeof e.onSignal=="function"?e.onSignal:null,h=e.experimentContainer||null,m=null,S=e.knownInputs||null;function y(){return m||(h?(typeof h=="string"?m=document.querySelector(h):h.nodeType&&(m=h),m):null)}function C(s){if(!s)return!1;if(S)try{if(s.matches(S))return!0}catch{}var w=y();return w?w.contains(s):null}function E(s,w,I){if(p)try{p({type:s,event:w||null,trialId:l?l.trialId:null,data:I||{}})}catch{}}var T=z(),u={pasteCount:0,copyCount:0,dropCount:0,tabAwaySums:[],charsPerSec:[],sidebarEvents:[],devToolsEvents:[],aiExtensionsFound:[],keyboardShortcuts:[],windowPositions:[],idleGaps:[],extensionInjections:[],layoutShifts:[],zoomChanges:[],hardScore:{},softScore:0,trialsCompleted:0},l=null,f=[],d=[],v=[];function b(s){if(!T.transition(s))throw new Error("[cyborg-hunter] invalid lifecycle call: cannot transition from '"+T.current+"' to '"+s+"'. Expected order: init() \u2192 startSession() \u2192 (startTrial \u2192 endTrial)* \u2192 destroy(). Common cause: a startTrial() without a preceding endTrial(), or a call after destroy().")}function x(s,w,I,M){s.addEventListener(w,I,M||!1),f.push({target:s,event:w,handler:I,options:M||!1})}function A(s,w,I,M){s.addEventListener(w,I,M||!1),d.push({target:s,event:w,handler:I,options:M||!1})}function k(s){v.push(s)}function P(){d.forEach(function(s){s.target.removeEventListener(s.event,s.handler,s.options)}),d=[],K()}function R(){return{config:a,sessionData:u,listeners:f,addListener:x,addInterval:k,fireSignal:E,getTrialData:function(){return l}}}function L(){return{config:a,sessionData:u,trialData:l,listeners:f,addTrialListener:A,addInterval:k,fireSignal:E,isKnownInput:C,getTrialData:function(){return l}}}var O={startSession:function(){b("session");var s=R();se(s),ee(s)},startTrial:function(s){if(b("trial"),s=s||{},s.experimentContainer!=null&&(h=s.experimentContainer,m=null),s.knownInputs!=null&&(S=s.knownInputs),l={trialId:s.trialId||"trial-"+u.trialsCompleted,phase:s.phase||"default",startTime:performance.now(),pasteEvents:[],copyEvents:[],dropEvents:[],editTimestamps:[],mouseEvents:[],tabAwayEvents:[],idleGaps:[],foreignInputEvents:[],syntheticInsertions:[]},K(),!a.decoyAnswers)s.decoyAnswer!=null&&console.warn("[cyborg-hunter] decoyAnswer provided but decoyAnswers is not enabled. Add decoyAnswers: true to init() config.");else if(s.decoyAnswer===!1)l.decoy={level:0,source:"skipped"};else{var w=l.trialId,I=function(){ce(s,w,a,l,function(){return l})},M=typeof s.decoyAnswer=="string"&&s.decoyAnswer.length>0,he=a.decoyMap&&a.decoyMap[w];M||he?I():(s.decoyAnswer!=null&&console.warn("[cyborg-hunter] decoyAnswer must be a non-empty string, got: "+typeof s.decoyAnswer+". Falling through to auto-detection."),setTimeout(I,0))}var N=L();Z(N),oe(N),re(N),te(N),le(N),(h||S)&&ae(N)},endTrial:function(){if(b("session"),!l)return null;l.decoy&&(l.decoy.survivedTrial=!!document.getElementById("ch-decoy")),P();var s=F(l);s.duration_ms=performance.now()-l.startTime,s.libraryVersion=G,s.participantId=a.participantId;var w=ie(l.editTimestamps);w!==null&&(s.charsPerSec=w,u.charsPerSec.push(w));var I=ne(l.mouseEvents,a.thresholds.mouseBotMinEvents);I&&(s.mouseMetrics=I),u.trialsCompleted++;var M=$(l,u,a,s);return s.trialSoftScore=M.trialSoftScore,s.trialSignals=M.trialSignals,u.softScore+=M.trialSoftScore,u.hardScore=M.updatedHardScore,l=null,s},getSessionScore:function(){return{hardScore:F(u.hardScore),softScore:u.softScore,softScoreThreshold:a.scoring.softScoreThreshold,anyHardTriggered:Object.keys(u.hardScore).some(function(s){return u.hardScore[s].triggered}),trialsCompleted:u.trialsCompleted}},getSessionReport:function(){var s=F(u);return s.hardScore=F(u.hardScore),s.softScore=u.softScore,s.softScoreThreshold=a.scoring.softScoreThreshold,s.anyHardTriggered=Object.keys(u.hardScore).some(function(w){return u.hardScore[w].triggered}),s.trialsCompleted=u.trialsCompleted,s.config={preset:a.preset,participantId:a.participantId},s.libraryVersion=G,s},shouldScreenout:function(){return Q(u,a)},getRawData:function(){return F({session:u,config:{preset:a.preset,participantId:a.participantId},libraryVersion:G})},getTrialSnapshot:function(){return l?F(l):null},destroy:function(){T.transition("destroyed"),f.forEach(function(s){s.options&&s.options._isMutationObserver||s.options&&s.options._isResizeObserver?s.handler.disconnect():s.target.removeEventListener(s.event,s.handler,s.options)}),f=[],P(),v.forEach(function(s){clearInterval(s)}),v=[],H=null}};return Object.freeze(O),H=O,O}return Se(Ce);})();
5
+ Participants should indicate that the answer cannot be determined from the information provided. Deviations are flagged for manual review.`:n==="answer-key"?'Answer key (internal): The correct response for this question is "'+e+'".':typeof n=="string"&&n.indexOf("{decoyText}")!==-1?n.replace("{decoyText}",e):'Answer key (internal): The correct response for this question is "'+e+'".'}function H(e,n){var t=document.createElement("div");t.id="ch-decoy",t.setAttribute("data-ch-decoy","true");var o=n||"offscreen";o==="transparent"?t.style.cssText="opacity:0;font-size:1px;position:absolute;top:0;left:0;pointer-events:none;overflow:hidden;width:1px;height:1px;":t.style.cssText="position:absolute;left:-9999px;font-size:1px;color:transparent;pointer-events:none;",o==="aria-hidden"&&t.setAttribute("aria-hidden","true"),t.textContent=e,document.body.appendChild(t)}function U(){var e=document.getElementById("ch-decoy");e&&e.remove()}function Le(e){if(e.labels&&e.labels.length>0){var n=e.labels[0].textContent.trim();if(n)return n}var t=e.closest("label");if(t){var o=t.textContent.trim();if(o)return o}if(e.parentElement){var r=e.parentElement.textContent.trim();if(r)return r}return e.value||""}function ke(e,n){for(var t=e.textContent.trim().toLowerCase(),o=["next","continue","submit","back","previous","start","finish","ok"],r=0;r<o.length;r++)if(t===o[r])return!0;if(n&&n.length)for(var i=0;i<n.length;i++)try{if(e.matches(n[i]))return!0}catch{}return!1}function De(e){var n=document.querySelectorAll('input[type="radio"]');if(n.length>0){var t=Le(n[0]);if(t)return{text:t,source:"auto-radio",optionCount:n.length}}var o=document.querySelector("select");if(o&&o.options.length>0){var r=o.options[0].textContent.trim();if(r)return{text:r,source:"auto-select",optionCount:o.options.length}}for(var i=document.querySelectorAll("button"),a=0;a<i.length;a++)if(!ke(i[a],e)){var c=i[a].textContent.trim();if(c)return{text:c,source:"auto-button",optionCount:i.length}}return null}var O=0;function he(){O=0}function ge(e,n,t,o,r){if(t.decoyAnswers){var i=r();if(!(i&&i.trialId!==n)){O++;var a=null;if(typeof e.decoyAnswer=="string"&&e.decoyAnswer.length>0){var c=R(e.decoyAnswer,t.decoyFraming,null,O);H(c,t.decoyVisibility),a={level:1,injectedText:e.decoyAnswer,source:"per-trial-override",framing:t.decoyFraming}}else if(t.decoyMap&&t.decoyMap[n]){var v=t.decoyMap[n],d=typeof v=="string"?v:v.text,h=typeof v=="object"&&v.type?v.type:"unknown",m=R(d,t.decoyFraming,h,O);H(m,t.decoyVisibility),a={level:2,injectedText:d,type:h,source:"decoyMap",framing:t.decoyFraming}}else if(t.decoyMap){console.warn('[cyborg-hunter] no decoyMap entry for trialId "'+n+'". Falling back to Level 3/4.');var T=R(null,"calibration-metadata",null,O);H(T,t.decoyVisibility),a={level:4,injectedText:T,source:"calibration-fallback",framing:"calibration-metadata"}}else try{var y=De(t.decoyExcludeButtons);if(y){var k=R(y.text,t.decoyFraming,null,O);H(k,t.decoyVisibility),a={level:3,injectedText:y.text,source:y.source,optionCount:y.optionCount,framing:t.decoyFraming}}else{var M=R(null,"calibration-metadata",null,O);H(M,t.decoyVisibility),a={level:4,injectedText:M,source:"calibration-fallback",framing:"calibration-metadata"}}}catch{var b=R(null,"calibration-metadata",null,O);H(b,t.decoyVisibility),a={level:4,injectedText:b,source:"calibration-error",framing:"calibration-metadata"}}var E=r();E&&a&&(E.decoy=a)}}}function ve(e){for(var n=document.querySelectorAll(e),t=0;t<n.length;t++)n[t].style.userSelect="none",n[t].style.webkitUserSelect="none",n[t].style.msUserSelect="none"}function me(e,n){for(var t=document.querySelectorAll(e),o=0;o<t.length;o++){var r=document.createElement("span");r.textContent=n,r.setAttribute("data-honeypot","true"),r.style.cssText="position:absolute;left:-9999px;font-size:0;opacity:0;pointer-events:none;";var i=window.getComputedStyle(t[o]).position;i==="static"&&(t[o].style.position="relative"),t[o].appendChild(r)}}function ye(e,n){for(var t=document.querySelectorAll(e),o=0;o<t.length;o++)t[o].setAttribute("alt",n)}var V=null;function we(e){V&&(V.destroy(),V=null);var n=e.preset||"standard",t=$[n];if(!t)throw new Error("[cyborg-hunter] unknown preset '"+n+"'. Use: permissive, standard, strict.");var o=e.scoring||{},r=t.scoring,i={hard:Object.assign({},r.hard,o.hard||{}),soft:Object.assign({},r.soft,o.soft||{}),softScoreThreshold:o.softScoreThreshold!=null?o.softScoreThreshold:r.softScoreThreshold},a={participantId:e.participantId||"unknown",preset:n,signals:Object.assign({},t.signals,e.signals||{}),thresholds:Object.assign({},z,t.thresholds||{},e.thresholds||{}),scoring:i,screenout:Object.assign({},t.screenout,e.screenout||{}),domProtection:Object.assign({preventTextSelection:!1,honeypotText:null,logCopyAttempts:!0,renderStimulusAsCanvas:!1},e.domProtection||{}),collectForPostHoc:Object.assign({fullKeystrokeTimestamps:!1,rawMouseTrack:!1,responseText:!1,windowPositionLog:!0,elementTrace:!1,pasteDropContent:!0,foreignInputContent:!0},e.collectForPostHoc||{}),decoyAnswers:e.decoyAnswers||!1,decoyMap:e.decoyMap||null,decoyVisibility:e.decoyVisibility||"offscreen",decoyFraming:e.decoyFraming||"answer-key",decoyExcludeButtons:e.decoyExcludeButtons||[".jspsych-btn"]},c=["participantId","preset","signals","thresholds","scoring","screenout","domProtection","collectForPostHoc","decoyAnswers","decoyMap","decoyVisibility","decoyFraming","decoyExcludeButtons","onSignal","experimentContainer","knownInputs"],v=ee(e,c);v.forEach(function(s){console.warn("[cyborg-hunter] "+s)}),te(i).forEach(function(s){console.warn("[cyborg-hunter] "+s)}),Object.freeze(a),Object.freeze(a.signals),Object.freeze(a.thresholds),Object.freeze(a.scoring),Object.freeze(a.scoring.hard),Object.freeze(a.scoring.soft),Object.freeze(a.screenout);var d=typeof e.onSignal=="function"?e.onSignal:null,h=e.experimentContainer||null,m=null,T=e.knownInputs||null;function y(){return m||(h?(typeof h=="string"?m=document.querySelector(h):h.nodeType&&(m=h),m):null)}function k(s){if(!s)return!1;if(T)try{if(s.matches(T))return!0}catch{}var w=y();return w?w.contains(s):null}function M(s,w,x){if(d)try{d({type:s,event:w||null,trialId:p?p.trialId:null,data:x||{}})}catch{}}var b=J(),E=[],u={pasteCount:0,copyCount:0,dropCount:0,tabAwaySums:[],tabAwayEvents:[],charsPerSec:[],sidebarEvents:[],devToolsEvents:[],aiExtensionsFound:[],keyboardShortcuts:[],windowPositions:[],idleGaps:[],extensionInjections:[],viewportWidthShifts:E,layoutShifts:E,zoomChanges:[],hardScore:{},softScore:0,trialsCompleted:0},p=null,S=[],l=[],f=[],g=[];he();function I(s){if(!b.transition(s))throw new Error("[cyborg-hunter] invalid lifecycle call: cannot transition from '"+b.current+"' to '"+s+"'. Expected order: init() \u2192 startSession() \u2192 (startTrial \u2192 endTrial)* \u2192 destroy(). Common cause: a startTrial() without a preceding endTrial(), or a call after destroy().")}function D(s,w,x,A){s.addEventListener(w,x,A||!1),S.push({target:s,event:w,handler:x,options:A||!1})}function P(s,w,x,A){s.addEventListener(w,x,A||!1),l.push({target:s,event:w,handler:x,options:A||!1})}function L(s){f.push(s)}function j(s){g.push(s)}function C(){l.forEach(function(s){s.target.removeEventListener(s.event,s.handler,s.options)}),l=[],g.forEach(function(s){clearInterval(s)}),g=[],U()}function K(){return{config:a,sessionData:u,listeners:S,addListener:D,addInterval:L,fireSignal:M,getTrialData:function(){return p}}}function F(){return{config:a,sessionData:u,trialData:p,listeners:S,addTrialListener:P,addInterval:L,addTrialInterval:j,fireSignal:M,isKnownInput:k,getTrialData:function(){return p}}}var B={startSession:function(){I("session");var s=K();pe(s),ae(s)},startTrial:function(s){if(I("trial"),s=s||{},s.experimentContainer!=null&&(h=s.experimentContainer,m=null),s.knownInputs!=null&&(T=s.knownInputs),p={trialId:s.trialId||"trial-"+u.trialsCompleted,phase:s.phase||"default",startTime:performance.now(),pasteEvents:[],copyEvents:[],dropEvents:[],editTimestamps:[],mouseEvents:[],tabAwayEvents:[],idleGaps:[],foreignInputEvents:[],syntheticInsertions:[]},U(),!a.decoyAnswers)s.decoyAnswer!=null&&console.warn("[cyborg-hunter] decoyAnswer provided but decoyAnswers is not enabled. Add decoyAnswers: true to init() config.");else if(s.decoyAnswer===!1)p.decoy={level:0,source:"skipped"};else{var w=p.trialId,x=function(){ge(s,w,a,p,function(){return p})},A=typeof s.decoyAnswer=="string"&&s.decoyAnswer.length>0,Se=a.decoyMap&&a.decoyMap[w];A||Se?x():(s.decoyAnswer!=null&&console.warn("[cyborg-hunter] decoyAnswer must be a non-empty string, got: "+typeof s.decoyAnswer+". Falling through to auto-detection."),setTimeout(x,0))}var G=F();oe(G),ce(G),se(G),ie(G),fe(G),(h||T)&&ue(G)},endTrial:function(){if(I("session"),!p)return null;p.decoy&&(p.decoy.survivedTrial=!!document.getElementById("ch-decoy")),C();var s=N(p);s.duration_ms=performance.now()-p.startTime,s.libraryVersion=q,s.participantId=a.participantId,s.timestamp=new Date().toISOString();var w=de(p.editTimestamps);w!==null&&(s.charsPerSec=w,u.charsPerSec.push(w)),!a.signals.keystrokeDynamics&&!a.collectForPostHoc.fullKeystrokeTimestamps&&(s.editTimestamps=[]);var x=le(p.mouseEvents,a.thresholds.mouseBotMinEvents);x&&(s.mouseMetrics=x),a.collectForPostHoc.rawMouseTrack&&(s.mouseTrack=s.mouseEvents),delete s.mouseEvents,u.trialsCompleted++;var A=re(p,u,a,s);return s.trialSoftScore=A.trialSoftScore,s.trialSignals=A.trialSignals,u.softScore+=A.trialSoftScore,u.hardScore=A.updatedHardScore,p=null,s},getSessionScore:function(){return{hardScore:N(u.hardScore),softScore:u.softScore,softScoreThreshold:a.scoring.softScoreThreshold,anyHardTriggered:Object.keys(u.hardScore).some(function(s){return u.hardScore[s].triggered}),trialsCompleted:u.trialsCompleted}},getSessionReport:function(){var s=N(u);return s.hardScore=N(u.hardScore),s.softScore=u.softScore,s.softScoreThreshold=a.scoring.softScoreThreshold,s.anyHardTriggered=Object.keys(u.hardScore).some(function(w){return u.hardScore[w].triggered}),s.trialsCompleted=u.trialsCompleted,s.config={preset:a.preset,participantId:a.participantId,thresholds:{tabAwayDurationMs:a.thresholds.tabAwayDurationMs,typingSpeedCps:a.thresholds.typingSpeedCps}},s.libraryVersion=q,s},shouldScreenout:function(){return ne(u,a)},getRawData:function(){return N({session:u,config:{preset:a.preset,participantId:a.participantId},libraryVersion:q})},getTrialSnapshot:function(){return p?N(p):null},destroy:function(){b.transition("destroyed"),S.forEach(function(s){s.options&&s.options._isMutationObserver||s.options&&s.options._isResizeObserver?s.handler.disconnect():s.target.removeEventListener(s.event,s.handler,s.options)}),S=[],C(),f.forEach(function(s){clearInterval(s)}),f=[],V=null}};return Object.freeze(B),V=B,B}return xe(Fe);})();
6
6
  if(typeof window!=="undefined")window.IntegrityMonitor=CyborgHunter;
@@ -1 +1 @@
1
- (()=>{var n=class{static info={name:"cyborg-hunter",version:"0.3.0",data:{integrity:{type:"object"}}};constructor(t){this.jsPsych=t,this.monitor=null,this.params={},this._monitoring=!1,this._trialStart_perfNow=null}initialize(t){this.params=t;let{autoMonitor:r,excludeTrialTypes:i,...e}=t,o=window.CyborgHunter||window.IntegrityMonitor;if(!o)throw new Error("CyborgHunter not found. Load cyborg-hunter.min.js before the jsPsych extension.");this.monitor=o.init(e),this.monitor.startSession()}on_start(t){}on_load(t){if(this._monitoring=!1,this.params.autoMonitor===!1&&!(t&&t.trialId))return;if(this.params.excludeTrialTypes){let i=this.jsPsych.getCurrentTrial().type?.info?.name||"";if(this.params.excludeTrialTypes.includes(i))return}this._monitoring=!0,this._trialStart_perfNow=performance.now();let r=this.jsPsych.getProgress().current_trial_global;this.monitor.startTrial({trialId:t&&t.trialId||`trial-${r}`,phase:t?.phase||null,decoyAnswer:t?.decoyAnswer||null,experimentContainer:t?.experimentContainer||null})}on_finish(t){if(!this._monitoring)return{};this._monitoring=!1;let r=this.monitor.endTrial();return r.trialStart_perfNow=this._trialStart_perfNow,this._trialStart_perfNow=null,{integrity:r}}finalize(){if(this.monitor){try{let t=this.monitor.getSessionReport(),{sidebarEvents:r=[],keyboardShortcuts:i=[],windowPositions:e=[],layoutShifts:o=[],zoomChanges:y=[],idleGaps:d=[],extensionInjections:u=[],tabAwaySums:g=[],charsPerSec:f=[],aiExtensionsFound:p=[],hardScore:m,softScore:s,anyHardTriggered:a,trialsCompleted:w,softScoreThreshold:_,pasteCount:h=0,copyCount:l=0,dropCount:c=0,libraryVersion:S,config:P,...C}=t;this.jsPsych.data.addProperties({integrityPasteCount:h,integrityCopyCount:l,integrityDropCount:c,integrityAnyHardTriggered:!!a,integritySoftScore:s,cyborgHunterVersion:S}),this.jsPsych.data.addDataToLastTrial({integritySession:{pasteCount:h,copyCount:l,dropCount:c,sidebarEvents:r,keyboardShortcuts:i,windowPositions:e,layoutShifts:o,zoomChanges:y,idleGaps:d,extensionInjections:u,tabAwaySums:g,charsPerSec:f,aiExtensionsFound:p,...C},integrityScore:{hardScore:m,softScore:s,anyHardTriggered:a,trialsCompleted:w,softScoreThreshold:_}})}catch(t){console.warn("[cyborg-hunter] Failed to save session report:",t);try{this.jsPsych.data.addProperties({cyborgHunterFinalizeError:String(t&&t.message?t.message:t)})}catch{}}this.monitor.destroy()}}};typeof window<"u"&&(window.jsPsychCyborgHunter=n);})();
1
+ (()=>{var n=class{static info={name:"cyborg-hunter",version:"0.7.2",data:{integrity:{type:"object"}}};constructor(t){this.jsPsych=t,this.monitor=null,this.params={},this._monitoring=!1,this._trialStart_perfNow=null}initialize(t){this.params=t;let{autoMonitor:r,excludeTrialTypes:i,...e}=t,o=window.CyborgHunter||window.IntegrityMonitor;if(!o)throw new Error("CyborgHunter not found. Load cyborg-hunter.min.js before the jsPsych extension.");this.monitor=o.init(e),this.monitor.startSession()}on_start(t){}on_load(t){if(this._monitoring=!1,this.params.autoMonitor===!1&&!(t&&t.trialId))return;if(this.params.excludeTrialTypes){let i=this.jsPsych.getCurrentTrial().type?.info?.name||"";if(this.params.excludeTrialTypes.includes(i))return}this._monitoring=!0,this._trialStart_perfNow=performance.now();let r=this.jsPsych.getProgress().current_trial_global;this.monitor.startTrial({trialId:t&&t.trialId||`trial-${r}`,phase:t?.phase||null,decoyAnswer:t?.decoyAnswer??null,experimentContainer:t?.experimentContainer||null})}on_finish(t){if(!this._monitoring)return{};this._monitoring=!1;let r=this.monitor.endTrial();return r.trialStart_perfNow=this._trialStart_perfNow,this._trialStart_perfNow=null,{integrity:r}}finalize(){if(this.monitor){try{let t=this.monitor.getSessionReport(),{sidebarEvents:r=[],keyboardShortcuts:i=[],windowPositions:e=[],layoutShifts:o=[],zoomChanges:y=[],idleGaps:d=[],extensionInjections:u=[],tabAwaySums:g=[],charsPerSec:f=[],aiExtensionsFound:p=[],hardScore:m,softScore:s,anyHardTriggered:a,trialsCompleted:w,softScoreThreshold:_,pasteCount:h=0,copyCount:l=0,dropCount:c=0,libraryVersion:S,config:C,...b}=t;this.jsPsych.data.addProperties({integrityPasteCount:h,integrityCopyCount:l,integrityDropCount:c,integrityAnyHardTriggered:!!a,integritySoftScore:s,cyborgHunterVersion:S}),this.jsPsych.data.addDataToLastTrial({integritySession:{pasteCount:h,copyCount:l,dropCount:c,sidebarEvents:r,keyboardShortcuts:i,windowPositions:e,layoutShifts:o,zoomChanges:y,idleGaps:d,extensionInjections:u,tabAwaySums:g,charsPerSec:f,aiExtensionsFound:p,config:C,...b},integrityScore:{hardScore:m,softScore:s,anyHardTriggered:a,trialsCompleted:w,softScoreThreshold:_}})}catch(t){console.warn("[cyborg-hunter] Failed to save session report:",t);try{this.jsPsych.data.addProperties({cyborgHunterFinalizeError:String(t&&t.message?t.message:t)})}catch{}}this.monitor.destroy()}}};typeof window<"u"&&(window.jsPsychCyborgHunter=n);})();