Spaces:
Running
Running
Add 1 files
Browse files- index.html +107 -42
index.html
CHANGED
@@ -4,7 +4,20 @@
|
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
<title>Local Image Viewer</title>
|
|
|
7 |
<script src="https://cdn.tailwindcss.com"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
9 |
<style>
|
10 |
.dropzone {
|
@@ -77,19 +90,19 @@
|
|
77 |
<div class="container mx-auto px-4 py-8">
|
78 |
<div class="max-w-6xl mx-auto">
|
79 |
<h1 class="text-3xl font-bold text-center text-gray-800 mb-2">Local Image Viewer</h1>
|
80 |
-
<p class="text-center text-gray-600 mb-8">View your local WebP, PNG, and
|
81 |
|
82 |
<div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8">
|
83 |
<!-- Dropzone area -->
|
84 |
-
<div id="dropzone" class="dropzone p-12 text-center cursor-pointer">
|
85 |
<div class="flex flex-col items-center justify-center">
|
86 |
-
<i class="fas fa-images text-5xl text-gray-400 mb-4"></i>
|
87 |
<h3 class="text-xl font-semibold text-gray-700 mb-2">Drag & Drop Images Here</h3>
|
88 |
<p class="text-gray-500 mb-4">or</p>
|
89 |
-
<button id="browseBtn" class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-lg transition">
|
90 |
Browse Files
|
91 |
</button>
|
92 |
-
<input type="file" id="fileInput" class="hidden" accept=".webp,.png,.jpg,.jpeg" multiple>
|
93 |
</div>
|
94 |
</div>
|
95 |
|
@@ -101,20 +114,20 @@
|
|
101 |
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
|
102 |
<h3 class="font-medium text-gray-700">Files (<span id="fileCount">0</span>)</h3>
|
103 |
<div class="relative">
|
104 |
-
<button id="sortBtn" class="text-gray-600 hover:text-gray-800">
|
105 |
-
<i class="fas fa-sort"></i>
|
106 |
</button>
|
107 |
<div id="sortDropdown" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg z-10 py-1">
|
108 |
-
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="name-asc">Name (A-Z)</div>
|
109 |
-
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="name-desc">Name (Z-A)</div>
|
110 |
-
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="size-asc">Size (Small to Large)</div>
|
111 |
-
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="size-desc">Size (Large to Small)</div>
|
112 |
-
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="date-asc">Date (Oldest First)</div>
|
113 |
-
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="date-desc">Date (Newest First)</div>
|
114 |
</div>
|
115 |
</div>
|
116 |
</div>
|
117 |
-
<ul id="fileList" class="divide-y divide-gray-200">
|
118 |
<!-- Files will be listed here -->
|
119 |
</ul>
|
120 |
</div>
|
@@ -123,12 +136,12 @@
|
|
123 |
<div class="w-full md:w-3/4 p-4 flex flex-col items-center justify-center">
|
124 |
<div class="relative w-full max-w-3xl">
|
125 |
<!-- Navigation buttons -->
|
126 |
-
<button id="prevBtn" class="nav-btn absolute left-0 top-1/2 -translate-y-1/2 bg-white/80 hover:bg-white text-gray-800 p-3 rounded-full shadow-md ml-4 z-10">
|
127 |
-
<i class="fas fa-chevron-left text-xl"></i>
|
128 |
</button>
|
129 |
|
130 |
-
<button id="nextBtn" class="nav-btn absolute right-0 top-1/2 -translate-y-1/2 bg-white/80 hover:bg-white text-gray-800 p-3 rounded-full shadow-md mr-4 z-10">
|
131 |
-
<i class="fas fa-chevron-right text-xl"></i>
|
132 |
</button>
|
133 |
|
134 |
<!-- Image display area -->
|
@@ -146,17 +159,17 @@
|
|
146 |
<p id="fileInfo" class="text-sm text-gray-500">-</p>
|
147 |
</div>
|
148 |
<div class="flex space-x-2">
|
149 |
-
<button id="zoomInBtn" class="nav-btn bg-gray-200 hover:bg-gray-300 text-gray-700 p-2 rounded">
|
150 |
-
<i class="fas fa-search-plus"></i>
|
151 |
</button>
|
152 |
-
<button id="zoomOutBtn" class="nav-btn bg-gray-200 hover:bg-gray-300 text-gray-700 p-2 rounded">
|
153 |
-
<i class="fas fa-search-minus"></i>
|
154 |
</button>
|
155 |
-
<button id="resetZoomBtn" class="nav-btn bg-gray-200 hover:bg-gray-300 text-gray-700 p-2 rounded">
|
156 |
-
<i class="fas fa-expand"></i>
|
157 |
</button>
|
158 |
-
<button id="fullscreenBtn" class="nav-btn bg-gray-200 hover:bg-gray-300 text-gray-700 p-2 rounded">
|
159 |
-
<i class="fas fa-expand-arrows-alt"></i>
|
160 |
</button>
|
161 |
</div>
|
162 |
</div>
|
@@ -178,23 +191,23 @@
|
|
178 |
|
179 |
<div class="text-center text-gray-500 text-sm mt-8">
|
180 |
<p>Use arrow keys to navigate between images</p>
|
181 |
-
<p class="mt-1">Supported formats: WebP, PNG, JPEG</p>
|
182 |
</div>
|
183 |
</div>
|
184 |
</div>
|
185 |
|
186 |
<!-- Fullscreen container -->
|
187 |
-
<div id="fullscreenContainer">
|
188 |
<img id="fullscreenImage" src="" alt="Fullscreen Image">
|
189 |
<div id="fullscreenControls">
|
190 |
-
<button id="fsPrevBtn" class="nav-btn bg-white/20 hover:bg-white/40 text-white p-3 rounded-full">
|
191 |
-
<i class="fas fa-chevron-left text-xl"></i>
|
192 |
</button>
|
193 |
-
<button id="fsCloseBtn" class="nav-btn bg-white/20 hover:bg-white/40 text-white p-3 rounded-full">
|
194 |
-
<i class="fas fa-times text-xl"></i>
|
195 |
</button>
|
196 |
-
<button id="fsNextBtn" class="nav-btn bg-white/20 hover:bg-white/40 text-white p-3 rounded-full">
|
197 |
-
<i class="fas fa-chevron-right text-xl"></i>
|
198 |
</button>
|
199 |
</div>
|
200 |
</div>
|
@@ -296,7 +309,8 @@
|
|
296 |
// Sort functionality
|
297 |
sortBtn.addEventListener('click', (e) => {
|
298 |
e.stopPropagation();
|
299 |
-
sortDropdown.classList.toggle('hidden');
|
|
|
300 |
});
|
301 |
|
302 |
document.querySelectorAll('.sort-option').forEach(option => {
|
@@ -306,12 +320,14 @@
|
|
306 |
updateFileList();
|
307 |
showImage(currentFileIndex);
|
308 |
sortDropdown.classList.add('hidden');
|
|
|
309 |
});
|
310 |
});
|
311 |
|
312 |
// Close dropdown when clicking outside
|
313 |
document.addEventListener('click', () => {
|
314 |
sortDropdown.classList.add('hidden');
|
|
|
315 |
});
|
316 |
|
317 |
// Fullscreen functionality
|
@@ -379,13 +395,26 @@
|
|
379 |
// Handle dropped or selected files
|
380 |
function handleFiles(newFiles) {
|
381 |
// Filter for supported image types
|
382 |
-
const supportedTypes = [
|
383 |
-
|
384 |
-
|
385 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
386 |
|
387 |
if (imageFiles.length === 0) {
|
388 |
-
alert('No supported image files found. Please upload WebP, PNG, or
|
389 |
return;
|
390 |
}
|
391 |
|
@@ -443,6 +472,7 @@
|
|
443 |
files.forEach((file, index) => {
|
444 |
const listItem = document.createElement('li');
|
445 |
listItem.className = `file-item cursor-pointer ${index === currentFileIndex ? 'active' : ''}`;
|
|
|
446 |
listItem.innerHTML = `
|
447 |
<div class="flex items-center p-3">
|
448 |
<div class="flex-shrink-0 h-10 w-10 rounded bg-gray-200 overflow-hidden">
|
@@ -459,6 +489,15 @@
|
|
459 |
showImage(index);
|
460 |
});
|
461 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
462 |
fileList.appendChild(listItem);
|
463 |
|
464 |
// Load thumbnail
|
@@ -484,8 +523,10 @@
|
|
484 |
document.querySelectorAll('.file-item').forEach((item, i) => {
|
485 |
if (i === index) {
|
486 |
item.classList.add('active');
|
|
|
487 |
} else {
|
488 |
item.classList.remove('active');
|
|
|
489 |
}
|
490 |
});
|
491 |
|
@@ -504,6 +545,7 @@
|
|
504 |
img.style.transform = `scale(${zoomLevel})`;
|
505 |
img.style.transformOrigin = 'center center';
|
506 |
img.style.transition = 'transform 0.2s ease';
|
|
|
507 |
|
508 |
// Add drag to pan functionality
|
509 |
let isDragging = false;
|
@@ -545,11 +587,11 @@
|
|
545 |
|
546 |
// Update file info
|
547 |
fileName.textContent = file.name;
|
548 |
-
fileInfo.textContent = `${file
|
549 |
|
550 |
// Load image dimensions after the image is loaded
|
551 |
img.onload = () => {
|
552 |
-
fileInfo.textContent = `${img.naturalWidth}×${img.naturalHeight} • ${file
|
553 |
};
|
554 |
};
|
555 |
reader.readAsDataURL(file);
|
@@ -559,6 +601,27 @@
|
|
559 |
nextBtn.disabled = index === files.length - 1;
|
560 |
}
|
561 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
562 |
// Navigation functions
|
563 |
function showPreviousImage() {
|
564 |
if (currentFileIndex > 0) {
|
@@ -612,11 +675,13 @@
|
|
612 |
fullscreenImage.src = img.src;
|
613 |
fullscreenContainer.style.display = 'flex';
|
614 |
document.body.style.overflow = 'hidden';
|
|
|
615 |
}
|
616 |
|
617 |
function closeFullscreen() {
|
618 |
fullscreenContainer.style.display = 'none';
|
619 |
document.body.style.overflow = '';
|
|
|
620 |
}
|
621 |
|
622 |
function updateFullscreenImage() {
|
|
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
<title>Local Image Viewer</title>
|
7 |
+
<!-- Local Tailwind CSS -->
|
8 |
<script src="https://cdn.tailwindcss.com"></script>
|
9 |
+
<script>
|
10 |
+
tailwind.config = {
|
11 |
+
theme: {
|
12 |
+
extend: {
|
13 |
+
colors: {
|
14 |
+
primary: '#3B82F6',
|
15 |
+
}
|
16 |
+
}
|
17 |
+
}
|
18 |
+
}
|
19 |
+
</script>
|
20 |
+
<!-- Local Font Awesome -->
|
21 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
22 |
<style>
|
23 |
.dropzone {
|
|
|
90 |
<div class="container mx-auto px-4 py-8">
|
91 |
<div class="max-w-6xl mx-auto">
|
92 |
<h1 class="text-3xl font-bold text-center text-gray-800 mb-2">Local Image Viewer</h1>
|
93 |
+
<p class="text-center text-gray-600 mb-8">View your local WebP, PNG, JPEG, AVIF, and HEIC files with ease</p>
|
94 |
|
95 |
<div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8">
|
96 |
<!-- Dropzone area -->
|
97 |
+
<div id="dropzone" class="dropzone p-12 text-center cursor-pointer" role="region" aria-label="File drop zone">
|
98 |
<div class="flex flex-col items-center justify-center">
|
99 |
+
<i class="fas fa-images text-5xl text-gray-400 mb-4" aria-hidden="true"></i>
|
100 |
<h3 class="text-xl font-semibold text-gray-700 mb-2">Drag & Drop Images Here</h3>
|
101 |
<p class="text-gray-500 mb-4">or</p>
|
102 |
+
<button id="browseBtn" class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-lg transition" aria-label="Browse files">
|
103 |
Browse Files
|
104 |
</button>
|
105 |
+
<input type="file" id="fileInput" class="hidden" accept=".webp,.png,.jpg,.jpeg,.avif,.heic,.heif" multiple>
|
106 |
</div>
|
107 |
</div>
|
108 |
|
|
|
114 |
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
|
115 |
<h3 class="font-medium text-gray-700">Files (<span id="fileCount">0</span>)</h3>
|
116 |
<div class="relative">
|
117 |
+
<button id="sortBtn" class="text-gray-600 hover:text-gray-800" aria-label="Sort options" aria-haspopup="true" aria-expanded="false">
|
118 |
+
<i class="fas fa-sort" aria-hidden="true"></i>
|
119 |
</button>
|
120 |
<div id="sortDropdown" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg z-10 py-1">
|
121 |
+
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="name-asc" role="menuitem">Name (A-Z)</div>
|
122 |
+
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="name-desc" role="menuitem">Name (Z-A)</div>
|
123 |
+
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="size-asc" role="menuitem">Size (Small to Large)</div>
|
124 |
+
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="size-desc" role="menuitem">Size (Large to Small)</div>
|
125 |
+
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="date-asc" role="menuitem">Date (Oldest First)</div>
|
126 |
+
<div class="sort-option px-4 py-2 text-sm text-gray-700 cursor-pointer" data-sort="date-desc" role="menuitem">Date (Newest First)</div>
|
127 |
</div>
|
128 |
</div>
|
129 |
</div>
|
130 |
+
<ul id="fileList" class="divide-y divide-gray-200" role="list">
|
131 |
<!-- Files will be listed here -->
|
132 |
</ul>
|
133 |
</div>
|
|
|
136 |
<div class="w-full md:w-3/4 p-4 flex flex-col items-center justify-center">
|
137 |
<div class="relative w-full max-w-3xl">
|
138 |
<!-- Navigation buttons -->
|
139 |
+
<button id="prevBtn" class="nav-btn absolute left-0 top-1/2 -translate-y-1/2 bg-white/80 hover:bg-white text-gray-800 p-3 rounded-full shadow-md ml-4 z-10" aria-label="Previous image">
|
140 |
+
<i class="fas fa-chevron-left text-xl" aria-hidden="true"></i>
|
141 |
</button>
|
142 |
|
143 |
+
<button id="nextBtn" class="nav-btn absolute right-0 top-1/2 -translate-y-1/2 bg-white/80 hover:bg-white text-gray-800 p-3 rounded-full shadow-md mr-4 z-10" aria-label="Next image">
|
144 |
+
<i class="fas fa-chevron-right text-xl" aria-hidden="true"></i>
|
145 |
</button>
|
146 |
|
147 |
<!-- Image display area -->
|
|
|
159 |
<p id="fileInfo" class="text-sm text-gray-500">-</p>
|
160 |
</div>
|
161 |
<div class="flex space-x-2">
|
162 |
+
<button id="zoomInBtn" class="nav-btn bg-gray-200 hover:bg-gray-300 text-gray-700 p-2 rounded" aria-label="Zoom in">
|
163 |
+
<i class="fas fa-search-plus" aria-hidden="true"></i>
|
164 |
</button>
|
165 |
+
<button id="zoomOutBtn" class="nav-btn bg-gray-200 hover:bg-gray-300 text-gray-700 p-2 rounded" aria-label="Zoom out">
|
166 |
+
<i class="fas fa-search-minus" aria-hidden="true"></i>
|
167 |
</button>
|
168 |
+
<button id="resetZoomBtn" class="nav-btn bg-gray-200 hover:bg-gray-300 text-gray-700 p-2 rounded" aria-label="Reset zoom">
|
169 |
+
<i class="fas fa-expand" aria-hidden="true"></i>
|
170 |
</button>
|
171 |
+
<button id="fullscreenBtn" class="nav-btn bg-gray-200 hover:bg-gray-300 text-gray-700 p-2 rounded" aria-label="Fullscreen">
|
172 |
+
<i class="fas fa-expand-arrows-alt" aria-hidden="true"></i>
|
173 |
</button>
|
174 |
</div>
|
175 |
</div>
|
|
|
191 |
|
192 |
<div class="text-center text-gray-500 text-sm mt-8">
|
193 |
<p>Use arrow keys to navigate between images</p>
|
194 |
+
<p class="mt-1">Supported formats: WebP, PNG, JPEG, AVIF, HEIC</p>
|
195 |
</div>
|
196 |
</div>
|
197 |
</div>
|
198 |
|
199 |
<!-- Fullscreen container -->
|
200 |
+
<div id="fullscreenContainer" role="dialog" aria-modal="true" aria-label="Fullscreen image viewer">
|
201 |
<img id="fullscreenImage" src="" alt="Fullscreen Image">
|
202 |
<div id="fullscreenControls">
|
203 |
+
<button id="fsPrevBtn" class="nav-btn bg-white/20 hover:bg-white/40 text-white p-3 rounded-full" aria-label="Previous image">
|
204 |
+
<i class="fas fa-chevron-left text-xl" aria-hidden="true"></i>
|
205 |
</button>
|
206 |
+
<button id="fsCloseBtn" class="nav-btn bg-white/20 hover:bg-white/40 text-white p-3 rounded-full" aria-label="Close fullscreen">
|
207 |
+
<i class="fas fa-times text-xl" aria-hidden="true"></i>
|
208 |
</button>
|
209 |
+
<button id="fsNextBtn" class="nav-btn bg-white/20 hover:bg-white/40 text-white p-3 rounded-full" aria-label="Next image">
|
210 |
+
<i class="fas fa-chevron-right text-xl" aria-hidden="true"></i>
|
211 |
</button>
|
212 |
</div>
|
213 |
</div>
|
|
|
309 |
// Sort functionality
|
310 |
sortBtn.addEventListener('click', (e) => {
|
311 |
e.stopPropagation();
|
312 |
+
const isExpanded = sortDropdown.classList.toggle('hidden');
|
313 |
+
sortBtn.setAttribute('aria-expanded', !isExpanded);
|
314 |
});
|
315 |
|
316 |
document.querySelectorAll('.sort-option').forEach(option => {
|
|
|
320 |
updateFileList();
|
321 |
showImage(currentFileIndex);
|
322 |
sortDropdown.classList.add('hidden');
|
323 |
+
sortBtn.setAttribute('aria-expanded', 'false');
|
324 |
});
|
325 |
});
|
326 |
|
327 |
// Close dropdown when clicking outside
|
328 |
document.addEventListener('click', () => {
|
329 |
sortDropdown.classList.add('hidden');
|
330 |
+
sortBtn.setAttribute('aria-expanded', 'false');
|
331 |
});
|
332 |
|
333 |
// Fullscreen functionality
|
|
|
395 |
// Handle dropped or selected files
|
396 |
function handleFiles(newFiles) {
|
397 |
// Filter for supported image types
|
398 |
+
const supportedTypes = [
|
399 |
+
'image/webp',
|
400 |
+
'image/png',
|
401 |
+
'image/jpeg',
|
402 |
+
'image/avif',
|
403 |
+
'image/heic',
|
404 |
+
'image/heif'
|
405 |
+
];
|
406 |
+
|
407 |
+
const imageFiles = Array.from(newFiles).filter(file => {
|
408 |
+
// Check MIME type first
|
409 |
+
if (supportedTypes.includes(file.type)) return true;
|
410 |
+
|
411 |
+
// Fallback for file extensions (some browsers might not recognize HEIC/AVIF MIME types)
|
412 |
+
const extension = file.name.split('.').pop().toLowerCase();
|
413 |
+
return ['webp', 'png', 'jpg', 'jpeg', 'avif', 'heic', 'heif'].includes(extension);
|
414 |
+
});
|
415 |
|
416 |
if (imageFiles.length === 0) {
|
417 |
+
alert('No supported image files found. Please upload WebP, PNG, JPEG, AVIF, or HEIC files.');
|
418 |
return;
|
419 |
}
|
420 |
|
|
|
472 |
files.forEach((file, index) => {
|
473 |
const listItem = document.createElement('li');
|
474 |
listItem.className = `file-item cursor-pointer ${index === currentFileIndex ? 'active' : ''}`;
|
475 |
+
listItem.setAttribute('role', 'listitem');
|
476 |
listItem.innerHTML = `
|
477 |
<div class="flex items-center p-3">
|
478 |
<div class="flex-shrink-0 h-10 w-10 rounded bg-gray-200 overflow-hidden">
|
|
|
489 |
showImage(index);
|
490 |
});
|
491 |
|
492 |
+
// Keyboard navigation for file list items
|
493 |
+
listItem.addEventListener('keydown', (e) => {
|
494 |
+
if (e.key === 'Enter' || e.key === ' ') {
|
495 |
+
e.preventDefault();
|
496 |
+
showImage(index);
|
497 |
+
}
|
498 |
+
});
|
499 |
+
|
500 |
+
listItem.setAttribute('tabindex', '0');
|
501 |
fileList.appendChild(listItem);
|
502 |
|
503 |
// Load thumbnail
|
|
|
523 |
document.querySelectorAll('.file-item').forEach((item, i) => {
|
524 |
if (i === index) {
|
525 |
item.classList.add('active');
|
526 |
+
item.setAttribute('aria-selected', 'true');
|
527 |
} else {
|
528 |
item.classList.remove('active');
|
529 |
+
item.setAttribute('aria-selected', 'false');
|
530 |
}
|
531 |
});
|
532 |
|
|
|
545 |
img.style.transform = `scale(${zoomLevel})`;
|
546 |
img.style.transformOrigin = 'center center';
|
547 |
img.style.transition = 'transform 0.2s ease';
|
548 |
+
img.setAttribute('alt', `Preview of ${file.name}`);
|
549 |
|
550 |
// Add drag to pan functionality
|
551 |
let isDragging = false;
|
|
|
587 |
|
588 |
// Update file info
|
589 |
fileName.textContent = file.name;
|
590 |
+
fileInfo.textContent = `${getFileType(file)} • ${formatFileSize(file.size)}`;
|
591 |
|
592 |
// Load image dimensions after the image is loaded
|
593 |
img.onload = () => {
|
594 |
+
fileInfo.textContent = `${img.naturalWidth}×${img.naturalHeight} • ${getFileType(file)} • ${formatFileSize(file.size)}`;
|
595 |
};
|
596 |
};
|
597 |
reader.readAsDataURL(file);
|
|
|
601 |
nextBtn.disabled = index === files.length - 1;
|
602 |
}
|
603 |
|
604 |
+
// Helper to get file type
|
605 |
+
function getFileType(file) {
|
606 |
+
if (file.type) {
|
607 |
+
const type = file.type.split('/')[1];
|
608 |
+
if (type) return type.toUpperCase();
|
609 |
+
}
|
610 |
+
|
611 |
+
// Fallback for file extension
|
612 |
+
const extension = file.name.split('.').pop().toLowerCase();
|
613 |
+
switch(extension) {
|
614 |
+
case 'jpg':
|
615 |
+
case 'jpeg': return 'JPEG';
|
616 |
+
case 'png': return 'PNG';
|
617 |
+
case 'webp': return 'WEBP';
|
618 |
+
case 'avif': return 'AVIF';
|
619 |
+
case 'heic':
|
620 |
+
case 'heif': return 'HEIC';
|
621 |
+
default: return extension.toUpperCase();
|
622 |
+
}
|
623 |
+
}
|
624 |
+
|
625 |
// Navigation functions
|
626 |
function showPreviousImage() {
|
627 |
if (currentFileIndex > 0) {
|
|
|
675 |
fullscreenImage.src = img.src;
|
676 |
fullscreenContainer.style.display = 'flex';
|
677 |
document.body.style.overflow = 'hidden';
|
678 |
+
fullscreenContainer.setAttribute('aria-hidden', 'false');
|
679 |
}
|
680 |
|
681 |
function closeFullscreen() {
|
682 |
fullscreenContainer.style.display = 'none';
|
683 |
document.body.style.overflow = '';
|
684 |
+
fullscreenContainer.setAttribute('aria-hidden', 'true');
|
685 |
}
|
686 |
|
687 |
function updateFullscreenImage() {
|