smartcomply-web-sdk 1.0.63 → 1.0.65
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/dist/esm/flow/DocumentCapture.d.ts +12 -8
- package/dist/esm/flow/DocumentCapture.d.ts.map +1 -1
- package/dist/esm/flow/DocumentCapture.js +49 -117
- package/dist/esm/flow/DocumentCapture.js.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.d.ts +1 -0
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +14 -3
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/esm/modules/liveness/liveness.js +13 -9
- package/dist/esm/modules/liveness/liveness.js.map +1 -1
- package/dist/flow/DocumentCapture.d.ts +12 -8
- package/dist/flow/DocumentCapture.d.ts.map +1 -1
- package/dist/flow/DocumentCapture.js +48 -116
- package/dist/flow/DocumentCapture.js.map +1 -1
- package/dist/flow/SmartComplyFlow.d.ts +1 -0
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/flow/SmartComplyFlow.js +14 -3
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/modules/liveness/liveness.js +13 -9
- package/dist/modules/liveness/liveness.js.map +1 -1
- package/dist/smartcomply.browser.js +53 -219
- package/dist/smartcomply.browser.js.map +4 -4
- package/package.json +1 -1
|
@@ -5372,7 +5372,7 @@ var SmartComplySDK = (() => {
|
|
|
5372
5372
|
tempVideo.muted = true;
|
|
5373
5373
|
tempVideo.playsInline = true;
|
|
5374
5374
|
await tempVideo.play();
|
|
5375
|
-
ui2.mount(container, { time_limit_seconds: 0, instruction: "
|
|
5375
|
+
ui2.mount(container, { time_limit_seconds: 0, instruction: "Fit your face inside the oval outline" });
|
|
5376
5376
|
const uiVideo = ui2.getVideoElement();
|
|
5377
5377
|
if (uiVideo) {
|
|
5378
5378
|
uiVideo.srcObject = stream;
|
|
@@ -5382,7 +5382,7 @@ var SmartComplySDK = (() => {
|
|
|
5382
5382
|
container.appendChild(tempVideo);
|
|
5383
5383
|
}
|
|
5384
5384
|
await faceEngine.init();
|
|
5385
|
-
ui2.updateInstruction("
|
|
5385
|
+
ui2.updateInstruction("Fit your face inside the oval outline");
|
|
5386
5386
|
const videoForDetection = uiVideo || tempVideo;
|
|
5387
5387
|
await new Promise((resolve) => {
|
|
5388
5388
|
let stableFrames = 0;
|
|
@@ -5402,7 +5402,7 @@ var SmartComplySDK = (() => {
|
|
|
5402
5402
|
if (detection.faceDetected) {
|
|
5403
5403
|
stableFrames++;
|
|
5404
5404
|
missedFrames = 0;
|
|
5405
|
-
ui2.updateInstruction("
|
|
5405
|
+
ui2.updateInstruction("Good — hold that position");
|
|
5406
5406
|
if (stableFrames >= STABLE_REQUIRED) {
|
|
5407
5407
|
resolve();
|
|
5408
5408
|
return;
|
|
@@ -5411,7 +5411,7 @@ var SmartComplySDK = (() => {
|
|
|
5411
5411
|
missedFrames++;
|
|
5412
5412
|
if (missedFrames > MISS_TOLERANCE) {
|
|
5413
5413
|
stableFrames = 0;
|
|
5414
|
-
ui2.updateInstruction("Move your face
|
|
5414
|
+
ui2.updateInstruction("Move closer so your whole face fits in the oval");
|
|
5415
5415
|
}
|
|
5416
5416
|
}
|
|
5417
5417
|
}
|
|
@@ -5419,7 +5419,7 @@ var SmartComplySDK = (() => {
|
|
|
5419
5419
|
};
|
|
5420
5420
|
requestAnimationFrame(checkFace);
|
|
5421
5421
|
});
|
|
5422
|
-
ui2.updateInstruction("
|
|
5422
|
+
ui2.updateInstruction("Getting ready…");
|
|
5423
5423
|
await new Promise((r2) => setTimeout(r2, 500));
|
|
5424
5424
|
const challengeActions = actions || [...DEFAULT_CHALLENGE_ACTIONS];
|
|
5425
5425
|
let entry;
|
|
@@ -5448,10 +5448,10 @@ var SmartComplySDK = (() => {
|
|
|
5448
5448
|
tempVideo.remove();
|
|
5449
5449
|
const uiChallenge = {
|
|
5450
5450
|
time_limit_seconds: LIVENESS_TIME_LIMIT_SECONDS,
|
|
5451
|
-
instruction: "Blink and look
|
|
5451
|
+
instruction: "Blink naturally and look slowly left, right, up or down"
|
|
5452
5452
|
};
|
|
5453
5453
|
ui2.unmount();
|
|
5454
|
-
const videoEl = ui2.mount(container, uiChallenge);
|
|
5454
|
+
const videoEl = ui2.mount(container, { ...uiChallenge, instruction: "Starting scan…" });
|
|
5455
5455
|
videoEl.srcObject = stream;
|
|
5456
5456
|
await videoEl.play();
|
|
5457
5457
|
await new Promise((r2) => setTimeout(r2, 1e3));
|
|
@@ -5476,7 +5476,7 @@ var SmartComplySDK = (() => {
|
|
|
5476
5476
|
ui2.updateScanProgress(scan.livenessConfirmed);
|
|
5477
5477
|
ui2.setFaceTracking(detection.faceDetected);
|
|
5478
5478
|
if (!detection.faceDetected) {
|
|
5479
|
-
ui2.updateInstruction("Move your face
|
|
5479
|
+
ui2.updateInstruction("Move closer so your whole face fits in the oval");
|
|
5480
5480
|
} else {
|
|
5481
5481
|
ui2.updateInstruction(uiChallenge.instruction);
|
|
5482
5482
|
}
|
|
@@ -5845,126 +5845,6 @@ var SmartComplySDK = (() => {
|
|
|
5845
5845
|
return `#${(r2 << 16 | g2 << 8 | b2).toString(16).padStart(6, "0")}`;
|
|
5846
5846
|
}
|
|
5847
5847
|
|
|
5848
|
-
// src/utils/ImageQuality.ts
|
|
5849
|
-
function calculateSharpness(imageData) {
|
|
5850
|
-
const { width, height, data } = imageData;
|
|
5851
|
-
const gray = new Uint8Array(width * height);
|
|
5852
|
-
for (let i2 = 0; i2 < width * height; i2++) {
|
|
5853
|
-
const idx = i2 * 4;
|
|
5854
|
-
gray[i2] = Math.round(0.299 * data[idx] + 0.587 * data[idx + 1] + 0.114 * data[idx + 2]);
|
|
5855
|
-
}
|
|
5856
|
-
const laplacian = new Float32Array(width * height);
|
|
5857
|
-
let sum = 0;
|
|
5858
|
-
let sumSq = 0;
|
|
5859
|
-
let count = 0;
|
|
5860
|
-
for (let y2 = 1; y2 < height - 1; y2++) {
|
|
5861
|
-
for (let x2 = 1; x2 < width - 1; x2++) {
|
|
5862
|
-
const idx = y2 * width + x2;
|
|
5863
|
-
const val = -1 * gray[idx - width] + -1 * gray[idx - 1] + 4 * gray[idx] + -1 * gray[idx + 1] + -1 * gray[idx + width];
|
|
5864
|
-
laplacian[idx] = val;
|
|
5865
|
-
sum += val;
|
|
5866
|
-
sumSq += val * val;
|
|
5867
|
-
count++;
|
|
5868
|
-
}
|
|
5869
|
-
}
|
|
5870
|
-
const mean = sum / count;
|
|
5871
|
-
const variance = sumSq / count - mean * mean;
|
|
5872
|
-
return Math.abs(variance);
|
|
5873
|
-
}
|
|
5874
|
-
function detectGlare(imageData, threshold = 250) {
|
|
5875
|
-
const { data } = imageData;
|
|
5876
|
-
let overexposed = 0;
|
|
5877
|
-
for (let i2 = 0; i2 < data.length; i2 += 4) {
|
|
5878
|
-
const brightness = (data[i2] + data[i2 + 1] + data[i2 + 2]) / 3;
|
|
5879
|
-
if (brightness > threshold) {
|
|
5880
|
-
overexposed++;
|
|
5881
|
-
}
|
|
5882
|
-
}
|
|
5883
|
-
return overexposed / (data.length / 4);
|
|
5884
|
-
}
|
|
5885
|
-
function calculateContrast(imageData) {
|
|
5886
|
-
const { data } = imageData;
|
|
5887
|
-
let sum = 0;
|
|
5888
|
-
let sumSq = 0;
|
|
5889
|
-
const pixelCount = data.length / 4;
|
|
5890
|
-
for (let i2 = 0; i2 < data.length; i2 += 4) {
|
|
5891
|
-
const gray = 0.299 * data[i2] + 0.587 * data[i2 + 1] + 0.114 * data[i2 + 2];
|
|
5892
|
-
sum += gray;
|
|
5893
|
-
sumSq += gray * gray;
|
|
5894
|
-
}
|
|
5895
|
-
const mean = sum / pixelCount;
|
|
5896
|
-
const variance = sumSq / pixelCount - mean * mean;
|
|
5897
|
-
return Math.sqrt(variance);
|
|
5898
|
-
}
|
|
5899
|
-
function calculateBrightness(imageData) {
|
|
5900
|
-
const { data } = imageData;
|
|
5901
|
-
let sum = 0;
|
|
5902
|
-
for (let i2 = 0; i2 < data.length; i2 += 4) {
|
|
5903
|
-
sum += (data[i2] + data[i2 + 1] + data[i2 + 2]) / 3;
|
|
5904
|
-
}
|
|
5905
|
-
return sum / (data.length / 4);
|
|
5906
|
-
}
|
|
5907
|
-
function assessQuality(imageData) {
|
|
5908
|
-
const sharpness = calculateSharpness(imageData);
|
|
5909
|
-
const glare = detectGlare(imageData);
|
|
5910
|
-
const contrast = calculateContrast(imageData);
|
|
5911
|
-
const brightness = calculateBrightness(imageData);
|
|
5912
|
-
const isBlurry = sharpness < 80;
|
|
5913
|
-
const hasGlare = glare > 0.15;
|
|
5914
|
-
const isTooDark = brightness < 50;
|
|
5915
|
-
const isTooBright = brightness > 200;
|
|
5916
|
-
const lowContrast = contrast < 30;
|
|
5917
|
-
let score = 100;
|
|
5918
|
-
if (isBlurry) score -= 40;
|
|
5919
|
-
if (hasGlare) score -= 25;
|
|
5920
|
-
if (isTooDark) score -= 20;
|
|
5921
|
-
if (isTooBright) score -= 20;
|
|
5922
|
-
if (lowContrast) score -= 15;
|
|
5923
|
-
const metrics = {
|
|
5924
|
-
sharpness: Math.round(sharpness * 10) / 10,
|
|
5925
|
-
contrast: Math.round(contrast * 10) / 10,
|
|
5926
|
-
brightness: Math.round(brightness * 10) / 10,
|
|
5927
|
-
isBlurry,
|
|
5928
|
-
hasGlare,
|
|
5929
|
-
overallScore: Math.max(0, score)
|
|
5930
|
-
};
|
|
5931
|
-
const issues = [];
|
|
5932
|
-
const recommendations = [];
|
|
5933
|
-
if (isBlurry) {
|
|
5934
|
-
issues.push("Too blurry");
|
|
5935
|
-
recommendations.push("Hold camera steady");
|
|
5936
|
-
recommendations.push("Ensure good lighting");
|
|
5937
|
-
}
|
|
5938
|
-
if (hasGlare) {
|
|
5939
|
-
issues.push("Too much glare");
|
|
5940
|
-
recommendations.push("Avoid direct light on document");
|
|
5941
|
-
recommendations.push("Tilt document slightly");
|
|
5942
|
-
}
|
|
5943
|
-
if (isTooDark) {
|
|
5944
|
-
issues.push("Too dark");
|
|
5945
|
-
recommendations.push("Move to brighter area");
|
|
5946
|
-
recommendations.push("Turn on more lights");
|
|
5947
|
-
}
|
|
5948
|
-
if (isTooBright) {
|
|
5949
|
-
issues.push("Too bright");
|
|
5950
|
-
recommendations.push("Reduce lighting");
|
|
5951
|
-
recommendations.push("Move away from direct sun");
|
|
5952
|
-
}
|
|
5953
|
-
if (lowContrast) {
|
|
5954
|
-
issues.push("Low contrast");
|
|
5955
|
-
recommendations.push("Ensure even lighting");
|
|
5956
|
-
}
|
|
5957
|
-
const isValid = score >= 60;
|
|
5958
|
-
const message = issues.length > 0 ? issues.join(", ") : "Good quality";
|
|
5959
|
-
return {
|
|
5960
|
-
isValid,
|
|
5961
|
-
message,
|
|
5962
|
-
recommendations: recommendations.slice(0, 3),
|
|
5963
|
-
// Max 3 recommendations
|
|
5964
|
-
quality: metrics
|
|
5965
|
-
};
|
|
5966
|
-
}
|
|
5967
|
-
|
|
5968
5848
|
// src/utils/documentRules.ts
|
|
5969
5849
|
function requiresBackSide(documentType) {
|
|
5970
5850
|
const type = documentType.toLowerCase();
|
|
@@ -6057,6 +5937,7 @@ var SmartComplySDK = (() => {
|
|
|
6057
5937
|
this.orientationHandler = null;
|
|
6058
5938
|
this.resolveCapture = null;
|
|
6059
5939
|
this.rejectCapture = null;
|
|
5940
|
+
this.frontViaCamera = false;
|
|
6060
5941
|
this.frontBlob = null;
|
|
6061
5942
|
this.backBlob = null;
|
|
6062
5943
|
this.currentSide = "front";
|
|
@@ -6088,6 +5969,7 @@ var SmartComplySDK = (() => {
|
|
|
6088
5969
|
this.rejectCapture = reject;
|
|
6089
5970
|
this.frontBlob = null;
|
|
6090
5971
|
this.backBlob = null;
|
|
5972
|
+
this.frontViaCamera = false;
|
|
6091
5973
|
this.currentSide = "front";
|
|
6092
5974
|
this.requiresBackOverride = requiresBack;
|
|
6093
5975
|
this.renderChoiceScreen(container, theme, documentType);
|
|
@@ -6248,8 +6130,7 @@ var SmartComplySDK = (() => {
|
|
|
6248
6130
|
this.showError(err);
|
|
6249
6131
|
return;
|
|
6250
6132
|
}
|
|
6251
|
-
this.
|
|
6252
|
-
this.compressImage(file, 2e3, 1500, 0.92).then((c2) => this.handleFileUploadComplete(c2, container, theme, documentType)).catch(() => this.handleFileUploadComplete(file, container, theme, documentType));
|
|
6133
|
+
this.ensureUnderUploadLimit(file).then((c2) => this.handleFileUploadComplete(c2, container, theme, documentType));
|
|
6253
6134
|
}
|
|
6254
6135
|
});
|
|
6255
6136
|
this.root.appendChild(fileInput);
|
|
@@ -6450,54 +6331,17 @@ var SmartComplySDK = (() => {
|
|
|
6450
6331
|
if (!ctx) return;
|
|
6451
6332
|
ctx.drawImage(this.videoEl, 0, 0, canvas.width, canvas.height);
|
|
6452
6333
|
canvas.toBlob(
|
|
6453
|
-
|
|
6334
|
+
(blob) => {
|
|
6454
6335
|
if (blob) {
|
|
6455
6336
|
this.stopCamera();
|
|
6456
|
-
this.
|
|
6457
|
-
const quality = await this._assessImageQuality(canvas, ctx);
|
|
6458
|
-
this.showPreview(container, theme, blob, documentType, quality);
|
|
6337
|
+
this.showPreview(container, theme, blob, documentType, true);
|
|
6459
6338
|
}
|
|
6460
6339
|
},
|
|
6461
6340
|
"image/jpeg",
|
|
6462
6341
|
0.92
|
|
6463
6342
|
);
|
|
6464
6343
|
}
|
|
6465
|
-
|
|
6466
|
-
* Runs the local sharpness/glare/contrast/brightness check against a
|
|
6467
|
-
* captured frame. Returns null (never blocks) if quality can't be computed
|
|
6468
|
-
* — e.g. a tainted canvas from a cross-origin file, or an unsupported browser.
|
|
6469
|
-
*/
|
|
6470
|
-
async _assessImageQuality(canvas, ctx) {
|
|
6471
|
-
try {
|
|
6472
|
-
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
6473
|
-
return assessQuality(imageData);
|
|
6474
|
-
} catch {
|
|
6475
|
-
return null;
|
|
6476
|
-
}
|
|
6477
|
-
}
|
|
6478
|
-
/** Same as _assessImageQuality but starting from a Blob/File (upload path). */
|
|
6479
|
-
async _assessBlobQuality(blob) {
|
|
6480
|
-
try {
|
|
6481
|
-
const url = URL.createObjectURL(blob);
|
|
6482
|
-
const imgEl = document.createElement("img");
|
|
6483
|
-
await new Promise((resolve, reject) => {
|
|
6484
|
-
imgEl.onload = () => resolve();
|
|
6485
|
-
imgEl.onerror = () => reject(new Error("Image load failed"));
|
|
6486
|
-
imgEl.src = url;
|
|
6487
|
-
});
|
|
6488
|
-
const canvas = document.createElement("canvas");
|
|
6489
|
-
canvas.width = imgEl.naturalWidth;
|
|
6490
|
-
canvas.height = imgEl.naturalHeight;
|
|
6491
|
-
const ctx = canvas.getContext("2d");
|
|
6492
|
-
URL.revokeObjectURL(url);
|
|
6493
|
-
if (!ctx) return null;
|
|
6494
|
-
ctx.drawImage(imgEl, 0, 0);
|
|
6495
|
-
return this._assessImageQuality(canvas, ctx);
|
|
6496
|
-
} catch {
|
|
6497
|
-
return null;
|
|
6498
|
-
}
|
|
6499
|
-
}
|
|
6500
|
-
showPreview(container, theme, imageData, documentType, quality) {
|
|
6344
|
+
showPreview(container, theme, imageData, documentType, viaCamera) {
|
|
6501
6345
|
if (this.root) this.root.innerHTML = "";
|
|
6502
6346
|
else {
|
|
6503
6347
|
this.root = document.createElement("div");
|
|
@@ -6530,29 +6374,9 @@ var SmartComplySDK = (() => {
|
|
|
6530
6374
|
sublabel.style.cssText = `color:${theme.textMuted};font-size:${TYPE.xs}px;text-align:center;line-height:1.6;`;
|
|
6531
6375
|
sublabel.textContent = "All text must be readable and all four corners must be visible.";
|
|
6532
6376
|
this.root.appendChild(sublabel);
|
|
6533
|
-
const qualityIsPoor = !!quality && !quality.isValid;
|
|
6534
|
-
if (qualityIsPoor && quality) {
|
|
6535
|
-
const warn = document.createElement("div");
|
|
6536
|
-
warn.style.cssText = `
|
|
6537
|
-
width:100%;box-sizing:border-box;padding:10px 14px;border-radius:10px;
|
|
6538
|
-
background:#eab30815;border:1px solid #eab30840;
|
|
6539
|
-
color:${theme.text};font-size:12px;line-height:1.5;
|
|
6540
|
-
`;
|
|
6541
|
-
const issueLine = document.createElement("div");
|
|
6542
|
-
issueLine.style.cssText = "font-weight:700;color:#b8860b;";
|
|
6543
|
-
issueLine.textContent = `⚠️ ${quality.message}`;
|
|
6544
|
-
warn.appendChild(issueLine);
|
|
6545
|
-
if (quality.recommendations.length > 0) {
|
|
6546
|
-
const rec = document.createElement("div");
|
|
6547
|
-
rec.style.cssText = "margin-top:4px;color:" + theme.textSecondary + ";";
|
|
6548
|
-
rec.textContent = quality.recommendations.join(" · ");
|
|
6549
|
-
warn.appendChild(rec);
|
|
6550
|
-
}
|
|
6551
|
-
this.root.appendChild(warn);
|
|
6552
|
-
}
|
|
6553
6377
|
const btnRow = document.createElement("div");
|
|
6554
6378
|
btnRow.style.cssText = "display:flex;gap:12px;width:100%;";
|
|
6555
|
-
const retakeBtn = this.createButton("Retake", theme,
|
|
6379
|
+
const retakeBtn = this.createButton("Retake", theme, false);
|
|
6556
6380
|
retakeBtn.style.flex = "1";
|
|
6557
6381
|
retakeBtn.addEventListener("click", () => {
|
|
6558
6382
|
if (img._blobUrl) {
|
|
@@ -6561,38 +6385,26 @@ var SmartComplySDK = (() => {
|
|
|
6561
6385
|
this.root.innerHTML = "";
|
|
6562
6386
|
this.renderChoiceScreen(container, theme, documentType);
|
|
6563
6387
|
});
|
|
6564
|
-
const
|
|
6388
|
+
const useBtn = this.createButton("Use Photo ✓", theme, true);
|
|
6389
|
+
useBtn.style.flex = "1";
|
|
6390
|
+
useBtn.addEventListener("click", () => {
|
|
6565
6391
|
if (img._blobUrl) {
|
|
6566
6392
|
URL.revokeObjectURL(img._blobUrl);
|
|
6567
6393
|
}
|
|
6568
6394
|
useBtn.textContent = "Processing...";
|
|
6569
6395
|
useBtn.disabled = true;
|
|
6570
|
-
this.
|
|
6571
|
-
};
|
|
6572
|
-
const useBtn = this.createButton(qualityIsPoor ? "Use Anyway ⚠" : "Use Photo ✓", theme, !qualityIsPoor);
|
|
6573
|
-
useBtn.style.flex = "1";
|
|
6574
|
-
if (qualityIsPoor) {
|
|
6575
|
-
let confirming = false;
|
|
6576
|
-
useBtn.addEventListener("click", () => {
|
|
6577
|
-
if (!confirming) {
|
|
6578
|
-
confirming = true;
|
|
6579
|
-
useBtn.textContent = "Tap again to confirm ⚠";
|
|
6580
|
-
return;
|
|
6581
|
-
}
|
|
6582
|
-
proceedWithPhoto();
|
|
6583
|
-
});
|
|
6584
|
-
} else {
|
|
6585
|
-
useBtn.addEventListener("click", proceedWithPhoto);
|
|
6586
|
-
}
|
|
6396
|
+
this.ensureUnderUploadLimit(imageData).then((c2) => this.handleCaptureComplete(c2, container, theme, documentType, viaCamera));
|
|
6397
|
+
});
|
|
6587
6398
|
btnRow.appendChild(retakeBtn);
|
|
6588
6399
|
btnRow.appendChild(useBtn);
|
|
6589
6400
|
this.root.appendChild(btnRow);
|
|
6590
6401
|
}
|
|
6591
|
-
handleCaptureComplete(imageData, container, theme, documentType) {
|
|
6402
|
+
handleCaptureComplete(imageData, container, theme, documentType, viaCamera) {
|
|
6592
6403
|
const requiresBack = this.shouldCaptureBackSide(documentType);
|
|
6593
6404
|
const wasCapturingFront = this.currentSide === "front";
|
|
6594
6405
|
if (wasCapturingFront) {
|
|
6595
6406
|
this.frontBlob = imageData;
|
|
6407
|
+
this.frontViaCamera = viaCamera;
|
|
6596
6408
|
} else {
|
|
6597
6409
|
this.backBlob = imageData;
|
|
6598
6410
|
}
|
|
@@ -6608,28 +6420,44 @@ var SmartComplySDK = (() => {
|
|
|
6608
6420
|
}
|
|
6609
6421
|
const result = {
|
|
6610
6422
|
front: this.frontBlob,
|
|
6611
|
-
back: this.backBlob || void 0
|
|
6423
|
+
back: this.backBlob || void 0,
|
|
6424
|
+
viaCamera: this.frontViaCamera
|
|
6612
6425
|
};
|
|
6613
6426
|
this.resolveCapture?.(result);
|
|
6614
6427
|
}
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
this.showPreview(container, theme, imageData, documentType, quality);
|
|
6428
|
+
handleFileUploadComplete(imageData, container, theme, documentType) {
|
|
6429
|
+
this.showPreview(container, theme, imageData, documentType, false);
|
|
6618
6430
|
}
|
|
6619
6431
|
validateFile(file) {
|
|
6620
6432
|
const allowedTypes = ["image/jpeg", "image/jpg", "image/png"];
|
|
6621
6433
|
if (!allowedTypes.includes(file.type)) {
|
|
6622
6434
|
return "Invalid file type. Please upload a JPEG or PNG image.";
|
|
6623
6435
|
}
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
return "File is too large. Please upload an image smaller than 10MB.";
|
|
6436
|
+
if (file.size > MAX_DOCUMENT_BYTES) {
|
|
6437
|
+
return `File is too large. Please upload an image smaller than ${Math.round(MAX_DOCUMENT_BYTES / (1024 * 1024))}MB.`;
|
|
6627
6438
|
}
|
|
6628
6439
|
if (file.size < 1024) {
|
|
6629
6440
|
return "File is too small. Please upload a valid image file.";
|
|
6630
6441
|
}
|
|
6631
6442
|
return null;
|
|
6632
6443
|
}
|
|
6444
|
+
/**
|
|
6445
|
+
* Only shrinks/re-encodes when the file exceeds the backend's real upload
|
|
6446
|
+
* ceiling — otherwise returns the original bytes completely untouched.
|
|
6447
|
+
* Document photos become the face-comparison reference image later
|
|
6448
|
+
* (see backend _crop_and_save_face), so any avoidable re-encode is a
|
|
6449
|
+
* quality loss with no upside; compression here exists solely to keep
|
|
6450
|
+
* oversized captures under MAX_DOCUMENT_BYTES, not to "optimize" images
|
|
6451
|
+
* that already fit.
|
|
6452
|
+
*/
|
|
6453
|
+
async ensureUnderUploadLimit(source) {
|
|
6454
|
+
if (source.size <= MAX_DOCUMENT_BYTES) return source;
|
|
6455
|
+
try {
|
|
6456
|
+
return await this.compressImage(source, DOC_CAMERA_IDEAL_WIDTH, DOC_CAMERA_IDEAL_HEIGHT, 0.92);
|
|
6457
|
+
} catch {
|
|
6458
|
+
return source;
|
|
6459
|
+
}
|
|
6460
|
+
}
|
|
6633
6461
|
handleFileUpload(container, theme) {
|
|
6634
6462
|
const input = this.root?.querySelector("#sc-doc-file-input");
|
|
6635
6463
|
if (input) input.click();
|
|
@@ -7038,6 +6866,11 @@ var SmartComplySDK = (() => {
|
|
|
7038
6866
|
this.idNumber = "";
|
|
7039
6867
|
this.documentBlob = null;
|
|
7040
6868
|
this.documentBackBlob = null;
|
|
6869
|
+
// Whether the document's front image came from the live camera vs. a
|
|
6870
|
+
// gallery/file upload — gates whether pre-warming the liveness camera on
|
|
6871
|
+
// the confirm screens makes sense (reusing an already-open camera) or
|
|
6872
|
+
// would surprise a user who hasn't touched their camera yet this session.
|
|
6873
|
+
this._documentViaCamera = false;
|
|
7041
6874
|
this.identityCheckId = null;
|
|
7042
6875
|
this.verificationResult = null;
|
|
7043
6876
|
this.livenessEntryId = null;
|
|
@@ -7939,7 +7772,7 @@ var SmartComplySDK = (() => {
|
|
|
7939
7772
|
this._renderRetryLimitReached(container);
|
|
7940
7773
|
return;
|
|
7941
7774
|
}
|
|
7942
|
-
this._prewarmCamera();
|
|
7775
|
+
if (this._documentViaCamera) this._prewarmCamera();
|
|
7943
7776
|
const stepBadge = document.createElement("div");
|
|
7944
7777
|
stepBadge.style.cssText = `
|
|
7945
7778
|
display:inline-flex;align-items:center;gap:5px;
|
|
@@ -8231,6 +8064,7 @@ var SmartComplySDK = (() => {
|
|
|
8231
8064
|
async processDocumentCaptureResult(result, captureContainer, container) {
|
|
8232
8065
|
this.documentBlob = await _compressImage(result.front, 1600, 0.85);
|
|
8233
8066
|
this.documentBackBlob = result.back ? await _compressImage(result.back, 1600, 0.85) : null;
|
|
8067
|
+
this._documentViaCamera = result.viaCamera;
|
|
8234
8068
|
await this.processDocumentBlob(this.documentBlob, captureContainer, container);
|
|
8235
8069
|
}
|
|
8236
8070
|
/**
|
|
@@ -8380,7 +8214,7 @@ var SmartComplySDK = (() => {
|
|
|
8380
8214
|
container.appendChild(title);
|
|
8381
8215
|
const desc = document.createElement("div");
|
|
8382
8216
|
desc.style.cssText = `color:${this.theme.textSecondary};font-size:13px;margin-top:-8px;line-height:1.5;`;
|
|
8383
|
-
desc.textContent = "
|
|
8217
|
+
desc.textContent = "Fit your face inside the oval, then blink naturally while looking around for a few seconds.";
|
|
8384
8218
|
container.appendChild(desc);
|
|
8385
8219
|
const livenessContainer = document.createElement("div");
|
|
8386
8220
|
livenessContainer.style.cssText = "margin:8px 0;width:100%;";
|