Initial commit: Grav CMS setup with HTML reference material
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,662 @@
|
||||
/**
|
||||
* Unified Grav Form FilePond Handler
|
||||
*
|
||||
* This script initializes and configures FilePond instances for file uploads
|
||||
* within Grav forms. It works with both normal and XHR form submissions.
|
||||
* It also handles reinitializing FilePond instances after XHR form submissions.
|
||||
*/
|
||||
|
||||
// Immediately-Invoked Function Expression for scoping
|
||||
(function () {
|
||||
// Check if script already loaded
|
||||
if (window.gravFilepondHandlerLoaded) {
|
||||
console.log('FilePond unified handler already loaded, skipping.');
|
||||
return;
|
||||
}
|
||||
window.gravFilepondHandlerLoaded = true;
|
||||
|
||||
// Debugging - set to false for production
|
||||
const debug = true;
|
||||
|
||||
// Helper function for logging
|
||||
function log(message, type = 'log') {
|
||||
if (!debug && type !== 'error') return;
|
||||
|
||||
const prefix = '[FilePond Handler]';
|
||||
if (type === 'error') {
|
||||
console.error(prefix, message);
|
||||
} else if (type === 'warn') {
|
||||
console.warn(prefix, message);
|
||||
} else {
|
||||
console.log(prefix, message);
|
||||
}
|
||||
}
|
||||
|
||||
// Track FilePond instances with their configuration
|
||||
const pondInstances = new Map();
|
||||
|
||||
// Get translations from global object if available
|
||||
const translations = window.GravForm?.translations?.PLUGIN_FORM || {
|
||||
FILEPOND_REMOVE_FILE: 'Remove file',
|
||||
FILEPOND_REMOVE_FILE_CONFIRMATION: 'Are you sure you want to remove this file?',
|
||||
FILEPOND_CANCEL_UPLOAD: 'Cancel upload',
|
||||
FILEPOND_ERROR_FILESIZE: 'File is too large',
|
||||
FILEPOND_ERROR_FILETYPE: 'Invalid file type'
|
||||
};
|
||||
|
||||
// Track initialization state
|
||||
let initialized = false;
|
||||
|
||||
/**
|
||||
* Get standard FilePond configuration for an element
|
||||
* This is used for both initial setup and reinit after XHR
|
||||
* @param {HTMLElement} element - The file input element
|
||||
* @param {HTMLElement} container - The container element
|
||||
* @returns {Object} Configuration object for FilePond
|
||||
*/
|
||||
function getFilepondConfig(element, container) {
|
||||
if (!container) {
|
||||
log('Container not provided for config extraction', 'error');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if the field is required - this is correct location
|
||||
const isRequired = element.hasAttribute('required') ||
|
||||
container.hasAttribute('required') ||
|
||||
container.getAttribute('data-required') === 'true';
|
||||
|
||||
// Then, add this code to remove the required attribute from the actual input
|
||||
// to prevent browser validation errors, but keep track of the requirement
|
||||
if (isRequired) {
|
||||
// Store the required state on the container for our custom validation
|
||||
container.setAttribute('data-required', 'true');
|
||||
// Remove the required attribute from the input to avoid browser validation errors
|
||||
element.removeAttribute('required');
|
||||
}
|
||||
|
||||
try {
|
||||
// Get settings from data attributes
|
||||
const settingsAttr = container.getAttribute('data-grav-file-settings');
|
||||
if (!settingsAttr) {
|
||||
log('No file settings found for FilePond element', 'warn');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse settings
|
||||
let settings;
|
||||
try {
|
||||
settings = JSON.parse(settingsAttr);
|
||||
log('Parsed settings:', settings);
|
||||
} catch (e) {
|
||||
log(`Error parsing file settings: ${e.message}`, 'error');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse FilePond options
|
||||
const filepondOptionsAttr = container.getAttribute('data-filepond-options') || '{}';
|
||||
let filepondOptions;
|
||||
try {
|
||||
filepondOptions = JSON.parse(filepondOptionsAttr);
|
||||
log('Parsed FilePond options:', filepondOptions);
|
||||
} catch (e) {
|
||||
log(`Error parsing FilePond options: ${e.message}`, 'error');
|
||||
filepondOptions = {};
|
||||
}
|
||||
|
||||
// Get URLs for upload and remove
|
||||
const uploadUrl = container.getAttribute('data-file-url-add');
|
||||
const removeUrl = container.getAttribute('data-file-url-remove');
|
||||
|
||||
if (!uploadUrl) {
|
||||
log('Upload URL not found for FilePond element', 'warn');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse previously uploaded files
|
||||
const existingFiles = [];
|
||||
const fileDataElements = container.querySelectorAll('[data-file]');
|
||||
log(`Found ${fileDataElements.length} existing file data elements`);
|
||||
|
||||
fileDataElements.forEach(fileData => {
|
||||
try {
|
||||
const fileAttr = fileData.getAttribute('data-file');
|
||||
log('File data attribute:', fileAttr);
|
||||
|
||||
const fileJson = JSON.parse(fileAttr);
|
||||
|
||||
if (fileJson && fileJson.name) {
|
||||
existingFiles.push({
|
||||
source: fileJson.name,
|
||||
options: {
|
||||
type: 'local',
|
||||
file: {
|
||||
name: fileJson.name,
|
||||
size: fileJson.size,
|
||||
type: fileJson.type
|
||||
},
|
||||
metadata: {
|
||||
poster: fileJson.thumb_url || fileJson.path
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
log(`Error parsing file data: ${e.message}`, 'error');
|
||||
}
|
||||
});
|
||||
|
||||
log('Existing files:', existingFiles);
|
||||
|
||||
// Get form elements for Grav integration
|
||||
const fieldName = container.getAttribute('data-file-field-name');
|
||||
const form = element.closest('form');
|
||||
const formNameInput = form ? form.querySelector('[name="__form-name__"]') : document.querySelector('[name="__form-name__"]');
|
||||
const formIdInput = form ? form.querySelector('[name="__unique_form_id__"]') : document.querySelector('[name="__unique_form_id__"]');
|
||||
const formNonceInput = form ? form.querySelector('[name="form-nonce"]') : document.querySelector('[name="form-nonce"]');
|
||||
|
||||
if (!formNameInput || !formIdInput || !formNonceInput) {
|
||||
log('Missing required form inputs for proper Grav integration', 'warn');
|
||||
}
|
||||
|
||||
// Configure FilePond
|
||||
const options = {
|
||||
// Core settings
|
||||
name: settings.paramName,
|
||||
maxFiles: settings.limit || null,
|
||||
maxFileSize: `${settings.filesize}MB`,
|
||||
acceptedFileTypes: settings.accept,
|
||||
files: existingFiles,
|
||||
|
||||
// Server configuration - modified for Grav
|
||||
server: {
|
||||
process: {
|
||||
url: uploadUrl,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
ondata: (formData) => {
|
||||
// Safety check - ensure formData is valid
|
||||
if (!formData) {
|
||||
console.error('FormData is undefined in ondata');
|
||||
return new FormData(); // Return empty FormData as fallback
|
||||
}
|
||||
|
||||
// Add all required Grav form fields
|
||||
if (formNameInput) formData.append('__form-name__', formNameInput.value);
|
||||
if (formIdInput) formData.append('__unique_form_id__', formIdInput.value);
|
||||
formData.append('__form-file-uploader__', '1');
|
||||
if (formNonceInput) formData.append('form-nonce', formNonceInput.value);
|
||||
formData.append('task', 'filesupload');
|
||||
|
||||
// Use fieldName from the outer scope
|
||||
if (fieldName) {
|
||||
formData.append('name', fieldName);
|
||||
} else {
|
||||
console.error('Field name is undefined, falling back to default');
|
||||
formData.append('name', 'files');
|
||||
}
|
||||
|
||||
// Add URI if needed
|
||||
const uriInput = document.querySelector('[name="uri"]');
|
||||
if (uriInput) {
|
||||
formData.append('uri', uriInput.value);
|
||||
}
|
||||
|
||||
// Note: Don't try to append file here, FilePond will do that based on the name parameter
|
||||
// Just return the modified formData
|
||||
log('Prepared form data for Grav upload');
|
||||
return formData;
|
||||
}
|
||||
},
|
||||
revert: removeUrl ? {
|
||||
url: removeUrl,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
ondata: (formData, file) => {
|
||||
// Add all required Grav form fields
|
||||
if (formNameInput) formData.append('__form-name__', formNameInput.value);
|
||||
if (formIdInput) formData.append('__unique_form_id__', formIdInput.value);
|
||||
formData.append('__form-file-remover__', '1');
|
||||
if (formNonceInput) formData.append('form-nonce', formNonceInput.value);
|
||||
formData.append('name', fieldName);
|
||||
|
||||
// Add filename
|
||||
formData.append('filename', file.filename);
|
||||
|
||||
log('Prepared form data for file removal');
|
||||
return formData;
|
||||
}
|
||||
} : null
|
||||
},
|
||||
|
||||
// Image Transform settings - both FilePond native settings and our custom ones
|
||||
// Native settings
|
||||
allowImagePreview: true,
|
||||
allowImageResize: true,
|
||||
allowImageTransform: true,
|
||||
imagePreviewHeight: filepondOptions.imagePreviewHeight || 256,
|
||||
|
||||
// Transform settings
|
||||
imageTransformOutputMimeType: filepondOptions.imageTransformOutputMimeType || 'image/jpeg',
|
||||
imageTransformOutputQuality: filepondOptions.imageTransformOutputQuality || settings.resizeQuality || 90,
|
||||
imageTransformOutputStripImageHead: filepondOptions.imageTransformOutputStripImageHead !== false,
|
||||
|
||||
// Resize settings
|
||||
imageResizeTargetWidth: filepondOptions.imageResizeTargetWidth || settings.resizeWidth || null,
|
||||
imageResizeTargetHeight: filepondOptions.imageResizeTargetHeight || settings.resizeHeight || null,
|
||||
imageResizeMode: filepondOptions.imageResizeMode || 'cover',
|
||||
imageResizeUpscale: filepondOptions.imageResizeUpscale || false,
|
||||
|
||||
// Crop settings
|
||||
allowImageCrop: filepondOptions.allowImageCrop || false,
|
||||
imageCropAspectRatio: filepondOptions.imageCropAspectRatio || null,
|
||||
|
||||
// Labels and translations
|
||||
labelIdle: filepondOptions.labelIdle || '<span class="filepond--label-action">Browse</span> or drop files',
|
||||
labelFileTypeNotAllowed: translations.FILEPOND_ERROR_FILETYPE || 'Invalid file type',
|
||||
labelFileSizeNotAllowed: translations.FILEPOND_ERROR_FILESIZE || 'File is too large',
|
||||
labelFileLoading: 'Loading',
|
||||
labelFileProcessing: 'Uploading',
|
||||
labelFileProcessingComplete: 'Upload complete',
|
||||
labelFileProcessingAborted: 'Upload cancelled',
|
||||
labelTapToCancel: translations.FILEPOND_CANCEL_UPLOAD || 'Cancel upload',
|
||||
labelTapToRetry: 'Retry',
|
||||
labelTapToUndo: 'Undo',
|
||||
labelButtonRemoveItem: translations.FILEPOND_REMOVE_FILE || 'Remove',
|
||||
|
||||
// Style settings
|
||||
stylePanelLayout: filepondOptions.stylePanelLayout || 'compact',
|
||||
styleLoadIndicatorPosition: filepondOptions.styleLoadIndicatorPosition || 'center bottom',
|
||||
styleProgressIndicatorPosition: filepondOptions.styleProgressIndicatorPosition || 'center bottom',
|
||||
styleButtonRemoveItemPosition: filepondOptions.styleButtonRemoveItemPosition || 'right',
|
||||
|
||||
// Override with any remaining user-provided options
|
||||
...filepondOptions
|
||||
};
|
||||
|
||||
log('Prepared FilePond configuration:', options);
|
||||
|
||||
return options;
|
||||
} catch (e) {
|
||||
log(`Error creating FilePond configuration: ${e.message}`, 'error');
|
||||
console.error(e); // Full error in console
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a single FilePond instance
|
||||
* @param {HTMLElement} element - The file input element to initialize
|
||||
* @returns {FilePond|null} The created FilePond instance, or null if creation failed
|
||||
*/
|
||||
function initializeSingleFilePond(element) {
|
||||
const container = element.closest('.filepond-root');
|
||||
|
||||
if (!container) {
|
||||
log('FilePond container not found for input element', 'error');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't initialize twice
|
||||
if (container.classList.contains('filepond--hopper') || container.querySelector('.filepond--hopper')) {
|
||||
log('FilePond already initialized for this element, skipping');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the element ID or create a unique one for tracking
|
||||
const elementId = element.id || `filepond-${Math.random().toString(36).substring(2, 15)}`;
|
||||
|
||||
// Get configuration
|
||||
const config = getFilepondConfig(element, container);
|
||||
if (!config) {
|
||||
log('Failed to get configuration, cannot initialize FilePond', 'error');
|
||||
return null;
|
||||
}
|
||||
|
||||
log(`Initializing FilePond element ${elementId} with config`, config);
|
||||
|
||||
try {
|
||||
// Create FilePond instance
|
||||
const pond = FilePond.create(element, config);
|
||||
log(`FilePond instance created successfully for element ${elementId}`);
|
||||
|
||||
// Store the instance and its configuration for potential reinit
|
||||
pondInstances.set(elementId, {
|
||||
instance: pond,
|
||||
config: config,
|
||||
container: container
|
||||
});
|
||||
|
||||
// Add a reference to the element for easier lookup
|
||||
element.filepondId = elementId;
|
||||
container.filepondId = elementId;
|
||||
|
||||
// Handle form submission to ensure files are processed before submit
|
||||
const form = element.closest('form');
|
||||
if (form && !form._filepond_handler_attached) {
|
||||
form._filepond_handler_attached = true;
|
||||
|
||||
form.addEventListener('submit', function (e) {
|
||||
// Check for all FilePond instances in this form
|
||||
const formPonds = Array.from(pondInstances.values())
|
||||
.filter(info => info.instance && info.container.closest('form') === form);
|
||||
|
||||
const processingFiles = formPonds.reduce((total, info) => {
|
||||
return total + info.instance.getFiles().filter(file =>
|
||||
file.status === FilePond.FileStatus.PROCESSING_QUEUED ||
|
||||
file.status === FilePond.FileStatus.PROCESSING
|
||||
).length;
|
||||
}, 0);
|
||||
|
||||
if (processingFiles > 0) {
|
||||
e.preventDefault();
|
||||
alert('Please wait for all files to finish uploading before submitting the form.');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return pond;
|
||||
} catch (e) {
|
||||
log(`Error creating FilePond instance: ${e.message}`, 'error');
|
||||
console.error(e); // Full error in console
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main FilePond initialization function
|
||||
* This will find and initialize all uninitialized FilePond elements
|
||||
*/
|
||||
function initializeFilePond() {
|
||||
log('Starting FilePond initialization');
|
||||
|
||||
// Make sure we have the libraries loaded
|
||||
if (typeof window.FilePond === 'undefined') {
|
||||
log('FilePond library not found. Will retry in 500ms...', 'warn');
|
||||
setTimeout(initializeFilePond, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
log('FilePond library found, continuing initialization');
|
||||
|
||||
// Register plugins if available
|
||||
try {
|
||||
if (window.FilePondPluginFileValidateSize) {
|
||||
FilePond.registerPlugin(FilePondPluginFileValidateSize);
|
||||
log('Registered FileValidateSize plugin');
|
||||
}
|
||||
|
||||
if (window.FilePondPluginFileValidateType) {
|
||||
FilePond.registerPlugin(FilePondPluginFileValidateType);
|
||||
log('Registered FileValidateType plugin');
|
||||
}
|
||||
|
||||
if (window.FilePondPluginImagePreview) {
|
||||
FilePond.registerPlugin(FilePondPluginImagePreview);
|
||||
log('Registered ImagePreview plugin');
|
||||
}
|
||||
|
||||
if (window.FilePondPluginImageResize) {
|
||||
FilePond.registerPlugin(FilePondPluginImageResize);
|
||||
log('Registered ImageResize plugin');
|
||||
}
|
||||
|
||||
if (window.FilePondPluginImageTransform) {
|
||||
FilePond.registerPlugin(FilePondPluginImageTransform);
|
||||
log('Registered ImageTransform plugin');
|
||||
}
|
||||
} catch (e) {
|
||||
log(`Error registering plugins: ${e.message}`, 'error');
|
||||
}
|
||||
|
||||
// Find all FilePond elements
|
||||
const elements = document.querySelectorAll('.filepond-root input[type="file"]:not(.filepond--browser)');
|
||||
|
||||
if (elements.length === 0) {
|
||||
log('No FilePond form elements found on the page');
|
||||
return;
|
||||
}
|
||||
|
||||
log(`Found ${elements.length} FilePond element(s)`);
|
||||
|
||||
// Process each FilePond element
|
||||
elements.forEach((element, index) => {
|
||||
log(`Initializing FilePond element #${index + 1}`);
|
||||
initializeSingleFilePond(element);
|
||||
});
|
||||
|
||||
initialized = true;
|
||||
log('FilePond initialization complete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitialize a specific FilePond instance
|
||||
* @param {HTMLElement} container - The FilePond container element
|
||||
* @returns {FilePond|null} The reinitialized FilePond instance, or null if reinitialization failed
|
||||
*/
|
||||
function reinitializeSingleFilePond(container) {
|
||||
if (!container) {
|
||||
log('No container provided for reinitialization', 'error');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if this is a FilePond container
|
||||
if (!container.classList.contains('filepond-root')) {
|
||||
log('Container is not a FilePond container', 'warn');
|
||||
return null;
|
||||
}
|
||||
|
||||
log(`Reinitializing FilePond container: ${container.id || 'unnamed'}`);
|
||||
|
||||
// If already initialized, destroy first
|
||||
if (container.classList.contains('filepond--hopper') || container.querySelector('.filepond--hopper')) {
|
||||
log('Container already has an active FilePond instance, destroying it first');
|
||||
|
||||
// Try to find and destroy through our internal tracking
|
||||
const elementId = container.filepondId;
|
||||
if (elementId && pondInstances.has(elementId)) {
|
||||
const info = pondInstances.get(elementId);
|
||||
if (info.instance) {
|
||||
log(`Destroying tracked FilePond instance for element ${elementId}`);
|
||||
info.instance.destroy();
|
||||
pondInstances.delete(elementId);
|
||||
}
|
||||
} else {
|
||||
// Fallback: Try to find via child element with class
|
||||
const pondElement = container.querySelector('.filepond--root');
|
||||
if (pondElement && pondElement._pond) {
|
||||
log('Destroying FilePond instance via DOM reference');
|
||||
pondElement._pond.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for the file input
|
||||
const input = container.querySelector('input[type="file"]:not(.filepond--browser)');
|
||||
if (!input) {
|
||||
log('No file input found in container for reinitialization', 'error');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a new instance
|
||||
return initializeSingleFilePond(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitialize all FilePond instances
|
||||
* This is used after XHR form submissions
|
||||
*/
|
||||
function reinitializeFilePond() {
|
||||
log('Reinitializing all FilePond instances');
|
||||
|
||||
// Find all FilePond containers
|
||||
const containers = document.querySelectorAll('.filepond-root');
|
||||
if (containers.length === 0) {
|
||||
log('No FilePond containers found for reinitialization');
|
||||
return;
|
||||
}
|
||||
|
||||
log(`Found ${containers.length} FilePond container(s) for reinitialization`);
|
||||
|
||||
// Process each container
|
||||
containers.forEach((container, index) => {
|
||||
log(`Reinitializing FilePond container #${index + 1}`);
|
||||
reinitializeSingleFilePond(container);
|
||||
});
|
||||
|
||||
log('FilePond reinitialization complete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to support XHR form interaction
|
||||
* This hooks into the GravFormXHR system if available
|
||||
*/
|
||||
function setupXHRIntegration() {
|
||||
// Only run if GravFormXHR is available
|
||||
if (window.GravFormXHR) {
|
||||
log('Setting up XHR integration for FilePond');
|
||||
|
||||
// Store original submit function
|
||||
const originalSubmit = window.GravFormXHR.submit;
|
||||
|
||||
// Override to handle FilePond files
|
||||
window.GravFormXHR.submit = function (form) {
|
||||
if (!form) {
|
||||
return originalSubmit.apply(this, arguments);
|
||||
}
|
||||
|
||||
// Check for any FilePond instances in the form
|
||||
let hasPendingUploads = false;
|
||||
|
||||
// First check via our tracking
|
||||
Array.from(pondInstances.values()).forEach(info => {
|
||||
if (info.container.closest('form') === form) {
|
||||
const processingFiles = info.instance.getFiles().filter(file =>
|
||||
file.status === FilePond.FileStatus.PROCESSING_QUEUED ||
|
||||
file.status === FilePond.FileStatus.PROCESSING);
|
||||
|
||||
if (processingFiles.length > 0) {
|
||||
hasPendingUploads = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Fallback check for any untracked instances
|
||||
if (!hasPendingUploads) {
|
||||
const filepondContainers = form.querySelectorAll('.filepond-root');
|
||||
filepondContainers.forEach(container => {
|
||||
const pondElement = container.querySelector('.filepond--root');
|
||||
if (pondElement && pondElement._pond) {
|
||||
const pond = pondElement._pond;
|
||||
const processingFiles = pond.getFiles().filter(file =>
|
||||
file.status === FilePond.FileStatus.PROCESSING_QUEUED ||
|
||||
file.status === FilePond.FileStatus.PROCESSING);
|
||||
|
||||
if (processingFiles.length > 0) {
|
||||
hasPendingUploads = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (hasPendingUploads) {
|
||||
alert('Please wait for all files to finish uploading before submitting the form.');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call the original submit function
|
||||
return originalSubmit.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Set up listeners for form updates
|
||||
document.addEventListener('grav-form-updated', function (e) {
|
||||
log('Detected form update event, reinitializing FilePond instances');
|
||||
setTimeout(reinitializeFilePond, 100);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup mutation observer to detect dynamically added FilePond elements
|
||||
*/
|
||||
function setupMutationObserver() {
|
||||
if (window.MutationObserver) {
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
let shouldCheck = false;
|
||||
|
||||
for (const mutation of mutations) {
|
||||
if (mutation.type === 'childList' && mutation.addedNodes.length) {
|
||||
for (const node of mutation.addedNodes) {
|
||||
if (node.nodeType === 1) {
|
||||
if (node.classList && node.classList.contains('filepond-root') ||
|
||||
node.querySelector && node.querySelector('.filepond-root')) {
|
||||
shouldCheck = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCheck) break;
|
||||
}
|
||||
|
||||
if (shouldCheck) {
|
||||
log('DOM changes detected that might include FilePond elements');
|
||||
// Delay to ensure DOM is fully updated
|
||||
setTimeout(initializeFilePond, 50);
|
||||
}
|
||||
});
|
||||
|
||||
// Start observing
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
log('MutationObserver set up for FilePond elements');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize when DOM is ready
|
||||
*/
|
||||
function domReadyInit() {
|
||||
log('DOM ready, initializing FilePond');
|
||||
initializeFilePond();
|
||||
setupXHRIntegration();
|
||||
setupMutationObserver();
|
||||
}
|
||||
|
||||
// Handle different document ready states
|
||||
if (document.readyState === 'loading') {
|
||||
log('Document still loading, adding DOMContentLoaded listener');
|
||||
document.addEventListener('DOMContentLoaded', domReadyInit);
|
||||
} else {
|
||||
log('Document already loaded, initializing now');
|
||||
setTimeout(domReadyInit, 0);
|
||||
}
|
||||
|
||||
// Also support initialization via window load event as a fallback
|
||||
window.addEventListener('load', function () {
|
||||
log('Window load event fired');
|
||||
if (!initialized) {
|
||||
log('FilePond not yet initialized, initializing now');
|
||||
initializeFilePond();
|
||||
}
|
||||
});
|
||||
|
||||
// Expose functions to global scope for external usage
|
||||
window.GravFilePond = {
|
||||
initialize: initializeFilePond,
|
||||
reinitialize: reinitializeFilePond,
|
||||
reinitializeContainer: reinitializeSingleFilePond,
|
||||
getInstances: () => Array.from(pondInstances.values()).map(info => info.instance)
|
||||
};
|
||||
|
||||
// Log initialization start
|
||||
log('FilePond unified handler script loaded and ready');
|
||||
})();
|
||||
Reference in New Issue
Block a user