@liedekef/ftable 1.1.6 → 1.1.7

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.
Files changed (39) hide show
  1. package/ftable.esm.js +41 -159
  2. package/ftable.js +42 -160
  3. package/ftable.min.js +3 -3
  4. package/ftable.umd.js +42 -160
  5. package/package.json +1 -1
  6. package/themes/basic/ftable_basic.css +25 -25
  7. package/themes/basic/ftable_basic.min.css +1 -1
  8. package/themes/ftable_theme_base.less +35 -35
  9. package/themes/lightcolor/blue/ftable.css +33 -33
  10. package/themes/lightcolor/blue/ftable.min.css +1 -1
  11. package/themes/lightcolor/ftable_lightcolor_base.less +6 -5
  12. package/themes/lightcolor/gray/ftable.css +33 -33
  13. package/themes/lightcolor/gray/ftable.min.css +1 -1
  14. package/themes/lightcolor/green/ftable.css +33 -33
  15. package/themes/lightcolor/green/ftable.min.css +1 -1
  16. package/themes/lightcolor/orange/ftable.css +33 -33
  17. package/themes/lightcolor/orange/ftable.min.css +1 -1
  18. package/themes/lightcolor/red/ftable.css +33 -33
  19. package/themes/lightcolor/red/ftable.min.css +1 -1
  20. package/themes/metro/blue/ftable.css +25 -25
  21. package/themes/metro/blue/ftable.min.css +1 -1
  22. package/themes/metro/brown/ftable.css +25 -25
  23. package/themes/metro/brown/ftable.min.css +1 -1
  24. package/themes/metro/crimson/ftable.css +25 -25
  25. package/themes/metro/crimson/ftable.min.css +1 -1
  26. package/themes/metro/darkgray/ftable.css +25 -25
  27. package/themes/metro/darkgray/ftable.min.css +1 -1
  28. package/themes/metro/darkorange/ftable.css +25 -25
  29. package/themes/metro/darkorange/ftable.min.css +1 -1
  30. package/themes/metro/green/ftable.css +25 -25
  31. package/themes/metro/green/ftable.min.css +1 -1
  32. package/themes/metro/lightgray/ftable.css +25 -25
  33. package/themes/metro/lightgray/ftable.min.css +1 -1
  34. package/themes/metro/pink/ftable.css +25 -25
  35. package/themes/metro/pink/ftable.min.css +1 -1
  36. package/themes/metro/purple/ftable.css +25 -25
  37. package/themes/metro/purple/ftable.min.css +1 -1
  38. package/themes/metro/red/ftable.css +25 -25
  39. package/themes/metro/red/ftable.min.css +1 -1
package/ftable.esm.js CHANGED
@@ -1,6 +1,4 @@
1
1
 
