@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 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 specific context
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
- // Determine if we should skip caching for this specific context
648
- const shouldSkipCache = this.shouldForceRefreshForContext(field, context, params);
649
- const cacheKey = this.generateOptionsCacheKey(context, params);
650
- // Skip cache if configured or forceRefresh requested
651
- if (!shouldSkipCache && !params.forceRefresh) {
652
- const cached = this.resolvedFieldOptions.get(fieldName)[cacheKey];
653
- if (cached) return cached;
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
- // Create temp field with original options for resolution
658
- const tempField = { ...field, options: originalOptions };
659
- const resolved = await this.resolveOptions(tempField, {
660
- ...params
661
- }, context, shouldSkipCache);
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 originalOptions;
651
+ return optionsSource;
669
652
  }
670
653
  }
671
654
 
672
- /**
673
- * Clear resolved options for specific field or all fields
674
- * @param {string|null} fieldName - Field name to clear, or null for all fields
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.resolveAsyncFieldOptions().then(() => {
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.resolveAsyncFieldOptions() during load, but that would slow down the loading ...
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 resolveAsyncFieldOptions() {
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
- const originalOptions = this.formBuilder.originalFieldOptions.get(fieldName);
2399
-
2400
- if (this.formBuilder.shouldResolveOptions(originalOptions)) {
2401
- try {
2402
- // Check if already resolved to avoid duplicate work
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
- // Get table-specific options
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
- let optionsSource;
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 cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
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 cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
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 specific context
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
- // Determine if we should skip caching for this specific context
649
- const shouldSkipCache = this.shouldForceRefreshForContext(field, context, params);
650
- const cacheKey = this.generateOptionsCacheKey(context, params);
651
- // Skip cache if configured or forceRefresh requested
652
- if (!shouldSkipCache && !params.forceRefresh) {
653
- const cached = this.resolvedFieldOptions.get(fieldName)[cacheKey];
654
- if (cached) return cached;
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
- // Create temp field with original options for resolution
659
- const tempField = { ...field, options: originalOptions };
660
- const resolved = await this.resolveOptions(tempField, {
661
- ...params
662
- }, context, shouldSkipCache);
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 originalOptions;
652
+ return optionsSource;
670
653
  }
671
654
  }
672
655
 
673
- /**
674
- * Clear resolved options for specific field or all fields
675
- * @param {string|null} fieldName - Field name to clear, or null for all fields
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.resolveAsyncFieldOptions().then(() => {
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.resolveAsyncFieldOptions() during load, but that would slow down the loading ...
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 resolveAsyncFieldOptions() {
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
- const originalOptions = this.formBuilder.originalFieldOptions.get(fieldName);
2400
-
2401
- if (this.formBuilder.shouldResolveOptions(originalOptions)) {
2402
- try {
2403
- // Check if already resolved to avoid duplicate work
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
- // Get table-specific options
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
- let optionsSource;
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 cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
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 cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
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={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"};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:"&times;",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:"&nbsp;",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("&laquo;",1,1===this.state.currentPage,"ftable-page-number-first"),this.createPageButton("&lsaquo;",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("&rsaquo;",this.state.currentPage+1,this.state.currentPage>=e,"ftable-page-number-next"),this.createPageButton("&raquo;",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={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"};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:"&times;",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:"&nbsp;",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("&laquo;",1,1===this.state.currentPage,"ftable-page-number-first"),this.createPageButton("&lsaquo;",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("&rsaquo;",this.state.currentPage+1,this.state.currentPage>=e,"ftable-page-number-next"),this.createPageButton("&raquo;",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,a=[];for(s of t.map(e=>this.getKeyValue(e.recordData)))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)});e=a.filter(e=>!e.success).length;0<e&&this.showError(e+` of ${a.length} records could not be deleted`)}}showColumn(e){this.setColumnVisibility(e,!0)}hideColumn(e){this.setColumnVisibility(e,!1)}setColumnVisibility(t,s){var a=this.options.fields[t];if(a)if(!s&&this.isFieldSorted(t))this.showError(`Cannot hide column "${a.title||t}" because it is currently sorted`);else if("fixed"!==a.visibility){a.visibility=s?"visible":"hidden";a=this.columnList.indexOf(t);if(0<=a){let e=a+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=>{s?O.show(e):O.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,s={}){try{var a=this.keyField;if(!a)throw new Error("No key field defined in fTable options");var i={[a]:e,...s},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)||O.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,s){this.options.openChildAsAccordion&&this.closeAllChildTables(),this.closeChildTable(e);var a=O.create("tr",{className:"ftable-child-row"}),i=O.create("td",{attributes:{colspan:this.getVisibleColumnCount()},parent:a}),i=O.create("div",{className:"ftable-child-table-container",parent:i}),a=(e.parentNode.insertBefore(a,e.nextSibling),(e.childRow=a).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}}));a.close;return a.close=()=>{this.closeChildTable(e)},a.init(),s&&s(a),e.childTable=a}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 s,a=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||"":(s=this.getSortingInfo(),e.innerHTML=a+s+i,0<this.state.sorting.length&&((a=document.createElement("button")).textContent=t.resetSorting||"Reset Sorting",a.style.marginLeft="10px",a.classList.add("ftable-sorting-reset-btn"),a.addEventListener("click",e=>{e.preventDefault(),this.state.sorting=[],this.updateSortingHeaders(),this.load(),this.saveState()}),e.appendChild(a)),this.options.tableReset&&((s=document.createElement("button")).textContent=t.resetTable||"Reset Table",s.style.marginLeft="10px",s.classList.add("ftable-table-reset-btn"),s.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(s)))}}}static _waitForFieldReady(s,e,a,i=5e3){if(e){let t=e.querySelector(`[name="${s}"]`);if(t&&"SELECT"===t.tagName)if(1<t.options.length)a();else{let e=new MutationObserver(()=>{1<t.options.length&&(e.disconnect(),a())});e.observe(t,{childList:!0}),0<i&&setTimeout(()=>{e.disconnect(),1<t.options.length?a():console.warn(`FTable: Timeout waiting for field '${s}' to load options`)},i)}else console.warn(`FTable: Field '${s}' not found or not a <select>`)}else console.warn(`FTable: No form provided for waitForFieldReady('${s}')`)}static async waitForFieldReady(t,s,a=5e3){return new Promise(e=>{n._waitForFieldReady(t,s,e,a)})}}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);
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 specific context
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
- // Determine if we should skip caching for this specific context
649
- const shouldSkipCache = this.shouldForceRefreshForContext(field, context, params);
650
- const cacheKey = this.generateOptionsCacheKey(context, params);
651
- // Skip cache if configured or forceRefresh requested
652
- if (!shouldSkipCache && !params.forceRefresh) {
653
- const cached = this.resolvedFieldOptions.get(fieldName)[cacheKey];
654
- if (cached) return cached;
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
- // Create temp field with original options for resolution
659
- const tempField = { ...field, options: originalOptions };
660
- const resolved = await this.resolveOptions(tempField, {
661
- ...params
662
- }, context, shouldSkipCache);
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 originalOptions;
652
+ return optionsSource;
670
653
  }
671
654
  }
672
655
 
673
- /**
674
- * Clear resolved options for specific field or all fields
675
- * @param {string|null} fieldName - Field name to clear, or null for all fields
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.resolveAsyncFieldOptions().then(() => {
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.resolveAsyncFieldOptions() during load, but that would slow down the loading ...
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 resolveAsyncFieldOptions() {
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
- const originalOptions = this.formBuilder.originalFieldOptions.get(fieldName);
2400
-
2401
- if (this.formBuilder.shouldResolveOptions(originalOptions)) {
2402
- try {
2403
- // Check if already resolved to avoid duplicate work
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
- // Get table-specific options
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
- let optionsSource;
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 cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
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 cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liedekef/ftable",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "Modern, lightweight, jQuery-free CRUD table for dynamic AJAX-powered tables.",
5
5
  "main": "ftable.js",
6
6
  "module": "ftable.esm.js",