vumichien's picture
Up
f7596a7
raw
history blame
11.8 kB
<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Uploader</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.upload-container {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}
.image-card {
margin-bottom: 20px;
transition: transform 0.3s;
}
.image-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
.copy-btn {
cursor: pointer;
}
.image-preview {
max-height: 200px;
object-fit: cover;
width: 100%;
}
.code-container {
background-color: #f5f5f5;
padding: 10px;
border-radius: 4px;
margin-top: 10px;
font-family: monospace;
font-size: 0.8rem;
overflow-x: auto;
white-space: nowrap;
}
.hidden {
display: none;
}
#uploadProgress {
margin-top: 10px;
}
.success-message {
background-color: #d1e7dd;
color: #0f5132;
padding: 15px;
border-radius: 4px;
margin-top: 15px;
}
</style>
</head>
<body>
<div class="container py-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>🖼️ Image Uploader & Embed Link Generator</h1>
<a href="/logout" class="btn btn-outline-danger">Logout</a>
</div>
<div class="upload-container">
<h2>Upload New Image</h2>
<form id="uploadForm" enctype="multipart/form-data">
<div class="mb-3">
<label for="file" class="form-label">Select an image to upload</label>
<input class="form-control" type="file" id="file" name="file" accept="image/*">
</div>
<button type="submit" class="btn btn-primary">Upload & Generate Links</button>
</form>
<div id="uploadProgress" class="progress hidden">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div>
</div>
<div id="uploadResult" class="hidden success-message">
<h4>✅ Upload Successful!</h4>
<div id="previewContainer" class="mt-3">
<img id="imagePreview" class="img-fluid img-thumbnail mb-3" alt="Uploaded image preview">
</div>
<h5>Embed Options:</h5>
<div class="mb-3">
<h6>1. Direct URL (hosted on this server)</h6>
<div class="input-group">
<input type="text" id="directUrl" class="form-control" readonly>
<button class="btn btn-outline-secondary copy-btn" data-target="directUrl">Copy</button>
</div>
</div>
<div class="mb-3">
<h6>2. HTML Embed Code</h6>
<div class="code-container" id="htmlEmbed"></div>
<button class="btn btn-sm btn-outline-secondary mt-2 copy-btn" data-target="htmlEmbed">Copy HTML</button>
</div>
<div class="mb-3">
<h6>3. Base64 Embed (works anywhere without hosting)</h6>
<div class="code-container" id="base64Embed">Base64 code is truncated for display. Use the copy button to get the full code.</div>
<button class="btn btn-sm btn-outline-secondary mt-2 copy-btn" data-target="base64Embed">Copy Base64 HTML</button>
</div>
<p class="text-muted mt-3">
<small>Tip: Base64 encoding embeds the image directly in your HTML, so you don't need to host the image separately.</small>
</p>
</div>
</div>
<div class="row">
<div class="col-12">
<h2 class="mb-4">Your Uploaded Images</h2>
{% if not uploaded_images %}
<div class="alert alert-info">
No images have been uploaded yet. Upload your first image above!
</div>
{% else %}
<div class="row">
{% for image in uploaded_images %}
<div class="col-md-4">
<div class="card image-card">
<img src="{{ image.url }}" class="card-img-top image-preview" alt="{{ image.name }}">
<div class="card-body">
<h5 class="card-title text-truncate">{{ image.name }}</h5>
<div class="d-flex justify-content-between mt-3">
<a href="/view/{{ image.name }}" class="btn btn-sm btn-primary">View Details</a>
<button class="btn btn-sm btn-danger delete-btn" data-filename="{{ image.name }}">Delete</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const uploadForm = document.getElementById('uploadForm');
const uploadProgress = document.getElementById('uploadProgress');
const progressBar = uploadProgress.querySelector('.progress-bar');
const uploadResult = document.getElementById('uploadResult');
// Handle form submission
uploadForm.addEventListener('submit', function(e) {
e.preventDefault();
const fileInput = document.getElementById('file');
if (!fileInput.files.length) {
alert('Please select a file to upload.');
return;
}
const file = fileInput.files[0];
const formData = new FormData();
formData.append('file', file);
// Show progress
uploadProgress.classList.remove('hidden');
progressBar.style.width = '0%';
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100;
progressBar.style.width = percentComplete + '%';
}
});
xhr.addEventListener('load', function() {
if (xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
// Update preview
document.getElementById('imagePreview').src = response.file_url;
document.getElementById('directUrl').value = window.location.origin + response.file_url;
document.getElementById('htmlEmbed').textContent = response.embed_html;
// Store the full base64 embed, but display truncated version
const base64EmbedEl = document.getElementById('base64Embed');
base64EmbedEl.textContent = 'Base64 embed code (truncated for display): ' +
response.base64_embed.substring(0, 50) + '...' +
response.base64_embed.substring(response.base64_embed.length - 20);
base64EmbedEl.dataset.fullCode = response.base64_embed;
// Show results
uploadResult.classList.remove('hidden');
// Reset form for next upload
uploadForm.reset();
// Refresh the page after 2 seconds to show the new image in the gallery
setTimeout(() => {
window.location.reload();
}, 2000);
} else {
alert('Upload failed. Please try again.');
}
uploadProgress.classList.add('hidden');
});
xhr.addEventListener('error', function() {
alert('Upload failed. Please try again.');
uploadProgress.classList.add('hidden');
});
xhr.open('POST', '/upload/');
xhr.send(formData);
});
// Handle copy buttons
document.querySelectorAll('.copy-btn').forEach(function(btn) {
btn.addEventListener('click', function() {
const targetId = this.dataset.target;
const targetEl = document.getElementById(targetId);
let textToCopy;
if (targetEl.tagName === 'INPUT') {
textToCopy = targetEl.value;
} else {
// For base64, we have the full code in the data attribute
textToCopy = targetEl.dataset.fullCode || targetEl.textContent;
}
navigator.clipboard.writeText(textToCopy).then(() => {
// Change button text temporarily
const originalText = this.textContent;
this.textContent = 'Copied!';
setTimeout(() => {
this.textContent = originalText;
}, 1500);
}).catch(err => {
console.error('Failed to copy text: ', err);
});
});
});
// Handle delete buttons
document.querySelectorAll('.delete-btn').forEach(function(btn) {
btn.addEventListener('click', function() {
const filename = this.dataset.filename;
if (confirm(`Are you sure you want to delete ${filename}?`)) {
fetch(`/delete/${filename}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
if (data.success) {
window.location.reload();
} else {
alert('Failed to delete the image.');
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred while deleting the image.');
});
}
});
});
});
</script>
</body>
</html>