2
- // Modern fTable - Vanilla JS Refactor
3
-
4
2
  const FTABLE_DEFAULT_MESSAGES = {
5
3
  serverCommunicationError: 'An error occurred while communicating to the server.',
6
4
  loadingMessage: 'Loading records...',
@@ -748,7 +746,9 @@ class FTableFormBuilder {
748
746
  }
749
747
 
750
748
  try {
751
- const response = await FTableHttpClient.get(url);
749
+ const response = this.options.forcePost
750
+ ? await FTableHttpClient.post(url)
751
+ : await FTableHttpClient.get(url);
752
752
  const options = response.Options || response.options || response || [];
753
753
 
754
754
  // Only cache if noCache is false
@@ -1354,10 +1354,14 @@ class FTable extends FTableEventEmitter {
1354
1354
  this.element = typeof element === 'string' ?
1355
1355
  document.querySelector(element) : element;
1356
1356
 
1357
+ if (!this.element) {
1358
+ return;
1359
+ }
1360
+
1357
1361
  // Prevent double initialization
1358
- if (element.ftableInstance) {
1362
+ if (this.element.ftableInstance) {
1359
1363
  //console.warn('FTable is already initialized on this element. Using that.');
1360
- return element.ftableInstance;
1364
+ return this.element.ftableInstance;
1361
1365
  }
1362
1366
 
1363
1367
  this.options = this.mergeOptions(options);
@@ -1394,6 +1398,7 @@ class FTable extends FTableEventEmitter {
1394
1398
  logLevel: FTableLogger.LOG_LEVELS.WARN,
1395
1399
  actions: {},
1396
1400
  fields: {},
1401
+ forcePost: true,
1397
1402
  animationsEnabled: true,
1398
1403
  loadingAnimationDelay: 1000,
1399
1404
  defaultDateLocale: '',
@@ -2520,13 +2525,15 @@ class FTable extends FTableEventEmitter {
2520
2525
  // Create overlay to capture clicks outside menu
2521
2526
  this.elements.columnSelectionOverlay = FTableDOMHelper.create('div', {
2522
2527
  className: 'ftable-contextmenu-overlay',
2523
- parent: this.elements.mainContainer
2528
+ //parent: this.elements.mainContainer
2529
+ parent: document.body
2524
2530
  });
2525
2531
 
2526
2532
  // Create the menu
2527
2533
  this.elements.columnSelectionMenu = FTableDOMHelper.create('div', {
2528
2534
  className: 'ftable-column-selection-container',
2529
- parent: this.elements.columnSelectionOverlay
2535
+ //parent: this.elements.columnSelectionOverlay
2536
+ parent: document.body
2530
2537
  });
2531
2538
 
2532
2539
  // Populate menu with column options
@@ -2612,29 +2619,36 @@ class FTable extends FTableEventEmitter {
2612
2619
  }
2613
2620
 
2614
2621
  positionColumnSelectionMenu(e) {
2615
- const containerRect = this.elements.mainContainer.getBoundingClientRect();
2616
- const menuWidth = 200; // Approximate menu width
2617
- const menuHeight = this.columnList.length * 30 + 20; // Approximate height
2622
+ const self = this;
2618
2623
 
2619
- let left = e.clientX - containerRect.left;
2620
- let top = e.clientY - containerRect.top;
2624
+ // Use clientX/clientY (relative to viewport)
2625
+ let left = e.clientX;
2626
+ let top = e.clientY;
2621
2627
 
2622
- // Adjust position to keep menu within container bounds
2623
- if (left + menuWidth > containerRect.width) {
2624
- left = Math.max(0, containerRect.width - menuWidth);
2625
- }
2628
+ // Define minimum width
2629
+ const minWidth = 100;
2626
2630
 
2627
- if (top + menuHeight > containerRect.height) {
2628
- top = Math.max(0, containerRect.height - menuHeight);
2629
- }
2631
+ // Position the menu
2632
+ self.elements.columnSelectionMenu.style.position = 'absolute';
2633
+ self.elements.columnSelectionMenu.style.left = `${left}px`;
2634
+ self.elements.columnSelectionMenu.style.top = `${top}px`;
2635
+ self.elements.columnSelectionMenu.style.minWidth = `${minWidth}px`;
2636
+ self.elements.columnSelectionMenu.style.boxSizing = 'border-box';
2637
+
2638
+ // Optional: Adjust if menu would overflow right edge
2639
+ const menuWidth = self.elements.columnSelectionMenu.offsetWidth;
2640
+ const windowWidth = window.innerWidth;
2630
2641
 
2631
- this.elements.columnSelectionMenu.style.left = left + 'px';
2632
- this.elements.columnSelectionMenu.style.top = top + 'px';
2642
+ if (left + menuWidth > windowWidth) {
2643
+ left = Math.max(10, windowWidth - menuWidth - 10); // 10px margin
2644
+ self.elements.columnSelectionMenu.style.left = `${left}px`;
2645
+ }
2633
2646
  }
2634
2647
 
2635
2648
  hideColumnSelectionMenu() {
2636
2649
  if (this.elements.columnSelectionOverlay) {
2637
2650
  this.elements.columnSelectionOverlay.remove();
2651
+ this.elements.columnSelectionMenu.remove();
2638
2652
  this.elements.columnSelectionOverlay = null;
2639
2653
  this.elements.columnSelectionMenu = null;
2640
2654
  }
@@ -2877,7 +2891,9 @@ class FTable extends FTableEventEmitter {
2877
2891
  if (typeof listAction === 'function') {
2878
2892
  data = await listAction(params);
2879
2893
  } else if (typeof listAction === 'string') {
2880
- data = await FTableHttpClient.get(listAction, params);
2894
+ data = this.options.forcePost
2895
+ ? await FTableHttpClient.post(listAction, params)
2896
+ : await FTableHttpClient.get(listAction, params);
2881
2897
  } else {
2882
2898
  throw new Error('No valid listAction provided');
2883
2899
  }
@@ -4411,7 +4427,9 @@ class FTable extends FTableEventEmitter {
4411
4427
  ...params
4412
4428
  };
4413
4429
 
4414
- const response = await FTableHttpClient.get(url, fullParams);
4430
+ const response = this.options.forcePost
4431
+ ? await FTableHttpClient.post(url, fullParams)
4432
+ : await FTableHttpClient.get(url, fullParams);
4415
4433
 
4416
4434
  if (!response || !response.Record) {
4417
4435
  throw new Error('Invalid response or missing Record');
@@ -4713,140 +4731,4 @@ class FTable extends FTableEventEmitter {
4713
4731
  }
4714
4732
  }
4715
4733
 
4716
- // Export for use
4717
- //window.FTable = FTable;
4718
-
4719
- // Usage example:
4720
- /*
4721
- const table = new FTable('#myTable', {
4722
- title: 'My Data Table',
4723
- paging: true,
4724
- pageSize: 25,
4725
- sorting: true,
4726
- selecting: true,
4727
- actions: {
4728
- listAction: '/api/users',
4729
- createAction: '/api/users',
4730
- updateAction: '/api/users',
4731
- deleteAction: '/api/users'
4732
- },
4733
- fields: {
4734
- id: { key: true, list: false },
4735
- name: {
4736
- title: 'Name',
4737
- type: 'text',
4738
- inputAttributes: "maxlength=100 required"
4739
- },
4740
- email: {
4741
- title: 'Email',
4742
- type: 'email',
4743
- width: '40%',
4744
- inputAttributes: {
4745
- pattern: '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$'
4746
- }
4747
- },
4748
- created: { title: 'Created', type: 'date', width: '30%' }
4749
- },
4750
- toolbarsearch: true,
4751
- childTable: {
4752
- title: 'Child Records',
4753
- actions: {
4754
- listAction: '/api/users/{id}/orders', // {id} will be replaced with parent record id
4755
- createAction: '/api/users/{id}/orders',
4756
- updateAction: '/api/orders',
4757
- deleteAction: '/api/orders'
4758
- },
4759
- fields: {
4760
- orderId: { key: true, list: false },
4761
- orderDate: { title: 'Date', type: 'date' },
4762
- amount: { title: 'Amount', type: 'number' }
4763
- }
4764
- },
4765
- childTableColumnsVisible: true,
4766
-
4767
- });
4768
-
4769
- // Or dynamic child table
4770
- childTable: async function(parentRecord) {
4771
- return {
4772
- title: `Orders for ${parentRecord.name}`,
4773
- actions: {
4774
- listAction: `/api/users/${parentRecord.id}/orders`
4775
- },
4776
- fields: {
4777
- // Dynamic fields based on parent
4778
- }
4779
- };
4780
- }
4781
-
4782
- // function for select options
4783
- fields: {
4784
- assignee: {
4785
- title: 'Assigned To',
4786
- type: 'select',
4787
- options: async function(params) {
4788
- // params contains dependsOnValue, dependsOnField, etc.
4789
- const department = params.dependsOnValue;
4790
- const response = await fetch(`/api/users?department=${department}`);
4791
- return response.json();
4792
- },
4793
- dependsOn: 'department'
4794
- }
4795
- }
4796
-
4797
- // child table:
4798
- phoneNumbers: {
4799
- title: 'Phones',
4800
- display: (data) => {
4801
- const img = document.createElement('img');
4802
- img.className = 'child-opener-image';
4803
- img.src = '/Content/images/Misc/phone.png';
4804
- img.title = 'Edit phone numbers';
4805
- img.style.cursor = 'pointer';
4806
-
4807
- parentRow = img.closest('tr');
4808
- img.addEventListener('click', () => {
4809
- e.stopPropagation();
4810
- if (parentRow.childRow) {
4811
- myTable.closeChildTable(parentRow);
4812
- } else {
4813
- myTable.openChildTable( parentRow, {
4814
- title: `${data.record.Name} - Phone numbers`,
4815
- actions: {
4816
- listAction: `/PagingPerson/PhoneList?PersonId=${data.record.PersonId}`,
4817
- deleteAction: '/PagingPerson/DeletePhone',
4818
- updateAction: '/PagingPerson/UpdatePhone',
4819
- createAction: `/PagingPerson/CreatePhone?PersonId=${data.record.PersonId}`
4820
- },
4821
- fields: {
4822
- PhoneId: { key: true },
4823
- Number: { title: 'Number', type: 'text' },
4824
- Type: { title: 'Type', options: { 0: 'Home', 1: 'Work', 2: 'Mobile' } }
4825
- }
4826
- }, (childTable) => {
4827
- console.log('Child table created');
4828
- };
4829
- }
4830
- });
4831
- img.addEventListener('click', (e) => {
4832
- e.stopPropagation();
4833
- if (parentRow.childRow) {
4834
- myTable.closeChildTable(parentRow);
4835
- } else {
4836
- myTable.openChildTable(parentRow, childOptions);
4837
- }
4838
- });
4839
-
4840
- return img;
4841
- }
4842
- }
4843
-
4844
- // Clear specific options cache
4845
- table.clearOptionsCache('/api/countries');
4846
-
4847
- table.load();
4848
- */
4849
-
4850
- window.FTable = FTable;
4851
-
4852
4734
  export default FTable;
package/ftable.js CHANGED
@@ -4,9 +4,7 @@
4
4
  typeof define === 'function' && define.amd ? define(factory) :
5
5
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.FTable = factory());
6
6
  }(this, (function () {
7
- // Modern fTable - Vanilla JS Refactor
8
-
9
- const FTABLE_DEFAULT_MESSAGES = {
7
+ const FTABLE_DEFAULT_MESSAGES = {
10
8
  serverCommunicationError: 'An error occurred while communicating to the server.',
11
9
  loadingMessage: 'Loading records...',
12
10
  noDataAvailable: 'No data available!',
@@ -753,7 +751,9 @@ class FTableFormBuilder {
753
751
  }
754
752
 
755
753
  try {
756
- const response = await FTableHttpClient.get(url);
754
+ const response = this.options.forcePost
755
+ ? await FTableHttpClient.post(url)
756
+ : await FTableHttpClient.get(url);
757
757
  const options = response.Options || response.options || response || [];
758
758
 
759
759
  // Only cache if noCache is false
@@ -1359,10 +1359,14 @@ class FTable extends FTableEventEmitter {
1359
1359
  this.element = typeof element === 'string' ?
1360
1360
  document.querySelector(element) : element;
1361
1361
 
1362
+ if (!this.element) {
1363
+ return;
1364
+ }
1365
+
1362
1366
  // Prevent double initialization
1363
- if (element.ftableInstance) {
1367
+ if (this.element.ftableInstance) {
1364
1368
  //console.warn('FTable is already initialized on this element. Using that.');
1365
- return element.ftableInstance;
1369
+ return this.element.ftableInstance;
1366
1370
  }
1367
1371
 
1368
1372
  this.options = this.mergeOptions(options);
@@ -1399,6 +1403,7 @@ class FTable extends FTableEventEmitter {
1399
1403
  logLevel: FTableLogger.LOG_LEVELS.WARN,
1400
1404
  actions: {},
1401
1405
  fields: {},
1406
+ forcePost: true,
1402
1407
  animationsEnabled: true,
1403
1408
  loadingAnimationDelay: 1000,
1404
1409
  defaultDateLocale: '',
@@ -2525,13 +2530,15 @@ class FTable extends FTableEventEmitter {
2525
2530
  // Create overlay to capture clicks outside menu
2526
2531
  this.elements.columnSelectionOverlay = FTableDOMHelper.create('div', {
2527
2532
  className: 'ftable-contextmenu-overlay',
2528
- parent: this.elements.mainContainer
2533
+ //parent: this.elements.mainContainer
2534
+ parent: document.body
2529
2535
  });
2530
2536
 
2531
2537
  // Create the menu
2532
2538
  this.elements.columnSelectionMenu = FTableDOMHelper.create('div', {
2533
2539
  className: 'ftable-column-selection-container',
2534
- parent: this.elements.columnSelectionOverlay
2540
+ //parent: this.elements.columnSelectionOverlay
2541
+ parent: document.body
2535
2542
  });
2536
2543
 
2537
2544
  // Populate menu with column options
@@ -2617,29 +2624,36 @@ class FTable extends FTableEventEmitter {
2617
2624
  }
2618
2625
 
2619
2626
  positionColumnSelectionMenu(e) {
2620
- const containerRect = this.elements.mainContainer.getBoundingClientRect();
2621
- const menuWidth = 200; // Approximate menu width
2622
- const menuHeight = this.columnList.length * 30 + 20; // Approximate height
2627
+ const self = this;
2623
2628
 
2624
- let left = e.clientX - containerRect.left;
2625
- let top = e.clientY - containerRect.top;
2629
+ // Use clientX/clientY (relative to viewport)
2630
+ let left = e.clientX;
2631
+ let top = e.clientY;
2626
2632
 
2627
- // Adjust position to keep menu within container bounds
2628
- if (left + menuWidth > containerRect.width) {
2629
- left = Math.max(0, containerRect.width - menuWidth);
2630
- }
2633
+ // Define minimum width
2634
+ const minWidth = 100;
2631
2635
 
2632
- if (top + menuHeight > containerRect.height) {
2633
- top = Math.max(0, containerRect.height - menuHeight);
2634
- }
2636
+ // Position the menu
2637
+ self.elements.columnSelectionMenu.style.position = 'absolute';
2638
+ self.elements.columnSelectionMenu.style.left = `${left}px`;
2639
+ self.elements.columnSelectionMenu.style.top = `${top}px`;
2640
+ self.elements.columnSelectionMenu.style.minWidth = `${minWidth}px`;
2641
+ self.elements.columnSelectionMenu.style.boxSizing = 'border-box';
2642
+
2643
+ // Optional: Adjust if menu would overflow right edge
2644
+ const menuWidth = self.elements.columnSelectionMenu.offsetWidth;
2645
+ const windowWidth = window.innerWidth;
2635
2646
 
2636
- this.elements.columnSelectionMenu.style.left = left + 'px';
2637
- this.elements.columnSelectionMenu.style.top = top + 'px';
2647
+ if (left + menuWidth > windowWidth) {
2648
+ left = Math.max(10, windowWidth - menuWidth - 10); // 10px margin
2649
+ self.elements.columnSelectionMenu.style.left = `${left}px`;
2650
+ }
2638
2651
  }
2639
2652
 
2640
2653
  hideColumnSelectionMenu() {
2641
2654
  if (this.elements.columnSelectionOverlay) {
2642
2655
  this.elements.columnSelectionOverlay.remove();
2656
+ this.elements.columnSelectionMenu.remove();
2643
2657
  this.elements.columnSelectionOverlay = null;
2644
2658
  this.elements.columnSelectionMenu = null;
2645
2659
  }
@@ -2882,7 +2896,9 @@ class FTable extends FTableEventEmitter {
2882
2896
  if (typeof listAction === 'function') {
2883
2897
  data = await listAction(params);
2884
2898
  } else if (typeof listAction === 'string') {
2885
- data = await FTableHttpClient.get(listAction, params);
2899
+ data = this.options.forcePost
2900
+ ? await FTableHttpClient.post(listAction, params)
2901
+ : await FTableHttpClient.get(listAction, params);
2886
2902
  } else {
2887
2903
  throw new Error('No valid listAction provided');
2888
2904
  }
@@ -4416,7 +4432,9 @@ class FTable extends FTableEventEmitter {
4416
4432
  ...params
4417
4433
  };
4418
4434
 
4419
- const response = await FTableHttpClient.get(url, fullParams);
4435
+ const response = this.options.forcePost
4436
+ ? await FTableHttpClient.post(url, fullParams)
4437
+ : await FTableHttpClient.get(url, fullParams);
4420
4438
 
4421
4439
  if (!response || !response.Record) {
4422
4440
  throw new Error('Invalid response or missing Record');
@@ -4718,141 +4736,5 @@ class FTable extends FTableEventEmitter {
4718
4736
  }
4719
4737
  }
4720
4738
 
4721
- // Export for use
4722
- //window.FTable = FTable;
4723
-
4724
- // Usage example:
4725
- /*
4726
- const table = new FTable('#myTable', {
4727
- title: 'My Data Table',
4728
- paging: true,
4729
- pageSize: 25,
4730
- sorting: true,
4731
- selecting: true,
4732
- actions: {
4733
- listAction: '/api/users',
4734
- createAction: '/api/users',
4735
- updateAction: '/api/users',
4736
- deleteAction: '/api/users'
4737
- },
4738
- fields: {
4739
- id: { key: true, list: false },
4740
- name: {
4741
- title: 'Name',
4742
- type: 'text',
4743
- inputAttributes: "maxlength=100 required"
4744
- },
4745
- email: {
4746
- title: 'Email',
4747
- type: 'email',
4748
- width: '40%',
4749
- inputAttributes: {
4750
- pattern: '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$'
4751
- }
4752
- },
4753
- created: { title: 'Created', type: 'date', width: '30%' }
4754
- },
4755
- toolbarsearch: true,
4756
- childTable: {
4757
- title: 'Child Records',
4758
- actions: {
4759
- listAction: '/api/users/{id}/orders', // {id} will be replaced with parent record id
4760
- createAction: '/api/users/{id}/orders',
4761
- updateAction: '/api/orders',
4762
- deleteAction: '/api/orders'
4763
- },
4764
- fields: {
4765
- orderId: { key: true, list: false },
4766
- orderDate: { title: 'Date', type: 'date' },
4767
- amount: { title: 'Amount', type: 'number' }
4768
- }
4769
- },
4770
- childTableColumnsVisible: true,
4771
-
4772
- });
4773
-
4774
- // Or dynamic child table
4775
- childTable: async function(parentRecord) {
4776
- return {
4777
- title: `Orders for ${parentRecord.name}`,
4778
- actions: {
4779
- listAction: `/api/users/${parentRecord.id}/orders`
4780
- },
4781
- fields: {
4782
- // Dynamic fields based on parent
4783
- }
4784
- };
4785
- }
4786
-
4787
- // function for select options
4788
- fields: {
4789
- assignee: {
4790
- title: 'Assigned To',
4791
- type: 'select',
4792
- options: async function(params) {
4793
- // params contains dependsOnValue, dependsOnField, etc.
4794
- const department = params.dependsOnValue;
4795
- const response = await fetch(`/api/users?department=${department}`);
4796
- return response.json();
4797
- },
4798
- dependsOn: 'department'
4799
- }
4800
- }
4801
-
4802
- // child table:
4803
- phoneNumbers: {
4804
- title: 'Phones',
4805
- display: (data) => {
4806
- const img = document.createElement('img');
4807
- img.className = 'child-opener-image';
4808
- img.src = '/Content/images/Misc/phone.png';
4809
- img.title = 'Edit phone numbers';
4810
- img.style.cursor = 'pointer';
4811
-
4812
- parentRow = img.closest('tr');
4813
- img.addEventListener('click', () => {
4814
- e.stopPropagation();
4815
- if (parentRow.childRow) {
4816
- myTable.closeChildTable(parentRow);
4817
- } else {
4818
- myTable.openChildTable( parentRow, {
4819
- title: `${data.record.Name} - Phone numbers`,
4820
- actions: {
4821
- listAction: `/PagingPerson/PhoneList?PersonId=${data.record.PersonId}`,
4822
- deleteAction: '/PagingPerson/DeletePhone',
4823
- updateAction: '/PagingPerson/UpdatePhone',
4824
- createAction: `/PagingPerson/CreatePhone?PersonId=${data.record.PersonId}`
4825
- },
4826
- fields: {
4827
- PhoneId: { key: true },
4828
- Number: { title: 'Number', type: 'text' },
4829
- Type: { title: 'Type', options: { 0: 'Home', 1: 'Work', 2: 'Mobile' } }
4830
- }
4831
- }, (childTable) => {
4832
- console.log('Child table created');
4833
- };
4834
- }
4835
- });
4836
- img.addEventListener('click', (e) => {
4837
- e.stopPropagation();
4838
- if (parentRow.childRow) {
4839
- myTable.closeChildTable(parentRow);
4840
- } else {
4841
- myTable.openChildTable(parentRow, childOptions);
4842
- }
4843
- });
4844
-
4845
- return img;
4846
- }
4847
- }
4848
-
4849
- // Clear specific options cache
4850
- table.clearOptionsCache('/api/countries');
4851
-
4852
- table.load();
4853
- */
4854
-
4855
- window.FTable = FTable;
4856
-
4857
4739
  return FTable;
4858
4740
  })));