(function ($) { 'use strict'; $(document).ready(function () { var $uploadWrap = $('.studiou-fpp-upload-wrap'); if (!$uploadWrap.length) { return; } var productId = $uploadWrap.data('product-id'); var maxFileSize = parseInt($uploadWrap.data('max-file-size'), 10) || 50; var chunkSize = parseInt(studiouWcfppFront.chunkSize, 10) || 1048576; var uploadedAttachmentId = null; var uploadedFileRecordId = null; var isUploading = false; var originalGalleryImages = {}; // Store original src/srcset to restore on remove // Upload area elements var $dropzone = $uploadWrap.find('#studiou-fpp-dropzone'); var $fileInput = $uploadWrap.find('#studiou-fpp-file-input'); var $progress = $uploadWrap.find('.studiou-fpp-progress'); var $progressFill = $uploadWrap.find('.studiou-fpp-progress-fill'); var $progressText = $uploadWrap.find('.studiou-fpp-progress-text'); var $preview = $uploadWrap.find('.studiou-fpp-preview'); var $previewImg = $preview.find('img'); var $previewName = $uploadWrap.find('.studiou-fpp-preview-name'); var $removeBtn = $uploadWrap.find('.studiou-fpp-remove-file'); var $messages = $uploadWrap.find('.studiou-fpp-messages'); // Block all add-to-cart actions until photo is uploaded // Intercept form submissions $(document).on('submit', 'form.cart, form.variations_form', function (e) { if (!uploadedAttachmentId) { e.preventDefault(); showMessage(studiouWcfppFront.i18n.uploadFile, 'error'); scrollToUpload(); return false; } }); // Intercept add-to-cart button clicks (covers AJAX and non-AJAX buttons) $(document).on('click', '.single_add_to_cart_button, .add_to_cart_button, button[name="add-to-cart"], input[name="add-to-cart"]', function (e) { if (!uploadedAttachmentId) { e.preventDefault(); e.stopImmediatePropagation(); showMessage(studiouWcfppFront.i18n.uploadFile, 'error'); scrollToUpload(); return false; } }); // Intercept add-to-cart links (e.g. ?add-to-cart=ID) $(document).on('click', 'a[href*="add-to-cart"]', function (e) { if (!uploadedAttachmentId) { e.preventDefault(); e.stopImmediatePropagation(); showMessage(studiouWcfppFront.i18n.uploadFile, 'error'); scrollToUpload(); return false; } }); function scrollToUpload() { $('html, body').animate({ scrollTop: $uploadWrap.offset().top - 100 }, 400); } // Reset upload after successful WC add-to-cart $(document.body).on('added_to_cart', function () { resetUpload(); }); // Drag and drop $dropzone.on('dragover dragenter', function (e) { e.preventDefault(); e.stopPropagation(); $(this).addClass('studiou-fpp-dragover'); }); $dropzone.on('dragleave drop', function (e) { e.preventDefault(); e.stopPropagation(); $(this).removeClass('studiou-fpp-dragover'); }); $dropzone.on('drop', function (e) { var files = e.originalEvent.dataTransfer.files; if (files.length > 0) { handleFile(files[0]); } }); $dropzone.on('click', function () { if (!isUploading) { $fileInput.trigger('click'); } }); $fileInput.on('change', function () { if (this.files.length > 0) { handleFile(this.files[0]); } }); // Remove file $removeBtn.on('click', function () { removeUploadedFile(); }); function handleFile(file) { if (isUploading) { return; } var maxBytes = maxFileSize * 1024 * 1024; if (file.size > maxBytes) { showMessage(studiouWcfppFront.i18n.fileTooLarge.replace('%s', maxFileSize), 'error'); return; } clearMessages(); startChunkedUpload(file); } function startChunkedUpload(file) { isUploading = true; var totalChunks = Math.ceil(file.size / chunkSize); var uploadId = 'upload_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); var currentChunk = 0; $dropzone.hide(); $progress.show(); function uploadNextChunk() { var start = currentChunk * chunkSize; var end = Math.min(start + chunkSize, file.size); var blob = file.slice(start, end); var formData = new FormData(); formData.append('action', 'studiou_wcfpp_upload_chunk'); formData.append('nonce', studiouWcfppFront.nonce); formData.append('product_id', productId); formData.append('upload_id', uploadId); formData.append('chunk_index', currentChunk); formData.append('total_chunks', totalChunks); formData.append('file_name', file.name); formData.append('file_size', file.size); formData.append('chunk', blob, 'chunk'); // Last chunk triggers server-side assembly - needs longer timeout var isLastChunk = (currentChunk === totalChunks - 1); if (isLastChunk) { $progressFill.css('width', '100%'); $progressText.text(studiouWcfppFront.i18n.processing || 'Processing...'); } $.ajax({ url: studiouWcfppFront.ajaxUrl, type: 'POST', data: formData, processData: false, contentType: false, timeout: isLastChunk ? 120000 : 30000, success: function (response) { if (response.success) { if (!isLastChunk) { currentChunk++; var percent = Math.round((currentChunk / totalChunks) * 100); $progressFill.css('width', percent + '%'); $progressText.text(percent + '%'); } if (response.data.complete) { uploadedAttachmentId = response.data.attachment_id; uploadedFileRecordId = response.data.file_record_id; // Store in WC session so any add-to-cart button picks it up storeInSession( response.data.attachment_id, response.data.file_record_id, response.data.file_name ); showPreview(response.data.thumbnail_url, response.data.file_name); updateProductGallery(response.data.preview_url || response.data.thumbnail_url); $progress.hide(); isUploading = false; } else { uploadNextChunk(); } } else { uploadFailed(response.data ? response.data.message : studiouWcfppFront.i18n.uploadError); } }, error: function () { uploadFailed(studiouWcfppFront.i18n.uploadError); } }); } uploadNextChunk(); } function storeInSession(attachmentId, fileRecordId, fileName) { $.ajax({ url: studiouWcfppFront.ajaxUrl, type: 'POST', data: { action: 'studiou_wcfpp_set_upload_session', nonce: studiouWcfppFront.nonce, attachment_id: attachmentId, file_record_id: fileRecordId, file_name: fileName } }); } function clearSession() { $.ajax({ url: studiouWcfppFront.ajaxUrl, type: 'POST', data: { action: 'studiou_wcfpp_clear_upload_session', nonce: studiouWcfppFront.nonce } }); } function uploadFailed(message) { isUploading = false; $progress.hide(); $dropzone.show(); showMessage(message, 'error'); $fileInput.val(''); } function updateProductGallery(imageUrl) { if (!imageUrl) return; // Find the main product image (try common WC / theme selectors) var selectors = [ '.woocommerce-product-gallery__image img', '.product .wp-post-image', '.product-image img', '.product-thumbnail img', '.entry-summary img.attachment-woocommerce_thumbnail', '.product-gallery img:first' ]; var $img = null; for (var i = 0; i < selectors.length; i++) { $img = $(selectors[i]).first(); if ($img.length) break; } if (!$img || !$img.length) return; // Save original attributes for restoration if (!originalGalleryImages.src) { originalGalleryImages.src = $img.attr('src'); originalGalleryImages.srcset = $img.attr('srcset') || ''; originalGalleryImages.sizes = $img.attr('sizes') || ''; // Also save the parent link href if it's a gallery link var $parentLink = $img.closest('a'); if ($parentLink.length) { originalGalleryImages.href = $parentLink.attr('href'); } } // Replace with uploaded image $img.attr('src', imageUrl); $img.removeAttr('srcset'); $img.removeAttr('sizes'); var $parentLink = $img.closest('a'); if ($parentLink.length) { $parentLink.attr('href', imageUrl); } } function restoreProductGallery() { if (!originalGalleryImages.src) return; var selectors = [ '.woocommerce-product-gallery__image img', '.product .wp-post-image', '.product-image img', '.product-thumbnail img', '.entry-summary img.attachment-woocommerce_thumbnail', '.product-gallery img:first' ]; var $img = null; for (var i = 0; i < selectors.length; i++) { $img = $(selectors[i]).first(); if ($img.length) break; } if (!$img || !$img.length) return; $img.attr('src', originalGalleryImages.src); if (originalGalleryImages.srcset) { $img.attr('srcset', originalGalleryImages.srcset); } if (originalGalleryImages.sizes) { $img.attr('sizes', originalGalleryImages.sizes); } if (originalGalleryImages.href) { var $parentLink = $img.closest('a'); if ($parentLink.length) { $parentLink.attr('href', originalGalleryImages.href); } } originalGalleryImages = {}; } function showPreview(thumbnailUrl, fileName) { if (thumbnailUrl) { $previewImg.attr('src', thumbnailUrl).show(); } else { $previewImg.hide(); } $previewName.text(fileName); $preview.show(); } function removeUploadedFile() { if (!uploadedFileRecordId) { return; } $.ajax({ url: studiouWcfppFront.ajaxUrl, type: 'POST', data: { action: 'studiou_wcfpp_remove_upload', nonce: studiouWcfppFront.nonce, file_record_id: uploadedFileRecordId }, success: function () { resetUpload(); } }); } function resetUpload() { uploadedAttachmentId = null; uploadedFileRecordId = null; clearSession(); restoreProductGallery(); $preview.hide(); $previewImg.attr('src', ''); $previewName.text(''); $dropzone.show(); $fileInput.val(''); clearMessages(); } function showMessage(text, type) { var cls = type === 'error' ? 'studiou-fpp-msg-error' : 'studiou-fpp-msg-success'; $messages.html('