Spaces:
Running
on
T4
Running
on
T4
updates
Browse files- static/index.html +353 -58
- static/json_viewer.css +71 -0
- static/json_viewer.js +269 -0
static/index.html
CHANGED
@@ -24,6 +24,10 @@
|
|
24 |
type="module"
|
25 |
src="https://display.truepic.com/truepic_display.es.js"
|
26 |
></script>
|
|
|
|
|
|
|
|
|
27 |
</head>
|
28 |
<body>
|
29 |
<div class="container-fluid mt-2" id="head">
|
@@ -83,10 +87,18 @@
|
|
83 |
<p>Ornare a est accumsan et platea quis rhoncus.</p>
|
84 |
</div>
|
85 |
<div class="col flex-grow-0">
|
86 |
-
<a id="download-button" class="btn btn-outline-primary"
|
|
|
|
|
87 |
</div>
|
88 |
<div class="col flex-grow-0">
|
89 |
-
<button
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
</div>
|
91 |
</div>
|
92 |
</div>
|
@@ -94,26 +106,116 @@
|
|
94 |
<div class="display-verify">
|
95 |
<div class="row mt-5 pb-4" id="original-image">
|
96 |
<div class="col flex-grow-0">
|
97 |
-
|
98 |
</div>
|
99 |
<div class="col">
|
100 |
-
<strong>Uploaded image</strong><br/>
|
101 |
-
Content Credentials <span id="contentCredentialResults"></span
|
|
|
102 |
Digital watermark <span id="digitalWatermarkResults"></span>
|
103 |
</div>
|
104 |
</div>
|
105 |
<div class="mt-3" id="resultLabel">
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
|
|
|
|
|
|
|
|
|
|
110 |
<img
|
111 |
src="images/spinner.svg"
|
112 |
class="spinner"
|
113 |
style="display: none"
|
114 |
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
</div>
|
116 |
-
|
117 |
</div>
|
118 |
</div>
|
119 |
<div class="col right-column">
|
@@ -178,7 +280,12 @@
|
|
178 |
<div class="form-group mb-3">
|
179 |
<label>Upload image</label>
|
180 |
<div class="custom-select">
|
181 |
-
<input
|
|
|
|
|
|
|
|
|
|
|
182 |
</div>
|
183 |
</div>
|
184 |
|
@@ -228,13 +335,23 @@
|
|
228 |
const imageGenForm = document.querySelector(".image-gen-form");
|
229 |
const imagePrompt = document.getElementById("prompt");
|
230 |
const model = document.getElementById("model");
|
231 |
-
const generateImageContainer = document.querySelector(
|
|
|
|
|
232 |
const generateActionMenu = document.querySelector(".action-menu");
|
233 |
-
const verifyImageContainer = document.querySelector(
|
|
|
|
|
234 |
const uploadedImageContainer = document.querySelector("#original-image");
|
235 |
const downloadButton = document.getElementById("download-button");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
|
237 |
-
|
238 |
generateTab.addEventListener("click", (event) => {
|
239 |
event.target.classList.add("active");
|
240 |
verifyTab.classList.remove("active");
|
@@ -251,13 +368,15 @@
|
|
251 |
setVerifyElementsDisplay("block");
|
252 |
});
|
253 |
|
254 |
-
document
|
255 |
-
|
256 |
-
|
|
|
|
|
257 |
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
|
262 |
function setGenerateElementsDisplay(displayStatus) {
|
263 |
displayGenerate.forEach((item) => {
|
@@ -277,20 +396,22 @@
|
|
277 |
});
|
278 |
|
279 |
function submitForm() {
|
280 |
-
const file =
|
281 |
|
282 |
let fileReader = new FileReader();
|
283 |
fileReader.readAsDataURL(file);
|
284 |
-
fileReader.onload = function (){
|
285 |
-
|
286 |
-
|
|
|
|
|
|
|
|
|
|
|
287 |
|
288 |
-
placeholder = document.querySelector('.display-verify .placeholder');
|
289 |
-
spinner = document.querySelector('.display-verify .spinner');
|
290 |
-
|
291 |
if (placeholder) placeholder.remove();
|
292 |
if (document.getElementById("result"))
|
293 |
-
|
294 |
spinner.style.display = "block";
|
295 |
|
296 |
const formData = new FormData(uploadForm);
|
@@ -311,24 +432,48 @@
|
|
311 |
.then((data) => {
|
312 |
console.log(data);
|
313 |
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
|
321 |
uploadedImageContainer.style.display = "flex";
|
322 |
-
document.getElementById(
|
323 |
|
324 |
-
document.querySelector(
|
|
|
325 |
|
326 |
-
if (data.result_media !=
|
327 |
const path = "/" + data.result_media;
|
328 |
|
329 |
var truepicDisplay = document.createElement("truepic-display");
|
330 |
-
|
331 |
-
/*
|
332 |
truepicDisplay.addEventListener(
|
333 |
"validate",
|
334 |
setVerificationOutputFromValidation
|
@@ -337,8 +482,6 @@
|
|
337 |
"validate",
|
338 |
setCertificateOutputFromValidation
|
339 |
);
|
340 |
-
*/
|
341 |
-
|
342 |
|
343 |
truepicDisplay.setAttribute("id", "result");
|
344 |
truepicDisplay.setAttribute("active", "");
|
@@ -368,19 +511,17 @@
|
|
368 |
|
369 |
imageGenForm.addEventListener("submit", async (event) => {
|
370 |
event.preventDefault();
|
371 |
-
|
372 |
-
placeholder = document.querySelector(
|
373 |
-
spinner = document.querySelector(
|
374 |
generateActionMenu.style.display = "none";
|
375 |
|
376 |
-
// verificationDetails.style.display = "none";
|
377 |
// parameters.style.display = "none";
|
378 |
-
// downloadLink.style.display = "none";
|
379 |
|
380 |
try {
|
381 |
if (placeholder) placeholder.remove();
|
382 |
if (document.getElementById("result"))
|
383 |
-
document.getElementById("result").remove();
|
384 |
|
385 |
spinner.style.display = "block";
|
386 |
|
@@ -388,16 +529,6 @@
|
|
388 |
const path = "/" + resp;
|
389 |
|
390 |
var truepicDisplay = document.createElement("truepic-display");
|
391 |
-
/*
|
392 |
-
truepicDisplay.addEventListener(
|
393 |
-
"validate",
|
394 |
-
setVerificationOutputFromValidation
|
395 |
-
);
|
396 |
-
truepicDisplay.addEventListener(
|
397 |
-
"validate",
|
398 |
-
setCertificateOutputFromValidation
|
399 |
-
);
|
400 |
-
*/
|
401 |
|
402 |
truepicDisplay.setAttribute("id", "result");
|
403 |
truepicDisplay.setAttribute("active", "");
|
@@ -421,10 +552,174 @@
|
|
421 |
promptParam.innerHTML = textGenInput.value;
|
422 |
parameters.style.display = "block";
|
423 |
*/
|
424 |
-
|
425 |
} catch (err) {
|
426 |
console.error(err);
|
427 |
}
|
428 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
429 |
</script>
|
430 |
</html>
|
|
|
24 |
type="module"
|
25 |
src="https://display.truepic.com/truepic_display.es.js"
|
26 |
></script>
|
27 |
+
|
28 |
+
<link rel="stylesheet" href="json_viewer.css" />
|
29 |
+
<script src="https://unpkg.com/@peculiar/x509"></script>
|
30 |
+
<script type="text/javascript" src="json_viewer.js"></script>
|
31 |
</head>
|
32 |
<body>
|
33 |
<div class="container-fluid mt-2" id="head">
|
|
|
87 |
<p>Ornare a est accumsan et platea quis rhoncus.</p>
|
88 |
</div>
|
89 |
<div class="col flex-grow-0">
|
90 |
+
<a id="download-button" class="btn btn-outline-primary"
|
91 |
+
>Download image <img src="images/download_icon.svg"
|
92 |
+
/></a>
|
93 |
</div>
|
94 |
<div class="col flex-grow-0">
|
95 |
+
<button
|
96 |
+
type="button"
|
97 |
+
id="goto-verify-button"
|
98 |
+
class="btn btn-outline-primary"
|
99 |
+
>
|
100 |
+
Go to verify tab <img src="images/link_icon.svg" />
|
101 |
+
</button>
|
102 |
</div>
|
103 |
</div>
|
104 |
</div>
|
|
|
106 |
<div class="display-verify">
|
107 |
<div class="row mt-5 pb-4" id="original-image">
|
108 |
<div class="col flex-grow-0">
|
109 |
+
<img id="uploaded-image" class="thumbnail" />
|
110 |
</div>
|
111 |
<div class="col">
|
112 |
+
<strong>Uploaded image</strong><br />
|
113 |
+
Content Credentials <span id="contentCredentialResults"></span
|
114 |
+
><br />
|
115 |
Digital watermark <span id="digitalWatermarkResults"></span>
|
116 |
</div>
|
117 |
</div>
|
118 |
<div class="mt-3" id="resultLabel">
|
119 |
+
<strong>Result</strong>
|
120 |
+
<div
|
121 |
+
class="alert alert-secondary"
|
122 |
+
role="alert"
|
123 |
+
id="verifyResultDescription"
|
124 |
+
></div>
|
125 |
+
</div>
|
126 |
+
<div class="image-container">
|
127 |
+
<img src="images/placeholder.png" class="placeholder" />
|
128 |
<img
|
129 |
src="images/spinner.svg"
|
130 |
class="spinner"
|
131 |
style="display: none"
|
132 |
/>
|
133 |
+
</div>
|
134 |
+
<h3>Verification Output</h3>
|
135 |
+
<div id="verification-output"></div>
|
136 |
+
|
137 |
+
<h3>Certificates</h3>
|
138 |
+
<div class="certificate" id="certificate-output">
|
139 |
+
<p>
|
140 |
+
Details about the certificate used for signing.<br />
|
141 |
+
Truepic maintains the first and only purpose-built C2PA
|
142 |
+
certificate authority.
|
143 |
+
</p>
|
144 |
+
|
145 |
+
<p>
|
146 |
+
<strong style="font-weight: 600">Certificate chain</strong>
|
147 |
+
</p>
|
148 |
+
<ul id="certificate-list"></ul>
|
149 |
+
<p><strong style="font-weight: 600">Details</strong></p>
|
150 |
+
<div class="details">
|
151 |
+
<strong>Basic info</strong>
|
152 |
+
<div>
|
153 |
+
<label>Type</label>
|
154 |
+
X.509 Certificate
|
155 |
+
</div>
|
156 |
+
<div>
|
157 |
+
<label>Serial Number</label>
|
158 |
+
<div class="serialNumber"></div>
|
159 |
+
</div>
|
160 |
+
<div>
|
161 |
+
<label>Issued</label>
|
162 |
+
<div class="issued"></div>
|
163 |
+
</div>
|
164 |
+
<div>
|
165 |
+
<label>Expired</label>
|
166 |
+
<div class="expired"></div>
|
167 |
+
</div>
|
168 |
+
<strong>Subject</strong>
|
169 |
+
<div>
|
170 |
+
<label>Common Name</label>
|
171 |
+
<div class="subjectCommonName"></div>
|
172 |
+
</div>
|
173 |
+
<div>
|
174 |
+
<label>Organization</label>
|
175 |
+
<div class="subjectOrganization"></div>
|
176 |
+
</div>
|
177 |
+
<div>
|
178 |
+
<label>Organization Unit</label>
|
179 |
+
<div class="subjectOrganizationUnit"></div>
|
180 |
+
</div>
|
181 |
+
<div>
|
182 |
+
<label>Country</label>
|
183 |
+
<div class="subjectCountry"></div>
|
184 |
+
</div>
|
185 |
+
|
186 |
+
<strong>Issuer</strong>
|
187 |
+
<div>
|
188 |
+
<label>Common Name</label>
|
189 |
+
<div class="issuerCommonName"></div>
|
190 |
+
</div>
|
191 |
+
<div>
|
192 |
+
<label>Organization</label>
|
193 |
+
<div class="issuerOrganization"></div>
|
194 |
+
</div>
|
195 |
+
<div>
|
196 |
+
<label>Organization Unit</label>
|
197 |
+
<div class="issuerOrganizationUnit"></div>
|
198 |
+
</div>
|
199 |
+
<div>
|
200 |
+
<label>Country</label>
|
201 |
+
<div class="issuerCountry"></div>
|
202 |
+
</div>
|
203 |
+
<strong>Public Key Info</strong>
|
204 |
+
|
205 |
+
<div>
|
206 |
+
<label>Algorithm</label>
|
207 |
+
<div class="algorithm"></div>
|
208 |
+
</div>
|
209 |
+
<div id="modulusContainer">
|
210 |
+
<label>Modulus</label>
|
211 |
+
<div class="modulus"></div>
|
212 |
+
</div>
|
213 |
+
<div id="curveContainer">
|
214 |
+
<label>Curve</label>
|
215 |
+
<div class="namedCurve"></div>
|
216 |
+
</div>
|
217 |
</div>
|
218 |
+
</div>
|
219 |
</div>
|
220 |
</div>
|
221 |
<div class="col right-column">
|
|
|
280 |
<div class="form-group mb-3">
|
281 |
<label>Upload image</label>
|
282 |
<div class="custom-select">
|
283 |
+
<input
|
284 |
+
type="file"
|
285 |
+
class="form-control"
|
286 |
+
name="fileUpload"
|
287 |
+
id="fileUpload"
|
288 |
+
/>
|
289 |
</div>
|
290 |
</div>
|
291 |
|
|
|
335 |
const imageGenForm = document.querySelector(".image-gen-form");
|
336 |
const imagePrompt = document.getElementById("prompt");
|
337 |
const model = document.getElementById("model");
|
338 |
+
const generateImageContainer = document.querySelector(
|
339 |
+
".display-generate .image-container"
|
340 |
+
);
|
341 |
const generateActionMenu = document.querySelector(".action-menu");
|
342 |
+
const verifyImageContainer = document.querySelector(
|
343 |
+
".display-verify .image-container"
|
344 |
+
);
|
345 |
const uploadedImageContainer = document.querySelector("#original-image");
|
346 |
const downloadButton = document.getElementById("download-button");
|
347 |
+
const verifyResultDescription = document.getElementById(
|
348 |
+
"verifyResultDescription"
|
349 |
+
);
|
350 |
+
const verificationOutput = document.getElementById("verification-output");
|
351 |
+
const certificateList = document.getElementById("certificate-list");
|
352 |
+
var certificates = [];
|
353 |
+
|
354 |
|
|
|
355 |
generateTab.addEventListener("click", (event) => {
|
356 |
event.target.classList.add("active");
|
357 |
verifyTab.classList.remove("active");
|
|
|
368 |
setVerifyElementsDisplay("block");
|
369 |
});
|
370 |
|
371 |
+
document
|
372 |
+
.getElementById("goto-verify-button")
|
373 |
+
.addEventListener("click", (event) => {
|
374 |
+
verifyTab.classList.add("active");
|
375 |
+
generateTab.classList.remove("active");
|
376 |
|
377 |
+
setVerifyElementsDisplay("block");
|
378 |
+
setGenerateElementsDisplay("none");
|
379 |
+
});
|
380 |
|
381 |
function setGenerateElementsDisplay(displayStatus) {
|
382 |
displayGenerate.forEach((item) => {
|
|
|
396 |
});
|
397 |
|
398 |
function submitForm() {
|
399 |
+
const file = document.getElementById("fileUpload").files[0];
|
400 |
|
401 |
let fileReader = new FileReader();
|
402 |
fileReader.readAsDataURL(file);
|
403 |
+
fileReader.onload = function () {
|
404 |
+
document
|
405 |
+
.getElementById("uploaded-image")
|
406 |
+
.setAttribute("src", fileReader.result);
|
407 |
+
};
|
408 |
+
|
409 |
+
placeholder = document.querySelector(".display-verify .placeholder");
|
410 |
+
spinner = document.querySelector(".display-verify .spinner");
|
411 |
|
|
|
|
|
|
|
412 |
if (placeholder) placeholder.remove();
|
413 |
if (document.getElementById("result"))
|
414 |
+
document.getElementById("result").remove(); // JCL make sure to remove the correct one
|
415 |
spinner.style.display = "block";
|
416 |
|
417 |
const formData = new FormData(uploadForm);
|
|
|
432 |
.then((data) => {
|
433 |
console.log(data);
|
434 |
|
435 |
+
document.getElementById("contentCredentialResults").innerHTML =
|
436 |
+
data.contains_c2pa;
|
437 |
+
document.getElementById("digitalWatermarkResults").innerHTML =
|
438 |
+
data.contains_watermark;
|
439 |
+
|
440 |
+
if (
|
441 |
+
data.contains_c2pa == "true" &&
|
442 |
+
data.contains_watermark == "true"
|
443 |
+
) {
|
444 |
+
verifyResultDescription.innerHTML =
|
445 |
+
"Your image contains content credentials, which are displayed below.";
|
446 |
+
} else if (
|
447 |
+
data.contains_c2pa == "false" &&
|
448 |
+
data.contains_watermark == "true"
|
449 |
+
) {
|
450 |
+
verifyResultDescription.innerHTML =
|
451 |
+
"The watermark was found, but image modifications were also detected. The last untampered, signed version on file is displayed.";
|
452 |
+
} else if (
|
453 |
+
data.contains_c2pa == "true" &&
|
454 |
+
data.contains_watermark == "false"
|
455 |
+
) {
|
456 |
+
verifyResultDescription.innerHTML =
|
457 |
+
"Your image contains content credentials, which are displayed below. A watermark was not detected.";
|
458 |
+
} else if (
|
459 |
+
data.contains_c2pa == "false" &&
|
460 |
+
data.contains_watermark == "false"
|
461 |
+
) {
|
462 |
+
verifyResultDescription.innerHTML =
|
463 |
+
"Nothing to show: this image contains neither content credentials, nor a watermark.";
|
464 |
+
}
|
465 |
|
466 |
uploadedImageContainer.style.display = "flex";
|
467 |
+
document.getElementById("resultLabel").style.display = "block";
|
468 |
|
469 |
+
document.querySelector(".display-verify .spinner").style.display =
|
470 |
+
"none";
|
471 |
|
472 |
+
if (data.result_media != "n/a") {
|
473 |
const path = "/" + data.result_media;
|
474 |
|
475 |
var truepicDisplay = document.createElement("truepic-display");
|
476 |
+
|
|
|
477 |
truepicDisplay.addEventListener(
|
478 |
"validate",
|
479 |
setVerificationOutputFromValidation
|
|
|
482 |
"validate",
|
483 |
setCertificateOutputFromValidation
|
484 |
);
|
|
|
|
|
485 |
|
486 |
truepicDisplay.setAttribute("id", "result");
|
487 |
truepicDisplay.setAttribute("active", "");
|
|
|
511 |
|
512 |
imageGenForm.addEventListener("submit", async (event) => {
|
513 |
event.preventDefault();
|
514 |
+
|
515 |
+
placeholder = document.querySelector(".display-generate .placeholder");
|
516 |
+
spinner = document.querySelector(".display-generate .spinner");
|
517 |
generateActionMenu.style.display = "none";
|
518 |
|
|
|
519 |
// parameters.style.display = "none";
|
|
|
520 |
|
521 |
try {
|
522 |
if (placeholder) placeholder.remove();
|
523 |
if (document.getElementById("result"))
|
524 |
+
document.getElementById("result").remove(); // JCL make sure to remove the correct one
|
525 |
|
526 |
spinner.style.display = "block";
|
527 |
|
|
|
529 |
const path = "/" + resp;
|
530 |
|
531 |
var truepicDisplay = document.createElement("truepic-display");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
532 |
|
533 |
truepicDisplay.setAttribute("id", "result");
|
534 |
truepicDisplay.setAttribute("active", "");
|
|
|
552 |
promptParam.innerHTML = textGenInput.value;
|
553 |
parameters.style.display = "block";
|
554 |
*/
|
|
|
555 |
} catch (err) {
|
556 |
console.error(err);
|
557 |
}
|
558 |
});
|
559 |
+
|
560 |
+
function setVerificationOutputFromValidation(event) {
|
561 |
+
//verificationDetails.style.display = "block";
|
562 |
+
return setVerificationOutput(event.detail.manifestStore.toJSON());
|
563 |
+
}
|
564 |
+
|
565 |
+
function setCertificateOutputFromValidation(event) {
|
566 |
+
return setCertificateOutput(event.detail.manifestStore);
|
567 |
+
}
|
568 |
+
|
569 |
+
function setVerificationOutput(output = null) {
|
570 |
+
verificationOutput.innerHTML = "";
|
571 |
+
|
572 |
+
if (!output) {
|
573 |
+
return;
|
574 |
+
}
|
575 |
+
|
576 |
+
const viewer = new JSONViewer();
|
577 |
+
|
578 |
+
verificationOutput.appendChild(viewer.getContainer());
|
579 |
+
|
580 |
+
viewer.showJSON(output);
|
581 |
+
}
|
582 |
+
|
583 |
+
function setCertificateOutput(manifestStore = null) {
|
584 |
+
const certificate = manifestStore?.activeManifest?.certificate;
|
585 |
+
|
586 |
+
if (!certificate) {
|
587 |
+
return;
|
588 |
+
}
|
589 |
+
|
590 |
+
certificates = [
|
591 |
+
{
|
592 |
+
der: certificate.der,
|
593 |
+
name: certificate.subjectName,
|
594 |
+
decoded: new x509.X509Certificate(certificate.der),
|
595 |
+
},
|
596 |
+
...certificate.chain.map((certificate) => ({
|
597 |
+
der: certificate.der,
|
598 |
+
decoded: new x509.X509Certificate(certificate.der),
|
599 |
+
})),
|
600 |
+
];
|
601 |
+
|
602 |
+
certificates.forEach((certificate) => {
|
603 |
+
certificate.transformed = transformCert(certificate.decoded);
|
604 |
+
});
|
605 |
+
|
606 |
+
console.log("certificates", certificates);
|
607 |
+
|
608 |
+
certificateList.innerHTML = "";
|
609 |
+
|
610 |
+
certificates.forEach((certificate, index) => {
|
611 |
+
var li = document.createElement("li");
|
612 |
+
if (index == 0) li.classList.add("active");
|
613 |
+
li.appendChild(
|
614 |
+
document.createTextNode(certificate.transformed.subjectCommonName)
|
615 |
+
);
|
616 |
+
li.addEventListener("click", function (e) {
|
617 |
+
setCertificate(index);
|
618 |
+
const lis = document.querySelectorAll("#certificate-list li");
|
619 |
+
|
620 |
+
lis.forEach((element) => {
|
621 |
+
element.classList.remove("active");
|
622 |
+
});
|
623 |
+
|
624 |
+
this.classList.add("active");
|
625 |
+
});
|
626 |
+
|
627 |
+
certificateList.appendChild(li);
|
628 |
+
});
|
629 |
+
|
630 |
+
setCertificate(0);
|
631 |
+
}
|
632 |
+
|
633 |
+
function transformCert(certificate) {
|
634 |
+
const {
|
635 |
+
issuer,
|
636 |
+
subject,
|
637 |
+
notAfter: expired,
|
638 |
+
notBefore: issued,
|
639 |
+
serialNumber,
|
640 |
+
publicKey: {
|
641 |
+
algorithm: {
|
642 |
+
name: algorithm,
|
643 |
+
modulusLength: modulus,
|
644 |
+
namedCurve: namedCurve,
|
645 |
+
},
|
646 |
+
},
|
647 |
+
} = certificate;
|
648 |
+
|
649 |
+
const parsedSubject = parseCertificateValues(subject);
|
650 |
+
const parsedIssuer = parseCertificateValues(issuer);
|
651 |
+
|
652 |
+
return {
|
653 |
+
issuerCommonName: parsedIssuer["CN"],
|
654 |
+
issuerOrganizationUnit: parsedIssuer["OU"],
|
655 |
+
issuerOrganization: parsedIssuer["O"],
|
656 |
+
issuerCountry: parsedIssuer["C"],
|
657 |
+
subjectCommonName: parsedSubject["CN"],
|
658 |
+
subjectOrganizationUnit: parsedSubject["OU"],
|
659 |
+
subjectOrganization: parsedSubject["O"],
|
660 |
+
subjectCountry: parsedSubject["C"],
|
661 |
+
issued,
|
662 |
+
expired,
|
663 |
+
serialNumber,
|
664 |
+
algorithm,
|
665 |
+
modulus,
|
666 |
+
namedCurve,
|
667 |
+
};
|
668 |
+
}
|
669 |
+
|
670 |
+
function parseCertificateValues(input) {
|
671 |
+
const params = new URLSearchParams(input.replaceAll(",", "&"));
|
672 |
+
const responses = {};
|
673 |
+
|
674 |
+
for (const entry of params.entries()) {
|
675 |
+
responses[entry[0].trim()] = entry[1];
|
676 |
+
}
|
677 |
+
|
678 |
+
return responses;
|
679 |
+
}
|
680 |
+
|
681 |
+
function setCertificate(ind) {
|
682 |
+
const certificate = certificates[ind].transformed;
|
683 |
+
|
684 |
+
document.querySelector(".details .issuerCommonName").innerHTML =
|
685 |
+
certificate.issuerCommonName;
|
686 |
+
document.querySelector(".details .issuerOrganizationUnit").innerHTML =
|
687 |
+
certificate.issuerOrganizationUnit;
|
688 |
+
document.querySelector(".details .issuerOrganization").innerHTML =
|
689 |
+
certificate.issuerOrganization;
|
690 |
+
document.querySelector(".details .issuerCountry").innerHTML =
|
691 |
+
certificate.issuerCountry;
|
692 |
+
document.querySelector(".details .subjectCommonName").innerHTML =
|
693 |
+
certificate.subjectCommonName;
|
694 |
+
document.querySelector(".details .subjectOrganizationUnit").innerHTML =
|
695 |
+
certificate.subjectOrganizationUnit;
|
696 |
+
document.querySelector(".details .subjectOrganization").innerHTML =
|
697 |
+
certificate.subjectOrganization;
|
698 |
+
document.querySelector(".details .subjectCountry").innerHTML =
|
699 |
+
certificate.subjectCountry;
|
700 |
+
document.querySelector(".details .issued").innerHTML = certificate.issued;
|
701 |
+
document.querySelector(".details .expired").innerHTML =
|
702 |
+
certificate.expired;
|
703 |
+
document.querySelector(".details .serialNumber").innerHTML =
|
704 |
+
certificate.serialNumber;
|
705 |
+
document.querySelector(".details .algorithm").innerHTML =
|
706 |
+
certificate.algorithm;
|
707 |
+
|
708 |
+
if (certificate.modulus !== undefined) {
|
709 |
+
document.querySelector(".details .modulus").innerHTML =
|
710 |
+
certificate.modulus;
|
711 |
+
document.querySelector("#modulusContainers").style.display = "block";
|
712 |
+
} else {
|
713 |
+
document.querySelector("#modulusContainers").style.display = "none";
|
714 |
+
}
|
715 |
+
|
716 |
+
if (certificate.namedCurve !== undefined) {
|
717 |
+
document.querySelector(".details .namedCurve").innerHTML =
|
718 |
+
certificate.namedCurve;
|
719 |
+
document.querySelector("#curveContainer").style.display = "block";
|
720 |
+
} else {
|
721 |
+
document.querySelector("#curveContainer").style.display = "none";
|
722 |
+
}
|
723 |
+
}
|
724 |
</script>
|
725 |
</html>
|
static/json_viewer.css
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.json-viewer {
|
2 |
+
color: #000;
|
3 |
+
padding-left: 20px;
|
4 |
+
}
|
5 |
+
|
6 |
+
.json-viewer ul {
|
7 |
+
list-style-type: none;
|
8 |
+
margin: 0;
|
9 |
+
margin: 0 0 0 1px;
|
10 |
+
border-left: 1px dotted #ccc;
|
11 |
+
padding-left: 2em;
|
12 |
+
}
|
13 |
+
|
14 |
+
.json-viewer .hide {
|
15 |
+
display: none;
|
16 |
+
}
|
17 |
+
|
18 |
+
.json-viewer .type-string {
|
19 |
+
color: #0b7500;
|
20 |
+
}
|
21 |
+
|
22 |
+
.json-viewer .type-date {
|
23 |
+
color: #cb7500;
|
24 |
+
}
|
25 |
+
|
26 |
+
.json-viewer .type-boolean {
|
27 |
+
color: #1a01cc;
|
28 |
+
font-weight: bold;
|
29 |
+
}
|
30 |
+
|
31 |
+
.json-viewer .type-number {
|
32 |
+
color: #1a01cc;
|
33 |
+
}
|
34 |
+
|
35 |
+
.json-viewer .type-null,
|
36 |
+
.json-viewer .type-undefined {
|
37 |
+
color: #90a;
|
38 |
+
}
|
39 |
+
|
40 |
+
.json-viewer a.list-link {
|
41 |
+
color: #000;
|
42 |
+
text-decoration: none;
|
43 |
+
position: relative;
|
44 |
+
}
|
45 |
+
|
46 |
+
.json-viewer a.list-link:before {
|
47 |
+
color: #aaa;
|
48 |
+
content: "\25BC";
|
49 |
+
position: absolute;
|
50 |
+
display: inline-block;
|
51 |
+
width: 1em;
|
52 |
+
left: -1em;
|
53 |
+
}
|
54 |
+
|
55 |
+
.json-viewer a.list-link.collapsed:before {
|
56 |
+
content: "\25B6";
|
57 |
+
}
|
58 |
+
|
59 |
+
.json-viewer a.list-link.empty:before {
|
60 |
+
content: "";
|
61 |
+
}
|
62 |
+
|
63 |
+
.json-viewer .items-ph {
|
64 |
+
color: #aaa;
|
65 |
+
padding: 0 1em;
|
66 |
+
}
|
67 |
+
|
68 |
+
.json-viewer .items-ph:hover {
|
69 |
+
text-decoration: underline;
|
70 |
+
}
|
71 |
+
|
static/json_viewer.js
ADDED
@@ -0,0 +1,269 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* JSONViewer - by Roman Makudera 2016 (c) MIT licence.
|
3 |
+
*/
|
4 |
+
var JSONViewer = (function(document) {
|
5 |
+
var Object_prototype_toString = ({}).toString;
|
6 |
+
var DatePrototypeAsString = Object_prototype_toString.call(new Date);
|
7 |
+
|
8 |
+
/** @constructor */
|
9 |
+
function JSONViewer() {
|
10 |
+
this._dom_container = document.createElement("pre");
|
11 |
+
this._dom_container.classList.add("json-viewer");
|
12 |
+
};
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Visualise JSON object.
|
16 |
+
*
|
17 |
+
* @param {Object|Array} json Input value
|
18 |
+
* @param {Number} [inputMaxLvl] Process only to max level, where 0..n, -1 unlimited
|
19 |
+
* @param {Number} [inputColAt] Collapse at level, where 0..n, -1 unlimited
|
20 |
+
*/
|
21 |
+
JSONViewer.prototype.showJSON = function(jsonValue, inputMaxLvl, inputColAt) {
|
22 |
+
// Process only to maxLvl, where 0..n, -1 unlimited
|
23 |
+
var maxLvl = typeof inputMaxLvl === "number" ? inputMaxLvl : -1; // max level
|
24 |
+
// Collapse at level colAt, where 0..n, -1 unlimited
|
25 |
+
var colAt = typeof inputColAt === "number" ? inputColAt : -1; // collapse at
|
26 |
+
|
27 |
+
this._dom_container.innerHTML = "";
|
28 |
+
walkJSONTree(this._dom_container, jsonValue, maxLvl, colAt, 0);
|
29 |
+
};
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Get container with pre object - this container is used for visualise JSON data.
|
33 |
+
*
|
34 |
+
* @return {Element}
|
35 |
+
*/
|
36 |
+
JSONViewer.prototype.getContainer = function() {
|
37 |
+
return this._dom_container;
|
38 |
+
};
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Recursive walk for input value.
|
42 |
+
*
|
43 |
+
* @param {Element} outputParent is the Element that will contain the new DOM
|
44 |
+
* @param {Object|Array} value Input value
|
45 |
+
* @param {Number} maxLvl Process only to max level, where 0..n, -1 unlimited
|
46 |
+
* @param {Number} colAt Collapse at level, where 0..n, -1 unlimited
|
47 |
+
* @param {Number} lvl Current level
|
48 |
+
*/
|
49 |
+
function walkJSONTree(outputParent, value, maxLvl, colAt, lvl) {
|
50 |
+
var isDate = Object_prototype_toString.call(value) === DatePrototypeAsString;
|
51 |
+
var realValue = !isDate && typeof value === "object" && value !== null && "toJSON" in value ? value.toJSON() : value;
|
52 |
+
if (typeof realValue === "object" && realValue !== null && !isDate) {
|
53 |
+
var isMaxLvl = maxLvl >= 0 && lvl >= maxLvl;
|
54 |
+
var isCollapse = colAt >= 0 && lvl >= colAt;
|
55 |
+
|
56 |
+
var isArray = Array.isArray(realValue);
|
57 |
+
var items = isArray ? realValue : Object.keys(realValue);
|
58 |
+
|
59 |
+
if (lvl === 0) {
|
60 |
+
// root level
|
61 |
+
var rootCount = _createItemsCount(items.length);
|
62 |
+
// hide/show
|
63 |
+
var rootLink = _createLink(isArray ? "[" : "{");
|
64 |
+
|
65 |
+
if (items.length) {
|
66 |
+
rootLink.addEventListener("click", function() {
|
67 |
+
if (isMaxLvl) return;
|
68 |
+
|
69 |
+
rootLink.classList.toggle("collapsed");
|
70 |
+
rootCount.classList.toggle("hide");
|
71 |
+
|
72 |
+
// main list
|
73 |
+
outputParent.querySelector("ul").classList.toggle("hide");
|
74 |
+
});
|
75 |
+
|
76 |
+
if (isCollapse) {
|
77 |
+
rootLink.classList.add("collapsed");
|
78 |
+
rootCount.classList.remove("hide");
|
79 |
+
}
|
80 |
+
}
|
81 |
+
else {
|
82 |
+
rootLink.classList.add("empty");
|
83 |
+
}
|
84 |
+
|
85 |
+
rootLink.appendChild(rootCount);
|
86 |
+
outputParent.appendChild(rootLink); // output the rootLink
|
87 |
+
}
|
88 |
+
|
89 |
+
if (items.length && !isMaxLvl) {
|
90 |
+
var len = items.length - 1;
|
91 |
+
var ulList = document.createElement("ul");
|
92 |
+
ulList.setAttribute("data-level", lvl);
|
93 |
+
ulList.classList.add("type-" + (isArray ? "array" : "object"));
|
94 |
+
|
95 |
+
items.forEach(function(key, ind) {
|
96 |
+
var item = isArray ? key : value[key];
|
97 |
+
var li = document.createElement("li");
|
98 |
+
|
99 |
+
if (typeof item === "object") {
|
100 |
+
// null && date
|
101 |
+
if (!item || item instanceof Date) {
|
102 |
+
li.appendChild(document.createTextNode(isArray ? "" : key + ": "));
|
103 |
+
li.appendChild(createSimpleViewOf(item ? item : null, true));
|
104 |
+
}
|
105 |
+
// array & object
|
106 |
+
else {
|
107 |
+
var itemIsArray = Array.isArray(item);
|
108 |
+
var itemLen = itemIsArray ? item.length : Object.keys(item).length;
|
109 |
+
|
110 |
+
// empty
|
111 |
+
if (!itemLen) {
|
112 |
+
li.appendChild(document.createTextNode(key + ": " + (itemIsArray ? "[]" : "{}")));
|
113 |
+
}
|
114 |
+
else {
|
115 |
+
// 1+ items
|
116 |
+
var itemTitle = (typeof key === "string" ? key + ": " : "") + (itemIsArray ? "[" : "{");
|
117 |
+
var itemLink = _createLink(itemTitle);
|
118 |
+
var itemsCount = _createItemsCount(itemLen);
|
119 |
+
|
120 |
+
// maxLvl - only text, no link
|
121 |
+
if (maxLvl >= 0 && lvl + 1 >= maxLvl) {
|
122 |
+
li.appendChild(document.createTextNode(itemTitle));
|
123 |
+
}
|
124 |
+
else {
|
125 |
+
itemLink.appendChild(itemsCount);
|
126 |
+
li.appendChild(itemLink);
|
127 |
+
}
|
128 |
+
|
129 |
+
walkJSONTree(li, item, maxLvl, colAt, lvl + 1);
|
130 |
+
li.appendChild(document.createTextNode(itemIsArray ? "]" : "}"));
|
131 |
+
|
132 |
+
var list = li.querySelector("ul");
|
133 |
+
var itemLinkCb = function() {
|
134 |
+
itemLink.classList.toggle("collapsed");
|
135 |
+
itemsCount.classList.toggle("hide");
|
136 |
+
list.classList.toggle("hide");
|
137 |
+
};
|
138 |
+
|
139 |
+
// hide/show
|
140 |
+
itemLink.addEventListener("click", itemLinkCb);
|
141 |
+
|
142 |
+
// collapse lower level
|
143 |
+
if (colAt >= 0 && lvl + 1 >= colAt) {
|
144 |
+
itemLinkCb();
|
145 |
+
}
|
146 |
+
}
|
147 |
+
}
|
148 |
+
}
|
149 |
+
// simple values
|
150 |
+
else {
|
151 |
+
// object keys with key:
|
152 |
+
if (!isArray) {
|
153 |
+
li.appendChild(document.createTextNode(key + ": "));
|
154 |
+
}
|
155 |
+
|
156 |
+
// recursive
|
157 |
+
walkJSONTree(li, item, maxLvl, colAt, lvl + 1);
|
158 |
+
}
|
159 |
+
|
160 |
+
// add comma to the end
|
161 |
+
if (ind < len) {
|
162 |
+
li.appendChild(document.createTextNode(","));
|
163 |
+
}
|
164 |
+
|
165 |
+
ulList.appendChild(li);
|
166 |
+
}, this);
|
167 |
+
|
168 |
+
outputParent.appendChild(ulList); // output ulList
|
169 |
+
}
|
170 |
+
else if (items.length && isMaxLvl) {
|
171 |
+
var itemsCount = _createItemsCount(items.length);
|
172 |
+
itemsCount.classList.remove("hide");
|
173 |
+
|
174 |
+
outputParent.appendChild(itemsCount); // output itemsCount
|
175 |
+
}
|
176 |
+
|
177 |
+
if (lvl === 0) {
|
178 |
+
// empty root
|
179 |
+
if (!items.length) {
|
180 |
+
var itemsCount = _createItemsCount(0);
|
181 |
+
itemsCount.classList.remove("hide");
|
182 |
+
|
183 |
+
outputParent.appendChild(itemsCount); // output itemsCount
|
184 |
+
}
|
185 |
+
|
186 |
+
// root cover
|
187 |
+
outputParent.appendChild(document.createTextNode(isArray ? "]" : "}"));
|
188 |
+
|
189 |
+
// collapse
|
190 |
+
if (isCollapse) {
|
191 |
+
outputParent.querySelector("ul").classList.add("hide");
|
192 |
+
}
|
193 |
+
}
|
194 |
+
} else {
|
195 |
+
// simple values
|
196 |
+
outputParent.appendChild( createSimpleViewOf(value, isDate) );
|
197 |
+
}
|
198 |
+
};
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Create simple value (no object|array).
|
202 |
+
*
|
203 |
+
* @param {Number|String|null|undefined|Date} value Input value
|
204 |
+
* @return {Element}
|
205 |
+
*/
|
206 |
+
function createSimpleViewOf(value, isDate) {
|
207 |
+
var spanEl = document.createElement("span");
|
208 |
+
var type = typeof value;
|
209 |
+
var asText = "" + value;
|
210 |
+
|
211 |
+
if (type === "string") {
|
212 |
+
asText = '"' + value + '"';
|
213 |
+
} else if (value === null) {
|
214 |
+
type = "null";
|
215 |
+
//asText = "null";
|
216 |
+
} else if (isDate) {
|
217 |
+
type = "date";
|
218 |
+
asText = value.toLocaleString();
|
219 |
+
}
|
220 |
+
|
221 |
+
spanEl.className = "type-" + type;
|
222 |
+
spanEl.textContent = asText;
|
223 |
+
|
224 |
+
return spanEl;
|
225 |
+
};
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Create items count element.
|
229 |
+
*
|
230 |
+
* @param {Number} count Items count
|
231 |
+
* @return {Element}
|
232 |
+
*/
|
233 |
+
function _createItemsCount(count) {
|
234 |
+
var itemsCount = document.createElement("span");
|
235 |
+
itemsCount.className = "items-ph hide";
|
236 |
+
itemsCount.innerHTML = _getItemsTitle(count);
|
237 |
+
|
238 |
+
return itemsCount;
|
239 |
+
};
|
240 |
+
|
241 |
+
/**
|
242 |
+
* Create clickable link.
|
243 |
+
*
|
244 |
+
* @param {String} title Link title
|
245 |
+
* @return {Element}
|
246 |
+
*/
|
247 |
+
function _createLink(title) {
|
248 |
+
var linkEl = document.createElement("a");
|
249 |
+
linkEl.classList.add("list-link");
|
250 |
+
linkEl.href = "javascript:void(0)";
|
251 |
+
linkEl.innerHTML = title || "";
|
252 |
+
|
253 |
+
return linkEl;
|
254 |
+
};
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Get correct item|s title for count.
|
258 |
+
*
|
259 |
+
* @param {Number} count Items count
|
260 |
+
* @return {String}
|
261 |
+
*/
|
262 |
+
function _getItemsTitle(count) {
|
263 |
+
var itemsTxt = count > 1 || count === 0 ? "items" : "item";
|
264 |
+
|
265 |
+
return (count + " " + itemsTxt);
|
266 |
+
};
|
267 |
+
|
268 |
+
return JSONViewer;
|
269 |
+
})(document);
|