smartcomply-web-sdk 1.0.69 → 1.0.70
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 +10 -0
- package/dist/esm/flow/DocumentCapture.d.ts.map +1 -1
- package/dist/esm/flow/DocumentCapture.js +92 -16
- package/dist/esm/flow/DocumentCapture.js.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +7 -5
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/flow/icons.d.ts +1 -1
- package/dist/esm/flow/icons.d.ts.map +1 -1
- package/dist/esm/flow/icons.js +1 -0
- package/dist/esm/flow/icons.js.map +1 -1
- package/dist/flow/DocumentCapture.d.ts +10 -0
- package/dist/flow/DocumentCapture.d.ts.map +1 -1
- package/dist/flow/DocumentCapture.js +92 -16
- package/dist/flow/DocumentCapture.js.map +1 -1
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/flow/SmartComplyFlow.js +7 -5
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/flow/icons.d.ts +1 -1
- package/dist/flow/icons.d.ts.map +1 -1
- package/dist/flow/icons.js +1 -0
- package/dist/flow/icons.js.map +1 -1
- package/dist/smartcomply.browser.js +89 -17
- package/dist/smartcomply.browser.js.map +2 -2
- package/package.json +1 -1
|
@@ -5999,7 +5999,8 @@ var SmartComplySDK = (() => {
|
|
|
5999
5999
|
passport: '<rect x="5" y="2" width="14" height="20" rx="2"/><circle cx="12" cy="9" r="2.5"/><path d="M9 15h6"/>',
|
|
6000
6000
|
car: '<path d="M4 16V11l2-5h12l2 5v5"/><path d="M4 16h16M7 16v2M17 16v2"/><circle cx="7.5" cy="14" r="1"/><circle cx="16.5" cy="14" r="1"/>',
|
|
6001
6001
|
ballot: '<rect x="4" y="8" width="16" height="12" rx="1"/><path d="M9 8V5a3 3 0 016 0v3"/><path d="M12 12v4"/>',
|
|
6002
|
-
refresh: '<path d="M4 4v6h6"/><path d="M20 20v-6h-6"/><path d="M20 10a8 8 0 00-14.6-4.6M4 14a8 8 0 0014.6 4.6"/>'
|
|
6002
|
+
refresh: '<path d="M4 4v6h6"/><path d="M20 20v-6h-6"/><path d="M20 10a8 8 0 00-14.6-4.6M4 14a8 8 0 0014.6 4.6"/>',
|
|
6003
|
+
rotate: '<rect x="7" y="7" width="12" height="12" rx="2"/><path d="M7 4h5a4 4 0 014 4"/><path d="M13 1.5L16 4l-3 2.5"/>'
|
|
6003
6004
|
};
|
|
6004
6005
|
function icon(name, size = 24) {
|
|
6005
6006
|
const d2 = PATHS[name];
|
|
@@ -6250,7 +6251,7 @@ var SmartComplySDK = (() => {
|
|
|
6250
6251
|
display:flex;align-items:center;gap:8px;
|
|
6251
6252
|
font-size:12px;color:${theme.textSecondary};line-height:1.5;
|
|
6252
6253
|
`;
|
|
6253
|
-
notice.innerHTML = backOptional ? `<span style="font-size:16px;flex-shrink:0;">ℹ️</span><span>After the <strong style="color:${theme.text}">front</strong>, you can optionally add the <strong style="color:${theme.text}">back</strong> if your
|
|
6254
|
+
notice.innerHTML = backOptional ? `<span style="font-size:16px;flex-shrink:0;">ℹ️</span><span>After the <strong style="color:${theme.text}">front</strong>, you can optionally add the <strong style="color:${theme.text}">back</strong> if your document has one.</span>` : `<span style="font-size:16px;flex-shrink:0;">ℹ️</span><span>You'll capture both the <strong style="color:${theme.text}">front</strong> and <strong style="color:${theme.text}">back</strong> of this document.</span>`;
|
|
6254
6255
|
this.root.appendChild(notice);
|
|
6255
6256
|
}
|
|
6256
6257
|
if (requiresBack && !isCapturingFront && backOptional) {
|
|
@@ -6261,7 +6262,7 @@ var SmartComplySDK = (() => {
|
|
|
6261
6262
|
border:1.5px dashed ${theme.border};border-radius:10px;cursor:pointer;
|
|
6262
6263
|
font-size:12.5px;font-weight:600;font-family:inherit;
|
|
6263
6264
|
`;
|
|
6264
|
-
skipBtn.textContent = "Skip —
|
|
6265
|
+
skipBtn.textContent = "Skip — my document has no back side";
|
|
6265
6266
|
skipBtn.addEventListener("click", () => {
|
|
6266
6267
|
this.handleCaptureComplete(null, container, theme, documentType, false, true);
|
|
6267
6268
|
});
|
|
@@ -6508,14 +6509,15 @@ var SmartComplySDK = (() => {
|
|
|
6508
6509
|
stepBadge.textContent = "STEP 1 OF 2 · CHECK THE PHOTO";
|
|
6509
6510
|
this.root.appendChild(stepBadge);
|
|
6510
6511
|
const img = document.createElement("img");
|
|
6511
|
-
|
|
6512
|
-
img.src =
|
|
6512
|
+
let currentUrl = URL.createObjectURL(imageData);
|
|
6513
|
+
img.src = currentUrl;
|
|
6513
6514
|
img.style.cssText = `
|
|
6514
6515
|
width:100%;max-height:300px;object-fit:contain;border-radius:${RADIUS.md}px;
|
|
6515
6516
|
border:2px solid ${theme.border};
|
|
6516
6517
|
`;
|
|
6517
6518
|
this.root.appendChild(img);
|
|
6518
|
-
|
|
6519
|
+
let currentBlob = imageData;
|
|
6520
|
+
let isRotating = false;
|
|
6519
6521
|
const label = document.createElement("div");
|
|
6520
6522
|
label.style.cssText = `color:${theme.text};font-size:${TYPE.base}px;font-weight:700;text-align:center;`;
|
|
6521
6523
|
label.textContent = "Does the photo look clear?";
|
|
@@ -6524,6 +6526,35 @@ var SmartComplySDK = (() => {
|
|
|
6524
6526
|
sublabel.style.cssText = `color:${theme.textMuted};font-size:${TYPE.xs}px;text-align:center;line-height:1.6;`;
|
|
6525
6527
|
sublabel.textContent = "All text must be readable and all four corners must be visible.";
|
|
6526
6528
|
this.root.appendChild(sublabel);
|
|
6529
|
+
const rotateBtn = document.createElement("button");
|
|
6530
|
+
rotateBtn.type = "button";
|
|
6531
|
+
rotateBtn.setAttribute("aria-label", "Rotate photo 90 degrees");
|
|
6532
|
+
rotateBtn.style.cssText = `
|
|
6533
|
+
display:flex;align-items:center;justify-content:center;gap:6px;
|
|
6534
|
+
margin:0 auto;padding:8px 16px;background:transparent;
|
|
6535
|
+
color:${theme.textSecondary};border:1.5px solid ${theme.border};
|
|
6536
|
+
border-radius:${RADIUS.pill}px;cursor:pointer;
|
|
6537
|
+
font-size:12.5px;font-weight:600;font-family:inherit;
|
|
6538
|
+
`;
|
|
6539
|
+
rotateBtn.innerHTML = `<span style="display:inline-flex;">${icon("rotate", 15)}</span><span>Rotate</span>`;
|
|
6540
|
+
rotateBtn.addEventListener("click", async () => {
|
|
6541
|
+
if (isRotating) return;
|
|
6542
|
+
isRotating = true;
|
|
6543
|
+
rotateBtn.disabled = true;
|
|
6544
|
+
try {
|
|
6545
|
+
const rotated = await this._rotateImage(currentBlob, 90);
|
|
6546
|
+
currentBlob = rotated;
|
|
6547
|
+
URL.revokeObjectURL(currentUrl);
|
|
6548
|
+
currentUrl = URL.createObjectURL(rotated);
|
|
6549
|
+
img.src = currentUrl;
|
|
6550
|
+
this._checkPhotoQuality(currentBlob, qualityNotice, theme);
|
|
6551
|
+
} catch {
|
|
6552
|
+
} finally {
|
|
6553
|
+
isRotating = false;
|
|
6554
|
+
rotateBtn.disabled = false;
|
|
6555
|
+
}
|
|
6556
|
+
});
|
|
6557
|
+
this.root.appendChild(rotateBtn);
|
|
6527
6558
|
const qualityNotice = document.createElement("div");
|
|
6528
6559
|
qualityNotice.style.cssText = `
|
|
6529
6560
|
display:none;width:100%;padding:10px 14px;border-radius:${RADIUS.md}px;
|
|
@@ -6532,27 +6563,24 @@ var SmartComplySDK = (() => {
|
|
|
6532
6563
|
`;
|
|
6533
6564
|
qualityNotice.setAttribute("role", "status");
|
|
6534
6565
|
this.root.appendChild(qualityNotice);
|
|
6535
|
-
this._checkPhotoQuality(
|
|
6566
|
+
this._checkPhotoQuality(currentBlob, qualityNotice, theme);
|
|
6536
6567
|
const btnRow = document.createElement("div");
|
|
6537
6568
|
btnRow.style.cssText = "display:flex;gap:12px;width:100%;";
|
|
6538
6569
|
const retakeBtn = this.createButton("Retake", theme, false);
|
|
6539
6570
|
retakeBtn.style.flex = "1";
|
|
6540
6571
|
retakeBtn.addEventListener("click", () => {
|
|
6541
|
-
|
|
6542
|
-
URL.revokeObjectURL(img._blobUrl);
|
|
6543
|
-
}
|
|
6572
|
+
URL.revokeObjectURL(currentUrl);
|
|
6544
6573
|
this.root.innerHTML = "";
|
|
6545
6574
|
this.renderChoiceScreen(container, theme, documentType);
|
|
6546
6575
|
});
|
|
6547
6576
|
const useBtn = this.createButton("Use Photo ✓", theme, true);
|
|
6548
6577
|
useBtn.style.flex = "1";
|
|
6549
6578
|
useBtn.addEventListener("click", () => {
|
|
6550
|
-
|
|
6551
|
-
URL.revokeObjectURL(img._blobUrl);
|
|
6552
|
-
}
|
|
6579
|
+
URL.revokeObjectURL(currentUrl);
|
|
6553
6580
|
useBtn.textContent = "Confirming photo…";
|
|
6554
6581
|
useBtn.disabled = true;
|
|
6555
|
-
|
|
6582
|
+
rotateBtn.disabled = true;
|
|
6583
|
+
this.ensureUnderUploadLimit(currentBlob).then((c2) => this.handleCaptureComplete(c2, container, theme, documentType, viaCamera));
|
|
6556
6584
|
});
|
|
6557
6585
|
btnRow.appendChild(retakeBtn);
|
|
6558
6586
|
btnRow.appendChild(useBtn);
|
|
@@ -6606,6 +6634,49 @@ var SmartComplySDK = (() => {
|
|
|
6606
6634
|
imgEl.src = url;
|
|
6607
6635
|
});
|
|
6608
6636
|
}
|
|
6637
|
+
/**
|
|
6638
|
+
* Rotate an image blob by a multiple of 90° and re-encode it — used by the
|
|
6639
|
+
* preview screen's rotate control for a document photographed or uploaded
|
|
6640
|
+
* sideways/upside-down. EXIF auto-rotation (see compressImage) only fixes
|
|
6641
|
+
* phone photos that embed correct orientation metadata; it can't help a
|
|
6642
|
+
* document whose actual pixel content is sideways (e.g. the phone itself
|
|
6643
|
+
* was held rotated relative to the document, or a pre-cropped/scanned
|
|
6644
|
+
* upload with no EXIF at all) — that needs the user to say so directly.
|
|
6645
|
+
*/
|
|
6646
|
+
_rotateImage(source, degrees) {
|
|
6647
|
+
return new Promise((resolve, reject) => {
|
|
6648
|
+
const url = URL.createObjectURL(source);
|
|
6649
|
+
const imgEl = document.createElement("img");
|
|
6650
|
+
imgEl.onload = () => {
|
|
6651
|
+
URL.revokeObjectURL(url);
|
|
6652
|
+
const { naturalWidth: w2, naturalHeight: h2 } = imgEl;
|
|
6653
|
+
const swapped = degrees === 90 || degrees === 270;
|
|
6654
|
+
const canvas = document.createElement("canvas");
|
|
6655
|
+
canvas.width = swapped ? h2 : w2;
|
|
6656
|
+
canvas.height = swapped ? w2 : h2;
|
|
6657
|
+
const ctx = canvas.getContext("2d");
|
|
6658
|
+
if (!ctx) {
|
|
6659
|
+
reject(new Error("Canvas unavailable"));
|
|
6660
|
+
return;
|
|
6661
|
+
}
|
|
6662
|
+
ctx.translate(canvas.width / 2, canvas.height / 2);
|
|
6663
|
+
ctx.rotate(degrees * Math.PI / 180);
|
|
6664
|
+
ctx.drawImage(imgEl, -w2 / 2, -h2 / 2);
|
|
6665
|
+
canvas.toBlob(
|
|
6666
|
+
(blob) => {
|
|
6667
|
+
blob ? resolve(blob) : reject(new Error("Rotation failed"));
|
|
6668
|
+
},
|
|
6669
|
+
"image/jpeg",
|
|
6670
|
+
0.95
|
|
6671
|
+
);
|
|
6672
|
+
};
|
|
6673
|
+
imgEl.onerror = () => {
|
|
6674
|
+
URL.revokeObjectURL(url);
|
|
6675
|
+
reject(new Error("Image load failed"));
|
|
6676
|
+
};
|
|
6677
|
+
imgEl.src = url;
|
|
6678
|
+
});
|
|
6679
|
+
}
|
|
6609
6680
|
handleCaptureComplete(imageData, container, theme, documentType, viaCamera, skipped = false) {
|
|
6610
6681
|
const requiresBack = this.shouldCaptureBackSide(documentType);
|
|
6611
6682
|
const wasCapturingFront = this.currentSide === "front";
|
|
@@ -6880,8 +6951,9 @@ var SmartComplySDK = (() => {
|
|
|
6880
6951
|
if (t2.includes("passport")) return "passport";
|
|
6881
6952
|
if (t2.includes("driver") || t2.includes("licen")) return "driver's license";
|
|
6882
6953
|
if (t2.includes("voter") || t2.includes("pvc")) return "voter's card";
|
|
6883
|
-
if (t2.includes("
|
|
6884
|
-
if (t2.includes("
|
|
6954
|
+
if (t2.includes("nin")) return "NIN document";
|
|
6955
|
+
if (t2.includes("national")) return "national ID card";
|
|
6956
|
+
if (t2.includes("bvn")) return "BVN document";
|
|
6885
6957
|
return "identity document";
|
|
6886
6958
|
}
|
|
6887
6959
|
};
|
|
@@ -6927,7 +6999,7 @@ var SmartComplySDK = (() => {
|
|
|
6927
6999
|
if (u2.includes("NIN") || u2.includes("NATIONAL IDENTITY NUMBER")) {
|
|
6928
7000
|
return {
|
|
6929
7001
|
icon: "🆔",
|
|
6930
|
-
desc: isDocumentFlow ? "Capture or upload your NIN card
|
|
7002
|
+
desc: isDocumentFlow ? "Capture or upload your NIN (slip or card) — add the back if your document has one" : "Enter your 11-digit National Identification Number (NIN)",
|
|
6931
7003
|
requiresBack: isDocumentFlow ? "optional" : false
|
|
6932
7004
|
};
|
|
6933
7005
|
}
|