1/* 2 * @copyright (c) 2020. 3 * @author Alan Fuller (support@fullworks) 4 * @licence GPL V3 https://www.gnu.org/licenses/gpl-3.0.en.html 5 * @link https://fullworks.net 6 * 7 * This file is part of a Fullworks plugin. 8 * 9 * This plugin is free software: you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation, either version 3 of the License, or 12 * (at your option) any later version. 13 * 14 * This plugin is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this plugin. https://www.gnu.org/licenses/gpl-3.0.en.html 21 */ 22 23 24/* <fs_premium_only> */ 25document.addEventListener('DOMContentLoaded', function() { 26 // Original elements from your code 27 let dropArea = document.getElementById('qem-drop-area'); 28 let fileInput = document.getElementById('qem-fileElem'); 29 let fileNameDisplay = document.getElementById('qem-file-name'); 30 let fileSelection = document.getElementById('qem-file-selection'); 31 let fileClear = document.getElementById('qem-file-clear'); 32 33 // Get max file size from localized data or use default 34 const MAX_FILE_SIZE = window.qem_data && window.qem_data.guest_max_file_size ? 35 parseInt(window.qem_data.guest_max_file_size) : 100 * 1024; // 100KB default 36 37 // Create image preview element if it doesn't exist 38 let imagePreview = document.getElementById('qem-image-preview'); 39 if (!imagePreview) { 40 imagePreview = document.createElement('div'); 41 imagePreview.id = 'qem-image-preview'; 42 imagePreview.className = 'qem-tw-mt-2 qem-tw-hidden'; 43 imagePreview.innerHTML = ` 44 <div class="qem-tw-border qem-tw-border-gray-300 qem-tw-rounded-md qem-tw-overflow-hidden qem-tw-w-full qem-tw-max-h-48 qem-tw-flex qem-tw-justify-center qem-tw-bg-gray-50"> 45 <img id="qem-preview-img" class="qem-tw-max-h-48 qem-tw-object-contain" src="" alt="Preview"> 46 </div> 47 <div class="qem-tw-mt-1 qem-tw-flex qem-tw-justify-between qem-tw-items-center"> 48 <span id="qem-image-size" class="qem-tw-text-xs qem-tw-text-gray-500"></span> 49 <span id="qem-image-dimensions" class="qem-tw-text-xs qem-tw-text-gray-500"></span> 50 </div> 51 `; 52 // Insert preview after the drop area 53 if (dropArea && dropArea.parentNode) { 54 dropArea.parentNode.insertBefore(imagePreview, dropArea.nextSibling); 55 } 56 } 57 58 // If we can't find the necessary elements, just exit 59 if (!dropArea || !fileInput) { 60 return; 61 } 62 63 const previewImg = document.getElementById('qem-preview-img'); 64 const imageSizeDisplay = document.getElementById('qem-image-size'); 65 const imageDimensionsDisplay = document.getElementById('qem-image-dimensions'); 66 67 // Add resize modal elements if they don't exist 68 let resizeModal = document.getElementById('qem-resize-modal'); 69 if (!resizeModal) { 70 const modal = document.createElement('div'); 71 modal.id = 'qem-resize-modal'; 72 modal.className = 'qem-tw-fixed qem-tw-inset-0 qem-tw-bg-gray-600 qem-tw-bg-opacity-50 qem-tw-hidden qem-tw-flex qem-tw-items-center qem-tw-justify-center qem-tw-z-50'; 73 modal.innerHTML = ` 74 <div class="qem-tw-bg-white qem-tw-p-6 qem-tw-rounded-lg qem-tw-shadow-xl qem-tw-max-w-md qem-tw-w-full"> 75 <h3 class="qem-tw-text-lg qem-tw-font-medium qem-tw-mb-4">${window.qem_data.guest_max_file_size_message || 'Image Size Exceeded'}</h3> 76 <p class="qem-tw-mb-4">${window.qem_data.guest_max_file_size_message_text || `The selected image exceeds the maximum allowed size (${(MAX_FILE_SIZE/1024/1024).toFixed(1)}MB). Would you like to resize it?`}</p> 77 <div class="qem-tw-flex qem-tw-justify-end qem-tw-space-x-3"> 78 <button id="qem-resize-cancel" class="qem-tw-px-4 qem-tw-py-2 qem-tw-border qem-tw-rounded qem-tw-text-gray-600">${window.qem_data.guest_max_file_size_message_cancel || 'Cancel'}</button> 79 <button id="qem-resize-confirm" class="qem-tw-px-4 qem-tw-py-2 qem-tw-bg-blue-500 qem-tw-text-white qem-tw-rounded">${window.qem_data.guest_max_file_size_message_confirm || 'Resize Image'}</button> 80 </div> 81 </div> 82`; 83 document.body.appendChild(modal); 84 resizeModal = modal; 85 } 86 87 // Get modal buttons 88 const resizeConfirmBtn = document.getElementById('qem-resize-confirm'); 89 const resizeCancelBtn = document.getElementById('qem-resize-cancel'); 90 91 // Store original file temporarily 92 let originalFile = null; 93
94 // Prevent default drag behaviors (from your original code) 95 ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { 96 dropArea.addEventListener(eventName, preventDefaults, false); 97 }); 98 99 // Highlight drop area when item is dragged over it (from your original code) 100 ['dragenter', 'dragover'].forEach(eventName => { 101 dropArea.addEventListener(eventName, highlight, false); 102 }); 103 104 ['dragleave', 'drop'].forEach(eventName => { 105 dropArea.addEventListener(eventName, unhighlight, false); 106 }); 107 108 // Handle dropped files (modified to check file size) 109 dropArea.addEventListener('drop', handleDrop, false); 110 111 // Handle file input change (modified to check file size) 112 fileInput.addEventListener('change', function() { 113 if (this.files && this.files.length > 0) { 114 checkFileSize(this.files); 115 } 116 }); 117 118 // Handle file clear button (from your original code) 119 if (fileClear) { 120 fileClear.addEventListener('click', function() { 121 clearFile(); 122 }); 123 } 124 125 // Modal buttons event listeners 126 if (resizeConfirmBtn) { 127 resizeConfirmBtn.addEventListener('click', function() { 128 if (originalFile) { 129 resizeImage(originalFile); 130 resizeModal.classList.add('qem-tw-hidden'); 131 } 132 }); 133 } 134 135 if (resizeCancelBtn) { 136 resizeCancelBtn.addEventListener('click', function() { 137 originalFile = null; 138 clearFile(); 139 resizeModal.classList.add('qem-tw-hidden'); 140 }); 141 } 142 143 function preventDefaults(e) { 144 e.preventDefault(); 145 e.stopPropagation(); 146 } 147 148 function highlight() { 149 dropArea.classList.add('qem-tw-border-gray-500'); 150 dropArea.classList.add('qem-tw-bg-gray-50'); 151 } 152 153 function unhighlight() { 154 dropArea.classList.remove('qem-tw-border-gray-500'); 155 dropArea.classList.remove('qem-tw-bg-gray-50'); 156 } 157 158 function handleDrop(e) { 159 let dt = e.dataTransfer; 160 let files = dt.files; 161 162 checkFileSize(files); 163 } 164 165 function formatFileSize(bytes) { 166 if (bytes < 1024) { 167 return bytes + ' bytes'; 168 } else if (bytes < 1024 * 1024) { 169 return (bytes / 1024).toFixed(1) + ' KB'; 170 } else { 171 return (bytes / (1024 * 1024)).toFixed(2) + ' MB'; 172 } 173 } 174 175 function checkFileSize(files) { 176 if (files && files.length > 0) { 177 const file = files[0]; 178 // Check if file is an image 179 if (!file.type.match('image.*')) { 180 handleFiles(files); // Process normally if not an image 181 return; 182 } 183 184 // Always create preview first 185 createImagePreview(file); 186 187 // Check file size 188 if (file.size > MAX_FILE_SIZE) { 189 // Store original file for potential resizing 190 originalFile = file; 191 // Show resize modal 192 if (resizeModal) { 193 resizeModal.classList.remove('qem-tw-hidden'); 194 } 195 196 // Important: Also show the original file name in the UI 197 updateFileDisplay(file); 198 } else { 199 // Process file normally if size is acceptable 200 handleFiles(files); 201 } 202 } 203 } 204 205 function handleFiles(files) { 206 fileInput.files = files; 207 if (files && files.length > 0) { 208 updateFileDisplay(files[0]); 209 } 210 } 211 212 function updateFileDisplay(file) { 213 // Update file name display 214 if (fileNameDisplay) { 215 fileNameDisplay.innerText = file.name; 216 } 217 218 // Show the file selection element 219 if (fileSelection) { 220 fileSelection.classList.remove('qem-tw-hidden'); 221 } 222 } 223 224 function createImagePreview(file) { 225 if (!previewImg || !imagePreview) return; 226 227 const reader = new FileReader(); 228 229 reader.onload = function(e) { 230 // Set preview image source 231 previewImg.src = e.target.result; 232 233 // Update file size display 234 if (imageSizeDisplay) { 235 imageSizeDisplay.textContent = formatFileSize(file.size); 236 } 237 238 // Load image to get dimensions 239 const img = new Image(); 240 img.onload = function() { 241 if (imageDimensionsDisplay) { 242 imageDimensionsDisplay.textContent = `${img.width} Ã ${img.height}`; 243 } 244 }; 245 img.src = e.target.result; 246 247 // Show the preview container 248 imagePreview.classList.remove('qem-tw-hidden'); 249 }; 250 251 reader.readAsDataURL(file); 252 } 253 254 function resizeImage(file) { 255 const reader = new FileReader(); 256 reader.onload = function(e) { 257 const img = new Image(); 258 img.onload = function() { 259 // Start with a reasonable scaling factor based on file size ratio 260 let scaleFactor = Math.sqrt(MAX_FILE_SIZE / file.size); 261 262 // Initial dimensions 263 let width = Math.floor(img.width * scaleFactor); 264 let height = Math.floor(img.height * scaleFactor); 265 266 // Create canvas for resizing 267 const canvas = document.createElement('canvas'); 268 269 // Function to actually perform the resize with given dimensions 270 const performResize = (w, h, quality = 0.85) => { 271 canvas.width = w; 272 canvas.height = h; 273 274 // Draw resized image on canvas 275 const ctx = canvas.getContext('2d'); 276 ctx.clearRect(0, 0, w, h); 277 ctx.drawImage(img, 0, 0, w, h); 278 279 // Convert canvas to blob 280 canvas.toBlob(function(blob) { 281 console.log(`Resize attempt: ${w}x${h}, Quality: ${quality}, Size: ${blob.size / 1024}KB, Target: ${MAX_FILE_SIZE / 1024}KB`); 282 283 // If the blob is still too large, try reducing further 284 if (blob.size > MAX_FILE_SIZE) { 285 // Try reducing quality first if it's a JPEG 286 if (file.type === 'image/jpeg' && quality > 0.65) {
287 // Reduce quality and try again with same dimensions 288 performResize(w, h, quality - 0.1); 289 } else { 290 // Reduce dimensions by 10% and try again 291 const newWidth = Math.floor(w * 0.9); 292 const newHeight = Math.floor(h * 0.9); 293 performResize(newWidth, newHeight, quality); 294 } 295 return; 296 } 297 298 // We've got a blob that's under the size limit 299 // Create a new File object 300 const resizedFile = new File([blob], file.name, { 301 type: file.type, 302 lastModified: new Date().getTime() 303 }); 304 305 // Create a DataTransfer to set the files property of the file input 306 const dataTransfer = new DataTransfer(); 307 dataTransfer.items.add(resizedFile); 308 fileInput.files = dataTransfer.files; 309 310 // Update UI 311 if (fileNameDisplay) { 312 fileNameDisplay.innerText = file.name + ' (resized)'; 313 } 314 if (fileSelection) { 315 fileSelection.classList.remove('qem-tw-hidden'); 316 } 317 318 // Update preview with new image 319 createImagePreview(resizedFile); 320 321 // Reset original file 322 originalFile = null; 323 324 // Log the size difference for debugging 325 console.log(`Original size: ${file.size / 1024}KB, Final resized: ${blob.size / 1024}KB`); 326 }, file.type, quality); 327 }; 328 329 // Start the resize process 330 performResize(width, height); 331 }; 332 img.src = e.target.result; 333 }; 334 reader.readAsDataURL(file); 335 } 336 337 function clearFile() { 338 fileInput.value = ''; 339 if (fileNameDisplay) { 340 fileNameDisplay.innerText = ''; 341 } 342 if (fileSelection) { 343 fileSelection.classList.add('qem-tw-hidden'); 344 } 345 346 // Clear and hide the image preview 347 if (previewImg) { 348 previewImg.src = ''; 349 } 350 if (imagePreview) { 351 imagePreview.classList.add('qem-tw-hidden'); 352 } 353 if (imageSizeDisplay) { 354 imageSizeDisplay.textContent = ''; 355 } 356 if (imageDimensionsDisplay) { 357 imageDimensionsDisplay.textContent = ''; 358 } 359 360 originalFile = null; 361 } 362}); 363/* </fs_premium_only> */
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.