309 lines
No EOL
9.6 KiB
HTML
309 lines
No EOL
9.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>Open Signature Generator</title>
|
|
<meta name="Open Signature Generator" />
|
|
<meta name="description" content="Generate and verify document signatures for free!" />
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
|
|
|
|
<!-- Add critical layout CSS inline to prevent layout shift -->
|
|
<style>
|
|
.hidden {
|
|
display: none !important;
|
|
}
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 100vh;
|
|
}
|
|
#document-container {
|
|
width: 70%;
|
|
max-width: 600px;
|
|
margin: 100px auto 20px auto;
|
|
padding: 40px;
|
|
}
|
|
#title-container {
|
|
position: absolute;
|
|
width: 100%;
|
|
top: 20px;
|
|
text-align: center;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="title-container">
|
|
<h1>
|
|
Open Signature Generator
|
|
</h1>
|
|
</div>
|
|
|
|
<div id="document-container">
|
|
<div id="loading-spinner" class="loading">Loading...</div>
|
|
<img id="signature-image" style="max-width: 100%; margin-bottom: 20px;">
|
|
<div id="verification-info"></div>
|
|
</div>
|
|
|
|
<div class="button-container"></div>
|
|
<a href="/" class="button">Go to Open Signature Generator!</a>
|
|
</div>
|
|
|
|
<div id="animated-background">
|
|
<svg viewBox="0 0 100 100"></svg>
|
|
</div>
|
|
</body>
|
|
|
|
<script>
|
|
const animatedBackground = document.getElementById('animated-background');
|
|
|
|
function stopAnimations() {
|
|
const animations = animatedBackground.querySelectorAll('animate, animateTransform');
|
|
animations.forEach(animation => {
|
|
animation.style.setProperty('animation-play-state', 'paused');
|
|
});
|
|
}
|
|
|
|
// Modify your initialization code
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// Extract the signature hash from the URL
|
|
let url = window.location.href;
|
|
let hash = url.split('/v/')[1];
|
|
|
|
let image_url = `/images/${hash}.png`;
|
|
|
|
// Set the image source
|
|
const signatureImage = document.getElementById('signature-image');
|
|
const loadingSpinner = document.getElementById('loading-spinner');
|
|
|
|
signatureImage.onload = function() {
|
|
loadingSpinner.style.display = 'none';
|
|
signatureImage.style.opacity = 1;
|
|
};
|
|
|
|
signatureImage.onerror = function() {
|
|
loadingSpinner.textContent = 'Error loading signature image';
|
|
loadingSpinner.style.display = 'none';
|
|
};
|
|
|
|
signatureImage.src = image_url;
|
|
|
|
// Fetch and display verification data
|
|
fetch(`/verify/${hash}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// Create a Date object from the time int and the timezone
|
|
const time = new Date(data.time * 1000);
|
|
const timezone_offset_minutes = data.timezone;
|
|
let timezone_string = "";
|
|
if (timezone_offset_minutes > 0) {
|
|
timezone_string = "UTC +" + Math.floor(timezone_offset_minutes/60) + ":" + String(Math.floor(timezone_offset_minutes%60)).padStart(2, '0');
|
|
} else {
|
|
timezone_string = "UTC " + Math.floor(timezone_offset_minutes/60) + ":" + String(Math.floor(timezone_offset_minutes%60)).padStart(2, '0');
|
|
}
|
|
|
|
if (data.invert) {
|
|
document.getElementById('signature-image').classList.add('invert');
|
|
}
|
|
|
|
const verificationInfo = document.getElementById('verification-info');
|
|
verificationInfo.innerHTML = `
|
|
<h3>Signature Details</h3>
|
|
<p><strong>Created:</strong> ${time.toLocaleString()}</p>
|
|
<p><strong>Creator Information:</strong>
|
|
<ul>
|
|
<li><strong>Signed Name:</strong> ${data.name}</li>
|
|
<li><strong>Signing Browser Timezone:</strong> ${timezone_string}</li>
|
|
<li><strong>IP Address:</strong> ${data.identity.ip}</li>
|
|
<li><strong>Approximate Physical Address:</strong> ${data.identity.address}</li>
|
|
<li><strong>Approximate GPS Location:</strong> ${data.identity.latlon}</li>
|
|
<li><strong>Internet Service Provider:</strong> ${data.identity.provider}</li>
|
|
<li><strong>Browser Information:</strong> ${data.identity.useragent}</li>
|
|
</ul>
|
|
</p>
|
|
${data.message ? `<p><strong>Message:</strong> ${data.message}</p>` : ''}
|
|
`;
|
|
verificationInfo.style.color = '#333';
|
|
})
|
|
.catch(error => {
|
|
const verificationInfo = document.getElementById('verification-info');
|
|
verificationInfo.innerHTML = `<p style="color: red">Error loading signature verification data.</p>`;
|
|
console.error('Error:', error);
|
|
});
|
|
|
|
// Stop animations after 5 minutes to save resources
|
|
setTimeout(stopAnimations, 300 * 1000);
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
body {
|
|
background-color: #000000;
|
|
margin: 0;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 100vh;
|
|
font-family: Arial, sans-serif;
|
|
}
|
|
|
|
#title-container {
|
|
position: absolute;
|
|
width: 100%;
|
|
top: 20px;
|
|
z-index: 2;
|
|
text-align: center;
|
|
}
|
|
|
|
#title-container h1 {
|
|
font-size: clamp(24px, calc(6vw + 8px), 64px);
|
|
margin-top: 20px;
|
|
color: white;
|
|
text-shadow: 5px 5px 10px rgba(0, 0, 0, 0.7);
|
|
}
|
|
|
|
#document-container {
|
|
background-color: rgb(236, 236, 234);
|
|
width: 70%;
|
|
max-width: 600px;
|
|
margin: 100px auto 20px auto;
|
|
padding: 40px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4);
|
|
position: relative;
|
|
z-index: 1;
|
|
overflow: hidden;
|
|
}
|
|
|
|
#signature-image {
|
|
border-radius: 1px;
|
|
border: 1px solid #000000;
|
|
}
|
|
|
|
.invert {
|
|
background-color: #000000;
|
|
}
|
|
|
|
#animated-background {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: -1;
|
|
overflow: clip;
|
|
}
|
|
|
|
@property --a {
|
|
syntax: '<color>';
|
|
inherits: false;
|
|
initial-value: #453f53;
|
|
}
|
|
@property --b {
|
|
syntax: '<color>';
|
|
inherits: false;
|
|
initial-value: #fff;
|
|
}
|
|
@property --c {
|
|
syntax: '<color>';
|
|
inherits: false;
|
|
initial-value: #777;
|
|
}
|
|
|
|
#animated-background svg {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: var(--pattern), var(--map, linear-gradient(90deg, #888, #fff));
|
|
background-blend-mode: multiply;
|
|
filter: contrast(5) blur(20px) saturate(35%) brightness(0.4);
|
|
mix-blend-mode: darken;
|
|
--pattern: repeating-radial-gradient(circle, var(--a), var(--b), var(--c) 15em);
|
|
/* We use steps here to limit framerate to reduce CPU usage displaying the animation*/
|
|
/* Because it is a slowly changing background, a low framerate does not impact apparent smoothness of the animation */
|
|
animation: bganimation 120s forwards steps(1200) infinite;
|
|
transform: translateX(35%) translateY(75%) scale(4.5)
|
|
}
|
|
|
|
@keyframes bganimation {
|
|
0% { --a: #453f53;
|
|
--b: #fff;
|
|
--c: #777;
|
|
transform: translateX(35%) translateY(75%) scale(4.5)}
|
|
33% { --a: #ce8083;
|
|
--b: #ac8cbd;
|
|
--c: #3b1c80;
|
|
transform: rotate(-10deg) scale(4.0,3.5) translateX(15%) translateY(25%)}
|
|
66% { --a: #309385;
|
|
--b: #5aa8fb;
|
|
--c: #866849;
|
|
transform: rotate(10deg) scale(4.5,3.5) translateX(25%) translateY(-15%)}
|
|
100% { --a: #453f53;
|
|
--b: #fff;
|
|
--c: #777;
|
|
transform: translateX(35%) translateY(75%) scale(4.5)}
|
|
}
|
|
|
|
.loading {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
font-size: 3.0em;
|
|
color: #666;
|
|
height: 100px;
|
|
position: relative;
|
|
}
|
|
|
|
.loading::after {
|
|
content: '';
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 4px solid #f3f3f3;
|
|
border-top: 4px solid #3498db;
|
|
border-radius: 50%;
|
|
animation: spin 1.0s ease-in-out infinite;
|
|
position: absolute;
|
|
margin-left: 10px;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(90deg); }
|
|
100% { transform: rotate(450deg); }
|
|
}
|
|
|
|
.button-container {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 20px;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.button {
|
|
padding: 10px 20px;
|
|
background-color: #1f4e82;
|
|
color: white;
|
|
text-decoration: none;
|
|
border-radius: 5px;
|
|
transition: background-color 0.3s;
|
|
}
|
|
|
|
.button:hover {
|
|
background-color: #095fbf;
|
|
}
|
|
|
|
#signature-image {
|
|
border: 1px solid #000000;
|
|
opacity: 0;
|
|
transition: opacity 0.5s ease-in-out;
|
|
}
|
|
|
|
</style>
|
|
</html> |