@liedekef/ftable 1.3.4 → 1.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ftable.esm.js +37 -125
- package/ftable.js +37 -125
- package/ftable.min.js +2 -2
- package/ftable.umd.js +37 -125
- package/package.json +1 -1
package/ftable.esm.js
CHANGED
|
@@ -622,112 +622,46 @@ class FTableFormBuilder {
|
|
|
622
622
|
this.options = options;
|
|
623
623
|
this.dependencies = new Map(); // Track field dependencies
|
|
624
624
|
this.optionsCache = new FTableOptionsCache();
|
|
625
|
-
this.originalFieldOptions = new Map(); // Store original field.options
|
|
626
|
-
this.resolvedFieldOptions = new Map(); // Store resolved options per context
|
|
627
|
-
|
|
628
|
-
// Initialize with empty cache objects
|
|
629
|
-
Object.keys(this.options.fields || {}).forEach(fieldName => {
|
|
630
|
-
this.resolvedFieldOptions.set(fieldName, {});
|
|
631
|
-
});
|
|
632
|
-
Object.entries(this.options.fields).forEach(([fieldName, field]) => {
|
|
633
|
-
this.originalFieldOptions.set(fieldName, field.options);
|
|
634
|
-
});
|
|
635
625
|
}
|
|
636
626
|
|
|
637
|
-
// Get options for
|
|
627
|
+
// Get options for a field, respecting context ('search' prefers searchOptions over options).
|
|
628
|
+
// URL-level caching and concurrent-request deduplication is handled by FTableOptionsCache
|
|
629
|
+
// inside resolveOptions
|
|
638
630
|
async getFieldOptions(fieldName, context = 'table', params = {}) {
|
|
639
631
|
const field = this.options.fields[fieldName];
|
|
640
|
-
const originalOptions = this.originalFieldOptions.get(fieldName);
|
|
641
|
-
|
|
642
|
-
// If no options or already resolved for this context with same params, return cached
|
|
643
|
-
if (!originalOptions) {
|
|
644
|
-
return null;
|
|
645
|
-
}
|
|
646
632
|
|
|
647
|
-
//
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
633
|
+
// For search context, prefer searchOptions and fall back to options
|
|
634
|
+
const optionsSource = (context === 'search')
|
|
635
|
+
? (field.searchOptions ?? field.options)
|
|
636
|
+
: field.options;
|
|
637
|
+
|
|
638
|
+
if (!optionsSource) return null;
|
|
639
|
+
|
|
640
|
+
const noCache = this.shouldSkipCache(field, context, params);
|
|
655
641
|
|
|
656
642
|
try {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
// we store the resolved option always
|
|
664
|
-
this.resolvedFieldOptions.get(fieldName)[cacheKey] = resolved;
|
|
665
|
-
return resolved;
|
|
643
|
+
return await this.resolveOptions(
|
|
644
|
+
{ ...field, options: optionsSource },
|
|
645
|
+
params,
|
|
646
|
+
context,
|
|
647
|
+
noCache
|
|
648
|
+
);
|
|
666
649
|
} catch (err) {
|
|
667
650
|
console.error(`Failed to resolve options for ${fieldName} (${context}):`, err);
|
|
668
|
-
return
|
|
651
|
+
return optionsSource;
|
|
669
652
|
}
|
|
670
653
|
}
|
|
671
654
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
* @param {string|null} context - Context to clear ('table', 'create', 'edit'), or null for all contexts
|
|
676
|
-
*/
|
|
677
|
-
clearResolvedOptions(fieldName = null, context = null) {
|
|
678
|
-
if (fieldName) {
|
|
679
|
-
// Clear specific field
|
|
680
|
-
if (this.resolvedFieldOptions.has(fieldName)) {
|
|
681
|
-
if (context) {
|
|
682
|
-
// Clear specific context for specific field
|
|
683
|
-
this.resolvedFieldOptions.get(fieldName)[context] = null;
|
|
684
|
-
} else {
|
|
685
|
-
// Clear all contexts for specific field
|
|
686
|
-
this.resolvedFieldOptions.set(fieldName, { table: null, create: null, edit: null });
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
} else {
|
|
690
|
-
// Clear all fields
|
|
691
|
-
if (context) {
|
|
692
|
-
// Clear specific context for all fields
|
|
693
|
-
this.resolvedFieldOptions.forEach((value, key) => {
|
|
694
|
-
this.resolvedFieldOptions.get(key)[context] = null;
|
|
695
|
-
});
|
|
696
|
-
} else {
|
|
697
|
-
// Clear everything
|
|
698
|
-
this.resolvedFieldOptions.forEach((value, key) => {
|
|
699
|
-
this.resolvedFieldOptions.set(key, { table: null, create: null, edit: null });
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
// Helper method to determine caching behavior
|
|
706
|
-
shouldForceRefreshForContext(field, context, params) {
|
|
707
|
-
// Rename to reflect what it actually does now
|
|
655
|
+
// Determine whether to bypass the URL cache for this field/context
|
|
656
|
+
shouldSkipCache(field, context, params) {
|
|
657
|
+
if (params.forceRefresh) return true;
|
|
708
658
|
if (!field.noCache) return false;
|
|
709
|
-
|
|
710
659
|
if (typeof field.noCache === 'boolean') return field.noCache;
|
|
711
660
|
if (typeof field.noCache === 'function') return field.noCache({ context, ...params });
|
|
712
661
|
if (typeof field.noCache === 'object') return field.noCache[context] === true;
|
|
713
|
-
|
|
714
662
|
return false;
|
|
715
663
|
}
|
|
716
664
|
|
|
717
|
-
generateOptionsCacheKey(context, params) {
|
|
718
|
-
// Create a unique key based on context and dependency values
|
|
719
|
-
const keyParts = [context];
|
|
720
|
-
|
|
721
|
-
if (params.dependedValues) {
|
|
722
|
-
// Include relevant dependency values in the cache key
|
|
723
|
-
Object.keys(params.dependedValues).sort().forEach(key => {
|
|
724
|
-
keyParts.push(`${key}=${params.dependedValues[key]}`);
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
return keyParts.join('|');
|
|
729
|
-
}
|
|
730
|
-
|
|
731
665
|
shouldIncludeField(field, formType) {
|
|
732
666
|
if (formType === 'create') {
|
|
733
667
|
return field.create !== false && !(field.key === true && field.create !== true);
|
|
@@ -800,12 +734,6 @@ class FTableFormBuilder {
|
|
|
800
734
|
return form;
|
|
801
735
|
}
|
|
802
736
|
|
|
803
|
-
shouldResolveOptions(options) {
|
|
804
|
-
return options &&
|
|
805
|
-
(typeof options === 'function' || typeof options === 'string') &&
|
|
806
|
-
!Array.isArray(options) &&
|
|
807
|
-
!(typeof options === 'object' && !Array.isArray(options) && Object.keys(options).length > 0);
|
|
808
|
-
}
|
|
809
737
|
|
|
810
738
|
buildDependencyMap() {
|
|
811
739
|
this.dependencies.clear();
|
|
@@ -2164,13 +2092,13 @@ class FTable extends FTableEventEmitter {
|
|
|
2164
2092
|
}
|
|
2165
2093
|
|
|
2166
2094
|
// Start resolving in background
|
|
2167
|
-
this.
|
|
2095
|
+
this.resolveAllFieldOptionsForTable().then(() => {
|
|
2168
2096
|
// re-render dynamic options rows — no server call
|
|
2169
2097
|
// this is needed so that once options are resolved, the table shows correct display values
|
|
2170
2098
|
// why: load() can actually finish faster than option resolving (and calling refreshDisplayValues
|
|
2171
2099
|
// there is then pointless, since the resolving hasn't finished yet),
|
|
2172
2100
|
// so we need to do it when the options are actually resolved (here)
|
|
2173
|
-
// We could call await this.
|
|
2101
|
+
// We could call await this.resolveAllFieldOptionsForTable() during load, but that would slow down the loading ...
|
|
2174
2102
|
setTimeout(() => {
|
|
2175
2103
|
this.refreshDisplayValues();
|
|
2176
2104
|
}, 0);
|
|
@@ -2391,22 +2319,17 @@ class FTable extends FTableEventEmitter {
|
|
|
2391
2319
|
}
|
|
2392
2320
|
}
|
|
2393
2321
|
|
|
2394
|
-
async
|
|
2322
|
+
async resolveAllFieldOptionsForTable() {
|
|
2323
|
+
this.tableOptionsCache = new Map();
|
|
2324
|
+
|
|
2395
2325
|
const promises = this.columnList.map(async (fieldName) => {
|
|
2396
2326
|
const field = this.options.fields[fieldName];
|
|
2397
2327
|
if (field.action) return; // Skip action columns
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
const cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
|
|
2404
|
-
if (!this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey]) {
|
|
2405
|
-
await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
2406
|
-
}
|
|
2407
|
-
} catch (err) {
|
|
2408
|
-
console.error(`Failed to resolve table options for ${fieldName}:`, err);
|
|
2409
|
-
}
|
|
2328
|
+
try {
|
|
2329
|
+
const resolved = await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
2330
|
+
if (resolved) this.tableOptionsCache.set(fieldName, resolved);
|
|
2331
|
+
} catch (err) {
|
|
2332
|
+
console.error(`Failed to resolve table options for ${fieldName}:`, err);
|
|
2410
2333
|
}
|
|
2411
2334
|
});
|
|
2412
2335
|
|
|
@@ -2425,9 +2348,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2425
2348
|
const cell = row.querySelector(`td[data-field-name="${fieldName}"]`);
|
|
2426
2349
|
if (!cell) continue;
|
|
2427
2350
|
|
|
2428
|
-
|
|
2429
|
-
const cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
|
|
2430
|
-
const resolvedOptions = this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey];
|
|
2351
|
+
const resolvedOptions = this.tableOptionsCache?.get(fieldName);
|
|
2431
2352
|
const value = this.getDisplayText(row.recordData, fieldName, resolvedOptions);
|
|
2432
2353
|
cell.innerHTML = field.listEscapeHTML ? FTableDOMHelper.escapeHtml(value) : value;
|
|
2433
2354
|
}
|
|
@@ -2872,8 +2793,8 @@ class FTable extends FTableEventEmitter {
|
|
|
2872
2793
|
Value: value,
|
|
2873
2794
|
DisplayText: displayText
|
|
2874
2795
|
}));
|
|
2875
|
-
} else if (field.options) {
|
|
2876
|
-
optionsSource = await this.formBuilder.getFieldOptions(fieldName);
|
|
2796
|
+
} else if (field.options || field.searchOptions) {
|
|
2797
|
+
optionsSource = await this.formBuilder.getFieldOptions(fieldName, 'search');
|
|
2877
2798
|
}
|
|
2878
2799
|
|
|
2879
2800
|
// If multiple, create custom UI
|
|
@@ -2976,14 +2897,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2976
2897
|
input.datalistElement = datalist;
|
|
2977
2898
|
|
|
2978
2899
|
// Load options for the datalist
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
// Use search-specific options if available
|
|
2982
|
-
if (field.searchOptions) {
|
|
2983
|
-
optionsSource = field.searchOptions;
|
|
2984
|
-
} else if (field.options) {
|
|
2985
|
-
optionsSource = await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
2986
|
-
}
|
|
2900
|
+
const optionsSource = await this.formBuilder.getFieldOptions(fieldName, 'search');
|
|
2987
2901
|
|
|
2988
2902
|
// Populate datalist with options
|
|
2989
2903
|
if (optionsSource) {
|
|
@@ -4020,8 +3934,7 @@ class FTable extends FTableEventEmitter {
|
|
|
4020
3934
|
|
|
4021
3935
|
addDataCell(row, record, fieldName) {
|
|
4022
3936
|
const field = this.options.fields[fieldName];
|
|
4023
|
-
const
|
|
4024
|
-
const resolvedOptions = this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey];
|
|
3937
|
+
const resolvedOptions = this.tableOptionsCache?.get(fieldName);
|
|
4025
3938
|
const value = this.getDisplayText(record, fieldName, resolvedOptions);
|
|
4026
3939
|
|
|
4027
3940
|
const cell = FTableDOMHelper.create('td', {
|
|
@@ -4524,8 +4437,7 @@ class FTable extends FTableEventEmitter {
|
|
|
4524
4437
|
if (!cell) return;
|
|
4525
4438
|
|
|
4526
4439
|
// Get display text
|
|
4527
|
-
const
|
|
4528
|
-
const resolvedOptions = this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey];
|
|
4440
|
+
const resolvedOptions = this.tableOptionsCache?.get(fieldName);
|
|
4529
4441
|
const value = this.getDisplayText(row.recordData, fieldName, resolvedOptions);
|
|
4530
4442
|
cell.innerHTML = field.listEscapeHTML ? FTableDOMHelper.escapeHtml(value) : value;
|
|
4531
4443
|
cell.className = `${field.listClass || ''} ${field.listClassEntry || ''}`.trim();
|
package/ftable.js
CHANGED
|
@@ -623,112 +623,46 @@ class FTableFormBuilder {
|
|
|
623
623
|
this.options = options;
|
|
624
624
|
this.dependencies = new Map(); // Track field dependencies
|
|
625
625
|
this.optionsCache = new FTableOptionsCache();
|
|
626
|
-
this.originalFieldOptions = new Map(); // Store original field.options
|
|
627
|
-
this.resolvedFieldOptions = new Map(); // Store resolved options per context
|
|
628
|
-
|
|
629
|
-
// Initialize with empty cache objects
|
|
630
|
-
Object.keys(this.options.fields || {}).forEach(fieldName => {
|
|
631
|
-
this.resolvedFieldOptions.set(fieldName, {});
|
|
632
|
-
});
|
|
633
|
-
Object.entries(this.options.fields).forEach(([fieldName, field]) => {
|
|
634
|
-
this.originalFieldOptions.set(fieldName, field.options);
|
|
635
|
-
});
|
|
636
626
|
}
|
|
637
627
|
|
|
638
|
-
// Get options for
|
|
628
|
+
// Get options for a field, respecting context ('search' prefers searchOptions over options).
|
|
629
|
+
// URL-level caching and concurrent-request deduplication is handled by FTableOptionsCache
|
|
630
|
+
// inside resolveOptions
|
|
639
631
|
async getFieldOptions(fieldName, context = 'table', params = {}) {
|
|
640
632
|
const field = this.options.fields[fieldName];
|
|
641
|
-
const originalOptions = this.originalFieldOptions.get(fieldName);
|
|
642
|
-
|
|
643
|
-
// If no options or already resolved for this context with same params, return cached
|
|
644
|
-
if (!originalOptions) {
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
633
|
|
|
648
|
-
//
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
634
|
+
// For search context, prefer searchOptions and fall back to options
|
|
635
|
+
const optionsSource = (context === 'search')
|
|
636
|
+
? (field.searchOptions ?? field.options)
|
|
637
|
+
: field.options;
|
|
638
|
+
|
|
639
|
+
if (!optionsSource) return null;
|
|
640
|
+
|
|
641
|
+
const noCache = this.shouldSkipCache(field, context, params);
|
|
656
642
|
|
|
657
643
|
try {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
// we store the resolved option always
|
|
665
|
-
this.resolvedFieldOptions.get(fieldName)[cacheKey] = resolved;
|
|
666
|
-
return resolved;
|
|
644
|
+
return await this.resolveOptions(
|
|
645
|
+
{ ...field, options: optionsSource },
|
|
646
|
+
params,
|
|
647
|
+
context,
|
|
648
|
+
noCache
|
|
649
|
+
);
|
|
667
650
|
} catch (err) {
|
|
668
651
|
console.error(`Failed to resolve options for ${fieldName} (${context}):`, err);
|
|
669
|
-
return
|
|
652
|
+
return optionsSource;
|
|
670
653
|
}
|
|
671
654
|
}
|
|
672
655
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
* @param {string|null} context - Context to clear ('table', 'create', 'edit'), or null for all contexts
|
|
677
|
-
*/
|
|
678
|
-
clearResolvedOptions(fieldName = null, context = null) {
|
|
679
|
-
if (fieldName) {
|
|
680
|
-
// Clear specific field
|
|
681
|
-
if (this.resolvedFieldOptions.has(fieldName)) {
|
|
682
|
-
if (context) {
|
|
683
|
-
// Clear specific context for specific field
|
|
684
|
-
this.resolvedFieldOptions.get(fieldName)[context] = null;
|
|
685
|
-
} else {
|
|
686
|
-
// Clear all contexts for specific field
|
|
687
|
-
this.resolvedFieldOptions.set(fieldName, { table: null, create: null, edit: null });
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
} else {
|
|
691
|
-
// Clear all fields
|
|
692
|
-
if (context) {
|
|
693
|
-
// Clear specific context for all fields
|
|
694
|
-
this.resolvedFieldOptions.forEach((value, key) => {
|
|
695
|
-
this.resolvedFieldOptions.get(key)[context] = null;
|
|
696
|
-
});
|
|
697
|
-
} else {
|
|
698
|
-
// Clear everything
|
|
699
|
-
this.resolvedFieldOptions.forEach((value, key) => {
|
|
700
|
-
this.resolvedFieldOptions.set(key, { table: null, create: null, edit: null });
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// Helper method to determine caching behavior
|
|
707
|
-
shouldForceRefreshForContext(field, context, params) {
|
|
708
|
-
// Rename to reflect what it actually does now
|
|
656
|
+
// Determine whether to bypass the URL cache for this field/context
|
|
657
|
+
shouldSkipCache(field, context, params) {
|
|
658
|
+
if (params.forceRefresh) return true;
|
|
709
659
|
if (!field.noCache) return false;
|
|
710
|
-
|
|
711
660
|
if (typeof field.noCache === 'boolean') return field.noCache;
|
|
712
661
|
if (typeof field.noCache === 'function') return field.noCache({ context, ...params });
|
|
713
662
|
if (typeof field.noCache === 'object') return field.noCache[context] === true;
|
|
714
|
-
|
|
715
663
|
return false;
|
|
716
664
|
}
|
|
717
665
|
|
|
718
|
-
generateOptionsCacheKey(context, params) {
|
|
719
|
-
// Create a unique key based on context and dependency values
|
|
720
|
-
const keyParts = [context];
|
|
721
|
-
|
|
722
|
-
if (params.dependedValues) {
|
|
723
|
-
// Include relevant dependency values in the cache key
|
|
724
|
-
Object.keys(params.dependedValues).sort().forEach(key => {
|
|
725
|
-
keyParts.push(`${key}=${params.dependedValues[key]}`);
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
return keyParts.join('|');
|
|
730
|
-
}
|
|
731
|
-
|
|
732
666
|
shouldIncludeField(field, formType) {
|
|
733
667
|
if (formType === 'create') {
|
|
734
668
|
return field.create !== false && !(field.key === true && field.create !== true);
|
|
@@ -801,12 +735,6 @@ class FTableFormBuilder {
|
|
|
801
735
|
return form;
|
|
802
736
|
}
|
|
803
737
|
|
|
804
|
-
shouldResolveOptions(options) {
|
|
805
|
-
return options &&
|
|
806
|
-
(typeof options === 'function' || typeof options === 'string') &&
|
|
807
|
-
!Array.isArray(options) &&
|
|
808
|
-
!(typeof options === 'object' && !Array.isArray(options) && Object.keys(options).length > 0);
|
|
809
|
-
}
|
|
810
738
|
|
|
811
739
|
buildDependencyMap() {
|
|
812
740
|
this.dependencies.clear();
|
|
@@ -2165,13 +2093,13 @@ class FTable extends FTableEventEmitter {
|
|
|
2165
2093
|
}
|
|
2166
2094
|
|
|
2167
2095
|
// Start resolving in background
|
|
2168
|
-
this.
|
|
2096
|
+
this.resolveAllFieldOptionsForTable().then(() => {
|
|
2169
2097
|
// re-render dynamic options rows — no server call
|
|
2170
2098
|
// this is needed so that once options are resolved, the table shows correct display values
|
|
2171
2099
|
// why: load() can actually finish faster than option resolving (and calling refreshDisplayValues
|
|
2172
2100
|
// there is then pointless, since the resolving hasn't finished yet),
|
|
2173
2101
|
// so we need to do it when the options are actually resolved (here)
|
|
2174
|
-
// We could call await this.
|
|
2102
|
+
// We could call await this.resolveAllFieldOptionsForTable() during load, but that would slow down the loading ...
|
|
2175
2103
|
setTimeout(() => {
|
|
2176
2104
|
this.refreshDisplayValues();
|
|
2177
2105
|
}, 0);
|
|
@@ -2392,22 +2320,17 @@ class FTable extends FTableEventEmitter {
|
|
|
2392
2320
|
}
|
|
2393
2321
|
}
|
|
2394
2322
|
|
|
2395
|
-
async
|
|
2323
|
+
async resolveAllFieldOptionsForTable() {
|
|
2324
|
+
this.tableOptionsCache = new Map();
|
|
2325
|
+
|
|
2396
2326
|
const promises = this.columnList.map(async (fieldName) => {
|
|
2397
2327
|
const field = this.options.fields[fieldName];
|
|
2398
2328
|
if (field.action) return; // Skip action columns
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
const cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
|
|
2405
|
-
if (!this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey]) {
|
|
2406
|
-
await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
2407
|
-
}
|
|
2408
|
-
} catch (err) {
|
|
2409
|
-
console.error(`Failed to resolve table options for ${fieldName}:`, err);
|
|
2410
|
-
}
|
|
2329
|
+
try {
|
|
2330
|
+
const resolved = await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
2331
|
+
if (resolved) this.tableOptionsCache.set(fieldName, resolved);
|
|
2332
|
+
} catch (err) {
|
|
2333
|
+
console.error(`Failed to resolve table options for ${fieldName}:`, err);
|
|
2411
2334
|
}
|
|
2412
2335
|
});
|
|
2413
2336
|
|
|
@@ -2426,9 +2349,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2426
2349
|
const cell = row.querySelector(`td[data-field-name="${fieldName}"]`);
|
|
2427
2350
|
if (!cell) continue;
|
|
2428
2351
|
|
|
2429
|
-
|
|
2430
|
-
const cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
|
|
2431
|
-
const resolvedOptions = this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey];
|
|
2352
|
+
const resolvedOptions = this.tableOptionsCache?.get(fieldName);
|
|
2432
2353
|
const value = this.getDisplayText(row.recordData, fieldName, resolvedOptions);
|
|
2433
2354
|
cell.innerHTML = field.listEscapeHTML ? FTableDOMHelper.escapeHtml(value) : value;
|
|
2434
2355
|
}
|
|
@@ -2873,8 +2794,8 @@ class FTable extends FTableEventEmitter {
|
|
|
2873
2794
|
Value: value,
|
|
2874
2795
|
DisplayText: displayText
|
|
2875
2796
|
}));
|
|
2876
|
-
} else if (field.options) {
|
|
2877
|
-
optionsSource = await this.formBuilder.getFieldOptions(fieldName);
|
|
2797
|
+
} else if (field.options || field.searchOptions) {
|
|
2798
|
+
optionsSource = await this.formBuilder.getFieldOptions(fieldName, 'search');
|
|
2878
2799
|
}
|
|
2879
2800
|
|
|
2880
2801
|
// If multiple, create custom UI
|
|
@@ -2977,14 +2898,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2977
2898
|
input.datalistElement = datalist;
|
|
2978
2899
|
|
|
2979
2900
|
// Load options for the datalist
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
// Use search-specific options if available
|
|
2983
|
-
if (field.searchOptions) {
|
|
2984
|
-
optionsSource = field.searchOptions;
|
|
2985
|
-
} else if (field.options) {
|
|
2986
|
-
optionsSource = await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
2987
|
-
}
|
|
2901
|
+
const optionsSource = await this.formBuilder.getFieldOptions(fieldName, 'search');
|
|
2988
2902
|
|
|
2989
2903
|
// Populate datalist with options
|
|
2990
2904
|
if (optionsSource) {
|
|
@@ -4021,8 +3935,7 @@ class FTable extends FTableEventEmitter {
|
|
|
4021
3935
|
|
|
4022
3936
|
addDataCell(row, record, fieldName) {
|
|
4023
3937
|
const field = this.options.fields[fieldName];
|
|
4024
|
-
const
|
|
4025
|
-
const resolvedOptions = this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey];
|
|
3938
|
+
const resolvedOptions = this.tableOptionsCache?.get(fieldName);
|
|
4026
3939
|
const value = this.getDisplayText(record, fieldName, resolvedOptions);
|
|
4027
3940
|
|
|
4028
3941
|
const cell = FTableDOMHelper.create('td', {
|
|
@@ -4525,8 +4438,7 @@ class FTable extends FTableEventEmitter {
|
|
|
4525
4438
|
if (!cell) return;
|
|
4526
4439
|
|
|
4527
4440
|
// Get display text
|
|
4528
|
-
const
|
|
4529
|
-
const resolvedOptions = this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey];
|
|
4441
|
+
const resolvedOptions = this.tableOptionsCache?.get(fieldName);
|
|
4530
4442
|
const value = this.getDisplayText(row.recordData, fieldName, resolvedOptions);
|
|
4531
4443
|
cell.innerHTML = field.listEscapeHTML ? FTableDOMHelper.escapeHtml(value) : value;
|
|
4532
4444
|
cell.className = `${field.listClass || ''} ${field.listClassEntry || ''}`.trim();
|
package/ftable.min.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(e=>{let s={serverCommunicationError:"An error occurred while communicating to the server.",loadingMessage:"Loading records...",noDataAvailable:"No data available!",addNewRecord:"Add new record",editRecord:"Edit record",areYouSure:"Are you sure?",deleteConfirmation:"This record will be deleted. Are you sure?",yes:"Yes",no:"No",save:"Save",saving:"Saving",cancel:"Cancel",deleteText:"Delete",deleting:"Deleting",error:"An error has occured",warning:"Warning",close:"Close",cannotLoadOptionsFor:"Cannot load options for field {0}!",pagingInfo:"Showing {0}-{1} of {2}",canNotDeletedRecords:"Can not delete {0} of {1} records!",deleteProgress:"Deleting {0} of {1} records, processing...",pageSizeChangeLabel:"Row count",gotoPageLabel:"Go to page",sortingInfoPrefix:"Sorting applied: ",sortingInfoSuffix:"",ascending:"Ascending",descending:"Descending",sortingInfoNone:"No sorting applied",resetSorting:"Reset sorting",csvExport:"CSV",printTable:"🖨️ Print",cloneRecord:"Clone Record",resetTable:"Reset table",resetTableConfirm:"This will reset all columns, pagesize, sorting to their defaults. Do you want to continue?",resetSearch:"Reset"};class t{constructor(){this.cache=new Map,this.pendingRequests=new Map}generateKey(e,t){return e+"?"+Object.keys(t||{}).sort().map(e=>e+"="+t[e]).join("&")}get(e,t){e=this.generateKey(e,t);return this.cache.get(e)}set(e,t,s){e=this.generateKey(e,t);this.cache.set(e,s)}clear(e=null,t=null){if(e)if(t){t=this.generateKey(e,t);this.cache.delete(t)}else{var s,a=e.split("?")[0];for([s]of this.cache)s.startsWith(a)&&this.cache.delete(s)}else this.cache.clear()}async getOrCreate(e,t,s){let a=this.generateKey(e,t);e=this.cache.get(a);return e||(this.pendingRequests.has(a)?this.pendingRequests.get(a):(t=(async()=>{try{var e=await s();return this.cache.set(a,e),e}finally{this.pendingRequests.delete(a)}})(),this.pendingRequests.set(a,t),t))}size(){return this.cache.size}}class a{static LOG_LEVELS={DEBUG:0,INFO:1,WARN:2,ERROR:3,NONE:4};constructor(e=a.LOG_LEVELS.WARN){this.level=e}log(t,e){var s;!window.console||t<this.level||(s=Object.keys(a.LOG_LEVELS).find(e=>a.LOG_LEVELS[e]===t),console.log(`fTable ${s}: `+e))}debug(e){this.log(a.LOG_LEVELS.DEBUG,e)}info(e){this.log(a.LOG_LEVELS.INFO,e)}warn(e){this.log(a.LOG_LEVELS.WARN,e)}error(e){this.log(a.LOG_LEVELS.ERROR,e)}}class O{static PROPERTY_ATTRIBUTES=new Set(["value","checked","selected","disabled","readOnly","name","id","type","placeholder","min","max","step","required","multiple","accept","className","textContent","innerHTML","title"]);static create(e,t={}){let s=document.createElement(e);return void 0!==t.style&&(s.style.cssText=t.style),O.PROPERTY_ATTRIBUTES.forEach(e=>{e in t&&null!==t[e]&&(s[e]=t[e])}),void 0!==t.parent&&t.parent.appendChild(s),t.attributes&&Object.entries(t.attributes).forEach(([e,t])=>{null!==t&&(O.PROPERTY_ATTRIBUTES.has(e)?s[e]=t:s.setAttribute(e,t))}),s}static find(e,t=document){return t.querySelector(e)}static findAll(e,t=document){return Array.from(t.querySelectorAll(e))}static addClass(e,t){e.classList.add(...t.split(" "))}static removeClass(e,t){e.classList.remove(...t.split(" "))}static toggleClass(e,t){e.classList.toggle(t)}static show(e){e.style.display=""}static hide(e){e.style.display="none"}static escapeHtml(e){if(!e)return e;let t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,e=>t[e])}}class l{static async request(e,t={}){var s={method:"GET",headers:{}},a={...s,...t};t.headers&&(a.headers={...s.headers,...t.headers});try{var i=await fetch(e,a);if(401===i.status)throw new Error("Unauthorized");if(!i.ok)throw new Error("HTTP error! status: "+i.status);var o=i.headers.get("content-type");if(o&&o.includes("application/json"))return await i.json();var r=await i.text();try{return JSON.parse(r)}catch{return{Result:"OK",Message:r}}}catch(e){throw e}}static async get(e,t={}){let a=new URL(e,window.location.href);return Object.entries(t).forEach(([e,s])=>{if(null!=s)if(Array.isArray(s)){let t=e.endsWith("[]")?e:e+"[]";s.forEach(e=>{null!=e&&a.searchParams.append(t,e)})}else a.searchParams.append(e,s)}),this.request(a.toString(),{method:"GET",headers:{"Content-Type":"application/x-www-form-urlencoded"}})}static async post(e,t={}){e=new URL(e,window.location.href);let a=new FormData;return Object.entries(t).forEach(([e,s])=>{if(null!=s)if(Array.isArray(s)){let t=e.endsWith("[]")?e:e+"[]";s.forEach(e=>{null!=e&&a.append(t,e)})}else a.append(e,s)}),this.request(e.toString(),{method:"POST",body:a})}}class i{constructor(e,t="localStorage"){this.prefix=e,this.method=t}set(e,t){var s,e=""+this.prefix+e;"localStorage"===this.method?localStorage.setItem(e,t):((s=new Date).setDate(s.getDate()+30),document.cookie=e+`=${t}; expires=${s.toUTCString()}; path=/`)}get(e){e=""+this.prefix+e;if("localStorage"===this.method)return localStorage.getItem(e);var t,s=e+"=";for(t of decodeURIComponent(document.cookie).split(";")){for(;" "===t.charAt(0);)t=t.substring(1);if(0===t.indexOf(s))return t.substring(s.length,t.length)}return null}remove(e){e=""+this.prefix+e;"localStorage"===this.method?localStorage.removeItem(e):document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"}generatePrefix(e,t){e=e?e+"#":"";return"ftable#"+(t=>{let s=0;if(0!==t.length)for(let e=0;e<t.length;e++){var a=t.charCodeAt(e);s=(s<<5)-s+a,s&=s}return s})(e+=t.join("$")+"#c"+t.length)}}class o{constructor(e={}){this.options={title:"Modal",content:"",buttons:[],className:"ftable-modal",parent:document.body,...e},this.overlay=null,this.modal=null,this.isOpen=!1}create(){this.overlay=O.create("div",{className:"ftable-modal-overlay",parent:this.options.parent}),this.modal=O.create("div",{className:"ftable-modal "+this.options.className,parent:this.overlay});O.create("h2",{className:"ftable-modal-header",textContent:this.options.title,parent:this.modal});O.create("span",{className:"ftable-modal-close",innerHTML:"×",parent:this.modal}).addEventListener("click",()=>this.close());var e=O.create("div",{className:"ftable-modal-body",parent:this.modal});if("string"==typeof this.options.content?e.innerHTML=this.options.content:e.appendChild(this.options.content),0<this.options.buttons.length){let s=O.create("div",{className:"ftable-modal-footer",parent:this.modal});this.options.buttons.forEach(e=>{var t=O.create("button",{className:"ftable-dialog-button "+(e.className||""),innerHTML:`<span>${e.text}</span>`,parent:s});e.onClick&&(t._originalOnClick=e.onClick,t.addEventListener("click",this._createWrappedClickHandler(t)))})}return this.options.closeOnOverlayClick&&this.overlay.addEventListener("click",e=>{e.target===this.overlay&&this.close()}),this.hide(),this}show(){return this.modal||this.create(),this.overlay.style.display="flex",this.isOpen=!0,this.modal.querySelectorAll(".ftable-dialog-button").forEach(e=>{e.disabled=!1}),this}hide(){return this.overlay&&(this.overlay.style.display="none"),this.isOpen=!1,this}close(){return this.hide(),this.options.onClose&&this.options.onClose(),this}destroy(){return this.overlay&&this.overlay.remove(),this.isOpen=!1,this}setContent(e){this.options.content=e;var t=this.modal.querySelector(".ftable-modal-body");t&&(t.innerHTML="","string"==typeof e?t.innerHTML=/<(div|ul|ol|table|p|h[1-6]|blockquote)/i.test(e)?e:`<p>${e}</p>`:t.appendChild(e))}_createWrappedClickHandler(a){return async e=>{a.disabled=!0;try{var t,s=a._originalOnClick;"function"==typeof s&&(t=s.call(a,e))instanceof Promise&&await t}catch(e){console.error("Modal button action failed:",e)}finally{a.disabled=!1}}}}class r{constructor(e){this.options=e,this.dependencies=new Map,this.optionsCache=new t,this.originalFieldOptions=new Map,this.resolvedFieldOptions=new Map,Object.keys(this.options.fields||{}).forEach(e=>{this.resolvedFieldOptions.set(e,{})}),Object.entries(this.options.fields).forEach(([e,t])=>{this.originalFieldOptions.set(e,t.options)})}async getFieldOptions(t,s="table",e={}){var a=this.options.fields[t],i=this.originalFieldOptions.get(t);if(!i)return null;var o=this.shouldForceRefreshForContext(a,s,e),r=this.generateOptionsCacheKey(s,e);if(!o&&!e.forceRefresh){var n=this.resolvedFieldOptions.get(t)[r];if(n)return n}try{var l={...a,options:i},c=await this.resolveOptions(l,{...e},s,o);return this.resolvedFieldOptions.get(t)[r]=c}catch(e){return console.error(`Failed to resolve options for ${t} (${s}):`,e),i}}clearResolvedOptions(e=null,s=null){e?this.resolvedFieldOptions.has(e)&&(s?this.resolvedFieldOptions.get(e)[s]=null:this.resolvedFieldOptions.set(e,{table:null,create:null,edit:null})):s?this.resolvedFieldOptions.forEach((e,t)=>{this.resolvedFieldOptions.get(t)[s]=null}):this.resolvedFieldOptions.forEach((e,t)=>{this.resolvedFieldOptions.set(t,{table:null,create:null,edit:null})})}shouldForceRefreshForContext(e,t,s){return!!e.noCache&&("boolean"==typeof e.noCache?e.noCache:"function"==typeof e.noCache?e.noCache({context:t,...s}):"object"==typeof e.noCache&&!0===e.noCache[t])}generateOptionsCacheKey(e,t){let s=[e];return t.dependedValues&&Object.keys(t.dependedValues).sort().forEach(e=>{s.push(e+"="+t.dependedValues[e])}),s.join("|")}shouldIncludeField(e,t){return"create"===t?!1!==e.create&&!(!0===e.key&&!0!==e.create):"edit"!==t||!1!==e.edit}createFieldContainer(e,t,s,a){var i=O.create("div",{className:"hidden"!=t.type?"ftable-input-field-container":"",attributes:{id:"ftable-input-field-container-div-"+e}}),t=("hidden"!=t.type&&O.create("div",{className:"ftable-input-label",textContent:t.inputTitle||t.title,parent:i}),this.createInput(e,t,s[e],a));return i.appendChild(t),i}async createForm(e="create",t={}){this.currentFormRecord=t;var s,a,i,o,r=O.create("form",{className:`ftable-dialog-form ftable-${e}-form`});this.buildDependencyMap();for([s,a]of Object.entries(this.options.fields))this.shouldIncludeField(a,e)&&(i={...a},a.dependsOn?i.options=a.options:(o=await this.getFieldOptions(s,e,{record:t,source:e}),i.options=o),o=this.createFieldContainer(s,i,t,e),r.appendChild(o));return this.setupDependencyListeners(r),r}shouldResolveOptions(e){return e&&("function"==typeof e||"string"==typeof e)&&!Array.isArray(e)&&!("object"==typeof e&&!Array.isArray(e)&&0<Object.keys(e).length)}buildDependencyMap(){this.dependencies.clear(),Object.entries(this.options.fields).forEach(([t,s])=>{if(s.dependsOn){let e;"string"==typeof s.dependsOn&&(e=s.dependsOn.split(",").map(e=>e.trim()).filter(e=>e)).forEach(e=>{this.dependencies.has(e)||this.dependencies.set(e,[]),this.dependencies.get(e).push(t)})}})}setupDependencyListeners(s){Array.from(this.dependencies.keys()).forEach(e=>{var t=s.querySelector(`[name="${e}"]`);t&&t.addEventListener("change",()=>{this.handleDependencyChange(s,e)})}),this.handleDependencyChange(s)}async resolveOptions(e,t={},s="",a=!1){if(!e.options)return[];if(Array.isArray(e.options)||"object"==typeof e.options)return e.options;let i;t={...t,source:s,clearCache:()=>{a=!0,this.updateFieldCacheSetting(e,s,!0)}};if("function"==typeof e.options)i=await e.options(t);else{if("string"!=typeof e.options)return[];i=e.options}t=i&&"object"==typeof i&&i.url;let o=t?i.url:i;if(a=t&&void 0!==i.noCache?i.noCache:a,"string"!=typeof o)return[];if(!a)return this.optionsCache.getOrCreate(o,{},async()=>{try{var e=this.options.forcePost?await l.post(o):await l.get(o);return e.Options||e.options||e||[]}catch(e){return console.error(`Failed to load options from ${o}:`,e),[]}});try{var r=this.options.forcePost?await l.post(o):await l.get(o);return r.Options||r.options||r||[]}catch(e){return console.error(`Failed to load options from ${o}:`,e),[]}}updateFieldCacheSetting(e,t,s){e.noCache?"boolean"==typeof e.noCache?e.noCache={table:e.noCache,create:e.noCache,edit:e.noCache,[t]:s}:"object"==typeof e.noCache&&(e.noCache[t]=s):e.noCache={[t]:s}}clearOptionsCache(e=null,t=null){this.optionsCache.clear(e,t)}getFormValues(e){var t={},s=e.elements;for(let e=0;e<s.length;e++){var a=s[e],i=a.name;if(i&&!a.disabled)switch(a.type){case"checkbox":t[i]=a.checked?a.value||"1":"0";break;case"radio":a.checked&&(t[i]=a.value);break;case"select-multiple":t[i]=Array.from(a.selectedOptions).map(e=>e.value);break;default:t[i]=a.value}}return t}async handleDependencyChange(e,s=""){var a,i,o=this.getFormValues(e),r=e.classList.contains("ftable-create-form")?"create":"edit",n=this.currentFormRecord||{},l={record:n,source:r,form:e,dependedValues:o};for([a,i]of Object.entries(this.options.fields))if(i.dependsOn){if(""!==s)if(!i.dependsOn.split(",").map(e=>e.trim()).filter(e=>e).includes(s))continue;let t=e.querySelector(`[name="${a}"]`);if(t&&this.shouldIncludeField(i,r))try{"SELECT"===t.tagName?t.innerHTML='<option value="">Loading...</option>':"INPUT"===t.tagName&&t.list&&(c=document.getElementById(t.list.id))&&(c.innerHTML="");var c,d=t.value||n[a]||"",h={...l,dependsOnField:i.dependsOn,dependsOnValue:o[i.dependsOn]},p=await this.getFieldOptions(a,r,h);"SELECT"===t.tagName?this.populateSelectOptions(t,p,d):"INPUT"===t.tagName&&t.list&&(this.populateDatalistOptions(t.list,p),d)&&(t.value=d),setTimeout(()=>{t.dispatchEvent(new Event("change",{bubbles:!0}))},0)}catch(e){console.error(`Error loading options for ${a}:`,e),"SELECT"===t.tagName&&(t.innerHTML='<option value="">Error</option>')}}}parseInputAttributes(e){if("string"!=typeof e)return e||{};for(var t={},s=/(\w+)(?:=("[^"]*"|'[^']*'|\S+))?/g;null!==(i=s.exec(e));){var a=i[1],i=i[2]?i[2].replace(/^["']|["']$/g,""):"";t[a]=""===i?"true":i}return t}createInput(e,t,s,a){var i=O.create("div",{className:`ftable-input ftable-${t.type||"text"}-input`});let o;switch(null===(s=void 0===s?null:s)&&t.defaultValue&&(s=t.defaultValue),!t.type&&t.options&&(t.type="select"),t.type){case"hidden":o=this.createHiddenInput(e,t,s);break;case"textarea":o=this.createTextarea(e,t,s);break;case"select":o=this.createSelect(e,t,s);break;case"checkbox":o=this.createCheckbox(e,t,s);break;case"radio":o=this.createRadioGroup(e,t,s);break;case"datalist":o=this.createDatalistInput(e,t,s);break;case"file":o=this.createFileInput(e,t,s);break;case"date":case"datetime":case"datetime-local":o=this.createDateInput(e,t,s);break;default:o=this.createTypedInput(e,t,s)}return"function"==typeof t.input?(a={field:t,record:this.currentFormRecord,inputField:o,formType:a},"string"==typeof(a=t.input(a))?i.innerHTML=a:a instanceof Node?i.appendChild(a):(i.appendChild(o),o.datalistElement&&o.datalistElement instanceof Node&&i.appendChild(o.datalistElement))):(i.appendChild(o),o.datalistElement&&o.datalistElement instanceof Node&&i.appendChild(o.datalistElement)),t.explain&&O.create("div",{className:"ftable-field-explain",innerHTML:`<small>${t.explain}</small>`,parent:i}),i}createDateInput(s,a,i){if("undefined"==typeof FDatepicker)return createTypedInput(s,a,i);{let e=a.dateFormat||this.options.defaultDateFormat;var o,r=document.createElement("div"),n=O.create("input",{id:"real-"+s,type:"hidden",value:i,name:s}),i={"data-date":i};a.inputAttributes&&(o=this.parseInputAttributes(a.inputAttributes),Object.assign(i,o));let t=O.create("input",{attributes:i,id:"Edit-"+s,type:"text",placeholder:a.placeholder||null,className:a.inputClass||"datepicker-input",readOnly:!0});switch(r.appendChild(n),r.appendChild(t),a.type){case"date":setTimeout(()=>{new FDatepicker(t,{format:e,altField:"real-"+s,altFormat:"Y-m-d"})},0);break;case"datetime":case"datetime-local":setTimeout(()=>{new FDatepicker(t,{format:e,timepicker:!0,altField:"real-"+s,altFormat:"Y-m-d H:i:00"})},0)}return r}}createTypedInput(e,t,s){var a,i=t.type||"text",o={};let r=e,n=(t.inputAttributes&&(a=this.parseInputAttributes(t.inputAttributes),Object.assign(o,a),void 0!==a.multiple&&!1!==a.multiple)&&(r=e+"[]"),O.create("input",{attributes:o,type:i,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:s,name:r}));return n.addEventListener("keypress",e=>{if(13===(e.keyCode||e.which))return e.preventDefault(),n.dispatchEvent(new Event("change",{bubbles:!0})),!1}),n}createDatalistInput(e,t,s){var a={list:e+"-datalist"},i=(t.inputAttributes&&(i=this.parseInputAttributes(t.inputAttributes),Object.assign(a,i)),O.create("input",{attributes:a,type:"search",name:e,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:s})),a=O.create("datalist",{id:e+"-datalist"});return t.options&&this.populateDatalistOptions(a,t.options),i.datalistElement=a,i}populateDatalistOptions(s,e){s.innerHTML="",Array.isArray(e)?e.forEach(e=>{O.create("option",{value:e.Value||e.value||e,textContent:e.DisplayText||e.text||e,parent:s})}):"object"==typeof e&&Object.entries(e).forEach(([e,t])=>{O.create("option",{value:e,textContent:t,parent:s})})}createHiddenInput(e,t,s){var a={};return t.inputAttributes&&(t=this.parseInputAttributes(t.inputAttributes),Object.assign(a,t)),O.create("input",{attributes:a,type:"hidden",name:e,id:"Edit-"+e,value:s})}createTextarea(e,t,s){var a,i={};return t.inputAttributes&&(a=this.parseInputAttributes(t.inputAttributes),Object.assign(i,a)),O.create("textarea",{attributes:i,name:e,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:s})}createSelect(e,t,s){var a={};let i=e,o=!1;if(t.inputAttributes&&(r=this.parseInputAttributes(t.inputAttributes),Object.assign(a,r),o=void 0!==r.multiple&&!1!==r.multiple)&&(i=e+"[]"),o)return this.createCustomMultiSelect(e,t,s,a,i);a.name=i;var r=O.create("select",{attributes:a,name:e,id:"Edit-"+e,className:t.inputClass||null});return t.options&&this.populateSelectOptions(r,t.options,s),r}createCustomMultiSelect(e,t,s,a,i){var o=Array.isArray(t.options)?t.options:t.options&&"object"==typeof t.options?Object.entries(t.options).map(([e,t])=>({Value:e,DisplayText:t})):[],r=t.livesearch??!1;return this._buildCustomMultiSelect({containerId:e,hiddenSelectId:"Edit-"+e,hiddenSelectName:i,extraClasses:"",containerDataFieldName:e,hiddenSelectAttributes:{},optionsSource:o,initialValues:Array.isArray(s)?s:s?s.toString().split(",").filter(e=>e):[],placeholderText:t.placeholder||this.options.messages?.multiSelectPlaceholder||"Click to select options...",livesearch:r,onChangeExtra:e=>{e.dispatchEvent(new Event("change",{bubbles:!0}))},buildHiddenSelectOnUpdate:!0})}_buildCustomMultiSelect(e){let{hiddenSelectId:t,hiddenSelectName:s,extraClasses:a,containerDataFieldName:i,hiddenSelectAttributes:o,optionsSource:r,initialValues:n,placeholderText:l,livesearch:c,onChangeExtra:d,buildHiddenSelectOnUpdate:h}=e;let p=(e=r)?(Array.isArray(e)?e:Object.entries(e).map(([e,t])=>({Value:e,DisplayText:t}))).map(e=>({optValue:void 0!==e.Value?e.Value:void 0!==e.value?e.value:e,optText:e.DisplayText||e.text||e,groupLabel:e.Group||e.group||null})).filter(e=>null!=e.optValue&&""!==e.optValue):[],u=new Map(p.map(e=>[e.optValue.toString(),e.optText]));e=["ftable-multiselect-container",...a.split(" ").filter(Boolean)].join(" ");let m=O.create("div",{className:e,attributes:{"data-field-name":i}}),f=O.create("select",{id:t,name:s,multiple:!0,style:"display: none;",attributes:o}),g=(m.appendChild(f),m.hiddenSelect=f,O.create("div",{className:"ftable-multiselect-display",parent:m,attributes:{tabindex:"0"}})),b=O.create("div",{className:"ftable-multiselect-selected",parent:g}),v=O.create("span",{className:"ftable-multiselect-placeholder",textContent:l,parent:b}),y=(O.create("button",{type:"button",className:"ftable-multiselect-toggle",innerHTML:"▼",parent:g,attributes:{tabindex:"-1"}}),null),w=null,C=new Set(n.map(e=>e.toString())),S=new Map,E=(h||p.forEach(({optValue:e,optText:t})=>{O.create("option",{value:e,textContent:t,parent:f})}),()=>{b.innerHTML="",h?(f.innerHTML="",C.forEach(e=>{var t=u.get(e)??e;O.create("option",{value:e,textContent:t,selected:!0,parent:f})})):Array.from(f.options).forEach(e=>{e.selected=C.has(e.value)}),0===C.size?(v.textContent=l,b.appendChild(v)):C.forEach(t=>{var e=O.create("span",{className:"ftable-multiselect-tag",parent:b});O.create("span",{className:"ftable-multiselect-tag-text",textContent:u.get(t)||t,parent:e}),O.create("span",{className:"ftable-multiselect-tag-remove",innerHTML:"×",parent:e}).addEventListener("click",e=>{e.stopPropagation(),C.delete(t);e=S.get(t);e&&(e.checked=!1),E(),d&&d(f)})}),d&&d(f)}),x=()=>{g.focus(),y&&(y.remove(),y=null),w&&(w.remove(),w=null),m._cleanupHandlers&&(m._cleanupHandlers(),m._cleanupHandlers=null)},A=()=>{var e,t,s;y&&(e=g.getBoundingClientRect(),s=window.pageYOffset||document.documentElement.scrollTop,t=window.pageXOffset||document.documentElement.scrollLeft,t=e.left+t,s=e.bottom+s+4,Object.assign(y.style,{position:"absolute",left:t+"px",top:s+"px",width:e.width+"px",minWidth:"fit-content",boxSizing:"border-box",zIndex:"10000"}),(s=y.getBoundingClientRect()).right>window.innerWidth)&&(t=Math.max(10,window.innerWidth-s.width-10),y.style.left=t+"px")},N=(e="")=>{if(y){Array.from(y.querySelectorAll(".ftable-multiselect-option, .ftable-multiselect-optgroup")).forEach(e=>e.remove());let t=e.toLowerCase();e=e?p.filter(e=>e.optText.toLowerCase().includes(t)):p;let i=new Set,o=null;e.forEach(({optValue:t,optText:e,groupLabel:s})=>{s&&s!==i[i.size-1]?i.has(s)||(i.add(s),o=O.create("div",{className:"ftable-multiselect-optgroup",textContent:s,parent:y})):s||(o=null);s=O.create("div",{className:"ftable-multiselect-option",parent:y});let a=O.create("input",{type:"checkbox",className:"ftable-multiselect-checkbox",checked:C.has(t.toString()),parent:s});S.set(t.toString(),a),O.create("label",{className:"ftable-multiselect-label",textContent:e,parent:s}),s.addEventListener("click",e=>{e.stopPropagation();e=t.toString();C.has(e)?(C.delete(e),a.checked=!1):(C.add(e),a.checked=!0),E()})})}},L=a=>{if(a&&a.stopPropagation(),y)x();else{if(document.querySelectorAll(".ftable-multiselect-dropdown").forEach(e=>e.remove()),document.querySelectorAll(".ftable-multiselect-overlay").forEach(e=>e.remove()),w=O.create("div",{className:"ftable-multiselect-overlay",parent:document.body}),y=O.create("div",{className:"ftable-multiselect-dropdown",parent:document.body,attributes:{tabindex:"-1",role:"listbox","aria-multiselectable":"true"}}),c){a=O.create("div",{className:"ftable-multiselect-livesearch-wrap",parent:y});let e=O.create("input",{type:"search",className:"ftable-multiselect-livesearch",placeholder:"Search...",parent:a,attributes:{autocomplete:"off"}});e.addEventListener("input",()=>{N(e.value)}),e.addEventListener("click",e=>e.stopPropagation()),setTimeout(()=>e.focus(),0)}N(),A(),c||y.focus(),y.addEventListener("keydown",e=>{var t,s;"Escape"===e.key?x():"ArrowDown"===e.key||"ArrowUp"===e.key?(e.preventDefault(),s=(t=Array.from(y.querySelectorAll(".ftable-multiselect-checkbox"))).indexOf(document.activeElement),t["ArrowDown"===e.key?s<t.length-1?s+1:0:0<s?s-1:t.length-1]?.focus()):" "!==e.key&&"Enter"!==e.key||(e.preventDefault(),document.activeElement.classList.contains("ftable-multiselect-checkbox")&&document.activeElement.click())}),w.addEventListener("click",e=>{e.target===w&&x()});let e=()=>A(),t=e=>{y&&y.contains(e.target)||A()},s=new ResizeObserver(()=>A());window.addEventListener("scroll",t,!0),window.addEventListener("resize",e),s.observe(b),m._cleanupHandlers=()=>{window.removeEventListener("scroll",t,!0),window.removeEventListener("resize",e),s.disconnect()}}},T=(g.addEventListener("click",L),g.querySelector(".ftable-multiselect-toggle").addEventListener("click",L),g.addEventListener("keydown",e=>{"ArrowDown"!==e.key&&"Enter"!==e.key||(e.preventDefault(),L())}),m.resetMultiSelect=()=>{C.clear(),S.forEach(e=>{e.checked=!1}),x(),E()},new MutationObserver(e=>{e.forEach(e=>{e.removedNodes.forEach(e=>{(e===m||e.contains&&e.contains(m))&&(x(),T.disconnect())})})}));return setTimeout(()=>{m.parentNode&&T.observe(m.parentNode,{childList:!0,subtree:!0})},0),E(),m}createRadioGroup(o,r,n){let l=O.create("div",{className:"ftable-radio-group"});return r.options&&(Array.isArray(r.options)?r.options:"object"==typeof r.options?Object.entries(r.options).map(([e,t])=>({Value:e,DisplayText:t})):[]).forEach((e,t)=>{var s=O.create("div",{className:"ftable-radio-wrapper",parent:l}),t=o+"_"+t,a={},i=(r.inputAttributes&&(i=this.parseInputAttributes(r.inputAttributes),Object.assign(a,i)),void 0!==e.Value?e.Value:void 0!==e.value?e.value:e);O.create("input",{attributes:a,type:"radio",name:o,id:t,value:i,className:r.inputClass||null,checked:i==n,parent:s}),O.create("label",{attributes:{for:t},textContent:e.DisplayText||e.text||e,parent:s})}),l}createCheckbox(e,t,s){var a=O.create("div",{className:"ftable-yesno-check-wrapper"}),s=[1,"1",!0,"true"].includes(s);let i=this.options.messages.no,o=this.options.messages.yes;return t.values&&"object"==typeof t.values&&(void 0!==t.values[0]&&(i=t.values[0]),void 0!==t.values[1])&&(o=t.values[1]),O.create("input",{className:["ftable-yesno-check-input",t.inputClass||""].filter(Boolean).join(" "),type:"checkbox",name:e,id:"Edit-"+e,value:"1",parent:a}).checked=s,t.label?O.create("label",{className:"ftable-yesno-check-fixedlabel",attributes:{for:"Edit-"+e},textContent:t.label,parent:a}):O.create("label",{className:"ftable-yesno-check-text",attributes:{for:"Edit-"+e,"data-yes":o,"data-no":i},parent:a}),a}populateSelectOptions(o,e,r){if(o.innerHTML="",Array.isArray(e)){let s=new Map,a=[],i=(e.forEach(e=>{var t=e.Group||e.group||null;(t?(s.has(t)||s.set(t,[]),s.get(t)):a).push(e)}),(e,t)=>{var s=void 0!==e.Value?e.Value:void 0!==e.value?e.value:e;let a=O.create("option",{value:s,textContent:e.DisplayText||e.text||e,selected:s==r,parent:t});e.Data&&"object"==typeof e.Data&&Object.entries(e.Data).forEach(([e,t])=>{a.setAttribute("data-"+e,t)})});a.forEach(e=>i(e,o)),s.forEach((e,t)=>{let s=O.create("optgroup",{attributes:{label:t},parent:o});e.forEach(e=>i(e,s))})}else"object"==typeof e&&Object.entries(e).forEach(([e,t])=>{O.create("option",{value:e,textContent:t,selected:e==r,parent:o})})}createFileInput(e,t,s){var a,i={};let o=e;return t.inputAttributes&&(a=this.parseInputAttributes(t.inputAttributes),Object.assign(i,a),void 0!==a.multiple&&!1!==a.multiple)&&(o=e+"[]"),O.create("input",{type:"file",id:"Edit-"+e,name:o,className:t.inputClass||null,attributes:i})}}class n extends class{constructor(){this.events={}}on(e,t){return this.events[e]||(this.events[e]=[]),this.events[e].push(t),this}once(t,s){let a=(...e)=>{this.off(t,a),s.apply(this,e)};return a.fn=s,this.on(t,a),this}emit(e,t={}){return this.events[e]&&this.events[e].forEach(e=>e(t)),this}off(e,t){return this.events[e]&&(this.events[e]=this.events[e].filter(e=>e!==t)),this}}{constructor(e,t={}){if(super(),this.element="string"==typeof e?document.querySelector(e):e,this.element){if(this.element.ftableInstance)return this.element.ftableInstance;this.options=this.mergeOptions(t),this.verifyOptions(),this.logger=new a(this.options.logLevel),this.userPrefs=new i("",this.options.saveUserPreferencesMethod),this.formBuilder=new r(this.options,this),this.state={records:[],totalRecordCount:0,currentPage:1,isLoading:!1,selectedRecords:new Set,sorting:[],searchQueries:{}},this.elements={},this.modals={},this.searchTimeout=null,this.lastSortEvent=null,this._recalculatedOnce=!1,this.shiftKeyDown=!1,this.lastSelectedRow=null,(this.element.ftableInstance=this).init()}}mergeOptions(e){var t={tableId:void 0,logLevel:a.LOG_LEVELS.WARN,actions:{},fields:{},forcePost:!0,closeOnOverlayClick:!0,animationsEnabled:!0,loadingAnimationDelay:1e3,defaultDateLocale:"",defaultDateFormat:"Y-m-d",saveUserPreferences:!0,saveUserPreferencesMethod:"localStorage",defaultSorting:"",tableReset:!1,paging:!1,pageList:"normal",pageSize:10,pageSizes:[10,25,50,100,250,500],gotoPageArea:"combobox",sorting:!1,multiSorting:!1,multiSortingCtrlKey:!0,selecting:!1,multiselect:!1,openChildAsAccordion:!1,toolbarsearch:!1,toolbarreset:!0,searchDebounceMs:300,listCache:3e4,messages:{...s}};return this.deepMerge(t,e)}deepMerge(e,t){var s,a={...e};for(s in t)t[s]&&"object"==typeof t[s]&&!Array.isArray(t[s])?a[s]=this.deepMerge(a[s]||{},t[s]):a[s]=t[s];return a}verifyOptions(){this.options.pageSize&&!this.options.pageSizes.includes(this.options.pageSize)&&(this.options.pageSize=this.options.pageSizes[0])}static setMessages(e){Object.assign(s,e)}init(){this.processFieldDefinitions(),this.createMainStructure(),this.setupFTableUserPreferences(),this.createTable(),this.createModals(),this.options.paging&&this.createPagingUI(),this.resolveAsyncFieldOptions().then(()=>{setTimeout(()=>{this.refreshDisplayValues()},0)}).catch(console.error),this.bindEvents(),this.updateSortingHeaders(),this.renderSortingInfo(),this.initColumnWidths()}initColumnWidths(){var e=this.columnList.filter(e=>{e=this.options.fields[e];return!e.action&&"hidden"!==e.visibility&&"separator"!==e.visibility});let t=e.length;e.forEach(e=>{e=this.options.fields[e];e.width=e.width||100/t+"%"})}normalizeColumnWidths(){var e=this.elements.mainContainer,t=this.columnList.map(e=>({th:this.elements.table.querySelector(`[data-field-name="${e}"]`),field:this.options.fields[e]})).filter(e=>e.th&&!e.field.action&&"hidden"!==e.field.visibility&&"separator"!==e.field.visibility);if(0!==t.length){let s=e.offsetWidth,a=0;t.forEach(e=>{var t=e.th.offsetWidth/s*100;e.field.width=t+"%",e.th.style.width=e.field.width,a+=t})}}parseDefaultSorting(e){let o=[];return e&&"string"==typeof e&&e.split(",").forEach(s=>{s=s.trim();if(s){var a=s.toUpperCase().indexOf(" DESC"),i=s.toUpperCase().indexOf(" ASC");let e="ASC",t=s;e=0<a?(t=s.slice(0,a).trim(),"DESC"):(t=(0<i?s.slice(0,i):s).trim(),"ASC");a=this.options.fields[t];a&&!1!==a.sorting&&o.push({fieldName:t,direction:e})}}),o}createPagingUI(){this.elements.bottomPanel=O.create("div",{className:"ftable-bottom-panel",parent:this.elements.mainContainer}),this.elements.leftArea=O.create("div",{className:"ftable-left-area",parent:this.elements.bottomPanel}),this.elements.rightArea=O.create("div",{className:"ftable-right-area",parent:this.elements.bottomPanel}),this.elements.pagingListArea=O.create("div",{className:"ftable-page-list",parent:this.elements.leftArea}),this.elements.pagingGotoArea=O.create("div",{className:"ftable-page-goto",parent:this.elements.leftArea}),this.elements.pageInfoSpan=O.create("div",{className:"ftable-page-info",parent:this.elements.rightArea}),!1!==this.options.pageSizeChangeArea&&this.createPageSizeSelector()}createPageSizeSelector(){var e=O.create("span",{className:"ftable-page-size-change",parent:this.elements.leftArea});O.create("span",{textContent:this.options.messages.pageSizeChangeLabel,parent:e});let s=O.create("select",{className:"ftable-page-size-select",parent:e});(this.options.pageSizes||[10,25,50,100,250,500]).forEach(e=>{var t=O.create("option",{attributes:{value:e},textContent:e.toString(),parent:s});e===this.state.pageSize&&(t.selected=!0)}),s.addEventListener("change",e=>{this.changePageSize(parseInt(e.target.value))})}processFieldDefinitions(){this.fieldList=Object.keys(this.options.fields),this.fieldList.forEach(e=>{var e=this.options.fields[e],t=!0===e.key;!!e.action?(e.list=!0,e.create=!1,e.edit=!1,e.sorting=!1,e.searchable=!1):t?(void 0!==e.create&&e.create||(e.create=!0,e.type="hidden"),void 0!==e.edit&&e.edit||(e.edit=!0,e.type="hidden")):(e.create=e.create??!0,e.edit=e.edit??!0,e.list=e.list??!0,e.sorting=e.sorting??!0),e.visibility=e.visibility??"visible"}),this.columnList=this.fieldList.filter(e=>!1!==this.options.fields[e].list),this._userPlacedActions=new Set(this.fieldList.filter(e=>this.options.fields[e].action).map(e=>this.options.fields[e].action)),this.keyField=this.fieldList.find(e=>!0===this.options.fields[e].key),this.keyField||this.logger.info("No key field defined")}async resolveAsyncFieldOptions(){var e=this.columnList.map(async t=>{var e=this.options.fields[t];if(!e.action){e=this.formBuilder.originalFieldOptions.get(t);if(this.formBuilder.shouldResolveOptions(e))try{var s=this.formBuilder.generateOptionsCacheKey("table",{});this.formBuilder.resolvedFieldOptions.get(t)?.[s]||await this.formBuilder.getFieldOptions(t,"table")}catch(e){console.error(`Failed to resolve table options for ${t}:`,e)}}});await Promise.all(e)}async refreshDisplayValues(){var e=this.elements.tableBody.querySelectorAll(".ftable-data-row");if(0!==e.length)for(var t of e)for(var s of this.columnList){var a,i,o=this.options.fields[s];!o.action&&o.options&&(a=t.querySelector(`td[data-field-name="${s}"]`))&&(i=this.formBuilder.generateOptionsCacheKey("table",{}),i=this.formBuilder.resolvedFieldOptions.get(s)?.[i],s=this.getDisplayText(t.recordData,s,i),a.innerHTML=o.listEscapeHTML?O.escapeHtml(s):s)}}createMainStructure(){this.elements.mainContainer=O.create("div",{className:"ftable-main-container",parent:this.element}),this.options.title&&(this.elements.titleDiv=O.create("div",{className:"ftable-title",parent:this.elements.mainContainer}),O.create("div",{className:"ftable-title-text",innerHTML:this.options.title,parent:this.elements.titleDiv})),this.elements.toolbarDiv=O.create("div",{className:"ftable-toolbar",parent:this.elements.titleDiv||this.elements.mainContainer}),this.elements.tableDiv=O.create("div",{className:"ftable-table-div",parent:this.elements.mainContainer})}createTable(){this.elements.table=O.create("table",{className:"ftable",parent:this.elements.tableDiv}),this.options.tableId&&(this.elements.table.id=this.options.tableId),this.createTableHeader(),this.createTableBody(),this.addNoDataRow()}createTableHeader(){var e=O.create("thead",{parent:this.elements.table});let o=O.create("tr",{parent:e});if(this.options.selecting&&this.options.selectingCheckboxes&&!this._userPlacedActions.has("select")){var t=O.create("th",{className:"ftable-command-column-header ftable-column-header-select",parent:o});if(this.options.multiselect){let e=O.create("input",{attributes:{type:"checkbox"},parent:t});e.addEventListener("change",()=>{this.toggleSelectAll(e.checked)})}}this.columnList.forEach(t=>{var s=this.options.fields[t];if(s.action){var a={select:"ftable-column-header-select",update:"ftable-column-header-edit",clone:"ftable-column-header-clone",delete:"ftable-column-header-delete"};let t=O.create("th",{className:"ftable-command-column-header "+(a[s.action]||""),parent:o});if(s.title&&(t.textContent=s.title),"select"===s.action&&this.options.selecting&&this.options.selectingCheckboxes&&this.options.multiselect){let e=O.create("input",{attributes:{type:"checkbox"},parent:t});e.addEventListener("change",()=>{this.toggleSelectAll(e.checked)})}void(s.width&&(t.style.width=s.width))}else{let e=O.create("th",{className:`ftable-column-header ${s.listClass||""} `+(s.listClassHeader||""),attributes:{"data-field-name":t},parent:o});s.width&&(e.style.width=s.width);var a=O.create("div",{className:"ftable-column-header-container",parent:e}),i=(s.tooltip&&a.setAttribute("title",s.tooltip),O.create("span",{className:"ftable-column-header-text",innerHTML:s.title||t,parent:a}));this.options.sorting&&!1!==s.sorting&&(O.addClass(i,"ftable-sortable-text"),O.addClass(e,"ftable-column-header-sortable"),e.addEventListener("click",e=>{e.preventDefault();e=e.ctrlKey||e.metaKey;this.sortByColumn(t,e)})),!1!==this.options.columnResizable&&!1!==s.columnResizable&&this.makeColumnResizable(e,a),"hidden"!==s.visibility&&"separator"!==s.visibility||O.hide(e)}}),this.options.actions.updateAction&&!this._userPlacedActions.has("update")&&O.create("th",{className:"ftable-command-column-header ftable-column-header-edit",parent:o}),this.options.actions.cloneAction&&!this._userPlacedActions.has("clone")&&O.create("th",{className:"ftable-command-column-header ftable-column-header-clone",parent:o}),this.options.actions.deleteAction&&!this._userPlacedActions.has("delete")&&O.create("th",{className:"ftable-command-column-header ftable-column-header-delete",parent:o}),this.options.toolbarsearch&&this.createSearchHeaderRow(e).catch(e=>{console.error("Failed to create search header row:",e)})}async createSearchHeaderRow(e){var t,s=O.create("tr",{className:"ftable-toolbarsearch-row",parent:e});this.options.selecting&&this.options.selectingCheckboxes&&!this._userPlacedActions.has("select")&&O.create("th",{className:"ftable-toolbarsearch-column-header",parent:s});for(let i of this.columnList){var o=this.options.fields[i];if(o.action)O.create("th",{className:"ftable-toolbarsearch-column-header ftable-command-column-header",parent:s});else{var r=!1!==o.searchable,n=O.create("th",{className:"ftable-toolbarsearch-column-header",parent:s});if(r){r=O.create("div",{className:"ftable-column-header-container",parent:n});let s,a="text";o.searchType?a=o.searchType:!o.type&&o.options?a="select":o.type&&(a=o.type);var l="ftable-toolbarsearch-"+i;switch(a){case"date":case"datetime":case"datetime-local":if("undefined"!=typeof FDatepicker){let e=o.searchDateFormat||o.dateFormat||this.options.defaultDateFormat;var c=document.createElement("div"),d=O.create("input",{className:"ftable-toolbarsearch-extra",type:"hidden",id:"ftable-toolbarsearch-extra-"+i,attributes:{"data-field-name":i}});let t=O.create("input",{className:"ftable-toolbarsearch",id:"ftable-toolbarsearch-"+i,type:"text",placeholder:o.searchPlaceholder||o.placeholder||"",readOnly:!0});switch(c.appendChild(d),c.appendChild(t),a){case"date":setTimeout(()=>{new FDatepicker(t,{format:e,altField:"ftable-toolbarsearch-extra-"+i,altFormat:"Y-m-d",autoClose:!0})},0);break;case"datetime":case"datetime-local":setTimeout(()=>{new FDatepicker(t,{format:e,timepicker:!0,altField:"ftable-toolbarsearch-extra-"+i,altFormat:"Y-m-d H:i:00"})},0)}s=c}else s=O.create("input",{className:"ftable-toolbarsearch",type:a,id:l,attributes:{"data-field-name":i}});break;case"checkbox":o.values||(o.values={0:this.options.messages.no,1:this.options.messages.yes}),s=await this.createSelectForSearch(i,o,!0);break;case"select":s=o.options?await this.createSelectForSearch(i,o,!1):O.create("input",{className:"ftable-toolbarsearch",type:"text",id:l,placeholder:o.searchPlaceholder||o.placeholder||"Search...",attributes:{"data-field-name":i}});break;case"datalist":s=await this.createDatalistForSearch(i,o);break;default:s=O.create("input",{className:"ftable-toolbarsearch",type:"text",id:l,placeholder:o.searchPlaceholder||o.placeholder||"Search...",attributes:{"data-field-name":i}})}if(s){r.appendChild(s),s.datalistElement&&s.datalistElement instanceof Node&&r.appendChild(s.datalistElement);let e=s;"SELECT"===(e=s.classList&&s.classList.contains("ftable-multiselect-container")&&s.hiddenSelect?s.hiddenSelect:e).tagName?e.addEventListener("change",e=>{this.handleSearchInputChange(e)}):e.addEventListener("input",e=>{this.handleSearchInputChange(e)})}}"hidden"!==o.visibility&&"separator"!==o.visibility||O.hide(n)}}this.options.toolbarsearch&&this.options.toolbarreset&&(e=O.create("th",{className:"ftable-toolbarsearch-column-header ftable-toolbarsearch-reset",parent:s}),0<(t=(this.options.actions.updateAction&&!this._userPlacedActions.has("update")?1:0)+(this.options.actions.deleteAction&&!this._userPlacedActions.has("delete")?1:0)+(this.options.actions.cloneAction&&!this._userPlacedActions.has("clone")?1:0))?e.colSpan=t:O.addClass(e,"ftable-command-column-header"),O.create("button",{className:"ftable-toolbarsearch-reset-button",textContent:this.options.messages.resetSearch,attributes:{id:"ftable-toolbarsearch-reset-button"},parent:e}).addEventListener("click",()=>this.resetSearch()))}async createSelectForSearch(e,t,s){var a,i="ftable-toolbarsearch-"+e,o={};let r=e,n=!1;t.searchAttributes?(a=this.formBuilder.parseInputAttributes(t.searchAttributes),Object.assign(o,a),n=void 0!==a.multiple&&!1!==a.multiple):t.inputAttributes&&(a=this.formBuilder.parseInputAttributes(t.inputAttributes),Object.assign(o,a),n=void 0!==a.multiple&&!1!==a.multiple),n&&(r=e+"[]"),o["data-field-name"]=r;let l;if(s&&t.values?l=Object.entries(t.values).map(([e,t])=>({Value:e,DisplayText:t})):t.options&&(l=await this.formBuilder.getFieldOptions(e)),n)return this.createCustomMultiSelectForSearch(i,e,t,l,o);let c=O.create("select",{attributes:o,id:i,className:"ftable-toolbarsearch"});return 0<l?.length&&(""===l[0].Value||""===l[0].value||""===l[0]||""===l[0].DisplayText&&null==l[0].Value)||O.create("option",{value:"",innerHTML:" ",parent:c}),l&&Array.isArray(l)?l.forEach(e=>{O.create("option",{value:void 0!==e.Value?e.Value:void 0!==e.value?e.value:e,textContent:e.DisplayText||e.text||e,parent:c})}):l&&"object"==typeof l&&Object.entries(l).forEach(([e,t])=>{O.create("option",{value:e,textContent:t,parent:c})}),c}createCustomMultiSelectForSearch(e,t,s,a,i){var o=s.livesearch??!1;return this.formBuilder._buildCustomMultiSelect({hiddenSelectId:e,hiddenSelectName:i["data-field-name"]||e,extraClasses:"ftable-multiselect-search ftable-toolbarsearch",containerDataFieldName:i["data-field-name"]||e,hiddenSelectAttributes:i,optionsSource:a,initialValues:[],placeholderText:s.searchPlaceholder||s.placeholder||this.options.messages?.multiSelectPlaceholder||"Click to select options...",livesearch:o,onChangeExtra:e=>{e.dispatchEvent(new Event("change",{bubbles:!0}))},buildHiddenSelectOnUpdate:!1})}async createDatalistForSearch(e,t){var s="ftable-toolbarsearch-"+e,a=s+"-datalist",i=O.create("datalist",{attributes:{id:a}}),s=O.create("input",{className:"ftable-toolbarsearch",type:"search",id:s,placeholder:t.searchPlaceholder||t.placeholder||"Type or select...",attributes:{"data-field-name":e,list:a}});s.datalistElement=i;let o;return t.searchOptions?o=t.searchOptions:t.options&&(o=await this.formBuilder.getFieldOptions(e,"table")),o&&this.formBuilder.populateDatalistOptions(i,o),s}handleSearchInputChange(e){var e=e.target,t=e.getAttribute("data-field-name");let s;s=e.multiple&&e.options?Array.from(e.selectedOptions).map(e=>e.value).filter(e=>""!==e.trim()).map(e=>e.trim()):e.value.trim(),this.state.currentPage=1,Array.isArray(s)&&0<s.length||!Array.isArray(s)&&s?this.state.searchQueries[t]=s:delete this.state.searchQueries[t],clearTimeout(this.searchTimeout),this.searchTimeout=setTimeout(()=>{this.load()},this.options.searchDebounceMs)}resetSearch(){this.state.searchQueries={},this.elements.table.querySelectorAll(".ftable-toolbarsearch").forEach(e=>{"SELECT"===e.tagName?e.selectedIndex=0:e.value=""}),this.elements.table.querySelectorAll(".ftable-multiselect-container").forEach(e=>{"function"==typeof e.resetMultiSelect&&e.resetMultiSelect()}),this.load()}getNextResizableHeader(t){var s=Array.from(this.elements.table.querySelectorAll("thead th.ftable-column-header-resizable"));for(let e=s.indexOf(t)+1;e<s.length;e++)if(null!==s[e].offsetParent)return s[e];return null}makeColumnResizable(a,e){O.addClass(a,"ftable-column-header-resizable"),this.elements.resizeBar||(this.elements.resizeBar=O.create("div",{className:"ftable-column-resize-bar",parent:this.elements.mainContainer}),O.hide(this.elements.resizeBar));e=O.create("div",{className:"ftable-column-resize-handler",parent:e});let i=!1,o=0,r=0,n,l=null,c=0,d=null,h=(e.addEventListener("mousedown",e=>{e.preventDefault(),e.stopPropagation(),i=!0,n=this.elements.mainContainer.getBoundingClientRect(),o=e.clientX,r=a.offsetWidth,(l=this.getNextResizableHeader(a))&&(c=l.offsetWidth,e=l.dataset.fieldName,d=this.options.fields[e]);e=a.getBoundingClientRect();this.elements.resizeBar.style.left=e.right-n.left+"px",this.elements.resizeBar.style.top=e.top-n.top+"px",this.elements.resizeBar.style.height=this.elements.table.offsetHeight+"px",O.show(this.elements.resizeBar),document.addEventListener("mousemove",h),document.addEventListener("mouseup",p)}),e=>{i&&(this.elements.resizeBar.style.left=e.clientX-n.left+"px")}),p=e=>{var t,s;i&&(i=!1,e=e.clientX-o,s=n.width,t=Math.max(50,r+e)/s*100,l&&(e=Math.max(50,c-e)/s*100,d.width=e.toFixed(2)+"%",l.style.width=d.width),(s=this.options.fields[a.dataset.fieldName]).width=t.toFixed(2)+"%",a.style.width=s.width,this.normalizeColumnWidths(),this.options.saveUserPreferences&&this.saveColumnSettings(),O.hide(this.elements.resizeBar),document.removeEventListener("mousemove",h),document.removeEventListener("mouseup",p))}}saveColumnSettings(){if(this.options.saveUserPreferences){let a={};this.columnList.forEach(e=>{var t,s=this.options.fields[e];s.action||(t=this.elements.table.querySelector(`[data-field-name="${e}"]`))&&(a[e]={width:t.style.width||s.width||"auto",visibility:s.visibility||"visible"})}),this.userPrefs.set("column-settings",JSON.stringify(a))}}saveState(){var e;this.options.saveUserPreferences&&(e={sorting:this.state.sorting,pageSize:this.state.pageSize},this.userPrefs.set("table-state",JSON.stringify(e)))}loadColumnSettings(){if(this.options.saveUserPreferences){var e=this.userPrefs.get("column-settings");if(e)try{var t=JSON.parse(e);Object.entries(t).forEach(([e,t])=>{e=this.options.fields[e];e&&!e.action&&(t.width&&(e.width=t.width),t.visibility)&&(e.visibility=t.visibility)})}catch(e){this.logger.warn("Failed to load column settings:",e)}}}loadState(){if(this.options.saveUserPreferences){var e=this.userPrefs.get("table-state");if(e)try{var t=JSON.parse(e);Array.isArray(t.sorting)&&(this.state.sorting=t.sorting),t.pageSize&&this.options.pageSizes.includes(t.pageSize)&&(this.state.pageSize=t.pageSize)}catch(e){this.logger.warn("Failed to load table state:",e)}}}createTableBody(){this.elements.tableBody=O.create("tbody",{parent:this.elements.table})}addNoDataRow(){var e,t;this.elements.tableBody.querySelector(".ftable-no-data-row")||(e=O.create("tr",{className:"ftable-no-data-row",parent:this.elements.tableBody}),t=this.elements.table.querySelector("thead tr").children.length,O.create("td",{attributes:{colspan:t},textContent:this.options.messages.noDataAvailable,parent:e}))}removeNoDataRow(){var e=this.elements.tableBody.querySelector(".ftable-no-data-row");e&&e.remove()}createModals(){this.options.actions.createAction&&this.createAddRecordModal(),this.options.actions.updateAction&&this.createEditRecordModal(),this.options.actions.deleteAction&&this.createDeleteConfirmModal(),this.createErrorModal(),this.createWarningModal(),this.createInfoModal(),this.createLoadingModal(),Object.values(this.modals).forEach(e=>e.create())}createAddRecordModal(){this.modals.addRecord=new o({parent:this.elements.mainContainer,title:this.options.messages.addNewRecord,className:"ftable-add-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>{this.modals.addRecord.close(),this.emit("formClosed",{form:this.currentForm,formType:"create",record:null}),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null}},{text:this.options.messages.save,className:"ftable-dialog-savebutton",onClick:()=>this.saveNewRecord()}]})}createEditRecordModal(){this.modals.editRecord=new o({parent:this.elements.mainContainer,title:this.options.messages.editRecord,className:"ftable-edit-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>{this.modals.editRecord.close(),this.emit("formClosed",{form:this.currentForm,formType:"edit",record:null}),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null}},{text:this.options.messages.save,className:"ftable-dialog-savebutton",onClick:()=>this.saveEditedRecord()}]})}createDeleteConfirmModal(){this.modals.deleteConfirm=new o({parent:this.elements.mainContainer,title:this.options.messages.areYouSure,className:"ftable-delete-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>this.modals.deleteConfirm.close()},{text:this.options.messages.deleteText,className:"ftable-dialog-deletebutton",onClick:()=>this.confirmDelete()}]})}createErrorModal(){this.modals.error=new o({parent:this.elements.mainContainer,title:this.options.messages.error,className:"ftable-error-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.close,className:"ftable-dialog-closebutton",onClick:()=>this.modals.error.close()}]})}createWarningModal(){this.modals.warning=new o({parent:this.elements.mainContainer,title:this.options.messages.warning,className:"ftable-warning-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.close,className:"ftable-dialog-closebutton",onClick:()=>this.modals.warning.close()}]})}createInfoModal(){this.modals.info=new o({parent:this.elements.mainContainer,title:"",className:"ftable-info-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.close,className:"ftable-dialog-closebutton",onClick:()=>this.modals.info.close()}]})}createLoadingModal(){this.modals.loading=new o({parent:this.elements.mainContainer,title:"",className:"ftable-loading-modal",closeOnOverlayClick:!1,content:`<div class="ftable-loading-message">${this.options.messages.loadingMessage}</div>`})}bindEvents(){this.subscribeOptionEvents(),this.createCustomToolbarItems(),this.createToolbarButtons(),this.bindKeyboardEvents(),!1!==this.options.columnSelectable&&this.createColumnSelectionMenu()}subscribeOptionEvents(){["formCreated","formClosed","recordsLoaded","recordAdded","recordUpdated","recordDeleted","selectionChanged"].forEach(e=>{"function"==typeof this.options[e]&&this.on(e,this.options[e])})}createColumnSelectionMenu(){this.elements.columnSelectionOverlay=null,this.elements.columnSelectionMenu=null,this.elements.table.querySelector("thead").addEventListener("contextmenu",e=>{e.preventDefault(),this.showColumnSelectionMenu(e)})}showColumnSelectionMenu(e){this.hideColumnSelectionMenu(),this.elements.columnSelectionOverlay=O.create("div",{className:"ftable-contextmenu-overlay",parent:document.body}),this.elements.columnSelectionMenu=O.create("div",{className:"ftable-column-selection-container",parent:document.body}),this.populateColumnSelectionMenu(),this.positionColumnSelectionMenu(e),this.elements.columnSelectionOverlay.addEventListener("click",e=>{e.target===this.elements.columnSelectionOverlay&&this.hideColumnSelectionMenu()}),this.elements.columnSelectionOverlay.addEventListener("contextmenu",e=>{e.preventDefault(),this.hideColumnSelectionMenu()})}populateColumnSelectionMenu(){let l=O.create("ul",{className:"ftable-column-select-list",parent:this.elements.columnSelectionMenu});this.columnList.forEach(t=>{var e=this.options.fields[t];if(!e.action){var s="hidden"!==e.visibility,a="fixed"===e.visibility,i="separator"===e.visibility,o=this.isFieldSorted(t),r=O.create("li",{className:"ftable-column-select-item",parent:l}),n=O.create("label",{className:"ftable-column-select-label",parent:r});if(!i){let e=O.create("input",{attributes:{type:"checkbox",id:"column-"+t},parent:n});e.checked=s,(a||o&&s)&&(e.disabled=!0,r.style.opacity="0.6"),e.disabled||e.addEventListener("change",()=>{this.setColumnVisibility(t,e.checked)})}a=O.create("span",{textContent:e.title||t,style:i?"font-weight: bold;":null,parent:n});o&&((s=O.create("span",{className:"ftable-sort-indicator",textContent:" (sorted)",parent:a})).style.fontSize="0.8em",s.style.color="#666")}})}positionColumnSelectionMenu(e){var t=this,s=e.pageX,e=e.pageY,e=(t.elements.columnSelectionMenu.style.position="absolute",t.elements.columnSelectionMenu.style.left=s+"px",t.elements.columnSelectionMenu.style.top=e+"px",t.elements.columnSelectionMenu.style.minWidth="100px",t.elements.columnSelectionMenu.style.boxSizing="border-box",t.elements.columnSelectionMenu.offsetWidth),a=window.innerWidth;a<s+e&&(s=Math.max(10,a-e-10),t.elements.columnSelectionMenu.style.left=s+"px")}hideColumnSelectionMenu(){this.elements.columnSelectionOverlay&&(this.elements.columnSelectionOverlay.remove(),this.elements.columnSelectionMenu.remove(),this.elements.columnSelectionOverlay=null,this.elements.columnSelectionMenu=null)}isFieldSorted(t){return this.state.sorting.some(e=>e.fieldName===t)}createToolbarButtons(){this.options.csvExport&&this.addToolbarButton({text:this.options.messages.csvExport,className:"ftable-toolbar-item-csv",onClick:()=>{var e=this.options.title?this.options.title.replace(/[^a-z0-9]/gi,"-").toLowerCase()+".csv":"table-export.csv";this.exportToCSV(e)}}),this.options.printTable&&this.addToolbarButton({text:this.options.messages.printTable,className:"ftable-toolbar-item-print",onClick:()=>{this.printTable()}}),this.options.actions.createAction&&this.addToolbarButton({text:this.options.messages.addNewRecord,className:"ftable-toolbar-item-add-record",addIconSpan:!0,onClick:()=>this.showAddRecordForm()})}addToolbarButton(t){var e,s=O.create("button",{className:"ftable-toolbar-item "+(t.className||""),id:t.id||null,title:t.title||null,textContent:t.text||null,type:"button",parent:this.elements.toolbarDiv});return t.addIconSpan&&(e=O.create("span",{className:"ftable-toolbar-item-icon "+(t.className||""),parent:s}),t.text)&&(s.textContent="",s.append(e,t.text||"")),t.icon&&(e=O.create("img",{attributes:{src:t.icon,alt:"",width:16,height:16,style:"margin-right: 6px; vertical-align: middle;"},parent:s}),t.text)&&(s.textContent="",s.append(e,t.text||"")),t.onClick&&s.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),t.onClick(e)}),t.disabled&&(s.disabled=!0),s}createCustomToolbarItems(){this.options.toolbar&&this.options.toolbar.items&&this.options.toolbar.items.forEach((e,t)=>{this.addToolbarButton({text:e.text||"",className:"ftable-toolbar-item-custom "+(e.buttonClass||""),id:e.buttonId||"ftable-toolbar-item-custom-id-"+t,title:e.tooltip||"",icon:e.icon||null,onClick:"function"==typeof e.click?e.click:null})})}bindKeyboardEvents(){this.options.selecting&&(document.addEventListener("keydown",e=>{"Shift"===e.key&&(this.shiftKeyDown=!0)}),document.addEventListener("keyup",e=>{"Shift"===e.key&&(this.shiftKeyDown=!1)}))}setupFTableUserPreferences(){var e;this.options.saveUserPreferences&&(e=this.userPrefs.generatePrefix(this.options.tableId||"",this.fieldList),this.userPrefs=new i(e,this.options.saveUserPreferencesMethod),this.loadState(),this.loadColumnSettings())}async load(e={},t={}){if(!this.state.isLoading){t.fromPage1&&(this.state.currentPage=1),this.state.isLoading=!0,this.showLoadingIndicator();try{var s={...e,...this.buildLoadParams()},a=await this.performLoad(s);this.processLoadedData(a),this.emit("recordsLoaded",{records:a.Records,serverResponse:a})}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Load failed: "+e.message)}finally{this.state.isLoading=!1,this.hideLoadingIndicator()}this.renderSortingInfo(),this.normalizeColumnWidths()}}buildLoadParams(){var e,t={};if(this.options.paging&&(this.state.pageSize||(this.state.pageSize=this.options.pageSize),t.jtStartIndex=(this.state.currentPage-1)*this.state.pageSize,t.jtPageSize=this.state.pageSize),this.options.sorting&&(0<this.state.sorting.length?t.jtSorting=this.state.sorting.map(e=>e.fieldName+" "+e.direction).join(", "):this.options.defaultSorting&&(t.jtSorting=this.parseDefaultSorting(this.options.defaultSorting).map(e=>e.fieldName+" "+e.direction).join(", "))),this.options.toolbarsearch&&0<Object.keys(this.state.searchQueries).length){let s=[],a=[];Object.entries(this.state.searchQueries).forEach(([t,e])=>{Array.isArray(e)?e.forEach(e=>{""!==e&&null!=e&&(s.push(e),a.push(t))}):""!==e&&(s.push(e),a.push(t))}),0<s.length&&(t.q=s,t.opt=a)}return"function"==typeof this.options.listQueryParams&&(e=this.options.listQueryParams(),Object.assign(t,e)),t}isCacheExpired(e,t){return!e||!e.timestamp||t<Date.now()-e.timestamp}async performLoad(e){var t=this.options.actions.listAction;if(this.options.listCache&&"string"==typeof t){var s=this.formBuilder.optionsCache.get(t,e);if(s&&!this.isCacheExpired(s,this.options.listCache))return s.data}let a;if("function"==typeof t)a=await t(e);else{if("string"!=typeof t)throw new Error("No valid listAction provided");a=this.options.forcePost?await l.post(t,e):await l.get(t,e)}if(a&&"OK"===a.Result)return this.options.listCache&&"string"==typeof t&&this.formBuilder.optionsCache.set(t,e,{data:a,timestamp:Date.now()}),a;throw new Error(a?.Message||"Invalid response from server")}processLoadedData(e){"OK"!==e.Result?this.showError(e.Message||"Unknown error occurred"):(this.state.records=e.Records||[],this.state.totalRecordCount=e.TotalRecordCount||this.state.records.length,this.options.paging&&0===this.state.records.length&&0<this.state.totalRecordCount?(this.state.currentPage=1,this.load()):(this.renderTableData(),this.updatePagingInfo()))}renderTableData(){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>e.remove()),0===this.state.records.length?this.addNoDataRow():(this.removeNoDataRow(),this.state.records.forEach(e=>{e=this.createTableRow(e);this.elements.tableBody.appendChild(e)}),this.refreshRowStyles())}createTableRow(s){let a=O.create("tr",{className:"ftable-data-row",attributes:{"data-record-key":this.getKeyValue(s)}});return a.recordData=s,this.options.selecting&&this.options.selectingCheckboxes&&!this._userPlacedActions.has("select")&&this.addSelectingCell(a),this.columnList.forEach(e=>{var t=this.options.fields[e];if(t.action)switch(t.action){case"select":this.addSelectingCell(a);break;case"update":this.addEditCell(a);break;case"clone":this.addCloneCell(a);break;case"delete":this.addDeleteCell(a)}else this.addDataCell(a,s,e)}),this.options.actions.updateAction&&!this._userPlacedActions.has("update")&&this.addEditCell(a),this.options.actions.cloneAction&&!this._userPlacedActions.has("clone")&&this.addCloneCell(a),this.options.actions.deleteAction&&!this._userPlacedActions.has("delete")&&this.addDeleteCell(a),this.options.selecting&&this.makeRowSelectable(a),a}addSelectingCell(t){var e=O.create("td",{className:"ftable-command-column ftable-selecting-column",parent:t});O.create("input",{className:"norowselectonclick",attributes:{type:"checkbox"},parent:e}).addEventListener("change",e=>{this.toggleRowSelection(t)})}addDataCell(e,t,s){var a=this.options.fields[s],i=this.formBuilder.generateOptionsCacheKey("table",{}),i=this.formBuilder.resolvedFieldOptions.get(s)?.[i],t=this.getDisplayText(t,s,i),i=O.create("td",{className:`${a.listClass||""} `+(a.listClassEntry||""),innerHTML:a.listEscapeHTML?O.escapeHtml(t):t,attributes:{"data-field-name":s},parent:e});"fixed"===a.visibility||"hidden"!==a.visibility&&"separator"!==a.visibility||O.hide(i)}addEditCell(t){var e=O.create("td",{className:"ftable-command-column",parent:t});O.create("button",{className:"ftable-command-button ftable-edit-command-button",attributes:{title:this.options.messages.editRecord},innerHTML:`<span>${this.options.messages.editRecord}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.editRecord(t)})}addCloneCell(t){var e=O.create("td",{className:"ftable-command-column",parent:t});O.create("button",{className:"ftable-command-button ftable-clone-command-button",attributes:{title:this.options.messages.cloneRecord||"Clone"},innerHTML:`<span>${this.options.messages.cloneRecord||"Clone"}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.cloneRecord(t)})}addDeleteCell(t){var e=O.create("td",{className:"ftable-command-column",parent:t});O.create("button",{className:"ftable-command-button ftable-delete-command-button",attributes:{title:this.options.messages.deleteText},innerHTML:`<span>${this.options.messages.deleteText}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.deleteRecord(t)})}getDisplayText(e,t,s=null){var a=this.options.fields[t],i=e[t],s=s||a.options;let o=i;return"date"===a.type&&i&&(o="undefined"!=typeof FDatepicker?FDatepicker.formatDate(this._parseDate(i),a.dateFormat||this.options.defaultDateFormat):this.formatDate(i,a.dateLocale||this.options.defaultDateLocale)),"datetime-local"!==a.type&&"datetime"!==a.type||!i||(o="undefined"!=typeof FDatepicker?FDatepicker.formatDate(this._parseDate(i),a.dateFormat||this.options.defaultDateFormat):this.formatDateTime(i,a.dateLocale||this.options.defaultDateLocale)),"checkbox"===a.type&&(o=this.getCheckboxText(t,i)),s&&(t=this.findOptionByValue(s,i),o=t?t.DisplayText||t.text||t:i),(o=a.display&&"function"==typeof a.display?a.display({record:e,value:i,displayValue:o}):o)||""}_parseDate(e){return e.includes("Date")?new Date(parseInt(e.substr(6),10)):10==e.length?new Date(parseInt(e.substr(0,4),10),parseInt(e.substr(5,2),10)-1,parseInt(e.substr(8,2),10)):19==e.length?new Date(parseInt(e.substr(0,4),10),parseInt(e.substr(5,2),10)-1,parseInt(e.substr(8,2),10),parseInt(e.substr(11,2),10),parseInt(e.substr(14,2),10),parseInt(e.substr(17,2),10)):new Date(e)}formatDate(e,t){if(!e)return"";var s=this._parseDate(e);try{return isNaN(s.getTime())?e:s.toLocaleDateString(t,{year:"numeric",month:"2-digit",day:"2-digit"})}catch{return e}}formatDateTime(e,t){if(!e)return"";var s=this._parseDate(e);try{return isNaN(s.getTime())?e:s.toLocaleString(t)}catch{return e}}getCheckboxText(e,t){e=this.options.fields[e];return e.values&&e.values[t]?e.values[t]:t?this.options.messages.yes:this.options.messages.no}findOptionByValue(e,t){return Array.isArray(e)?e.find(e=>(e.Value||e.value)===t||e===t):"object"==typeof e&&null!==e&&e.hasOwnProperty(t)?e[t]:null}refreshRowStyles(){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach((e,t)=>{t%2==0?O.addClass(e,"ftable-row-even"):O.removeClass(e,"ftable-row-even")})}getKeyValue(e){return this.keyField?e[this.keyField]:null}async showAddRecordForm(){var e=await this.formBuilder.createForm("create");this.modals.addRecord.setContent(e),this.modals.addRecord.show(),this.currentForm=e,this.emit("formCreated",{form:e,formType:"create",record:null})}async saveNewRecord(){if(this.currentForm)if(this.currentForm.checkValidity()){var e=this.getFormData(this.currentForm);try{var t=await this.performCreate(e);"OK"===t.Result?(this.clearListCache(),this.modals.addRecord.close(),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null,this.emit("formClosed",{form:this.currentForm,formType:"create",record:null}),t.Message&&this.showInfo(t.Message),await this.load(),this.emit("recordAdded",{record:t.Record})):this.showError(t.Message||"Create failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Create failed: "+e.message)}}else this.currentForm.reportValidity()}async editRecord(e){var t=e.recordData,s=await this.formBuilder.createForm("edit",t);this.modals.editRecord.setContent(s),this.modals.editRecord.show(),this.currentForm=s,this.currentEditingRow=e,this.emit("formCreated",{form:s,formType:"edit",record:t})}async saveEditedRecord(){if(this.currentForm&&this.currentEditingRow)if(this.currentForm.checkValidity()){var e=this.getFormData(this.currentForm);try{var t=await this.performUpdate(e);"OK"===t.Result?(this.clearListCache(),this.modals.editRecord.close(),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null,this.emit("formClosed",{form:this.currentForm,formType:"edit",record:this.currentEditingRow.recordData}),this.updateRowData(this.currentEditingRow,t.Record||e),t.Message&&this.showInfo(t.Message),this.emit("recordUpdated",{record:t.Record||e,row:this.currentEditingRow})):this.showError(t.Message||"Update failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Update failed: "+e.message)}}else this.currentForm.reportValidity()}async cloneRecord(e){var e={...e.recordData},t=(this.keyField&&(e[this.keyField]=""),await this.formBuilder.createForm("create",e));this.modals.addRecord.options.content=t,this.modals.addRecord.setContent(t),this.modals.addRecord.show(),this.currentForm=t,this.emit("formCreated",{form:t,formType:"create",record:e})}async deleteRows(e){if(e.length){var t=this.options.messages.areYouSure;if(confirm(t)){var s,a=[];for(s of e)try{var i=await this.performDelete(s);a.push({key:s,success:"OK"===i.Result,result:i})}catch(e){a.push({key:s,success:!1,error:e.message})}a.filter(e=>e.success).forEach(({key:e})=>{e=this.getRowByKey(e);e&&this.removeRowFromTable(e)});t=a.filter(e=>!e.success).length;0<t&&this.showError(t+` of ${a.length} records could not be deleted`),this.refreshRowStyles(),this.updatePagingInfo()}}}deleteRecord(e){var t=e.recordData;let s=this.options.messages.deleteConfirmation;if("function"==typeof this.options.deleteConfirmation){t={row:e,record:t,deleteConfirmMessage:s,cancel:!1,cancelMessage:this.options.messages.cancel};if(this.options.deleteConfirmation(t),t.cancel)return void(t.cancelMessage&&this.showError(t.cancelMessage));s=t.deleteConfirmMessage}this.modals.deleteConfirm.setContent(`<p>${s}</p>`),this.modals.deleteConfirm.show(),this.currentDeletingRow=e}async confirmDelete(){if(this.currentDeletingRow){var e=this.getKeyValue(this.currentDeletingRow.recordData);try{var t=await this.performDelete(e);"OK"===t.Result?(this.clearListCache(),this.modals.deleteConfirm.close(),this.removeRowFromTable(this.currentDeletingRow),t.Message&&this.showInfo(t.Message),this.emit("recordDeleted",{record:this.currentDeletingRow.recordData})):this.showError(t.Message||"Delete failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Delete failed: "+e.message)}}}async performCreate(e){var t=this.options.actions.createAction;if("function"==typeof t)return t(e);if("string"==typeof t)return l.post(t,e);throw new Error("No valid createAction provided")}async performUpdate(e){var t=this.options.actions.updateAction;if("function"==typeof t)return t(e);if("string"==typeof t)return l.post(t,e);throw new Error("No valid updateAction provided")}async performDelete(e){var t=this.options.actions.deleteAction;let s;if(s=null===e||"object"!=typeof e||Array.isArray(e)?{[this.keyField]:e}:e,"function"==typeof t)return t(s);if("string"==typeof t)return l.post(t,s);throw new Error("No valid deleteAction provided")}getFormData(e){var t,s,a,i={};for([t,s]of new FormData(e).entries())t.endsWith("[]")?(i[a=t.slice(0,-2)]||(i[a]=[]),i[a].push(s)):i.hasOwnProperty(t)?Array.isArray(i[t])?i[t].push(s):i[t]=[i[t],s]:i[t]=s;return i}updateRowData(i,e){i.recordData={...i.recordData,...e},Object.keys(e).forEach(e=>{var t,s,a=this.options.fields[e];a&&(t=i.querySelector(`td[data-field-name="${e}"]`))&&(s=this.formBuilder.generateOptionsCacheKey("table",{}),s=this.formBuilder.resolvedFieldOptions.get(e)?.[s],e=this.getDisplayText(i.recordData,e,s),t.innerHTML=a.listEscapeHTML?O.escapeHtml(e):e,t.className=(`${a.listClass||""} `+(a.listClassEntry||"")).trim())})}removeRowFromTable(e){e.remove(),0===this.elements.tableBody.querySelectorAll(".ftable-data-row").length&&this.addNoDataRow(),this.refreshRowStyles()}makeRowSelectable(t){!1!==this.options.selectOnRowClick&&t.addEventListener("click",e=>{["INPUT","BUTTON","SELECT","TEXTAREA","A"].includes(e.target.tagName)||e.target.classList.contains("norowselectonclick")||this.toggleRowSelection(t)})}toggleRowSelection(e){var t=e.classList.contains("ftable-row-selected");if(this.shiftKeyDown&&this.lastSelectedRow&&this.options.multiselect){this.clearAllSelections();var s=Array.from(this.elements.tableBody.querySelectorAll(".ftable-data-row")),a=s.indexOf(this.lastSelectedRow),i=s.indexOf(e),[i,o]=a<i?[a,i]:[i,a];for(let e=i;e<=o;e++)this.selectRow(s[e])}else this.options.multiselect||this.clearAllSelections(),t?this.deselectRow(e):this.selectRow(e);t&&!this.shiftKeyDown||(this.lastSelectedRow=e),this.emit("selectionChanged",{selectedRows:this.getSelectedRows()})}selectRow(e){O.addClass(e,"ftable-row-selected");var t=e.querySelector('input[type="checkbox"]'),t=(t&&(t.checked=!0),this.getKeyValue(e.recordData));t&&this.state.selectedRecords.add(t)}deselectRow(e){O.removeClass(e,"ftable-row-selected");var t=e.querySelector('input[type="checkbox"]'),t=(t&&(t.checked=!1),this.getKeyValue(e.recordData));t&&this.state.selectedRecords.delete(t)}recalcColumnWidths(){this.columnList.forEach(e=>{var t=this.options.fields[e],e=this.elements.table.querySelector(`[data-field-name="${e}"]`);e&&t.width&&(e.style.width=t.width)}),this.elements.table.offsetHeight}recalcColumnWidthsOnce(){this._recalculatedOnce||(this.recalcColumnWidths(),this._recalculatedOnce=!0)}clearAllSelections(){this.elements.tableBody.querySelectorAll(".ftable-row-selected").forEach(e=>this.deselectRow(e))}toggleSelectAll(t){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>{t?this.selectRow(e):this.deselectRow(e)}),this.emit("selectionChanged",{selectedRows:this.getSelectedRows()})}getSelectedRows(){return Array.from(this.elements.tableBody.querySelectorAll(".ftable-row-selected"))}sortByColumn(s,a=!1){var i=this.options.fields[s];if(i&&!1!==i.sorting){i=this.state.sorting.findIndex(e=>e.fieldName===s);let e=!0,t="ASC";0<=i?"ASC"===this.state.sorting[i].direction?(t="DESC",this.state.sorting[i].direction=t):(this.state.sorting.splice(i,1),e=!1):this.state.sorting.push({fieldName:s,direction:t}),(!this.options.multiSorting||this.options.multiSortingCtrlKey&&!a)&&(this.state.sorting=e?[{fieldName:s,direction:t}]:[]),this.updateSortingHeaders(),this.load(),this.saveState()}}updateSortingHeaders(){this.elements.table.querySelectorAll(".ftable-column-header-sortable").forEach(e=>{O.removeClass(e,"ftable-column-header-sorted-asc ftable-column-header-sorted-desc")}),this.state.sorting.forEach(e=>{var t=this.elements.table.querySelector(`[data-field-name="${e.fieldName}"]`);t&&O.addClass(t,"ftable-column-header-sorted-"+e.direction.toLowerCase())})}updatePagingInfo(){var e,t,s;this.options.paging&&this.elements.pageInfoSpan&&(this.state.totalRecordCount<=0?(this.elements.pageInfoSpan.textContent="",this.elements.pagingListArea.innerHTML=""):(e=(this.state.currentPage-1)*this.state.pageSize+1,t=Math.min(this.state.currentPage*this.state.pageSize,this.state.totalRecordCount),s=this.options.messages.pagingInfo||"Showing {0}-{1} of {2}",this.elements.pageInfoSpan.textContent=s.replace(/\{0\}/g,e).replace(/\{1\}/g,t).replace(/\{2\}/g,this.state.totalRecordCount),this.createPageListNavigation(),this.createPageGotoNavigation()))}createPageListNavigation(){if(this.elements.pagingListArea){this.elements.pagingListArea.innerHTML="";var e=Math.ceil(this.state.totalRecordCount/this.state.pageSize);if(!(e<=1)){if(this.createPageButton("«",1,1===this.state.currentPage,"ftable-page-number-first"),this.createPageButton("‹",this.state.currentPage-1,1===this.state.currentPage,"ftable-page-number-previous"),"normal"==this.options.pageList){var s=this.calculatePageNumbers(e);let t=0;s.forEach(e=>{1<e-t&&O.create("span",{className:"ftable-page-number-space",textContent:"...",parent:this.elements.pagingListArea}),this.createPageButton(e.toString(),e,!1,e===this.state.currentPage?"ftable-page-number ftable-page-number-active":"ftable-page-number"),t=e})}this.createPageButton("›",this.state.currentPage+1,this.state.currentPage>=e,"ftable-page-number-next"),this.createPageButton("»",e,this.state.currentPage>=e,"ftable-page-number-last")}}}createPageGotoNavigation(){if(this.options.paging&&"none"!==this.options.gotoPageArea){let s=Math.ceil(this.state.totalRecordCount/this.state.pageSize);if(s<=1)this.elements.pagingGotoArea.style.display="none",this.elements.pagingGotoArea.innerHTML="";else{this.elements.pagingGotoArea.style.display="inline-block",this.elements.pagingGotoArea.innerHTML="";O.create("span",{textContent:this.options.messages.gotoPageLabel+": ",parent:this.elements.pagingGotoArea});var e="ftable-goto-page-"+(this.options.tableId||"default");if("combobox"===this.options.gotoPageArea){this.elements.gotoPageSelect=O.create("select",{id:e,className:"ftable-page-goto-select",parent:this.elements.pagingGotoArea});for(let e=1;e<=s;e++)O.create("option",{attributes:{value:e},textContent:e,parent:this.elements.gotoPageSelect});this.elements.gotoPageSelect.value=this.state.currentPage,this.elements.gotoPageSelect.addEventListener("change",e=>{e=parseInt(e.target.value);1<=e&&e<=s&&this.changePage(e)})}else"textbox"===this.options.gotoPageArea&&(this.elements.gotoPageInput=O.create("input",{attributes:{type:"number",id:e,min:"1",max:s,value:this.state.currentPage,className:"ftable-page-goto-input",style:"width: 65px; margin-left: 4px;"},parent:this.elements.pagingGotoArea}),this.elements.gotoPageInput.addEventListener("change",e=>{var t=parseInt(e.target.value);1<=t&&t<=s?this.changePage(t):e.target.value=this.state.currentPage}))}}else this.elements.pagingGotoArea.style.display="none",this.elements.pagingGotoArea.innerHTML=""}createPageButton(e,t,s,a){a=O.create("span",{className:a+(s?" ftable-page-number-disabled":""),innerHTML:e,parent:this.elements.pagingListArea});s||(a.style.cursor="pointer",a.addEventListener("click",e=>{e.preventDefault(),this.changePage(t)}))}calculatePageNumbers(t){if(t<=7)return Array.from({length:t},(e,t)=>t+1);var s=this.state.currentPage,a=new Set([1,2,t-1,t]);for(let e=Math.max(1,s-1);e<=Math.min(t,s+1);e++)a.add(e);return Array.from(a).sort((e,t)=>e-t)}changePage(e){var t=Math.ceil(this.state.totalRecordCount/this.state.pageSize);(e=Math.max(1,Math.min(e,t)))!==this.state.currentPage&&(this.state.currentPage=e,this.load())}changePageSize(e){this.state.pageSize=e,this.state.currentPage=1,this.load(),this.saveState()}showLoadingIndicator(){0===this.options.loadingAnimationDelay?this.modals.loading&&this.modals.loading.show():this.loadingTimeout=setTimeout(()=>{this.modals.loading&&this.modals.loading.show(),this.loadingShownAt=Date.now()},this.options.loadingAnimationDelay||500)}hideLoadingIndicator(){this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null);var e=this.loadingShownAt?Date.now()-this.loadingShownAt:0;this.modals.loading&&(e<200?setTimeout(()=>{this.modals.loading.hide()},200-e):this.modals.loading.hide()),this.loadingShownAt=null}showError(e){this.modals.error?(this.modals.error.setContent(e),this.modals.error.show()):alert(e)}showWarning(e){this.modals.warning?(this.modals.warning.setContent(e),this.modals.warning.show()):alert(e)}showInfo(e){this.modals.info?(this.modals.info.setContent(e),this.modals.info.show()):alert(e)}reload(e=!1){return this.clearListCache(),e?this.preservedSelections=new Set(this.state.selectedRecords):this.state.selectedRecords.clear(),this.load().then(()=>(e&&this.preservedSelections&&(this.restoreSelections(),this.preservedSelections=null),this))}clearListCache(){this.options.actions.listAction&&"string"==typeof this.options.actions.listAction&&this.formBuilder.optionsCache.clear(this.options.actions.listAction)}restoreSelections(){if(this.preservedSelections)return this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>{var t=this.getKeyValue(e.recordData);t&&this.preservedSelections.has(t)&&this.selectRow(e)}),this}getRowByKey(e){return this.elements.tableBody.querySelector(`[data-record-key="${e}"]`)}destroy(){this.element&&this.element.ftableInstance&&(this.element.ftableInstance=null),Object.values(this.modals).forEach(e=>e.destroy()),this.elements.mainContainer&&this.elements.mainContainer.remove(),this.searchTimeout&&clearTimeout(this.searchTimeout),this.loadingTimeout&&clearTimeout(this.loadingTimeout),window.removeEventListener("resize",this.handleResize),this.options=null,this.state=null,this.elements=null,this.formBuilder=null,this.modals=null}setOption(e,t){return this.options[e]=t,this}getState(){return{...this.state}}addFilter(t,e,s="equals"){return this.state.filters||(this.state.filters=[]),this.state.filters=this.state.filters.filter(e=>e.fieldName!==t),null!=e&&""!==e&&this.state.filters.push({fieldName:t,value:e,operator:s}),this}clearFilters(){return this.state.filters=[],this}exportToCSV(e="table-data.csv"){var t,s,a,i=this.elements.table.cloneNode(!0),o=[],r=e=>`"${String(e||"").replace(/"/g,'""')}"`,n=[];for(t of i.querySelectorAll("thead th"))t.classList.contains("ftable-command-column-header")||t.classList.contains("ftable-toolbarsearch-column-header")||"none"===t.style.display||(s=t.textContent.trim(),n.push(r(s)));o.push(n.join(","));for(a of i.querySelectorAll("tbody tr"))if("none"!==a.style.display){var l,c,d,h=[];let e=!1;for(l of a.querySelectorAll("td"))l.classList.contains("ftable-command-column")||"none"===l.style.display||(l.querySelector("img, button, input, select")&&(l.innerHTML=l.textContent),c=l.innerHTML.replace(/<br\s*\/?>/gi,"\n"),(d=document.createElement("div")).innerHTML=c,h.push(r(d.textContent||"")),e=!0);e&&o.push(h.join(","))}var i=o.join("\n"),i=new Blob(["\ufeff"+i],{type:"text/csv;charset=utf-8;"}),p=document.createElement("a");p.href=URL.createObjectURL(i),p.download=e,p.click(),p.remove()}printTable(){var e=window.open("","_blank","width=800,height=600"),t=this.elements.table.outerHTML,t=`
|
|
1
|
+
(e=>{let a={serverCommunicationError:"An error occurred while communicating to the server.",loadingMessage:"Loading records...",noDataAvailable:"No data available!",addNewRecord:"Add new record",editRecord:"Edit record",areYouSure:"Are you sure?",deleteConfirmation:"This record will be deleted. Are you sure?",yes:"Yes",no:"No",save:"Save",saving:"Saving",cancel:"Cancel",deleteText:"Delete",deleting:"Deleting",error:"An error has occured",warning:"Warning",close:"Close",cannotLoadOptionsFor:"Cannot load options for field {0}!",pagingInfo:"Showing {0}-{1} of {2}",canNotDeletedRecords:"Can not delete {0} of {1} records!",deleteProgress:"Deleting {0} of {1} records, processing...",pageSizeChangeLabel:"Row count",gotoPageLabel:"Go to page",sortingInfoPrefix:"Sorting applied: ",sortingInfoSuffix:"",ascending:"Ascending",descending:"Descending",sortingInfoNone:"No sorting applied",resetSorting:"Reset sorting",csvExport:"CSV",printTable:"🖨️ Print",cloneRecord:"Clone Record",resetTable:"Reset table",resetTableConfirm:"This will reset all columns, pagesize, sorting to their defaults. Do you want to continue?",resetSearch:"Reset"};class t{constructor(){this.cache=new Map,this.pendingRequests=new Map}generateKey(e,t){return e+"?"+Object.keys(t||{}).sort().map(e=>e+"="+t[e]).join("&")}get(e,t){e=this.generateKey(e,t);return this.cache.get(e)}set(e,t,a){e=this.generateKey(e,t);this.cache.set(e,a)}clear(e=null,t=null){if(e)if(t){t=this.generateKey(e,t);this.cache.delete(t)}else{var a,s=e.split("?")[0];for([a]of this.cache)a.startsWith(s)&&this.cache.delete(a)}else this.cache.clear()}async getOrCreate(e,t,a){let s=this.generateKey(e,t);e=this.cache.get(s);return e||(this.pendingRequests.has(s)?this.pendingRequests.get(s):(t=(async()=>{try{var e=await a();return this.cache.set(s,e),e}finally{this.pendingRequests.delete(s)}})(),this.pendingRequests.set(s,t),t))}size(){return this.cache.size}}class s{static LOG_LEVELS={DEBUG:0,INFO:1,WARN:2,ERROR:3,NONE:4};constructor(e=s.LOG_LEVELS.WARN){this.level=e}log(t,e){var a;!window.console||t<this.level||(a=Object.keys(s.LOG_LEVELS).find(e=>s.LOG_LEVELS[e]===t),console.log(`fTable ${a}: `+e))}debug(e){this.log(s.LOG_LEVELS.DEBUG,e)}info(e){this.log(s.LOG_LEVELS.INFO,e)}warn(e){this.log(s.LOG_LEVELS.WARN,e)}error(e){this.log(s.LOG_LEVELS.ERROR,e)}}class k{static PROPERTY_ATTRIBUTES=new Set(["value","checked","selected","disabled","readOnly","name","id","type","placeholder","min","max","step","required","multiple","accept","className","textContent","innerHTML","title"]);static create(e,t={}){let a=document.createElement(e);return void 0!==t.style&&(a.style.cssText=t.style),k.PROPERTY_ATTRIBUTES.forEach(e=>{e in t&&null!==t[e]&&(a[e]=t[e])}),void 0!==t.parent&&t.parent.appendChild(a),t.attributes&&Object.entries(t.attributes).forEach(([e,t])=>{null!==t&&(k.PROPERTY_ATTRIBUTES.has(e)?a[e]=t:a.setAttribute(e,t))}),a}static find(e,t=document){return t.querySelector(e)}static findAll(e,t=document){return Array.from(t.querySelectorAll(e))}static addClass(e,t){e.classList.add(...t.split(" "))}static removeClass(e,t){e.classList.remove(...t.split(" "))}static toggleClass(e,t){e.classList.toggle(t)}static show(e){e.style.display=""}static hide(e){e.style.display="none"}static escapeHtml(e){if(!e)return e;let t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,e=>t[e])}}class l{static async request(e,t={}){var a={method:"GET",headers:{}},s={...a,...t};t.headers&&(s.headers={...a.headers,...t.headers});try{var i=await fetch(e,s);if(401===i.status)throw new Error("Unauthorized");if(!i.ok)throw new Error("HTTP error! status: "+i.status);var o=i.headers.get("content-type");if(o&&o.includes("application/json"))return await i.json();var r=await i.text();try{return JSON.parse(r)}catch{return{Result:"OK",Message:r}}}catch(e){throw e}}static async get(e,t={}){let s=new URL(e,window.location.href);return Object.entries(t).forEach(([e,a])=>{if(null!=a)if(Array.isArray(a)){let t=e.endsWith("[]")?e:e+"[]";a.forEach(e=>{null!=e&&s.searchParams.append(t,e)})}else s.searchParams.append(e,a)}),this.request(s.toString(),{method:"GET",headers:{"Content-Type":"application/x-www-form-urlencoded"}})}static async post(e,t={}){e=new URL(e,window.location.href);let s=new FormData;return Object.entries(t).forEach(([e,a])=>{if(null!=a)if(Array.isArray(a)){let t=e.endsWith("[]")?e:e+"[]";a.forEach(e=>{null!=e&&s.append(t,e)})}else s.append(e,a)}),this.request(e.toString(),{method:"POST",body:s})}}class i{constructor(e,t="localStorage"){this.prefix=e,this.method=t}set(e,t){var a,e=""+this.prefix+e;"localStorage"===this.method?localStorage.setItem(e,t):((a=new Date).setDate(a.getDate()+30),document.cookie=e+`=${t}; expires=${a.toUTCString()}; path=/`)}get(e){e=""+this.prefix+e;if("localStorage"===this.method)return localStorage.getItem(e);var t,a=e+"=";for(t of decodeURIComponent(document.cookie).split(";")){for(;" "===t.charAt(0);)t=t.substring(1);if(0===t.indexOf(a))return t.substring(a.length,t.length)}return null}remove(e){e=""+this.prefix+e;"localStorage"===this.method?localStorage.removeItem(e):document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"}generatePrefix(e,t){e=e?e+"#":"";return"ftable#"+(t=>{let a=0;if(0!==t.length)for(let e=0;e<t.length;e++){var s=t.charCodeAt(e);a=(a<<5)-a+s,a&=a}return a})(e+=t.join("$")+"#c"+t.length)}}class o{constructor(e={}){this.options={title:"Modal",content:"",buttons:[],className:"ftable-modal",parent:document.body,...e},this.overlay=null,this.modal=null,this.isOpen=!1}create(){this.overlay=k.create("div",{className:"ftable-modal-overlay",parent:this.options.parent}),this.modal=k.create("div",{className:"ftable-modal "+this.options.className,parent:this.overlay});k.create("h2",{className:"ftable-modal-header",textContent:this.options.title,parent:this.modal});k.create("span",{className:"ftable-modal-close",innerHTML:"×",parent:this.modal}).addEventListener("click",()=>this.close());var e=k.create("div",{className:"ftable-modal-body",parent:this.modal});if("string"==typeof this.options.content?e.innerHTML=this.options.content:e.appendChild(this.options.content),0<this.options.buttons.length){let a=k.create("div",{className:"ftable-modal-footer",parent:this.modal});this.options.buttons.forEach(e=>{var t=k.create("button",{className:"ftable-dialog-button "+(e.className||""),innerHTML:`<span>${e.text}</span>`,parent:a});e.onClick&&(t._originalOnClick=e.onClick,t.addEventListener("click",this._createWrappedClickHandler(t)))})}return this.options.closeOnOverlayClick&&this.overlay.addEventListener("click",e=>{e.target===this.overlay&&this.close()}),this.hide(),this}show(){return this.modal||this.create(),this.overlay.style.display="flex",this.isOpen=!0,this.modal.querySelectorAll(".ftable-dialog-button").forEach(e=>{e.disabled=!1}),this}hide(){return this.overlay&&(this.overlay.style.display="none"),this.isOpen=!1,this}close(){return this.hide(),this.options.onClose&&this.options.onClose(),this}destroy(){return this.overlay&&this.overlay.remove(),this.isOpen=!1,this}setContent(e){this.options.content=e;var t=this.modal.querySelector(".ftable-modal-body");t&&(t.innerHTML="","string"==typeof e?t.innerHTML=/<(div|ul|ol|table|p|h[1-6]|blockquote)/i.test(e)?e:`<p>${e}</p>`:t.appendChild(e))}_createWrappedClickHandler(s){return async e=>{s.disabled=!0;try{var t,a=s._originalOnClick;"function"==typeof a&&(t=a.call(s,e))instanceof Promise&&await t}catch(e){console.error("Modal button action failed:",e)}finally{s.disabled=!1}}}}class r{constructor(e){this.options=e,this.dependencies=new Map,this.optionsCache=new t}async getFieldOptions(t,a="table",e={}){var s=this.options.fields[t],i="search"===a?s.searchOptions??s.options:s.options;if(!i)return null;var o=this.shouldSkipCache(s,a,e);try{return await this.resolveOptions({...s,options:i},e,a,o)}catch(e){return console.error(`Failed to resolve options for ${t} (${a}):`,e),i}}shouldSkipCache(e,t,a){return!!a.forceRefresh||!!e.noCache&&("boolean"==typeof e.noCache?e.noCache:"function"==typeof e.noCache?e.noCache({context:t,...a}):"object"==typeof e.noCache&&!0===e.noCache[t])}shouldIncludeField(e,t){return"create"===t?!1!==e.create&&!(!0===e.key&&!0!==e.create):"edit"!==t||!1!==e.edit}createFieldContainer(e,t,a,s){var i=k.create("div",{className:"hidden"!=t.type?"ftable-input-field-container":"",attributes:{id:"ftable-input-field-container-div-"+e}}),t=("hidden"!=t.type&&k.create("div",{className:"ftable-input-label",textContent:t.inputTitle||t.title,parent:i}),this.createInput(e,t,a[e],s));return i.appendChild(t),i}async createForm(e="create",t={}){this.currentFormRecord=t;var a,s,i,o,r=k.create("form",{className:`ftable-dialog-form ftable-${e}-form`});this.buildDependencyMap();for([a,s]of Object.entries(this.options.fields))this.shouldIncludeField(s,e)&&(i={...s},s.dependsOn?i.options=s.options:(o=await this.getFieldOptions(a,e,{record:t,source:e}),i.options=o),o=this.createFieldContainer(a,i,t,e),r.appendChild(o));return this.setupDependencyListeners(r),r}buildDependencyMap(){this.dependencies.clear(),Object.entries(this.options.fields).forEach(([t,a])=>{if(a.dependsOn){let e;"string"==typeof a.dependsOn&&(e=a.dependsOn.split(",").map(e=>e.trim()).filter(e=>e)).forEach(e=>{this.dependencies.has(e)||this.dependencies.set(e,[]),this.dependencies.get(e).push(t)})}})}setupDependencyListeners(a){Array.from(this.dependencies.keys()).forEach(e=>{var t=a.querySelector(`[name="${e}"]`);t&&t.addEventListener("change",()=>{this.handleDependencyChange(a,e)})}),this.handleDependencyChange(a)}async resolveOptions(e,t={},a="",s=!1){if(!e.options)return[];if(Array.isArray(e.options)||"object"==typeof e.options)return e.options;let i;t={...t,source:a,clearCache:()=>{s=!0,this.updateFieldCacheSetting(e,a,!0)}};if("function"==typeof e.options)i=await e.options(t);else{if("string"!=typeof e.options)return[];i=e.options}t=i&&"object"==typeof i&&i.url;let o=t?i.url:i;if(s=t&&void 0!==i.noCache?i.noCache:s,"string"!=typeof o)return[];if(!s)return this.optionsCache.getOrCreate(o,{},async()=>{try{var e=this.options.forcePost?await l.post(o):await l.get(o);return e.Options||e.options||e||[]}catch(e){return console.error(`Failed to load options from ${o}:`,e),[]}});try{var r=this.options.forcePost?await l.post(o):await l.get(o);return r.Options||r.options||r||[]}catch(e){return console.error(`Failed to load options from ${o}:`,e),[]}}updateFieldCacheSetting(e,t,a){e.noCache?"boolean"==typeof e.noCache?e.noCache={table:e.noCache,create:e.noCache,edit:e.noCache,[t]:a}:"object"==typeof e.noCache&&(e.noCache[t]=a):e.noCache={[t]:a}}clearOptionsCache(e=null,t=null){this.optionsCache.clear(e,t)}getFormValues(e){var t={},a=e.elements;for(let e=0;e<a.length;e++){var s=a[e],i=s.name;if(i&&!s.disabled)switch(s.type){case"checkbox":t[i]=s.checked?s.value||"1":"0";break;case"radio":s.checked&&(t[i]=s.value);break;case"select-multiple":t[i]=Array.from(s.selectedOptions).map(e=>e.value);break;default:t[i]=s.value}}return t}async handleDependencyChange(e,a=""){var s,i,o=this.getFormValues(e),r=e.classList.contains("ftable-create-form")?"create":"edit",n=this.currentFormRecord||{},l={record:n,source:r,form:e,dependedValues:o};for([s,i]of Object.entries(this.options.fields))if(i.dependsOn){if(""!==a)if(!i.dependsOn.split(",").map(e=>e.trim()).filter(e=>e).includes(a))continue;let t=e.querySelector(`[name="${s}"]`);if(t&&this.shouldIncludeField(i,r))try{"SELECT"===t.tagName?t.innerHTML='<option value="">Loading...</option>':"INPUT"===t.tagName&&t.list&&(c=document.getElementById(t.list.id))&&(c.innerHTML="");var c,d=t.value||n[s]||"",h={...l,dependsOnField:i.dependsOn,dependsOnValue:o[i.dependsOn]},p=await this.getFieldOptions(s,r,h);"SELECT"===t.tagName?this.populateSelectOptions(t,p,d):"INPUT"===t.tagName&&t.list&&(this.populateDatalistOptions(t.list,p),d)&&(t.value=d),setTimeout(()=>{t.dispatchEvent(new Event("change",{bubbles:!0}))},0)}catch(e){console.error(`Error loading options for ${s}:`,e),"SELECT"===t.tagName&&(t.innerHTML='<option value="">Error</option>')}}}parseInputAttributes(e){if("string"!=typeof e)return e||{};for(var t={},a=/(\w+)(?:=("[^"]*"|'[^']*'|\S+))?/g;null!==(i=a.exec(e));){var s=i[1],i=i[2]?i[2].replace(/^["']|["']$/g,""):"";t[s]=""===i?"true":i}return t}createInput(e,t,a,s){var i=k.create("div",{className:`ftable-input ftable-${t.type||"text"}-input`});let o;switch(null===(a=void 0===a?null:a)&&t.defaultValue&&(a=t.defaultValue),!t.type&&t.options&&(t.type="select"),t.type){case"hidden":o=this.createHiddenInput(e,t,a);break;case"textarea":o=this.createTextarea(e,t,a);break;case"select":o=this.createSelect(e,t,a);break;case"checkbox":o=this.createCheckbox(e,t,a);break;case"radio":o=this.createRadioGroup(e,t,a);break;case"datalist":o=this.createDatalistInput(e,t,a);break;case"file":o=this.createFileInput(e,t,a);break;case"date":case"datetime":case"datetime-local":o=this.createDateInput(e,t,a);break;default:o=this.createTypedInput(e,t,a)}return"function"==typeof t.input?(s={field:t,record:this.currentFormRecord,inputField:o,formType:s},"string"==typeof(s=t.input(s))?i.innerHTML=s:s instanceof Node?i.appendChild(s):(i.appendChild(o),o.datalistElement&&o.datalistElement instanceof Node&&i.appendChild(o.datalistElement))):(i.appendChild(o),o.datalistElement&&o.datalistElement instanceof Node&&i.appendChild(o.datalistElement)),t.explain&&k.create("div",{className:"ftable-field-explain",innerHTML:`<small>${t.explain}</small>`,parent:i}),i}createDateInput(a,s,i){if("undefined"==typeof FDatepicker)return createTypedInput(a,s,i);{let e=s.dateFormat||this.options.defaultDateFormat;var o,r=document.createElement("div"),n=k.create("input",{id:"real-"+a,type:"hidden",value:i,name:a}),i={"data-date":i};s.inputAttributes&&(o=this.parseInputAttributes(s.inputAttributes),Object.assign(i,o));let t=k.create("input",{attributes:i,id:"Edit-"+a,type:"text",placeholder:s.placeholder||null,className:s.inputClass||"datepicker-input",readOnly:!0});switch(r.appendChild(n),r.appendChild(t),s.type){case"date":setTimeout(()=>{new FDatepicker(t,{format:e,altField:"real-"+a,altFormat:"Y-m-d"})},0);break;case"datetime":case"datetime-local":setTimeout(()=>{new FDatepicker(t,{format:e,timepicker:!0,altField:"real-"+a,altFormat:"Y-m-d H:i:00"})},0)}return r}}createTypedInput(e,t,a){var s,i=t.type||"text",o={};let r=e,n=(t.inputAttributes&&(s=this.parseInputAttributes(t.inputAttributes),Object.assign(o,s),void 0!==s.multiple&&!1!==s.multiple)&&(r=e+"[]"),k.create("input",{attributes:o,type:i,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:a,name:r}));return n.addEventListener("keypress",e=>{if(13===(e.keyCode||e.which))return e.preventDefault(),n.dispatchEvent(new Event("change",{bubbles:!0})),!1}),n}createDatalistInput(e,t,a){var s={list:e+"-datalist"},i=(t.inputAttributes&&(i=this.parseInputAttributes(t.inputAttributes),Object.assign(s,i)),k.create("input",{attributes:s,type:"search",name:e,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:a})),s=k.create("datalist",{id:e+"-datalist"});return t.options&&this.populateDatalistOptions(s,t.options),i.datalistElement=s,i}populateDatalistOptions(a,e){a.innerHTML="",Array.isArray(e)?e.forEach(e=>{k.create("option",{value:e.Value||e.value||e,textContent:e.DisplayText||e.text||e,parent:a})}):"object"==typeof e&&Object.entries(e).forEach(([e,t])=>{k.create("option",{value:e,textContent:t,parent:a})})}createHiddenInput(e,t,a){var s={};return t.inputAttributes&&(t=this.parseInputAttributes(t.inputAttributes),Object.assign(s,t)),k.create("input",{attributes:s,type:"hidden",name:e,id:"Edit-"+e,value:a})}createTextarea(e,t,a){var s,i={};return t.inputAttributes&&(s=this.parseInputAttributes(t.inputAttributes),Object.assign(i,s)),k.create("textarea",{attributes:i,name:e,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:a})}createSelect(e,t,a){var s={};let i=e,o=!1;if(t.inputAttributes&&(r=this.parseInputAttributes(t.inputAttributes),Object.assign(s,r),o=void 0!==r.multiple&&!1!==r.multiple)&&(i=e+"[]"),o)return this.createCustomMultiSelect(e,t,a,s,i);s.name=i;var r=k.create("select",{attributes:s,name:e,id:"Edit-"+e,className:t.inputClass||null});return t.options&&this.populateSelectOptions(r,t.options,a),r}createCustomMultiSelect(e,t,a,s,i){var o=Array.isArray(t.options)?t.options:t.options&&"object"==typeof t.options?Object.entries(t.options).map(([e,t])=>({Value:e,DisplayText:t})):[],r=t.livesearch??!1;return this._buildCustomMultiSelect({containerId:e,hiddenSelectId:"Edit-"+e,hiddenSelectName:i,extraClasses:"",containerDataFieldName:e,hiddenSelectAttributes:{},optionsSource:o,initialValues:Array.isArray(a)?a:a?a.toString().split(",").filter(e=>e):[],placeholderText:t.placeholder||this.options.messages?.multiSelectPlaceholder||"Click to select options...",livesearch:r,onChangeExtra:e=>{e.dispatchEvent(new Event("change",{bubbles:!0}))},buildHiddenSelectOnUpdate:!0})}_buildCustomMultiSelect(e){let{hiddenSelectId:t,hiddenSelectName:a,extraClasses:s,containerDataFieldName:i,hiddenSelectAttributes:o,optionsSource:r,initialValues:n,placeholderText:l,livesearch:c,onChangeExtra:d,buildHiddenSelectOnUpdate:h}=e;let p=(e=r)?(Array.isArray(e)?e:Object.entries(e).map(([e,t])=>({Value:e,DisplayText:t}))).map(e=>({optValue:void 0!==e.Value?e.Value:void 0!==e.value?e.value:e,optText:e.DisplayText||e.text||e,groupLabel:e.Group||e.group||null})).filter(e=>null!=e.optValue&&""!==e.optValue):[],u=new Map(p.map(e=>[e.optValue.toString(),e.optText]));e=["ftable-multiselect-container",...s.split(" ").filter(Boolean)].join(" ");let m=k.create("div",{className:e,attributes:{"data-field-name":i}}),f=k.create("select",{id:t,name:a,multiple:!0,style:"display: none;",attributes:o}),g=(m.appendChild(f),m.hiddenSelect=f,k.create("div",{className:"ftable-multiselect-display",parent:m,attributes:{tabindex:"0"}})),b=k.create("div",{className:"ftable-multiselect-selected",parent:g}),v=k.create("span",{className:"ftable-multiselect-placeholder",textContent:l,parent:b}),y=(k.create("button",{type:"button",className:"ftable-multiselect-toggle",innerHTML:"▼",parent:g,attributes:{tabindex:"-1"}}),null),w=null,C=new Set(n.map(e=>e.toString())),S=new Map,E=(h||p.forEach(({optValue:e,optText:t})=>{k.create("option",{value:e,textContent:t,parent:f})}),()=>{b.innerHTML="",h?(f.innerHTML="",C.forEach(e=>{var t=u.get(e)??e;k.create("option",{value:e,textContent:t,selected:!0,parent:f})})):Array.from(f.options).forEach(e=>{e.selected=C.has(e.value)}),0===C.size?(v.textContent=l,b.appendChild(v)):C.forEach(t=>{var e=k.create("span",{className:"ftable-multiselect-tag",parent:b});k.create("span",{className:"ftable-multiselect-tag-text",textContent:u.get(t)||t,parent:e}),k.create("span",{className:"ftable-multiselect-tag-remove",innerHTML:"×",parent:e}).addEventListener("click",e=>{e.stopPropagation(),C.delete(t);e=S.get(t);e&&(e.checked=!1),E(),d&&d(f)})}),d&&d(f)}),x=()=>{g.focus(),y&&(y.remove(),y=null),w&&(w.remove(),w=null),m._cleanupHandlers&&(m._cleanupHandlers(),m._cleanupHandlers=null)},A=()=>{var e,t,a;y&&(e=g.getBoundingClientRect(),a=window.pageYOffset||document.documentElement.scrollTop,t=window.pageXOffset||document.documentElement.scrollLeft,t=e.left+t,a=e.bottom+a+4,Object.assign(y.style,{position:"absolute",left:t+"px",top:a+"px",width:e.width+"px",minWidth:"fit-content",boxSizing:"border-box",zIndex:"10000"}),(a=y.getBoundingClientRect()).right>window.innerWidth)&&(t=Math.max(10,window.innerWidth-a.width-10),y.style.left=t+"px")},N=(e="")=>{if(y){Array.from(y.querySelectorAll(".ftable-multiselect-option, .ftable-multiselect-optgroup")).forEach(e=>e.remove());let t=e.toLowerCase();e=e?p.filter(e=>e.optText.toLowerCase().includes(t)):p;let i=new Set,o=null;e.forEach(({optValue:t,optText:e,groupLabel:a})=>{a&&a!==i[i.size-1]?i.has(a)||(i.add(a),o=k.create("div",{className:"ftable-multiselect-optgroup",textContent:a,parent:y})):a||(o=null);a=k.create("div",{className:"ftable-multiselect-option",parent:y});let s=k.create("input",{type:"checkbox",className:"ftable-multiselect-checkbox",checked:C.has(t.toString()),parent:a});S.set(t.toString(),s),k.create("label",{className:"ftable-multiselect-label",textContent:e,parent:a}),a.addEventListener("click",e=>{e.stopPropagation();e=t.toString();C.has(e)?(C.delete(e),s.checked=!1):(C.add(e),s.checked=!0),E()})})}},T=s=>{if(s&&s.stopPropagation(),y)x();else{if(document.querySelectorAll(".ftable-multiselect-dropdown").forEach(e=>e.remove()),document.querySelectorAll(".ftable-multiselect-overlay").forEach(e=>e.remove()),w=k.create("div",{className:"ftable-multiselect-overlay",parent:document.body}),y=k.create("div",{className:"ftable-multiselect-dropdown",parent:document.body,attributes:{tabindex:"-1",role:"listbox","aria-multiselectable":"true"}}),c){s=k.create("div",{className:"ftable-multiselect-livesearch-wrap",parent:y});let e=k.create("input",{type:"search",className:"ftable-multiselect-livesearch",placeholder:"Search...",parent:s,attributes:{autocomplete:"off"}});e.addEventListener("input",()=>{N(e.value)}),e.addEventListener("click",e=>e.stopPropagation()),setTimeout(()=>e.focus(),0)}N(),A(),c||y.focus(),y.addEventListener("keydown",e=>{var t,a;"Escape"===e.key?x():"ArrowDown"===e.key||"ArrowUp"===e.key?(e.preventDefault(),a=(t=Array.from(y.querySelectorAll(".ftable-multiselect-checkbox"))).indexOf(document.activeElement),t["ArrowDown"===e.key?a<t.length-1?a+1:0:0<a?a-1:t.length-1]?.focus()):" "!==e.key&&"Enter"!==e.key||(e.preventDefault(),document.activeElement.classList.contains("ftable-multiselect-checkbox")&&document.activeElement.click())}),w.addEventListener("click",e=>{e.target===w&&x()});let e=()=>A(),t=e=>{y&&y.contains(e.target)||A()},a=new ResizeObserver(()=>A());window.addEventListener("scroll",t,!0),window.addEventListener("resize",e),a.observe(b),m._cleanupHandlers=()=>{window.removeEventListener("scroll",t,!0),window.removeEventListener("resize",e),a.disconnect()}}},L=(g.addEventListener("click",T),g.querySelector(".ftable-multiselect-toggle").addEventListener("click",T),g.addEventListener("keydown",e=>{"ArrowDown"!==e.key&&"Enter"!==e.key||(e.preventDefault(),T())}),m.resetMultiSelect=()=>{C.clear(),S.forEach(e=>{e.checked=!1}),x(),E()},new MutationObserver(e=>{e.forEach(e=>{e.removedNodes.forEach(e=>{(e===m||e.contains&&e.contains(m))&&(x(),L.disconnect())})})}));return setTimeout(()=>{m.parentNode&&L.observe(m.parentNode,{childList:!0,subtree:!0})},0),E(),m}createRadioGroup(o,r,n){let l=k.create("div",{className:"ftable-radio-group"});return r.options&&(Array.isArray(r.options)?r.options:"object"==typeof r.options?Object.entries(r.options).map(([e,t])=>({Value:e,DisplayText:t})):[]).forEach((e,t)=>{var a=k.create("div",{className:"ftable-radio-wrapper",parent:l}),t=o+"_"+t,s={},i=(r.inputAttributes&&(i=this.parseInputAttributes(r.inputAttributes),Object.assign(s,i)),void 0!==e.Value?e.Value:void 0!==e.value?e.value:e);k.create("input",{attributes:s,type:"radio",name:o,id:t,value:i,className:r.inputClass||null,checked:i==n,parent:a}),k.create("label",{attributes:{for:t},textContent:e.DisplayText||e.text||e,parent:a})}),l}createCheckbox(e,t,a){var s=k.create("div",{className:"ftable-yesno-check-wrapper"}),a=[1,"1",!0,"true"].includes(a);let i=this.options.messages.no,o=this.options.messages.yes;return t.values&&"object"==typeof t.values&&(void 0!==t.values[0]&&(i=t.values[0]),void 0!==t.values[1])&&(o=t.values[1]),k.create("input",{className:["ftable-yesno-check-input",t.inputClass||""].filter(Boolean).join(" "),type:"checkbox",name:e,id:"Edit-"+e,value:"1",parent:s}).checked=a,t.label?k.create("label",{className:"ftable-yesno-check-fixedlabel",attributes:{for:"Edit-"+e},textContent:t.label,parent:s}):k.create("label",{className:"ftable-yesno-check-text",attributes:{for:"Edit-"+e,"data-yes":o,"data-no":i},parent:s}),s}populateSelectOptions(o,e,r){if(o.innerHTML="",Array.isArray(e)){let a=new Map,s=[],i=(e.forEach(e=>{var t=e.Group||e.group||null;(t?(a.has(t)||a.set(t,[]),a.get(t)):s).push(e)}),(e,t)=>{var a=void 0!==e.Value?e.Value:void 0!==e.value?e.value:e;let s=k.create("option",{value:a,textContent:e.DisplayText||e.text||e,selected:a==r,parent:t});e.Data&&"object"==typeof e.Data&&Object.entries(e.Data).forEach(([e,t])=>{s.setAttribute("data-"+e,t)})});s.forEach(e=>i(e,o)),a.forEach((e,t)=>{let a=k.create("optgroup",{attributes:{label:t},parent:o});e.forEach(e=>i(e,a))})}else"object"==typeof e&&Object.entries(e).forEach(([e,t])=>{k.create("option",{value:e,textContent:t,selected:e==r,parent:o})})}createFileInput(e,t,a){var s,i={};let o=e;return t.inputAttributes&&(s=this.parseInputAttributes(t.inputAttributes),Object.assign(i,s),void 0!==s.multiple&&!1!==s.multiple)&&(o=e+"[]"),k.create("input",{type:"file",id:"Edit-"+e,name:o,className:t.inputClass||null,attributes:i})}}class n extends class{constructor(){this.events={}}on(e,t){return this.events[e]||(this.events[e]=[]),this.events[e].push(t),this}once(t,a){let s=(...e)=>{this.off(t,s),a.apply(this,e)};return s.fn=a,this.on(t,s),this}emit(e,t={}){return this.events[e]&&this.events[e].forEach(e=>e(t)),this}off(e,t){return this.events[e]&&(this.events[e]=this.events[e].filter(e=>e!==t)),this}}{constructor(e,t={}){if(super(),this.element="string"==typeof e?document.querySelector(e):e,this.element){if(this.element.ftableInstance)return this.element.ftableInstance;this.options=this.mergeOptions(t),this.verifyOptions(),this.logger=new s(this.options.logLevel),this.userPrefs=new i("",this.options.saveUserPreferencesMethod),this.formBuilder=new r(this.options,this),this.state={records:[],totalRecordCount:0,currentPage:1,isLoading:!1,selectedRecords:new Set,sorting:[],searchQueries:{}},this.elements={},this.modals={},this.searchTimeout=null,this.lastSortEvent=null,this._recalculatedOnce=!1,this.shiftKeyDown=!1,this.lastSelectedRow=null,(this.element.ftableInstance=this).init()}}mergeOptions(e){var t={tableId:void 0,logLevel:s.LOG_LEVELS.WARN,actions:{},fields:{},forcePost:!0,closeOnOverlayClick:!0,animationsEnabled:!0,loadingAnimationDelay:1e3,defaultDateLocale:"",defaultDateFormat:"Y-m-d",saveUserPreferences:!0,saveUserPreferencesMethod:"localStorage",defaultSorting:"",tableReset:!1,paging:!1,pageList:"normal",pageSize:10,pageSizes:[10,25,50,100,250,500],gotoPageArea:"combobox",sorting:!1,multiSorting:!1,multiSortingCtrlKey:!0,selecting:!1,multiselect:!1,openChildAsAccordion:!1,toolbarsearch:!1,toolbarreset:!0,searchDebounceMs:300,listCache:3e4,messages:{...a}};return this.deepMerge(t,e)}deepMerge(e,t){var a,s={...e};for(a in t)t[a]&&"object"==typeof t[a]&&!Array.isArray(t[a])?s[a]=this.deepMerge(s[a]||{},t[a]):s[a]=t[a];return s}verifyOptions(){this.options.pageSize&&!this.options.pageSizes.includes(this.options.pageSize)&&(this.options.pageSize=this.options.pageSizes[0])}static setMessages(e){Object.assign(a,e)}init(){this.processFieldDefinitions(),this.createMainStructure(),this.setupFTableUserPreferences(),this.createTable(),this.createModals(),this.options.paging&&this.createPagingUI(),this.resolveAllFieldOptionsForTable().then(()=>{setTimeout(()=>{this.refreshDisplayValues()},0)}).catch(console.error),this.bindEvents(),this.updateSortingHeaders(),this.renderSortingInfo(),this.initColumnWidths()}initColumnWidths(){var e=this.columnList.filter(e=>{e=this.options.fields[e];return!e.action&&"hidden"!==e.visibility&&"separator"!==e.visibility});let t=e.length;e.forEach(e=>{e=this.options.fields[e];e.width=e.width||100/t+"%"})}normalizeColumnWidths(){var e=this.elements.mainContainer,t=this.columnList.map(e=>({th:this.elements.table.querySelector(`[data-field-name="${e}"]`),field:this.options.fields[e]})).filter(e=>e.th&&!e.field.action&&"hidden"!==e.field.visibility&&"separator"!==e.field.visibility);if(0!==t.length){let a=e.offsetWidth,s=0;t.forEach(e=>{var t=e.th.offsetWidth/a*100;e.field.width=t+"%",e.th.style.width=e.field.width,s+=t})}}parseDefaultSorting(e){let o=[];return e&&"string"==typeof e&&e.split(",").forEach(a=>{a=a.trim();if(a){var s=a.toUpperCase().indexOf(" DESC"),i=a.toUpperCase().indexOf(" ASC");let e="ASC",t=a;e=0<s?(t=a.slice(0,s).trim(),"DESC"):(t=(0<i?a.slice(0,i):a).trim(),"ASC");s=this.options.fields[t];s&&!1!==s.sorting&&o.push({fieldName:t,direction:e})}}),o}createPagingUI(){this.elements.bottomPanel=k.create("div",{className:"ftable-bottom-panel",parent:this.elements.mainContainer}),this.elements.leftArea=k.create("div",{className:"ftable-left-area",parent:this.elements.bottomPanel}),this.elements.rightArea=k.create("div",{className:"ftable-right-area",parent:this.elements.bottomPanel}),this.elements.pagingListArea=k.create("div",{className:"ftable-page-list",parent:this.elements.leftArea}),this.elements.pagingGotoArea=k.create("div",{className:"ftable-page-goto",parent:this.elements.leftArea}),this.elements.pageInfoSpan=k.create("div",{className:"ftable-page-info",parent:this.elements.rightArea}),!1!==this.options.pageSizeChangeArea&&this.createPageSizeSelector()}createPageSizeSelector(){var e=k.create("span",{className:"ftable-page-size-change",parent:this.elements.leftArea});k.create("span",{textContent:this.options.messages.pageSizeChangeLabel,parent:e});let a=k.create("select",{className:"ftable-page-size-select",parent:e});(this.options.pageSizes||[10,25,50,100,250,500]).forEach(e=>{var t=k.create("option",{attributes:{value:e},textContent:e.toString(),parent:a});e===this.state.pageSize&&(t.selected=!0)}),a.addEventListener("change",e=>{this.changePageSize(parseInt(e.target.value))})}processFieldDefinitions(){this.fieldList=Object.keys(this.options.fields),this.fieldList.forEach(e=>{var e=this.options.fields[e],t=!0===e.key;!!e.action?(e.list=!0,e.create=!1,e.edit=!1,e.sorting=!1,e.searchable=!1):t?(void 0!==e.create&&e.create||(e.create=!0,e.type="hidden"),void 0!==e.edit&&e.edit||(e.edit=!0,e.type="hidden")):(e.create=e.create??!0,e.edit=e.edit??!0,e.list=e.list??!0,e.sorting=e.sorting??!0),e.visibility=e.visibility??"visible"}),this.columnList=this.fieldList.filter(e=>!1!==this.options.fields[e].list),this._userPlacedActions=new Set(this.fieldList.filter(e=>this.options.fields[e].action).map(e=>this.options.fields[e].action)),this.keyField=this.fieldList.find(e=>!0===this.options.fields[e].key),this.keyField||this.logger.info("No key field defined")}async resolveAllFieldOptionsForTable(){this.tableOptionsCache=new Map;var e=this.columnList.map(async t=>{var e=this.options.fields[t];if(!e.action)try{var a=await this.formBuilder.getFieldOptions(t,"table");a&&this.tableOptionsCache.set(t,a)}catch(e){console.error(`Failed to resolve table options for ${t}:`,e)}});await Promise.all(e)}async refreshDisplayValues(){var e=this.elements.tableBody.querySelectorAll(".ftable-data-row");if(0!==e.length)for(var t of e)for(var a of this.columnList){var s,i,o=this.options.fields[a];!o.action&&o.options&&(s=t.querySelector(`td[data-field-name="${a}"]`))&&(i=this.tableOptionsCache?.get(a),a=this.getDisplayText(t.recordData,a,i),s.innerHTML=o.listEscapeHTML?k.escapeHtml(a):a)}}createMainStructure(){this.elements.mainContainer=k.create("div",{className:"ftable-main-container",parent:this.element}),this.options.title&&(this.elements.titleDiv=k.create("div",{className:"ftable-title",parent:this.elements.mainContainer}),k.create("div",{className:"ftable-title-text",innerHTML:this.options.title,parent:this.elements.titleDiv})),this.elements.toolbarDiv=k.create("div",{className:"ftable-toolbar",parent:this.elements.titleDiv||this.elements.mainContainer}),this.elements.tableDiv=k.create("div",{className:"ftable-table-div",parent:this.elements.mainContainer})}createTable(){this.elements.table=k.create("table",{className:"ftable",parent:this.elements.tableDiv}),this.options.tableId&&(this.elements.table.id=this.options.tableId),this.createTableHeader(),this.createTableBody(),this.addNoDataRow()}createTableHeader(){var e=k.create("thead",{parent:this.elements.table});let o=k.create("tr",{parent:e});if(this.options.selecting&&this.options.selectingCheckboxes&&!this._userPlacedActions.has("select")){var t=k.create("th",{className:"ftable-command-column-header ftable-column-header-select",parent:o});if(this.options.multiselect){let e=k.create("input",{attributes:{type:"checkbox"},parent:t});e.addEventListener("change",()=>{this.toggleSelectAll(e.checked)})}}this.columnList.forEach(t=>{var a=this.options.fields[t];if(a.action){var s={select:"ftable-column-header-select",update:"ftable-column-header-edit",clone:"ftable-column-header-clone",delete:"ftable-column-header-delete"};let t=k.create("th",{className:"ftable-command-column-header "+(s[a.action]||""),parent:o});if(a.title&&(t.textContent=a.title),"select"===a.action&&this.options.selecting&&this.options.selectingCheckboxes&&this.options.multiselect){let e=k.create("input",{attributes:{type:"checkbox"},parent:t});e.addEventListener("change",()=>{this.toggleSelectAll(e.checked)})}void(a.width&&(t.style.width=a.width))}else{let e=k.create("th",{className:`ftable-column-header ${a.listClass||""} `+(a.listClassHeader||""),attributes:{"data-field-name":t},parent:o});a.width&&(e.style.width=a.width);var s=k.create("div",{className:"ftable-column-header-container",parent:e}),i=(a.tooltip&&s.setAttribute("title",a.tooltip),k.create("span",{className:"ftable-column-header-text",innerHTML:a.title||t,parent:s}));this.options.sorting&&!1!==a.sorting&&(k.addClass(i,"ftable-sortable-text"),k.addClass(e,"ftable-column-header-sortable"),e.addEventListener("click",e=>{e.preventDefault();e=e.ctrlKey||e.metaKey;this.sortByColumn(t,e)})),!1!==this.options.columnResizable&&!1!==a.columnResizable&&this.makeColumnResizable(e,s),"hidden"!==a.visibility&&"separator"!==a.visibility||k.hide(e)}}),this.options.actions.updateAction&&!this._userPlacedActions.has("update")&&k.create("th",{className:"ftable-command-column-header ftable-column-header-edit",parent:o}),this.options.actions.cloneAction&&!this._userPlacedActions.has("clone")&&k.create("th",{className:"ftable-command-column-header ftable-column-header-clone",parent:o}),this.options.actions.deleteAction&&!this._userPlacedActions.has("delete")&&k.create("th",{className:"ftable-command-column-header ftable-column-header-delete",parent:o}),this.options.toolbarsearch&&this.createSearchHeaderRow(e).catch(e=>{console.error("Failed to create search header row:",e)})}async createSearchHeaderRow(e){var t,a=k.create("tr",{className:"ftable-toolbarsearch-row",parent:e});this.options.selecting&&this.options.selectingCheckboxes&&!this._userPlacedActions.has("select")&&k.create("th",{className:"ftable-toolbarsearch-column-header",parent:a});for(let i of this.columnList){var o=this.options.fields[i];if(o.action)k.create("th",{className:"ftable-toolbarsearch-column-header ftable-command-column-header",parent:a});else{var r=!1!==o.searchable,n=k.create("th",{className:"ftable-toolbarsearch-column-header",parent:a});if(r){r=k.create("div",{className:"ftable-column-header-container",parent:n});let a,s="text";o.searchType?s=o.searchType:!o.type&&o.options?s="select":o.type&&(s=o.type);var l="ftable-toolbarsearch-"+i;switch(s){case"date":case"datetime":case"datetime-local":if("undefined"!=typeof FDatepicker){let e=o.searchDateFormat||o.dateFormat||this.options.defaultDateFormat;var c=document.createElement("div"),d=k.create("input",{className:"ftable-toolbarsearch-extra",type:"hidden",id:"ftable-toolbarsearch-extra-"+i,attributes:{"data-field-name":i}});let t=k.create("input",{className:"ftable-toolbarsearch",id:"ftable-toolbarsearch-"+i,type:"text",placeholder:o.searchPlaceholder||o.placeholder||"",readOnly:!0});switch(c.appendChild(d),c.appendChild(t),s){case"date":setTimeout(()=>{new FDatepicker(t,{format:e,altField:"ftable-toolbarsearch-extra-"+i,altFormat:"Y-m-d",autoClose:!0})},0);break;case"datetime":case"datetime-local":setTimeout(()=>{new FDatepicker(t,{format:e,timepicker:!0,altField:"ftable-toolbarsearch-extra-"+i,altFormat:"Y-m-d H:i:00"})},0)}a=c}else a=k.create("input",{className:"ftable-toolbarsearch",type:s,id:l,attributes:{"data-field-name":i}});break;case"checkbox":o.values||(o.values={0:this.options.messages.no,1:this.options.messages.yes}),a=await this.createSelectForSearch(i,o,!0);break;case"select":a=o.options?await this.createSelectForSearch(i,o,!1):k.create("input",{className:"ftable-toolbarsearch",type:"text",id:l,placeholder:o.searchPlaceholder||o.placeholder||"Search...",attributes:{"data-field-name":i}});break;case"datalist":a=await this.createDatalistForSearch(i,o);break;default:a=k.create("input",{className:"ftable-toolbarsearch",type:"text",id:l,placeholder:o.searchPlaceholder||o.placeholder||"Search...",attributes:{"data-field-name":i}})}if(a){r.appendChild(a),a.datalistElement&&a.datalistElement instanceof Node&&r.appendChild(a.datalistElement);let e=a;"SELECT"===(e=a.classList&&a.classList.contains("ftable-multiselect-container")&&a.hiddenSelect?a.hiddenSelect:e).tagName?e.addEventListener("change",e=>{this.handleSearchInputChange(e)}):e.addEventListener("input",e=>{this.handleSearchInputChange(e)})}}"hidden"!==o.visibility&&"separator"!==o.visibility||k.hide(n)}}this.options.toolbarsearch&&this.options.toolbarreset&&(e=k.create("th",{className:"ftable-toolbarsearch-column-header ftable-toolbarsearch-reset",parent:a}),0<(t=(this.options.actions.updateAction&&!this._userPlacedActions.has("update")?1:0)+(this.options.actions.deleteAction&&!this._userPlacedActions.has("delete")?1:0)+(this.options.actions.cloneAction&&!this._userPlacedActions.has("clone")?1:0))?e.colSpan=t:k.addClass(e,"ftable-command-column-header"),k.create("button",{className:"ftable-toolbarsearch-reset-button",textContent:this.options.messages.resetSearch,attributes:{id:"ftable-toolbarsearch-reset-button"},parent:e}).addEventListener("click",()=>this.resetSearch()))}async createSelectForSearch(e,t,a){var s,i="ftable-toolbarsearch-"+e,o={};let r=e,n=!1;t.searchAttributes?(s=this.formBuilder.parseInputAttributes(t.searchAttributes),Object.assign(o,s),n=void 0!==s.multiple&&!1!==s.multiple):t.inputAttributes&&(s=this.formBuilder.parseInputAttributes(t.inputAttributes),Object.assign(o,s),n=void 0!==s.multiple&&!1!==s.multiple),n&&(r=e+"[]"),o["data-field-name"]=r;let l;if(a&&t.values?l=Object.entries(t.values).map(([e,t])=>({Value:e,DisplayText:t})):(t.options||t.searchOptions)&&(l=await this.formBuilder.getFieldOptions(e,"search")),n)return this.createCustomMultiSelectForSearch(i,e,t,l,o);let c=k.create("select",{attributes:o,id:i,className:"ftable-toolbarsearch"});return 0<l?.length&&(""===l[0].Value||""===l[0].value||""===l[0]||""===l[0].DisplayText&&null==l[0].Value)||k.create("option",{value:"",innerHTML:" ",parent:c}),l&&Array.isArray(l)?l.forEach(e=>{k.create("option",{value:void 0!==e.Value?e.Value:void 0!==e.value?e.value:e,textContent:e.DisplayText||e.text||e,parent:c})}):l&&"object"==typeof l&&Object.entries(l).forEach(([e,t])=>{k.create("option",{value:e,textContent:t,parent:c})}),c}createCustomMultiSelectForSearch(e,t,a,s,i){var o=a.livesearch??!1;return this.formBuilder._buildCustomMultiSelect({hiddenSelectId:e,hiddenSelectName:i["data-field-name"]||e,extraClasses:"ftable-multiselect-search ftable-toolbarsearch",containerDataFieldName:i["data-field-name"]||e,hiddenSelectAttributes:i,optionsSource:s,initialValues:[],placeholderText:a.searchPlaceholder||a.placeholder||this.options.messages?.multiSelectPlaceholder||"Click to select options...",livesearch:o,onChangeExtra:e=>{e.dispatchEvent(new Event("change",{bubbles:!0}))},buildHiddenSelectOnUpdate:!1})}async createDatalistForSearch(e,t){var a="ftable-toolbarsearch-"+e,s=a+"-datalist",i=k.create("datalist",{attributes:{id:s}}),a=k.create("input",{className:"ftable-toolbarsearch",type:"search",id:a,placeholder:t.searchPlaceholder||t.placeholder||"Type or select...",attributes:{"data-field-name":e,list:s}}),t=(a.datalistElement=i,await this.formBuilder.getFieldOptions(e,"search"));return t&&this.formBuilder.populateDatalistOptions(i,t),a}handleSearchInputChange(e){var e=e.target,t=e.getAttribute("data-field-name");let a;a=e.multiple&&e.options?Array.from(e.selectedOptions).map(e=>e.value).filter(e=>""!==e.trim()).map(e=>e.trim()):e.value.trim(),this.state.currentPage=1,Array.isArray(a)&&0<a.length||!Array.isArray(a)&&a?this.state.searchQueries[t]=a:delete this.state.searchQueries[t],clearTimeout(this.searchTimeout),this.searchTimeout=setTimeout(()=>{this.load()},this.options.searchDebounceMs)}resetSearch(){this.state.searchQueries={},this.elements.table.querySelectorAll(".ftable-toolbarsearch").forEach(e=>{"SELECT"===e.tagName?e.selectedIndex=0:e.value=""}),this.elements.table.querySelectorAll(".ftable-multiselect-container").forEach(e=>{"function"==typeof e.resetMultiSelect&&e.resetMultiSelect()}),this.load()}getNextResizableHeader(t){var a=Array.from(this.elements.table.querySelectorAll("thead th.ftable-column-header-resizable"));for(let e=a.indexOf(t)+1;e<a.length;e++)if(null!==a[e].offsetParent)return a[e];return null}makeColumnResizable(s,e){k.addClass(s,"ftable-column-header-resizable"),this.elements.resizeBar||(this.elements.resizeBar=k.create("div",{className:"ftable-column-resize-bar",parent:this.elements.mainContainer}),k.hide(this.elements.resizeBar));e=k.create("div",{className:"ftable-column-resize-handler",parent:e});let i=!1,o=0,r=0,n,l=null,c=0,d=null,h=(e.addEventListener("mousedown",e=>{e.preventDefault(),e.stopPropagation(),i=!0,n=this.elements.mainContainer.getBoundingClientRect(),o=e.clientX,r=s.offsetWidth,(l=this.getNextResizableHeader(s))&&(c=l.offsetWidth,e=l.dataset.fieldName,d=this.options.fields[e]);e=s.getBoundingClientRect();this.elements.resizeBar.style.left=e.right-n.left+"px",this.elements.resizeBar.style.top=e.top-n.top+"px",this.elements.resizeBar.style.height=this.elements.table.offsetHeight+"px",k.show(this.elements.resizeBar),document.addEventListener("mousemove",h),document.addEventListener("mouseup",p)}),e=>{i&&(this.elements.resizeBar.style.left=e.clientX-n.left+"px")}),p=e=>{var t,a;i&&(i=!1,e=e.clientX-o,a=n.width,t=Math.max(50,r+e)/a*100,l&&(e=Math.max(50,c-e)/a*100,d.width=e.toFixed(2)+"%",l.style.width=d.width),(a=this.options.fields[s.dataset.fieldName]).width=t.toFixed(2)+"%",s.style.width=a.width,this.normalizeColumnWidths(),this.options.saveUserPreferences&&this.saveColumnSettings(),k.hide(this.elements.resizeBar),document.removeEventListener("mousemove",h),document.removeEventListener("mouseup",p))}}saveColumnSettings(){if(this.options.saveUserPreferences){let s={};this.columnList.forEach(e=>{var t,a=this.options.fields[e];a.action||(t=this.elements.table.querySelector(`[data-field-name="${e}"]`))&&(s[e]={width:t.style.width||a.width||"auto",visibility:a.visibility||"visible"})}),this.userPrefs.set("column-settings",JSON.stringify(s))}}saveState(){var e;this.options.saveUserPreferences&&(e={sorting:this.state.sorting,pageSize:this.state.pageSize},this.userPrefs.set("table-state",JSON.stringify(e)))}loadColumnSettings(){if(this.options.saveUserPreferences){var e=this.userPrefs.get("column-settings");if(e)try{var t=JSON.parse(e);Object.entries(t).forEach(([e,t])=>{e=this.options.fields[e];e&&!e.action&&(t.width&&(e.width=t.width),t.visibility)&&(e.visibility=t.visibility)})}catch(e){this.logger.warn("Failed to load column settings:",e)}}}loadState(){if(this.options.saveUserPreferences){var e=this.userPrefs.get("table-state");if(e)try{var t=JSON.parse(e);Array.isArray(t.sorting)&&(this.state.sorting=t.sorting),t.pageSize&&this.options.pageSizes.includes(t.pageSize)&&(this.state.pageSize=t.pageSize)}catch(e){this.logger.warn("Failed to load table state:",e)}}}createTableBody(){this.elements.tableBody=k.create("tbody",{parent:this.elements.table})}addNoDataRow(){var e,t;this.elements.tableBody.querySelector(".ftable-no-data-row")||(e=k.create("tr",{className:"ftable-no-data-row",parent:this.elements.tableBody}),t=this.elements.table.querySelector("thead tr").children.length,k.create("td",{attributes:{colspan:t},textContent:this.options.messages.noDataAvailable,parent:e}))}removeNoDataRow(){var e=this.elements.tableBody.querySelector(".ftable-no-data-row");e&&e.remove()}createModals(){this.options.actions.createAction&&this.createAddRecordModal(),this.options.actions.updateAction&&this.createEditRecordModal(),this.options.actions.deleteAction&&this.createDeleteConfirmModal(),this.createErrorModal(),this.createWarningModal(),this.createInfoModal(),this.createLoadingModal(),Object.values(this.modals).forEach(e=>e.create())}createAddRecordModal(){this.modals.addRecord=new o({parent:this.elements.mainContainer,title:this.options.messages.addNewRecord,className:"ftable-add-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>{this.modals.addRecord.close(),this.emit("formClosed",{form:this.currentForm,formType:"create",record:null}),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null}},{text:this.options.messages.save,className:"ftable-dialog-savebutton",onClick:()=>this.saveNewRecord()}]})}createEditRecordModal(){this.modals.editRecord=new o({parent:this.elements.mainContainer,title:this.options.messages.editRecord,className:"ftable-edit-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>{this.modals.editRecord.close(),this.emit("formClosed",{form:this.currentForm,formType:"edit",record:null}),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null}},{text:this.options.messages.save,className:"ftable-dialog-savebutton",onClick:()=>this.saveEditedRecord()}]})}createDeleteConfirmModal(){this.modals.deleteConfirm=new o({parent:this.elements.mainContainer,title:this.options.messages.areYouSure,className:"ftable-delete-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>this.modals.deleteConfirm.close()},{text:this.options.messages.deleteText,className:"ftable-dialog-deletebutton",onClick:()=>this.confirmDelete()}]})}createErrorModal(){this.modals.error=new o({parent:this.elements.mainContainer,title:this.options.messages.error,className:"ftable-error-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.close,className:"ftable-dialog-closebutton",onClick:()=>this.modals.error.close()}]})}createWarningModal(){this.modals.warning=new o({parent:this.elements.mainContainer,title:this.options.messages.warning,className:"ftable-warning-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.close,className:"ftable-dialog-closebutton",onClick:()=>this.modals.warning.close()}]})}createInfoModal(){this.modals.info=new o({parent:this.elements.mainContainer,title:"",className:"ftable-info-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.close,className:"ftable-dialog-closebutton",onClick:()=>this.modals.info.close()}]})}createLoadingModal(){this.modals.loading=new o({parent:this.elements.mainContainer,title:"",className:"ftable-loading-modal",closeOnOverlayClick:!1,content:`<div class="ftable-loading-message">${this.options.messages.loadingMessage}</div>`})}bindEvents(){this.subscribeOptionEvents(),this.createCustomToolbarItems(),this.createToolbarButtons(),this.bindKeyboardEvents(),!1!==this.options.columnSelectable&&this.createColumnSelectionMenu()}subscribeOptionEvents(){["formCreated","formClosed","recordsLoaded","recordAdded","recordUpdated","recordDeleted","selectionChanged"].forEach(e=>{"function"==typeof this.options[e]&&this.on(e,this.options[e])})}createColumnSelectionMenu(){this.elements.columnSelectionOverlay=null,this.elements.columnSelectionMenu=null,this.elements.table.querySelector("thead").addEventListener("contextmenu",e=>{e.preventDefault(),this.showColumnSelectionMenu(e)})}showColumnSelectionMenu(e){this.hideColumnSelectionMenu(),this.elements.columnSelectionOverlay=k.create("div",{className:"ftable-contextmenu-overlay",parent:document.body}),this.elements.columnSelectionMenu=k.create("div",{className:"ftable-column-selection-container",parent:document.body}),this.populateColumnSelectionMenu(),this.positionColumnSelectionMenu(e),this.elements.columnSelectionOverlay.addEventListener("click",e=>{e.target===this.elements.columnSelectionOverlay&&this.hideColumnSelectionMenu()}),this.elements.columnSelectionOverlay.addEventListener("contextmenu",e=>{e.preventDefault(),this.hideColumnSelectionMenu()})}populateColumnSelectionMenu(){let l=k.create("ul",{className:"ftable-column-select-list",parent:this.elements.columnSelectionMenu});this.columnList.forEach(t=>{var e=this.options.fields[t];if(!e.action){var a="hidden"!==e.visibility,s="fixed"===e.visibility,i="separator"===e.visibility,o=this.isFieldSorted(t),r=k.create("li",{className:"ftable-column-select-item",parent:l}),n=k.create("label",{className:"ftable-column-select-label",parent:r});if(!i){let e=k.create("input",{attributes:{type:"checkbox",id:"column-"+t},parent:n});e.checked=a,(s||o&&a)&&(e.disabled=!0,r.style.opacity="0.6"),e.disabled||e.addEventListener("change",()=>{this.setColumnVisibility(t,e.checked)})}s=k.create("span",{textContent:e.title||t,style:i?"font-weight: bold;":null,parent:n});o&&((a=k.create("span",{className:"ftable-sort-indicator",textContent:" (sorted)",parent:s})).style.fontSize="0.8em",a.style.color="#666")}})}positionColumnSelectionMenu(e){var t=this,a=e.pageX,e=e.pageY,e=(t.elements.columnSelectionMenu.style.position="absolute",t.elements.columnSelectionMenu.style.left=a+"px",t.elements.columnSelectionMenu.style.top=e+"px",t.elements.columnSelectionMenu.style.minWidth="100px",t.elements.columnSelectionMenu.style.boxSizing="border-box",t.elements.columnSelectionMenu.offsetWidth),s=window.innerWidth;s<a+e&&(a=Math.max(10,s-e-10),t.elements.columnSelectionMenu.style.left=a+"px")}hideColumnSelectionMenu(){this.elements.columnSelectionOverlay&&(this.elements.columnSelectionOverlay.remove(),this.elements.columnSelectionMenu.remove(),this.elements.columnSelectionOverlay=null,this.elements.columnSelectionMenu=null)}isFieldSorted(t){return this.state.sorting.some(e=>e.fieldName===t)}createToolbarButtons(){this.options.csvExport&&this.addToolbarButton({text:this.options.messages.csvExport,className:"ftable-toolbar-item-csv",onClick:()=>{var e=this.options.title?this.options.title.replace(/[^a-z0-9]/gi,"-").toLowerCase()+".csv":"table-export.csv";this.exportToCSV(e)}}),this.options.printTable&&this.addToolbarButton({text:this.options.messages.printTable,className:"ftable-toolbar-item-print",onClick:()=>{this.printTable()}}),this.options.actions.createAction&&this.addToolbarButton({text:this.options.messages.addNewRecord,className:"ftable-toolbar-item-add-record",addIconSpan:!0,onClick:()=>this.showAddRecordForm()})}addToolbarButton(t){var e,a=k.create("button",{className:"ftable-toolbar-item "+(t.className||""),id:t.id||null,title:t.title||null,textContent:t.text||null,type:"button",parent:this.elements.toolbarDiv});return t.addIconSpan&&(e=k.create("span",{className:"ftable-toolbar-item-icon "+(t.className||""),parent:a}),t.text)&&(a.textContent="",a.append(e,t.text||"")),t.icon&&(e=k.create("img",{attributes:{src:t.icon,alt:"",width:16,height:16,style:"margin-right: 6px; vertical-align: middle;"},parent:a}),t.text)&&(a.textContent="",a.append(e,t.text||"")),t.onClick&&a.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),t.onClick(e)}),t.disabled&&(a.disabled=!0),a}createCustomToolbarItems(){this.options.toolbar&&this.options.toolbar.items&&this.options.toolbar.items.forEach((e,t)=>{this.addToolbarButton({text:e.text||"",className:"ftable-toolbar-item-custom "+(e.buttonClass||""),id:e.buttonId||"ftable-toolbar-item-custom-id-"+t,title:e.tooltip||"",icon:e.icon||null,onClick:"function"==typeof e.click?e.click:null})})}bindKeyboardEvents(){this.options.selecting&&(document.addEventListener("keydown",e=>{"Shift"===e.key&&(this.shiftKeyDown=!0)}),document.addEventListener("keyup",e=>{"Shift"===e.key&&(this.shiftKeyDown=!1)}))}setupFTableUserPreferences(){var e;this.options.saveUserPreferences&&(e=this.userPrefs.generatePrefix(this.options.tableId||"",this.fieldList),this.userPrefs=new i(e,this.options.saveUserPreferencesMethod),this.loadState(),this.loadColumnSettings())}async load(e={},t={}){if(!this.state.isLoading){t.fromPage1&&(this.state.currentPage=1),this.state.isLoading=!0,this.showLoadingIndicator();try{var a={...e,...this.buildLoadParams()},s=await this.performLoad(a);this.processLoadedData(s),this.emit("recordsLoaded",{records:s.Records,serverResponse:s})}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Load failed: "+e.message)}finally{this.state.isLoading=!1,this.hideLoadingIndicator()}this.renderSortingInfo(),this.normalizeColumnWidths()}}buildLoadParams(){var e,t={};if(this.options.paging&&(this.state.pageSize||(this.state.pageSize=this.options.pageSize),t.jtStartIndex=(this.state.currentPage-1)*this.state.pageSize,t.jtPageSize=this.state.pageSize),this.options.sorting&&(0<this.state.sorting.length?t.jtSorting=this.state.sorting.map(e=>e.fieldName+" "+e.direction).join(", "):this.options.defaultSorting&&(t.jtSorting=this.parseDefaultSorting(this.options.defaultSorting).map(e=>e.fieldName+" "+e.direction).join(", "))),this.options.toolbarsearch&&0<Object.keys(this.state.searchQueries).length){let a=[],s=[];Object.entries(this.state.searchQueries).forEach(([t,e])=>{Array.isArray(e)?e.forEach(e=>{""!==e&&null!=e&&(a.push(e),s.push(t))}):""!==e&&(a.push(e),s.push(t))}),0<a.length&&(t.q=a,t.opt=s)}return"function"==typeof this.options.listQueryParams&&(e=this.options.listQueryParams(),Object.assign(t,e)),t}isCacheExpired(e,t){return!e||!e.timestamp||t<Date.now()-e.timestamp}async performLoad(e){var t=this.options.actions.listAction;if(this.options.listCache&&"string"==typeof t){var a=this.formBuilder.optionsCache.get(t,e);if(a&&!this.isCacheExpired(a,this.options.listCache))return a.data}let s;if("function"==typeof t)s=await t(e);else{if("string"!=typeof t)throw new Error("No valid listAction provided");s=this.options.forcePost?await l.post(t,e):await l.get(t,e)}if(s&&"OK"===s.Result)return this.options.listCache&&"string"==typeof t&&this.formBuilder.optionsCache.set(t,e,{data:s,timestamp:Date.now()}),s;throw new Error(s?.Message||"Invalid response from server")}processLoadedData(e){"OK"!==e.Result?this.showError(e.Message||"Unknown error occurred"):(this.state.records=e.Records||[],this.state.totalRecordCount=e.TotalRecordCount||this.state.records.length,this.options.paging&&0===this.state.records.length&&0<this.state.totalRecordCount?(this.state.currentPage=1,this.load()):(this.renderTableData(),this.updatePagingInfo()))}renderTableData(){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>e.remove()),0===this.state.records.length?this.addNoDataRow():(this.removeNoDataRow(),this.state.records.forEach(e=>{e=this.createTableRow(e);this.elements.tableBody.appendChild(e)}),this.refreshRowStyles())}createTableRow(a){let s=k.create("tr",{className:"ftable-data-row",attributes:{"data-record-key":this.getKeyValue(a)}});return s.recordData=a,this.options.selecting&&this.options.selectingCheckboxes&&!this._userPlacedActions.has("select")&&this.addSelectingCell(s),this.columnList.forEach(e=>{var t=this.options.fields[e];if(t.action)switch(t.action){case"select":this.addSelectingCell(s);break;case"update":this.addEditCell(s);break;case"clone":this.addCloneCell(s);break;case"delete":this.addDeleteCell(s)}else this.addDataCell(s,a,e)}),this.options.actions.updateAction&&!this._userPlacedActions.has("update")&&this.addEditCell(s),this.options.actions.cloneAction&&!this._userPlacedActions.has("clone")&&this.addCloneCell(s),this.options.actions.deleteAction&&!this._userPlacedActions.has("delete")&&this.addDeleteCell(s),this.options.selecting&&this.makeRowSelectable(s),s}addSelectingCell(t){var e=k.create("td",{className:"ftable-command-column ftable-selecting-column",parent:t});k.create("input",{className:"norowselectonclick",attributes:{type:"checkbox"},parent:e}).addEventListener("change",e=>{this.toggleRowSelection(t)})}addDataCell(e,t,a){var s=this.options.fields[a],i=this.tableOptionsCache?.get(a),t=this.getDisplayText(t,a,i),i=k.create("td",{className:`${s.listClass||""} `+(s.listClassEntry||""),innerHTML:s.listEscapeHTML?k.escapeHtml(t):t,attributes:{"data-field-name":a},parent:e});"fixed"===s.visibility||"hidden"!==s.visibility&&"separator"!==s.visibility||k.hide(i)}addEditCell(t){var e=k.create("td",{className:"ftable-command-column",parent:t});k.create("button",{className:"ftable-command-button ftable-edit-command-button",attributes:{title:this.options.messages.editRecord},innerHTML:`<span>${this.options.messages.editRecord}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.editRecord(t)})}addCloneCell(t){var e=k.create("td",{className:"ftable-command-column",parent:t});k.create("button",{className:"ftable-command-button ftable-clone-command-button",attributes:{title:this.options.messages.cloneRecord||"Clone"},innerHTML:`<span>${this.options.messages.cloneRecord||"Clone"}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.cloneRecord(t)})}addDeleteCell(t){var e=k.create("td",{className:"ftable-command-column",parent:t});k.create("button",{className:"ftable-command-button ftable-delete-command-button",attributes:{title:this.options.messages.deleteText},innerHTML:`<span>${this.options.messages.deleteText}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.deleteRecord(t)})}getDisplayText(e,t,a=null){var s=this.options.fields[t],i=e[t],a=a||s.options;let o=i;return"date"===s.type&&i&&(o="undefined"!=typeof FDatepicker?FDatepicker.formatDate(this._parseDate(i),s.dateFormat||this.options.defaultDateFormat):this.formatDate(i,s.dateLocale||this.options.defaultDateLocale)),"datetime-local"!==s.type&&"datetime"!==s.type||!i||(o="undefined"!=typeof FDatepicker?FDatepicker.formatDate(this._parseDate(i),s.dateFormat||this.options.defaultDateFormat):this.formatDateTime(i,s.dateLocale||this.options.defaultDateLocale)),"checkbox"===s.type&&(o=this.getCheckboxText(t,i)),a&&(t=this.findOptionByValue(a,i),o=t?t.DisplayText||t.text||t:i),(o=s.display&&"function"==typeof s.display?s.display({record:e,value:i,displayValue:o}):o)||""}_parseDate(e){return e.includes("Date")?new Date(parseInt(e.substr(6),10)):10==e.length?new Date(parseInt(e.substr(0,4),10),parseInt(e.substr(5,2),10)-1,parseInt(e.substr(8,2),10)):19==e.length?new Date(parseInt(e.substr(0,4),10),parseInt(e.substr(5,2),10)-1,parseInt(e.substr(8,2),10),parseInt(e.substr(11,2),10),parseInt(e.substr(14,2),10),parseInt(e.substr(17,2),10)):new Date(e)}formatDate(e,t){if(!e)return"";var a=this._parseDate(e);try{return isNaN(a.getTime())?e:a.toLocaleDateString(t,{year:"numeric",month:"2-digit",day:"2-digit"})}catch{return e}}formatDateTime(e,t){if(!e)return"";var a=this._parseDate(e);try{return isNaN(a.getTime())?e:a.toLocaleString(t)}catch{return e}}getCheckboxText(e,t){e=this.options.fields[e];return e.values&&e.values[t]?e.values[t]:t?this.options.messages.yes:this.options.messages.no}findOptionByValue(e,t){return Array.isArray(e)?e.find(e=>(e.Value||e.value)===t||e===t):"object"==typeof e&&null!==e&&e.hasOwnProperty(t)?e[t]:null}refreshRowStyles(){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach((e,t)=>{t%2==0?k.addClass(e,"ftable-row-even"):k.removeClass(e,"ftable-row-even")})}getKeyValue(e){return this.keyField?e[this.keyField]:null}async showAddRecordForm(){var e=await this.formBuilder.createForm("create");this.modals.addRecord.setContent(e),this.modals.addRecord.show(),this.currentForm=e,this.emit("formCreated",{form:e,formType:"create",record:null})}async saveNewRecord(){if(this.currentForm)if(this.currentForm.checkValidity()){var e=this.getFormData(this.currentForm);try{var t=await this.performCreate(e);"OK"===t.Result?(this.clearListCache(),this.modals.addRecord.close(),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null,this.emit("formClosed",{form:this.currentForm,formType:"create",record:null}),t.Message&&this.showInfo(t.Message),await this.load(),this.emit("recordAdded",{record:t.Record})):this.showError(t.Message||"Create failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Create failed: "+e.message)}}else this.currentForm.reportValidity()}async editRecord(e){var t=e.recordData,a=await this.formBuilder.createForm("edit",t);this.modals.editRecord.setContent(a),this.modals.editRecord.show(),this.currentForm=a,this.currentEditingRow=e,this.emit("formCreated",{form:a,formType:"edit",record:t})}async saveEditedRecord(){if(this.currentForm&&this.currentEditingRow)if(this.currentForm.checkValidity()){var e=this.getFormData(this.currentForm);try{var t=await this.performUpdate(e);"OK"===t.Result?(this.clearListCache(),this.modals.editRecord.close(),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null,this.emit("formClosed",{form:this.currentForm,formType:"edit",record:this.currentEditingRow.recordData}),this.updateRowData(this.currentEditingRow,t.Record||e),t.Message&&this.showInfo(t.Message),this.emit("recordUpdated",{record:t.Record||e,row:this.currentEditingRow})):this.showError(t.Message||"Update failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Update failed: "+e.message)}}else this.currentForm.reportValidity()}async cloneRecord(e){var e={...e.recordData},t=(this.keyField&&(e[this.keyField]=""),await this.formBuilder.createForm("create",e));this.modals.addRecord.options.content=t,this.modals.addRecord.setContent(t),this.modals.addRecord.show(),this.currentForm=t,this.emit("formCreated",{form:t,formType:"create",record:e})}async deleteRows(e){if(e.length){var t=this.options.messages.areYouSure;if(confirm(t)){var a,s=[];for(a of e)try{var i=await this.performDelete(a);s.push({key:a,success:"OK"===i.Result,result:i})}catch(e){s.push({key:a,success:!1,error:e.message})}s.filter(e=>e.success).forEach(({key:e})=>{e=this.getRowByKey(e);e&&this.removeRowFromTable(e)});t=s.filter(e=>!e.success).length;0<t&&this.showError(t+` of ${s.length} records could not be deleted`),this.refreshRowStyles(),this.updatePagingInfo()}}}deleteRecord(e){var t=e.recordData;let a=this.options.messages.deleteConfirmation;if("function"==typeof this.options.deleteConfirmation){t={row:e,record:t,deleteConfirmMessage:a,cancel:!1,cancelMessage:this.options.messages.cancel};if(this.options.deleteConfirmation(t),t.cancel)return void(t.cancelMessage&&this.showError(t.cancelMessage));a=t.deleteConfirmMessage}this.modals.deleteConfirm.setContent(`<p>${a}</p>`),this.modals.deleteConfirm.show(),this.currentDeletingRow=e}async confirmDelete(){if(this.currentDeletingRow){var e=this.getKeyValue(this.currentDeletingRow.recordData);try{var t=await this.performDelete(e);"OK"===t.Result?(this.clearListCache(),this.modals.deleteConfirm.close(),this.removeRowFromTable(this.currentDeletingRow),t.Message&&this.showInfo(t.Message),this.emit("recordDeleted",{record:this.currentDeletingRow.recordData})):this.showError(t.Message||"Delete failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Delete failed: "+e.message)}}}async performCreate(e){var t=this.options.actions.createAction;if("function"==typeof t)return t(e);if("string"==typeof t)return l.post(t,e);throw new Error("No valid createAction provided")}async performUpdate(e){var t=this.options.actions.updateAction;if("function"==typeof t)return t(e);if("string"==typeof t)return l.post(t,e);throw new Error("No valid updateAction provided")}async performDelete(e){var t=this.options.actions.deleteAction;let a;if(a=null===e||"object"!=typeof e||Array.isArray(e)?{[this.keyField]:e}:e,"function"==typeof t)return t(a);if("string"==typeof t)return l.post(t,a);throw new Error("No valid deleteAction provided")}getFormData(e){var t,a,s,i={};for([t,a]of new FormData(e).entries())t.endsWith("[]")?(i[s=t.slice(0,-2)]||(i[s]=[]),i[s].push(a)):i.hasOwnProperty(t)?Array.isArray(i[t])?i[t].push(a):i[t]=[i[t],a]:i[t]=a;return i}updateRowData(i,e){i.recordData={...i.recordData,...e},Object.keys(e).forEach(e=>{var t,a,s=this.options.fields[e];s&&(t=i.querySelector(`td[data-field-name="${e}"]`))&&(a=this.tableOptionsCache?.get(e),e=this.getDisplayText(i.recordData,e,a),t.innerHTML=s.listEscapeHTML?k.escapeHtml(e):e,t.className=(`${s.listClass||""} `+(s.listClassEntry||"")).trim())})}removeRowFromTable(e){e.remove(),0===this.elements.tableBody.querySelectorAll(".ftable-data-row").length&&this.addNoDataRow(),this.refreshRowStyles()}makeRowSelectable(t){!1!==this.options.selectOnRowClick&&t.addEventListener("click",e=>{["INPUT","BUTTON","SELECT","TEXTAREA","A"].includes(e.target.tagName)||e.target.classList.contains("norowselectonclick")||this.toggleRowSelection(t)})}toggleRowSelection(e){var t=e.classList.contains("ftable-row-selected");if(this.shiftKeyDown&&this.lastSelectedRow&&this.options.multiselect){this.clearAllSelections();var a=Array.from(this.elements.tableBody.querySelectorAll(".ftable-data-row")),s=a.indexOf(this.lastSelectedRow),i=a.indexOf(e),[i,o]=s<i?[s,i]:[i,s];for(let e=i;e<=o;e++)this.selectRow(a[e])}else this.options.multiselect||this.clearAllSelections(),t?this.deselectRow(e):this.selectRow(e);t&&!this.shiftKeyDown||(this.lastSelectedRow=e),this.emit("selectionChanged",{selectedRows:this.getSelectedRows()})}selectRow(e){k.addClass(e,"ftable-row-selected");var t=e.querySelector('input[type="checkbox"]'),t=(t&&(t.checked=!0),this.getKeyValue(e.recordData));t&&this.state.selectedRecords.add(t)}deselectRow(e){k.removeClass(e,"ftable-row-selected");var t=e.querySelector('input[type="checkbox"]'),t=(t&&(t.checked=!1),this.getKeyValue(e.recordData));t&&this.state.selectedRecords.delete(t)}recalcColumnWidths(){this.columnList.forEach(e=>{var t=this.options.fields[e],e=this.elements.table.querySelector(`[data-field-name="${e}"]`);e&&t.width&&(e.style.width=t.width)}),this.elements.table.offsetHeight}recalcColumnWidthsOnce(){this._recalculatedOnce||(this.recalcColumnWidths(),this._recalculatedOnce=!0)}clearAllSelections(){this.elements.tableBody.querySelectorAll(".ftable-row-selected").forEach(e=>this.deselectRow(e))}toggleSelectAll(t){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>{t?this.selectRow(e):this.deselectRow(e)}),this.emit("selectionChanged",{selectedRows:this.getSelectedRows()})}getSelectedRows(){return Array.from(this.elements.tableBody.querySelectorAll(".ftable-row-selected"))}sortByColumn(a,s=!1){var i=this.options.fields[a];if(i&&!1!==i.sorting){i=this.state.sorting.findIndex(e=>e.fieldName===a);let e=!0,t="ASC";0<=i?"ASC"===this.state.sorting[i].direction?(t="DESC",this.state.sorting[i].direction=t):(this.state.sorting.splice(i,1),e=!1):this.state.sorting.push({fieldName:a,direction:t}),(!this.options.multiSorting||this.options.multiSortingCtrlKey&&!s)&&(this.state.sorting=e?[{fieldName:a,direction:t}]:[]),this.updateSortingHeaders(),this.load(),this.saveState()}}updateSortingHeaders(){this.elements.table.querySelectorAll(".ftable-column-header-sortable").forEach(e=>{k.removeClass(e,"ftable-column-header-sorted-asc ftable-column-header-sorted-desc")}),this.state.sorting.forEach(e=>{var t=this.elements.table.querySelector(`[data-field-name="${e.fieldName}"]`);t&&k.addClass(t,"ftable-column-header-sorted-"+e.direction.toLowerCase())})}updatePagingInfo(){var e,t,a;this.options.paging&&this.elements.pageInfoSpan&&(this.state.totalRecordCount<=0?(this.elements.pageInfoSpan.textContent="",this.elements.pagingListArea.innerHTML=""):(e=(this.state.currentPage-1)*this.state.pageSize+1,t=Math.min(this.state.currentPage*this.state.pageSize,this.state.totalRecordCount),a=this.options.messages.pagingInfo||"Showing {0}-{1} of {2}",this.elements.pageInfoSpan.textContent=a.replace(/\{0\}/g,e).replace(/\{1\}/g,t).replace(/\{2\}/g,this.state.totalRecordCount),this.createPageListNavigation(),this.createPageGotoNavigation()))}createPageListNavigation(){if(this.elements.pagingListArea){this.elements.pagingListArea.innerHTML="";var e=Math.ceil(this.state.totalRecordCount/this.state.pageSize);if(!(e<=1)){if(this.createPageButton("«",1,1===this.state.currentPage,"ftable-page-number-first"),this.createPageButton("‹",this.state.currentPage-1,1===this.state.currentPage,"ftable-page-number-previous"),"normal"==this.options.pageList){var a=this.calculatePageNumbers(e);let t=0;a.forEach(e=>{1<e-t&&k.create("span",{className:"ftable-page-number-space",textContent:"...",parent:this.elements.pagingListArea}),this.createPageButton(e.toString(),e,!1,e===this.state.currentPage?"ftable-page-number ftable-page-number-active":"ftable-page-number"),t=e})}this.createPageButton("›",this.state.currentPage+1,this.state.currentPage>=e,"ftable-page-number-next"),this.createPageButton("»",e,this.state.currentPage>=e,"ftable-page-number-last")}}}createPageGotoNavigation(){if(this.options.paging&&"none"!==this.options.gotoPageArea){let a=Math.ceil(this.state.totalRecordCount/this.state.pageSize);if(a<=1)this.elements.pagingGotoArea.style.display="none",this.elements.pagingGotoArea.innerHTML="";else{this.elements.pagingGotoArea.style.display="inline-block",this.elements.pagingGotoArea.innerHTML="";k.create("span",{textContent:this.options.messages.gotoPageLabel+": ",parent:this.elements.pagingGotoArea});var e="ftable-goto-page-"+(this.options.tableId||"default");if("combobox"===this.options.gotoPageArea){this.elements.gotoPageSelect=k.create("select",{id:e,className:"ftable-page-goto-select",parent:this.elements.pagingGotoArea});for(let e=1;e<=a;e++)k.create("option",{attributes:{value:e},textContent:e,parent:this.elements.gotoPageSelect});this.elements.gotoPageSelect.value=this.state.currentPage,this.elements.gotoPageSelect.addEventListener("change",e=>{e=parseInt(e.target.value);1<=e&&e<=a&&this.changePage(e)})}else"textbox"===this.options.gotoPageArea&&(this.elements.gotoPageInput=k.create("input",{attributes:{type:"number",id:e,min:"1",max:a,value:this.state.currentPage,className:"ftable-page-goto-input",style:"width: 65px; margin-left: 4px;"},parent:this.elements.pagingGotoArea}),this.elements.gotoPageInput.addEventListener("change",e=>{var t=parseInt(e.target.value);1<=t&&t<=a?this.changePage(t):e.target.value=this.state.currentPage}))}}else this.elements.pagingGotoArea.style.display="none",this.elements.pagingGotoArea.innerHTML=""}createPageButton(e,t,a,s){s=k.create("span",{className:s+(a?" ftable-page-number-disabled":""),innerHTML:e,parent:this.elements.pagingListArea});a||(s.style.cursor="pointer",s.addEventListener("click",e=>{e.preventDefault(),this.changePage(t)}))}calculatePageNumbers(t){if(t<=7)return Array.from({length:t},(e,t)=>t+1);var a=this.state.currentPage,s=new Set([1,2,t-1,t]);for(let e=Math.max(1,a-1);e<=Math.min(t,a+1);e++)s.add(e);return Array.from(s).sort((e,t)=>e-t)}changePage(e){var t=Math.ceil(this.state.totalRecordCount/this.state.pageSize);(e=Math.max(1,Math.min(e,t)))!==this.state.currentPage&&(this.state.currentPage=e,this.load())}changePageSize(e){this.state.pageSize=e,this.state.currentPage=1,this.load(),this.saveState()}showLoadingIndicator(){0===this.options.loadingAnimationDelay?this.modals.loading&&this.modals.loading.show():this.loadingTimeout=setTimeout(()=>{this.modals.loading&&this.modals.loading.show(),this.loadingShownAt=Date.now()},this.options.loadingAnimationDelay||500)}hideLoadingIndicator(){this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null);var e=this.loadingShownAt?Date.now()-this.loadingShownAt:0;this.modals.loading&&(e<200?setTimeout(()=>{this.modals.loading.hide()},200-e):this.modals.loading.hide()),this.loadingShownAt=null}showError(e){this.modals.error?(this.modals.error.setContent(e),this.modals.error.show()):alert(e)}showWarning(e){this.modals.warning?(this.modals.warning.setContent(e),this.modals.warning.show()):alert(e)}showInfo(e){this.modals.info?(this.modals.info.setContent(e),this.modals.info.show()):alert(e)}reload(e=!1){return this.clearListCache(),e?this.preservedSelections=new Set(this.state.selectedRecords):this.state.selectedRecords.clear(),this.load().then(()=>(e&&this.preservedSelections&&(this.restoreSelections(),this.preservedSelections=null),this))}clearListCache(){this.options.actions.listAction&&"string"==typeof this.options.actions.listAction&&this.formBuilder.optionsCache.clear(this.options.actions.listAction)}restoreSelections(){if(this.preservedSelections)return this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>{var t=this.getKeyValue(e.recordData);t&&this.preservedSelections.has(t)&&this.selectRow(e)}),this}getRowByKey(e){return this.elements.tableBody.querySelector(`[data-record-key="${e}"]`)}destroy(){this.element&&this.element.ftableInstance&&(this.element.ftableInstance=null),Object.values(this.modals).forEach(e=>e.destroy()),this.elements.mainContainer&&this.elements.mainContainer.remove(),this.searchTimeout&&clearTimeout(this.searchTimeout),this.loadingTimeout&&clearTimeout(this.loadingTimeout),window.removeEventListener("resize",this.handleResize),this.options=null,this.state=null,this.elements=null,this.formBuilder=null,this.modals=null}setOption(e,t){return this.options[e]=t,this}getState(){return{...this.state}}addFilter(t,e,a="equals"){return this.state.filters||(this.state.filters=[]),this.state.filters=this.state.filters.filter(e=>e.fieldName!==t),null!=e&&""!==e&&this.state.filters.push({fieldName:t,value:e,operator:a}),this}clearFilters(){return this.state.filters=[],this}exportToCSV(e="table-data.csv"){var t,a,s,i=this.elements.table.cloneNode(!0),o=[],r=e=>`"${String(e||"").replace(/"/g,'""')}"`,n=[];for(t of i.querySelectorAll("thead th"))t.classList.contains("ftable-command-column-header")||t.classList.contains("ftable-toolbarsearch-column-header")||"none"===t.style.display||(a=t.textContent.trim(),n.push(r(a)));o.push(n.join(","));for(s of i.querySelectorAll("tbody tr"))if("none"!==s.style.display){var l,c,d,h=[];let e=!1;for(l of s.querySelectorAll("td"))l.classList.contains("ftable-command-column")||"none"===l.style.display||(l.querySelector("img, button, input, select")&&(l.innerHTML=l.textContent),c=l.innerHTML.replace(/<br\s*\/?>/gi,"\n"),(d=document.createElement("div")).innerHTML=c,h.push(r(d.textContent||"")),e=!0);e&&o.push(h.join(","))}var i=o.join("\n"),i=new Blob(["\ufeff"+i],{type:"text/csv;charset=utf-8;"}),p=document.createElement("a");p.href=URL.createObjectURL(i),p.download=e,p.click(),p.remove()}printTable(){var e=window.open("","_blank","width=800,height=600"),t=this.elements.table.outerHTML,t=`
|
|
2
2
|
<!DOCTYPE html>
|
|
3
3
|
<html>
|
|
4
4
|
<head>
|
|
@@ -32,4 +32,4 @@
|
|
|
32
32
|
</script>
|
|
33
33
|
</body>
|
|
34
34
|
</html>
|
|
35
|
-
`;e.document.write(t),e.document.close()}async bulkDelete(e="Delete selected records?"){var t=this.getSelectedRows();if(0===t.length)this.showError("No records selected");else if(confirm(e)){var s
|
|
35
|
+
`;e.document.write(t),e.document.close()}async bulkDelete(e="Delete selected records?"){var t=this.getSelectedRows();if(0===t.length)this.showError("No records selected");else if(confirm(e)){var a,s=[];for(a of t.map(e=>this.getKeyValue(e.recordData)))try{var i=await this.performDelete(a);s.push({key:a,success:"OK"===i.Result,result:i})}catch(e){s.push({key:a,success:!1,error:e.message})}s.filter(e=>e.success).forEach(({key:e})=>{e=this.getRowByKey(e);e&&this.removeRowFromTable(e)});e=s.filter(e=>!e.success).length;0<e&&this.showError(e+` of ${s.length} records could not be deleted`)}}showColumn(e){this.setColumnVisibility(e,!0)}hideColumn(e){this.setColumnVisibility(e,!1)}setColumnVisibility(t,a){var s=this.options.fields[t];if(s)if(!a&&this.isFieldSorted(t))this.showError(`Cannot hide column "${s.title||t}" because it is currently sorted`);else if("fixed"!==s.visibility){s.visibility=a?"visible":"hidden";s=this.columnList.indexOf(t);if(0<=s){let e=s+1;this.options.selecting&&this.options.selectingCheckboxes&&(e+=1);t=`th:nth-child(${e}), td:nth-child(${e})`;this.elements.table.querySelectorAll(t).forEach(e=>{a?k.show(e):k.hide(e)})}this.options.saveUserPreferences&&(this.saveColumnSettings(),this.saveState())}}editRecordByKey(e){var t=this.getRowByKey(e);t?this.editRecord(t):this.showError(`Record with key '${e}' not found`)}async editRecordViaAjax(e,t,a={}){try{var s=this.keyField;if(!s)throw new Error("No key field defined in fTable options");var i={[s]:e,...a},o=this.options.forcePost?await l.post(t,i):await l.get(t,i);if(!o||!o.Record)throw new Error("Invalid response or missing Record");var r=o.Record,n=this.getRowByKey(e)||k.create("tr",{className:"ftable-data-row",attributes:{"data-record-key":e}});n.recordData={...r},await this.editRecord(n)}catch(e){this.showError("Failed to load record for editing."),this.logger.error("editRecordViaAjax failed: "+e.message)}}openChildTable(e,t,a){this.options.openChildAsAccordion&&this.closeAllChildTables(),this.closeChildTable(e);var s=k.create("tr",{className:"ftable-child-row"}),i=k.create("td",{attributes:{colspan:this.getVisibleColumnCount()},parent:s}),i=k.create("div",{className:"ftable-child-table-container",parent:i}),s=(e.parentNode.insertBefore(s,e.nextSibling),(e.childRow=s).parentRow=e,new n(i,{...t,paging:void 0===t.paging||t.paging,pageSize:t.pageSize||10,sorting:void 0===t.sorting||t.sorting,selecting:!1,toolbarsearch:!0,messages:{...this.options.messages,...t.messages}}));s.close;return s.close=()=>{this.closeChildTable(e)},s.init(),a&&a(s),e.childTable=s}closeChildTable(e){e.childRow&&(e.childTable&&"function"==typeof e.childTable.destroy&&e.childTable.destroy(),e.childRow.remove(),e.childRow=null,e.childTable=null)}closeAllChildTables(){Object.values(this.elements.tableRows).forEach(e=>{e.childTable&&this.closeChildTable(e)})}getSortingInfo(){let t=this.options.messages||{};return this.state.sorting.map(e=>(this.options.fields[e.fieldName]?.title||e.fieldName)+` (${"ASC"===e.direction?t.ascending||"ascending":t.descending||"descending"})`).join(", ")}renderSortingInfo(){if(this.options.sortingInfoSelector&&this.options.sorting){var e=document.querySelector(this.options.sortingInfoSelector);if(e){e.innerHTML="";let t=this.options.messages||{};var a,s=t.sortingInfoPrefix?`<span class="ftable-sorting-prefix">${t.sortingInfoPrefix}</span> `:"",i=t.sortingInfoSuffix?` <span class="ftable-sorting-suffix">${t.sortingInfoSuffix}</span>`:"";0===this.state.sorting.length?e.innerHTML=t.sortingInfoNone||"":(a=this.getSortingInfo(),e.innerHTML=s+a+i,0<this.state.sorting.length&&((s=document.createElement("button")).textContent=t.resetSorting||"Reset Sorting",s.style.marginLeft="10px",s.classList.add("ftable-sorting-reset-btn"),s.addEventListener("click",e=>{e.preventDefault(),this.state.sorting=[],this.updateSortingHeaders(),this.load(),this.saveState()}),e.appendChild(s)),this.options.tableReset&&((a=document.createElement("button")).textContent=t.resetTable||"Reset Table",a.style.marginLeft="10px",a.classList.add("ftable-table-reset-btn"),a.addEventListener("click",e=>{e.preventDefault();e=t.resetTableConfirm;confirm(e)&&(this.userPrefs.remove("column-settings"),this.userPrefs.remove("table-state"),this.state.sorting=[],this.state.pageSize=this.options.pageSize,this.columnList.forEach(e=>{e=this.options.fields[e];e.visibility="fixed"===e.visibility?"fixed":"visible"}),location.reload())}),e.appendChild(a)))}}}static _waitForFieldReady(a,e,s,i=5e3){if(e){let t=e.querySelector(`[name="${a}"]`);if(t&&"SELECT"===t.tagName)if(1<t.options.length)s();else{let e=new MutationObserver(()=>{1<t.options.length&&(e.disconnect(),s())});e.observe(t,{childList:!0}),0<i&&setTimeout(()=>{e.disconnect(),1<t.options.length?s():console.warn(`FTable: Timeout waiting for field '${a}' to load options`)},i)}else console.warn(`FTable: Field '${a}' not found or not a <select>`)}else console.warn(`FTable: No form provided for waitForFieldReady('${a}')`)}static async waitForFieldReady(t,a,s=5e3){return new Promise(e=>{n._waitForFieldReady(t,a,e,s)})}}e.FTable=n,e.FtableModal=o,e.FTableHttpClient=l,"undefined"!=typeof module&&module.exports&&(module.exports=n,module.exports.FtableModal=o,module.exports.FTableHttpClient=l)})("undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:this);
|
package/ftable.umd.js
CHANGED
|
@@ -623,112 +623,46 @@ class FTableFormBuilder {
|
|
|
623
623
|
this.options = options;
|
|
624
624
|
this.dependencies = new Map(); // Track field dependencies
|
|
625
625
|
this.optionsCache = new FTableOptionsCache();
|
|
626
|
-
this.originalFieldOptions = new Map(); // Store original field.options
|
|
627
|
-
this.resolvedFieldOptions = new Map(); // Store resolved options per context
|
|
628
|
-
|
|
629
|
-
// Initialize with empty cache objects
|
|
630
|
-
Object.keys(this.options.fields || {}).forEach(fieldName => {
|
|
631
|
-
this.resolvedFieldOptions.set(fieldName, {});
|
|
632
|
-
});
|
|
633
|
-
Object.entries(this.options.fields).forEach(([fieldName, field]) => {
|
|
634
|
-
this.originalFieldOptions.set(fieldName, field.options);
|
|
635
|
-
});
|
|
636
626
|
}
|
|
637
627
|
|
|
638
|
-
// Get options for
|
|
628
|
+
// Get options for a field, respecting context ('search' prefers searchOptions over options).
|
|
629
|
+
// URL-level caching and concurrent-request deduplication is handled by FTableOptionsCache
|
|
630
|
+
// inside resolveOptions
|
|
639
631
|
async getFieldOptions(fieldName, context = 'table', params = {}) {
|
|
640
632
|
const field = this.options.fields[fieldName];
|
|
641
|
-
const originalOptions = this.originalFieldOptions.get(fieldName);
|
|
642
|
-
|
|
643
|
-
// If no options or already resolved for this context with same params, return cached
|
|
644
|
-
if (!originalOptions) {
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
633
|
|
|
648
|
-
//
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
634
|
+
// For search context, prefer searchOptions and fall back to options
|
|
635
|
+
const optionsSource = (context === 'search')
|
|
636
|
+
? (field.searchOptions ?? field.options)
|
|
637
|
+
: field.options;
|
|
638
|
+
|
|
639
|
+
if (!optionsSource) return null;
|
|
640
|
+
|
|
641
|
+
const noCache = this.shouldSkipCache(field, context, params);
|
|
656
642
|
|
|
657
643
|
try {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
// we store the resolved option always
|
|
665
|
-
this.resolvedFieldOptions.get(fieldName)[cacheKey] = resolved;
|
|
666
|
-
return resolved;
|
|
644
|
+
return await this.resolveOptions(
|
|
645
|
+
{ ...field, options: optionsSource },
|
|
646
|
+
params,
|
|
647
|
+
context,
|
|
648
|
+
noCache
|
|
649
|
+
);
|
|
667
650
|
} catch (err) {
|
|
668
651
|
console.error(`Failed to resolve options for ${fieldName} (${context}):`, err);
|
|
669
|
-
return
|
|
652
|
+
return optionsSource;
|
|
670
653
|
}
|
|
671
654
|
}
|
|
672
655
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
* @param {string|null} context - Context to clear ('table', 'create', 'edit'), or null for all contexts
|
|
677
|
-
*/
|
|
678
|
-
clearResolvedOptions(fieldName = null, context = null) {
|
|
679
|
-
if (fieldName) {
|
|
680
|
-
// Clear specific field
|
|
681
|
-
if (this.resolvedFieldOptions.has(fieldName)) {
|
|
682
|
-
if (context) {
|
|
683
|
-
// Clear specific context for specific field
|
|
684
|
-
this.resolvedFieldOptions.get(fieldName)[context] = null;
|
|
685
|
-
} else {
|
|
686
|
-
// Clear all contexts for specific field
|
|
687
|
-
this.resolvedFieldOptions.set(fieldName, { table: null, create: null, edit: null });
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
} else {
|
|
691
|
-
// Clear all fields
|
|
692
|
-
if (context) {
|
|
693
|
-
// Clear specific context for all fields
|
|
694
|
-
this.resolvedFieldOptions.forEach((value, key) => {
|
|
695
|
-
this.resolvedFieldOptions.get(key)[context] = null;
|
|
696
|
-
});
|
|
697
|
-
} else {
|
|
698
|
-
// Clear everything
|
|
699
|
-
this.resolvedFieldOptions.forEach((value, key) => {
|
|
700
|
-
this.resolvedFieldOptions.set(key, { table: null, create: null, edit: null });
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// Helper method to determine caching behavior
|
|
707
|
-
shouldForceRefreshForContext(field, context, params) {
|
|
708
|
-
// Rename to reflect what it actually does now
|
|
656
|
+
// Determine whether to bypass the URL cache for this field/context
|
|
657
|
+
shouldSkipCache(field, context, params) {
|
|
658
|
+
if (params.forceRefresh) return true;
|
|
709
659
|
if (!field.noCache) return false;
|
|
710
|
-
|
|
711
660
|
if (typeof field.noCache === 'boolean') return field.noCache;
|
|
712
661
|
if (typeof field.noCache === 'function') return field.noCache({ context, ...params });
|
|
713
662
|
if (typeof field.noCache === 'object') return field.noCache[context] === true;
|
|
714
|
-
|
|
715
663
|
return false;
|
|
716
664
|
}
|
|
717
665
|
|
|
718
|
-
generateOptionsCacheKey(context, params) {
|
|
719
|
-
// Create a unique key based on context and dependency values
|
|
720
|
-
const keyParts = [context];
|
|
721
|
-
|
|
722
|
-
if (params.dependedValues) {
|
|
723
|
-
// Include relevant dependency values in the cache key
|
|
724
|
-
Object.keys(params.dependedValues).sort().forEach(key => {
|
|
725
|
-
keyParts.push(`${key}=${params.dependedValues[key]}`);
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
return keyParts.join('|');
|
|
730
|
-
}
|
|
731
|
-
|
|
732
666
|
shouldIncludeField(field, formType) {
|
|
733
667
|
if (formType === 'create') {
|
|
734
668
|
return field.create !== false && !(field.key === true && field.create !== true);
|
|
@@ -801,12 +735,6 @@ class FTableFormBuilder {
|
|
|
801
735
|
return form;
|
|
802
736
|
}
|
|
803
737
|
|
|
804
|
-
shouldResolveOptions(options) {
|
|
805
|
-
return options &&
|
|
806
|
-
(typeof options === 'function' || typeof options === 'string') &&
|
|
807
|
-
!Array.isArray(options) &&
|
|
808
|
-
!(typeof options === 'object' && !Array.isArray(options) && Object.keys(options).length > 0);
|
|
809
|
-
}
|
|
810
738
|
|
|
811
739
|
buildDependencyMap() {
|
|
812
740
|
this.dependencies.clear();
|
|
@@ -2165,13 +2093,13 @@ class FTable extends FTableEventEmitter {
|
|
|
2165
2093
|
}
|
|
2166
2094
|
|
|
2167
2095
|
// Start resolving in background
|
|
2168
|
-
this.
|
|
2096
|
+
this.resolveAllFieldOptionsForTable().then(() => {
|
|
2169
2097
|
// re-render dynamic options rows — no server call
|
|
2170
2098
|
// this is needed so that once options are resolved, the table shows correct display values
|
|
2171
2099
|
// why: load() can actually finish faster than option resolving (and calling refreshDisplayValues
|
|
2172
2100
|
// there is then pointless, since the resolving hasn't finished yet),
|
|
2173
2101
|
// so we need to do it when the options are actually resolved (here)
|
|
2174
|
-
// We could call await this.
|
|
2102
|
+
// We could call await this.resolveAllFieldOptionsForTable() during load, but that would slow down the loading ...
|
|
2175
2103
|
setTimeout(() => {
|
|
2176
2104
|
this.refreshDisplayValues();
|
|
2177
2105
|
}, 0);
|
|
@@ -2392,22 +2320,17 @@ class FTable extends FTableEventEmitter {
|
|
|
2392
2320
|
}
|
|
2393
2321
|
}
|
|
2394
2322
|
|
|
2395
|
-
async
|
|
2323
|
+
async resolveAllFieldOptionsForTable() {
|
|
2324
|
+
this.tableOptionsCache = new Map();
|
|
2325
|
+
|
|
2396
2326
|
const promises = this.columnList.map(async (fieldName) => {
|
|
2397
2327
|
const field = this.options.fields[fieldName];
|
|
2398
2328
|
if (field.action) return; // Skip action columns
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
const cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
|
|
2405
|
-
if (!this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey]) {
|
|
2406
|
-
await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
2407
|
-
}
|
|
2408
|
-
} catch (err) {
|
|
2409
|
-
console.error(`Failed to resolve table options for ${fieldName}:`, err);
|
|
2410
|
-
}
|
|
2329
|
+
try {
|
|
2330
|
+
const resolved = await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
2331
|
+
if (resolved) this.tableOptionsCache.set(fieldName, resolved);
|
|
2332
|
+
} catch (err) {
|
|
2333
|
+
console.error(`Failed to resolve table options for ${fieldName}:`, err);
|
|
2411
2334
|
}
|
|
2412
2335
|
});
|
|
2413
2336
|
|
|
@@ -2426,9 +2349,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2426
2349
|
const cell = row.querySelector(`td[data-field-name="${fieldName}"]`);
|
|
2427
2350
|
if (!cell) continue;
|
|
2428
2351
|
|
|
2429
|
-
|
|
2430
|
-
const cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
|
|
2431
|
-
const resolvedOptions = this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey];
|
|
2352
|
+
const resolvedOptions = this.tableOptionsCache?.get(fieldName);
|
|
2432
2353
|
const value = this.getDisplayText(row.recordData, fieldName, resolvedOptions);
|
|
2433
2354
|
cell.innerHTML = field.listEscapeHTML ? FTableDOMHelper.escapeHtml(value) : value;
|
|
2434
2355
|
}
|
|
@@ -2873,8 +2794,8 @@ class FTable extends FTableEventEmitter {
|
|
|
2873
2794
|
Value: value,
|
|
2874
2795
|
DisplayText: displayText
|
|
2875
2796
|
}));
|
|
2876
|
-
} else if (field.options) {
|
|
2877
|
-
optionsSource = await this.formBuilder.getFieldOptions(fieldName);
|
|
2797
|
+
} else if (field.options || field.searchOptions) {
|
|
2798
|
+
optionsSource = await this.formBuilder.getFieldOptions(fieldName, 'search');
|
|
2878
2799
|
}
|
|
2879
2800
|
|
|
2880
2801
|
// If multiple, create custom UI
|
|
@@ -2977,14 +2898,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2977
2898
|
input.datalistElement = datalist;
|
|
2978
2899
|
|
|
2979
2900
|
// Load options for the datalist
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
// Use search-specific options if available
|
|
2983
|
-
if (field.searchOptions) {
|
|
2984
|
-
optionsSource = field.searchOptions;
|
|
2985
|
-
} else if (field.options) {
|
|
2986
|
-
optionsSource = await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
2987
|
-
}
|
|
2901
|
+
const optionsSource = await this.formBuilder.getFieldOptions(fieldName, 'search');
|
|
2988
2902
|
|
|
2989
2903
|
// Populate datalist with options
|
|
2990
2904
|
if (optionsSource) {
|
|
@@ -4021,8 +3935,7 @@ class FTable extends FTableEventEmitter {
|
|
|
4021
3935
|
|
|
4022
3936
|
addDataCell(row, record, fieldName) {
|
|
4023
3937
|
const field = this.options.fields[fieldName];
|
|
4024
|
-
const
|
|
4025
|
-
const resolvedOptions = this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey];
|
|
3938
|
+
const resolvedOptions = this.tableOptionsCache?.get(fieldName);
|
|
4026
3939
|
const value = this.getDisplayText(record, fieldName, resolvedOptions);
|
|
4027
3940
|
|
|
4028
3941
|
const cell = FTableDOMHelper.create('td', {
|
|
@@ -4525,8 +4438,7 @@ class FTable extends FTableEventEmitter {
|
|
|
4525
4438
|
if (!cell) return;
|
|
4526
4439
|
|
|
4527
4440
|
// Get display text
|
|
4528
|
-
const
|
|
4529
|
-
const resolvedOptions = this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey];
|
|
4441
|
+
const resolvedOptions = this.tableOptionsCache?.get(fieldName);
|
|
4530
4442
|
const value = this.getDisplayText(row.recordData, fieldName, resolvedOptions);
|
|
4531
4443
|
cell.innerHTML = field.listEscapeHTML ? FTableDOMHelper.escapeHtml(value) : value;
|
|
4532
4444
|
cell.className = `${field.listClass || ''} ${field.listClassEntry || ''}`.trim();
|