V1.1 Gut!
This commit is contained in:
@@ -139,6 +139,7 @@ body {
|
|||||||
background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);
|
background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
border: 2px solid #667eea;
|
border: 2px solid #667eea;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-button {
|
.nav-button {
|
||||||
@@ -171,6 +172,32 @@ body {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-jump {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-left: 10px;
|
||||||
|
padding-left: 15px;
|
||||||
|
border-left: 2px solid rgba(102, 126, 234, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#jumpToPageInput {
|
||||||
|
width: 60px;
|
||||||
|
padding: 8px;
|
||||||
|
border: 2px solid #667eea;
|
||||||
|
border-radius: 6px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
#jumpToPageInput:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #764ba2;
|
||||||
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
.page-selector label {
|
.page-selector label {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -222,6 +249,46 @@ body {
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.signature-placeholder {
|
||||||
|
background: white;
|
||||||
|
border: 2px dashed #667eea;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
max-width: 400px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-icon {
|
||||||
|
font-size: 1.8em;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-text {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #667eea;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
#placeholderSignatureImage {
|
||||||
|
max-width: 200px;
|
||||||
|
max-height: 80px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.signature-overlay {
|
.signature-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 3px dashed #667eea;
|
border: 3px dashed #667eea;
|
||||||
|
|||||||
@@ -44,6 +44,18 @@
|
|||||||
<button class="nav-button" id="prevPageBtn" onclick="changePage(-1)">◀ Zurück</button>
|
<button class="nav-button" id="prevPageBtn" onclick="changePage(-1)">◀ Zurück</button>
|
||||||
<span id="pageInfo">Seite 1 von 1</span>
|
<span id="pageInfo">Seite 1 von 1</span>
|
||||||
<button class="nav-button" id="nextPageBtn" onclick="changePage(1)">Weiter ▶</button>
|
<button class="nav-button" id="nextPageBtn" onclick="changePage(1)">Weiter ▶</button>
|
||||||
|
<div class="page-jump">
|
||||||
|
<input type="number" id="jumpToPageInput" min="1" max="1" placeholder="Seite" style="width: 60px; padding: 5px; border: 1px solid #667eea; border-radius: 4px; text-align: center;">
|
||||||
|
<button class="nav-button" id="jumpToPageBtn" onclick="jumpToPage()">Springe zu Seite</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="signature-placeholder" id="signaturePlaceholder" style="display: none;">
|
||||||
|
<div class="placeholder-content" id="placeholderContent">
|
||||||
|
<span class="placeholder-icon">✍️</span>
|
||||||
|
<span class="placeholder-text">Empfangene Unterschrift wird hier angezeigt</span>
|
||||||
|
</div>
|
||||||
|
<img id="placeholderSignatureImage" src="" alt="Unterschrift" style="display: none;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pdf-preview" id="pdfPreview">
|
<div class="pdf-preview" id="pdfPreview">
|
||||||
|
|||||||
465
js/master.js
465
js/master.js
@@ -114,6 +114,27 @@ document.getElementById('pdfInput').addEventListener('change', async (e) => {
|
|||||||
document.getElementById('uploadSection').style.display = 'none';
|
document.getElementById('uploadSection').style.display = 'none';
|
||||||
document.getElementById('statusSection').style.display = 'block';
|
document.getElementById('statusSection').style.display = 'block';
|
||||||
|
|
||||||
|
// Show signature placeholder
|
||||||
|
document.getElementById('signaturePlaceholder').style.display = 'block';
|
||||||
|
|
||||||
|
// If signature already exists, make sure overlay will be visible
|
||||||
|
if (signatureDataUrl) {
|
||||||
|
const overlay = document.getElementById('signatureOverlay');
|
||||||
|
if (overlay) {
|
||||||
|
// Ensure overlay is ready to be shown
|
||||||
|
overlay.style.display = 'none'; // Reset first
|
||||||
|
overlay.classList.remove('show'); // Reset class
|
||||||
|
// Reset position to default when loading new PDF
|
||||||
|
signaturePos.x = 0.5;
|
||||||
|
signaturePos.y = 0.1;
|
||||||
|
console.log('Overlay zurückgesetzt für neues PDF-Laden, Position auf Standard zurückgesetzt');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Reset position to default if no signature
|
||||||
|
signaturePos.x = 0.5;
|
||||||
|
signaturePos.y = 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
// Render PDF preview using the copy
|
// Render PDF preview using the copy
|
||||||
await renderPdfPreview(renderCopy);
|
await renderPdfPreview(renderCopy);
|
||||||
|
|
||||||
@@ -174,6 +195,111 @@ async function renderPdfPreview(pdfBytes, pageNum = 1) {
|
|||||||
// Update page navigation
|
// Update page navigation
|
||||||
updatePageNavigation();
|
updatePageNavigation();
|
||||||
|
|
||||||
|
// If signature was already received, show it again after PDF loads
|
||||||
|
setTimeout(() => {
|
||||||
|
const overlay = document.getElementById('signatureOverlay');
|
||||||
|
const placeholder = document.getElementById('signaturePlaceholder');
|
||||||
|
|
||||||
|
if (signatureDataUrl && overlay) {
|
||||||
|
// Signature was already received, restore it
|
||||||
|
console.log('=== Unterschrift war bereits vorhanden, zeige sie wieder an ===');
|
||||||
|
const img = document.getElementById('signatureImage');
|
||||||
|
const placeholderImg = document.getElementById('placeholderSignatureImage');
|
||||||
|
const placeholderContent = document.getElementById('placeholderContent');
|
||||||
|
|
||||||
|
// Show in placeholder
|
||||||
|
placeholder.style.display = 'block';
|
||||||
|
placeholderImg.src = signatureDataUrl;
|
||||||
|
placeholderImg.style.display = 'block';
|
||||||
|
placeholderContent.style.display = 'none';
|
||||||
|
|
||||||
|
// Show in overlay - IMPORTANT: Set image src FIRST, then show overlay
|
||||||
|
img.src = signatureDataUrl;
|
||||||
|
|
||||||
|
// Ensure overlay is visible
|
||||||
|
overlay.classList.add('show');
|
||||||
|
overlay.style.display = 'block'; // Force display
|
||||||
|
|
||||||
|
console.log('Overlay show-Klasse hinzugefügt:', overlay.classList.contains('show'));
|
||||||
|
|
||||||
|
// Position overlay (only if single page view, not all pages)
|
||||||
|
const pdfPreview = document.getElementById('pdfPreview');
|
||||||
|
if (!pdfPreview.classList.contains('scrollable')) {
|
||||||
|
// Single page view - position overlay
|
||||||
|
const canvas = document.getElementById('pdfCanvas');
|
||||||
|
if (canvas && canvas.clientWidth > 0 && canvas.clientHeight > 0) {
|
||||||
|
const overlayWidth = 250;
|
||||||
|
// Calculate position and ensure it's within bounds
|
||||||
|
let leftPos = canvas.clientWidth * signaturePos.x - overlayWidth / 2;
|
||||||
|
let topPos = canvas.clientHeight * signaturePos.y;
|
||||||
|
|
||||||
|
// Clamp position to visible area
|
||||||
|
leftPos = Math.max(0, Math.min(leftPos, canvas.clientWidth - overlayWidth));
|
||||||
|
topPos = Math.max(0, Math.min(topPos, canvas.clientHeight - 100)); // 100px min height for overlay
|
||||||
|
|
||||||
|
overlay.style.left = leftPos + 'px';
|
||||||
|
overlay.style.top = topPos + 'px';
|
||||||
|
overlay.style.position = 'absolute';
|
||||||
|
overlay.style.zIndex = '10';
|
||||||
|
|
||||||
|
// Update signaturePos with clamped values
|
||||||
|
signaturePos.x = (leftPos + overlayWidth / 2) / canvas.clientWidth;
|
||||||
|
signaturePos.y = topPos / canvas.clientHeight;
|
||||||
|
|
||||||
|
console.log('Overlay positioniert nach PDF-Laden:', overlay.style.left, overlay.style.top);
|
||||||
|
console.log('Canvas Größe:', canvas.clientWidth, 'x', canvas.clientHeight);
|
||||||
|
console.log('Overlay sichtbar:', overlay.classList.contains('show'), 'display:', overlay.style.display);
|
||||||
|
} else {
|
||||||
|
// Retry positioning - canvas might not be ready yet
|
||||||
|
console.log('Canvas noch nicht bereit, versuche erneut...');
|
||||||
|
setTimeout(() => {
|
||||||
|
const canvas = document.getElementById('pdfCanvas');
|
||||||
|
if (canvas && canvas.clientWidth > 0) {
|
||||||
|
const overlayWidth = 250;
|
||||||
|
// Calculate position and ensure it's within bounds
|
||||||
|
let leftPos = canvas.clientWidth * signaturePos.x - overlayWidth / 2;
|
||||||
|
let topPos = canvas.clientHeight * signaturePos.y;
|
||||||
|
|
||||||
|
// Clamp position to visible area
|
||||||
|
leftPos = Math.max(0, Math.min(leftPos, canvas.clientWidth - overlayWidth));
|
||||||
|
topPos = Math.max(0, Math.min(topPos, canvas.clientHeight - 100));
|
||||||
|
|
||||||
|
overlay.style.left = leftPos + 'px';
|
||||||
|
overlay.style.top = topPos + 'px';
|
||||||
|
overlay.style.position = 'absolute';
|
||||||
|
overlay.style.zIndex = '10';
|
||||||
|
overlay.classList.add('show');
|
||||||
|
overlay.style.display = 'block';
|
||||||
|
|
||||||
|
// Update signaturePos with clamped values
|
||||||
|
signaturePos.x = (leftPos + overlayWidth / 2) / canvas.clientWidth;
|
||||||
|
signaturePos.y = topPos / canvas.clientHeight;
|
||||||
|
|
||||||
|
console.log('Overlay positioniert (2. Versuch nach PDF-Laden):', overlay.style.left, overlay.style.top);
|
||||||
|
console.log('Overlay sichtbar:', overlay.classList.contains('show'));
|
||||||
|
} else {
|
||||||
|
console.error('Canvas auch nach Retry nicht gefunden!');
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// All pages view - hide overlay
|
||||||
|
overlay.classList.remove('show');
|
||||||
|
overlay.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show controls
|
||||||
|
document.getElementById('signatureControls').classList.add('show');
|
||||||
|
} else {
|
||||||
|
// No signature yet, show placeholder
|
||||||
|
if (!overlay.classList.contains('show')) {
|
||||||
|
placeholder.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
placeholder.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 300); // Increased delay to ensure canvas is ready
|
||||||
|
|
||||||
console.log('PDF Seite', pageNum, 'erfolgreich gerendert');
|
console.log('PDF Seite', pageNum, 'erfolgreich gerendert');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Rendern des PDFs:', error);
|
console.error('Fehler beim Rendern des PDFs:', error);
|
||||||
@@ -207,6 +333,14 @@ async function renderAllPages(pdfBytes) {
|
|||||||
const pdfPreview = document.getElementById('pdfPreview');
|
const pdfPreview = document.getElementById('pdfPreview');
|
||||||
pdfPreview.classList.add('scrollable');
|
pdfPreview.classList.add('scrollable');
|
||||||
|
|
||||||
|
// Hide signature overlay when showing all pages
|
||||||
|
const overlay = document.getElementById('signatureOverlay');
|
||||||
|
if (overlay) {
|
||||||
|
overlay.classList.remove('show');
|
||||||
|
overlay.style.display = 'none';
|
||||||
|
console.log('Overlay ausgeblendet beim Rendern aller Seiten');
|
||||||
|
}
|
||||||
|
|
||||||
// Render all pages
|
// Render all pages
|
||||||
for (let pageNum = 1; pageNum <= numPages; pageNum++) {
|
for (let pageNum = 1; pageNum <= numPages; pageNum++) {
|
||||||
|
|
||||||
@@ -253,6 +387,7 @@ function updatePageNavigation() {
|
|||||||
const pageInfo = document.getElementById('pageInfo');
|
const pageInfo = document.getElementById('pageInfo');
|
||||||
const prevBtn = document.getElementById('prevPageBtn');
|
const prevBtn = document.getElementById('prevPageBtn');
|
||||||
const nextBtn = document.getElementById('nextPageBtn');
|
const nextBtn = document.getElementById('nextPageBtn');
|
||||||
|
const jumpInput = document.getElementById('jumpToPageInput');
|
||||||
const pageSelector = document.getElementById('pageSelector');
|
const pageSelector = document.getElementById('pageSelector');
|
||||||
const currentPageNumSpan = document.getElementById('currentPageNum');
|
const currentPageNumSpan = document.getElementById('currentPageNum');
|
||||||
|
|
||||||
@@ -262,6 +397,12 @@ function updatePageNavigation() {
|
|||||||
prevBtn.disabled = currentPageNum <= 1;
|
prevBtn.disabled = currentPageNum <= 1;
|
||||||
nextBtn.disabled = currentPageNum >= totalPages;
|
nextBtn.disabled = currentPageNum >= totalPages;
|
||||||
|
|
||||||
|
// Update jump input max value and placeholder
|
||||||
|
if (jumpInput) {
|
||||||
|
jumpInput.max = totalPages;
|
||||||
|
jumpInput.placeholder = `1-${totalPages}`;
|
||||||
|
}
|
||||||
|
|
||||||
// Show page selector if signature is visible
|
// Show page selector if signature is visible
|
||||||
if (document.getElementById('signatureOverlay').classList.contains('show')) {
|
if (document.getElementById('signatureOverlay').classList.contains('show')) {
|
||||||
pageSelector.style.display = 'block';
|
pageSelector.style.display = 'block';
|
||||||
@@ -273,12 +414,98 @@ function updatePageNavigation() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function jumpToPage() {
|
||||||
|
const jumpInput = document.getElementById('jumpToPageInput');
|
||||||
|
const pageNum = parseInt(jumpInput.value);
|
||||||
|
|
||||||
|
if (isNaN(pageNum) || pageNum < 1 || pageNum > totalPages) {
|
||||||
|
alert(`Bitte geben Sie eine gültige Seitennummer zwischen 1 und ${totalPages} ein.`);
|
||||||
|
jumpInput.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageNum === currentPageNum) {
|
||||||
|
jumpInput.value = '';
|
||||||
|
return; // Already on this page
|
||||||
|
}
|
||||||
|
|
||||||
|
await renderPdfPreview(originalPdfBytes, pageNum);
|
||||||
|
|
||||||
|
// Reposition overlay if signature is visible
|
||||||
|
setTimeout(() => {
|
||||||
|
const overlay = document.getElementById('signatureOverlay');
|
||||||
|
if (overlay && overlay.classList.contains('show')) {
|
||||||
|
const canvas = document.getElementById('pdfCanvas');
|
||||||
|
if (canvas && canvas.clientWidth > 0 && canvas.clientHeight > 0) {
|
||||||
|
const overlayWidth = 250;
|
||||||
|
// Calculate position and ensure it's within bounds
|
||||||
|
let leftPos = canvas.clientWidth * signaturePos.x - overlayWidth / 2;
|
||||||
|
let topPos = canvas.clientHeight * signaturePos.y;
|
||||||
|
|
||||||
|
// Clamp position to visible area
|
||||||
|
leftPos = Math.max(0, Math.min(leftPos, canvas.clientWidth - overlayWidth));
|
||||||
|
topPos = Math.max(0, Math.min(topPos, canvas.clientHeight - 100));
|
||||||
|
|
||||||
|
overlay.style.left = leftPos + 'px';
|
||||||
|
overlay.style.top = topPos + 'px';
|
||||||
|
overlay.style.position = 'absolute';
|
||||||
|
|
||||||
|
// Update signaturePos with clamped values
|
||||||
|
signaturePos.x = (leftPos + overlayWidth / 2) / canvas.clientWidth;
|
||||||
|
signaturePos.y = topPos / canvas.clientHeight;
|
||||||
|
|
||||||
|
console.log('Overlay nach Seitenwechsel positioniert:', overlay.style.left, overlay.style.top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// Send page change to signature station
|
||||||
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: 'page_change',
|
||||||
|
pageNum: pageNum,
|
||||||
|
totalPages: totalPages
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear input
|
||||||
|
jumpInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
async function changePage(direction) {
|
async function changePage(direction) {
|
||||||
const newPage = currentPageNum + direction;
|
const newPage = currentPageNum + direction;
|
||||||
if (newPage < 1 || newPage > totalPages) return;
|
if (newPage < 1 || newPage > totalPages) return;
|
||||||
|
|
||||||
await renderPdfPreview(originalPdfBytes, newPage);
|
await renderPdfPreview(originalPdfBytes, newPage);
|
||||||
|
|
||||||
|
// Reposition overlay if signature is visible
|
||||||
|
setTimeout(() => {
|
||||||
|
const overlay = document.getElementById('signatureOverlay');
|
||||||
|
if (overlay && overlay.classList.contains('show')) {
|
||||||
|
const canvas = document.getElementById('pdfCanvas');
|
||||||
|
if (canvas && canvas.clientWidth > 0 && canvas.clientHeight > 0) {
|
||||||
|
const overlayWidth = 250;
|
||||||
|
// Calculate position and ensure it's within bounds
|
||||||
|
let leftPos = canvas.clientWidth * signaturePos.x - overlayWidth / 2;
|
||||||
|
let topPos = canvas.clientHeight * signaturePos.y;
|
||||||
|
|
||||||
|
// Clamp position to visible area
|
||||||
|
leftPos = Math.max(0, Math.min(leftPos, canvas.clientWidth - overlayWidth));
|
||||||
|
topPos = Math.max(0, Math.min(topPos, canvas.clientHeight - 100));
|
||||||
|
|
||||||
|
overlay.style.left = leftPos + 'px';
|
||||||
|
overlay.style.top = topPos + 'px';
|
||||||
|
overlay.style.position = 'absolute';
|
||||||
|
|
||||||
|
// Update signaturePos with clamped values
|
||||||
|
signaturePos.x = (leftPos + overlayWidth / 2) / canvas.clientWidth;
|
||||||
|
signaturePos.y = topPos / canvas.clientHeight;
|
||||||
|
|
||||||
|
console.log('Overlay nach Seitenwechsel positioniert:', overlay.style.left, overlay.style.top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
// Send page change to signature station
|
// Send page change to signature station
|
||||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
@@ -308,12 +535,37 @@ function updatePageSelection() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Placeholder handling is now simpler - just show/hide
|
||||||
|
|
||||||
async function showSignatureOverlay(signatureDataUrl) {
|
async function showSignatureOverlay(signatureDataUrl) {
|
||||||
|
console.log('=== showSignatureOverlay aufgerufen ===');
|
||||||
|
console.log('signatureDataUrl Länge:', signatureDataUrl ? signatureDataUrl.length : 'NULL');
|
||||||
|
|
||||||
const overlay = document.getElementById('signatureOverlay');
|
const overlay = document.getElementById('signatureOverlay');
|
||||||
const img = document.getElementById('signatureImage');
|
const img = document.getElementById('signatureImage');
|
||||||
|
const placeholder = document.getElementById('signaturePlaceholder');
|
||||||
|
const placeholderImg = document.getElementById('placeholderSignatureImage');
|
||||||
|
const placeholderContent = document.getElementById('placeholderContent');
|
||||||
|
|
||||||
|
if (!overlay || !img || !placeholder) {
|
||||||
|
console.error('FEHLER: Overlay, img oder placeholder nicht gefunden!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show signature in placeholder first (always visible)
|
||||||
|
placeholder.style.display = 'block';
|
||||||
|
placeholderImg.src = signatureDataUrl;
|
||||||
|
placeholderImg.style.display = 'block';
|
||||||
|
placeholderContent.style.display = 'none';
|
||||||
|
|
||||||
|
console.log('Unterschrift im Platzhalter angezeigt');
|
||||||
|
|
||||||
|
// Also show in overlay for dragging on PDF
|
||||||
img.src = signatureDataUrl;
|
img.src = signatureDataUrl;
|
||||||
overlay.classList.add('show');
|
overlay.classList.add('show');
|
||||||
|
overlay.style.display = 'block'; // Force display
|
||||||
|
overlay.style.position = 'absolute'; // Ensure positioning works
|
||||||
|
overlay.style.zIndex = '10'; // Ensure it's on top
|
||||||
|
|
||||||
document.getElementById('signatureControls').classList.add('show');
|
document.getElementById('signatureControls').classList.add('show');
|
||||||
|
|
||||||
@@ -324,90 +576,159 @@ async function showSignatureOverlay(signatureDataUrl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('statusMessage').className = 'status signed';
|
document.getElementById('statusMessage').className = 'status signed';
|
||||||
document.getElementById('statusMessage').textContent = '✅ Unterschrift erhalten! Wähle die Seite(n) und positioniere sie.';
|
document.getElementById('statusMessage').textContent = '✅ Unterschrift erhalten! Unterschrift oben sichtbar. Ziehe sie auf das PDF.';
|
||||||
document.getElementById('downloadButton').disabled = true;
|
document.getElementById('downloadButton').disabled = true;
|
||||||
document.getElementById('downloadHint').style.display = 'none';
|
document.getElementById('downloadHint').style.display = 'none';
|
||||||
|
|
||||||
// Position signature initially (use clientWidth for displayed size)
|
// Initialize drag and resize handlers (only once)
|
||||||
const canvas = document.getElementById('pdfCanvas');
|
initializeDragAndResize();
|
||||||
overlay.style.left = (canvas.clientWidth * signaturePos.x) + 'px';
|
|
||||||
overlay.style.top = (canvas.clientHeight * signaturePos.y) + 'px';
|
|
||||||
|
|
||||||
// Make signature draggable
|
// Wait for canvas to be ready, then position signature overlay
|
||||||
makeDraggable(overlay);
|
setTimeout(() => {
|
||||||
makeResizable(overlay);
|
const canvas = document.getElementById('pdfCanvas');
|
||||||
|
if (canvas && canvas.clientWidth > 0 && canvas.clientHeight > 0) {
|
||||||
|
// Position signature overlay on PDF (centered horizontally, top area)
|
||||||
|
const overlayWidth = 250; // Initial width
|
||||||
|
// Calculate position and ensure it's within bounds
|
||||||
|
let leftPos = canvas.clientWidth * signaturePos.x - overlayWidth / 2;
|
||||||
|
let topPos = canvas.clientHeight * signaturePos.y;
|
||||||
|
|
||||||
|
// Clamp position to visible area
|
||||||
|
leftPos = Math.max(0, Math.min(leftPos, canvas.clientWidth - overlayWidth));
|
||||||
|
topPos = Math.max(0, Math.min(topPos, canvas.clientHeight - 100)); // 100px min height for overlay
|
||||||
|
|
||||||
|
overlay.style.left = leftPos + 'px';
|
||||||
|
overlay.style.top = topPos + 'px';
|
||||||
|
overlay.style.position = 'absolute';
|
||||||
|
|
||||||
|
// Update signaturePos with clamped values
|
||||||
|
signaturePos.x = (leftPos + overlayWidth / 2) / canvas.clientWidth;
|
||||||
|
signaturePos.y = topPos / canvas.clientHeight;
|
||||||
|
|
||||||
|
console.log('Overlay positioniert bei:', overlay.style.left, overlay.style.top);
|
||||||
|
console.log('Canvas Größe:', canvas.clientWidth, 'x', canvas.clientHeight);
|
||||||
|
console.log('Overlay sichtbar:', overlay.classList.contains('show'));
|
||||||
|
} else {
|
||||||
|
console.error('Canvas nicht gefunden oder noch nicht geladen');
|
||||||
|
// Retry after a bit longer delay
|
||||||
|
setTimeout(() => {
|
||||||
|
const canvas = document.getElementById('pdfCanvas');
|
||||||
|
if (canvas && canvas.clientWidth > 0) {
|
||||||
|
const overlayWidth = 250;
|
||||||
|
// Calculate position and ensure it's within bounds
|
||||||
|
let leftPos = canvas.clientWidth * signaturePos.x - overlayWidth / 2;
|
||||||
|
let topPos = canvas.clientHeight * signaturePos.y;
|
||||||
|
|
||||||
|
// Clamp position to visible area
|
||||||
|
leftPos = Math.max(0, Math.min(leftPos, canvas.clientWidth - overlayWidth));
|
||||||
|
topPos = Math.max(0, Math.min(topPos, canvas.clientHeight - 100));
|
||||||
|
|
||||||
|
overlay.style.left = leftPos + 'px';
|
||||||
|
overlay.style.top = topPos + 'px';
|
||||||
|
overlay.style.position = 'absolute';
|
||||||
|
|
||||||
|
// Update signaturePos with clamped values
|
||||||
|
signaturePos.x = (leftPos + overlayWidth / 2) / canvas.clientWidth;
|
||||||
|
signaturePos.y = topPos / canvas.clientHeight;
|
||||||
|
|
||||||
|
console.log('Overlay positioniert (2. Versuch) bei:', overlay.style.left, overlay.style.top);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeDraggable(element) {
|
// Dragging state (shared across calls)
|
||||||
let isDragging = false;
|
let isDragging = false;
|
||||||
let startX, startY, initialX, initialY;
|
let isResizing = false;
|
||||||
|
let dragStartX, dragStartY, dragInitialX, dragInitialY;
|
||||||
|
let resizeStartX, resizeStartWidth;
|
||||||
|
|
||||||
element.addEventListener('mousedown', (e) => {
|
// Initialize dragging/resizing once
|
||||||
|
let dragInitialized = false;
|
||||||
|
|
||||||
|
function initializeDragAndResize() {
|
||||||
|
if (dragInitialized) return; // Only initialize once
|
||||||
|
|
||||||
|
const overlay = document.getElementById('signatureOverlay');
|
||||||
|
const resizeHandle = document.getElementById('resizeHandle');
|
||||||
|
|
||||||
|
if (!overlay || !resizeHandle) return;
|
||||||
|
|
||||||
|
// Make draggable
|
||||||
|
overlay.addEventListener('mousedown', (e) => {
|
||||||
if (e.target.classList.contains('resize-handle') || e.target.classList.contains('handle')) {
|
if (e.target.classList.contains('resize-handle') || e.target.classList.contains('handle')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
startX = e.clientX;
|
dragStartX = e.clientX;
|
||||||
startY = e.clientY;
|
dragStartY = e.clientY;
|
||||||
initialX = element.offsetLeft;
|
dragInitialX = overlay.offsetLeft;
|
||||||
initialY = element.offsetTop;
|
dragInitialY = overlay.offsetTop;
|
||||||
element.style.cursor = 'grabbing';
|
overlay.style.cursor = 'grabbing';
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('mousemove', (e) => {
|
document.addEventListener('mousemove', (e) => {
|
||||||
if (!isDragging) return;
|
if (isDragging && overlay.classList.contains('show')) {
|
||||||
|
const dx = e.clientX - dragStartX;
|
||||||
|
const dy = e.clientY - dragStartY;
|
||||||
|
overlay.style.left = (dragInitialX + dx) + 'px';
|
||||||
|
overlay.style.top = (dragInitialY + dy) + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
const dx = e.clientX - startX;
|
if (isResizing && overlay.classList.contains('show')) {
|
||||||
const dy = e.clientY - startY;
|
const dx = e.clientX - resizeStartX;
|
||||||
|
const newWidth = Math.max(100, resizeStartWidth + dx);
|
||||||
element.style.left = (initialX + dx) + 'px';
|
overlay.querySelector('img').style.maxWidth = newWidth + 'px';
|
||||||
element.style.top = (initialY + dy) + 'px';
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('mouseup', () => {
|
document.addEventListener('mouseup', () => {
|
||||||
if (isDragging) {
|
if (isDragging) {
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
element.style.cursor = 'move';
|
overlay.style.cursor = 'move';
|
||||||
updateSignaturePosition();
|
updateSignaturePosition();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeResizable(element) {
|
|
||||||
const resizeHandle = document.getElementById('resizeHandle');
|
|
||||||
let isResizing = false;
|
|
||||||
let startX, startWidth;
|
|
||||||
|
|
||||||
resizeHandle.addEventListener('mousedown', (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
isResizing = true;
|
|
||||||
startX = e.clientX;
|
|
||||||
startWidth = element.querySelector('img').width;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', (e) => {
|
|
||||||
if (!isResizing) return;
|
|
||||||
|
|
||||||
const dx = e.clientX - startX;
|
|
||||||
const newWidth = Math.max(100, startWidth + dx);
|
|
||||||
element.querySelector('img').style.maxWidth = newWidth + 'px';
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('mouseup', () => {
|
|
||||||
if (isResizing) {
|
if (isResizing) {
|
||||||
isResizing = false;
|
isResizing = false;
|
||||||
updateSignatureScale();
|
updateSignatureScale();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Make resizable
|
||||||
|
resizeHandle.addEventListener('mousedown', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
isResizing = true;
|
||||||
|
resizeStartX = e.clientX;
|
||||||
|
resizeStartWidth = overlay.querySelector('img').width;
|
||||||
|
});
|
||||||
|
|
||||||
|
dragInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeDraggable(element) {
|
||||||
|
// Function kept for compatibility, but actual initialization is in initializeDragAndResize
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeResizable(element) {
|
||||||
|
// Function kept for compatibility, but actual initialization is in initializeDragAndResize
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSignaturePosition() {
|
function updateSignaturePosition() {
|
||||||
const overlay = document.getElementById('signatureOverlay');
|
const overlay = document.getElementById('signatureOverlay');
|
||||||
const canvas = document.getElementById('pdfCanvas');
|
const canvas = document.getElementById('pdfCanvas');
|
||||||
|
|
||||||
signaturePos.x = overlay.offsetLeft / canvas.width;
|
// Use clientWidth/clientHeight instead of width/height (display size vs internal size)
|
||||||
signaturePos.y = overlay.offsetTop / canvas.height;
|
const canvasDisplayWidth = canvas.clientWidth || canvas.width;
|
||||||
console.log('Neue Position:', signaturePos);
|
const canvasDisplayHeight = canvas.clientHeight || canvas.height;
|
||||||
|
|
||||||
|
if (canvasDisplayWidth > 0 && canvasDisplayHeight > 0) {
|
||||||
|
signaturePos.x = overlay.offsetLeft / canvasDisplayWidth;
|
||||||
|
signaturePos.y = overlay.offsetTop / canvasDisplayHeight;
|
||||||
|
console.log('Neue Position:', signaturePos, 'Canvas Display:', canvasDisplayWidth, 'x', canvasDisplayHeight);
|
||||||
|
} else {
|
||||||
|
console.error('Canvas Display Größe ist 0!');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSignatureScale() {
|
function updateSignatureScale() {
|
||||||
@@ -527,16 +848,39 @@ async function addSignatureToPdf() {
|
|||||||
|
|
||||||
function removeSignatureOverlay() {
|
function removeSignatureOverlay() {
|
||||||
// Only remove the overlay, keep signedPdfBytes intact
|
// Only remove the overlay, keep signedPdfBytes intact
|
||||||
document.getElementById('signatureOverlay').classList.remove('show');
|
const overlay = document.getElementById('signatureOverlay');
|
||||||
|
overlay.classList.remove('show');
|
||||||
|
overlay.style.display = 'none'; // Explicitly hide overlay
|
||||||
document.getElementById('signatureControls').classList.remove('show');
|
document.getElementById('signatureControls').classList.remove('show');
|
||||||
|
|
||||||
|
// Reset placeholder to default state
|
||||||
|
const placeholder = document.getElementById('signaturePlaceholder');
|
||||||
|
const placeholderImg = document.getElementById('placeholderSignatureImage');
|
||||||
|
const placeholderContent = document.getElementById('placeholderContent');
|
||||||
|
placeholderImg.style.display = 'none';
|
||||||
|
placeholderContent.style.display = 'flex';
|
||||||
|
placeholder.style.display = 'block';
|
||||||
|
|
||||||
signatureDataUrl = null;
|
signatureDataUrl = null;
|
||||||
console.log('removeSignatureOverlay() aufgerufen - signedPdfBytes:', signedPdfBytes ? signedPdfBytes.length + ' bytes' : 'NULL');
|
console.log('removeSignatureOverlay() aufgerufen - signedPdfBytes:', signedPdfBytes ? signedPdfBytes.length + ' bytes' : 'NULL');
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeSignature() {
|
function removeSignature() {
|
||||||
// Complete reset including signedPdfBytes
|
// Complete reset including signedPdfBytes
|
||||||
document.getElementById('signatureOverlay').classList.remove('show');
|
const overlay = document.getElementById('signatureOverlay');
|
||||||
|
overlay.classList.remove('show');
|
||||||
|
overlay.style.display = 'none'; // Explicitly hide overlay
|
||||||
document.getElementById('signatureControls').classList.remove('show');
|
document.getElementById('signatureControls').classList.remove('show');
|
||||||
|
|
||||||
|
// Reset placeholder to default state
|
||||||
|
const placeholder = document.getElementById('signaturePlaceholder');
|
||||||
|
const placeholderImg = document.getElementById('placeholderSignatureImage');
|
||||||
|
const placeholderContent = document.getElementById('placeholderContent');
|
||||||
|
placeholder.style.display = 'block';
|
||||||
|
placeholderImg.style.display = 'none';
|
||||||
|
placeholderImg.src = '';
|
||||||
|
placeholderContent.style.display = 'flex';
|
||||||
|
|
||||||
document.getElementById('statusMessage').className = 'status waiting';
|
document.getElementById('statusMessage').className = 'status waiting';
|
||||||
document.getElementById('statusMessage').textContent = 'Warte auf Unterschrift von der Signatur-Station...';
|
document.getElementById('statusMessage').textContent = 'Warte auf Unterschrift von der Signatur-Station...';
|
||||||
document.getElementById('downloadButton').disabled = true;
|
document.getElementById('downloadButton').disabled = true;
|
||||||
@@ -753,5 +1097,20 @@ document.getElementById('discardButton').addEventListener('click', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialize drag and resize on load
|
||||||
|
initializeDragAndResize();
|
||||||
|
|
||||||
|
// Add Enter key handler for jump to page input
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const jumpInput = document.getElementById('jumpToPageInput');
|
||||||
|
if (jumpInput) {
|
||||||
|
jumpInput.addEventListener('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
jumpToPage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Connect WebSocket on load
|
// Connect WebSocket on load
|
||||||
connectWebSocket();
|
connectWebSocket();
|
||||||
|
|||||||
Reference in New Issue
Block a user