From d38d825e3484d822ea3c1f0fb1df37ecf386b18a Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 04 Jan 2026 19:54:16 +0000
Subject: [PATCH] =TaxonomyCreator.js debugging
---
assets/js/concise/FormController.js | 223 +++++++++++--------------------------------------------
1 files changed, 45 insertions(+), 178 deletions(-)
diff --git a/assets/js/concise/FormController.js b/assets/js/concise/FormController.js
index 38c1c22..a0388e2 100644
--- a/assets/js/concise/FormController.js
+++ b/assets/js/concise/FormController.js
@@ -1,7 +1,3 @@
-/**
- * Enhanced FormController - Manages forms with special fields, caching, and queue integration
- * Works with DataStore for CRUD operations and standalone for front-end forms
- */
class FormController {
constructor(config = {}) {
this.config = {
@@ -289,6 +285,7 @@
status: '',
options: {
autosave: 'autosave' in formElement.dataset,
+ autoUpload: true,
saveDelay: this.autoSaveDefaults.delay,
endpoint: formElement.dataset.save ?? '',
formStatus: true,
@@ -334,7 +331,7 @@
this.initCharacterLimits(form);
// Initialize image upload fields
- this.initImageUploadFields(form);
+ this.initImageUploadFields(form, formConfig);
// Initialize tabs if present
if (window.jvbTabs && form.querySelector('nav.tabs')) {
@@ -958,8 +955,8 @@
/**
* Initialize image upload fields
*/
- initImageUploadFields(form) {
- window.jvbUploads.scanFields(form);
+ initImageUploadFields(form, config) {
+ window.jvbUploads.scanFields(form, config.options.autoUpload);
}
/* ========== Event Handlers ========== */
@@ -981,14 +978,7 @@
fullData: formData,
config: formConfig
});
-
- // Don't delete yet - wait for success/error from subscriber
- return;
}
-
- // For forms that submit normally (not prevented)
- // We can clean up the cache on successful submission
- // This would typically be called from handleFormSuccess
}
handleFormSuccess(form, data) {
@@ -1320,7 +1310,7 @@
message: 'Please enter a valid URL starting with https://'
},
phone: {
- pattern: /^[\d\s\-\+\(\)\.]+$/,
+ pattern: /^[\d\s\-+().]+$/,
message: 'Please enter a valid phone number'
},
number: {
@@ -1543,145 +1533,6 @@
}
}
- /**
- * Validate all fields in a container (useful for step validation)
- */
- validateAllFields(container) {
- if (!container) return true;
-
- const fields = container.querySelectorAll('.field:not([hidden])');
- let allValid = true;
-
- fields.forEach(fieldWrapper => {
- // Skip complex parent wrappers (repeater, group) - validate their children
- if (this.isComplexFieldWrapper(fieldWrapper)) {
- return;
- }
-
- const input = fieldWrapper.querySelector('input:not([type="hidden"]), textarea, select');
- if (input && !input.closest('[hidden]')) {
- // Mark as touched so validation will run
- const fieldName = fieldWrapper.dataset.field;
- if (fieldName) {
- this.touchedFields.add(fieldName);
- }
-
- const isValid = this.validateField(input, fieldWrapper);
- if (!isValid) {
- allValid = false;
-
- // Scroll to first error
- if (allValid === false) {
- input.scrollIntoView({ behavior: 'smooth', block: 'center' });
- input.focus();
- }
- }
- }
- });
-
- return allValid;
- }
-
- /**
- * Check if field wrapper is a complex type (repeater, group, etc.)
- */
- isComplexFieldWrapper(fieldWrapper) {
- return fieldWrapper.classList.contains('repeater') ||
- fieldWrapper.classList.contains('group') ||
- fieldWrapper.classList.contains('upload');
- }
-
- /**
- * Special validation for repeater fields
- */
- attachRepeaterValidation(form) {
- // When a repeater row is added, attach validation to its fields
- form.addEventListener('click', (e) => {
- if (e.target.closest('.add-repeater-row')) {
- // Wait for the DOM to update
- setTimeout(() => {
- const repeaterRows = form.querySelectorAll('.repeater-row');
- repeaterRows.forEach(row => {
- const inputs = row.querySelectorAll('input, textarea, select');
- inputs.forEach(input => {
- const fieldWrapper = this.findFieldWrapper(input);
- if (fieldWrapper) {
- // Validation listeners are already attached via event delegation
- // Just clear any existing validation state for new rows
- this.clearValidation(fieldWrapper);
- }
- });
- });
- }, 100);
- }
- });
- }
-
- /**
- * Special validation for group fields
- */
- attachGroupValidation(form) {
- // Group fields might have conditional fields
- // Validate when conditions change
- form.addEventListener('change', (e) => {
- const changedInput = e.target.closest('input, select');
- if (!changedInput) return;
-
- // Check if this change affects conditional fields
- const fieldName = changedInput.name;
- if (!fieldName) return;
-
- // Find any conditional fields that depend on this field
- const conditionalFields = form.querySelectorAll(`[data-show-if*="${fieldName}"]`);
- conditionalFields.forEach(conditionalField => {
- // Clear validation for hidden fields
- if (conditionalField.hidden) {
- this.clearValidation(conditionalField);
- }
- });
- });
- }
-
- /**
- * Reset validation state for a form
- */
- resetForm(form) {
- if (!form) return;
-
- // Clear all touched fields
- this.touchedFields.clear();
-
- // Clear all validation states
- const fields = form.querySelectorAll('.field');
- fields.forEach(fieldWrapper => {
- this.clearValidation(fieldWrapper);
- });
- }
-
- /**
- * Get validation errors for a form
- */
- getFormErrors(form) {
- const errors = {};
- const fields = form.querySelectorAll('.field.has-error');
-
- fields.forEach(fieldWrapper => {
- const fieldName = fieldWrapper.dataset.field;
- const message = fieldWrapper.querySelector('.validation-message');
- if (fieldName && message) {
- errors[fieldName] = message.textContent;
- }
- });
-
- return errors;
- }
-
- /**
- * Add custom validator
- */
- addValidator(name, validator) {
- this.validators[name] = validator;
- }
/* ========== Auto-save functionality ========== */
/**
* Get appropriate delay based on field type and context
@@ -1875,7 +1726,7 @@
/* ========== Form Data Methods ========== */
- collectFormData(form, isInit = false) {
+ collectFormData(form) {
if (Object.hasOwn(form.dataset, 'timeline')) {
return this.collectTimeline(form);
}
@@ -1912,7 +1763,7 @@
if (this.ignore.includes(key) || key.endsWith('_temp')) {
continue;
}
- const match = key.match(/^\[(\d+)\](.+)$/);
+ const match = key.match(/^\[(\d+)](.+)$/);
if (match) {
// Timeline-specific field: [postId]fieldName
const [, postId, fieldName] = match;
@@ -1950,7 +1801,7 @@
getFieldProcessor(key) {
if (key.includes('::')) return this.processGroupField;
if (key.includes(':')) return this.processRepeaterField;
- if (/\[[^\]]+\]/.test(key)) return this.processLocationField;
+ if (/\[[^\]]+]/.test(key)) return this.processLocationField;
return this.processRegularField;
}
@@ -2122,7 +1973,9 @@
let p = field.querySelector('p');
title.textContent = fieldInfo.label;
- let formatted = this.formatFieldValue(value, fieldInfo.type);
+
+
+ let formatted = this.formatFieldValue(value, fieldInfo.type, form);
if (this.isHtmlContent(formatted)) {
p.innerHTML = formatted;
} else {
@@ -2131,6 +1984,27 @@
summary.append(field);
}
+ let uploads = form.querySelectorAll('[data-upload-field]');
+ if (uploads) {
+ uploads.forEach(upload => {
+ let label = upload.querySelector('h2').textContent;
+
+ let imgs = upload.querySelectorAll('.item-grid.preview img');
+ if (imgs) {
+ let field = wrapper.cloneNode(true);
+ let title = field.querySelector('h3');
+ let p = field.querySelector('p');
+ p.remove();
+
+ title.textContent = label;
+ imgs.forEach(img => {
+ img = img.cloneNode(true);
+ field.append(img);
+ });
+ summary.append(field);
+ }
+ });
+ }
// Remove template
wrapper.remove();
@@ -2152,10 +2026,8 @@
if (Array.isArray(value) && value.length === 0) {
return true;
}
- if (typeof value === 'object' && Object.keys(value).length === 0) {
- return true;
- }
- return false;
+ return typeof value === 'object' && Object.keys(value).length === 0;
+
}
/**
@@ -2166,8 +2038,6 @@
// Try to find label by 'for' attribute (exact match)
let label = form.querySelector(`label[for="${fieldName}"]`);
let input = form.querySelector(`[name=${fieldName}]`);
- let fieldWrapper = input?.closest('.field');
-
// Try to find the input field - check multiple patterns
if (!input) {
// Try exact match first
@@ -2193,12 +2063,12 @@
// Try closest field wrapper first
const field = input.closest('.field, fieldset');
if (field) {
- label = field.querySelector('label, legend');
+ label = field.querySelector('label, legend, h2');
}
}
// Get field wrapper - always use base name (no special characters)
- fieldWrapper = form.querySelector(`.field[data-field="${fieldName}"], fieldset[data-field="${fieldName}"]`);
+ let fieldWrapper = form.querySelector(`.field[data-field="${fieldName}"], fieldset[data-field="${fieldName}"]`);
// Determine field type
let fieldType = 'text';
@@ -2258,7 +2128,7 @@
if (Array.isArray(value)) {
return this.formatArrayValue(value);
}
- return (value === '1' || value === 1 || value === true) ? 'Yes' : 'No';
+ return (value === '1' || value === 1 || value === true) ? 'Yes' : value;
case 'select':
// Handle both single and multi-select
@@ -2285,8 +2155,7 @@
case 'location':
return this.formatLocationValue(value);
- case 'file':
- case 'image':
+ case 'upload':
return this.formatFileValue(value);
case 'number':
@@ -2529,13 +2398,6 @@
}
/**
- * Convert newlines to <br> tags (kept for backwards compatibility)
- */
- nl2br(text) {
- return this.formatPlainText(text);
- }
-
- /**
* Event system
*/
subscribe(callback) {
@@ -2594,6 +2456,11 @@
}
}
-document.addEventListener('DOMContentLoaded', () => {
- window.jvbForm = FormController;
+document.addEventListener('DOMContentLoaded', async function () {
+ window.auth.subscribe(event => {
+ if (event === 'auth-loaded') {
+ window.jvbForm = FormController;
+ }
+ });
+
});
--
Gitblit v1.10.0