@liedekef/ftable 1.1.49 → 1.1.51

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 (38) hide show
  1. package/ftable.esm.js +347 -91
  2. package/ftable.js +347 -91
  3. package/ftable.min.js +2 -24
  4. package/ftable.umd.js +347 -91
  5. package/package.json +1 -1
  6. package/themes/basic/ftable_basic.css +18 -7
  7. package/themes/basic/ftable_basic.min.css +1 -1
  8. package/themes/ftable_theme_base.less +21 -10
  9. package/themes/lightcolor/blue/ftable.css +18 -7
  10. package/themes/lightcolor/blue/ftable.min.css +1 -1
  11. package/themes/lightcolor/gray/ftable.css +18 -7
  12. package/themes/lightcolor/gray/ftable.min.css +1 -1
  13. package/themes/lightcolor/green/ftable.css +18 -7
  14. package/themes/lightcolor/green/ftable.min.css +1 -1
  15. package/themes/lightcolor/orange/ftable.css +18 -7
  16. package/themes/lightcolor/orange/ftable.min.css +1 -1
  17. package/themes/lightcolor/red/ftable.css +18 -7
  18. package/themes/lightcolor/red/ftable.min.css +1 -1
  19. package/themes/metro/blue/ftable.css +18 -7
  20. package/themes/metro/blue/ftable.min.css +1 -1
  21. package/themes/metro/brown/ftable.css +18 -7
  22. package/themes/metro/brown/ftable.min.css +1 -1
  23. package/themes/metro/crimson/ftable.css +18 -7
  24. package/themes/metro/crimson/ftable.min.css +1 -1
  25. package/themes/metro/darkgray/ftable.css +18 -7
  26. package/themes/metro/darkgray/ftable.min.css +1 -1
  27. package/themes/metro/darkorange/ftable.css +18 -7
  28. package/themes/metro/darkorange/ftable.min.css +1 -1
  29. package/themes/metro/green/ftable.css +18 -7
  30. package/themes/metro/green/ftable.min.css +1 -1
  31. package/themes/metro/lightgray/ftable.css +18 -7
  32. package/themes/metro/lightgray/ftable.min.css +1 -1
  33. package/themes/metro/pink/ftable.css +18 -7
  34. package/themes/metro/pink/ftable.min.css +1 -1
  35. package/themes/metro/purple/ftable.css +18 -7
  36. package/themes/metro/purple/ftable.min.css +1 -1
  37. package/themes/metro/red/ftable.css +18 -7
  38. package/themes/metro/red/ftable.min.css +1 -1
