easyproctor-hml 2.5.15 → 2.5.17

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/esm/index.js CHANGED
@@ -8791,6 +8791,14 @@ var BaseDetection = class {
8791
8791
  return "Face n\xE3o centralizada, mova-se para cima";
8792
8792
  case "wrong_face_position_move_bottom_detected":
8793
8793
  return "Face n\xE3o centralizada, mova-se para baixo";
8794
+ case "face_turned_left_detected":
8795
+ return "Face virada para a esquerda, centralize-a";
8796
+ case "face_turned_right_detected":
8797
+ return "Face virada para a direita, centralize-a";
8798
+ case "face_turned_up_detected":
8799
+ return "Face virada para cima, centralize-a";
8800
+ case "face_turned_down_detected":
8801
+ return "Face virada para baixo, centralize-a";
8794
8802
  default:
8795
8803
  return description;
8796
8804
  }
@@ -8821,7 +8829,14 @@ function buildVideoPreview() {
8821
8829
  // src/modules/faceDetection.ts
8822
8830
  var FaceDetection = class extends BaseDetection {
8823
8831
  constructor(options, paramsConfig, classVideo = "videoPreviewFrameDetection", classDiv = "liveViewFrameDetection") {
8824
- super("FaceDetector", `https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite`, options, paramsConfig, classVideo, classDiv);
8832
+ super(
8833
+ "FaceDetector",
8834
+ `https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite`,
8835
+ options,
8836
+ paramsConfig,
8837
+ classVideo,
8838
+ classDiv
8839
+ );
8825
8840
  this.emmitedPositionAlert = false;
8826
8841
  this.emmitedFaceAlert = false;
8827
8842
  }
@@ -8967,8 +8982,8 @@ var FaceDetection = class extends BaseDetection {
8967
8982
  for (const keypoint of detection.keypoints) {
8968
8983
  const keypointEl = document.createElement("span");
8969
8984
  keypointEl.className = "key-point";
8970
- let kpX = keypoint.x < 2 ? keypoint.x * videoElement.videoWidth : keypoint.x;
8971
- let kpY = keypoint.y < 2 ? keypoint.y * videoElement.videoHeight : keypoint.y;
8985
+ const kpX = keypoint.x < 2 ? keypoint.x * videoElement.videoWidth : keypoint.x;
8986
+ const kpY = keypoint.y < 2 ? keypoint.y * videoElement.videoHeight : keypoint.y;
8972
8987
  const mirroredKpX = videoElement.videoWidth - kpX;
8973
8988
  const screenX = mirroredKpX * scaleX;
8974
8989
  const screenY = kpY * scaleY;
@@ -9007,55 +9022,87 @@ var FaceDetection = class extends BaseDetection {
9007
9022
  }
9008
9023
  }
9009
9024
  if (result.detections.length === 0) return;
9010
- let face = result.detections[0].boundingBox;
9011
- let video = document.getElementById(this.classVideo);
9025
+ let failedFacePosition = false;
9026
+ const video = document.getElementById(this.classVideo);
9012
9027
  const imageWidth = video.videoWidth;
9013
9028
  const imageHeight = video.videoHeight;
9014
- let failedFacePosition = false;
9029
+ const detection = result.detections[0];
9030
+ const face = detection.boundingBox;
9031
+ const keypoints = detection.keypoints;
9032
+ const rightEye = keypoints[0];
9033
+ const leftEye = keypoints[1];
9034
+ const mouth = keypoints[3];
9035
+ const rightEar = keypoints[4];
9036
+ const leftEar = keypoints[5];
9037
+ const nose = keypoints[2];
9015
9038
  if (imageWidth > imageHeight) {
9016
9039
  if (face.height / imageHeight > 0.7) {
9017
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_size_detected", "position_detection_on_stream");
9040
+ !this.emmitedPositionAlert && this.handleAlert(
9041
+ "wrong_face_size_detected",
9042
+ "position_detection_on_stream"
9043
+ );
9018
9044
  failedFacePosition = true;
9019
9045
  this.emmitedPositionAlert = true;
9020
9046
  } else if (face.width / imageWidth > 0.7) {
9021
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_size_detected", "position_detection_on_stream");
9047
+ !this.emmitedPositionAlert && this.handleAlert(
9048
+ "wrong_face_size_detected",
9049
+ "position_detection_on_stream"
9050
+ );
9022
9051
  this.emmitedPositionAlert = true;
9023
9052
  failedFacePosition = true;
9024
9053
  }
9025
9054
  }
9026
- let start = [face.originX, face.originY];
9027
- let end = [face.originX + face.width, face.originY + face.height];
9055
+ const start = [face.originX, face.originY];
9056
+ const end = [face.originX + face.width, face.originY + face.height];
9028
9057
  if (start[0] < 0.1 * face.width || start[1] < 0.2 * face.height || end[0] > imageWidth - 0.1 * face.width || end[1] > imageHeight - 5) {
9029
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_edge_detected", "position_detection_on_stream");
9058
+ !this.emmitedPositionAlert && this.handleAlert(
9059
+ "wrong_face_position_edge_detected",
9060
+ "position_detection_on_stream"
9061
+ );
9030
9062
  this.emmitedPositionAlert = true;
9031
9063
  failedFacePosition = true;
9032
9064
  }
9033
- let leftGap = start[0];
9034
- let rightGap = imageWidth - end[0];
9035
- let topGap = start[1];
9036
- let bottomGap = imageHeight - end[1];
9065
+ const leftGap = start[0];
9066
+ const rightGap = imageWidth - end[0];
9067
+ const topGap = start[1];
9068
+ const bottomGap = imageHeight - end[1];
9037
9069
  if (leftGap > 2 * rightGap) {
9038
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_right_detected", "position_detection_on_stream");
9070
+ !this.emmitedPositionAlert && this.handleAlert(
9071
+ "wrong_face_position_move_right_detected",
9072
+ "position_detection_on_stream"
9073
+ );
9039
9074
  this.emmitedPositionAlert = true;
9040
9075
  failedFacePosition = true;
9041
9076
  }
9042
9077
  if (topGap > 4 * bottomGap) {
9043
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_top_detected", "position_detection_on_stream");
9078
+ !this.emmitedPositionAlert && this.handleAlert(
9079
+ "wrong_face_position_move_top_detected",
9080
+ "position_detection_on_stream"
9081
+ );
9044
9082
  this.emmitedPositionAlert = true;
9045
9083
  failedFacePosition = true;
9046
9084
  }
9047
9085
  if (rightGap > 2 * leftGap) {
9048
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_left_detected", "position_detection_on_stream");
9086
+ !this.emmitedPositionAlert && this.handleAlert(
9087
+ "wrong_face_position_move_left_detected",
9088
+ "position_detection_on_stream"
9089
+ );
9049
9090
  this.emmitedPositionAlert = true;
9050
9091
  failedFacePosition = true;
9051
9092
  }
9052
9093
  if (bottomGap > 3 * topGap) {
9053
- !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_bottom_detected", "position_detection_on_stream");
9094
+ !this.emmitedPositionAlert && this.handleAlert(
9095
+ "wrong_face_position_move_bottom_detected",
9096
+ "position_detection_on_stream"
9097
+ );
9054
9098
  this.emmitedPositionAlert = true;
9055
9099
  failedFacePosition = true;
9056
9100
  }
9057
9101
  if (failedFacePosition == false) {
9058
- this.emmitedPositionAlert && this.handleOk("ok_position_face_detected", "position_detection_on_stream");
9102
+ this.emmitedPositionAlert && this.handleOk(
9103
+ "ok_position_face_detected",
9104
+ "position_detection_on_stream"
9105
+ );
9059
9106
  this.emmitedPositionAlert = false;
9060
9107
  }
9061
9108
  }
@@ -13574,8 +13621,20 @@ var DeviceCheckerUI = class {
13574
13621
  );
13575
13622
  checkmark_FacePosition.appendChild(checkmark_stem_FacePosition);
13576
13623
  checkmark_FacePosition.appendChild(checkmark_kick_FacePosition);
13624
+ const info_FacePosition = document.createElement("span");
13625
+ const info_stem_FacePosition = document.createElement("div");
13626
+ const info_kick_FacePosition = document.createElement("div");
13627
+ info_FacePosition.style.marginLeft = "10px";
13628
+ info_FacePosition.setAttribute("class", "info");
13629
+ info_FacePosition.setAttribute("id", "info_FacePosition");
13630
+ info_FacePosition.style.display = "none";
13631
+ info_stem_FacePosition.setAttribute("class", "info_stem");
13632
+ info_kick_FacePosition.setAttribute("class", "info_kick");
13633
+ info_FacePosition.appendChild(info_stem_FacePosition);
13634
+ info_FacePosition.appendChild(info_kick_FacePosition);
13577
13635
  alertDivFacePosition.appendChild(checkmark_FacePosition);
13578
13636
  alertDivFacePosition.appendChild(facePositionAlert);
13637
+ alertDivFacePosition.appendChild(info_FacePosition);
13579
13638
  const alertDivAmbientVerify = document.createElement("div");
13580
13639
  alertDivAmbientVerify.setAttribute("class", "alert-div");
13581
13640
  alertDivAmbientVerify.setAttribute("id", "alertDivAmbientVerify");
@@ -13906,6 +13965,8 @@ var DeviceCheckerUI = class {
13906
13965
  realtimeAlertsUI(response) {
13907
13966
  const facePositionAlert = document.getElementById("facePositionAlert");
13908
13967
  const ambientVerifyAlert = document.getElementById("ambientVerifyAlert");
13968
+ const alertDivFacePosition = document.getElementById("alertDivFacePosition");
13969
+ const info_FacePosition = document.getElementById("info_FacePosition");
13909
13970
  const checkmark_FacePosition = document.getElementById("checkmark_FacePosition");
13910
13971
  const checkmark_stem_FacePosition = document.getElementById("checkmark_stem_FacePosition");
13911
13972
  const checkmark_kick_FacePosition = document.getElementById("checkmark_kick_FacePosition");
@@ -13916,6 +13977,8 @@ var DeviceCheckerUI = class {
13916
13977
  if (response.status === "OK") {
13917
13978
  facePositionAlert && (facePositionAlert.style.color = "#16A34A");
13918
13979
  ambientVerifyAlert && (ambientVerifyAlert.style.color = "#16A34A");
13980
+ alertDivFacePosition && alertDivFacePosition.setAttribute("title", ``);
13981
+ info_FacePosition && (info_FacePosition.style.display = "none");
13919
13982
  alertDivAmbientVerify && alertDivAmbientVerify.setAttribute("title", ``);
13920
13983
  if (checkmark_FacePosition) {
13921
13984
  checkmark_FacePosition.setAttribute("class", "checkmark");
@@ -13930,6 +13993,8 @@ var DeviceCheckerUI = class {
13930
13993
  } else {
13931
13994
  if (checkmark_FacePosition && (response.type === "position_detection_on_stream" || response.type === "face_detection_on_stream")) {
13932
13995
  facePositionAlert && (facePositionAlert.style.color = "#FF0000");
13996
+ info_FacePosition && (info_FacePosition.style.display = "block");
13997
+ alertDivFacePosition && alertDivFacePosition.setAttribute("title", response.description);
13933
13998
  checkmark_FacePosition.setAttribute("class", "checkmark_error");
13934
13999
  checkmark_stem_FacePosition.setAttribute("class", "checkmark_stem_error");
13935
14000
  checkmark_kick_FacePosition.setAttribute("class", "checkmark_kick_error");
@@ -15128,64 +15193,187 @@ var ProctoringUploader = class {
15128
15193
  var AlertRecorder = class {
15129
15194
  constructor(options, optionsProctoring) {
15130
15195
  this.alerts = [];
15196
+ // Variáveis para controle de inatividade
15197
+ this.lastActivityTime = Date.now();
15198
+ this.IDLE_THRESHOLD_MS = 3e4;
15199
+ // ==========================================
15200
+ // HANDLERS (Funções Arrow para preservar o 'this')
15201
+ // ==========================================
15202
+ // 1. LOST / RETURN FOCUS
15203
+ this.handleLostFocus = () => {
15204
+ if (this.getRelativeTime() > 1e4) {
15205
+ this.createAlert(25 /* FocusOff */, 3 /* Screen */, "Usu\xE1rio mudou de aba ou minimizou");
15206
+ const alertPayload = {
15207
+ begin: this.getRelativeTime(),
15208
+ end: 0,
15209
+ alert: 25 /* FocusOff */,
15210
+ type: 3 /* Screen */
15211
+ };
15212
+ this.onLostFocusCallback(alertPayload);
15213
+ this.alerts.push(alertPayload);
15214
+ }
15215
+ };
15216
+ this.handleReturnFocus = () => {
15217
+ const lastAlert = this.alerts[this.alerts.length - 1];
15218
+ if (lastAlert) {
15219
+ this.onFocusCallback({
15220
+ begin: lastAlert.begin,
15221
+ end: this.getRelativeTime(),
15222
+ alert: 25 /* FocusOff */,
15223
+ type: 3 /* Screen */
15224
+ });
15225
+ lastAlert.end = this.getRelativeTime();
15226
+ }
15227
+ };
15228
+ // 2. SPLIT SCREEN DETECTION
15229
+ this.handleResize = () => {
15230
+ clearTimeout(this.resizeTimeout);
15231
+ this.resizeTimeout = setTimeout(() => {
15232
+ const screenWidth = window.screen.availWidth;
15233
+ const windowWidth = window.outerWidth;
15234
+ if (windowWidth < screenWidth * 0.85) {
15235
+ const msg = `Split Screen Detectado: Janela ocupa ${(windowWidth / screenWidth * 100).toFixed(0)}% da tela`;
15236
+ this.createAlert(200 /* System */, 3 /* Screen */, msg);
15237
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15238
+ status: "ALERT",
15239
+ description: msg,
15240
+ type: "split_screen"
15241
+ });
15242
+ }
15243
+ }, 1e3);
15244
+ };
15245
+ // 3. DETECÇÃO DE INATIVIDADE (NO INPUT)
15246
+ this.handleUserActivity = (e3) => {
15247
+ this.lastActivityTime = Date.now();
15248
+ console.log("\u{1F449} handleUserActivity", e3);
15249
+ };
15250
+ // 5. CLIPBOARD HANDLERS
15251
+ this.handleCopy = (e3) => {
15252
+ e3.preventDefault();
15253
+ const msg = "Tentativa de Copiar (Clipboard)";
15254
+ this.createAlert(100 /* ForbiddenAction */, 6 /* System */, msg);
15255
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15256
+ status: "ALERT",
15257
+ description: msg,
15258
+ type: "clipboard_copy"
15259
+ });
15260
+ };
15261
+ this.handleCut = (e3) => {
15262
+ e3.preventDefault();
15263
+ const msg = "Tentativa de Recortar (Clipboard)";
15264
+ this.createAlert(100 /* ForbiddenAction */, 6 /* System */, msg);
15265
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15266
+ status: "ALERT",
15267
+ description: msg,
15268
+ type: "clipboard_cut"
15269
+ });
15270
+ };
15271
+ this.handlePaste = (e3) => {
15272
+ e3.preventDefault();
15273
+ const msg = "Tentativa de Colar (Clipboard)";
15274
+ this.createAlert(100 /* ForbiddenAction */, 6 /* System */, msg);
15275
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15276
+ status: "ALERT",
15277
+ description: msg,
15278
+ type: "clipboard_paste"
15279
+ });
15280
+ };
15131
15281
  this.onLostFocusCallback = options.onLostFocusCallback;
15132
15282
  this.onFocusCallback = options.onFocusCallback;
15283
+ this.onRealtimeAlertCallback = options.onRealtimeAlertCallback;
15133
15284
  this.optionsProctoring = optionsProctoring;
15134
15285
  }
15135
15286
  async startRecording() {
15136
15287
  this.startTime = new Date(Date.now());
15137
- if (this.optionsProctoring.captureScreen) {
15138
- window.addEventListener("blur", () => this.onLostFocus());
15139
- window.addEventListener("focus", () => this.onReturnFocus());
15140
- }
15288
+ this.alerts = [];
15289
+ this.attachListeners();
15141
15290
  }
15142
15291
  async pauseRecording() {
15143
- window.removeEventListener("blur", () => this.onLostFocus());
15144
- window.removeEventListener("focus", () => this.onReturnFocus());
15292
+ this.detachListeners();
15145
15293
  }
15146
15294
  async resumeRecording() {
15147
- if (this.optionsProctoring.captureScreen) {
15148
- window.addEventListener("blur", () => this.onLostFocus());
15149
- window.addEventListener("focus", () => this.onReturnFocus());
15150
- }
15295
+ this.attachListeners();
15151
15296
  }
15152
15297
  async stopRecording() {
15153
- window.removeEventListener("blur", () => this.onLostFocus());
15154
- window.removeEventListener("focus", () => this.onReturnFocus());
15298
+ this.detachListeners();
15155
15299
  }
15156
15300
  async saveOnSession(session) {
15157
15301
  this.alerts.forEach((alert) => {
15158
15302
  session.addAlert(alert);
15159
15303
  });
15160
15304
  }
15161
- onLostFocus() {
15162
- var _a2;
15163
- if (Date.now() - ((_a2 = this.startTime) == null ? void 0 : _a2.getTime()) > 1e4) {
15164
- this.onLostFocusCallback({
15165
- begin: Date.now() - this.startTime.getTime(),
15166
- end: 0,
15167
- alert: 25 /* FocusOff */,
15168
- type: 3 /* Screen */
15169
- });
15170
- this.alerts.push({
15171
- begin: Date.now() - this.startTime.getTime(),
15172
- end: 0,
15173
- alert: 25 /* FocusOff */,
15174
- type: 3 /* Screen */
15175
- });
15176
- }
15305
+ // ==========================================
15306
+ // GERENCIAMENTO DE LISTENERS
15307
+ // ==========================================
15308
+ attachListeners() {
15309
+ if (this.optionsProctoring.captureScreen) {
15310
+ window.addEventListener("blur", this.handleLostFocus);
15311
+ window.addEventListener("focus", this.handleReturnFocus);
15312
+ window.addEventListener("resize", this.handleResize);
15313
+ window.document.addEventListener("copy", this.handleCopy);
15314
+ window.document.addEventListener("cut", this.handleCut);
15315
+ window.document.addEventListener("paste", this.handlePaste);
15316
+ }
15317
+ }
15318
+ detachListeners() {
15319
+ window.removeEventListener("blur", this.handleLostFocus);
15320
+ window.removeEventListener("focus", this.handleReturnFocus);
15321
+ window.removeEventListener("resize", this.handleResize);
15322
+ window.document.removeEventListener("copy", this.handleCopy);
15323
+ window.document.removeEventListener("cut", this.handleCut);
15324
+ window.document.removeEventListener("paste", this.handlePaste);
15325
+ }
15326
+ // private startIdleChecker() {
15327
+ // this.stopIdleChecker(); // Garante que não tenha duplicação
15328
+ // this.idleCheckInterval = setInterval(() => {
15329
+ // const idleTime = Date.now() - this.lastActivityTime;
15330
+ // if (idleTime > this.IDLE_THRESHOLD_MS) {
15331
+ // // Verifica se já não mandamos esse alerta recentemente para não floodar
15332
+ // const lastAlert = this.alerts[this.alerts.length - 1];
15333
+ // // Se o último alerta não foi de inatividade ou foi há muito tempo, cria novo
15334
+ // if (!lastAlert || lastAlert.alert !== AlertCategory.ForbiddenAction || (this.getRelativeTime() - lastAlert.begin > 5000)) {
15335
+ // const msg = "Usuário inativo por muito tempo";
15336
+ // // Assumindo ForbiddenAction ou outra categoria adequada
15337
+ // this.createAlert(AlertCategory.ForbiddenAction, AlertType.System, msg);
15338
+ // this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15339
+ // status: 'ALERT',
15340
+ // description: msg,
15341
+ // type: 'user_idle'
15342
+ // });
15343
+ // }
15344
+ // }
15345
+ // }, 5000); // Checa a cada 5 segundos
15346
+ // }
15347
+ // private stopIdleChecker() {
15348
+ // if (this.idleCheckInterval) {
15349
+ // clearInterval(this.idleCheckInterval);
15350
+ // this.idleCheckInterval = null;
15351
+ // }
15352
+ // }
15353
+ // 4. BACKGROUND EVENTS (Eventos disparados manualmente)
15354
+ addBackgroundEvent(description, category = 200 /* System */) {
15355
+ this.createAlert(category, 6 /* System */, description);
15356
+ this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15357
+ status: "ALERT",
15358
+ description,
15359
+ type: "background_event"
15360
+ });
15177
15361
  }
15178
- onReturnFocus() {
15179
- const lastAlert = this.alerts[this.alerts.length - 1];
15180
- if (lastAlert) {
15181
- this.onFocusCallback({
15182
- begin: lastAlert.begin,
15183
- end: Date.now() - this.startTime.getTime(),
15184
- alert: 25 /* FocusOff */,
15185
- type: 3 /* Screen */
15186
- });
15187
- lastAlert.end = Date.now() - this.startTime.getTime();
15188
- }
15362
+ // ==========================================
15363
+ // HELPERS
15364
+ // ==========================================
15365
+ getRelativeTime() {
15366
+ return Date.now() - this.startTime.getTime();
15367
+ }
15368
+ createAlert(category, type, description) {
15369
+ this.alerts.push({
15370
+ begin: this.getRelativeTime(),
15371
+ end: this.getRelativeTime(),
15372
+ // Eventos pontuais começam e terminam no mesmo ms (exceto focus)
15373
+ alert: category,
15374
+ type
15375
+ // description: description // Se sua interface Alert suportar descrição, descomente
15376
+ });
15189
15377
  }
15190
15378
  addAlert({ alert, type }) {
15191
15379
  this.alerts.push({
@@ -21827,7 +22015,8 @@ var Proctoring = class {
21827
22015
  const alertRecorder = new AlertRecorder(
21828
22016
  {
21829
22017
  onFocusCallback: (response) => this.onFocusAlertRecorderCallback(response),
21830
- onLostFocusCallback: (response) => this.onLostFocusAlertRecorderCallback(response)
22018
+ onLostFocusCallback: (response) => this.onLostFocusAlertRecorderCallback(response),
22019
+ onRealtimeAlertCallback: (response) => this.onRealtimeAlertsCallback(response)
21831
22020
  },
21832
22021
  options
21833
22022
  );