@tsocial/tvweb-sdk.tiki 0.0.0-beta.11012024

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.
@@ -0,0 +1,389 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
6
+ <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-eval' 'unsafe-inline' https: data:; worker-src blob:">
7
+ <meta name="description" content="Vanilla JS demo" />
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
11
+ <link rel="stylesheet" href="./style.css" />
12
+ </head>
13
+ <body>
14
+ <noscript>You need to enable JavaScript to run this app.</noscript>
15
+ <h2>Extract ID Info & Liveness detection</h2>
16
+ <div id="container"></div>
17
+
18
+ <div id="content">
19
+ <div style="margin-bottom: 10px">
20
+ Language
21
+ <select id="select-lang" onchange="onSelectLang()">
22
+ <option value="vi" selected>vi</option>
23
+ <option value="en">en</option>
24
+ </select>
25
+ </div>
26
+
27
+ <div style="margin-bottom: 10px" id="keys-input-placeholder">
28
+ </div>
29
+
30
+ <div style="display: none; margin-top: 10px;">
31
+ <label>
32
+ Cached User ID
33
+ <input id="user-id" readonly style="width: 300px" />
34
+ </label>
35
+ <button onclick="clearUserId()">Clear user id (x-request-id)</button>
36
+ </div>
37
+
38
+ <div style="display: none; margin-top: 10px;">
39
+ <div>
40
+ Tracking config
41
+ <select id="select-tracking-config-template" onchange="onSelectTrackingConfigTemplate()">
42
+ <option value="0" selected>default</option>
43
+ <option value="1">Engineering test (...9fe5)</option>
44
+ </select>
45
+ </div>
46
+ <textarea cols="40" rows="8" id="tracking-config"></textarea>
47
+ </div>
48
+
49
+ <hr />
50
+ <h4>Extract ID Info</h4>
51
+ <div style="display: none; margin-right: 20px;">
52
+ <div>Settings (notice that if <strong>Api check: true</strong>, settings from api will overwrite these settings)</div>
53
+ <textarea cols="40" rows="15" id="id-card-settings"></textarea>
54
+ </div>
55
+ <div style="display: none">
56
+ <div>Steps</div>
57
+ <textarea cols="40" rows="15" id="id-card-steps"></textarea>
58
+ </div>
59
+ <button onclick="startExtractIDInfo()">Start</button>
60
+ <pre id="result-extract-id-info"></pre>
61
+ <div id="read-id-card-loading" style="margin-top: 10px; display: none;">
62
+ Api checking
63
+ <div class="dot-windmill"></div>
64
+ </div>
65
+
66
+ <hr />
67
+
68
+ <h4>Liveness detection</h4>
69
+ <div>
70
+ Mode:
71
+ <select id="select-mode"></select>
72
+ </div>
73
+ <div style="display: none">
74
+ Camera:
75
+ <select id="select-camera">
76
+ <option value="true">Front</option>
77
+ <option value="false">Back</option>
78
+ </select>
79
+ </div>
80
+ <button onclick="startLivenessDetection()" id="btn-start-liveness" style="margin-top: 10px">
81
+ Liveness detection
82
+ </button>
83
+ <div id="liveness-loading" style="padding-top: 10px; display: none;">
84
+ Api checking
85
+ <div class="dot-windmill"></div>
86
+ </div>
87
+ <div id="result-liveness-detection"></div>
88
+ </div>
89
+
90
+ <script src="https://unpkg.com/jsqr@1.4.0/dist/jsQR.js"></script>
91
+ <script src="./qr-scanner-for-input-keys.js"></script>
92
+ <script src="./keys-input.js"></script>
93
+ <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
94
+ <script src="https://unpkg.com/@tsocial/trustvision-sdk@2.10.1/dist/trustvision-sdk.umd.js"></script>
95
+ <script src="../../build/tvweb-sdk.tiki.standalone.min.js"></script>
96
+ <script>
97
+ const keys = [
98
+ {
99
+ accessKey: "f051dfd3-8a6e-4a15-a4e8-7ea8cdc84a31",
100
+ secretKey: "vgiKCenoKvhPq79KABbFmxCBu6WX1tdw",
101
+ },
102
+ {
103
+ accessKey: "b96f7a1a-a7f1-46e8-9d7f-5f43d2d51217",
104
+ secretKey: "SYsUyQ792pLm7rnDRQFfIL6cgAecZCOp",
105
+ },
106
+ {
107
+ accessKey: "db3d055f-ba7b-4177-b7e8-110068bda3b2",
108
+ secretKey: "R1zjKNbTH3RkT2xgO2CarxAsFbcLCdsf",
109
+ },
110
+ {
111
+ accessKey: "4e24c8f3-7fe0-4824-9339-9a1216a72ac4",
112
+ secretKey: "CkoyKslrfBGv4m53LIlH0Q8yJYf4O56e",
113
+ },
114
+ ];
115
+ let listObjUrls = [];
116
+ function onSelectLang() {
117
+ const langEle = document.getElementById("select-lang");
118
+ localStorage.setItem('lang', langEle.value);
119
+ location.reload();
120
+ }
121
+ function createObjectURL(blob) {
122
+ const objUrl = URL.createObjectURL(blob);
123
+ listObjUrls.push(objUrl);
124
+ return objUrl;
125
+ }
126
+ function revokeObjectURL() {
127
+ if (listObjUrls.length === 0) return;
128
+ listObjUrls.map((item) => URL.revokeObjectURL(item));
129
+ listObjUrls = [];
130
+ }
131
+ </script>
132
+ <script type="text/javascript">
133
+ const lang = localStorage.getItem('lang');
134
+ if (lang) {
135
+ const langEle = document.getElementById("select-lang");
136
+ langEle.value = lang;
137
+ }
138
+ const tv = new TVWebSDK.SDK({
139
+ container: document.getElementById("container"),
140
+ lang: lang || "vi",
141
+ assetRoot: "https://unpkg.com/@tsocial/tvweb-sdk.tiki@latest/assets",
142
+ enableAntiDebug: false,
143
+ });
144
+ window.tv = tv;
145
+ tv.runPreloadEKYCResources();
146
+ const resultExtractIdInfoEl = document.getElementById(
147
+ "result-extract-id-info"
148
+ );
149
+ const resultLivenessDetectionEl = document.getElementById(
150
+ "result-liveness-detection"
151
+ );
152
+ const selectModeEl = document.getElementById("select-mode");
153
+
154
+ const userIdEl = document.getElementById("user-id");
155
+ const trackingConfigEl = document.getElementById("tracking-config");
156
+ const readIdCardLoadingEl = document.getElementById("read-id-card-loading");
157
+ const statusWarmupReadIDCardEl = document.getElementById("status-warmup-read-id-card");
158
+ const livenessLoadingEl = document.getElementById("liveness-loading");
159
+ const startLivenessBtn = document.getElementById("btn-start-liveness");
160
+ const idCardSettingsEl = document.getElementById("id-card-settings");
161
+ const idCardStepsEl = document.getElementById("id-card-steps");
162
+
163
+ userIdEl.value = JSON.parse(localStorage.getItem("user-id"));
164
+
165
+ Object.keys(TVWebSDK.Constants.Mode).forEach((k) => {
166
+ const option = document.createElement("option");
167
+ option.appendChild(document.createTextNode(k));
168
+ option.setAttribute("value", TVWebSDK.Constants.Mode[k]);
169
+ selectModeEl.appendChild(option);
170
+ });
171
+
172
+ idCardSettingsEl.value = JSON.stringify(TVWebSDK.defaultClientSettings, null, 2);
173
+ idCardStepsEl.value = JSON.stringify(TVWebSDK.defaultReadIDCardSteps, null, 2);
174
+ trackingConfigEl.value = JSON.stringify(TVWebSDK.TrackingContext.defaultTrackingConfig, null, 2);
175
+
176
+ function clearUserId() {
177
+ window.localStorage.removeItem('user-id');
178
+ userIdEl.value = JSON.parse(localStorage.getItem("user-id"));
179
+ }
180
+
181
+ function handleStepDoneIDCard ({ stepNumber, image, qrScannedResult, recordedVideos, apiResult }) {
182
+ const steps = JSON.parse(idCardStepsEl.value)
183
+ console.log('image', image)
184
+
185
+ resultExtractIdInfoEl.appendChild(document.createTextNode(`\n-----------------------------------------\n`))
186
+ resultExtractIdInfoEl.appendChild(document.createTextNode(`\nSTEP NUMBER ${stepNumber}:\n`));
187
+
188
+
189
+ const imgEl = document.createElement("img");
190
+ imgEl.width = "300";
191
+ imgEl.src = createObjectURL(image.blob);
192
+ resultExtractIdInfoEl.appendChild(imgEl);
193
+ resultExtractIdInfoEl.appendChild(document.createElement("br"));
194
+ resultExtractIdInfoEl.appendChild(document.createTextNode(`Recorded videos: ${recordedVideos.length}\n`));
195
+
196
+ if (qrScannedResult) {
197
+ const { result, image } = qrScannedResult
198
+
199
+ if (image && image.blob) {
200
+ const qrImgEl = document.createElement("img");
201
+ qrImgEl.width = "200";
202
+ qrImgEl.src = URL.createObjectURL(image.blob);
203
+ resultExtractIdInfoEl.appendChild(qrImgEl);
204
+ }
205
+ resultExtractIdInfoEl.appendChild(document.createTextNode(`\n${result}\n`));
206
+ }
207
+
208
+ if (apiResult) {
209
+ resultExtractIdInfoEl.appendChild(document.createTextNode("API Result:\n"));
210
+ const apiResultEl = document.createElement('textarea');
211
+ apiResultEl.value = JSON.stringify(apiResult, null, 2);
212
+ apiResultEl.cols = 55
213
+ apiResultEl.rows = 15
214
+ resultExtractIdInfoEl.appendChild(apiResultEl);
215
+ }
216
+
217
+ // all steps done
218
+ if (stepNumber === steps.length - 1) {
219
+ tv.destroyView();
220
+ readIdCardLoadingEl.style.display = 'none';
221
+ }
222
+ }
223
+
224
+ async function handleDetectIdCard (props) {
225
+ const { cardType, image, cardSide } = props
226
+ const apiClient = new trustvisionSdk.default(inputAccessKey.value, inputSecretKey.value, inputApiUrl.value);
227
+ const resultUpload = await apiClient.uploadImage({
228
+ file: image.blob,
229
+ label: `id_card.${cardType}.${cardSide}`,
230
+ });
231
+ const imageId = resultUpload.data.image_id
232
+ const resultDetect = await apiClient.httpClient.post('/v1/detect_id_cards_sync', {
233
+ card_type: cardType,
234
+ image: {
235
+ id: imageId,
236
+ }
237
+ });
238
+ return _.get(resultDetect, 'data.image.cards.0')
239
+ }
240
+
241
+ function startExtractIDInfo() {
242
+ revokeObjectURL();
243
+ resultExtractIdInfoEl.innerHTML = "";
244
+ document.body.style.height = 0;
245
+
246
+ const commonProps = {
247
+ onError: (e) => {
248
+ resultExtractIdInfoEl.appendChild(
249
+ document.createTextNode(`Error:\n${JSON.stringify(e, null, 2)}`)
250
+ );
251
+ tv.destroyView();
252
+ document.body.style.height = 'auto';
253
+ readIdCardLoadingEl.style.display = 'none';
254
+ },
255
+ onClose: () => {
256
+ document.body.style.height = 'auto';
257
+ tv.destroyView();
258
+ },
259
+ detectIdCard: handleDetectIdCard,
260
+ onStepDone: handleStepDoneIDCard,
261
+ tracking: JSON.parse(trackingConfigEl.value),
262
+ }
263
+
264
+ if (inputAccessKey.value) {
265
+ console.log('With api call')
266
+ tv.readIDCardWithApiCall({
267
+ apiCredentials: {
268
+ accessKey: inputAccessKey.value,
269
+ secretKey: inputSecretKey.value,
270
+ apiUrl: inputApiUrl.value,
271
+ },
272
+ ...commonProps,
273
+ })
274
+ } else {
275
+ console.log('UI only')
276
+ tv.readIDCardUIOnly(commonProps)
277
+ }
278
+ }
279
+
280
+ function startLivenessDetection() {
281
+ revokeObjectURL();
282
+ const livenessDetectionMode = selectModeEl.value;
283
+ const cameraSelected = document.getElementById("select-camera");
284
+ document.body.style.height = 0;
285
+
286
+ tv.livenessDetection({
287
+ apiCheck: !!inputAccessKey.value,
288
+ mode: livenessDetectionMode,
289
+ apiCredentials: {
290
+ accessKey: inputAccessKey.value,
291
+ secretKey: inputSecretKey.value,
292
+ apiUrl: inputApiUrl.value,
293
+ },
294
+ onLivenessDetectionDone: handleLivenessDetectionDone,
295
+ captureFrameSettings: {
296
+ enable: true,
297
+ framesIntervalTime: 180,
298
+ framesBatchLength: 15,
299
+ },
300
+ onError: (e) => {
301
+ livenessLoadingEl.style.display = "none";
302
+ startLivenessBtn.disabled = false;
303
+ resultLivenessDetectionEl.appendChild(
304
+ document.createTextNode(`${JSON.stringify(e, null, 2)}`)
305
+ );
306
+ },
307
+ frontCamera: cameraSelected.value === "true",
308
+ onProcessing: () => {
309
+ livenessLoadingEl.style.display = "block";
310
+ startLivenessBtn.disabled = true;
311
+
312
+ resultLivenessDetectionEl.innerHTML = "";
313
+ document.body.style.height = 'auto';
314
+ setTimeout(() => {
315
+ tv.destroyView();
316
+ }, 250);
317
+ },
318
+ onClose: () => { document.body.style.height = 'auto'; tv.destroyView(); },
319
+ });
320
+ }
321
+
322
+ function handleReadIDCardResult({ image, recordedVideos, apiResult }) {
323
+ const imgEl = document.createElement("img");
324
+ imgEl.width = "200";
325
+ imgEl.src = createObjectURL(image);
326
+ resultExtractIdInfoEl.appendChild(imgEl);
327
+ resultExtractIdInfoEl.appendChild(document.createElement("br"));
328
+ resultExtractIdInfoEl.appendChild(document.createTextNode(`Recorded videos: ${recordedVideos.length}\n`));
329
+
330
+ if (!!inputAccessKey.value) {
331
+ resultExtractIdInfoEl.appendChild(document.createTextNode("API Result:\n"));
332
+ resultExtractIdInfoEl.appendChild(
333
+ document.createTextNode(`${JSON.stringify(apiResult, null, 2)}`)
334
+ );
335
+ if (cardSide === TVWebSDK.Constants.IDCardSide.FRONT) {
336
+ const frontCardId = apiResult.cardInfo.image1.id;
337
+ inputFrontIdEl.value = frontCardId;
338
+ }
339
+ }
340
+
341
+ tv.destroyView();
342
+ readIdCardLoadingEl.style.display = 'none';
343
+ }
344
+
345
+ function handleLivenessDetectionDone(result) {
346
+ const { frontalFaces, apiCheckPassed, steps } = result;
347
+ resultLivenessDetectionEl.innerHTML = "";
348
+
349
+ if (!inputAccessKey.value) {
350
+ livenessLoadingEl.style.display = "none";
351
+ startLivenessBtn.disabled = false;
352
+ resultLivenessDetectionEl.appendChild(
353
+ document.createTextNode("Random frontal faces detected: ")
354
+ );
355
+ frontalFaces.forEach((blob) => {
356
+ const imgEl = document.createElement("img");
357
+ imgEl.width = "200";
358
+ imgEl.style.marginRight = "5px";
359
+ imgEl.src = createObjectURL(blob);
360
+ resultLivenessDetectionEl.appendChild(imgEl);
361
+ });
362
+ resultLivenessDetectionEl.appendChild(document.createElement("br"));
363
+ resultLivenessDetectionEl.appendChild(document.createElement("br"));
364
+ resultLivenessDetectionEl.appendChild(
365
+ document.createTextNode("Directional faces:")
366
+ );
367
+ steps.forEach((s) => {
368
+ const imgEl = document.createElement("img");
369
+ imgEl.width = "200";
370
+ imgEl.style.marginRight = "5px";
371
+ imgEl.src = createObjectURL(s.image.blob);
372
+ resultLivenessDetectionEl.appendChild(imgEl);
373
+ });
374
+ resultLivenessDetectionEl.appendChild(document.createElement("br"));
375
+ resultLivenessDetectionEl.appendChild(document.createElement("br"));
376
+ resultLivenessDetectionEl.appendChild(
377
+ document.createTextNode("Video:")
378
+ );
379
+ } else {
380
+ livenessLoadingEl.style.display = "none";
381
+ startLivenessBtn.disabled = false;
382
+ resultLivenessDetectionEl.appendChild(
383
+ document.createTextNode(`apiCheckPassed: ${apiCheckPassed}`)
384
+ );
385
+ }
386
+ }
387
+ </script>
388
+ </body>
389
+ </html>
@@ -0,0 +1,71 @@
1
+ /* eslint-disable prefer-destructuring */
2
+ /* eslint-disable no-var */
3
+ var inputAccessKey = {
4
+ value: '',
5
+ };
6
+ var inputSecretKey = {
7
+ value: '',
8
+ };
9
+ var inputApiUrl = {
10
+ value: '',
11
+ };
12
+ const keyInputHtml = `
13
+ <button id="key-import-string">IMPORT KEYS FROM STRING</button>
14
+ <button id="key-import-qr">IMPORT KEYS FROM QR</button>
15
+ <br/>
16
+ <br/>
17
+ <textarea cols="40" rows="5" id="key-input-string" hidden></textarea>
18
+ <br/>
19
+ <div id="key-parse"></div>
20
+ `;
21
+ const keyPlaceholder = document.getElementById('keys-input-placeholder');
22
+ keyPlaceholder.insertAdjacentHTML('afterbegin', keyInputHtml);
23
+
24
+ function onImportStrClick() {
25
+ const curState = document.getElementById('key-input-string').hidden;
26
+ document.getElementById('key-input-string').hidden = !curState;
27
+
28
+ const keyParsedPlaceholder = document.getElementById('key-parse');
29
+ keyParsedPlaceholder.innerHTML = '';
30
+ }
31
+
32
+ function importKeys(keys) {
33
+ let keyStr = '';
34
+ if (keys?.inputType === 'insertFromPaste') {
35
+ keyStr = keys?.target?.value;
36
+ }
37
+ if (typeof keys === 'string') keyStr = keys;
38
+ if (!keyStr) return;
39
+ // parse key
40
+ const keyParsed = keyStr.split(',');
41
+ // format: key_name/api_url/access_key/secret_key
42
+ const keyDisplayHtml = `
43
+ <span>Key Name: <i>${keyParsed[0]}</i></span><br/>
44
+ <span id="api-url-value">Api Url: <i>${keyParsed[1]}</i></span><br/>
45
+ <span id="access-key-value">Access Key: <i>${keyParsed[2]}</i></span><br/>
46
+ <span id="secret-value">Secret Key: <i>${keyParsed[3]}</i></span><br/>
47
+ `;
48
+ const keyParsedPlaceholder = document.getElementById('key-parse');
49
+ keyParsedPlaceholder.innerHTML = keyDisplayHtml;
50
+ document.getElementById('key-input-string').hidden = true;
51
+
52
+ inputApiUrl.value = keyParsed[1];
53
+ inputAccessKey.value = keyParsed[2];
54
+ inputSecretKey.value = keyParsed[3];
55
+ }
56
+
57
+ document.getElementById('key-import-string').addEventListener('click', onImportStrClick);
58
+ // eslint-disable-next-line no-undef
59
+ document.getElementById('key-import-qr').addEventListener('click', startQRScan);
60
+ document.getElementById('key-input-string').addEventListener('input', importKeys);
61
+
62
+ // function getKeysValue() {
63
+ // const apiUrl = document.getElementById('api-url-value').innerText;
64
+ // const accessKey = document.getElementById('access-key-value').innerText;
65
+ // const secretKey = document.getElementById('secret-value').innerText;
66
+ // return {
67
+ // apiUrl: apiUrl.slice(apiUrl.indexOf(':') + 2),
68
+ // accessKey: accessKey.slice(accessKey.indexOf(':') + 2),
69
+ // secretKey: secretKey.slice(secretKey.indexOf(':') + 2),
70
+ // };
71
+ // }
@@ -0,0 +1,81 @@
1
+ /* eslint-disable vars-on-top */
2
+ /* eslint-disable no-var */
3
+ const placeholderEl = document.getElementById('keys-input-placeholder');
4
+
5
+ placeholderEl.insertAdjacentHTML(
6
+ 'beforeend',
7
+ `<div id="loadingMessage" hidden>🎥 Unable to access video stream (please make sure you have a webcam enabled)</div>
8
+ <canvas id="canvas" style="width: 100%; max-width: 640px" hidden></canvas>`
9
+ );
10
+
11
+ var video = document.createElement('video');
12
+ var canvasElement = document.getElementById('canvas');
13
+ var canvas = canvasElement.getContext('2d');
14
+ var loadingMessage = document.getElementById('loadingMessage');
15
+
16
+ var videoStream;
17
+
18
+ // eslint-disable-next-line no-unused-vars
19
+ function startQRScan() {
20
+ loadingMessage.hidden = false;
21
+ loadingMessage.innerText = '⌛ Loading video...';
22
+ const keyParsedPlaceholder = document.getElementById('key-parse');
23
+ keyParsedPlaceholder.innerHTML = '';
24
+ // Use facingMode: environment to attemt to get the front camera on phones
25
+ navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } }).then(function (stream) {
26
+ video.srcObject = stream;
27
+ video.setAttribute('playsinline', true); // required to tell iOS safari we don't want fullscreen
28
+ video.play();
29
+ videoStream = stream;
30
+ // eslint-disable-next-line no-use-before-define
31
+ requestAnimationFrame(tick);
32
+ });
33
+ }
34
+
35
+ function drawLine(begin, end, color) {
36
+ canvas.beginPath();
37
+ canvas.moveTo(begin.x, begin.y);
38
+ canvas.lineTo(end.x, end.y);
39
+ canvas.lineWidth = 4;
40
+ canvas.strokeStyle = color;
41
+ canvas.stroke();
42
+ }
43
+
44
+ function onScanQRDone(qr) {
45
+ // stop camera
46
+ videoStream.getTracks().forEach(function (track) {
47
+ track.stop();
48
+ });
49
+ canvasElement.hidden = true;
50
+ // eslint-disable-next-line no-undef
51
+ importKeys(qr);
52
+ }
53
+
54
+ function tick() {
55
+ let shouldStop = false;
56
+ if (video.readyState === video.HAVE_ENOUGH_DATA) {
57
+ loadingMessage.hidden = true;
58
+ canvasElement.hidden = false;
59
+
60
+ canvasElement.height = video.videoHeight;
61
+ canvasElement.width = video.videoWidth;
62
+ canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
63
+ const imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height);
64
+ // eslint-disable-next-line no-undef
65
+ const code = jsQR(imageData.data, imageData.width, imageData.height, {
66
+ inversionAttempts: 'dontInvert',
67
+ });
68
+ if (code) {
69
+ shouldStop = true;
70
+ drawLine(code.location.topLeftCorner, code.location.topRightCorner, '#FF3B58');
71
+ drawLine(code.location.topRightCorner, code.location.bottomRightCorner, '#FF3B58');
72
+ drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, '#FF3B58');
73
+ drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, '#FF3B58');
74
+
75
+ setTimeout(() => {
76
+ onScanQRDone(code.data);
77
+ }, 250);
78
+ }
79
+ }
80
+ if (!shouldStop) requestAnimationFrame(tick);
81
+ }
@@ -0,0 +1,65 @@
1
+ html {
2
+ font-family: 'Inter', sans-serif;
3
+ }
4
+
5
+ #result-extract-id-info {
6
+ max-width: 100%;
7
+ white-space: break-spaces;
8
+ }
9
+ .key-input {
10
+ max-width: 500px;
11
+ width: calc(100% - 8px);
12
+ }
13
+ .dot-windmill {
14
+ position: relative;
15
+ top: -10px;
16
+ width: 10px;
17
+ height: 10px;
18
+ border-radius: 5px;
19
+ background-color: #9880ff;
20
+ color: #9880ff;
21
+ transform-origin: 5px 15px;
22
+ animation: dotWindmill 2s infinite linear;
23
+ margin-left: 100px;
24
+ margin-top: -10px;
25
+ }
26
+
27
+ .dot-windmill::before,
28
+ .dot-windmill::after {
29
+ content: "";
30
+ display: inline-block;
31
+ position: absolute;
32
+ }
33
+
34
+ .dot-windmill::before {
35
+ left: -8.66px;
36
+ top: 15px;
37
+ width: 10px;
38
+ height: 10px;
39
+ border-radius: 5px;
40
+ background-color: #9880ff;
41
+ color: #9880ff;
42
+ }
43
+
44
+ .dot-windmill::after {
45
+ left: 8.66px;
46
+ top: 15px;
47
+ width: 10px;
48
+ height: 10px;
49
+ border-radius: 5px;
50
+ background-color: #9880ff;
51
+ color: #9880ff;
52
+ }
53
+
54
+ @keyframes dotWindmill {
55
+ 0% {
56
+ transform: rotateZ(0deg) translate3d(0, 0, 0);
57
+ }
58
+ 100% {
59
+ transform: rotateZ(720deg) translate3d(0, 0, 0);
60
+ }
61
+ }
62
+
63
+ #result-biz-ocr {
64
+ white-space: pre;
65
+ }
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8"/>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
6
+ <meta name="description" content="Vanilla JS demo"/>
7
+ <title>TrustVision SDK Examples</title>
8
+ </head>
9
+ <body>
10
+ <noscript>You need to enable JavaScript to run this app.</noscript>
11
+ <h2>TrustVision SDK Examples</h2>
12
+ <ul>
13
+ <li>
14
+ <a href="extract-id-info-and-liveness-detection/index.html">Extract ID info & Liveness detection</a>
15
+ </li>
16
+ <li>
17
+ <a href="native-camera/index.html">Native camera</a>
18
+ </li>
19
+ </ul>
20
+ </body>
21
+ </html>
@@ -0,0 +1,44 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8"/>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
6
+ <meta name="description" content="Vanilla JS demo"/>
7
+ </head>
8
+ <body>
9
+ <noscript>You need to enable JavaScript to run this app.</noscript>
10
+ <h2>Native Camera</h2>
11
+ <button id="btn-open-camera">Open camera</button>
12
+ <div id="container"></div>
13
+ <img id="img-result" width="300"/>
14
+ <script src="../../build/tvweb-sdk.tiki.standalone.min.js"></script>
15
+ <script type="text/javascript">
16
+ const tv = new TVWebSDK.SDK({
17
+ container: document.getElementById('container'),
18
+ lang: 'vi',
19
+ assetRoot: 'https://unpkg.com/@tsocial/tvweb-sdk.tiki@latest/assets',
20
+ })
21
+ const buttonOpenCamera = document.getElementById('btn-open-camera')
22
+ const imgResultEl = document.getElementById('img-result')
23
+
24
+ function handleCaptureDone ({ file, encrypted }) {
25
+ imgResultEl.src = URL.createObjectURL(file)
26
+ imgResultEl.onload = function () {
27
+ URL.revokeObjectURL(this.src)
28
+ }
29
+ if (encrypted) {
30
+ alert(JSON.stringify(encrypted))
31
+ }
32
+ }
33
+
34
+ const { openCamera } = tv.initNativeCamera({
35
+ frontCamera: false,
36
+ onCaptureDone: handleCaptureDone,
37
+ useFileBrowse: true,
38
+ outputEncryptionSettings: { key: 'abc123' }
39
+ })
40
+
41
+ buttonOpenCamera.onclick = openCamera
42
+ </script>
43
+ </body>
44
+ </html>