package/ftable.esm.js CHANGED
@@ -1429,7 +1429,10 @@ class FTableFormBuilder {
1429
1429
  // Create display area
1430
1430
  const display = FTableDOMHelper.create('div', {
1431
1431
  className: 'ftable-multiselect-display',
1432
- parent: container
1432
+ parent: container,
1433
+ attributes: {
1434
+ tabindex: '0' // Makes it focusable and in tab order
1435
+ }
1433
1436
  });
1434
1437
 
1435
1438
  const selectedDisplay = FTableDOMHelper.create('div', {
@@ -1449,15 +1452,15 @@ class FTableFormBuilder {
1449
1452
  type: 'button',
1450
1453
  className: 'ftable-multiselect-toggle',
1451
1454
  innerHTML: '▼',
1452
- parent: display
1455
+ parent: display,
1456
+ attributes: {
1457
+ tabindex: '-1' // this skips regular focus when tabbing
1458
+ }
1453
1459
  });
1454
1460
 
1455
- // Create options dropdown
1456
- const dropdown = FTableDOMHelper.create('div', {
1457
- className: 'ftable-multiselect-dropdown',
1458
- parent: container,
1459
- style: 'display: none;'
1460
- });
1461
+ // Dropdown and overlay will be created on demand and appended to body
1462
+ let dropdown = null;
1463
+ let dropdownOverlay = null;
1461
1464
 
1462
1465
  // Store selected values and checkbox references
1463
1466
  const selectedValues = new Set(
@@ -1525,9 +1528,54 @@ class FTableFormBuilder {
1525
1528
  hiddenInput.value = Array.from(selectedValues).join(',');
1526
1529
  };
1527
1530
 
1531
+ // Function to close dropdown
1532
+ const closeDropdown = () => {
1533
+ display.focus(); // Return focus to the trigger
1534
+ if (dropdown) {
1535
+ dropdown.remove();
1536
+ dropdown = null;
1537
+ }
1538
+ if (dropdownOverlay) {
1539
+ dropdownOverlay.remove();
1540
+ dropdownOverlay = null;
1541
+ }
1542
+ if (container._cleanupHandlers) {
1543
+ container._cleanupHandlers();
1544
+ container._cleanupHandlers = null;
1545
+ }
1546
+ };
1547
+
1548
+ // Function to position dropdown
1549
+ const positionDropdown = () => {
1550
+ if (!dropdown) return;
1551
+
1552
+ const rect = display.getBoundingClientRect();
1553
+ const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
1554
+ const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
1555
+
1556
+ let left = rect.left + scrollLeft;
1557
+ let top = rect.bottom + scrollTop + 4; // 4px gap
1558
+
1559
+ dropdown.style.position = 'absolute';
1560
+ dropdown.style.left = `${left}px`;
1561
+ dropdown.style.top = `${top}px`;
1562
+ dropdown.style.width = `${rect.width}px`;
1563
+ dropdown.style.minWidth = `${rect.width}px`;
1564
+ dropdown.style.boxSizing = 'border-box';
1565
+ dropdown.style.zIndex = '10000';
1566
+
1567
+ // Adjust horizontal position if needed
1568
+ const dropdownRect = dropdown.getBoundingClientRect();
1569
+ const viewportWidth = window.innerWidth;
1570
+ if (dropdownRect.right > viewportWidth) {
1571
+ left = Math.max(10, viewportWidth - dropdownRect.width - 10);
1572
+ dropdown.style.left = `${left}px`;
1573
+ }
1574
+ };
1575
+
1528
1576
  // Populate options
1529
1577
  const populateOptions = () => {
1530
- if (!field.options) return;
1578
+ if (!field.options || !dropdown) return;
1531
1579
 
1532
1580
  const options = Array.isArray(field.options) ? field.options :
1533
1581
  Object.entries(field.options).map(([k, v]) => ({Value: k, DisplayText: v}));
@@ -1584,27 +1632,123 @@ class FTableFormBuilder {
1584
1632
  // Toggle dropdown
1585
1633
  const toggleDropdown = (e) => {
1586
1634
  if (e) e.stopPropagation();
1587
- const isVisible = dropdown.style.display !== 'none';
1588
- dropdown.style.display = isVisible ? 'none' : 'block';
1589
1635
 
1590
- if (!isVisible) {
1591
- // Close other dropdowns
1592
- document.querySelectorAll('.ftable-multiselect-dropdown').forEach(dd => {
1593
- if (dd !== dropdown) dd.style.display = 'none';
1636
+ if (dropdown) {
1637
+ // Dropdown is open, close it
1638
+ closeDropdown();
1639
+ } else {
1640
+ // Close any other open multiselect dropdowns
1641
+ document.querySelectorAll('.ftable-multiselect-dropdown').forEach(dd => dd.remove());
1642
+ document.querySelectorAll('.ftable-multiselect-overlay').forEach(ov => ov.remove());
1643
+
1644
+ // Create overlay
1645
+ dropdownOverlay = FTableDOMHelper.create('div', {
1646
+ className: 'ftable-multiselect-overlay',
1647
+ parent: document.body
1648
+ });
1649
+
1650
+ // Create dropdown
1651
+ dropdown = FTableDOMHelper.create('div', {
1652
+ className: 'ftable-multiselect-dropdown',
1653
+ parent: document.body,
1654
+ attributes: {
1655
+ tabindex: '-1',
1656
+ role: 'listbox',
1657
+ 'aria-multiselectable': 'true'
1658
+ }
1659
+ });
1660
+
1661
+ // Populate options
1662
+ populateOptions();
1663
+
1664
+ // Position dropdown
1665
+ positionDropdown();
1666
+
1667
+ // dropdown focus
1668
+ dropdown.focus();
1669
+
1670
+ // Add keyboard navigation
1671
+ dropdown.addEventListener('keydown', (e) => {
1672
+ if (e.key === 'Escape') {
1673
+ closeDropdown();
1674
+ } else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
1675
+ e.preventDefault();
1676
+ // Navigate between options
1677
+ const checkboxes = Array.from(dropdown.querySelectorAll('.ftable-multiselect-checkbox'));
1678
+ const current = document.activeElement;
1679
+ const currentIndex = checkboxes.indexOf(current);
1680
+
1681
+ let nextIndex;
1682
+ if (e.key === 'ArrowDown') {
1683
+ nextIndex = currentIndex < checkboxes.length - 1 ? currentIndex + 1 : 0;
1684
+ } else {
1685
+ nextIndex = currentIndex > 0 ? currentIndex - 1 : checkboxes.length - 1;
1686
+ }
1687
+
1688
+ checkboxes[nextIndex].focus();
1689
+ } else if (e.key === ' ' || e.key === 'Enter') {
1690
+ e.preventDefault();
1691
+ // Toggle the focused checkbox
1692
+ if (document.activeElement.classList.contains('ftable-multiselect-checkbox')) {
1693
+ document.activeElement.click();
1694
+ }
1695
+ }
1696
+ });
1697
+
1698
+ // Handle clicks outside
1699
+ dropdownOverlay.addEventListener('click', (event) => {
1700
+ if (event.target === dropdownOverlay) {
1701
+ closeDropdown();
1702
+ }
1594
1703
  });
1704
+
1705
+ // Reposition on scroll/resize
1706
+ const scrollHandler = (e) => {
1707
+ if (dropdown && dropdown.contains(e.target)) {
1708
+ return; // Allow scrolling inside dropdown
1709
+ }
1710
+ positionDropdown();
1711
+ };
1712
+ const repositionHandler = () => positionDropdown();
1713
+ window.addEventListener('scroll', scrollHandler, true);
1714
+ window.addEventListener('resize', repositionHandler);
1715
+
1716
+ // Store cleanup function
1717
+ container._cleanupHandlers = () => {
1718
+ window.removeEventListener('scroll', scrollHandler, true);
1719
+ window.removeEventListener('resize', repositionHandler);
1720
+ };
1595
1721
  }
1596
1722
  };
1597
1723
 
1598
1724
  display.addEventListener('click', toggleDropdown);
1599
1725
  toggleBtn.addEventListener('click', toggleDropdown);
1600
-
1601
- // Close dropdown when clicking outside
1602
- document.addEventListener('click', (e) => {
1603
- if (!container.contains(e.target)) {
1604
- dropdown.style.display = 'none';
1726
+ display.addEventListener('keydown', (e) => {
1727
+ if (e.key === 'ArrowDown' || e.key === 'Enter') {
1728
+ e.preventDefault();
1729
+ toggleDropdown();
1605
1730
  }
1606
1731
  });
1607
1732
 
1733
+ // Clean up when container is removed from DOM
1734
+ const observer = new MutationObserver((mutations) => {
1735
+ mutations.forEach((mutation) => {
1736
+ mutation.removedNodes.forEach((node) => {
1737
+ if (node === container || node.contains && node.contains(container)) {
1738
+ closeDropdown();
1739
+ observer.disconnect();
1740
+ }
1741
+ });
1742
+ });
1743
+ });
1744
+
1745
+ // Start observing once container is in the DOM
1746
+ setTimeout(() => {
1747
+ if (container.parentNode) {
1748
+ observer.observe(container.parentNode, { childList: true, subtree: true });
1749
+ }
1750
+ }, 0);
1751
+
1608
1752
  // Initialize
1609
1753
  populateOptions();
1610
1754
  updateDisplay();
@@ -1931,9 +2075,6 @@ class FTable extends FTableEventEmitter {
1931
2075
  this.updateSortingHeaders();
1932
2076
  this.renderSortingInfo();
1933
2077
 
1934
- // Add essential CSS if not already present
1935
- //this.addEssentialCSS();
1936
-
1937
2078
  // now make sure all tables have a % width
1938
2079
  this.initColumnWidths();
1939
2080
  }
@@ -2013,40 +2154,6 @@ class FTable extends FTableEventEmitter {
2013
2154
  return result;
2014
2155
  }
2015
2156
 
2016
- addEssentialCSS() {
2017
- // Check if our CSS is already added
2018
- if (document.querySelector('#ftable-essential-css')) return;
2019
-
2020
- const css = `
2021
- .ftable-row-animation {
2022
- transition: background-color 0.3s ease;
2023
- }
2024
-
2025
- .ftable-row-added {
2026
- background-color: #d4edda !important;
2027
- }
2028
-
2029
- .ftable-row-edited {
2030
- background-color: #d1ecf1 !important;
2031
- }
2032
-
2033
- .ftable-row-deleted {
2034
- opacity: 0;
2035
- transform: translateY(-10px);
2036
- transition: opacity 0.3s ease, transform 0.3s ease;
2037
- }
2038
-
2039
- .ftable-toolbarsearch {
2040
- width: 90%;
2041
- }
2042
- `;
2043
-
2044
- const style = document.createElement('style');
2045
- style.id = 'ftable-essential-css';
2046
- style.textContent = css;
2047
- document.head.appendChild(style);
2048
- }
2049
-
2050
2157
  createPagingUI() {
2051
2158
  this.elements.bottomPanel = FTableDOMHelper.create('div', {
2052
2159
  className: 'ftable-bottom-panel',
@@ -2680,7 +2787,10 @@ class FTable extends FTableEventEmitter {
2680
2787
  // Create display area
2681
2788
  const display = FTableDOMHelper.create('div', {
2682
2789
  className: 'ftable-multiselect-display',
2683
- parent: container
2790
+ parent: container,
2791
+ attributes: {
2792
+ tabindex: '0' // Makes it focusable and in tab order
2793
+ }
2684
2794
  });
2685
2795
 
2686
2796
  const selectedDisplay = FTableDOMHelper.create('div', {
@@ -2700,15 +2810,15 @@ class FTable extends FTableEventEmitter {
2700
2810
  type: 'button',
2701
2811
  className: 'ftable-multiselect-toggle',
2702
2812
  innerHTML: '▼',
2703
- parent: display
2813
+ parent: display,
2814
+ attributes: {
2815
+ tabindex: '-1' // this skips regular focus when tabbing
2816
+ }
2704
2817
  });
2705
2818
 
2706
- // Create options dropdown
2707
- const dropdown = FTableDOMHelper.create('div', {
2708
- className: 'ftable-multiselect-dropdown',
2709
- parent: container,
2710
- style: 'display: none;'
2711
- });
2819
+ // Dropdown and overlay will be created on demand and appended to body
2820
+ let dropdown = null;
2821
+ let dropdownOverlay = null;
2712
2822
 
2713
2823
  // Store selected values and checkbox references
2714
2824
  const selectedValues = new Set();
@@ -2775,18 +2885,54 @@ class FTable extends FTableEventEmitter {
2775
2885
  }
2776
2886
  };
2777
2887
 
2778
- // Add reset method to container
2779
- container.resetMultiSelect = () => {
2780
- selectedValues.clear();
2781
- checkboxMap.forEach(checkbox => {
2782
- checkbox.checked = false;
2783
- });
2784
- updateDisplay();
2888
+ // Function to close dropdown
2889
+ const closeDropdown = () => {
2890
+ display.focus(); // Return focus to the trigger
2891
+ if (dropdown) {
2892
+ dropdown.remove();
2893
+ dropdown = null;
2894
+ }
2895
+ if (dropdownOverlay) {
2896
+ dropdownOverlay.remove();
2897
+ dropdownOverlay = null;
2898
+ }
2899
+ if (container._cleanupHandlers) {
2900
+ container._cleanupHandlers();
2901
+ container._cleanupHandlers = null;
2902
+ }
2903
+ };
2904
+
2905
+ // Function to position dropdown
2906
+ const positionDropdown = () => {
2907
+ if (!dropdown) return;
2908
+
2909
+ const rect = display.getBoundingClientRect();
2910
+ const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
2911
+ const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
2912
+
2913
+ let left = rect.left + scrollLeft;
2914
+ let top = rect.bottom + scrollTop + 4; // 4px gap
2915
+
2916
+ dropdown.style.position = 'absolute';
2917
+ dropdown.style.left = `${left}px`;
2918
+ dropdown.style.top = `${top}px`;
2919
+ dropdown.style.width = `${rect.width}px`;
2920
+ dropdown.style.minWidth = `${rect.width}px`;
2921
+ dropdown.style.boxSizing = 'border-box';
2922
+ dropdown.style.zIndex = '10000';
2923
+
2924
+ // Adjust horizontal position if needed
2925
+ const dropdownRect = dropdown.getBoundingClientRect();
2926
+ const viewportWidth = window.innerWidth;
2927
+ if (dropdownRect.right > viewportWidth) {
2928
+ left = Math.max(10, viewportWidth - dropdownRect.width - 10);
2929
+ dropdown.style.left = `${left}px`;
2930
+ }
2785
2931
  };
2786
2932
 
2787
2933
  // Populate options in both hidden select and dropdown
2788
2934
  const populateOptions = () => {
2789
- if (!optionsSource) return;
2935
+ if (!optionsSource || !dropdown) return;
2790
2936
 
2791
2937
  const options = Array.isArray(optionsSource) ? optionsSource :
2792
2938
  Object.entries(optionsSource).map(([k, v]) => ({Value: k, DisplayText: v}));
@@ -2802,12 +2948,14 @@ class FTable extends FTableEventEmitter {
2802
2948
 
2803
2949
  const optText = option.DisplayText || option.text || option;
2804
2950
 
2805
- // Add to hidden select
2806
- FTableDOMHelper.create('option', {
2807
- value: optValue,
2808
- textContent: optText,
2809
- parent: hiddenSelect
2810
- });
2951
+ // Add to hidden select (only once)
2952
+ if (!hiddenSelect.querySelector(`option[value="${optValue}"]`)) {
2953
+ FTableDOMHelper.create('option', {
2954
+ value: optValue,
2955
+ textContent: optText,
2956
+ parent: hiddenSelect
2957
+ });
2958
+ }
2811
2959
 
2812
2960
  // Add to visual dropdown
2813
2961
  const optionDiv = FTableDOMHelper.create('div', {
@@ -2821,6 +2969,9 @@ class FTable extends FTableEventEmitter {
2821
2969
  parent: optionDiv
2822
2970
  });
2823
2971
 
2972
+ // Set initial checked state
2973
+ checkbox.checked = selectedValues.has(optValue.toString());
2974
+
2824
2975
  // Store checkbox reference
2825
2976
  checkboxMap.set(optValue.toString(), checkbox);
2826
2977
 
@@ -2850,29 +3001,134 @@ class FTable extends FTableEventEmitter {
2850
3001
  // Toggle dropdown
2851
3002
  const toggleDropdown = (e) => {
2852
3003
  if (e) e.stopPropagation();
2853
- const isVisible = dropdown.style.display !== 'none';
2854
- dropdown.style.display = isVisible ? 'none' : 'block';
2855
3004
 
2856
- if (!isVisible) {
2857
- // Close other dropdowns
2858
- document.querySelectorAll('.ftable-multiselect-dropdown').forEach(dd => {
2859
- if (dd !== dropdown) dd.style.display = 'none';
3005
+ if (dropdown) {
3006
+ // Dropdown is open, close it
3007
+ closeDropdown();
3008
+ } else {
3009
+ // Close any other open multiselect dropdowns
3010
+ document.querySelectorAll('.ftable-multiselect-dropdown').forEach(dd => dd.remove());
3011
+ document.querySelectorAll('.ftable-multiselect-overlay').forEach(ov => ov.remove());
3012
+
3013
+ // Create overlay
3014
+ dropdownOverlay = FTableDOMHelper.create('div', {
3015
+ className: 'ftable-multiselect-overlay',
3016
+ parent: document.body
3017
+ });
3018
+
3019
+ // Create dropdown
3020
+ dropdown = FTableDOMHelper.create('div', {
3021
+ className: 'ftable-multiselect-dropdown',
3022
+ parent: document.body,
3023
+ attributes: {
3024
+ tabindex: '-1',
3025
+ role: 'listbox',
3026
+ 'aria-multiselectable': 'true'
3027
+ }
3028
+ });
3029
+
3030
+ // Populate options
3031
+ populateOptions();
3032
+
3033
+ // Position dropdown
3034
+ positionDropdown();
3035
+
3036
+ // dropdown focus
3037
+ dropdown.focus();
3038
+
3039
+ // Add keyboard navigation
3040
+ dropdown.addEventListener('keydown', (e) => {
3041
+ if (e.key === 'Escape') {
3042
+ closeDropdown();
3043
+ } else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
3044
+ e.preventDefault();
3045
+ // Navigate between options
3046
+ const checkboxes = Array.from(dropdown.querySelectorAll('.ftable-multiselect-checkbox'));
3047
+ const current = document.activeElement;
3048
+ const currentIndex = checkboxes.indexOf(current);
3049
+
3050
+ let nextIndex;
3051
+ if (e.key === 'ArrowDown') {
3052
+ nextIndex = currentIndex < checkboxes.length - 1 ? currentIndex + 1 : 0;
3053
+ } else {
3054
+ nextIndex = currentIndex > 0 ? currentIndex - 1 : checkboxes.length - 1;
3055
+ }
3056
+
3057
+ checkboxes[nextIndex].focus();
3058
+ } else if (e.key === ' ' || e.key === 'Enter') {
3059
+ e.preventDefault();
3060
+ // Toggle the focused checkbox
3061
+ if (document.activeElement.classList.contains('ftable-multiselect-checkbox')) {
3062
+ document.activeElement.click();
3063
+ }
3064
+ }
2860
3065
  });
3066
+
3067
+ // Handle clicks outside
3068
+ dropdownOverlay.addEventListener('click', (event) => {
3069
+ if (event.target === dropdownOverlay) {
3070
+ closeDropdown();
3071
+ }
3072
+ });
3073
+
3074
+ // Reposition on scroll/resize
3075
+ const scrollHandler = (e) => {
3076
+ if (dropdown && dropdown.contains(e.target)) {
3077
+ return; // Allow scrolling inside dropdown
3078
+ }
3079
+ positionDropdown();
3080
+ };
3081
+ const repositionHandler = () => positionDropdown();
3082
+ window.addEventListener('scroll', scrollHandler, true);
3083
+ window.addEventListener('resize', repositionHandler);
3084
+
3085
+ // Store cleanup function
3086
+ container._cleanupHandlers = () => {
3087
+ window.removeEventListener('scroll', scrollHandler, true);
3088
+ window.removeEventListener('resize', repositionHandler);
3089
+ };
2861
3090
  }
2862
3091
  };
2863
3092
 
2864
3093
  display.addEventListener('click', toggleDropdown);
2865
3094
  toggleBtn.addEventListener('click', toggleDropdown);
2866
-
2867
- // Close dropdown when clicking outside
2868
- document.addEventListener('click', (e) => {
2869
- if (!container.contains(e.target)) {
2870
- dropdown.style.display = 'none';
3095
+ display.addEventListener('keydown', (e) => {
3096
+ if (e.key === 'ArrowDown' || e.key === 'Enter') {
3097
+ e.preventDefault();
3098
+ toggleDropdown();
2871
3099
  }
2872
3100
  });
2873
3101
 
3102
+ // Add reset method to container
3103
+ container.resetMultiSelect = () => {
3104
+ selectedValues.clear();
3105
+ checkboxMap.forEach(checkbox => {
3106
+ checkbox.checked = false;
3107
+ });
3108
+ closeDropdown();
3109
+ updateDisplay();
3110
+ };
3111
+
3112
+ // Clean up when container is removed from DOM
3113
+ const observer = new MutationObserver((mutations) => {
3114
+ mutations.forEach((mutation) => {
3115
+ mutation.removedNodes.forEach((node) => {
3116
+ if (node === container || node.contains && node.contains(container)) {
3117
+ closeDropdown();
3118
+ observer.disconnect();
3119
+ }
3120
+ });
3121
+ });
3122
+ });
3123
+
3124
+ // Start observing once container is in the DOM
3125
+ setTimeout(() => {
3126
+ if (container.parentNode) {
3127
+ observer.observe(container.parentNode, { childList: true, subtree: true });
3128
+ }
3129
+ }, 0);
3130
+
2874
3131
  // Initialize
2875
- populateOptions();
2876
3132
  updateDisplay();
2877
3133
 
2878
3134
  return container;