smartcomply-web-sdk 1.0.51 → 1.0.52
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/camera/ActionDetector.d.ts +0 -5
- package/dist/camera/ActionDetector.d.ts.map +1 -1
- package/dist/camera/ActionDetector.js +1 -12
- package/dist/camera/ActionDetector.js.map +1 -1
- package/dist/esm/camera/ActionDetector.d.ts +0 -5
- package/dist/esm/camera/ActionDetector.d.ts.map +1 -1
- package/dist/esm/camera/ActionDetector.js +1 -12
- package/dist/esm/camera/ActionDetector.js.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +144 -144
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/modules/liveness/LivenessUI.d.ts +0 -2
- package/dist/esm/modules/liveness/LivenessUI.d.ts.map +1 -1
- package/dist/esm/modules/liveness/LivenessUI.js.map +1 -1
- package/dist/esm/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/esm/modules/liveness/liveness.js +1 -3
- package/dist/esm/modules/liveness/liveness.js.map +1 -1
- package/dist/esm/utils/constants.d.ts +0 -2
- package/dist/esm/utils/constants.d.ts.map +1 -1
- package/dist/esm/utils/constants.js +0 -2
- package/dist/esm/utils/constants.js.map +1 -1
- package/dist/flow/SmartComplyFlow.js +144 -144
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/modules/liveness/LivenessUI.d.ts +0 -2
- package/dist/modules/liveness/LivenessUI.d.ts.map +1 -1
- package/dist/modules/liveness/LivenessUI.js.map +1 -1
- package/dist/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/modules/liveness/liveness.js +1 -3
- package/dist/modules/liveness/liveness.js.map +1 -1
- package/dist/smartcomply.browser.js +86 -100
- package/dist/smartcomply.browser.js.map +2 -2
- package/dist/utils/constants.d.ts +0 -2
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +1 -3
- package/dist/utils/constants.js.map +1 -1
- package/package.json +1 -1
|
@@ -4460,8 +4460,7 @@ var SmartComplySDK = (() => {
|
|
|
4460
4460
|
var BLINK_HOLD_FRAMES = 2;
|
|
4461
4461
|
var YAW_MOTION_THRESHOLD_DEG = 6;
|
|
4462
4462
|
var ActionDetector = class {
|
|
4463
|
-
|
|
4464
|
-
constructor(_requiredActions) {
|
|
4463
|
+
constructor() {
|
|
4465
4464
|
this.blinkHoldCount = 0;
|
|
4466
4465
|
this.blinkDetected = false;
|
|
4467
4466
|
this.motionDetected = false;
|
|
@@ -4498,24 +4497,13 @@ var SmartComplySDK = (() => {
|
|
|
4498
4497
|
faceDetected,
|
|
4499
4498
|
blinkDetected: this.blinkDetected,
|
|
4500
4499
|
motionDetected: this.motionDetected,
|
|
4501
|
-
livenessConfirmed: this.blinkDetected || this.motionDetected
|
|
4502
|
-
progress: 0
|
|
4503
|
-
// caller fills in time-based progress
|
|
4500
|
+
livenessConfirmed: this.blinkDetected || this.motionDetected
|
|
4504
4501
|
};
|
|
4505
4502
|
}
|
|
4506
4503
|
getHeadYaw(matrix) {
|
|
4507
4504
|
if (!matrix || matrix.length < 16) return 0;
|
|
4508
4505
|
return Math.atan2(-matrix[2], matrix[0]) * (180 / Math.PI);
|
|
4509
4506
|
}
|
|
4510
|
-
livenessConfirmed() {
|
|
4511
|
-
return this.blinkDetected || this.motionDetected;
|
|
4512
|
-
}
|
|
4513
|
-
reset() {
|
|
4514
|
-
this.blinkHoldCount = 0;
|
|
4515
|
-
this.blinkDetected = false;
|
|
4516
|
-
this.motionDetected = false;
|
|
4517
|
-
this.baselineYaw = null;
|
|
4518
|
-
}
|
|
4519
4507
|
};
|
|
4520
4508
|
|
|
4521
4509
|
// src/modules/liveness/LivenessUI.ts
|
|
@@ -4970,7 +4958,7 @@ var SmartComplySDK = (() => {
|
|
|
4970
4958
|
tempVideo.muted = true;
|
|
4971
4959
|
tempVideo.playsInline = true;
|
|
4972
4960
|
await tempVideo.play();
|
|
4973
|
-
ui2.mount(container, {
|
|
4961
|
+
ui2.mount(container, { time_limit_seconds: 0, instruction: "Centre your face in the oval" });
|
|
4974
4962
|
const uiVideo = ui2.getVideoElement();
|
|
4975
4963
|
if (uiVideo) {
|
|
4976
4964
|
uiVideo.srcObject = stream;
|
|
@@ -4983,7 +4971,6 @@ var SmartComplySDK = (() => {
|
|
|
4983
4971
|
ui2.updateInstruction("Centre your face in the oval");
|
|
4984
4972
|
const videoForDetection = uiVideo || tempVideo;
|
|
4985
4973
|
await new Promise((resolve) => {
|
|
4986
|
-
let attempts = 0;
|
|
4987
4974
|
let stableFrames = 0;
|
|
4988
4975
|
let missedFrames = 0;
|
|
4989
4976
|
const STABLE_REQUIRED = 4;
|
|
@@ -5045,7 +5032,6 @@ var SmartComplySDK = (() => {
|
|
|
5045
5032
|
tempVideo.srcObject = null;
|
|
5046
5033
|
tempVideo.remove();
|
|
5047
5034
|
const uiChallenge = {
|
|
5048
|
-
actions: entry.challenge_actions,
|
|
5049
5035
|
time_limit_seconds: LIVENESS_TIME_LIMIT_SECONDS,
|
|
5050
5036
|
instruction: "Blink and look around naturally"
|
|
5051
5037
|
};
|
|
@@ -6347,13 +6333,13 @@ var SmartComplySDK = (() => {
|
|
|
6347
6333
|
|
|
6348
6334
|
// src/flow/SmartComplyFlow.ts
|
|
6349
6335
|
var COUNTRY_LABELS = {
|
|
6350
|
-
nigeria: "
|
|
6351
|
-
global: "
|
|
6352
|
-
ghana: "
|
|
6353
|
-
kenya: "
|
|
6354
|
-
south_africa: "
|
|
6355
|
-
united_states: "
|
|
6356
|
-
united_kingdom: "
|
|
6336
|
+
nigeria: "🇳🇬 Nigeria",
|
|
6337
|
+
global: "🌍 Other Countries",
|
|
6338
|
+
ghana: "🇬🇭 Ghana",
|
|
6339
|
+
kenya: "🇰🇪 Kenya",
|
|
6340
|
+
south_africa: "🇿🇦 South Africa",
|
|
6341
|
+
united_states: "🇺🇸 United States",
|
|
6342
|
+
united_kingdom: "🇬🇧 United Kingdom"
|
|
6357
6343
|
};
|
|
6358
6344
|
function isUploadFieldType(type) {
|
|
6359
6345
|
return type === "upload" || type === "image" || type === "file";
|
|
@@ -6367,37 +6353,37 @@ var SmartComplySDK = (() => {
|
|
|
6367
6353
|
return t2.includes("voter") || t2.includes("driver") || t2.includes("license") || t2.includes("national id") || t2.includes("national id card");
|
|
6368
6354
|
};
|
|
6369
6355
|
if (u2.includes("BVN") || u2.includes("BANK VERIFICATION")) {
|
|
6370
|
-
return { icon: "
|
|
6356
|
+
return { icon: "🏦", desc: "Enter your 11-digit Bank Verification Number (BVN)", requiresBack: false };
|
|
6371
6357
|
}
|
|
6372
6358
|
if (u2.includes("NIN") || u2.includes("NATIONAL IDENTITY NUMBER")) {
|
|
6373
|
-
return { icon: "
|
|
6359
|
+
return { icon: "🆔", desc: "Enter your 11-digit National Identification Number (NIN)", requiresBack: false };
|
|
6374
6360
|
}
|
|
6375
6361
|
if (u2.includes("GHANA")) {
|
|
6376
|
-
if (isDocumentFlow) return { icon: "
|
|
6377
|
-
return { icon: "
|
|
6362
|
+
if (isDocumentFlow) return { icon: "🪪", desc: "Capture or upload your Ghana Card", requiresBack: true };
|
|
6363
|
+
return { icon: "🪪", desc: "Enter your Ghana Card number", requiresBack: false };
|
|
6378
6364
|
}
|
|
6379
6365
|
if (u2.includes("SSNIT"))
|
|
6380
|
-
return { icon: "
|
|
6366
|
+
return { icon: "🏛️", desc: "Enter your SSNIT number", requiresBack: false };
|
|
6381
6367
|
if (u2.includes("KENYA") || u2.includes("NATIONAL") && u2.includes("KENYA"))
|
|
6382
|
-
return { icon: "
|
|
6368
|
+
return { icon: "🆔", desc: "Enter your Kenya National ID number", requiresBack: false };
|
|
6383
6369
|
if (u2.includes("TIN") || u2.includes("TAX IDENTIFICATION"))
|
|
6384
|
-
return { icon: "
|
|
6370
|
+
return { icon: "🧾", desc: "Enter your Tax Identification Number (TIN)", requiresBack: false };
|
|
6385
6371
|
if (u2.includes("CAC") || u2.includes("CORPORATE AFFAIRS") || u2.includes("COMPANY"))
|
|
6386
|
-
return { icon: "
|
|
6372
|
+
return { icon: "🏢", desc: advanced ? "Enter RC number, company name & type" : "Enter your company registration number", requiresBack: false };
|
|
6387
6373
|
if (u2.includes("VOTER") || u2.includes("PVC")) {
|
|
6388
6374
|
const requiresBack = requiresBackSide(typeName);
|
|
6389
6375
|
return {
|
|
6390
|
-
icon: "
|
|
6376
|
+
icon: "🗳️",
|
|
6391
6377
|
desc: isDocumentFlow ? `Capture or upload your voter's card${requiresBack ? " (front & back)" : ""}` : "Enter your voter ID number",
|
|
6392
6378
|
requiresBack: isDocumentFlow ? requiresBack : false
|
|
6393
6379
|
};
|
|
6394
6380
|
}
|
|
6395
6381
|
if (u2.includes("PASSPORT"))
|
|
6396
|
-
return { icon: "
|
|
6382
|
+
return { icon: "📘", desc: isDocumentFlow ? "Capture or upload your passport" : "Enter your passport number", requiresBack: false };
|
|
6397
6383
|
if (u2.includes("DRIVER") || u2.includes("LICENSE") || u2.includes("LICENCE")) {
|
|
6398
6384
|
const requiresBack = requiresBackSide(typeName);
|
|
6399
6385
|
return {
|
|
6400
|
-
icon: "
|
|
6386
|
+
icon: "🚗",
|
|
6401
6387
|
desc: isDocumentFlow ? `Capture or upload your driver's license${requiresBack ? " (front & back)" : ""}` : "Enter your driver's license number",
|
|
6402
6388
|
requiresBack: isDocumentFlow ? requiresBack : false
|
|
6403
6389
|
};
|
|
@@ -6405,15 +6391,15 @@ var SmartComplySDK = (() => {
|
|
|
6405
6391
|
if (u2.includes("NATIONAL ID") || u2.includes("NATIONAL") && u2.includes("CARD")) {
|
|
6406
6392
|
const requiresBack = requiresBackSide(typeName);
|
|
6407
6393
|
return {
|
|
6408
|
-
icon: "
|
|
6394
|
+
icon: "🪪",
|
|
6409
6395
|
desc: isDocumentFlow ? `Capture or upload your ID card${requiresBack ? " (front & back)" : ""}` : "Enter your National ID number",
|
|
6410
6396
|
requiresBack: isDocumentFlow ? requiresBack : false
|
|
6411
6397
|
};
|
|
6412
6398
|
}
|
|
6413
6399
|
if (u2.includes("LIVENESS") || u2.includes("FACE COMPARISON") || u2.includes("FACE MATCH"))
|
|
6414
|
-
return { icon: "
|
|
6400
|
+
return { icon: "🤳", desc: "We'll capture a short selfie video to verify you", requiresBack: false };
|
|
6415
6401
|
return {
|
|
6416
|
-
icon: isDocumentFlow ? "
|
|
6402
|
+
icon: isDocumentFlow ? "📄" : "🆔",
|
|
6417
6403
|
desc: isDocumentFlow ? "Capture or upload your document" : "Enter your ID number",
|
|
6418
6404
|
requiresBack: false
|
|
6419
6405
|
};
|
|
@@ -6551,7 +6537,7 @@ var SmartComplySDK = (() => {
|
|
|
6551
6537
|
this.livenessEntryId = null;
|
|
6552
6538
|
this.isDestroyed = false;
|
|
6553
6539
|
this.headerBackBtn = null;
|
|
6554
|
-
// Retry safety
|
|
6540
|
+
// Retry safety — max 3 confirmation rejections or liveness failures per session
|
|
6555
6541
|
this._confirmRetries = 0;
|
|
6556
6542
|
this._livenessFailures = 0;
|
|
6557
6543
|
// IVS result stored for confirm_identity screen
|
|
@@ -6566,9 +6552,9 @@ var SmartComplySDK = (() => {
|
|
|
6566
6552
|
this._ocrExpirySoon = false;
|
|
6567
6553
|
// Camera pre-warm stream (created during confirmation screen, used in liveness)
|
|
6568
6554
|
this._prewarmedStream = null;
|
|
6569
|
-
// OCR gate poll timer
|
|
6555
|
+
// OCR gate poll timer — stored so it can be cancelled on navigation or close
|
|
6570
6556
|
this._ocrPollTimer = null;
|
|
6571
|
-
// UI timeout from _startStatusPolling
|
|
6557
|
+
// UI timeout from _startStatusPolling — stored so close() can cancel it
|
|
6572
6558
|
this._uiTimeoutId = null;
|
|
6573
6559
|
// Session expiry
|
|
6574
6560
|
this.sessionExpiryTimer = null;
|
|
@@ -6643,9 +6629,9 @@ var SmartComplySDK = (() => {
|
|
|
6643
6629
|
}
|
|
6644
6630
|
this.options.onClose?.();
|
|
6645
6631
|
}
|
|
6646
|
-
//
|
|
6632
|
+
// ─────────────────────────────────────────────────────────────────
|
|
6647
6633
|
// Initialization
|
|
6648
|
-
//
|
|
6634
|
+
// ─────────────────────────────────────────────────────────────────
|
|
6649
6635
|
mount() {
|
|
6650
6636
|
this.injectStyles();
|
|
6651
6637
|
this.overlay = document.createElement("div");
|
|
@@ -6730,9 +6716,9 @@ var SmartComplySDK = (() => {
|
|
|
6730
6716
|
this.showErrorStep(err.message || "Failed to initialize. Please try again.");
|
|
6731
6717
|
}
|
|
6732
6718
|
}
|
|
6733
|
-
//
|
|
6719
|
+
// ─────────────────────────────────────────────────────────────────
|
|
6734
6720
|
// Step rendering
|
|
6735
|
-
//
|
|
6721
|
+
// ─────────────────────────────────────────────────────────────────
|
|
6736
6722
|
showStep(step) {
|
|
6737
6723
|
this.currentStep = step;
|
|
6738
6724
|
if (!this.contentArea) return;
|
|
@@ -6807,7 +6793,7 @@ var SmartComplySDK = (() => {
|
|
|
6807
6793
|
wrapper.style.transform = "translateX(0)";
|
|
6808
6794
|
});
|
|
6809
6795
|
}
|
|
6810
|
-
//
|
|
6796
|
+
// ── Loading ─────────────────────────────────────────────────────
|
|
6811
6797
|
renderLoading(container) {
|
|
6812
6798
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;gap:16px;";
|
|
6813
6799
|
const spinnerWrap = document.createElement("div");
|
|
@@ -6838,7 +6824,7 @@ var SmartComplySDK = (() => {
|
|
|
6838
6824
|
sub.textContent = "Connecting to the verification service...";
|
|
6839
6825
|
container.appendChild(sub);
|
|
6840
6826
|
}
|
|
6841
|
-
//
|
|
6827
|
+
// ── Welcome ────────────────────────────────────────────────────
|
|
6842
6828
|
renderWelcome(container) {
|
|
6843
6829
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:12px;";
|
|
6844
6830
|
const isDocFlow = this.isDocumentFlow();
|
|
@@ -6852,7 +6838,7 @@ var SmartComplySDK = (() => {
|
|
|
6852
6838
|
background:linear-gradient(135deg,${this.theme.primary}22,${this.theme.primaryGlow});
|
|
6853
6839
|
border:1.5px solid ${this.theme.primary}33;
|
|
6854
6840
|
`;
|
|
6855
|
-
iconWrap.textContent = isDocFlow ? "
|
|
6841
|
+
iconWrap.textContent = isDocFlow ? "🪪" : "🛡️";
|
|
6856
6842
|
topRow.appendChild(iconWrap);
|
|
6857
6843
|
const badge = document.createElement("div");
|
|
6858
6844
|
badge.style.cssText = `
|
|
@@ -6863,7 +6849,7 @@ var SmartComplySDK = (() => {
|
|
|
6863
6849
|
color:${this.theme.primary};
|
|
6864
6850
|
border:1px solid ${this.theme.primary}30;
|
|
6865
6851
|
`;
|
|
6866
|
-
badge.textContent = isDocFlow ? "
|
|
6852
|
+
badge.textContent = isDocFlow ? "📄 Document Verification" : "🔍 Data Verification";
|
|
6867
6853
|
topRow.appendChild(badge);
|
|
6868
6854
|
container.appendChild(topRow);
|
|
6869
6855
|
const titleWrap = document.createElement("div");
|
|
@@ -6885,13 +6871,13 @@ var SmartComplySDK = (() => {
|
|
|
6885
6871
|
titleWrap.appendChild(desc);
|
|
6886
6872
|
container.appendChild(titleWrap);
|
|
6887
6873
|
const steps = isDocFlow ? [
|
|
6888
|
-
{ icon: "
|
|
6889
|
-
{ icon: "
|
|
6890
|
-
{ icon: "
|
|
6874
|
+
{ icon: "🪪", text: "Choose your document type", sub: "Passport, National ID, Driver License" },
|
|
6875
|
+
{ icon: "📷", text: "Scan your document", sub: "Upload or capture a photo of your ID" },
|
|
6876
|
+
{ icon: "🤳", text: "Face check", sub: "Quick selfie to confirm it's you" }
|
|
6891
6877
|
] : [
|
|
6892
|
-
{ icon: "
|
|
6893
|
-
{ icon: "
|
|
6894
|
-
{ icon: "
|
|
6878
|
+
{ icon: "🆔", text: "Choose your ID type", sub: "NIN, BVN, Voter's Card and more" },
|
|
6879
|
+
{ icon: "📝", text: "Enter your ID details", sub: "Exactly as shown on your ID" },
|
|
6880
|
+
{ icon: "🤳", text: "Face check", sub: "Quick selfie to confirm it's you" }
|
|
6895
6881
|
];
|
|
6896
6882
|
const stepsWrap = document.createElement("div");
|
|
6897
6883
|
stepsWrap.style.cssText = `
|
|
@@ -6931,7 +6917,7 @@ var SmartComplySDK = (() => {
|
|
|
6931
6917
|
stepsWrap.appendChild(row);
|
|
6932
6918
|
});
|
|
6933
6919
|
container.appendChild(stepsWrap);
|
|
6934
|
-
const btn = this.createPrimaryButton("Get Started
|
|
6920
|
+
const btn = this.createPrimaryButton("Get Started →");
|
|
6935
6921
|
btn.style.cssText += "margin-top:2px;";
|
|
6936
6922
|
btn.addEventListener("click", () => this.showStep("country"));
|
|
6937
6923
|
container.appendChild(btn);
|
|
@@ -6940,10 +6926,10 @@ var SmartComplySDK = (() => {
|
|
|
6940
6926
|
display:flex;align-items:center;gap:6px;
|
|
6941
6927
|
color:${this.theme.textMuted};font-size:10.5px;
|
|
6942
6928
|
`;
|
|
6943
|
-
trust.textContent = "
|
|
6929
|
+
trust.textContent = "🔒 End-to-end encrypted";
|
|
6944
6930
|
container.appendChild(trust);
|
|
6945
6931
|
}
|
|
6946
|
-
//
|
|
6932
|
+
// ── Country Select ──────────────────────────────────────────────
|
|
6947
6933
|
renderCountrySelect(container) {
|
|
6948
6934
|
container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
|
|
6949
6935
|
const channels = this.sdkConfig?.channels || {};
|
|
@@ -6971,7 +6957,7 @@ var SmartComplySDK = (() => {
|
|
|
6971
6957
|
container.appendChild(card);
|
|
6972
6958
|
}
|
|
6973
6959
|
}
|
|
6974
|
-
//
|
|
6960
|
+
// ── ID Type Select ──────────────────────────────────────────────
|
|
6975
6961
|
renderIdTypeSelect(container) {
|
|
6976
6962
|
container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
|
|
6977
6963
|
const globalDocFlow = this.isDocumentFlow();
|
|
@@ -6993,7 +6979,7 @@ var SmartComplySDK = (() => {
|
|
|
6993
6979
|
const requiresBack = requiresBackFromBackend ?? info.requiresBack;
|
|
6994
6980
|
let subtitle2 = info.desc;
|
|
6995
6981
|
if (requiresBack && isThisDocFlow) {
|
|
6996
|
-
subtitle2 += "
|
|
6982
|
+
subtitle2 += " ⚠️ Both sides required";
|
|
6997
6983
|
}
|
|
6998
6984
|
const displayName = typeName.replace(/\s*with\s+face\s*/gi, " ").replace(/\s*\(NIN\)\s*/gi, " ").replace(/\s*\(BVN\)\s*/gi, " ").replace(/\s{2,}/g, " ").trim();
|
|
6999
6985
|
const card = this.createOptionCard(
|
|
@@ -7026,7 +7012,7 @@ var SmartComplySDK = (() => {
|
|
|
7026
7012
|
container.appendChild(card);
|
|
7027
7013
|
}
|
|
7028
7014
|
}
|
|
7029
|
-
//
|
|
7015
|
+
// ── ID Input (data verification — dynamic fields) ─────────────
|
|
7030
7016
|
renderIdInput(container) {
|
|
7031
7017
|
container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
|
|
7032
7018
|
const dataFields = this.selectedChannelFields.filter((f2) => f2.type !== "upload");
|
|
@@ -7056,13 +7042,13 @@ var SmartComplySDK = (() => {
|
|
|
7056
7042
|
{ match: "BVN", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "BVN must be exactly 11 digits" } },
|
|
7057
7043
|
{ match: "NIN", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "NIN must be exactly 11 digits" } },
|
|
7058
7044
|
{ match: "NATIONAL IDENTITY", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "NIN must be exactly 11 digits" } },
|
|
7059
|
-
{ match: "PASSPORT", rule: { minLen: 6, maxLen: 20, alphanumeric: true, hint: "Passport number must be 6
|
|
7060
|
-
{ match: "DRIVER", rule: { minLen: 3, maxLen: 30, alphanumeric: true, hint: "Licence number must be 3
|
|
7061
|
-
{ match: "VOTER", rule: { minLen: 6, maxLen: 30, alphanumeric: true, hint: "Voter ID must be 6
|
|
7062
|
-
{ match: "GHANA", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "Ghana Card number must be 8
|
|
7063
|
-
{ match: "SSNIT", rule: { minLen: 7, maxLen: 15, alphanumeric: true, hint: "SSNIT number must be 7
|
|
7064
|
-
{ match: "TIN", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "TIN must be 8
|
|
7065
|
-
{ match: "KRA", rule: { minLen: 10, maxLen: 12, alphanumeric: true, hint: "KRA PIN must be 10
|
|
7045
|
+
{ match: "PASSPORT", rule: { minLen: 6, maxLen: 20, alphanumeric: true, hint: "Passport number must be 6–20 alphanumeric characters" } },
|
|
7046
|
+
{ match: "DRIVER", rule: { minLen: 3, maxLen: 30, alphanumeric: true, hint: "Licence number must be 3–30 alphanumeric characters" } },
|
|
7047
|
+
{ match: "VOTER", rule: { minLen: 6, maxLen: 30, alphanumeric: true, hint: "Voter ID must be 6–30 alphanumeric characters" } },
|
|
7048
|
+
{ match: "GHANA", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "Ghana Card number must be 8–20 alphanumeric characters" } },
|
|
7049
|
+
{ match: "SSNIT", rule: { minLen: 7, maxLen: 15, alphanumeric: true, hint: "SSNIT number must be 7–15 alphanumeric characters" } },
|
|
7050
|
+
{ match: "TIN", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "TIN must be 8–20 characters" } },
|
|
7051
|
+
{ match: "KRA", rule: { minLen: 10, maxLen: 12, alphanumeric: true, hint: "KRA PIN must be 10–12 characters" } }
|
|
7066
7052
|
];
|
|
7067
7053
|
function getFieldRule(label) {
|
|
7068
7054
|
const upper = label.toUpperCase();
|
|
@@ -7302,7 +7288,7 @@ var SmartComplySDK = (() => {
|
|
|
7302
7288
|
setTimeout(() => firstInput.focus(), 350);
|
|
7303
7289
|
}
|
|
7304
7290
|
}
|
|
7305
|
-
//
|
|
7291
|
+
// ── Document Capture ────────────────────────────────────────────
|
|
7306
7292
|
renderDocumentCapture(container) {
|
|
7307
7293
|
container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
|
|
7308
7294
|
const rawName = this.selectedIdTypeName || this.selectedIdType || "your document";
|
|
@@ -7338,7 +7324,7 @@ var SmartComplySDK = (() => {
|
|
|
7338
7324
|
this.showErrorStep(err.message || "Document capture failed. Please try again.");
|
|
7339
7325
|
});
|
|
7340
7326
|
}
|
|
7341
|
-
//
|
|
7327
|
+
// ── W3: Identity Confirmation (data verification) ───────────────
|
|
7342
7328
|
renderConfirmIdentity(container) {
|
|
7343
7329
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:16px;padding:8px 0;";
|
|
7344
7330
|
if (this._confirmRetries >= _SmartComplyFlow.MAX_RETRIES) {
|
|
@@ -7348,7 +7334,7 @@ var SmartComplySDK = (() => {
|
|
|
7348
7334
|
this._prewarmCamera();
|
|
7349
7335
|
const iconWrap = document.createElement("div");
|
|
7350
7336
|
iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
7351
|
-
iconWrap.textContent = "
|
|
7337
|
+
iconWrap.textContent = "🛡️";
|
|
7352
7338
|
container.appendChild(iconWrap);
|
|
7353
7339
|
const title = document.createElement("h2");
|
|
7354
7340
|
title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
|
|
@@ -7385,7 +7371,7 @@ var SmartComplySDK = (() => {
|
|
|
7385
7371
|
sub.textContent = "Your details have been confirmed. Please continue to the face check.";
|
|
7386
7372
|
container.appendChild(sub);
|
|
7387
7373
|
}
|
|
7388
|
-
const yesBtn = this.createPrimaryButton("Yes, that's me
|
|
7374
|
+
const yesBtn = this.createPrimaryButton("Yes, that's me — continue");
|
|
7389
7375
|
yesBtn.addEventListener("click", () => {
|
|
7390
7376
|
this._confirmRetries = 0;
|
|
7391
7377
|
this.showStep("liveness");
|
|
@@ -7402,7 +7388,7 @@ var SmartComplySDK = (() => {
|
|
|
7402
7388
|
});
|
|
7403
7389
|
container.appendChild(noBtn);
|
|
7404
7390
|
}
|
|
7405
|
-
//
|
|
7391
|
+
// ── W4: Document Confirmation (document verification) ────────────
|
|
7406
7392
|
renderDocumentConfirm(container) {
|
|
7407
7393
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:8px 0;";
|
|
7408
7394
|
if (this._confirmRetries >= _SmartComplyFlow.MAX_RETRIES) {
|
|
@@ -7412,7 +7398,7 @@ var SmartComplySDK = (() => {
|
|
|
7412
7398
|
this._prewarmCamera();
|
|
7413
7399
|
const iconWrap = document.createElement("div");
|
|
7414
7400
|
iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
7415
|
-
iconWrap.textContent = "
|
|
7401
|
+
iconWrap.textContent = "📄";
|
|
7416
7402
|
container.appendChild(iconWrap);
|
|
7417
7403
|
const title = document.createElement("h2");
|
|
7418
7404
|
title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
|
|
@@ -7435,7 +7421,7 @@ var SmartComplySDK = (() => {
|
|
|
7435
7421
|
if (this._ocrExpirySoon && !this._ocrIsExpired) {
|
|
7436
7422
|
const warn = document.createElement("div");
|
|
7437
7423
|
warn.style.cssText = `width:100%;padding:10px 14px;border-radius:10px;background:${this.theme.warning}18;border:1px solid ${this.theme.warning}40;color:${this.theme.warning};font-size:12px;text-align:left;line-height:1.5;`;
|
|
7438
|
-
warn.textContent = "
|
|
7424
|
+
warn.textContent = "⚠️ Your document expires soon. Some services may not accept it, but you can still continue.";
|
|
7439
7425
|
container.appendChild(warn);
|
|
7440
7426
|
}
|
|
7441
7427
|
const card = document.createElement("div");
|
|
@@ -7489,7 +7475,7 @@ var SmartComplySDK = (() => {
|
|
|
7489
7475
|
});
|
|
7490
7476
|
container.appendChild(noBtn);
|
|
7491
7477
|
}
|
|
7492
|
-
//
|
|
7478
|
+
// ── W6+W7+W8: OCR Gate (document verification) ───────────────────
|
|
7493
7479
|
renderOcrGate(container) {
|
|
7494
7480
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:20px 0;text-align:center;";
|
|
7495
7481
|
const spinnerWrap = document.createElement("div");
|
|
@@ -7498,18 +7484,18 @@ var SmartComplySDK = (() => {
|
|
|
7498
7484
|
ring.style.cssText = `position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.9s linear infinite;`;
|
|
7499
7485
|
const centre = document.createElement("div");
|
|
7500
7486
|
centre.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;";
|
|
7501
|
-
centre.textContent = "
|
|
7487
|
+
centre.textContent = "📄";
|
|
7502
7488
|
spinnerWrap.appendChild(ring);
|
|
7503
7489
|
spinnerWrap.appendChild(centre);
|
|
7504
7490
|
container.appendChild(spinnerWrap);
|
|
7505
7491
|
const statusEl = document.createElement("div");
|
|
7506
7492
|
statusEl.setAttribute("aria-live", "polite");
|
|
7507
7493
|
statusEl.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
|
|
7508
|
-
statusEl.textContent = "Reading your document
|
|
7494
|
+
statusEl.textContent = "Reading your document…";
|
|
7509
7495
|
container.appendChild(statusEl);
|
|
7510
7496
|
const subEl = document.createElement("div");
|
|
7511
7497
|
subEl.style.cssText = `color:${this.theme.textSecondary};font-size:12px;max-width:260px;line-height:1.5;`;
|
|
7512
|
-
subEl.textContent = "This usually takes 5
|
|
7498
|
+
subEl.textContent = "This usually takes 5–15 seconds. Please wait.";
|
|
7513
7499
|
container.appendChild(subEl);
|
|
7514
7500
|
if (!this.livenessEntryId) {
|
|
7515
7501
|
this.showErrorStep("Document upload failed. Please try again.");
|
|
@@ -7554,8 +7540,8 @@ var SmartComplySDK = (() => {
|
|
|
7554
7540
|
if (polls < MAX_POLLS && !this.isDestroyed) {
|
|
7555
7541
|
this._ocrPollTimer = setTimeout(poll, 2e3);
|
|
7556
7542
|
} else if (!this.isDestroyed) {
|
|
7557
|
-
statusEl.textContent = "Taking longer than usual
|
|
7558
|
-
subEl.textContent = "We'll continue
|
|
7543
|
+
statusEl.textContent = "Taking longer than usual…";
|
|
7544
|
+
subEl.textContent = "We'll continue — you can confirm your details on the next screen.";
|
|
7559
7545
|
this._ocrPollTimer = setTimeout(() => {
|
|
7560
7546
|
if (!this.isDestroyed) this.showStep("document_confirm");
|
|
7561
7547
|
}, 2e3);
|
|
@@ -7574,7 +7560,7 @@ var SmartComplySDK = (() => {
|
|
|
7574
7560
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:20px 0;";
|
|
7575
7561
|
const icon = document.createElement("div");
|
|
7576
7562
|
icon.style.cssText = `width:72px;height:72px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
7577
|
-
icon.textContent = "
|
|
7563
|
+
icon.textContent = "📅";
|
|
7578
7564
|
container.appendChild(icon);
|
|
7579
7565
|
const t2 = document.createElement("h2");
|
|
7580
7566
|
t2.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
|
|
@@ -7603,7 +7589,7 @@ var SmartComplySDK = (() => {
|
|
|
7603
7589
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:20px 0;";
|
|
7604
7590
|
const icon = document.createElement("div");
|
|
7605
7591
|
icon.style.cssText = `width:72px;height:72px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
7606
|
-
icon.textContent = "
|
|
7592
|
+
icon.textContent = "🔍";
|
|
7607
7593
|
container.appendChild(icon);
|
|
7608
7594
|
const t2 = document.createElement("h2");
|
|
7609
7595
|
t2.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
|
|
@@ -7623,7 +7609,7 @@ var SmartComplySDK = (() => {
|
|
|
7623
7609
|
});
|
|
7624
7610
|
container.appendChild(retryBtn);
|
|
7625
7611
|
}
|
|
7626
|
-
//
|
|
7612
|
+
// ── W11: You're all done (end-user final screen) ─────────────────
|
|
7627
7613
|
renderDone(container) {
|
|
7628
7614
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:16px;padding:24px 0;";
|
|
7629
7615
|
container.setAttribute("role", "status");
|
|
@@ -7636,7 +7622,7 @@ var SmartComplySDK = (() => {
|
|
|
7636
7622
|
});
|
|
7637
7623
|
const iconWrap = document.createElement("div");
|
|
7638
7624
|
iconWrap.style.cssText = `width:80px;height:80px;border-radius:50%;background:${this.theme.successBg};border:3px solid ${this.theme.success};display:flex;align-items:center;justify-content:center;font-size:36px;font-weight:700;color:${this.theme.success};animation:sc-pop 0.4s ease;`;
|
|
7639
|
-
iconWrap.textContent = "
|
|
7625
|
+
iconWrap.textContent = "✓";
|
|
7640
7626
|
container.appendChild(iconWrap);
|
|
7641
7627
|
const title = document.createElement("h2");
|
|
7642
7628
|
title.style.cssText = `color:${this.theme.text};font-size:22px;font-weight:700;margin:0;`;
|
|
@@ -7648,7 +7634,7 @@ var SmartComplySDK = (() => {
|
|
|
7648
7634
|
container.appendChild(desc);
|
|
7649
7635
|
const info = document.createElement("div");
|
|
7650
7636
|
info.style.cssText = `color:${this.theme.textMuted};font-size:11px;display:flex;align-items:center;gap:4px;`;
|
|
7651
|
-
info.textContent = "
|
|
7637
|
+
info.textContent = "🔒 Your data is encrypted and secure";
|
|
7652
7638
|
container.appendChild(info);
|
|
7653
7639
|
const doneBtn = this.createPrimaryButton("Close");
|
|
7654
7640
|
doneBtn.addEventListener("click", () => {
|
|
@@ -7656,12 +7642,12 @@ var SmartComplySDK = (() => {
|
|
|
7656
7642
|
});
|
|
7657
7643
|
container.appendChild(doneBtn);
|
|
7658
7644
|
}
|
|
7659
|
-
//
|
|
7645
|
+
// ── W14: Retry limit reached ─────────────────────────────────────
|
|
7660
7646
|
_renderRetryLimitReached(container) {
|
|
7661
7647
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:24px 0;";
|
|
7662
7648
|
const icon = document.createElement("div");
|
|
7663
7649
|
icon.style.cssText = `width:72px;height:72px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
7664
|
-
icon.textContent = "
|
|
7650
|
+
icon.textContent = "⚠️";
|
|
7665
7651
|
container.appendChild(icon);
|
|
7666
7652
|
const t2 = document.createElement("h2");
|
|
7667
7653
|
t2.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
|
|
@@ -7678,7 +7664,7 @@ var SmartComplySDK = (() => {
|
|
|
7678
7664
|
});
|
|
7679
7665
|
container.appendChild(closeBtn);
|
|
7680
7666
|
}
|
|
7681
|
-
//
|
|
7667
|
+
// ── W13: Camera pre-warm ─────────────────────────────────────────
|
|
7682
7668
|
_prewarmCamera() {
|
|
7683
7669
|
if (this._prewarmedStream || this.isDestroyed) return;
|
|
7684
7670
|
navigator.mediaDevices?.getUserMedia?.({ video: { facingMode: "user" } }).then((stream) => {
|
|
@@ -7717,7 +7703,7 @@ var SmartComplySDK = (() => {
|
|
|
7717
7703
|
<div style="display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;">
|
|
7718
7704
|
<div style="position:relative;width:52px;height:52px;">
|
|
7719
7705
|
<div style="position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.8s linear infinite;"></div>
|
|
7720
|
-
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;"
|
|
7706
|
+
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔍</div>
|
|
7721
7707
|
</div>
|
|
7722
7708
|
<div style="color:${this.theme.text};font-size:14px;font-weight:600;">Scanning document...</div>
|
|
7723
7709
|
<div style="color:${this.theme.textSecondary};font-size:12px;text-align:center;max-width:200px;line-height:1.5;">Running OCR and verifying your identity</div>
|
|
@@ -7775,7 +7761,7 @@ var SmartComplySDK = (() => {
|
|
|
7775
7761
|
<div style="display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;">
|
|
7776
7762
|
<div style="position:relative;width:52px;height:52px;">
|
|
7777
7763
|
<div style="position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.8s linear infinite;"></div>
|
|
7778
|
-
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;"
|
|
7764
|
+
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔄</div>
|
|
7779
7765
|
</div>
|
|
7780
7766
|
<div style="color:${this.theme.text};font-size:14px;font-weight:600;">Retrying...</div>
|
|
7781
7767
|
<div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>
|
|
@@ -7816,7 +7802,7 @@ var SmartComplySDK = (() => {
|
|
|
7816
7802
|
wrap.setAttribute("role", "alert");
|
|
7817
7803
|
const icon = document.createElement("div");
|
|
7818
7804
|
icon.style.cssText = `width:56px;height:56px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:26px;`;
|
|
7819
|
-
icon.textContent = "
|
|
7805
|
+
icon.textContent = "🔍";
|
|
7820
7806
|
wrap.appendChild(icon);
|
|
7821
7807
|
const titleEl = document.createElement("div");
|
|
7822
7808
|
titleEl.style.cssText = `color:${this.theme.text};font-size:14px;font-weight:700;`;
|
|
@@ -7840,7 +7826,7 @@ var SmartComplySDK = (() => {
|
|
|
7840
7826
|
this.showErrorStep(userMessage);
|
|
7841
7827
|
}
|
|
7842
7828
|
}
|
|
7843
|
-
//
|
|
7829
|
+
// ── Liveness ────────────────────────────────────────────────────
|
|
7844
7830
|
renderLiveness(container) {
|
|
7845
7831
|
container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
|
|
7846
7832
|
if (this._livenessFailures >= _SmartComplyFlow.MAX_RETRIES) {
|
|
@@ -7899,7 +7885,7 @@ var SmartComplySDK = (() => {
|
|
|
7899
7885
|
}
|
|
7900
7886
|
renderResult(_container) {
|
|
7901
7887
|
}
|
|
7902
|
-
//
|
|
7888
|
+
// ── Error ───────────────────────────────────────────────────────
|
|
7903
7889
|
showErrorStep(message) {
|
|
7904
7890
|
this.currentStep = "error";
|
|
7905
7891
|
if (!this.contentArea) return;
|
|
@@ -7916,7 +7902,7 @@ var SmartComplySDK = (() => {
|
|
|
7916
7902
|
const isDocQuality = /blurry|blur|quality|glare|dark|bright|lighting/i.test(message);
|
|
7917
7903
|
const isExpiredDoc = /document.*expired|expired.*document/i.test(message);
|
|
7918
7904
|
const isFaceMatch = /face.*match|selfie.*match|does not match/i.test(message);
|
|
7919
|
-
const errorIcon = isCameraErr ? "
|
|
7905
|
+
const errorIcon = isCameraErr ? "📷" : isNetworkErr ? "📡" : isAuthErr ? "⏱" : isDocQuality ? "🔍" : isExpiredDoc ? "📅" : isFaceMatch ? "🤳" : "✕";
|
|
7920
7906
|
const errorTitle = isCameraErr ? "Camera Access Required" : isNetworkErr ? "Connection Problem" : isAuthErr ? "Session Expired" : isDocQuality ? "Document Photo Unclear" : isExpiredDoc ? "Document Expired" : isFaceMatch ? "Face Match Failed" : "Verification Unsuccessful";
|
|
7921
7907
|
const userMessage = isCameraErr ? "Please allow camera access in your browser settings and try again." : isNetworkErr ? "Check your internet connection and try again." : isAuthErr ? "Your session has expired. Please start again." : isDocQuality ? _sanitizeUserMessage(message) || "Please retake your document photo in good lighting with no glare." : isExpiredDoc ? "The document you submitted has expired. Please use a valid, current ID." : isFaceMatch ? "Your selfie did not match the photo on your ID. Please ensure you are using your own valid document and face the camera directly." : _sanitizeUserMessage(message) || "We were unable to complete your verification. Please try again.";
|
|
7922
7908
|
const iconWrap = document.createElement("div");
|
|
@@ -7952,9 +7938,9 @@ var SmartComplySDK = (() => {
|
|
|
7952
7938
|
wrapper.appendChild(closeBtn);
|
|
7953
7939
|
this.contentArea.appendChild(wrapper);
|
|
7954
7940
|
}
|
|
7955
|
-
//
|
|
7941
|
+
// ─────────────────────────────────────────────────────────────────
|
|
7956
7942
|
// UI Helpers
|
|
7957
|
-
//
|
|
7943
|
+
// ─────────────────────────────────────────────────────────────────
|
|
7958
7944
|
createHeader() {
|
|
7959
7945
|
const header = document.createElement("div");
|
|
7960
7946
|
header.setAttribute("role", "banner");
|
|
@@ -8021,7 +8007,7 @@ var SmartComplySDK = (() => {
|
|
|
8021
8007
|
stepIndicator.appendChild(this.stepLabel);
|
|
8022
8008
|
header.appendChild(stepIndicator);
|
|
8023
8009
|
const closeBtn = document.createElement("button");
|
|
8024
|
-
closeBtn.innerHTML = "
|
|
8010
|
+
closeBtn.innerHTML = "✕";
|
|
8025
8011
|
closeBtn.setAttribute("aria-label", "Close identity verification");
|
|
8026
8012
|
closeBtn.style.cssText = `
|
|
8027
8013
|
width:32px;height:32px;border-radius:8px;flex-shrink:0;
|
|
@@ -8102,7 +8088,7 @@ var SmartComplySDK = (() => {
|
|
|
8102
8088
|
}
|
|
8103
8089
|
const arrow = document.createElement("span");
|
|
8104
8090
|
arrow.style.cssText = `color:${this.theme.textMuted};font-size:18px;flex-shrink:0;`;
|
|
8105
|
-
arrow.textContent = "
|
|
8091
|
+
arrow.textContent = "→";
|
|
8106
8092
|
card.appendChild(textWrap);
|
|
8107
8093
|
card.appendChild(arrow);
|
|
8108
8094
|
return card;
|
|
@@ -8174,7 +8160,7 @@ var SmartComplySDK = (() => {
|
|
|
8174
8160
|
if (this.currentStep === "loading" || this.currentStep === "welcome") {
|
|
8175
8161
|
this.stepLabel.textContent = "";
|
|
8176
8162
|
} else if (this.currentStep === "done" || this.currentStep === "result") {
|
|
8177
|
-
this.stepLabel.textContent = "Complete
|
|
8163
|
+
this.stepLabel.textContent = "Complete ✓";
|
|
8178
8164
|
} else if (stage2Steps.includes(this.currentStep)) {
|
|
8179
8165
|
this.stepLabel.textContent = "Stage 2 of 2: Face Check";
|
|
8180
8166
|
} else if (stage1Steps.includes(this.currentStep)) {
|