diff --git a/mobile_upload.php b/mobile_upload.php
index e0a96a6..901f056 100644
--- a/mobile_upload.php
+++ b/mobile_upload.php
@@ -187,12 +187,142 @@ body {
padding: 14px 16px;
border-radius: 8px;
text-align: center;
- z-index: 100;
+ z-index: 2000;
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
animation: slideDown 0.2s;
}
.toast.error { background: #d9534f; }
@keyframes slideDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; } }
+
+/* Kamera-View Styles */
+.camera-view {
+ position: fixed;
+ inset: 0;
+ background: #000;
+ z-index: 1000;
+ display: flex;
+ flex-direction: column;
+}
+.camera-view.hidden { display: none; }
+
+#camera-stream {
+ flex: 1;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+#camera-canvas {
+ display: none;
+}
+
+.camera-controls {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 24px 20px 40px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: linear-gradient(transparent, rgba(0,0,0,0.7));
+}
+
+.camera-btn-close,
+.camera-btn-switch {
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ border: none;
+ background: rgba(255,255,255,0.2);
+ color: #fff;
+ font-size: 22px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.camera-btn-close:active,
+.camera-btn-switch:active {
+ background: rgba(255,255,255,0.4);
+}
+
+.camera-shutter {
+ width: 72px;
+ height: 72px;
+ border-radius: 50%;
+ border: 4px solid #fff;
+ background: rgba(255,255,255,0.3);
+ cursor: pointer;
+ position: relative;
+}
+.camera-shutter::after {
+ content: '';
+ position: absolute;
+ inset: 6px;
+ border-radius: 50%;
+ background: #fff;
+ transition: transform 0.1s;
+}
+.camera-shutter:active::after {
+ transform: scale(0.9);
+}
+
+.camera-counter {
+ position: absolute;
+ top: 16px;
+ right: 16px;
+ background: rgba(0,0,0,0.5);
+ color: #5cb85c;
+ padding: 8px 14px;
+ border-radius: 20px;
+ font-size: 14px;
+ font-weight: 600;
+}
+
+/* Foto-Vorschau */
+.photo-preview {
+ position: absolute;
+ inset: 0;
+ background: #000;
+ display: flex;
+ flex-direction: column;
+}
+.photo-preview.hidden { display: none; }
+
+.photo-preview img {
+ flex: 1;
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+}
+
+.preview-buttons {
+ display: flex;
+ gap: 12px;
+ padding: 20px;
+ background: rgba(0,0,0,0.8);
+}
+
+.btn-discard,
+.btn-save {
+ flex: 1;
+ padding: 16px;
+ font-size: 16px;
+ font-weight: 600;
+ border-radius: 10px;
+ border: none;
+ cursor: pointer;
+}
+.btn-discard {
+ background: #444;
+ color: #fff;
+}
+.btn-discard:active { background: #555; }
+.btn-save {
+ background: #5cb85c;
+ color: #fff;
+}
+.btn-save:active { background: #4cae4c; }
@@ -204,13 +334,37 @@ body {
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
0 Fotos
+
+
+
+
![]()
+
+
+
+
+
+
+
@@ -223,22 +377,204 @@ const TOKEN = ;
const URL_UPLOAD = window.location.pathname + '?token=' + encodeURIComponent(TOKEN);
let uploadedCount = 0;
-const cameraInput = document.getElementById('camera-input');
+// DOM-Elemente
const galleryInput = document.getElementById('gallery-input');
const uploadingArea = document.getElementById('uploading-area');
const uploadedList = document.getElementById('uploaded-list');
const uploadedCountEl = document.getElementById('uploaded-count');
-[cameraInput, galleryInput].forEach(inp => {
- inp.addEventListener('change', async () => {
- if (!inp.files || !inp.files.length) return;
- for (const file of inp.files) {
- await uploadFile(file);
- }
- inp.value = '';
- });
+// Kamera-Elemente
+const openCameraBtn = document.getElementById('open-camera-btn');
+const cameraView = document.getElementById('camera-view');
+const cameraStream = document.getElementById('camera-stream');
+const cameraCanvas = document.getElementById('camera-canvas');
+const cameraClose = document.getElementById('camera-close');
+const cameraShutter = document.getElementById('camera-shutter');
+const cameraSwitch = document.getElementById('camera-switch');
+const cameraCounter = document.getElementById('camera-counter');
+const photoPreview = document.getElementById('photo-preview');
+const previewImage = document.getElementById('preview-image');
+const previewDiscard = document.getElementById('preview-discard');
+const previewSave = document.getElementById('preview-save');
+
+// Kamera-Status
+let mediaStream = null;
+let currentFacingMode = 'environment'; // 'environment' = Rückkamera, 'user' = Frontkamera
+let currentPhotoBlob = null;
+let cameraSessionCount = 0;
+
+// Galerie-Input Handler
+galleryInput.addEventListener('change', async () => {
+ if (!galleryInput.files || !galleryInput.files.length) return;
+ for (const file of galleryInput.files) {
+ await uploadFile(file);
+ }
+ galleryInput.value = '';
});
+// === KAMERA-FUNKTIONEN ===
+
+// Kamera öffnen
+openCameraBtn.addEventListener('click', openCameraView);
+
+async function openCameraView() {
+ try {
+ const constraints = {
+ video: {
+ facingMode: currentFacingMode,
+ width: { ideal: 1920 },
+ height: { ideal: 1080 }
+ },
+ audio: false
+ };
+ mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
+ cameraStream.srcObject = mediaStream;
+ cameraView.classList.remove('hidden');
+ cameraSessionCount = 0;
+ updateCameraCounter();
+ document.body.style.overflow = 'hidden';
+ } catch (err) {
+ console.error('Kamera-Fehler:', err);
+ toast('Kamera nicht verfügbar: ' + err.message, true);
+ }
+}
+
+// Kamera schließen
+cameraClose.addEventListener('click', closeCameraView);
+
+function closeCameraView() {
+ if (mediaStream) {
+ mediaStream.getTracks().forEach(track => track.stop());
+ mediaStream = null;
+ }
+ cameraStream.srcObject = null;
+ cameraView.classList.add('hidden');
+ photoPreview.classList.add('hidden');
+ document.body.style.overflow = '';
+}
+
+// Foto aufnehmen
+cameraShutter.addEventListener('click', takePhoto);
+
+function takePhoto() {
+ if (!mediaStream) return;
+
+ const video = cameraStream;
+ const canvas = cameraCanvas;
+
+ // Canvas auf Video-Größe setzen
+ canvas.width = video.videoWidth;
+ canvas.height = video.videoHeight;
+
+ // Video-Frame auf Canvas zeichnen
+ const ctx = canvas.getContext('2d');
+ ctx.drawImage(video, 0, 0);
+
+ // Canvas zu Blob konvertieren
+ canvas.toBlob((blob) => {
+ if (blob) {
+ currentPhotoBlob = blob;
+ showPreview(blob);
+ }
+ }, 'image/jpeg', 0.85);
+}
+
+// Vorschau anzeigen
+function showPreview(blob) {
+ const url = URL.createObjectURL(blob);
+ previewImage.onload = () => URL.revokeObjectURL(url);
+ previewImage.src = url;
+ photoPreview.classList.remove('hidden');
+}
+
+// Foto verwerfen
+previewDiscard.addEventListener('click', discardPhoto);
+
+function discardPhoto() {
+ currentPhotoBlob = null;
+ photoPreview.classList.add('hidden');
+}
+
+// Foto speichern und Kamera offen lassen
+previewSave.addEventListener('click', savePhoto);
+
+async function savePhoto() {
+ if (!currentPhotoBlob) return;
+
+ // Vorschau schließen, zurück zur Kamera
+ photoPreview.classList.add('hidden');
+
+ // Upload im Hintergrund
+ const blob = currentPhotoBlob;
+ currentPhotoBlob = null;
+
+ const fname = 'foto_' + Date.now() + '.jpg';
+ const fd = new FormData();
+ fd.append('token', TOKEN);
+ fd.append('file', blob, fname);
+
+ try {
+ const r = await fetch(URL_UPLOAD, { method: 'POST', body: fd });
+ const data = await r.json();
+ if (data.success) {
+ uploadedCount++;
+ cameraSessionCount++;
+ uploadedCountEl.textContent = '(' + uploadedCount + ')';
+ updateCameraCounter();
+
+ // Item zur Liste hinzufügen
+ const item = document.createElement('div');
+ item.className = 'uploaded-item';
+ item.innerHTML = '✓' + escapeHtml(data.filename) + '';
+ uploadedList.prepend(item);
+
+ toast('Foto gespeichert');
+ } else {
+ toast('Fehler: ' + (data.error || 'unbekannt'), true);
+ }
+ } catch (e) {
+ toast('Netzwerkfehler', true);
+ }
+}
+
+// Kamera wechseln (Front/Back)
+cameraSwitch.addEventListener('click', switchCamera);
+
+async function switchCamera() {
+ currentFacingMode = currentFacingMode === 'environment' ? 'user' : 'environment';
+
+ // Alten Stream stoppen
+ if (mediaStream) {
+ mediaStream.getTracks().forEach(track => track.stop());
+ }
+
+ // Neuen Stream starten
+ try {
+ const constraints = {
+ video: {
+ facingMode: currentFacingMode,
+ width: { ideal: 1920 },
+ height: { ideal: 1080 }
+ },
+ audio: false
+ };
+ mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
+ cameraStream.srcObject = mediaStream;
+ } catch (err) {
+ console.error('Kamera-Wechsel fehlgeschlagen:', err);
+ toast('Kamera-Wechsel fehlgeschlagen', true);
+ // Zurück zur anderen Kamera
+ currentFacingMode = currentFacingMode === 'environment' ? 'user' : 'environment';
+ }
+}
+
+function updateCameraCounter() {
+ const text = cameraSessionCount === 1 ? '1 Foto' : cameraSessionCount + ' Fotos';
+ cameraCounter.textContent = text;
+}
+
+// === UPLOAD-FUNKTIONEN ===
+
async function uploadFile(file) {
const blob = await resizeImage(file, 2000);
const fname = file.name || 'photo.jpg';