|
1 | | -// File Drop Zone Functionality |
2 | | -function setupFileDropZone(dropZoneId, inputId, multiple = true) { |
3 | | - const dropZone = document.getElementById(dropZoneId); |
4 | | - const fileInput = document.getElementById(inputId); |
5 | | - |
6 | | - if (multiple) { |
7 | | - fileInput.multiple = true; |
| 1 | +class FileHandler { |
| 2 | + constructor() { |
| 3 | + this.setupGlobalFileHandlers(); |
8 | 4 | } |
9 | 5 |
|
10 | | - dropZone.addEventListener("click", () => fileInput.click()); |
| 6 | + setupFileDropZone(dropZoneId, inputId, multiple = true) { |
| 7 | + const dropZone = document.getElementById(dropZoneId); |
| 8 | + const fileInput = document.getElementById(inputId); |
11 | 9 |
|
12 | | - dropZone.addEventListener("dragover", (e) => { |
13 | | - e.preventDefault(); |
14 | | - dropZone.classList.add("dragover"); |
15 | | - }); |
| 10 | + if (!dropZone || !fileInput) return; |
16 | 11 |
|
17 | | - dropZone.addEventListener("dragleave", () => { |
18 | | - dropZone.classList.remove("dragover"); |
19 | | - }); |
| 12 | + fileInput.multiple = multiple; |
20 | 13 |
|
21 | | - dropZone.addEventListener("drop", (e) => { |
22 | | - e.preventDefault(); |
23 | | - dropZone.classList.remove("dragover"); |
24 | | - if (multiple) { |
25 | | - fileInput.files = e.dataTransfer.files; |
26 | | - } else { |
27 | | - fileInput.files = |
28 | | - e.dataTransfer.files.length > 0 |
29 | | - ? [e.dataTransfer.files[0]] |
30 | | - : new DataTransfer().files; |
31 | | - } |
32 | | - updateFileList(dropZoneId, fileInput.files); |
33 | | - }); |
| 14 | + // Click to select files |
| 15 | + dropZone.addEventListener("click", () => fileInput.click()); |
34 | 16 |
|
35 | | - fileInput.addEventListener("change", () => { |
36 | | - updateFileList(dropZoneId, fileInput.files); |
37 | | - }); |
38 | | -} |
| 17 | + // Drag and drop handlers |
| 18 | + this.setupDragAndDrop(dropZone, fileInput, multiple); |
39 | 19 |
|
40 | | -function updateFileList(dropZoneId, files) { |
41 | | - const dropZone = document.getElementById(dropZoneId); |
42 | | - const fileList = dropZone.querySelector(".file-list"); |
43 | | - const placeholder = dropZone.querySelector(".drop-placeholder"); |
44 | | - |
45 | | - if (files.length > 0) { |
46 | | - if (placeholder) placeholder.classList.add("hidden"); |
47 | | - fileList.innerHTML = ""; |
48 | | - |
49 | | - Array.from(files).forEach((file, index) => { |
50 | | - const fileItem = document.createElement("div"); |
51 | | - fileItem.className = |
52 | | - "flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-700 rounded-lg mb-2"; |
53 | | - fileItem.innerHTML = ` |
54 | | - <div class="flex items-center flex-1 min-w-0"> |
55 | | - <i class="fas fa-file text-gray-400 mr-3 flex-shrink-0"></i> |
56 | | - <span class="text-sm font-medium truncate">${file.name}</span> |
57 | | - </div> |
58 | | - <div class="flex items-center space-x-3 flex-shrink-0"> |
59 | | - <span class="text-xs text-gray-500 dark:text-gray-400">${(file.size / 1024 / 1024).toFixed(2)} MB</span> |
60 | | - <button type="button" onclick="removeFile('${dropZoneId}', ${index})" class="text-red-500 hover:text-red-700"> |
61 | | - <i class="fas fa-times"></i> |
62 | | - </button> |
63 | | - </div> |
64 | | - `; |
65 | | - fileList.appendChild(fileItem); |
| 20 | + // File input change handler |
| 21 | + fileInput.addEventListener("change", () => { |
| 22 | + this.updateFileList(dropZoneId, fileInput.files); |
66 | 23 | }); |
67 | | - } else { |
68 | | - if (placeholder) placeholder.classList.remove("hidden"); |
69 | | - fileList.innerHTML = ""; |
70 | 24 | } |
71 | | -} |
72 | 25 |
|
73 | | -function removeFile(dropZoneId, index) { |
74 | | - const fileInput = document.querySelector( |
75 | | - `#${dropZoneId.replace("-drop-zone", "-file-input")}`, |
76 | | - ); |
77 | | - const dt = new DataTransfer(); |
| 26 | + setupDragAndDrop(dropZone, fileInput, multiple) { |
| 27 | + dropZone.addEventListener("dragover", (e) => { |
| 28 | + e.preventDefault(); |
| 29 | + dropZone.classList.add("dragover"); |
| 30 | + }); |
| 31 | + |
| 32 | + dropZone.addEventListener("dragleave", () => { |
| 33 | + dropZone.classList.remove("dragover"); |
| 34 | + }); |
| 35 | + |
| 36 | + dropZone.addEventListener("drop", (e) => { |
| 37 | + e.preventDefault(); |
| 38 | + dropZone.classList.remove("dragover"); |
| 39 | + |
| 40 | + if (multiple) { |
| 41 | + fileInput.files = e.dataTransfer.files; |
| 42 | + } else { |
| 43 | + fileInput.files = |
| 44 | + e.dataTransfer.files.length > 0 |
| 45 | + ? [e.dataTransfer.files[0]] |
| 46 | + : new DataTransfer().files; |
| 47 | + } |
| 48 | + this.updateFileList(dropZone.id, fileInput.files); |
| 49 | + }); |
| 50 | + } |
78 | 51 |
|
79 | | - Array.from(fileInput.files).forEach((file, i) => { |
80 | | - if (i !== index) dt.items.add(file); |
81 | | - }); |
| 52 | + updateFileList(dropZoneId, files) { |
| 53 | + const dropZone = document.getElementById(dropZoneId); |
| 54 | + const fileList = dropZone.querySelector(".file-list"); |
| 55 | + const placeholder = dropZone.querySelector(".drop-placeholder"); |
82 | 56 |
|
83 | | - fileInput.files = dt.files; |
84 | | - updateFileList(dropZoneId, fileInput.files); |
85 | | -} |
| 57 | + if (files.length > 0) { |
| 58 | + if (placeholder) placeholder.classList.add("hidden"); |
| 59 | + fileList.innerHTML = ""; |
86 | 60 |
|
87 | | -// Tool Navigation |
88 | | -function showTool(toolId, category) { |
89 | | - // Hide all tool interfaces |
90 | | - document.querySelectorAll(".tool-interface").forEach((interface) => { |
91 | | - interface.classList.add("hidden"); |
92 | | - }); |
93 | | - |
94 | | - // Show selected tool |
95 | | - const selectedTool = document.getElementById(`tool-${toolId}`); |
96 | | - if (selectedTool) { |
97 | | - selectedTool.classList.remove("hidden"); |
| 61 | + Array.from(files).forEach((file, index) => { |
| 62 | + const fileItem = this.createFileItem(file, dropZoneId, index); |
| 63 | + fileList.appendChild(fileItem); |
| 64 | + }); |
| 65 | + } else { |
| 66 | + if (placeholder) placeholder.classList.remove("hidden"); |
| 67 | + fileList.innerHTML = ""; |
| 68 | + } |
98 | 69 | } |
99 | 70 |
|
100 | | - // Update active nav item |
101 | | - document.querySelectorAll(".tool-nav").forEach((nav) => { |
102 | | - nav.classList.remove("tool-active"); |
103 | | - }); |
104 | | - document.querySelector(`.tool-${toolId}`).classList.add("tool-active"); |
| 71 | + createFileItem(file, dropZoneId, index) { |
| 72 | + const fileItem = document.createElement("div"); |
| 73 | + fileItem.className = |
| 74 | + "flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-700 rounded-lg mb-2"; |
| 75 | + fileItem.innerHTML = ` |
| 76 | + <div class="flex items-center flex-1 min-w-0"> |
| 77 | + <i class="fas fa-file text-gray-400 mr-3 flex-shrink-0"></i> |
| 78 | + <span class="text-sm font-medium truncate">${file.name}</span> |
| 79 | + </div> |
| 80 | + <div class="flex items-center space-x-3 flex-shrink-0"> |
| 81 | + <span class="text-xs text-gray-500 dark:text-gray-400">${(file.size / 1024 / 1024).toFixed(2)} MB</span> |
| 82 | + <button type="button" onclick="fileHandler.removeFile('${dropZoneId}', ${index})" |
| 83 | + class="text-red-500 hover:text-red-700 transition-colors"> |
| 84 | + <i class="fas fa-times"></i> |
| 85 | + </button> |
| 86 | + </div> |
| 87 | + `; |
| 88 | + return fileItem; |
| 89 | + } |
105 | 90 |
|
106 | | - // Update URL without reload |
107 | | - history.pushState(null, "", `?tool=${toolId}`); |
| 91 | + removeFile(dropZoneId, index) { |
| 92 | + const fileInput = document.querySelector( |
| 93 | + `#${dropZoneId.replace("-drop-zone", "-file-input")}`, |
| 94 | + ); |
| 95 | + const dt = new DataTransfer(); |
108 | 96 |
|
109 | | - // Initialize tool-specific functionality |
110 | | - initializeTool(toolId); |
111 | | -} |
| 97 | + Array.from(fileInput.files).forEach((file, i) => { |
| 98 | + if (i !== index) dt.items.add(file); |
| 99 | + }); |
112 | 100 |
|
113 | | -function initializeTool(toolId) { |
114 | | - // Tool-specific initialization |
115 | | - const setupFunctions = { |
116 | | - convert_doc: () => |
117 | | - setupFileDropZone("doc-drop-zone", "doc-file-input", true), |
118 | | - convert_audio: () => |
119 | | - setupFileDropZone("audio-drop-zone", "audio-file-input", true), |
120 | | - convert_video: () => |
121 | | - setupFileDropZone("video-drop-zone", "video-file-input", false), |
122 | | - convert_image: () => |
123 | | - setupFileDropZone("image-drop-zone", "image-file-input", true), |
124 | | - ocr: () => setupFileDropZone("ocr-drop-zone", "ocr-file-input", true), |
125 | | - // Add more tool initializations |
126 | | - }; |
127 | | - |
128 | | - if (setupFunctions[toolId]) { |
129 | | - setupFunctions[toolId](); |
| 101 | + fileInput.files = dt.files; |
| 102 | + this.updateFileList(dropZoneId, fileInput.files); |
130 | 103 | } |
131 | | -} |
132 | 104 |
|
133 | | -// Load tool from URL parameter |
134 | | -document.addEventListener("DOMContentLoaded", function () { |
135 | | - const urlParams = new URLSearchParams(window.location.search); |
136 | | - const toolParam = urlParams.get("tool"); |
137 | | - if (toolParam) { |
138 | | - showTool(toolParam, "{{ category }}"); |
| 105 | + setupGlobalFileHandlers() { |
| 106 | + // Add any global file-related event listeners here |
139 | 107 | } |
140 | | -}); |
| 108 | + |
| 109 | + validateFiles(files, allowedTypes = []) { |
| 110 | + if (files.length === 0) return { valid: false, error: "No files selected" }; |
| 111 | + |
| 112 | + if (allowedTypes.length > 0) { |
| 113 | + for (let file of files) { |
| 114 | + const extension = file.name.split(".").pop().toLowerCase(); |
| 115 | + if (!allowedTypes.includes(extension)) { |
| 116 | + return { |
| 117 | + valid: false, |
| 118 | + error: `File type .${extension} is not allowed`, |
| 119 | + }; |
| 120 | + } |
| 121 | + } |
| 122 | + } |
| 123 | + |
| 124 | + return { valid: true }; |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +// Initialize global file handler |
| 129 | +window.fileHandler = new FileHandler(); |
0 commit comments