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