lexgui 0.4.0 → 0.4.2

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/build/lexgui.js CHANGED
@@ -12,7 +12,7 @@ console.warn( 'Script _build/lexgui.js_ is depracated and will be removed soon.
12
12
  */
13
13
 
14
14
  var LX = {
15
- version: "0.4.0",
15
+ version: "0.4.2",
16
16
  ready: false,
17
17
  components: [], // Specific pre-build components
18
18
  signals: {}, // Events and triggers
@@ -24,13 +24,13 @@ LX.MOUSE_LEFT_CLICK = 0;
24
24
  LX.MOUSE_MIDDLE_CLICK = 1;
25
25
  LX.MOUSE_RIGHT_CLICK = 2;
26
26
 
27
- LX.MOUSE_DOUBLE_CLICK = 2;
28
- LX.MOUSE_TRIPLE_CLICK = 3;
27
+ LX.MOUSE_DOUBLE_CLICK = 2;
28
+ LX.MOUSE_TRIPLE_CLICK = 3;
29
29
 
30
- LX.CURVE_MOVEOUT_CLAMP = 0;
30
+ LX.CURVE_MOVEOUT_CLAMP = 0;
31
31
  LX.CURVE_MOVEOUT_DELETE = 1;
32
32
 
33
- LX.DRAGGABLE_Z_INDEX = 101;
33
+ LX.DRAGGABLE_Z_INDEX = 101;
34
34
 
35
35
  function clamp( num, min, max ) { return Math.min( Math.max( num, min ), max ); }
36
36
  function round( number, precision ) { return precision == 0 ? Math.floor( number ) : +(( number ).toFixed( precision ?? 2 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' )); }
@@ -169,7 +169,6 @@ function getThemeColor( colorName )
169
169
  {
170
170
  const r = getComputedStyle( document.querySelector( ':root' ) );
171
171
  const value = r.getPropertyValue( '--' + colorName );
172
- const theme = document.documentElement.getAttribute( "data-theme" );
173
172
 
174
173
  if( value.includes( "light-dark" ) )
175
174
  {
@@ -243,7 +242,7 @@ LX.rgbToHex = rgbToHex;
243
242
  /**
244
243
  * @method measureRealWidth
245
244
  * @description Measure the pixel width of a text
246
- * @param {Object} value Text to measure
245
+ * @param {Number} value Text to measure
247
246
  * @param {Number} paddingPlusMargin Padding offset
248
247
  */
249
248
  function measureRealWidth( value, paddingPlusMargin = 8 )
@@ -443,7 +442,7 @@ function makeCollapsible( domEl, content, parent, options = { } )
443
442
  actionIcon.dataset[ "collapsed" ] = collapsed;
444
443
  actionIcon.style.marginLeft = "auto";
445
444
 
446
- actionIcon.addEventListener( "click", function(e) {
445
+ actionIcon.addEventListener( "click", function( e ) {
447
446
  e.preventDefault();
448
447
  e.stopPropagation();
449
448
  if( this.dataset[ "collapsed" ] )
@@ -574,12 +573,13 @@ LX.makeCodeSnippet = makeCodeSnippet;
574
573
  * @description Gets an SVG element using one of LX.ICONS
575
574
  * @param {String} iconName
576
575
  * @param {String} iconTitle
576
+ * @param {String} extraClass
577
577
  */
578
- function makeIcon( iconName, iconTitle )
578
+ function makeIcon( iconName, iconTitle, extraClass = "" )
579
579
  {
580
580
  const icon = document.createElement( "a" );
581
581
  icon.title = iconTitle ?? "";
582
- icon.className = "lexicon";
582
+ icon.className = "lexicon " + extraClass;
583
583
  icon.innerHTML = LX.ICONS[ iconName ] ?? "";
584
584
  return icon;
585
585
  }
@@ -790,17 +790,17 @@ function _createCommandbar( root )
790
790
  searchItem.entry_name = t;
791
791
  searchItem.callback = c;
792
792
  searchItem.item = i;
793
- searchItem.addEventListener('click', function(e) {
793
+ searchItem.addEventListener('click', function( e ) {
794
794
  this.callback.call( window, this.entry_name );
795
795
  LX.setCommandbarState( false );
796
796
  _resetBar( true );
797
797
  });
798
- searchItem.addEventListener('mouseenter', function(e) {
798
+ searchItem.addEventListener('mouseenter', function( e ) {
799
799
  commandbar.querySelectorAll(".hovered").forEach(e => e.classList.remove('hovered'));
800
800
  this.classList.add('hovered');
801
801
  hoverElId = allItems.indexOf( this );
802
802
  });
803
- searchItem.addEventListener('mouseleave', function(e) {
803
+ searchItem.addEventListener('mouseleave', function( e ) {
804
804
  this.classList.remove('hovered');
805
805
  });
806
806
  allItems.push( searchItem );
@@ -881,7 +881,7 @@ function _createCommandbar( root )
881
881
  }
882
882
  }
883
883
 
884
- input.addEventListener('input', function(e) {
884
+ input.addEventListener('input', function( e ) {
885
885
  commandbar._addElements( this.value.toLowerCase() );
886
886
  });
887
887
 
@@ -930,7 +930,19 @@ function init( options = { } )
930
930
  this.container = document.getElementById( options.container );
931
931
  }
932
932
 
933
- document.documentElement.setAttribute( "data-strictVP", ( options.strictViewport ?? true ) ? "true" : "false" );
933
+ this.usingStrictViewport = options.strictViewport ?? true;
934
+ document.documentElement.setAttribute( "data-strictVP", ( this.usingStrictViewport ) ? "true" : "false" );
935
+
936
+ if( !this.usingStrictViewport )
937
+ {
938
+ document.addEventListener( "scroll", e => {
939
+ // Get all active menuboxes
940
+ const mbs = document.body.querySelectorAll( ".lexmenubox" );
941
+ mbs.forEach( ( mb ) => {
942
+ mb._updatePosition();
943
+ } );
944
+ } );
945
+ }
934
946
 
935
947
  this.commandbar = _createCommandbar( this.container );
936
948
 
@@ -1297,7 +1309,7 @@ LX.makeContainer = makeContainer;
1297
1309
 
1298
1310
  class IEvent {
1299
1311
 
1300
- constructor(name, value, domEvent) {
1312
+ constructor( name, value, domEvent ) {
1301
1313
  this.name = name;
1302
1314
  this.value = value;
1303
1315
  this.domEvent = domEvent;
@@ -2067,7 +2079,7 @@ class Area {
2067
2079
  }
2068
2080
 
2069
2081
  // Generate DOM elements after adding all entries
2070
- sidebar._build();
2082
+ sidebar.update();
2071
2083
 
2072
2084
  LX.menubars.push( sidebar );
2073
2085
 
@@ -2370,17 +2382,17 @@ class Tabs {
2370
2382
 
2371
2383
  let that = this;
2372
2384
 
2373
- container.addEventListener("dragenter", function(e) {
2385
+ container.addEventListener("dragenter", function( e ) {
2374
2386
  e.preventDefault(); // Prevent default action (open as link for some elements)
2375
2387
  this.classList.add("dockingtab");
2376
2388
  });
2377
2389
 
2378
- container.addEventListener("dragleave", function(e) {
2390
+ container.addEventListener("dragleave", function( e ) {
2379
2391
  e.preventDefault(); // Prevent default action (open as link for some elements)
2380
2392
  this.classList.remove("dockingtab");
2381
2393
  });
2382
2394
 
2383
- container.addEventListener("drop", function(e) {
2395
+ container.addEventListener("drop", function( e ) {
2384
2396
  e.preventDefault(); // Prevent default action (open as link for some elements)
2385
2397
 
2386
2398
  const tab_id = e.dataTransfer.getData("source");
@@ -2677,6 +2689,195 @@ class Menubar {
2677
2689
  this.shorts = { };
2678
2690
  }
2679
2691
 
2692
+ _resetMenubar() {
2693
+
2694
+ // Menu entries are in the menubar..
2695
+ this.root.querySelectorAll(".lexmenuentry").forEach( _entry => {
2696
+ _entry.classList.remove( 'selected' );
2697
+ _entry.built = false;
2698
+ } );
2699
+
2700
+ // Menuboxes are in the root area!
2701
+ LX.root.querySelectorAll(".lexmenubox").forEach(e => e.remove());
2702
+
2703
+ // Next time we need to click again
2704
+ this.focused = false;
2705
+ }
2706
+
2707
+ _createSubmenu( o, k, c, d ) {
2708
+
2709
+ let menuElement = document.createElement('div');
2710
+ menuElement.className = "lexmenubox";
2711
+ menuElement.tabIndex = "0";
2712
+ c.currentMenu = menuElement;
2713
+ menuElement.parentEntry = c;
2714
+
2715
+ const isSubMenu = c.classList.contains( "lexmenuboxentry" );
2716
+ if( isSubMenu )
2717
+ {
2718
+ menuElement.dataset[ "submenu" ] = true;
2719
+ }
2720
+
2721
+ menuElement._updatePosition = () => {
2722
+
2723
+ // Remove transitions for this change..
2724
+ const transition = menuElement.style.transition;
2725
+ menuElement.style.transition = "none";
2726
+ flushCss( menuElement );
2727
+
2728
+ doAsync( () => {
2729
+ let rect = c.getBoundingClientRect();
2730
+ rect.x += document.scrollingElement.scrollLeft;
2731
+ rect.y += document.scrollingElement.scrollTop;
2732
+ menuElement.style.left = ( isSubMenu ? ( rect.x + rect.width ) : rect.x ) + "px";
2733
+ menuElement.style.top = ( isSubMenu ? rect.y : ( ( rect.y + rect.height ) ) - 4 ) + "px";
2734
+
2735
+ menuElement.style.transition = transition;
2736
+ } );
2737
+ };
2738
+
2739
+ menuElement._updatePosition();
2740
+
2741
+ doAsync( () => {
2742
+ menuElement.dataset[ "open" ] = true;
2743
+ }, 10 );
2744
+
2745
+ LX.root.appendChild( menuElement );
2746
+
2747
+ for( var i = 0; i < o[ k ].length; ++i )
2748
+ {
2749
+ const subitem = o[ k ][ i ];
2750
+ const subkey = Object.keys( subitem )[ 0 ];
2751
+ const hasSubmenu = subitem[ subkey ].length;
2752
+ const isCheckbox = subitem[ 'type' ] == 'checkbox';
2753
+ let subentry = document.createElement('div');
2754
+ subentry.className = "lexmenuboxentry";
2755
+ subentry.className += (i == o[k].length - 1 ? " last" : "") + ( subitem.disabled ? " disabled" : "" );
2756
+
2757
+ if( subkey == '' )
2758
+ {
2759
+ subentry.className = " lexseparator";
2760
+ }
2761
+ else
2762
+ {
2763
+ subentry.id = subkey;
2764
+ let subentrycont = document.createElement('div');
2765
+ subentrycont.innerHTML = "";
2766
+ subentrycont.classList = "lexmenuboxentrycontainer";
2767
+ subentry.appendChild(subentrycont);
2768
+ const icon = this.icons[ subkey ];
2769
+ if( isCheckbox )
2770
+ {
2771
+ subentrycont.innerHTML += "<input type='checkbox' >";
2772
+ }
2773
+ else if( icon )
2774
+ {
2775
+ subentrycont.innerHTML += "<a class='" + icon + " fa-sm'></a>";
2776
+ }
2777
+ else
2778
+ {
2779
+ subentrycont.innerHTML += "<a class='fa-solid fa-sm noicon'></a>";
2780
+ subentrycont.classList.add( "noicon" );
2781
+
2782
+ }
2783
+ subentrycont.innerHTML += "<div class='lexentryname'>" + subkey + "</div>";
2784
+ }
2785
+
2786
+ let checkboxInput = subentry.querySelector('input');
2787
+ if( checkboxInput )
2788
+ {
2789
+ checkboxInput.checked = subitem.checked ?? false;
2790
+ checkboxInput.addEventListener('change', e => {
2791
+ subitem.checked = checkboxInput.checked;
2792
+ const f = subitem[ 'callback' ];
2793
+ if( f )
2794
+ {
2795
+ f.call( this, subitem.checked, subkey, subentry );
2796
+ this._resetMenubar();
2797
+ }
2798
+ e.stopPropagation();
2799
+ e.stopImmediatePropagation();
2800
+ })
2801
+ }
2802
+
2803
+ menuElement.appendChild( subentry );
2804
+
2805
+ // Nothing more for separators
2806
+ if( subkey == '' )
2807
+ {
2808
+ continue;
2809
+ }
2810
+
2811
+ menuElement.addEventListener('keydown', e => {
2812
+ e.preventDefault();
2813
+ let short = this.shorts[ subkey ];
2814
+ if(!short) return;
2815
+ // check if it's a letter or other key
2816
+ short = short.length == 1 ? short.toLowerCase() : short;
2817
+ if( short == e.key )
2818
+ {
2819
+ subentry.click()
2820
+ }
2821
+ });
2822
+
2823
+ // Add callback
2824
+ subentry.addEventListener("click", e => {
2825
+ if( checkboxInput )
2826
+ {
2827
+ subitem.checked = !subitem.checked;
2828
+ }
2829
+ const f = subitem[ 'callback' ];
2830
+ if( f )
2831
+ {
2832
+ f.call( this, checkboxInput ? subitem.checked : subkey, checkboxInput ? subkey : subentry );
2833
+ this._resetMenubar();
2834
+ }
2835
+ e.stopPropagation();
2836
+ e.stopImmediatePropagation();
2837
+ });
2838
+
2839
+ // Add icon if has submenu, else check for shortcut
2840
+ if( !hasSubmenu)
2841
+ {
2842
+ if( this.shorts[ subkey ] )
2843
+ {
2844
+ let shortEl = document.createElement('div');
2845
+ shortEl.className = "lexentryshort";
2846
+ shortEl.innerText = this.shorts[ subkey ];
2847
+ subentry.appendChild( shortEl );
2848
+ }
2849
+ continue;
2850
+ }
2851
+
2852
+ let submenuIcon = document.createElement('a');
2853
+ submenuIcon.className = "fa-solid fa-angle-right fa-xs";
2854
+ subentry.appendChild( submenuIcon );
2855
+
2856
+ subentry.addEventListener("mouseover", e => {
2857
+ if( subentry.built )
2858
+ {
2859
+ return;
2860
+ }
2861
+ subentry.built = true;
2862
+ this._createSubmenu( subitem, subkey, subentry, ++d );
2863
+ e.stopPropagation();
2864
+ });
2865
+
2866
+ subentry.addEventListener("mouseleave", e => {
2867
+ if( subentry.currentMenu && ( subentry.currentMenu != e.toElement ) )
2868
+ {
2869
+ d = -1; // Reset depth
2870
+ delete subentry.built;
2871
+ subentry.currentMenu.remove();
2872
+ delete subentry.currentMenu;
2873
+ }
2874
+ });
2875
+ }
2876
+
2877
+ // Set final width
2878
+ menuElement.style.width = menuElement.offsetWidth + "px";
2879
+ }
2880
+
2680
2881
  /**
2681
2882
  * @method add
2682
2883
  * @param {Object} options:
@@ -2701,7 +2902,6 @@ class Menubar {
2701
2902
  this.shorts[ lastPath ] = options.short;
2702
2903
 
2703
2904
  let idx = 0;
2704
- let that = this;
2705
2905
 
2706
2906
  const _insertEntry = ( token, list ) => {
2707
2907
  if( token == undefined )
@@ -2780,176 +2980,11 @@ class Menubar {
2780
2980
  }
2781
2981
  }
2782
2982
 
2783
- const _resetMenubar = function() {
2784
- // Menu entries are in the menubar..
2785
- that.root.querySelectorAll(".lexmenuentry").forEach( _entry => {
2786
- _entry.classList.remove( 'selected' );
2787
- _entry.built = false;
2788
- } );
2789
- // Menuboxes are in the root area!
2790
- LX.root.querySelectorAll(".lexmenubox").forEach(e => e.remove());
2791
- // Next time we need to click again
2792
- that.focused = false;
2793
- };
2794
-
2795
- const create_submenu = function( o, k, c, d ) {
2796
-
2797
- let menuElement = document.createElement('div');
2798
- menuElement.className = "lexmenubox";
2799
- menuElement.tabIndex = "0";
2800
- c.currentMenu = menuElement;
2801
- const isSubMenu = c.classList.contains( "lexmenuboxentry" );
2802
- if( isSubMenu ) menuElement.dataset[ "submenu" ] = true;
2803
- var rect = c.getBoundingClientRect();
2804
- menuElement.style.left = ( isSubMenu ? ( rect.x + rect.width ) : rect.left ) + "px";
2805
- menuElement.style.top = ( isSubMenu ? rect.y : rect.bottom - 4 ) + "px";
2806
- rect = menuElement.getBoundingClientRect();
2807
-
2808
- doAsync( () => {
2809
- menuElement.dataset[ "open" ] = true;
2810
- }, 10 );
2811
-
2812
- LX.root.appendChild( menuElement );
2813
-
2814
- for( var i = 0; i < o[ k ].length; ++i )
2815
- {
2816
- const subitem = o[ k ][ i ];
2817
- const subkey = Object.keys( subitem )[ 0 ];
2818
- const hasSubmenu = subitem[ subkey ].length;
2819
- const isCheckbox = subitem[ 'type' ] == 'checkbox';
2820
- let subentry = document.createElement('div');
2821
- subentry.className = "lexmenuboxentry";
2822
- subentry.className += (i == o[k].length - 1 ? " last" : "") + ( subitem.disabled ? " disabled" : "" );
2823
-
2824
- if( subkey == '' )
2825
- {
2826
- subentry.className = " lexseparator";
2827
- }
2828
- else
2829
- {
2830
- subentry.id = subkey;
2831
- let subentrycont = document.createElement('div');
2832
- subentrycont.innerHTML = "";
2833
- subentrycont.classList = "lexmenuboxentrycontainer";
2834
- subentry.appendChild(subentrycont);
2835
- const icon = that.icons[ subkey ];
2836
- if( isCheckbox )
2837
- {
2838
- subentrycont.innerHTML += "<input type='checkbox' >";
2839
- }
2840
- else if( icon )
2841
- {
2842
- subentrycont.innerHTML += "<a class='" + icon + " fa-sm'></a>";
2843
- }
2844
- else
2845
- {
2846
- subentrycont.innerHTML += "<a class='fa-solid fa-sm noicon'></a>";
2847
- subentrycont.classList.add( "noicon" );
2848
-
2849
- }
2850
- subentrycont.innerHTML += "<div class='lexentryname'>" + subkey + "</div>";
2851
- }
2852
-
2853
- let checkboxInput = subentry.querySelector('input');
2854
- if( checkboxInput )
2855
- {
2856
- checkboxInput.checked = subitem.checked ?? false;
2857
- checkboxInput.addEventListener('change', e => {
2858
- subitem.checked = checkboxInput.checked;
2859
- const f = subitem[ 'callback' ];
2860
- if( f )
2861
- {
2862
- f.call( this, subitem.checked, subkey, subentry );
2863
- _resetMenubar();
2864
- }
2865
- e.stopPropagation();
2866
- e.stopImmediatePropagation();
2867
- })
2868
- }
2869
-
2870
- menuElement.appendChild( subentry );
2871
-
2872
- // Nothing more for separators
2873
- if( subkey == '' )
2874
- {
2875
- continue;
2876
- }
2877
-
2878
- menuElement.addEventListener('keydown', function(e) {
2879
- e.preventDefault();
2880
- let short = that.shorts[ subkey ];
2881
- if(!short) return;
2882
- // check if it's a letter or other key
2883
- short = short.length == 1 ? short.toLowerCase() : short;
2884
- if( short == e.key )
2885
- {
2886
- subentry.click()
2887
- }
2888
- });
2889
-
2890
- // Add callback
2891
- subentry.addEventListener("click", e => {
2892
- if( checkboxInput )
2893
- {
2894
- subitem.checked = !subitem.checked;
2895
- }
2896
- const f = subitem[ 'callback' ];
2897
- if( f )
2898
- {
2899
- f.call( this, checkboxInput ? subitem.checked : subkey, checkboxInput ? subkey : subentry );
2900
- _resetMenubar();
2901
- }
2902
- e.stopPropagation();
2903
- e.stopImmediatePropagation();
2904
- });
2905
-
2906
- // Add icon if has submenu, else check for shortcut
2907
- if( !hasSubmenu)
2908
- {
2909
- if( that.shorts[ subkey ] )
2910
- {
2911
- let shortEl = document.createElement('div');
2912
- shortEl.className = "lexentryshort";
2913
- shortEl.innerText = that.shorts[ subkey ];
2914
- subentry.appendChild( shortEl );
2915
- }
2916
- continue;
2917
- }
2918
-
2919
- let submenuIcon = document.createElement('a');
2920
- submenuIcon.className = "fa-solid fa-angle-right fa-xs";
2921
- subentry.appendChild( submenuIcon );
2922
-
2923
- subentry.addEventListener("mouseover", e => {
2924
- if( subentry.built )
2925
- {
2926
- return;
2927
- }
2928
- subentry.built = true;
2929
- create_submenu( subitem, subkey, subentry, ++d );
2930
- e.stopPropagation();
2931
- });
2932
-
2933
- subentry.addEventListener("mouseleave", (e) => {
2934
- if( subentry.currentMenu && ( subentry.currentMenu != e.toElement ) )
2935
- {
2936
- d = -1; // Reset depth
2937
- delete subentry.built;
2938
- subentry.currentMenu.remove();
2939
- delete subentry.currentMenu;
2940
- }
2941
- });
2942
- }
2943
-
2944
- // Set final width
2945
- menuElement.style.width = menuElement.offsetWidth + "px";
2946
- };
2947
-
2948
2983
  const _showEntry = () => {
2949
- _resetMenubar();
2984
+ this._resetMenubar();
2950
2985
  entry.classList.add( "selected" );
2951
2986
  entry.built = true;
2952
- create_submenu( item, key, entry, -1 );
2987
+ this._createSubmenu( item, key, entry, -1 );
2953
2988
  };
2954
2989
 
2955
2990
  entry.addEventListener("click", () => {
@@ -2980,22 +3015,22 @@ class Menubar {
2980
3015
  return;
2981
3016
  }
2982
3017
 
2983
- _resetMenubar();
3018
+ this._resetMenubar();
2984
3019
  });
2985
3020
  }
2986
3021
  }
2987
3022
 
2988
3023
  /**
2989
3024
  * @method getButton
2990
- * @param {String} title
3025
+ * @param {String} name
2991
3026
  */
2992
3027
 
2993
- getButton( title ) {
2994
- return this.buttons[ title ];
3028
+ getButton( name ) {
3029
+ return this.buttons[ name ];
2995
3030
  }
2996
3031
 
2997
3032
  /**
2998
- * @method getSubitems: recursive method to find subentries of a menu entry
3033
+ * @method getSubitems
2999
3034
  * @param {Object} item: parent item
3000
3035
  * @param {Array} tokens: split path strings
3001
3036
  */
@@ -3028,6 +3063,7 @@ class Menubar {
3028
3063
  * @param {String} path
3029
3064
  */
3030
3065
  getItem( path ) {
3066
+
3031
3067
  // process path
3032
3068
  const tokens = path.split("/");
3033
3069
 
@@ -3036,100 +3072,118 @@ class Menubar {
3036
3072
 
3037
3073
  /**
3038
3074
  * @method setButtonIcon
3039
- * @param {String} title
3075
+ * @param {String} name
3040
3076
  * @param {String} icon
3077
+ * @param {Function} callback
3078
+ * @param {Object} options
3041
3079
  */
3042
3080
 
3043
- setButtonIcon( title, icon, callback, options = {} ) {
3081
+ setButtonIcon( name, icon, callback, options = {} ) {
3044
3082
 
3045
- const button = this.buttons[ title ];
3083
+ if( !name )
3084
+ {
3085
+ throw( "Set Button Name!" );
3086
+ }
3087
+
3088
+ let button = this.buttons[ name ];
3046
3089
  if( button )
3047
3090
  {
3048
3091
  button.querySelector('a').className = "fa-solid" + " " + icon + " lexicon";
3092
+ return;
3049
3093
  }
3050
- else
3094
+
3095
+ // Otherwise, create it
3096
+ button = document.createElement('div');
3097
+ const disabled = options.disabled ?? false;
3098
+ button.className = "lexmenubutton main" + (disabled ? " disabled" : "");
3099
+ button.title = name;
3100
+ button.innerHTML = "<a class='" + icon + " lexicon'></a>";
3101
+
3102
+ if( options.float == "right" )
3051
3103
  {
3052
- let button = document.createElement('div');
3053
- const disabled = options.disabled ?? false;
3054
- button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3055
- button.title = title ?? "";
3056
- button.innerHTML = "<a class='" + icon + " lexicon' style='font-size:x-large;'></a>";
3057
- button.style.padding = "5px 10px";
3058
- button.style.maxHeight = "calc(100% - 10px)";
3059
- button.style.alignItems = "center";
3104
+ button.right = true;
3105
+ }
3060
3106
 
3061
- if( options.float == "right" )
3062
- {
3063
- button.right = true;
3064
- }
3107
+ if( this.root.lastChild && this.root.lastChild.right )
3108
+ {
3109
+ this.root.lastChild.before( button );
3110
+ }
3111
+ else if( options.float == "left" )
3112
+ {
3113
+ this.root.prepend( button );
3114
+ }
3115
+ else
3116
+ {
3117
+ this.root.appendChild( button );
3118
+ }
3065
3119
 
3066
- if( this.root.lastChild && this.root.lastChild.right )
3067
- {
3068
- this.root.lastChild.before( button );
3069
- }
3070
- else if( options.float == "left" )
3120
+ const _b = button.querySelector('a');
3121
+ _b.addEventListener("click", (e) => {
3122
+ if( callback && !disabled )
3071
3123
  {
3072
- this.root.prepend( button );
3073
- }
3074
- else
3075
- {
3076
- this.root.appendChild( button );
3124
+ callback.call( this, _b, e );
3077
3125
  }
3126
+ });
3078
3127
 
3079
- const _b = button.querySelector('a');
3080
- _b.addEventListener("click", (e) => {
3081
- if(callback && !disabled)
3082
- callback.call( this, _b, e );
3083
- });
3084
- }
3128
+ this.buttons[ name ] = button;
3085
3129
  }
3086
3130
 
3087
3131
  /**
3088
3132
  * @method setButtonImage
3089
- * @param {String} title
3133
+ * @param {String} name
3090
3134
  * @param {String} src
3135
+ * @param {Function} callback
3136
+ * @param {Object} options
3091
3137
  */
3092
3138
 
3093
- setButtonImage( title, src, callback, options = {} ) {
3094
- const button = this.buttons[ title ];
3139
+ setButtonImage( name, src, callback, options = {} ) {
3140
+
3141
+ if( !name )
3142
+ {
3143
+ throw( "Set Button Name!" );
3144
+ }
3145
+
3146
+ let button = this.buttons[ name ];
3095
3147
  if( button )
3096
3148
  {
3097
- button.querySelector('a').className = "fa-solid" + " " + icon + " lexicon";
3149
+ button.querySelector('img').src = src;
3150
+ return;
3098
3151
  }
3099
- else
3152
+
3153
+ // Otherwise, create it
3154
+ button = document.createElement('div');
3155
+ const disabled = options.disabled ?? false;
3156
+ button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3157
+ button.title = name;
3158
+ button.innerHTML = "<a><image src='" + src + "' class='lexicon' style='height:32px;'></a>";
3159
+
3160
+ if( options.float == "right" )
3100
3161
  {
3101
- let button = document.createElement('div');
3102
- const disabled = options.disabled ?? false;
3103
- button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3104
- button.title = title ?? "";
3105
- button.innerHTML = "<a><image src='" + src + "' class='lexicon' style='height:32px;'></a>";
3106
- button.style.padding = "5px";
3107
- button.style.alignItems = "center";
3162
+ button.right = true;
3163
+ }
3108
3164
 
3109
- if( options.float == "right" )
3110
- {
3111
- button.right = true;
3112
- }
3165
+ if( this.root.lastChild && this.root.lastChild.right )
3166
+ {
3167
+ this.root.lastChild.before( button );
3168
+ }
3169
+ else if( options.float == "left" )
3170
+ {
3171
+ this.root.prepend( button );
3172
+ }
3173
+ else
3174
+ {
3175
+ this.root.appendChild( button );
3176
+ }
3113
3177
 
3114
- if( this.root.lastChild && this.root.lastChild.right )
3178
+ const _b = button.querySelector('a');
3179
+ _b.addEventListener("click", (e) => {
3180
+ if( callback && !disabled )
3115
3181
  {
3116
- this.root.lastChild.before( button );
3117
- }
3118
- else if( options.float == "left" )
3119
- {
3120
- this.root.prepend( button );
3121
- }
3122
- else
3123
- {
3124
- this.root.appendChild( button );
3182
+ callback.call( this, _b, e );
3125
3183
  }
3184
+ });
3126
3185
 
3127
- const _b = button.querySelector('a');
3128
- _b.addEventListener("click", (e) => {
3129
- if(callback && !disabled)
3130
- callback.call( this, _b, e );
3131
- });
3132
- }
3186
+ this.buttons[ name ] = button;
3133
3187
  }
3134
3188
 
3135
3189
  /**
@@ -3235,20 +3289,21 @@ class SideBar {
3235
3289
 
3236
3290
  /**
3237
3291
  * @param {Object} options
3238
- * inset: TODO
3239
- * filter: TODO
3292
+ * filter: Add search bar to filter entries [false]
3240
3293
  * skipHeader: Do not use sidebar header [false]
3241
3294
  * headerImg: Image to be shown as avatar
3242
3295
  * headerIcon: Icon to be shown as avatar (from LX.ICONS)
3243
- * headerTitle
3244
- * headerSubtitle
3296
+ * headerTitle: Header title
3297
+ * headerSubtitle: Header subtitle
3298
+ * header: HTMLElement to add a custom header
3245
3299
  * skipFooter: Do not use sidebar footer [false]
3246
3300
  * footerImg: Image to be shown as avatar
3247
3301
  * footerIcon: Icon to be shown as avatar (from LX.ICONS)
3248
- * footerTitle
3249
- * footerSubtitle
3302
+ * footerTitle: Footer title
3303
+ * footerSubtitle: Footer subtitle
3304
+ * footer: HTMLElement to add a custom footer
3250
3305
  * collapsable: Sidebar can toggle between collapsed/expanded [true]
3251
- * collapseToIcons: When Sidebar collapses, icons remains visible [true]
3306
+ * collapseToIcons: When Sidebar collapses, icons remains visible [true]
3252
3307
  * onHeaderPressed: Function to call when header is pressed
3253
3308
  * onFooterPressed: Function to call when footer is pressed
3254
3309
  */
@@ -3258,12 +3313,12 @@ class SideBar {
3258
3313
  this.root = document.createElement( 'div' );
3259
3314
  this.root.className = "lexsidebar";
3260
3315
 
3261
- window.sidebar = this;
3262
-
3263
3316
  this.collapsable = options.collapsable ?? true;
3264
- this.collapseWidth = ( options.collapseToIcons ?? true ) ? "58px" : "0px";
3317
+ this._collapseWidth = ( options.collapseToIcons ?? true ) ? "58px" : "0px";
3265
3318
  this.collapsed = false;
3266
3319
 
3320
+ this.filterString = "";
3321
+
3267
3322
  doAsync( () => {
3268
3323
 
3269
3324
  this.root.parentElement.ogWidth = this.root.parentElement.style.width;
@@ -3276,64 +3331,19 @@ class SideBar {
3276
3331
  }
3277
3332
  });
3278
3333
 
3279
- }, 100 );
3280
-
3281
- // This account for header, footer and all inner paddings
3282
- let contentOffset = 32;
3334
+ }, 10 );
3283
3335
 
3284
3336
  // Header
3285
3337
  if( !( options.skipHeader ?? false ) )
3286
3338
  {
3287
- this.header = document.createElement( 'div' );
3339
+ this.header = options.header ?? this._generateDefaultHeader( options );
3340
+ console.assert( this.header.constructor === HTMLDivElement, "Use an HTMLDivElement to build your custom header" );
3288
3341
  this.header.className = "lexsidebarheader";
3289
3342
  this.root.appendChild( this.header );
3290
3343
 
3291
- this.header.addEventListener( "click", e => {
3292
- if( this.collapsed )
3293
- {
3294
- e.preventDefault();
3295
- e.stopPropagation();
3296
- this.toggleCollapsed();
3297
- }
3298
- else if( options.onHeaderPressed )
3299
- {
3300
- options.onHeaderPressed( e );
3301
- }
3302
- } );
3303
-
3304
- const avatar = document.createElement( 'span' );
3305
- avatar.className = "lexavatar";
3306
- this.header.appendChild( avatar );
3307
-
3308
- if( options.headerImage )
3309
- {
3310
- const avatarImg = document.createElement( 'img' );
3311
- avatarImg.src = options.headerImage;
3312
- avatar.appendChild( avatarImg );
3313
- }
3314
- else if( options.headerIcon )
3315
- {
3316
- const avatarIcon = LX.makeIcon( options.headerIcon );
3317
- avatar.appendChild( avatarIcon );
3318
- }
3319
-
3320
- // Info
3321
- {
3322
- const info = document.createElement( 'div' );
3323
- this.header.appendChild( info );
3324
-
3325
- const infoText = document.createElement( 'span' );
3326
- infoText.innerHTML = options.headerTitle ?? "";
3327
- info.appendChild( infoText );
3328
-
3329
- const infoSubtext = document.createElement( 'span' );
3330
- infoSubtext.innerHTML = options.headerSubtitle ?? "";
3331
- info.appendChild( infoSubtext );
3332
- }
3333
-
3334
3344
  if( this.collapsable )
3335
3345
  {
3336
- const icon = LX.makeIcon( "Sidebar", "Toggle Sidebar" );
3346
+ const icon = LX.makeIcon( "Sidebar", "Toggle Sidebar", "toggler" );
3337
3347
  this.header.appendChild( icon );
3338
3348
 
3339
3349
  icon.addEventListener( "click", (e) => {
@@ -3342,8 +3352,18 @@ class SideBar {
3342
3352
  this.toggleCollapsed();
3343
3353
  } );
3344
3354
  }
3355
+ }
3345
3356
 
3346
- contentOffset += 52;
3357
+ // Entry filter
3358
+ if( ( options.filter ?? false ) )
3359
+ {
3360
+ const panel = new Panel();
3361
+ panel.addText(null, "", (value, event) => {
3362
+ this.filterString = value;
3363
+ this.update();
3364
+ }, { placeholder: "Search...", icon: "fa-solid fa-magnifying-glass" });
3365
+ this.filter = panel.root.childNodes[ 0 ];
3366
+ this.root.appendChild( this.filter );
3347
3367
  }
3348
3368
 
3349
3369
  // Content
@@ -3356,78 +3376,143 @@ class SideBar {
3356
3376
  // Footer
3357
3377
  if( !( options.skipFooter ?? false ) )
3358
3378
  {
3359
- this.footer = document.createElement( 'div' );
3379
+ this.footer = options.footer ?? this._generateDefaultFooter( options );
3380
+ console.assert( this.footer.constructor === HTMLDivElement, "Use an HTMLDivElement to build your custom footer" );
3360
3381
  this.footer.className = "lexsidebarfooter";
3361
3382
  this.root.appendChild( this.footer );
3383
+ }
3362
3384
 
3363
- this.footer.addEventListener( "click", e => {
3364
- if( options.onFooterPressed )
3365
- {
3366
- options.onFooterPressed( e );
3367
- }
3368
- } );
3385
+ // Set width depending on header/footer
3386
+ doAsync( () => {
3387
+ // This account for header, footer and all inner paddings
3388
+ const contentOffset = 32 + ( this.header?.offsetHeight ?? 0 ) +
3389
+ ( this.filter?.offsetHeight ?? 0 ) +
3390
+ ( this.footer?.offsetHeight ?? 0 );
3391
+ this.content.style.height = `calc(100% - ${ contentOffset }px)`;
3392
+ }, 10 );
3393
+
3394
+ this.items = [ ];
3395
+ this.icons = { };
3396
+ this.groups = { };
3397
+ }
3398
+
3399
+ _generateDefaultHeader( options ) {
3369
3400
 
3370
- const avatar = document.createElement( 'span' );
3371
- avatar.className = "lexavatar";
3372
- this.footer.appendChild( avatar );
3401
+ const header = document.createElement( 'div' );
3373
3402
 
3374
- if( options.footerImage )
3403
+ header.addEventListener( "click", e => {
3404
+ if( this.collapsed )
3375
3405
  {
3376
- const avatarImg = document.createElement( 'img' );
3377
- avatarImg.src = options.footerImage;
3378
- avatar.appendChild( avatarImg );
3406
+ e.preventDefault();
3407
+ e.stopPropagation();
3408
+ this.toggleCollapsed();
3379
3409
  }
3380
- else if( options.footerIcon )
3410
+ else if( options.onHeaderPressed )
3381
3411
  {
3382
- const avatarIcon = LX.makeIcon( options.footerIcon );
3383
- avatar.appendChild( avatarIcon );
3412
+ options.onHeaderPressed( e );
3384
3413
  }
3414
+ } );
3385
3415
 
3386
- // Info
3416
+ const avatar = document.createElement( 'span' );
3417
+ avatar.className = "lexavatar";
3418
+ header.appendChild( avatar );
3419
+
3420
+ if( options.headerImage )
3421
+ {
3422
+ const avatarImg = document.createElement( 'img' );
3423
+ avatarImg.src = options.headerImage;
3424
+ avatar.appendChild( avatarImg );
3425
+ }
3426
+ else if( options.headerIcon )
3427
+ {
3428
+ const avatarIcon = LX.makeIcon( options.headerIcon );
3429
+ avatar.appendChild( avatarIcon );
3430
+ }
3431
+
3432
+ // Info
3433
+ {
3434
+ const info = document.createElement( 'div' );
3435
+ info.className = "infodefault";
3436
+ header.appendChild( info );
3437
+
3438
+ const infoText = document.createElement( 'span' );
3439
+ infoText.innerHTML = options.headerTitle ?? "";
3440
+ info.appendChild( infoText );
3441
+
3442
+ const infoSubtext = document.createElement( 'span' );
3443
+ infoSubtext.innerHTML = options.headerSubtitle ?? "";
3444
+ info.appendChild( infoSubtext );
3445
+ }
3446
+
3447
+ return header;
3448
+ }
3449
+
3450
+ _generateDefaultFooter( options ) {
3451
+
3452
+ const footer = document.createElement( 'div' );
3453
+
3454
+ footer.addEventListener( "click", e => {
3455
+ if( options.onFooterPressed )
3387
3456
  {
3388
- const info = document.createElement( 'div' );
3389
- this.footer.appendChild( info );
3457
+ options.onFooterPressed( e );
3458
+ }
3459
+ } );
3390
3460
 
3391
- const infoText = document.createElement( 'span' );
3392
- infoText.innerHTML = options.footerTitle ?? "";
3393
- info.appendChild( infoText );
3461
+ const avatar = document.createElement( 'span' );
3462
+ avatar.className = "lexavatar";
3463
+ footer.appendChild( avatar );
3464
+
3465
+ if( options.footerImage )
3466
+ {
3467
+ const avatarImg = document.createElement( 'img' );
3468
+ avatarImg.src = options.footerImage;
3469
+ avatar.appendChild( avatarImg );
3470
+ }
3471
+ else if( options.footerIcon )
3472
+ {
3473
+ const avatarIcon = LX.makeIcon( options.footerIcon );
3474
+ avatar.appendChild( avatarIcon );
3475
+ }
3394
3476
 
3395
- const infoSubtext = document.createElement( 'span' );
3396
- infoSubtext.innerHTML = options.footerSubtitle ?? "";
3397
- info.appendChild( infoSubtext );
3398
- }
3477
+ // Info
3478
+ {
3479
+ const info = document.createElement( 'div' );
3480
+ info.className = "infodefault";
3481
+ footer.appendChild( info );
3399
3482
 
3400
- const icon = LX.makeIcon( "MenuArrows" );
3401
- this.footer.appendChild( icon );
3483
+ const infoText = document.createElement( 'span' );
3484
+ infoText.innerHTML = options.footerTitle ?? "";
3485
+ info.appendChild( infoText );
3402
3486
 
3403
- contentOffset += 52;
3487
+ const infoSubtext = document.createElement( 'span' );
3488
+ infoSubtext.innerHTML = options.footerSubtitle ?? "";
3489
+ info.appendChild( infoSubtext );
3404
3490
  }
3405
3491
 
3406
- // Set width depending on header/footer
3407
- this.content.style.height = `calc(100% - ${ contentOffset }px)`;
3492
+ const icon = LX.makeIcon( "MenuArrows" );
3493
+ footer.appendChild( icon );
3408
3494
 
3409
- this.items = [ ];
3410
- this.icons = { };
3411
- this.groups = { };
3495
+ return footer;
3412
3496
  }
3413
3497
 
3414
3498
  /**
3415
3499
  * @method toggleCollapsed
3500
+ * @param {Boolean} force: Force collapsed state
3416
3501
  */
3417
3502
 
3418
- toggleCollapsed() {
3503
+ toggleCollapsed( force ) {
3419
3504
 
3420
3505
  if( !this.collapsable )
3421
3506
  {
3422
3507
  return;
3423
3508
  }
3424
3509
 
3425
- this.collapsed = !this.collapsed;
3510
+ this.collapsed = force ?? !this.collapsed;
3426
3511
 
3427
3512
  if( this.collapsed )
3428
3513
  {
3429
3514
  this.root.classList.add( "collapsing" );
3430
- this.root.parentElement.style.width = this.collapseWidth;
3515
+ this.root.parentElement.style.width = this._collapseWidth;
3431
3516
  }
3432
3517
  else
3433
3518
  {
@@ -3436,6 +3521,11 @@ class SideBar {
3436
3521
  this.root.parentElement.style.width = this.root.parentElement.ogWidth;
3437
3522
  }
3438
3523
 
3524
+ if( !this.resizeObserver )
3525
+ {
3526
+ throw( "Wait until ResizeObserver has been created!" );
3527
+ }
3528
+
3439
3529
  this.resizeObserver.observe( this.root.parentElement );
3440
3530
 
3441
3531
  doAsync( () => {
@@ -3475,9 +3565,9 @@ class SideBar {
3475
3565
  * @param {String} path
3476
3566
  * @param {Object} options:
3477
3567
  * callback: Function to call on each item
3478
- * icon: Entry icon
3479
- * collapsable: Add entry as a collapsable section
3480
3568
  * className: Add class to the entry DOM element
3569
+ * collapsable: Add entry as a collapsable section
3570
+ * icon: Entry icon
3481
3571
  */
3482
3572
 
3483
3573
  add( path, options = {} ) {
@@ -3494,7 +3584,6 @@ class SideBar {
3494
3584
  const lastPath = tokens[tokens.length - 1];
3495
3585
  this.icons[ lastPath ] = options.icon;
3496
3586
 
3497
-
3498
3587
  let idx = 0;
3499
3588
 
3500
3589
  const _insertEntry = ( token, list ) => {
@@ -3549,10 +3638,19 @@ class SideBar {
3549
3638
  if( !entry )
3550
3639
  return;
3551
3640
 
3552
- entry.domEl.click();
3641
+ entry.dom.click();
3553
3642
  }
3554
3643
 
3555
- _build() {
3644
+ update() {
3645
+
3646
+ // Reset first
3647
+
3648
+ this.content.innerHTML = "";
3649
+
3650
+ for( let item of this.items )
3651
+ {
3652
+ delete item.dom;
3653
+ }
3556
3654
 
3557
3655
  for( let item of this.items )
3558
3656
  {
@@ -3565,12 +3663,18 @@ class SideBar {
3565
3663
  }
3566
3664
 
3567
3665
  let key = Object.keys( item )[ 0 ];
3666
+
3667
+ if( this.filterString.length && !key.toLowerCase().includes( this.filterString.toLowerCase() ) )
3668
+ {
3669
+ continue;
3670
+ }
3671
+
3568
3672
  let pKey = key.replace( /\s/g, '' ).replaceAll( '.', '' );
3569
3673
  let currentGroup = null;
3570
3674
 
3571
3675
  let entry = document.createElement( 'div' );
3572
3676
  entry.className = "lexsidebarentry " + ( options.className ?? "" );
3573
- entry.id = pKey;
3677
+ entry.id = item.name = pKey;
3574
3678
 
3575
3679
  if( item.group )
3576
3680
  {
@@ -3641,7 +3745,7 @@ class SideBar {
3641
3745
 
3642
3746
  let itemDom = document.createElement( 'div' );
3643
3747
  entry.appendChild( itemDom );
3644
- item.dom = itemDom;
3748
+ item.dom = entry;
3645
3749
 
3646
3750
  if( options.type == "checkbox" )
3647
3751
  {
@@ -3697,6 +3801,8 @@ class SideBar {
3697
3801
  entry.classList.add( "selected" );
3698
3802
  });
3699
3803
 
3804
+ const isCollapsable = options.collapsable != undefined ? options.collapsable : ( options.collapsable || item[ key ].length );
3805
+
3700
3806
  if( options.action )
3701
3807
  {
3702
3808
  const actionIcon = LX.makeIcon( options.action.icon ?? "MoreHorizontal", options.action.name );
@@ -3709,7 +3815,7 @@ class SideBar {
3709
3815
  if( f ) f.call( this, key, e );
3710
3816
  } );
3711
3817
  }
3712
- else if( options.collapsable )
3818
+ else if( isCollapsable )
3713
3819
  {
3714
3820
  const collapsableContent = document.createElement( 'div' );
3715
3821
  Object.assign( collapsableContent.style, { width: "100%", display: "none" } );
@@ -3744,7 +3850,12 @@ class SideBar {
3744
3850
  let subentryContainer = document.createElement( 'div' );
3745
3851
  subentryContainer.className = "lexsidebarsubentrycontainer";
3746
3852
 
3747
- if( currentGroup )
3853
+ if( isCollapsable )
3854
+ {
3855
+ this.collapseContainer.appendChild( subentryContainer )
3856
+ delete this.collapseContainer;
3857
+ }
3858
+ else if( currentGroup )
3748
3859
  {
3749
3860
  currentGroup.appendChild( subentryContainer );
3750
3861
  }
@@ -3753,12 +3864,17 @@ class SideBar {
3753
3864
  this.content.appendChild( subentryContainer );
3754
3865
  }
3755
3866
 
3756
- for( var i = 0; i < item[ key ].length; ++i )
3867
+ for( let i = 0; i < item[ key ].length; ++i )
3757
3868
  {
3758
3869
  const subitem = item[ key ][ i ];
3759
3870
  const suboptions = subitem.options ?? {};
3760
3871
  const subkey = Object.keys( subitem )[ 0 ];
3761
3872
 
3873
+ if( this.filterString.length && !subkey.toLowerCase().includes( this.filterString.toLowerCase() ) )
3874
+ {
3875
+ continue;
3876
+ }
3877
+
3762
3878
  let subentry = document.createElement( 'div' );
3763
3879
  subentry.innerHTML = `<span>${ subkey }</span>`;
3764
3880
 
@@ -3809,31 +3925,34 @@ class Widget {
3809
3925
  static CHECKBOX = 5;
3810
3926
  static TOGGLE = 6;
3811
3927
  static RADIO = 7;
3812
- static COLOR = 8;
3813
- static RANGE = 9;
3814
- static NUMBER = 10;
3815
- static TITLE = 11;
3816
- static VECTOR = 12;
3817
- static TREE = 13;
3818
- static PROGRESS = 14;
3819
- static FILE = 15;
3820
- static LAYERS = 16;
3821
- static ARRAY = 17;
3822
- static LIST = 18;
3823
- static TAGS = 19;
3824
- static CURVE = 20;
3825
- static CARD = 21;
3826
- static IMAGE = 22;
3827
- static CONTENT = 23;
3828
- static CUSTOM = 24;
3829
- static SEPARATOR = 25;
3830
- static KNOB = 26;
3831
- static SIZE = 27;
3832
- static PAD = 28;
3833
- static FORM = 29;
3834
- static DIAL = 30;
3835
- static COUNTER = 31;
3836
- static TABLE = 32;
3928
+ static BUTTONS = 8;
3929
+ static COLOR = 9;
3930
+ static RANGE = 10;
3931
+ static NUMBER = 11;
3932
+ static TITLE = 12;
3933
+ static VECTOR = 13;
3934
+ static TREE = 14;
3935
+ static PROGRESS = 15;
3936
+ static FILE = 16;
3937
+ static LAYERS = 17;
3938
+ static ARRAY = 18;
3939
+ static LIST = 19;
3940
+ static TAGS = 20;
3941
+ static CURVE = 21;
3942
+ static CARD = 22;
3943
+ static IMAGE = 23;
3944
+ static CONTENT = 24;
3945
+ static CUSTOM = 25;
3946
+ static SEPARATOR = 26;
3947
+ static KNOB = 27;
3948
+ static SIZE = 28;
3949
+ static PAD = 29;
3950
+ static FORM = 30;
3951
+ static DIAL = 31;
3952
+ static COUNTER = 32;
3953
+ static TABLE = 33;
3954
+ static TABS = 34;
3955
+ static BLANK = 35;
3837
3956
 
3838
3957
  static NO_CONTEXT_TYPES = [
3839
3958
  Widget.BUTTON,
@@ -3842,10 +3961,11 @@ class Widget {
3842
3961
  Widget.PROGRESS
3843
3962
  ];
3844
3963
 
3845
- constructor( name, type, options ) {
3964
+ constructor( name, type, value, options ) {
3846
3965
  this.name = name;
3847
3966
  this.type = type;
3848
3967
  this.options = options;
3968
+ this._initialValue = value;
3849
3969
  }
3850
3970
 
3851
3971
  value() {
@@ -3858,22 +3978,31 @@ class Widget {
3858
3978
  console.warn( "Can't get value of " + this.typeName() );
3859
3979
  }
3860
3980
 
3861
- set( value, skipCallback = false, signalName = "" ) {
3981
+ set( value, skipCallback, event ) {
3862
3982
 
3863
3983
  if( this.onSetValue )
3864
3984
  {
3865
- return this.onSetValue( value, skipCallback );
3985
+ let resetButton = this.domEl.querySelector( ".lexwidgetname .lexicon" );
3986
+ if( resetButton )
3987
+ {
3988
+ resetButton.style.display = ( value != this.value() ? "block" : "none" );
3989
+ resetButton.style.display = ( value != this._initialValue ? "block" : "none" );
3990
+ }
3991
+
3992
+ return this.onSetValue( value, skipCallback ?? false, event );
3866
3993
  }
3867
3994
 
3868
3995
  console.warn("Can't set value of " + this.typeName());
3869
3996
  }
3870
3997
 
3871
- oncontextmenu(e) {
3998
+ oncontextmenu( e ) {
3872
3999
 
3873
- if( Widget.NO_CONTEXT_TYPES.includes(this.type) )
4000
+ if( Widget.NO_CONTEXT_TYPES.includes( this.type ) )
4001
+ {
3874
4002
  return;
4003
+ }
3875
4004
 
3876
- addContextMenu(this.typeName(), e, c => {
4005
+ addContextMenu( this.typeName(), e, c => {
3877
4006
  c.add("Copy", () => { this.copy() });
3878
4007
  c.add("Paste", { disabled: !this._can_paste(), callback: () => { this.paste() } } );
3879
4008
  });
@@ -3930,6 +4059,8 @@ class Widget {
3930
4059
  case Widget.DIAL: return "Dial";
3931
4060
  case Widget.COUNTER: return "Counter";
3932
4061
  case Widget.TABLE: return "Table";
4062
+ case Widget.TABS: return "Tabs";
4063
+ case Widget.BLANK: return "Blank";
3933
4064
  case Widget.CUSTOM: return this.customName;
3934
4065
  }
3935
4066
 
@@ -3943,26 +4074,31 @@ class Widget {
3943
4074
 
3944
4075
  LX.Widget = Widget;
3945
4076
 
3946
- function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
4077
+ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
3947
4078
  {
3948
4079
  let custom_idx = simple_guidGenerator();
3949
4080
 
3950
- Panel.prototype[ 'add' + custom_widget_name ] = function( name, instance, callback ) {
4081
+ Panel.prototype[ 'add' + customWidgetName ] = function( name, instance, callback ) {
3951
4082
 
3952
- let widget = this.create_widget(name, Widget.CUSTOM, options);
3953
- widget.customName = custom_widget_name;
4083
+ let widget = this._createWidget( Widget.CUSTOM, name, null, options );
4084
+ widget.customName = customWidgetName;
3954
4085
  widget.customIdx = custom_idx;
4086
+
3955
4087
  widget.onGetValue = () => {
3956
4088
  return instance;
3957
4089
  };
3958
- widget.onSetValue = ( newValue, skipCallback ) => {
4090
+
4091
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
3959
4092
  instance = newValue;
3960
4093
  refresh_widget();
3961
4094
  element.querySelector( ".lexcustomitems" ).toggleAttribute( 'hidden', false );
3962
- if( !skipCallback ) this._trigger( new IEvent( name, instance, null ), callback );
4095
+ if( !skipCallback )
4096
+ {
4097
+ this._trigger( new IEvent( name, instance, event ), callback );
4098
+ }
3963
4099
  };
3964
4100
 
3965
- let element = widget.domEl;
4101
+ const element = widget.domEl;
3966
4102
  element.style.flexWrap = "wrap";
3967
4103
 
3968
4104
  let container, custom_widgets;
@@ -3972,8 +4108,10 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
3972
4108
 
3973
4109
  const refresh_widget = () => {
3974
4110
 
3975
- if(instance)
4111
+ if( instance )
4112
+ {
3976
4113
  widget.instance = instance = Object.assign(deepCopy(default_instance), instance);
4114
+ }
3977
4115
 
3978
4116
  if(container) container.remove();
3979
4117
  if(custom_widgets) custom_widgets.remove();
@@ -3985,7 +4123,7 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
3985
4123
  this.queue(container);
3986
4124
 
3987
4125
  let buttonName = "<a class='fa-solid " + (options.icon ?? "fa-cube") + "' style='float:left'></a>";
3988
- buttonName += custom_widget_name + (!instance ? " [empty]" : "");
4126
+ buttonName += customWidgetName + (!instance ? " [empty]" : "");
3989
4127
  // Add alwayis icon to keep spacing right
3990
4128
  buttonName += "<a class='fa-solid " + (instance ? "fa-bars-staggered" : " ") + " menu' style='float:right; width:5%;'></a>";
3991
4129
 
@@ -3997,7 +4135,7 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
3997
4135
  else
3998
4136
  {
3999
4137
  addContextMenu(null, event, c => {
4000
- c.add("New " + custom_widget_name, () => {
4138
+ c.add("New " + customWidgetName, () => {
4001
4139
  instance = {};
4002
4140
  refresh_widget();
4003
4141
  element.querySelector(".lexcustomitems").toggleAttribute('hidden', false);
@@ -4109,10 +4247,10 @@ class NodeTree {
4109
4247
  _create_item( parent, node, level = 0, selectedId ) {
4110
4248
 
4111
4249
  const that = this;
4112
- const node_filter_input = this.domEl.querySelector( "#lexnodetree_filter" );
4250
+ const nodeFilterInput = this.domEl.querySelector( "#lexnodetree_filter" );
4113
4251
 
4114
4252
  node.children = node.children ?? [];
4115
- if( node_filter_input && !node.id.includes( node_filter_input.value ) || (selectedId != undefined) && selectedId != node.id )
4253
+ if( nodeFilterInput && !node.id.includes( nodeFilterInput.value ) || (selectedId != undefined) && selectedId != node.id )
4116
4254
  {
4117
4255
  for( var i = 0; i < node.children.length; ++i )
4118
4256
  {
@@ -4352,7 +4490,7 @@ class NodeTree {
4352
4490
  const nameInput = document.createElement( "input" );
4353
4491
  nameInput.toggleAttribute( "hidden", !node.rename );
4354
4492
  nameInput.value = node.id;
4355
- item.appendChild(nameInput);
4493
+ item.appendChild( nameInput );
4356
4494
 
4357
4495
  if( node.rename )
4358
4496
  {
@@ -4360,7 +4498,7 @@ class NodeTree {
4360
4498
  nameInput.focus();
4361
4499
  }
4362
4500
 
4363
- nameInput.addEventListener("keyup", function(e){
4501
+ nameInput.addEventListener("keyup", function( e ) {
4364
4502
  if( e.key == "Enter" )
4365
4503
  {
4366
4504
  this.value = this.value.replace(/\s/g, '_');
@@ -4383,7 +4521,7 @@ class NodeTree {
4383
4521
  }
4384
4522
  });
4385
4523
 
4386
- nameInput.addEventListener("blur", function(e){
4524
+ nameInput.addEventListener("blur", function( e ) {
4387
4525
  delete node.rename;
4388
4526
  that.refresh();
4389
4527
  });
@@ -4391,7 +4529,7 @@ class NodeTree {
4391
4529
  if( this.options.draggable ?? true )
4392
4530
  {
4393
4531
  // Drag nodes
4394
- if(parent) // Root doesn't move!
4532
+ if( parent ) // Root doesn't move!
4395
4533
  {
4396
4534
  item.addEventListener("dragstart", e => {
4397
4535
  window.__tree_node_dragged = node;
@@ -4458,7 +4596,7 @@ class NodeTree {
4458
4596
  // Show/hide children
4459
4597
  if( isParent )
4460
4598
  {
4461
- item.querySelector('a.hierarchy').addEventListener("click", function(e) {
4599
+ item.querySelector('a.hierarchy').addEventListener("click", function( e ) {
4462
4600
 
4463
4601
  handled = true;
4464
4602
  e.stopImmediatePropagation();
@@ -4476,40 +4614,44 @@ class NodeTree {
4476
4614
 
4477
4615
  // Add button icons
4478
4616
 
4617
+ const inputContainer = document.createElement( "div" );
4618
+ item.appendChild( inputContainer );
4619
+
4620
+ if( node.actions )
4621
+ {
4622
+ for( let i = 0; i < node.actions.length; ++i )
4623
+ {
4624
+ let a = node.actions[ i ];
4625
+ let actionEl = document.createElement('a');
4626
+ actionEl.className = "lexicon " + a.icon;
4627
+ actionEl.title = a.name;
4628
+ actionEl.addEventListener("click", function( e ) {
4629
+ a.callback( node, actionEl );
4630
+ e.stopPropagation();
4631
+ });
4632
+
4633
+ inputContainer.appendChild( actionEl );
4634
+ }
4635
+ }
4636
+
4479
4637
  if( !node.skipVisibility ?? false )
4480
4638
  {
4481
- let visibility = document.createElement('a');
4482
- visibility.className = "itemicon fa-solid fa-eye" + (!node.visible ? "-slash" : "");
4639
+ let visibility = document.createElement( 'a' );
4640
+ visibility.className = "lexicon fa-solid fa-eye" + ( !node.visible ? "-slash" : "" );
4483
4641
  visibility.title = "Toggle visible";
4484
- visibility.addEventListener("click", function(e) {
4642
+ visibility.addEventListener("click", function( e ) {
4485
4643
  e.stopPropagation();
4486
4644
  node.visible = node.visible === undefined ? false : !node.visible;
4487
- this.className = "itemicon fa-solid fa-eye" + (!node.visible ? "-slash" : "");
4645
+ this.className = "lexicon fa-solid fa-eye" + ( !node.visible ? "-slash" : "" );
4488
4646
  // Trigger visibility event
4489
4647
  if( that.onevent )
4490
4648
  {
4491
- const event = new TreeEvent(TreeEvent.NODE_VISIBILITY, node, node.visible);
4649
+ const event = new TreeEvent( TreeEvent.NODE_VISIBILITY, node, node.visible );
4492
4650
  that.onevent( event );
4493
4651
  }
4494
4652
  });
4495
4653
 
4496
- item.appendChild(visibility);
4497
- }
4498
-
4499
- if( node.actions )
4500
- {
4501
- for( var i = 0; i < node.actions.length; ++i )
4502
- {
4503
- let a = node.actions[i];
4504
- var actionEl = document.createElement('a');
4505
- actionEl.className = "itemicon " + a.icon;
4506
- actionEl.title = a.name;
4507
- actionEl.addEventListener("click", function(e) {
4508
- a.callback(node, actionEl);
4509
- e.stopPropagation();
4510
- });
4511
- item.appendChild(actionEl);
4512
- }
4654
+ inputContainer.appendChild( visibility );
4513
4655
  }
4514
4656
 
4515
4657
  if( selectedId != undefined && node.id == selectedId )
@@ -4570,28 +4712,35 @@ class Panel {
4570
4712
  */
4571
4713
 
4572
4714
  constructor( options = {} ) {
4715
+
4573
4716
  var root = document.createElement('div');
4574
4717
  root.className = "lexpanel";
4718
+
4575
4719
  if( options.id )
4720
+ {
4576
4721
  root.id = options.id;
4722
+ }
4723
+
4577
4724
  if( options.className )
4725
+ {
4578
4726
  root.className += " " + options.className;
4727
+ }
4579
4728
 
4580
4729
  root.style.width = options.width || "calc( 100% - 6px )";
4581
4730
  root.style.height = options.height || "100%";
4582
- Object.assign(root.style, options.style ?? {});
4731
+ Object.assign( root.style, options.style ?? {} );
4583
4732
 
4584
4733
  this._inline_widgets_left = -1;
4585
4734
  this._inline_queued_container = null;
4586
4735
 
4587
4736
  this.root = root;
4588
4737
 
4589
- this.onevent = (e => {});
4738
+ this.onevent = ( e => {} );
4590
4739
 
4591
4740
  // branches
4592
- this.branch_open = false;
4741
+ this._branchOpen = false;
4593
4742
  this.branches = [];
4594
- this.current_branch = null;
4743
+ this._currentBranch = null;
4595
4744
  this.widgets = {};
4596
4745
  this._queue = []; // Append widgets in other locations
4597
4746
  }
@@ -4632,13 +4781,9 @@ class Panel {
4632
4781
 
4633
4782
  attach( content ) {
4634
4783
 
4635
- if(!content)
4636
- throw("no content to attach");
4637
-
4784
+ console.assert( content, "No content to attach!" );
4638
4785
  content.parent = this;
4639
- let element = content.root ? content.root : content;
4640
- //this.root.style.maxHeight = "800px"; // limit size when attaching stuff from outside
4641
- this.root.appendChild( element );
4786
+ this.root.appendChild( content.root ? content.root : content );
4642
4787
  }
4643
4788
 
4644
4789
  /**
@@ -4647,18 +4792,18 @@ class Panel {
4647
4792
 
4648
4793
  clear() {
4649
4794
 
4650
- this.branch_open = false;
4795
+ this._branchOpen = false;
4651
4796
  this.branches = [];
4652
- this.current_branch = null;
4797
+ this._currentBranch = null;
4653
4798
 
4654
4799
  for( let w in this.widgets )
4655
4800
  {
4656
- if( this.widgets[w].options && this.widgets[w].options.signal )
4801
+ if( this.widgets[ w ].options && this.widgets[ w ].options.signal )
4657
4802
  {
4658
- const signal = this.widgets[w].options.signal;
4803
+ const signal = this.widgets[ w ].options.signal;
4659
4804
  for( let i = 0; i < LX.signals[signal].length; i++ )
4660
4805
  {
4661
- if( LX.signals[signal][i] == this.widgets[w] )
4806
+ if( LX.signals[signal][i] == this.widgets[ w ] )
4662
4807
  {
4663
4808
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
4664
4809
  }
@@ -4670,7 +4815,7 @@ class Panel {
4670
4815
  {
4671
4816
  for( let w = 0; w < this.signals.length; w++ )
4672
4817
  {
4673
- let widget = Object.values(this.signals[w])[0];
4818
+ let widget = Object.values(this.signals[ w ])[0];
4674
4819
  let signal = widget.options.signal;
4675
4820
  for( let i = 0; i < LX.signals[signal].length; i++ )
4676
4821
  {
@@ -4695,7 +4840,7 @@ class Panel {
4695
4840
  sameLine( number ) {
4696
4841
 
4697
4842
  this._inline_queued_container = this.queuedContainer;
4698
- this._inline_widgets_left = number || Infinity;
4843
+ this._inline_widgets_left = ( number || Infinity );
4699
4844
  }
4700
4845
 
4701
4846
  /**
@@ -4705,7 +4850,7 @@ class Panel {
4705
4850
 
4706
4851
  endLine( justifyContent ) {
4707
4852
 
4708
- if( this._inline_widgets_left == -1)
4853
+ if( this._inline_widgets_left == -1 )
4709
4854
  {
4710
4855
  console.warn("No pending widgets to be inlined!");
4711
4856
  return;
@@ -4727,27 +4872,37 @@ class Panel {
4727
4872
  // Push all elements single element or Array[element, container]
4728
4873
  for( let item of this._inlineWidgets )
4729
4874
  {
4730
- const is_pair = item.constructor == Array;
4875
+ const isPair = ( item.constructor == Array );
4731
4876
 
4732
- if(is_pair)
4877
+ if( isPair )
4733
4878
  {
4734
4879
  // eg. an array, inline items appended later to
4735
- if( this._inline_queued_container)
4736
- this._inlineContainer.appendChild( item[0] );
4880
+ if( this._inline_queued_container )
4881
+ {
4882
+ this._inlineContainer.appendChild( item[ 0 ] );
4883
+ }
4737
4884
  // eg. a dropdown, item is appended to parent, not to inline cont.
4738
4885
  else
4739
- item[1].appendChild(item[0]);
4886
+ {
4887
+ item[ 1 ].appendChild( item[ 0 ] );
4888
+ }
4740
4889
  }
4741
4890
  else
4891
+ {
4742
4892
  this._inlineContainer.appendChild( item );
4893
+ }
4743
4894
  }
4744
4895
 
4745
- if(!this._inline_queued_container)
4896
+ if( !this._inline_queued_container )
4746
4897
  {
4747
- if( this.current_branch)
4748
- this.current_branch.content.appendChild( this._inlineContainer );
4898
+ if( this._currentBranch )
4899
+ {
4900
+ this._currentBranch.content.appendChild( this._inlineContainer );
4901
+ }
4749
4902
  else
4903
+ {
4750
4904
  this.root.appendChild( this._inlineContainer );
4905
+ }
4751
4906
  }
4752
4907
  else
4753
4908
  {
@@ -4771,20 +4926,24 @@ class Panel {
4771
4926
 
4772
4927
  branch( name, options = {} ) {
4773
4928
 
4774
- if( this.branch_open )
4929
+ if( this._branchOpen )
4930
+ {
4775
4931
  this.merge();
4932
+ }
4776
4933
 
4777
4934
  // Create new branch
4778
- var branch = new Branch(name, options);
4935
+ var branch = new Branch( name, options );
4779
4936
  branch.panel = this;
4780
4937
 
4781
4938
  // Declare new open
4782
- this.branch_open = true;
4783
- this.current_branch = branch;
4939
+ this._branchOpen = true;
4940
+ this._currentBranch = branch;
4784
4941
 
4785
4942
  // Append to panel
4786
- if( this.branches.length == 0)
4943
+ if( this.branches.length == 0 )
4944
+ {
4787
4945
  branch.root.classList.add('first');
4946
+ }
4788
4947
 
4789
4948
  // This is the last!
4790
4949
  this.root.querySelectorAll(".lexbranch.last").forEach( e => { e.classList.remove("last"); } );
@@ -4796,16 +4955,15 @@ class Panel {
4796
4955
  // Add widget filter
4797
4956
  if( options.filter )
4798
4957
  {
4799
- this._addFilter( options.filter, {callback: this._searchWidgets.bind(this, branch.name)} );
4958
+ this._addFilter( options.filter, { callback: this._searchWidgets.bind( this, branch.name ) } );
4800
4959
  }
4801
4960
 
4802
4961
  return branch;
4803
4962
  }
4804
4963
 
4805
4964
  merge() {
4806
-
4807
- this.branch_open = false;
4808
- this.current_branch = null;
4965
+ this._branchOpen = false;
4966
+ this._currentBranch = null;
4809
4967
  }
4810
4968
 
4811
4969
  _pick( arg, def ) {
@@ -4831,9 +4989,16 @@ class Panel {
4831
4989
  Panel Widgets
4832
4990
  */
4833
4991
 
4834
- create_widget( name, type, options = {} ) {
4992
+ _createWidget( type, name, value, options = {} ) {
4993
+
4994
+ if( !LX.CREATED_INSTANCES )
4995
+ {
4996
+ LX.CREATED_INSTANCES = [];
4997
+ }
4835
4998
 
4836
- let widget = new Widget( name, type, options );
4999
+ LX.CREATED_INSTANCES[ type ] = true;
5000
+
5001
+ let widget = new Widget( name, type, value, options );
4837
5002
 
4838
5003
  let element = document.createElement( 'div' );
4839
5004
  element.className = "lexwidget";
@@ -4847,7 +5012,7 @@ class Panel {
4847
5012
 
4848
5013
  if( type != Widget.TITLE )
4849
5014
  {
4850
- element.style.width = "calc(100% - " + (this.current_branch || type == Widget.FILE ? 10 : 20) + "px)";
5015
+ element.style.width = "calc(100% - " + (this._currentBranch || type == Widget.FILE || type == Widget.TREE ? 10 : 20) + "px)";
4851
5016
 
4852
5017
  if( options.width )
4853
5018
  {
@@ -4869,7 +5034,7 @@ class Panel {
4869
5034
 
4870
5035
  if( name != undefined )
4871
5036
  {
4872
- if( !(options.hideName ?? false) )
5037
+ if( !( options.hideName ?? false ) )
4873
5038
  {
4874
5039
  let domName = document.createElement( 'div' );
4875
5040
  domName.className = "lexwidgetname";
@@ -4877,10 +5042,10 @@ class Panel {
4877
5042
  {
4878
5043
  domName.classList.add( "float-" + options.justifyName );
4879
5044
  }
4880
- domName.innerHTML = name || "";
5045
+ domName.innerHTML = name;
4881
5046
  domName.title = options.title ?? domName.innerHTML;
4882
5047
  domName.style.width = options.nameWidth || LX.DEFAULT_NAME_WIDTH;
4883
- element.appendChild(domName);
5048
+ element.appendChild( domName );
4884
5049
  element.domName = domName;
4885
5050
 
4886
5051
  // Copy-paste info
@@ -4888,10 +5053,23 @@ class Panel {
4888
5053
  e.preventDefault();
4889
5054
  widget.oncontextmenu( e );
4890
5055
  });
5056
+
5057
+ if( !( options.skipReset ?? false ) && ( value != null ) )
5058
+ {
5059
+ Panel._add_reset_property( domName, function( e ) {
5060
+ widget.set( widget._initialValue, false, e );
5061
+ // Og value, don't show it
5062
+ this.style.display = "none";
5063
+ });
5064
+ }
4891
5065
  }
4892
5066
 
4893
5067
  this.widgets[ name ] = widget;
4894
5068
  }
5069
+ else
5070
+ {
5071
+ options.hideName = true;
5072
+ }
4895
5073
 
4896
5074
  if( options.signal )
4897
5075
  {
@@ -4918,13 +5096,13 @@ class Panel {
4918
5096
  }
4919
5097
  else if( !this.queuedContainer )
4920
5098
  {
4921
- if( this.current_branch )
5099
+ if( this._currentBranch )
4922
5100
  {
4923
5101
  if( !options.skipWidget )
4924
5102
  {
4925
- this.current_branch.widgets.push( widget );
5103
+ this._currentBranch.widgets.push( widget );
4926
5104
  }
4927
- this.current_branch.content.appendChild( el );
5105
+ this._currentBranch.content.appendChild( el );
4928
5106
  }
4929
5107
  else
4930
5108
  {
@@ -4985,30 +5163,32 @@ class Panel {
4985
5163
  options.skipWidget = options.skipWidget ?? true;
4986
5164
  options.skipInlineCount = true;
4987
5165
 
4988
- let widget = this.create_widget(null, Widget.TEXT, options);
4989
- let element = widget.domEl;
5166
+ let widget = this._createWidget( Widget.TEXT, null, null, options );
5167
+ const element = widget.domEl;
4990
5168
  element.className += " lexfilter noname";
4991
5169
 
4992
5170
  let input = document.createElement('input');
4993
5171
  input.className = 'lexinput-filter';
4994
- input.setAttribute("placeholder", options.placeholder);
5172
+ input.setAttribute( "placeholder", options.placeholder );
4995
5173
  input.style.width = "calc( 100% - 17px )";
4996
5174
  input.value = options.filterValue || "";
4997
5175
 
4998
5176
  let searchIcon = document.createElement('a');
4999
5177
  searchIcon.className = "fa-solid fa-magnifying-glass";
5000
- element.appendChild(searchIcon);
5001
- element.appendChild(input);
5178
+ element.appendChild( searchIcon );
5179
+ element.appendChild( input );
5002
5180
 
5003
5181
  input.addEventListener("input", (e) => {
5004
- if(options.callback)
5005
- options.callback(input.value, e);
5182
+ if( options.callback )
5183
+ {
5184
+ options.callback( input.value, e );
5185
+ }
5006
5186
  });
5007
5187
 
5008
5188
  return element;
5009
5189
  }
5010
5190
 
5011
- _searchWidgets(branchName, value) {
5191
+ _searchWidgets( branchName, value ) {
5012
5192
 
5013
5193
  for( let b of this.branches )
5014
5194
  {
@@ -5082,10 +5262,14 @@ class Panel {
5082
5262
  _trigger( event, callback ) {
5083
5263
 
5084
5264
  if( callback )
5265
+ {
5085
5266
  callback.call( this, event.value, event.domEvent, event.name );
5267
+ }
5086
5268
 
5087
5269
  if( this.onevent )
5270
+ {
5088
5271
  this.onevent.call( this, event );
5272
+ }
5089
5273
  }
5090
5274
 
5091
5275
  /**
@@ -5100,7 +5284,7 @@ class Panel {
5100
5284
  return this.branches.find( b => b.name == name );
5101
5285
  }
5102
5286
 
5103
- return this.current_branch;
5287
+ return this._currentBranch;
5104
5288
  }
5105
5289
 
5106
5290
  /**
@@ -5110,9 +5294,9 @@ class Panel {
5110
5294
 
5111
5295
  queue( domEl ) {
5112
5296
 
5113
- if( !domEl && this.current_branch)
5297
+ if( !domEl && this._currentBranch)
5114
5298
  {
5115
- domEl = this.current_branch.root;
5299
+ domEl = this._currentBranch.root;
5116
5300
  }
5117
5301
 
5118
5302
  if( this.queuedContainer )
@@ -5145,12 +5329,14 @@ class Panel {
5145
5329
 
5146
5330
  addBlank( height = 8, width ) {
5147
5331
 
5148
- let widget = this.create_widget(null, Widget.addBlank);
5332
+ let widget = this._createWidget( Widget.BLANK );
5149
5333
  widget.domEl.className += " blank";
5150
5334
  widget.domEl.style.height = height + "px";
5151
5335
 
5152
- if(width)
5336
+ if( width )
5337
+ {
5153
5338
  widget.domEl.style.width = width;
5339
+ }
5154
5340
 
5155
5341
  return widget;
5156
5342
  }
@@ -5168,13 +5354,12 @@ class Panel {
5168
5354
 
5169
5355
  addTitle( name, options = {} ) {
5170
5356
 
5171
- if( !name )
5172
- {
5173
- throw( "Can't create Title without text!" );
5174
- }
5357
+ console.assert( name, "Can't create Title Widget without text!" );
5175
5358
 
5176
- let widget = this.create_widget( null, Widget.TITLE, options );
5177
- let element = widget.domEl;
5359
+ // Note: Titles are not registered in Panel.widgets by now
5360
+ let widget = this._createWidget( Widget.TITLE, null, null, options );
5361
+
5362
+ const element = widget.domEl;
5178
5363
  element.className = "lextitle";
5179
5364
 
5180
5365
  if( options.icon )
@@ -5185,7 +5370,7 @@ class Panel {
5185
5370
  element.appendChild( icon );
5186
5371
  }
5187
5372
 
5188
- let text = document.createElement( "span");
5373
+ let text = document.createElement( "span" );
5189
5374
  text.innerText = name;
5190
5375
  element.appendChild( text );
5191
5376
 
@@ -5211,6 +5396,7 @@ class Panel {
5211
5396
  * @param {String} value Text value
5212
5397
  * @param {Function} callback Callback function on change
5213
5398
  * @param {*} options:
5399
+ * hideName: Don't use name as label [false]
5214
5400
  * disabled: Make the widget disabled [false]
5215
5401
  * required: Make the input required
5216
5402
  * placeholder: Add input placeholder
@@ -5224,33 +5410,43 @@ class Panel {
5224
5410
 
5225
5411
  addText( name, value, callback, options = {} ) {
5226
5412
 
5227
- let widget = this.create_widget( name, Widget.TEXT, options );
5413
+ let widget = this._createWidget( Widget.TEXT, name, String( value ), options );
5228
5414
 
5229
5415
  widget.onGetValue = () => {
5230
- return wValue.value;
5416
+ return value;
5231
5417
  };
5232
5418
 
5233
- widget.onSetValue = ( newValue, skipCallback ) => {
5234
- this.disabled ? wValue.innerText = newValue : wValue.value = newValue;
5235
- Panel._dispatch_event( wValue, "focusout", skipCallback );
5419
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5420
+
5421
+ if( !widget.valid( newValue ) || ( this._lastValueTriggered == newValue ) )
5422
+ {
5423
+ return;
5424
+ }
5425
+
5426
+ this._lastValueTriggered = value = newValue;
5427
+
5428
+ if( options.disabled )
5429
+ {
5430
+ wValue.innerText = newValue;
5431
+ }
5432
+ else
5433
+ {
5434
+ wValue.value = newValue;
5435
+ }
5436
+
5437
+ if( !skipCallback )
5438
+ {
5439
+ this._trigger( new IEvent( name, newValue, event ), callback );
5440
+ }
5236
5441
  };
5237
5442
 
5238
- widget.valid = () => {
5239
- if( wValue.pattern == "" ) { return true; }
5443
+ widget.valid = ( v ) => {
5444
+ if( !v.length || wValue.pattern == "" ) return true;
5240
5445
  const regexp = new RegExp( wValue.pattern );
5241
- return regexp.test( wValue.value );
5446
+ return regexp.test( v );
5242
5447
  };
5243
5448
 
5244
- let element = widget.domEl;
5245
-
5246
- // Add reset functionality
5247
- if( widget.name && !( options.skipReset ?? false ) ) {
5248
- Panel._add_reset_property( element.domName, function() {
5249
- wValue.value = wValue.iValue;
5250
- this.style.display = "none";
5251
- Panel._dispatch_event( wValue, "focusout" );
5252
- } );
5253
- }
5449
+ const element = widget.domEl;
5254
5450
 
5255
5451
  // Add widget value
5256
5452
 
@@ -5273,7 +5469,7 @@ class Panel {
5273
5469
  wValue.type = options.type || "";
5274
5470
  wValue.value = wValue.iValue = value || "";
5275
5471
  wValue.style.width = "100%";
5276
- wValue.style.textAlign = options.float ?? "";
5472
+ wValue.style.textAlign = ( options.float ?? "" );
5277
5473
 
5278
5474
  wValue.setAttribute( "placeholder", options.placeholder ?? "" );
5279
5475
 
@@ -5287,39 +5483,25 @@ class Panel {
5287
5483
  wValue.setAttribute( "pattern", options.pattern );
5288
5484
  }
5289
5485
 
5290
- var resolve = ( function( val, event ) {
5291
-
5292
- if( !widget.valid() )
5293
- {
5294
- return;
5295
- }
5296
-
5297
- const skipCallback = event.detail;
5298
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5299
- if( btn ) btn.style.display = ( val != wValue.iValue ? "block" : "none" );
5300
- if( !skipCallback )
5301
- {
5302
- this._trigger( new IEvent( name, val, event ), callback );
5303
- }
5304
-
5305
- }).bind( this );
5306
-
5307
- const trigger = options.trigger ?? 'default';
5486
+ const trigger = options.trigger ?? "default";
5308
5487
 
5309
- if( trigger == 'default' )
5488
+ if( trigger == "default" )
5310
5489
  {
5311
5490
  wValue.addEventListener( "keyup", function( e ){
5312
- if(e.key == 'Enter')
5313
- resolve( e.target.value, e );
5491
+ if( e.key == "Enter" )
5492
+ {
5493
+ wValue.blur();
5494
+ }
5314
5495
  });
5496
+
5315
5497
  wValue.addEventListener( "focusout", function( e ){
5316
- resolve( e.target.value, e );
5498
+ widget.set( e.target.value, false, e );
5317
5499
  });
5318
5500
  }
5319
- else if( trigger == 'input' )
5501
+ else if( trigger == "input" )
5320
5502
  {
5321
5503
  wValue.addEventListener("input", function( e ){
5322
- resolve( e.target.value, e );
5504
+ widget.set( e.target.value, false, e );
5323
5505
  });
5324
5506
  }
5325
5507
 
@@ -5335,14 +5517,17 @@ class Panel {
5335
5517
  container.appendChild( icon );
5336
5518
  }
5337
5519
 
5338
- } else
5520
+ }
5521
+ else
5339
5522
  {
5340
5523
  wValue = document.createElement( options.url ? 'a' : 'div' );
5524
+
5341
5525
  if( options.url )
5342
5526
  {
5343
5527
  wValue.href = options.url;
5344
5528
  wValue.target = "_blank";
5345
5529
  }
5530
+
5346
5531
  const icon = options.warning ? '<i class="fa-solid fa-triangle-exclamation"></i>' : '';
5347
5532
  wValue.innerHTML = ( icon + value ) || "";
5348
5533
  wValue.style.width = "100%";
@@ -5355,7 +5540,8 @@ class Panel {
5355
5540
  element.appendChild( container );
5356
5541
 
5357
5542
  // Remove branch padding and margins
5358
- if( !widget.name )
5543
+ const useNameAsLabel = !( options.hideName ?? false );
5544
+ if( !useNameAsLabel )
5359
5545
  {
5360
5546
  element.className += " noname";
5361
5547
  container.style.width = "100%";
@@ -5369,7 +5555,8 @@ class Panel {
5369
5555
  * @param {String} name Widget name
5370
5556
  * @param {String} value Text Area value
5371
5557
  * @param {Function} callback Callback function on change
5372
- * @param {*} options:
5558
+ * @param {Object} options:
5559
+ * hideName: Don't use name as label [false]
5373
5560
  * disabled: Make the widget disabled [false]
5374
5561
  * placeholder: Add input placeholder
5375
5562
  * trigger: Choose onchange trigger (default, input) [default]
@@ -5381,82 +5568,76 @@ class Panel {
5381
5568
 
5382
5569
  addTextArea( name, value, callback, options = {} ) {
5383
5570
 
5384
- let widget = this.create_widget( name, Widget.TEXTAREA, options );
5571
+ let widget = this._createWidget( Widget.TEXTAREA, name, value, options );
5385
5572
 
5386
5573
  widget.onGetValue = () => {
5387
- return wValue.value;
5388
- };
5389
- widget.onSetValue = ( newValue, skipCallback ) => {
5390
- wValue.value = newValue;
5391
- Panel._dispatch_event( wValue, "focusout", skipCallback );
5574
+ return value;
5392
5575
  };
5393
5576
 
5394
- let element = widget.domEl;
5577
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5395
5578
 
5396
- // Add reset functionality
5397
- if( widget.name && !( options.skipReset ?? false ) ) {
5398
- Panel._add_reset_property( element.domName, function() {
5399
- wValue.value = wValue.iValue;
5400
- this.style.display = "none";
5401
- Panel._dispatch_event( wValue, "focusout" );
5402
- });
5403
- }
5579
+ wValue.value = value = newValue;
5580
+
5581
+ if( !skipCallback )
5582
+ {
5583
+ this._trigger( new IEvent( name, newValue, event ), callback );
5584
+ }
5585
+ };
5586
+
5587
+ const element = widget.domEl;
5404
5588
 
5405
5589
  // Add widget value
5406
5590
 
5407
- let container = document.createElement( 'div' );
5591
+ let container = document.createElement( "div" );
5408
5592
  container.className = "lextextarea";
5409
5593
  container.style.width = options.inputWidth || "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + " )";
5410
5594
  container.style.height = options.height;
5411
5595
  container.style.display = "flex";
5412
5596
 
5413
- let wValue = document.createElement( 'textarea' );
5597
+ let wValue = document.createElement( "textarea" );
5414
5598
  wValue.value = wValue.iValue = value || "";
5415
5599
  wValue.style.width = "100%";
5416
5600
  wValue.style.textAlign = options.float ?? "";
5417
5601
  Object.assign( wValue.style, options.style ?? {} );
5418
5602
 
5419
- if( options.disabled ?? false ) wValue.setAttribute("disabled", true);
5420
- if( options.placeholder ) wValue.setAttribute("placeholder", options.placeholder);
5603
+ if( options.disabled ?? false ) wValue.setAttribute( "disabled", true );
5604
+ if( options.placeholder ) wValue.setAttribute( "placeholder", options.placeholder );
5421
5605
 
5422
- var resolve = (function( val, event ) {
5423
- const skipCallback = event.detail;
5424
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5425
- if( btn ) btn.style.display = ( val != wValue.iValue ? "block" : "none" );
5426
- if( !skipCallback ) this._trigger( new IEvent( name, val, event ), callback );
5427
- }).bind(this);
5606
+ const trigger = options.trigger ?? "default";
5428
5607
 
5429
- const trigger = options.trigger ?? 'default';
5430
-
5431
- if(trigger == 'default')
5608
+ if( trigger == "default" )
5432
5609
  {
5433
- wValue.addEventListener("keyup", function(e){
5434
- if(e.key == 'Enter')
5435
- resolve(e.target.value, e);
5610
+ wValue.addEventListener("keyup", function( e ) {
5611
+ if( e.key == "Enter" )
5612
+ {
5613
+ wValue.blur();
5614
+ }
5436
5615
  });
5437
- wValue.addEventListener("focusout", function(e){
5438
- resolve(e.target.value, e);
5616
+
5617
+ wValue.addEventListener("focusout", function( e ) {
5618
+ widget.set( e.target.value, false, e );
5439
5619
  });
5440
5620
  }
5441
- else if(trigger == 'input')
5621
+ else if( trigger == "input" )
5442
5622
  {
5443
- wValue.addEventListener("input", function(e){
5444
- resolve(e.target.value, e);
5623
+ wValue.addEventListener("input", function( e ) {
5624
+ widget.set( e.target.value, false, e );
5445
5625
  });
5446
5626
  }
5447
5627
 
5448
- if(options.icon)
5628
+ if( options.icon )
5449
5629
  {
5450
5630
  let icon = document.createElement('a');
5451
5631
  icon.className = "inputicon " + options.icon;
5452
- container.appendChild(icon);
5632
+ container.appendChild( icon );
5453
5633
  }
5454
5634
 
5455
- container.appendChild(wValue);
5456
- element.appendChild(container);
5635
+ container.appendChild( wValue );
5636
+ element.appendChild( container );
5457
5637
 
5458
5638
  // Remove branch padding and margins
5459
- if( !widget.name )
5639
+ const useNameAsLabel = !( options.hideName ?? false );
5640
+ if( !useNameAsLabel )
5460
5641
  {
5461
5642
  element.className += " noname";
5462
5643
  container.style.width = "100%";
@@ -5469,7 +5650,7 @@ class Panel {
5469
5650
  // Update height depending on the content
5470
5651
  wValue.style.height = wValue.scrollHeight + "px";
5471
5652
  }
5472
- }, 10);
5653
+ }, 10 );
5473
5654
 
5474
5655
  return widget;
5475
5656
  }
@@ -5477,6 +5658,7 @@ class Panel {
5477
5658
  /**
5478
5659
  * @method addLabel
5479
5660
  * @param {String} value Information string
5661
+ * @param {Object} options Text options
5480
5662
  */
5481
5663
 
5482
5664
  addLabel( value, options = {} ) {
@@ -5491,6 +5673,7 @@ class Panel {
5491
5673
  * @param {String} value Button name
5492
5674
  * @param {Function} callback Callback function on click
5493
5675
  * @param {*} options:
5676
+ * hideName: Don't use name as label [false]
5494
5677
  * disabled: Make the widget disabled [false]
5495
5678
  * icon: Icon class to show as button value
5496
5679
  * img: Path to image to show as button value
@@ -5499,19 +5682,19 @@ class Panel {
5499
5682
 
5500
5683
  addButton( name, value, callback, options = {} ) {
5501
5684
 
5502
- let widget = this.create_widget( name, Widget.BUTTON, options );
5685
+ let widget = this._createWidget( Widget.BUTTON, name, null, options );
5503
5686
 
5504
5687
  widget.onGetValue = () => {
5505
5688
  return wValue.innerText;
5506
5689
  };
5507
5690
 
5508
- widget.onSetValue = ( newValue, skipCallback ) => {
5691
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5509
5692
  wValue.innerHTML =
5510
- (options.icon ? "<a class='" + options.icon + "'></a>" :
5511
- ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (newValue || "") + "</span>" ));
5693
+ ( options.icon ? "<a class='" + options.icon + "'></a>" :
5694
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + ( newValue || "" ) + "</span>" ) );
5512
5695
  };
5513
5696
 
5514
- let element = widget.domEl;
5697
+ const element = widget.domEl;
5515
5698
 
5516
5699
  var wValue = document.createElement( 'button' );
5517
5700
  wValue.title = options.title ?? "";
@@ -5523,22 +5706,22 @@ class Panel {
5523
5706
  }
5524
5707
 
5525
5708
  wValue.innerHTML =
5526
- (options.icon ? "<a class='" + options.icon + "'></a>" :
5527
- ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (value || "") + "</span>" ));
5709
+ ( options.icon ? "<a class='" + options.icon + "'></a>" :
5710
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + ( value || "" ) + "</span>" ) );
5528
5711
 
5529
- wValue.style.width = "calc( 100% - " + (options.nameWidth ?? LX.DEFAULT_NAME_WIDTH) + ")";
5712
+ wValue.style.width = "calc( 100% - " + ( options.nameWidth ?? LX.DEFAULT_NAME_WIDTH ) + ")";
5530
5713
 
5531
5714
  if( options.disabled )
5532
5715
  {
5533
5716
  wValue.setAttribute( "disabled", true );
5534
5717
  }
5535
5718
 
5536
- wValue.addEventListener("click", e => {
5719
+ wValue.addEventListener( "click", e => {
5537
5720
  if( options.selectable )
5538
5721
  {
5539
5722
  if( options.parent )
5540
5723
  {
5541
- options.parent.querySelectorAll(".lexbutton.selected").forEach( e => { if(e == wValue) return; e.classList.remove("selected") } );
5724
+ options.parent.querySelectorAll(".lexbutton.selected").forEach( e => { if( e == wValue ) return; e.classList.remove( "selected" ) } );
5542
5725
  }
5543
5726
 
5544
5727
  wValue.classList.toggle('selected');
@@ -5549,8 +5732,9 @@ class Panel {
5549
5732
 
5550
5733
  element.appendChild( wValue );
5551
5734
 
5552
- // Remove branch padding and margins
5553
- if( !widget.name )
5735
+ // Remove branch padding and
5736
+ const useNameAsLabel = !( options.hideName ?? false ) && !( options.icon || options.img );
5737
+ if( !useNameAsLabel )
5554
5738
  {
5555
5739
  wValue.className += " noname";
5556
5740
  wValue.style.width = "100%";
@@ -5564,6 +5748,7 @@ class Panel {
5564
5748
  * @param {String} name Widget name
5565
5749
  * @param {Array} values Each of the {value, callback, selected, disabled} items
5566
5750
  * @param {*} options:
5751
+ * hideName: Don't use name as label [false]
5567
5752
  * float: Justify content (left, center, right) [center]
5568
5753
  * @legacy selected: Selected item by default by value
5569
5754
  * noSelection: Buttons can be clicked, but they are not selectable
@@ -5572,8 +5757,8 @@ class Panel {
5572
5757
 
5573
5758
  addComboButtons( name, values, options = {} ) {
5574
5759
 
5575
- let widget = this.create_widget( name, Widget.BUTTON, options );
5576
- let element = widget.domEl;
5760
+ let widget = this._createWidget( Widget.BUTTONS, name, null, options );
5761
+ const element = widget.domEl;
5577
5762
 
5578
5763
  let that = this;
5579
5764
  let container = document.createElement('div');
@@ -5642,7 +5827,8 @@ class Panel {
5642
5827
  }
5643
5828
 
5644
5829
  // Remove branch padding and margins
5645
- if( !widget.name )
5830
+ const useNameAsLabel = !( options.hideName ?? false );
5831
+ if( !useNameAsLabel )
5646
5832
  {
5647
5833
  element.className += " noname";
5648
5834
  container.style.width = "100%";
@@ -5658,17 +5844,19 @@ class Panel {
5658
5844
  * @method addCard
5659
5845
  * @param {String} name Card Name
5660
5846
  * @param {*} options:
5661
- * title: title if any
5662
- * text: card text if any
5663
- * src: url of the image if any
5847
+ * text: Card text
5848
+ * link: Card link
5849
+ * title: Card dom title
5850
+ * src: url of the image
5664
5851
  * callback (Function): function to call on click
5665
5852
  */
5666
5853
 
5667
5854
  addCard( name, options = {} ) {
5668
5855
 
5669
5856
  options.hideName = true;
5670
- let widget = this.create_widget(name, Widget.CARD, options);
5671
- let element = widget.domEl;
5857
+
5858
+ let widget = this._createWidget( Widget.CARD, name, null, options );
5859
+ const element = widget.domEl;
5672
5860
 
5673
5861
  let container = document.createElement('div');
5674
5862
  container.className = "lexcard";
@@ -5680,7 +5868,7 @@ class Panel {
5680
5868
  img.src = options.img;
5681
5869
  container.appendChild(img);
5682
5870
 
5683
- if(options.link != undefined)
5871
+ if( options.link != undefined )
5684
5872
  {
5685
5873
  img.style.cursor = "pointer";
5686
5874
  img.addEventListener('click', function() {
@@ -5690,29 +5878,30 @@ class Panel {
5690
5878
  }
5691
5879
  }
5692
5880
 
5693
- let name_el = document.createElement('span');
5694
- name_el.innerText = name;
5881
+ let cardNameDom = document.createElement('span');
5882
+ cardNameDom.innerText = name;
5695
5883
 
5696
- if(options.link != undefined)
5884
+ if( options.link != undefined )
5697
5885
  {
5698
- let link_el = document.createElement('a');
5699
- link_el.innerText = name;
5700
- link_el.href = options.link;
5701
- link_el.target = options.target ?? "";
5702
- name_el.innerText = "";
5703
- name_el.appendChild(link_el);
5886
+ let cardLinkDom = document.createElement( 'a' );
5887
+ cardLinkDom.innerText = name;
5888
+ cardLinkDom.href = options.link;
5889
+ cardLinkDom.target = options.target ?? "";
5890
+ cardNameDom.innerText = "";
5891
+ cardNameDom.appendChild( cardLinkDom );
5704
5892
  }
5705
5893
 
5706
- container.appendChild(name_el);
5894
+ container.appendChild( cardNameDom );
5707
5895
 
5708
- if( options.callback ) {
5896
+ if( options.callback )
5897
+ {
5709
5898
  container.style.cursor = "pointer";
5710
- container.addEventListener("click", (e) => {
5711
- this._trigger( new IEvent(name, null, e), options.callback );
5899
+ container.addEventListener("click", ( e ) => {
5900
+ this._trigger( new IEvent( name, null, e ), options.callback );
5712
5901
  });
5713
5902
  }
5714
5903
 
5715
- element.appendChild(container);
5904
+ element.appendChild( container );
5716
5905
 
5717
5906
  return widget;
5718
5907
  }
@@ -5737,13 +5926,13 @@ class Panel {
5737
5926
  // Always hide name for this one
5738
5927
  options.hideName = true;
5739
5928
 
5740
- let widget = this.create_widget( name, Widget.FORM, options );
5929
+ let widget = this._createWidget( Widget.FORM, name, null, options );
5741
5930
 
5742
5931
  widget.onGetValue = () => {
5743
5932
  return container.formData;
5744
5933
  };
5745
5934
 
5746
- widget.onSetValue = ( newValue, skipCallback ) => {
5935
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5747
5936
  container.formData = newValue;
5748
5937
  const entries = container.querySelectorAll( ".lexwidget" );
5749
5938
  for( let i = 0; i < entries.length; ++i )
@@ -5762,7 +5951,7 @@ class Panel {
5762
5951
 
5763
5952
  // Add widget value
5764
5953
 
5765
- let element = widget.domEl;
5954
+ const element = widget.domEl;
5766
5955
 
5767
5956
  let container = document.createElement( 'div' );
5768
5957
  container.className = "lexformdata";
@@ -5816,31 +6005,29 @@ class Panel {
5816
6005
 
5817
6006
  element.appendChild( container );
5818
6007
 
5819
- if( !widget.name || options.hideName )
5820
- {
5821
- element.className += " noname";
5822
- container.style.width = "100%";
5823
- }
6008
+ // Form does not never use label
6009
+ element.className += " noname";
6010
+ container.style.width = "100%";
5824
6011
 
5825
6012
  return widget;
5826
6013
  }
5827
6014
 
5828
6015
  /**
5829
6016
  * @method addContent
6017
+ * @param {String} name Widget name
5830
6018
  * @param {HTMLElement/String} element
6019
+ * @param {Object} options
5831
6020
  */
5832
6021
 
5833
- addContent( element, options = {} ) {
6022
+ addContent( name, element, options = {} ) {
5834
6023
 
5835
- if( !element )
5836
- {
5837
- return;
5838
- }
6024
+ console.assert( element, "Empty content!" );
5839
6025
 
5840
6026
  if( element.constructor == String )
5841
6027
  {
5842
6028
  const tmp = document.createElement( "div" );
5843
6029
  tmp.innerHTML = element;
6030
+
5844
6031
  if( tmp.childElementCount > 1 )
5845
6032
  {
5846
6033
  element = tmp;
@@ -5851,27 +6038,28 @@ class Panel {
5851
6038
  }
5852
6039
  }
5853
6040
 
5854
- let widget = this.create_widget( null, Widget.CONTENT, options );
6041
+ options.hideName = true;
6042
+
6043
+ let widget = this._createWidget( Widget.CONTENT, name, null, options );
5855
6044
  widget.domEl.appendChild( element );
6045
+
5856
6046
  return widget;
5857
6047
  }
5858
6048
 
5859
6049
  /**
5860
6050
  * @method addImage
6051
+ * @param {String} name Widget name
5861
6052
  * @param {String} url Image Url
5862
6053
  * @param {*} options
6054
+ * hideName: Don't use name as label [false]
5863
6055
  */
5864
6056
 
5865
- async addImage( url, options = {} ) {
6057
+ async addImage( name, url, options = {} ) {
5866
6058
 
5867
- if( !url )
5868
- {
5869
- return;
5870
- }
6059
+ console.assert( url, "Empty src/url for Image!" );
5871
6060
 
5872
- options.hideName = true;
5873
- let widget = this.create_widget( null, Widget.IMAGE, options );
5874
- let element = widget.domEl;
6061
+ let widget = this._createWidget( Widget.IMAGE, name, null, options );
6062
+ const element = widget.domEl;
5875
6063
 
5876
6064
  let container = document.createElement( 'div' );
5877
6065
  container.className = "leximage";
@@ -5886,6 +6074,7 @@ class Panel {
5886
6074
  }
5887
6075
 
5888
6076
  await img.decode();
6077
+
5889
6078
  container.appendChild( img );
5890
6079
  element.appendChild( container );
5891
6080
 
@@ -5899,6 +6088,7 @@ class Panel {
5899
6088
  * @param {String} value Select by default option
5900
6089
  * @param {Function} callback Callback function on change
5901
6090
  * @param {*} options:
6091
+ * hideName: Don't use name as label [false]
5902
6092
  * filter: Add a search bar to the widget [false]
5903
6093
  * disabled: Make the widget disabled [false]
5904
6094
  * skipReset: Don't add the reset value button when value changes
@@ -5908,33 +6098,24 @@ class Panel {
5908
6098
 
5909
6099
  addDropdown( name, values, value, callback, options = {} ) {
5910
6100
 
5911
- let widget = this.create_widget( name, Widget.DROPDOWN, options );
6101
+ let widget = this._createWidget( Widget.DROPDOWN, name, value, options );
5912
6102
 
5913
6103
  widget.onGetValue = () => {
5914
6104
  return element.querySelector( "li.selected" ).getAttribute( 'value' );
5915
6105
  };
5916
6106
 
5917
- widget.onSetValue = ( newValue, skipCallback ) => {
5918
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5919
- if( btn ) btn.style.display = ( newValue != wValue.iValue ? "block" : "none" );
6107
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5920
6108
  value = newValue;
5921
6109
  list.querySelectorAll( 'li' ).forEach( e => { if( e.getAttribute('value') == value ) e.click() } );
5922
- if( !skipCallback ) this._trigger( new IEvent( name, value, null ), callback );
6110
+ if( !skipCallback )
6111
+ {
6112
+ this._trigger( new IEvent( name, value, event ), callback );
6113
+ }
5923
6114
  };
5924
6115
 
5925
- let element = widget.domEl;
6116
+ const element = widget.domEl;
5926
6117
  let that = this;
5927
6118
 
5928
- // Add reset functionality
5929
- if(widget.name && !( options.skipReset ?? false ))
5930
- {
5931
- Panel._add_reset_property( element.domName, function() {
5932
- value = wValue.iValue;
5933
- list.querySelectorAll( 'li' ).forEach( e => { if( e.getAttribute('value') == value ) e.click() } );
5934
- this.style.display = "none";
5935
- });
5936
- }
5937
-
5938
6119
  let container = document.createElement( 'div' );
5939
6120
  container.className = "lexdropdown";
5940
6121
  container.style.width = options.inputWidth || "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
@@ -6143,16 +6324,19 @@ class Panel {
6143
6324
 
6144
6325
  li.addEventListener( "click", e => {
6145
6326
  listDialog.close();
6327
+
6146
6328
  const currentSelected = element.querySelector( ".lexoptions .selected" );
6147
- if(currentSelected) currentSelected.classList.remove( "selected" );
6329
+ if(currentSelected)
6330
+ {
6331
+ currentSelected.classList.remove( "selected" );
6332
+ }
6333
+
6148
6334
  value = e.currentTarget.getAttribute( "value" );
6149
6335
  e.currentTarget.toggleAttribute( "hidden", false );
6150
6336
  e.currentTarget.classList.add( "selected" );
6151
- selectedOption.refresh(value);
6337
+ selectedOption.refresh( value );
6152
6338
 
6153
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6154
- if( btn ) btn.style.display = (value != wValue.iValue ? "block" : "none");
6155
- that._trigger( new IEvent( name, value, null ), callback );
6339
+ widget.set( value, false, e );
6156
6340
 
6157
6341
  // Reset filter
6158
6342
  if( filter )
@@ -6209,7 +6393,8 @@ class Panel {
6209
6393
  element.appendChild( container );
6210
6394
 
6211
6395
  // Remove branch padding and margins
6212
- if( !widget.name )
6396
+ const useNameAsLabel = !( options.hideName ?? false );
6397
+ if( !useNameAsLabel )
6213
6398
  {
6214
6399
  element.className += " noname";
6215
6400
  container.style.width = "100%";
@@ -6236,39 +6421,25 @@ class Panel {
6236
6421
 
6237
6422
  addCurve( name, values, callback, options = {} ) {
6238
6423
 
6239
- if( !name )
6240
- {
6241
- throw( "Set Widget Name!" );
6242
- }
6424
+ let defaultValues = JSON.parse( JSON.stringify( values ) );
6243
6425
 
6244
6426
  let that = this;
6245
- let widget = this.create_widget( name, Widget.CURVE, options );
6427
+ let widget = this._createWidget( Widget.CURVE, name, defaultValues, options );
6246
6428
 
6247
6429
  widget.onGetValue = () => {
6248
6430
  return JSON.parse(JSON.stringify( curveInstance.element.value ));
6249
6431
  };
6250
6432
 
6251
- widget.onSetValue = ( newValue, skipCallback ) => {
6252
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6253
- if( btn ) btn.style.display = ( newValue != curveInstance.element.value ? "block" : "none" );
6433
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6254
6434
  curveInstance.element.value = JSON.parse( JSON.stringify( newValue ) );
6255
6435
  curveInstance.redraw();
6256
- if( !skipCallback ) that._trigger( new IEvent( name, curveInstance.element.value, null ), callback );
6436
+ if( !skipCallback )
6437
+ {
6438
+ that._trigger( new IEvent( name, curveInstance.element.value, event ), callback );
6439
+ }
6257
6440
  };
6258
6441
 
6259
- let element = widget.domEl;
6260
- let defaultValues = JSON.parse( JSON.stringify( values ) );
6261
-
6262
- // Add reset functionality
6263
- if( !(options.skipReset ?? false) )
6264
- {
6265
- Panel._add_reset_property(element.domName, function(e) {
6266
- this.style.display = "none";
6267
- curveInstance.element.value = JSON.parse( JSON.stringify( defaultValues ) );
6268
- curveInstance.redraw();
6269
- that._trigger( new IEvent( name, curveInstance.element.value, e ), callback );
6270
- });
6271
- }
6442
+ const element = widget.domEl;
6272
6443
 
6273
6444
  // Add widget value
6274
6445
 
@@ -6277,9 +6448,7 @@ class Panel {
6277
6448
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6278
6449
 
6279
6450
  options.callback = (v, e) => {
6280
- let btn = element.querySelector(".lexwidgetname .lexicon");
6281
- if(btn) btn.style.display = (v != defaultValues ? "block" : "none");
6282
- that._trigger( new IEvent(name, v, e), callback );
6451
+ that._trigger( new IEvent( name, v, e ), callback );
6283
6452
  };
6284
6453
 
6285
6454
  options.name = name;
@@ -6318,34 +6487,25 @@ class Panel {
6318
6487
 
6319
6488
  addDial( name, values, callback, options = {} ) {
6320
6489
 
6490
+ let defaultValues = JSON.parse( JSON.stringify( values ) );
6491
+
6321
6492
  let that = this;
6322
- let widget = this.create_widget(name, Widget.DIAL, options);
6493
+ let widget = this._createWidget( Widget.DIAL, name, defaultValues, options );
6323
6494
 
6324
6495
  widget.onGetValue = () => {
6325
- return JSON.parse(JSON.stringify(curveInstance.element.value));
6496
+ return JSON.parse( JSON.stringify( curveInstance.element.value ) );
6326
6497
  };
6327
6498
 
6328
- widget.onSetValue = ( newValue, skipCallback ) => {
6329
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6330
- if( btn ) btn.style.display = ( newValue != curveInstance.element.value ? "block" : "none" );
6499
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6331
6500
  curveInstance.element.value = JSON.parse( JSON.stringify( newValue ) );
6332
6501
  curveInstance.redraw();
6333
- if( !skipCallback ) that._trigger( new IEvent( name, curveInstance.element.value, null ), callback );
6502
+ if( !skipCallback )
6503
+ {
6504
+ that._trigger( new IEvent( name, curveInstance.element.value, event ), callback );
6505
+ }
6334
6506
  };
6335
6507
 
6336
- let element = widget.domEl;
6337
- let defaultValues = JSON.parse( JSON.stringify( values ) );
6338
-
6339
- // Add reset functionality
6340
- if( widget.name && !(options.skipReset ?? false) )
6341
- {
6342
- Panel._add_reset_property(element.domName, function(e) {
6343
- this.style.display = "none";
6344
- curveInstance.element.value = JSON.parse( JSON.stringify( defaultValues ) );
6345
- curveInstance.redraw();
6346
- that._trigger( new IEvent( name, curveInstance.element.value, e ), callback );
6347
- });
6348
- }
6508
+ const element = widget.domEl;
6349
6509
 
6350
6510
  // Add widget value
6351
6511
 
@@ -6353,10 +6513,8 @@ class Panel {
6353
6513
  container.className = "lexcurve";
6354
6514
  container.style.width = widget.name ? "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")" : '100%';
6355
6515
 
6356
- options.callback = (v, e) => {
6357
- let btn = element.querySelector(".lexwidgetname .lexicon");
6358
- if(btn) btn.style.display = (v != defaultValues ? "block" : "none");
6359
- that._trigger( new IEvent(name, v, e), callback );
6516
+ options.callback = ( v, e ) => {
6517
+ that._trigger( new IEvent( name, v, e ), callback );
6360
6518
  };
6361
6519
 
6362
6520
  options.name = name;
@@ -6385,42 +6543,32 @@ class Panel {
6385
6543
  * @param {String} name Widget name
6386
6544
  * @param {Number} value Flag value by default option
6387
6545
  * @param {Function} callback Callback function on change
6388
- * @param {*} options:
6546
+ * @param {Object} options:
6389
6547
  */
6390
6548
 
6391
6549
  addLayers( name, value, callback, options = {} ) {
6392
6550
 
6393
- if( !name )
6394
- {
6395
- throw("Set Widget Name!");
6396
- }
6397
-
6398
6551
  let that = this;
6399
- let widget = this.create_widget(name, Widget.LAYERS, options);
6552
+ let widget = this._createWidget( Widget.LAYERS, name, value, options );
6553
+
6400
6554
  widget.onGetValue = () => {
6401
6555
  return element.value;
6402
6556
  };
6403
- widget.onSetValue = ( newValue, skipCallback ) => {
6404
- let btn = element.querySelector(".lexwidgetname .lexicon");
6405
- if(btn) btn.style.display = (newValue != defaultValue ? "block" : "none");
6557
+
6558
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6406
6559
  value = element.value = newValue;
6407
6560
  setLayers();
6408
- if( !skipCallback ) that._trigger( new IEvent(name, value), callback );
6561
+ if( !skipCallback )
6562
+ {
6563
+ that._trigger( new IEvent(name, value, event), callback );
6564
+ }
6409
6565
  };
6410
6566
 
6411
- let element = widget.domEl;
6412
-
6413
- // Add reset functionality
6414
- Panel._add_reset_property(element.domName, function(e) {
6415
- this.style.display = "none";
6416
- value = element.value = defaultValue;
6417
- setLayers();
6418
- that._trigger( new IEvent(name, value, e), callback );
6419
- });
6567
+ const element = widget.domEl;
6420
6568
 
6421
6569
  // Add widget value
6422
6570
 
6423
- var container = document.createElement('div');
6571
+ var container = document.createElement( "div" );
6424
6572
  container.className = "lexlayers";
6425
6573
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6426
6574
 
@@ -6432,6 +6580,7 @@ class Panel {
6432
6580
 
6433
6581
  let binary = value.toString( 2 );
6434
6582
  let nbits = binary.length;
6583
+
6435
6584
  // fill zeros
6436
6585
  for( var i = 0; i < (16 - nbits); ++i )
6437
6586
  {
@@ -6445,33 +6594,28 @@ class Panel {
6445
6594
  if( value != undefined )
6446
6595
  {
6447
6596
  const valueBit = binary[ 16 - bit - 1 ];
6448
- if(valueBit != undefined && valueBit == '1')
6597
+ if( valueBit != undefined && valueBit == '1' )
6598
+ {
6449
6599
  layer.classList.add('selected');
6600
+ }
6450
6601
  }
6451
6602
  layer.innerText = bit + 1;
6452
6603
  layer.title = "Bit " + bit + ", value " + (1 << bit);
6453
6604
  container.appendChild( layer );
6454
6605
 
6455
6606
  layer.addEventListener("click", e => {
6456
-
6457
6607
  e.stopPropagation();
6458
6608
  e.stopImmediatePropagation();
6459
6609
  e.target.classList.toggle('selected');
6460
6610
  value ^= ( 1 << bit );
6461
- element.value = value;
6462
-
6463
- let btn = element.querySelector(".lexwidgetname .lexicon");
6464
- if(btn) btn.style.display = (value != defaultValue ? "block" : "none");
6465
-
6466
- this._trigger( new IEvent(name, value, e), callback );
6611
+ widget.set( value, false, e );
6467
6612
  });
6468
6613
  }
6469
-
6470
6614
  };
6471
6615
 
6472
6616
  setLayers();
6473
6617
 
6474
- element.appendChild(container);
6618
+ element.appendChild( container );
6475
6619
 
6476
6620
  return widget;
6477
6621
  }
@@ -6487,12 +6631,8 @@ class Panel {
6487
6631
 
6488
6632
  addArray( name, values = [], callback, options = {} ) {
6489
6633
 
6490
- if( !name )
6491
- {
6492
- throw( "Set Widget Name!" );
6493
- }
6634
+ let widget = this._createWidget( Widget.ARRAY, name, null, options );
6494
6635
 
6495
- let widget = this.create_widget(name, Widget.ARRAY, options);
6496
6636
  widget.onGetValue = () => {
6497
6637
  let array_inputs = element.querySelectorAll("input");
6498
6638
  let values = [];
@@ -6500,13 +6640,17 @@ class Panel {
6500
6640
  values.push( v.value );
6501
6641
  return values;
6502
6642
  };
6503
- widget.onSetValue = ( newValue, skipCallback ) => {
6643
+
6644
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6504
6645
  values = newValue;
6505
6646
  updateItems();
6506
- if( !skipCallback ) this._trigger( new IEvent(name, values, null), callback );
6647
+ if( !skipCallback )
6648
+ {
6649
+ this._trigger( new IEvent( name, values, event ), callback );
6650
+ }
6507
6651
  };
6508
6652
 
6509
- let element = widget.domEl;
6653
+ const element = widget.domEl;
6510
6654
  element.style.flexWrap = "wrap";
6511
6655
 
6512
6656
  // Add dropdown array button
@@ -6519,10 +6663,10 @@ class Panel {
6519
6663
 
6520
6664
  this.queue( container );
6521
6665
 
6522
- const angle_down = `<a class='fa-solid fa-angle-down' style='float:right; margin-right: 3px;'></a>`;
6666
+ const angleDown = `<a class='fa-solid fa-angle-down' style='float:right; margin-right: 3px;'></a>`;
6523
6667
 
6524
6668
  let buttonName = "Array (size " + values.length + ")";
6525
- buttonName += angle_down;
6669
+ buttonName += angleDown;
6526
6670
  this.addButton(null, buttonName, () => {
6527
6671
  element.querySelector(".lexarrayitems").toggleAttribute('hidden');
6528
6672
  }, { buttonClass: 'array' });
@@ -6531,24 +6675,24 @@ class Panel {
6531
6675
 
6532
6676
  // Show elements
6533
6677
 
6534
- let array_items = document.createElement('div');
6535
- array_items.className = "lexarrayitems";
6536
- array_items.toggleAttribute('hidden', true);
6678
+ let arrayItems = document.createElement( "div" );
6679
+ arrayItems.className = "lexarrayitems";
6680
+ arrayItems.toggleAttribute( "hidden", true );
6537
6681
 
6538
- element.appendChild(container);
6539
- element.appendChild(array_items);
6682
+ element.appendChild( container );
6683
+ element.appendChild( arrayItems );
6540
6684
 
6541
6685
  const updateItems = () => {
6542
6686
 
6543
6687
  // Update num items
6544
6688
  let buttonEl = element.querySelector(".lexbutton.array span");
6545
6689
  buttonEl.innerHTML = "Array (size " + values.length + ")";
6546
- buttonEl.innerHTML += angle_down;
6690
+ buttonEl.innerHTML += angleDown;
6547
6691
 
6548
6692
  // Update inputs
6549
- array_items.innerHTML = "";
6693
+ arrayItems.innerHTML = "";
6550
6694
 
6551
- this.queue( array_items );
6695
+ this.queue( arrayItems );
6552
6696
 
6553
6697
  for( let i = 0; i < values.length; ++i )
6554
6698
  {
@@ -6594,7 +6738,7 @@ class Panel {
6594
6738
  this._trigger( new IEvent(name, values, event), callback );
6595
6739
  }, { buttonClass: 'array' });
6596
6740
 
6597
- // Stop pushing to array_items
6741
+ // Stop pushing to arrayItems
6598
6742
  this.clearQueue();
6599
6743
  };
6600
6744
 
@@ -6610,22 +6754,30 @@ class Panel {
6610
6754
  * @param {String} value Selected list value
6611
6755
  * @param {Function} callback Callback function on change
6612
6756
  * @param {*} options:
6757
+ * hideName: Don't use name as label [false]
6613
6758
  */
6614
6759
 
6615
6760
  addList( name, values, value, callback, options = {} ) {
6616
6761
 
6617
- let widget = this.create_widget( name, Widget.LIST, options );
6762
+ let widget = this._createWidget( Widget.LIST, name, value, options );
6618
6763
 
6619
6764
  widget.onGetValue = () => {
6620
6765
  return value;
6621
6766
  };
6622
- widget.onSetValue = ( newValue, skipCallback ) => {
6767
+
6768
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6623
6769
  listContainer.querySelectorAll( '.lexlistitem' ).forEach( e => e.classList.remove( 'selected' ) );
6624
6770
  const idx = values.indexOf( newValue );
6625
- if( idx == -1 ) return;
6771
+ if( idx == -1 )
6772
+ {
6773
+ return;
6774
+ }
6626
6775
  listContainer.children[ idx ].classList.toggle( 'selected' );
6627
6776
  value = newValue;
6628
- if( !skipCallback ) this._trigger( new IEvent( name, newValue ), callback );
6777
+ if( !skipCallback )
6778
+ {
6779
+ this._trigger( new IEvent( name, newValue, event ), callback );
6780
+ }
6629
6781
  };
6630
6782
 
6631
6783
  widget.updateValues = ( newValues ) => {
@@ -6659,7 +6811,7 @@ class Panel {
6659
6811
  }
6660
6812
  };
6661
6813
 
6662
- let element = widget.domEl;
6814
+ const element = widget.domEl;
6663
6815
 
6664
6816
  // Show list
6665
6817
 
@@ -6670,7 +6822,8 @@ class Panel {
6670
6822
  widget.updateValues( values );
6671
6823
 
6672
6824
  // Remove branch padding and margins
6673
- if( !widget.name )
6825
+ const useNameAsLabel = !( options.hideName ?? false );
6826
+ if( !useNameAsLabel )
6674
6827
  {
6675
6828
  element.className += " noname";
6676
6829
  listContainer.style.width = "100%";
@@ -6687,46 +6840,39 @@ class Panel {
6687
6840
  * @param {String} value Comma separated tags
6688
6841
  * @param {Function} callback Callback function on change
6689
6842
  * @param {*} options:
6843
+ * hideName: Don't use name as label [false]
6690
6844
  */
6691
6845
 
6692
6846
  addTags( name, value, callback, options = {} ) {
6693
6847
 
6694
6848
  value = value.replace( /\s/g, '' ).split( ',' );
6849
+
6695
6850
  let defaultValue = [].concat( value );
6696
- let widget = this.create_widget( name, Widget.TAGS, options );
6851
+ let widget = this._createWidget( Widget.TAGS, name, defaultValue, options );
6697
6852
 
6698
6853
  widget.onGetValue = () => {
6699
6854
  return [].concat( value );
6700
6855
  };
6701
- widget.onSetValue = ( newValue, skipCallback ) => {
6856
+
6857
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6702
6858
  value = [].concat( newValue );
6703
- create_tags();
6704
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6705
- if( btn ) btn.style.display = ( newValue != defaultValue ? "block" : "none" );
6706
- if( !skipCallback ) that._trigger( new IEvent( name, value ), callback );
6859
+ _generateTags();
6860
+ if( !skipCallback )
6861
+ {
6862
+ that._trigger( new IEvent( name, value, event ), callback );
6863
+ }
6707
6864
  };
6708
6865
 
6709
- let element = widget.domEl;
6866
+ const element = widget.domEl;
6710
6867
  let that = this;
6711
6868
 
6712
- // Add reset functionality
6713
- if(widget.name)
6714
- {
6715
- Panel._add_reset_property(element.domName, function(e) {
6716
- this.style.display = "none";
6717
- value = [].concat(defaultValue);
6718
- create_tags();
6719
- that._trigger( new IEvent(name, value, e), callback );
6720
- });
6721
- }
6722
-
6723
6869
  // Show tags
6724
6870
 
6725
6871
  const tagsContainer = document.createElement('div');
6726
6872
  tagsContainer.className = "lextags";
6727
6873
  tagsContainer.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6728
6874
 
6729
- const create_tags = () => {
6875
+ const _generateTags = () => {
6730
6876
 
6731
6877
  tagsContainer.innerHTML = "";
6732
6878
 
@@ -6744,9 +6890,7 @@ class Panel {
6744
6890
  removeButton.addEventListener( 'click', e => {
6745
6891
  tag.remove();
6746
6892
  value.splice( value.indexOf( tagName ), 1 );
6747
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6748
- if( btn ) btn.style.display = ( value != defaultValue ? "block" : "none" );
6749
- that._trigger( new IEvent( name, value, e ), callback );
6893
+ widget.set( value, false, e );
6750
6894
  } );
6751
6895
 
6752
6896
  tagsContainer.appendChild( tag );
@@ -6765,20 +6909,18 @@ class Panel {
6765
6909
  if( !val.length || value.indexOf( val ) > -1 )
6766
6910
  return;
6767
6911
  value.push( val );
6768
- create_tags();
6769
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6770
- if(btn) btn.style.display = "block";
6771
- that._trigger( new IEvent( name, value, e ), callback );
6912
+ widget.set( value, false, e );
6772
6913
  }
6773
6914
  };
6774
6915
 
6775
6916
  tagInput.focus();
6776
6917
  }
6777
6918
 
6778
- create_tags();
6919
+ _generateTags();
6779
6920
 
6780
6921
  // Remove branch padding and margins
6781
- if( !widget.name )
6922
+ const useNameAsLabel = !( options.hideName ?? false );
6923
+ if( !useNameAsLabel )
6782
6924
  {
6783
6925
  element.className += " noname";
6784
6926
  tagsContainer.style.width = "100%";
@@ -6808,44 +6950,44 @@ class Panel {
6808
6950
  throw( "Set Widget Name or at least a label!" );
6809
6951
  }
6810
6952
 
6811
- let widget = this.create_widget( name, Widget.CHECKBOX, options );
6953
+ let widget = this._createWidget( Widget.CHECKBOX, name, value, options );
6812
6954
 
6813
6955
  widget.onGetValue = () => {
6814
- return checkbox.checked;
6956
+ return value;
6815
6957
  };
6816
6958
 
6817
- widget.onSetValue = ( newValue, skipCallback ) => {
6818
- if( checkbox.checked !== newValue )
6959
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6960
+
6961
+ if( newValue == value )
6962
+ {
6963
+ return;
6964
+ }
6965
+
6966
+ checkbox.checked = value = newValue;
6967
+
6968
+ // Update suboptions menu
6969
+ element.querySelector( ".lexcheckboxsubmenu" )?.toggleAttribute( 'hidden', !newValue );
6970
+
6971
+ if( !skipCallback )
6819
6972
  {
6820
- checkbox.checked = newValue;
6821
- Panel._dispatch_event( checkbox, "change", skipCallback );
6973
+ this._trigger( new IEvent( name, newValue, event ), callback );
6822
6974
  }
6823
6975
  };
6824
6976
 
6825
- let element = widget.domEl;
6826
-
6827
- // Add reset functionality
6828
- if( name )
6829
- {
6830
- Panel._add_reset_property( element.domName, function() {
6831
- checkbox.checked = !checkbox.checked;
6832
- Panel._dispatch_event( checkbox, "change" );
6833
- });
6834
- }
6977
+ const element = widget.domEl;
6835
6978
 
6836
6979
  // Add widget value
6837
6980
 
6838
- var container = document.createElement('div');
6981
+ var container = document.createElement( "div" );
6839
6982
  container.className = "lexcheckboxcont";
6840
6983
 
6841
- let checkbox = document.createElement('input');
6984
+ let checkbox = document.createElement( "input" );
6842
6985
  checkbox.type = "checkbox";
6843
6986
  checkbox.className = "lexcheckbox " + ( options.className ?? "" );
6844
6987
  checkbox.checked = value;
6845
- checkbox.iValue = value;
6846
6988
  checkbox.disabled = options.disabled ?? false;
6847
6989
 
6848
- let valueName = document.createElement( 'span' );
6990
+ let valueName = document.createElement( "span" );
6849
6991
  valueName.className = "checkboxtext";
6850
6992
  valueName.innerHTML = options.label ?? "On";
6851
6993
 
@@ -6853,21 +6995,7 @@ class Panel {
6853
6995
  container.appendChild( valueName );
6854
6996
 
6855
6997
  checkbox.addEventListener( "change" , e => {
6856
-
6857
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
6858
-
6859
- // Reset button (default value)
6860
- if( !skipCallback )
6861
- {
6862
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6863
- if( btn ) btn.style.display = checkbox.checked != checkbox.iValue ? "block": "none";
6864
- }
6865
-
6866
- // Open suboptions
6867
- let submenu = element.querySelector( ".lexcheckboxsubmenu" );
6868
- if( submenu ) submenu.toggleAttribute( 'hidden', !checkbox.checked );
6869
-
6870
- if( !skipCallback ) this._trigger( new IEvent( name, checkbox.checked, e ), callback );
6998
+ widget.set( checkbox.checked, false, e );
6871
6999
  });
6872
7000
 
6873
7001
  element.appendChild( container );
@@ -6875,9 +7003,9 @@ class Panel {
6875
7003
  if( options.suboptions )
6876
7004
  {
6877
7005
  element.style.flexWrap = "wrap";
6878
- let suboptions = document.createElement('div');
7006
+ let suboptions = document.createElement( "div" );
6879
7007
  suboptions.className = "lexcheckboxsubmenu";
6880
- suboptions.toggleAttribute( 'hidden', !checkbox.checked );
7008
+ suboptions.toggleAttribute( "hidden", !checkbox.checked );
6881
7009
 
6882
7010
  this.queue( suboptions );
6883
7011
  options.suboptions.call(this, this);
@@ -6902,32 +7030,31 @@ class Panel {
6902
7030
 
6903
7031
  addToggle( name, value, callback, options = {} ) {
6904
7032
 
6905
- if( !name )
6906
- {
6907
- throw( "Set Widget Name!" );
6908
- }
6909
-
6910
- let widget = this.create_widget( name, Widget.TOGGLE, options );
7033
+ let widget = this._createWidget( Widget.TOGGLE, name, value, options );
6911
7034
 
6912
7035
  widget.onGetValue = () => {
6913
7036
  return toggle.checked;
6914
7037
  };
6915
7038
 
6916
- widget.onSetValue = ( newValue, skipCallback ) => {
6917
- if( toggle.checked !== newValue )
7039
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7040
+
7041
+ if( newValue == value )
6918
7042
  {
6919
- toggle.checked = newValue;
6920
- Panel._dispatch_event( toggle, "change", skipCallback );
7043
+ return;
6921
7044
  }
6922
- };
6923
7045
 
6924
- let element = widget.domEl;
7046
+ toggle.checked = value = newValue;
6925
7047
 
6926
- // Add reset functionality
6927
- Panel._add_reset_property( element.domName, function() {
6928
- toggle.checked = !toggle.checked;
6929
- Panel._dispatch_event( toggle, "change" );
6930
- });
7048
+ // Update suboptions menu
7049
+ element.querySelector( ".lextogglesubmenu" )?.toggleAttribute( 'hidden', !newValue );
7050
+
7051
+ if( !skipCallback )
7052
+ {
7053
+ this._trigger( new IEvent( name, newValue, event ), callback );
7054
+ }
7055
+ };
7056
+
7057
+ const element = widget.domEl;
6931
7058
 
6932
7059
  // Add widget value
6933
7060
 
@@ -6949,21 +7076,7 @@ class Panel {
6949
7076
  container.appendChild( valueName );
6950
7077
 
6951
7078
  toggle.addEventListener( "change" , e => {
6952
-
6953
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
6954
-
6955
- // Reset button (default value)
6956
- if( !skipCallback )
6957
- {
6958
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6959
- if( btn ) btn.style.display = toggle.checked != toggle.iValue ? "block": "none";
6960
- }
6961
-
6962
- // Open suboptions
6963
- let submenu = element.querySelector( ".lextogglesubmenu" );
6964
- if( submenu ) submenu.toggleAttribute( 'hidden', !toggle.checked );
6965
-
6966
- if( !skipCallback ) this._trigger( new IEvent( name, toggle.checked, e ), callback );
7079
+ widget.set( toggle.checked, false, e );
6967
7080
  });
6968
7081
 
6969
7082
  element.appendChild( container );
@@ -6987,43 +7100,43 @@ class Panel {
6987
7100
 
6988
7101
  /**
6989
7102
  * @method addRadioGroup
7103
+ * @param {String} name Widget name
6990
7104
  * @param {String} label Radio label
6991
7105
  * @param {Array} values Radio options
6992
7106
  * @param {Function} callback Callback function on change
6993
7107
  * @param {*} options:
6994
7108
  * disabled: Make the widget disabled [false]
6995
7109
  * className: Customize colors
7110
+ * selected: Index of the default selected option
6996
7111
  */
6997
7112
 
6998
- addRadioGroup( label, values, callback, options = {} ) {
7113
+ addRadioGroup( name, label, values, callback, options = {} ) {
6999
7114
 
7000
- let widget = this.create_widget( null, Widget.RADIO, options );
7115
+ let widget = this._createWidget( Widget.RADIO, name, null, options );
7116
+ let currentIndex = null;
7001
7117
 
7002
7118
  widget.onGetValue = () => {
7003
7119
  const items = container.querySelectorAll( 'button' );
7004
- for( let i = 0; i < items.length; ++i )
7005
- {
7006
- const optionItem = items[ i ];
7007
- if( optionItem.checked )
7008
- {
7009
- return [ i, values[ i ] ];
7010
- }
7011
- }
7120
+ return currentIndex ? [ currentIndex, items[ currentIndex ] ] : undefined;
7012
7121
  };
7013
7122
 
7014
- widget.onSetValue = ( newValue, skipCallback ) => {
7123
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7124
+ console.assert( newValue.constructor == Number, "RadioGroup _value_ must be an Array index!" );
7125
+
7015
7126
  const items = container.querySelectorAll( 'button' );
7016
- for( let i = 0; i < items.length; ++i )
7127
+ items.forEach( b => { b.checked = false; b.classList.remove( "checked" ) } );
7128
+
7129
+ const optionItem = items[ newValue ];
7130
+ optionItem.checked = !optionItem.checked;
7131
+ optionItem.classList.toggle( "checked" );
7132
+
7133
+ if( !skipCallback )
7017
7134
  {
7018
- const optionItem = items[ i ];
7019
- if( newValue == i )
7020
- {
7021
- Panel._dispatch_event( optionItem, "click", skipCallback );
7022
- }
7135
+ that._trigger( new IEvent( null, [ newValue, values[ newValue ] ], event ), callback );
7023
7136
  }
7024
7137
  };
7025
7138
 
7026
- let element = widget.domEl;
7139
+ const element = widget.domEl;
7027
7140
 
7028
7141
  // Add widget value
7029
7142
  var container = document.createElement( 'div' );
@@ -7046,18 +7159,12 @@ class Panel {
7046
7159
  optionButton.disabled = options.disabled ?? false;
7047
7160
  optionItem.appendChild( optionButton );
7048
7161
 
7049
- optionButton.addEventListener( "click", function( e ) {
7050
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
7051
- container.querySelectorAll( 'button' ).forEach( e => { e.checked = false; e.classList.remove( "checked" ) } );
7052
- this.checked = !this.checked;
7053
- this.classList.toggle( "checked" );
7054
- if( !skipCallback ) that._trigger( new IEvent( null, [ i, values[ i ] ], e ), callback );
7162
+ optionButton.addEventListener( "click", ( e ) => {
7163
+ widget.set( i, false, e );
7055
7164
  } );
7056
7165
 
7057
- {
7058
- const checkedSpan = document.createElement( 'span' );
7059
- optionButton.appendChild( checkedSpan );
7060
- }
7166
+ const checkedSpan = document.createElement( 'span' );
7167
+ optionButton.appendChild( checkedSpan );
7061
7168
 
7062
7169
  const optionLabel = document.createElement( 'span' );
7063
7170
  optionLabel.innerHTML = values[ i ];
@@ -7066,8 +7173,9 @@ class Panel {
7066
7173
 
7067
7174
  if( options.selected )
7068
7175
  {
7069
- console.assert( options.selected.constructor == Number );
7070
- widget.set( options.selected, true );
7176
+ console.assert( options.selected.constructor == Number, "RadioGroup _selected_ must be an Array index!" );
7177
+ currentIndex = options.selected;
7178
+ widget.set( currentIndex, true );
7071
7179
  }
7072
7180
 
7073
7181
  element.appendChild( container );
@@ -7087,30 +7195,36 @@ class Panel {
7087
7195
 
7088
7196
  addColor( name, value, callback, options = {} ) {
7089
7197
 
7090
- if( !name )
7091
- {
7092
- throw( "Set Widget Name!" );
7093
- }
7198
+ value = ( value.constructor === Array ) ? rgbToHex( value ) : value;
7094
7199
 
7095
- let widget = this.create_widget( name, Widget.COLOR, options );
7200
+ let widget = this._createWidget( Widget.COLOR, name, value, options );
7096
7201
 
7097
7202
  widget.onGetValue = () => {
7098
- return color.value;
7099
- };
7100
- widget.onSetValue = ( newValue, skipCallback ) => {
7101
- color.value = newValue;
7102
- Panel._dispatch_event( color, "input", skipCallback );
7203
+ return value;
7103
7204
  };
7104
7205
 
7105
- let element = widget.domEl;
7106
- let change_from_input = false;
7206
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7107
7207
 
7108
- // Add reset functionality
7109
- Panel._add_reset_property( element.domName, function() {
7110
- this.style.display = "none";
7111
- color.value = color.iValue;
7112
- Panel._dispatch_event( color, "input" );
7113
- });
7208
+ if( color.useRGB )
7209
+ {
7210
+ newValue = hexToRgb( newValue );
7211
+ }
7212
+
7213
+ // Means it was called from the color input listener, not the text
7214
+ if( event )
7215
+ {
7216
+ textWidget.set( newValue, true, event );
7217
+ }
7218
+
7219
+ color.value = value = newValue;
7220
+
7221
+ if( !skipCallback )
7222
+ {
7223
+ this._trigger( new IEvent( name, newValue, event ), callback );
7224
+ }
7225
+ };
7226
+
7227
+ const element = widget.domEl;
7114
7228
 
7115
7229
  // Add widget value
7116
7230
 
@@ -7124,7 +7238,7 @@ class Panel {
7124
7238
  color.className = "colorinput";
7125
7239
  color.id = "color" + simple_guidGenerator();
7126
7240
  color.useRGB = options.useRGB ?? false;
7127
- color.value = color.iValue = value.constructor === Array ? rgbToHex( value ) : value;
7241
+ color.value = color.iValue = value;
7128
7242
 
7129
7243
  if( options.disabled )
7130
7244
  {
@@ -7132,38 +7246,18 @@ class Panel {
7132
7246
  }
7133
7247
 
7134
7248
  color.addEventListener( "input", e => {
7135
- let val = e.target.value;
7136
-
7137
- const skipCallback = e.detail;
7138
-
7139
- // Change value (always hex)
7140
- if( !change_from_input )
7141
- text_widget.set( val );
7142
-
7143
- // Reset button (default value)
7144
- if( !skipCallback )
7145
- {
7146
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7147
- if( btn ) btn.style.display = val != color.iValue ? "block": "none";
7148
- }
7149
-
7150
- if( color.useRGB )
7151
- val = hexToRgb( val );
7152
-
7153
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7249
+ widget.set( e.target.value, false, e );
7154
7250
  }, false );
7155
7251
 
7156
7252
  container.appendChild( color );
7157
7253
 
7158
7254
  this.queue( container );
7159
7255
 
7160
- const text_widget = this.addText( null, color.value, v => {
7161
- change_from_input = true;
7256
+ const textWidget = this.addText( null, color.value, v => {
7162
7257
  widget.set( v );
7163
- change_from_input = false;
7164
7258
  }, { width: "calc( 100% - 32px )"});
7165
7259
 
7166
- text_widget.domEl.style.marginLeft = "4px";
7260
+ textWidget.domEl.style.marginLeft = "4px";
7167
7261
 
7168
7262
  this.clearQueue();
7169
7263
 
@@ -7178,6 +7272,7 @@ class Panel {
7178
7272
  * @param {Number} value Default number value
7179
7273
  * @param {Function} callback Callback function on change
7180
7274
  * @param {*} options:
7275
+ * hideName: Don't use name as label [false]
7181
7276
  * className: Extra classes to customize style
7182
7277
  * disabled: Make the widget disabled [false]
7183
7278
  * left: The slider goes to the left instead of the right
@@ -7188,28 +7283,28 @@ class Panel {
7188
7283
 
7189
7284
  addRange( name, value, callback, options = {} ) {
7190
7285
 
7191
- let widget = this.create_widget( name, Widget.RANGE, options );
7286
+ let widget = this._createWidget( Widget.RANGE, name, value, options );
7192
7287
 
7193
7288
  widget.onGetValue = () => {
7194
- return +slider.value;
7289
+ return value;
7195
7290
  };
7196
7291
 
7197
- widget.onSetValue = ( newValue, skipCallback ) => {
7198
- slider.value = newValue;
7199
- Panel._dispatch_event( slider, "input", skipCallback );
7200
- };
7292
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7293
+
7294
+ if( isNaN( newValue ) )
7295
+ {
7296
+ return;
7297
+ }
7201
7298
 
7202
- let element = widget.domEl;
7299
+ slider.value = value = clamp( +newValue, +slider.min, +slider.max );
7203
7300
 
7204
- // add reset functionality
7205
- if( widget.name )
7206
- {
7207
- Panel._add_reset_property( element.domName, function() {
7208
- this.style.display = "none";
7209
- slider.value = slider.iValue;
7210
- Panel._dispatch_event( slider, "input" );
7211
- });
7212
- }
7301
+ if( !skipCallback )
7302
+ {
7303
+ this._trigger( new IEvent( name, options.left ? ( ( +slider.max ) - value + ( +slider.min ) ) : value, event ), callback );
7304
+ }
7305
+ };
7306
+
7307
+ const element = widget.domEl;
7213
7308
 
7214
7309
  // add widget value
7215
7310
 
@@ -7237,30 +7332,7 @@ class Panel {
7237
7332
  }
7238
7333
 
7239
7334
  slider.addEventListener( "input", e => {
7240
-
7241
- if( isNaN( e.target.valueAsNumber ) )
7242
- {
7243
- return;
7244
- }
7245
-
7246
- const skipCallback = e.detail;
7247
-
7248
- let val = e.target.value = clamp( +e.target.valueAsNumber, +slider.min, +slider.max );
7249
- slider.value = val;
7250
-
7251
- // Reset button (default value)
7252
- if( !skipCallback )
7253
- {
7254
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7255
- if( btn ) btn.style.display = val != slider.iValue ? "block": "none";
7256
- }
7257
-
7258
- if( options.left )
7259
- {
7260
- val = ( +slider.max ) - val + ( +slider.min );
7261
- }
7262
-
7263
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7335
+ widget.set( e.target.valueAsNumber, false, e );
7264
7336
  }, { passive: false });
7265
7337
 
7266
7338
  slider.addEventListener( "mousedown", function( e ) {
@@ -7293,8 +7365,8 @@ class Panel {
7293
7365
  container.appendChild( slider );
7294
7366
  element.appendChild( container );
7295
7367
 
7296
- // Remove branch padding and margins
7297
- if( !widget.name )
7368
+ const useNameAsLabel = !( options.hideName ?? false );
7369
+ if( !useNameAsLabel )
7298
7370
  {
7299
7371
  element.className += " noname";
7300
7372
  container.style.width = "100%";
@@ -7309,6 +7381,7 @@ class Panel {
7309
7381
  * @param {Number} value Default number value
7310
7382
  * @param {Function} callback Callback function on change
7311
7383
  * @param {*} options:
7384
+ * hideName: Don't use name as label [false]
7312
7385
  * disabled: Make the widget disabled [false]
7313
7386
  * step: Step of the input
7314
7387
  * precision: The number of digits to appear after the decimal point
@@ -7321,28 +7394,40 @@ class Panel {
7321
7394
 
7322
7395
  addNumber( name, value, callback, options = {} ) {
7323
7396
 
7324
- let widget = this.create_widget( name, Widget.NUMBER, options );
7397
+ let widget = this._createWidget( Widget.NUMBER, name, value, options );
7325
7398
 
7326
7399
  widget.onGetValue = () => {
7327
- return +vecinput.value;
7400
+ return value;
7328
7401
  };
7329
7402
 
7330
- widget.onSetValue = ( newValue, skipCallback ) => {
7331
- vecinput.value = round( newValue, options.precision );
7332
- Panel._dispatch_event( vecinput, "change", skipCallback );
7333
- };
7403
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7334
7404
 
7335
- let element = widget.domEl;
7405
+ if( isNaN( newValue ) )
7406
+ {
7407
+ return;
7408
+ }
7336
7409
 
7337
- // add reset functionality
7338
- if( widget.name )
7339
- {
7340
- Panel._add_reset_property( element.domName, function() {
7341
- this.style.display = "none";
7342
- vecinput.value = vecinput.iValue;
7343
- Panel._dispatch_event( vecinput, "change" );
7344
- });
7345
- }
7410
+ value = clamp( +newValue, +vecinput.min, +vecinput.max );
7411
+ vecinput.value = value = round( value, options.precision );
7412
+
7413
+ // Update slider!
7414
+ if( box.querySelector( ".lexinputslider" ) )
7415
+ {
7416
+ box.querySelector( ".lexinputslider" ).value = value;
7417
+ }
7418
+
7419
+ if( options.units )
7420
+ {
7421
+ vecinput.unitSpan.style.left = measureRealWidth( value ) + "px";
7422
+ }
7423
+
7424
+ if( !skipCallback )
7425
+ {
7426
+ this._trigger( new IEvent( name, value, event ), callback );
7427
+ }
7428
+ };
7429
+
7430
+ const element = widget.domEl;
7346
7431
 
7347
7432
  // add widget value
7348
7433
 
@@ -7401,9 +7486,7 @@ class Panel {
7401
7486
  slider.value = value;
7402
7487
 
7403
7488
  slider.addEventListener( "input", function( e ) {
7404
- let new_value = +this.valueAsNumber;
7405
- vecinput.value = round( new_value, options.precision );
7406
- Panel._dispatch_event( vecinput, "change" );
7489
+ widget.set( this.valueAsNumber, false, e );
7407
7490
  }, false );
7408
7491
 
7409
7492
  slider.addEventListener( "mousedown", function( e ) {
@@ -7428,13 +7511,13 @@ class Panel {
7428
7511
  vecinput.max = slider.max = newMax ?? vecinput.max;
7429
7512
  vecinput.step = newStep ?? vecinput.step;
7430
7513
  slider.step = newStep ?? slider.step;
7431
- Panel._dispatch_event( vecinput, "change", true );
7514
+ widget.set( value, true );
7432
7515
  };
7433
7516
  }
7434
7517
 
7435
7518
  vecinput.addEventListener( "input", function( e ) {
7436
- let new_value = +this.valueAsNumber;
7437
- vecinput.value = round( new_value, options.precision );
7519
+ value = +this.valueAsNumber;
7520
+ value = round( value, options.precision );
7438
7521
  if( options.units )
7439
7522
  {
7440
7523
  vecinput.unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
@@ -7450,44 +7533,12 @@ class Panel {
7450
7533
  let mult = options.step ?? 1;
7451
7534
  if( e.shiftKey ) mult *= 10;
7452
7535
  else if( e.altKey ) mult *= 0.1;
7453
- let new_value = ( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) );
7454
- this.value = round( new_value, options.precision );
7455
- Panel._dispatch_event(vecinput, "change");
7536
+ value = ( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) );
7537
+ widget.set( value, false, e );
7456
7538
  }, { passive: false });
7457
7539
 
7458
- vecinput.addEventListener( "change", e => {
7459
-
7460
- if( isNaN( e.target.valueAsNumber ) )
7461
- {
7462
- return;
7463
- }
7464
-
7465
- const skipCallback = e.detail;
7466
-
7467
- let val = e.target.value = clamp( +e.target.valueAsNumber, +vecinput.min, +vecinput.max );
7468
- val = options.precision ? round( val, options.precision ) : val;
7469
-
7470
- // Update slider!
7471
- if( box.querySelector( ".lexinputslider" ) )
7472
- {
7473
- box.querySelector( ".lexinputslider" ).value = val;
7474
- }
7475
-
7476
- vecinput.value = val;
7477
-
7478
- if( options.units )
7479
- {
7480
- vecinput.unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
7481
- }
7482
-
7483
- // Reset button (default value)
7484
- if( !skipCallback )
7485
- {
7486
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7487
- if( btn ) btn.style.display = val != vecinput.iValue ? "block": "none";
7488
- }
7489
-
7490
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7540
+ vecinput.addEventListener( "change", function( e ) {
7541
+ widget.set( this.valueAsNumber, false, e );
7491
7542
  }, { passive: false });
7492
7543
 
7493
7544
  // Add drag input
@@ -7531,9 +7582,9 @@ class Panel {
7531
7582
  let mult = options.step ?? 1;
7532
7583
  if( e.shiftKey ) mult *= 10;
7533
7584
  else if( e.altKey ) mult *= 0.1;
7534
- let new_value = ( +vecinput.valueAsNumber + mult * dt );
7535
- vecinput.value = ( +new_value ).toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
7536
- Panel._dispatch_event( vecinput, "change" );
7585
+ value = ( +vecinput.valueAsNumber + mult * dt );
7586
+ widget.set( value, false, e );
7587
+ // vecinput.value = ( +new_value ).toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
7537
7588
  }
7538
7589
 
7539
7590
  e.stopPropagation();
@@ -7563,7 +7614,8 @@ class Panel {
7563
7614
  element.appendChild( container );
7564
7615
 
7565
7616
  // Remove branch padding and margins
7566
- if( !widget.name )
7617
+ const useNameAsLabel = !( options.hideName ?? false );
7618
+ if( !useNameAsLabel )
7567
7619
  {
7568
7620
  element.className += " noname";
7569
7621
  container.style.width = "100%";
@@ -7574,17 +7626,12 @@ class Panel {
7574
7626
 
7575
7627
  static VECTOR_COMPONENTS = { 0: 'x', 1: 'y', 2: 'z', 3: 'w' };
7576
7628
 
7577
- _add_vector( num_components, name, value, callback, options = {} ) {
7629
+ _add_vector( numComponents, name, value, callback, options = {} ) {
7578
7630
 
7579
- num_components = clamp( num_components, 2, 4 );
7580
- value = value ?? new Array( num_components ).fill( 0 );
7631
+ numComponents = clamp( numComponents, 2, 4 );
7632
+ value = value ?? new Array( numComponents ).fill( 0 );
7581
7633
 
7582
- if( !name )
7583
- {
7584
- throw( "Set Widget Name!" );
7585
- }
7586
-
7587
- let widget = this.create_widget( name, Widget.VECTOR, options );
7634
+ let widget = this._createWidget( Widget.VECTOR, name, [].concat( value ), options );
7588
7635
 
7589
7636
  widget.onGetValue = () => {
7590
7637
  let inputs = element.querySelectorAll( "input" );
@@ -7596,33 +7643,30 @@ class Panel {
7596
7643
  return value;
7597
7644
  };
7598
7645
 
7599
- widget.onSetValue = ( newValue, skipCallback ) => {
7600
- const inputs = element.querySelectorAll( ".vecinput" );
7601
- if( inputs.length == newValue.length )
7646
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7647
+
7648
+ if( vectorInputs.length != newValue.length )
7602
7649
  {
7603
7650
  console.error( "Input length does not match vector length." );
7604
7651
  return;
7605
7652
  }
7606
7653
 
7607
- for( let i = 0; i < inputs.length; ++i )
7654
+ for( let i = 0; i < vectorInputs.length; ++i )
7608
7655
  {
7609
7656
  let value = newValue[ i ];
7610
- inputs[ i ].value = round( value, options.precision ) ?? 0;
7611
- Panel._dispatch_event( inputs[ i ], "change", skipCallback );
7657
+ value = clamp( value, +vectorInputs[ i ].min, +vectorInputs[ i ].max );
7658
+ value = round( value, options.precision ) ?? 0;
7659
+ vectorInputs[ i ].value = newValue[ i ] = value;
7612
7660
  }
7613
- };
7614
7661
 
7615
- let element = widget.domEl;
7616
-
7617
- // Add reset functionality
7618
- Panel._add_reset_property( element.domName, function() {
7619
- this.style.display = "none";
7620
- for( let v of element.querySelectorAll( ".vecinput" ) )
7662
+ if( !skipCallback )
7621
7663
  {
7622
- v.value = v.iValue;
7623
- Panel._dispatch_event( v, "change" );
7664
+ this._trigger( new IEvent( name, newValue, event ), callback );
7624
7665
  }
7625
- });
7666
+ };
7667
+
7668
+ const element = widget.domEl;
7669
+ const vectorInputs = [];
7626
7670
 
7627
7671
  // Add widget value
7628
7672
 
@@ -7630,20 +7674,21 @@ class Panel {
7630
7674
  container.className = "lexvector";
7631
7675
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
7632
7676
 
7633
- for( let i = 0; i < num_components; ++i )
7677
+ for( let i = 0; i < numComponents; ++i )
7634
7678
  {
7635
7679
  let box = document.createElement( 'div' );
7636
7680
  box.className = "vecbox";
7637
7681
  box.innerHTML = "<span class='" + Panel.VECTOR_COMPONENTS[ i ] + "'></span>";
7638
7682
 
7639
7683
  let vecinput = document.createElement( 'input' );
7640
- vecinput.className = "vecinput v" + num_components;
7684
+ vecinput.className = "vecinput v" + numComponents;
7641
7685
  vecinput.min = options.min ?? -1e24;
7642
7686
  vecinput.max = options.max ?? 1e24;
7643
7687
  vecinput.step = options.step ?? "any";
7644
7688
  vecinput.type = "number";
7645
- vecinput.id = "vec" + num_components + "_" + simple_guidGenerator();
7689
+ vecinput.id = "vec" + numComponents + "_" + simple_guidGenerator();
7646
7690
  vecinput.idx = i;
7691
+ vectorInputs[ i ] = vecinput;
7647
7692
 
7648
7693
  if( value[ i ].constructor == Number )
7649
7694
  {
@@ -7693,21 +7738,12 @@ class Panel {
7693
7738
  return;
7694
7739
  }
7695
7740
 
7696
- const skipCallback = e.detail;
7697
-
7698
- let val = e.target.value = clamp( e.target.value, +vecinput.min, +vecinput.max );
7741
+ let val = clamp( e.target.value, +vecinput.min, +vecinput.max );
7699
7742
  val = round( val, options.precision );
7700
7743
 
7701
- // Reset button (default value)
7702
- if( !skipCallback )
7703
- {
7704
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7705
- if( btn ) btn.style.display = val != vecinput.iValue ? "block" : "none";
7706
- }
7707
-
7708
7744
  if( locker.locked )
7709
7745
  {
7710
- for( let v of element.querySelectorAll( ".vecinput" ) )
7746
+ for( let v of vectorInputs )
7711
7747
  {
7712
7748
  v.value = val;
7713
7749
  value[ v.idx ] = val;
@@ -7719,7 +7755,7 @@ class Panel {
7719
7755
  value[ e.target.idx ] = val;
7720
7756
  }
7721
7757
 
7722
- if( !skipCallback ) this._trigger( new IEvent( name, value, e ), callback );
7758
+ widget.set( value, false, e );
7723
7759
  }, false );
7724
7760
 
7725
7761
  // Add drag input
@@ -7810,17 +7846,14 @@ class Panel {
7810
7846
  if( options.min !== undefined || options.max !== undefined )
7811
7847
  {
7812
7848
  widget.setLimits = ( newMin, newMax, newStep ) => {
7813
- const inputs = element.querySelectorAll(".vecinput");
7814
- for( let v of inputs )
7849
+ for( let v of vectorInputs )
7815
7850
  {
7816
7851
  v.min = newMin ?? v.min;
7817
7852
  v.max = newMax ?? v.max;
7818
7853
  v.step = newStep ?? v.step;
7819
- Panel._dispatch_event( v, "change", true );
7820
7854
  }
7821
7855
 
7822
- // To call onChange callback
7823
- this._trigger( new IEvent( name, value ), callback );
7856
+ widget.set( value, true );
7824
7857
  };
7825
7858
  }
7826
7859
 
@@ -7881,13 +7914,14 @@ class Panel {
7881
7914
  * @param {Number} value Default number value
7882
7915
  * @param {Function} callback Callback function on change
7883
7916
  * @param {*} options:
7917
+ * hideName: Don't use name as label [false]
7884
7918
  * disabled: Make the widget disabled [false]
7885
7919
  * units: Unit as string added to the end of the value
7886
7920
  */
7887
7921
 
7888
7922
  addSize( name, value, callback, options = {} ) {
7889
7923
 
7890
- let widget = this.create_widget( name, Widget.SIZE, options );
7924
+ let widget = this._createWidget( Widget.SIZE, name, value, options );
7891
7925
 
7892
7926
  widget.onGetValue = () => {
7893
7927
  const value = [];
@@ -7898,14 +7932,14 @@ class Panel {
7898
7932
  return value;
7899
7933
  };
7900
7934
 
7901
- widget.onSetValue = ( newValue, skipCallback ) => {
7935
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7902
7936
  for( let i = 0; i < element.dimensions.length; ++i )
7903
7937
  {
7904
7938
  element.dimensions[ i ].set( newValue[ i ], skipCallback );
7905
7939
  }
7906
7940
  };
7907
7941
 
7908
- let element = widget.domEl;
7942
+ const element = widget.domEl;
7909
7943
 
7910
7944
  this.queue( element );
7911
7945
 
@@ -7978,7 +8012,8 @@ class Panel {
7978
8012
  }
7979
8013
 
7980
8014
  // Remove branch padding and margins
7981
- if( !widget.name )
8015
+ const useNameAsLabel = !( options.hideName ?? false );
8016
+ if( !useNameAsLabel )
7982
8017
  {
7983
8018
  element.className += " noname";
7984
8019
  container.style.width = "100%";
@@ -8002,18 +8037,13 @@ class Panel {
8002
8037
 
8003
8038
  addPad( name, value, callback, options = {} ) {
8004
8039
 
8005
- if( !name )
8006
- {
8007
- throw( "Set Widget Name!" );
8008
- }
8009
-
8010
- let widget = this.create_widget( name, Widget.PAD, options );
8040
+ let widget = this._createWidget( Widget.PAD, name, null, options );
8011
8041
 
8012
8042
  widget.onGetValue = () => {
8013
8043
  return thumb.value.xy;
8014
8044
  };
8015
8045
 
8016
- widget.onSetValue = ( newValue, skipCallback ) => {
8046
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8017
8047
  thumb.value.set( newValue[ 0 ], newValue[ 1 ] );
8018
8048
  _updateValue( thumb.value );
8019
8049
  if( !skipCallback )
@@ -8022,7 +8052,7 @@ class Panel {
8022
8052
  }
8023
8053
  };
8024
8054
 
8025
- let element = widget.domEl;
8055
+ const element = widget.domEl;
8026
8056
 
8027
8057
  var container = document.createElement( 'div' );
8028
8058
  container.className = "lexpad";
@@ -8127,17 +8157,13 @@ class Panel {
8127
8157
 
8128
8158
  addProgress( name, value, options = {} ) {
8129
8159
 
8130
- if( !name )
8131
- {
8132
- throw("Set Widget Name!");
8133
- }
8134
-
8135
- let widget = this.create_widget( name, Widget.PROGRESS, options );
8160
+ let widget = this._createWidget( Widget.PROGRESS, name, value, options );
8136
8161
 
8137
8162
  widget.onGetValue = () => {
8138
8163
  return progress.value;
8139
8164
  };
8140
- widget.onSetValue = ( newValue, skipCallback ) => {
8165
+
8166
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8141
8167
  element.querySelector("meter").value = newValue;
8142
8168
  _updateColor();
8143
8169
  if( element.querySelector("span") )
@@ -8146,7 +8172,7 @@ class Panel {
8146
8172
  }
8147
8173
  };
8148
8174
 
8149
- let element = widget.domEl;
8175
+ const element = widget.domEl;
8150
8176
 
8151
8177
  var container = document.createElement('div');
8152
8178
  container.className = "lexprogress";
@@ -8268,13 +8294,8 @@ class Panel {
8268
8294
 
8269
8295
  addFile( name, callback, options = { } ) {
8270
8296
 
8271
- if( !name )
8272
- {
8273
- throw( "Set Widget Name!" );
8274
- }
8275
-
8276
- let widget = this.create_widget( name, Widget.FILE, options );
8277
- let element = widget.domEl;
8297
+ let widget = this._createWidget( Widget.FILE, name, null, options );
8298
+ const element = widget.domEl;
8278
8299
 
8279
8300
  let local = options.local ?? true;
8280
8301
  let type = options.type ?? 'text';
@@ -8360,20 +8381,28 @@ class Panel {
8360
8381
 
8361
8382
  addTree( name, data, options = {} ) {
8362
8383
 
8384
+ options.hideName = true;
8385
+
8386
+ const widget = this._createWidget( Widget.TREE, name, null, options );
8387
+ const element = widget.domEl;
8388
+
8363
8389
  let container = document.createElement('div');
8364
8390
  container.className = "lextree";
8391
+ element.appendChild( container );
8365
8392
 
8366
8393
  if( name )
8367
8394
  {
8368
8395
  let title = document.createElement('span');
8369
8396
  title.innerHTML = name;
8370
- container.appendChild(title);
8397
+ container.appendChild( title );
8371
8398
  }
8372
8399
 
8373
8400
  let toolsDiv = document.createElement('div');
8374
8401
  toolsDiv.className = "lextreetools";
8375
- if(!name)
8402
+ if( !name )
8403
+ {
8376
8404
  toolsDiv.className += " notitle";
8405
+ }
8377
8406
 
8378
8407
  // Tree icons
8379
8408
  if( options.icons )
@@ -8384,7 +8413,7 @@ class Panel {
8384
8413
  iconEl.title = data.name;
8385
8414
  iconEl.className = "lexicon " + data.icon;
8386
8415
  iconEl.addEventListener("click", data.callback);
8387
- toolsDiv.appendChild(iconEl);
8416
+ toolsDiv.appendChild( iconEl );
8388
8417
  }
8389
8418
  }
8390
8419
 
@@ -8392,35 +8421,36 @@ class Panel {
8392
8421
 
8393
8422
  options.filter = options.filter ?? true;
8394
8423
 
8395
- let node_filter_input = null;
8396
- if(options.filter)
8424
+ let nodeFilterInput = null;
8425
+ if( options.filter )
8397
8426
  {
8398
- node_filter_input = document.createElement('input');
8399
- node_filter_input.id = "lexnodetree_filter";
8400
- node_filter_input.setAttribute("placeholder", "Filter..");
8401
- node_filter_input.style.width = "calc( 100% - 17px )";
8402
- node_filter_input.addEventListener('input', function(){
8427
+ nodeFilterInput = document.createElement('input');
8428
+ nodeFilterInput.id = "lexnodetree_filter";
8429
+ nodeFilterInput.setAttribute("placeholder", "Filter..");
8430
+ nodeFilterInput.style.width = "calc( 100% - 17px )";
8431
+ nodeFilterInput.addEventListener('input', function(){
8403
8432
  nodeTree.refresh();
8404
8433
  });
8405
8434
 
8406
8435
  let searchIcon = document.createElement('a');
8407
8436
  searchIcon.className = "lexicon fa-solid fa-magnifying-glass";
8408
- toolsDiv.appendChild(node_filter_input);
8409
- toolsDiv.appendChild(searchIcon);
8437
+ toolsDiv.appendChild( nodeFilterInput );
8438
+ toolsDiv.appendChild( searchIcon );
8410
8439
  }
8411
8440
 
8412
- if(options.icons || options.filter)
8413
- container.appendChild(toolsDiv);
8441
+ if( options.icons || options.filter )
8442
+ {
8443
+ container.appendChild( toolsDiv );
8444
+ }
8414
8445
 
8415
8446
  // Tree
8416
8447
 
8417
8448
  let list = document.createElement('ul');
8418
- list.addEventListener("contextmenu", function(e) {
8449
+ list.addEventListener("contextmenu", function( e ) {
8419
8450
  e.preventDefault();
8420
8451
  });
8421
8452
 
8422
- container.appendChild(list);
8423
- this.root.appendChild(container);
8453
+ container.appendChild( list );
8424
8454
 
8425
8455
  const nodeTree = new NodeTree( container, data, options );
8426
8456
  return nodeTree;
@@ -8434,13 +8464,14 @@ class Panel {
8434
8464
 
8435
8465
  var element = document.createElement('div');
8436
8466
  element.className = "lexseparator";
8437
- let widget = new Widget( null, Widget.SEPARATOR );
8467
+
8468
+ let widget = new Widget( Widget.SEPARATOR );
8438
8469
  widget.domEl = element;
8439
8470
 
8440
- if( this.current_branch )
8471
+ if( this._currentBranch )
8441
8472
  {
8442
- this.current_branch.content.appendChild( element );
8443
- this.current_branch.widgets.push( widget );
8473
+ this._currentBranch.content.appendChild( element );
8474
+ this._currentBranch.widgets.push( widget );
8444
8475
  }
8445
8476
  else
8446
8477
  {
@@ -8463,12 +8494,8 @@ class Panel {
8463
8494
 
8464
8495
  addTabs( tabs, options = {} ) {
8465
8496
 
8466
- let root = this.current_branch ? this.current_branch.content : this.root;
8467
-
8468
- if( !this.current_branch )
8469
- {
8470
- console.warn("No current branch!");
8471
- }
8497
+ const widget = this._createWidget( Widget.TABS, null, null, options );
8498
+ const element = widget.domEl;
8472
8499
 
8473
8500
  if( tabs.constructor != Array )
8474
8501
  {
@@ -8488,7 +8515,7 @@ class Panel {
8488
8515
  let tabContainer = document.createElement( 'div' );
8489
8516
  tabContainer.className = 'tabs';
8490
8517
  container.appendChild( tabContainer );
8491
- root.appendChild( container );
8518
+ element.appendChild( container );
8492
8519
 
8493
8520
  for( let i = 0; i < tabs.length; ++i )
8494
8521
  {
@@ -8512,10 +8539,9 @@ class Panel {
8512
8539
  container.appendChild( infoContainer );
8513
8540
 
8514
8541
  tabEl.addEventListener( 'click', e => {
8515
-
8516
8542
  // Change selected tab
8517
8543
  tabContainer.querySelectorAll( '.lextab' ).forEach( e => { e.classList.remove( 'selected' ); } );
8518
- e.target.classList.add( 'selected' );
8544
+ tabEl.classList.add( 'selected' );
8519
8545
  // Hide all tabs content
8520
8546
  container.querySelectorAll(".widgets").forEach( e => { e.toggleAttribute( 'hidden', true ); } );
8521
8547
  // Show tab content
@@ -8556,42 +8582,38 @@ class Panel {
8556
8582
 
8557
8583
  addCounter( name, value, callback, options = { } ) {
8558
8584
 
8559
- let widget = this.create_widget( name, Widget.COUNTER, options );
8585
+ let widget = this._createWidget( Widget.COUNTER, name, value, options );
8560
8586
 
8561
8587
  widget.onGetValue = () => {
8562
8588
  return counterText.count;
8563
8589
  };
8564
8590
 
8565
- widget.onSetValue = ( newValue, skipCallback ) => {
8566
- _onChange( newValue, skipCallback );
8591
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8592
+ newValue = clamp( newValue, min, max );
8593
+ counterText.count = newValue;
8594
+ counterText.innerHTML = newValue;
8595
+ if( !skipCallback )
8596
+ {
8597
+ this._trigger( new IEvent( name, newValue, event ), callback );
8598
+ }
8567
8599
  };
8568
8600
 
8569
- let element = widget.domEl;
8601
+ const element = widget.domEl;
8570
8602
 
8571
8603
  const min = options.min ?? 0;
8572
8604
  const max = options.max ?? 100;
8573
8605
  const step = options.step ?? 1;
8574
8606
 
8575
- const _onChange = ( value, skipCallback, event ) => {
8576
- value = clamp( value, min, max );
8577
- counterText.count = value;
8578
- counterText.innerHTML = value;
8579
- if( !skipCallback )
8580
- {
8581
- this._trigger( new IEvent( name, value, event ), callback );
8582
- }
8583
- }
8584
-
8585
8607
  const container = document.createElement( 'div' );
8586
8608
  container.className = "lexcounter";
8587
8609
  element.appendChild( container );
8588
8610
 
8589
8611
  this.queue( container );
8590
8612
 
8591
- this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-minus'></a>", (value, e) => {
8613
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-minus'></a>", ( value, e ) => {
8592
8614
  let mult = step ?? 1;
8593
8615
  if( e.shiftKey ) mult *= 10;
8594
- _onChange( counterText.count - mult, false, e );
8616
+ widget.set( counterText.count - mult, false, e );
8595
8617
  }, { className: "micro", skipInlineCount: true, title: "Minus" });
8596
8618
 
8597
8619
  this.clearQueue();
@@ -8616,10 +8638,10 @@ class Panel {
8616
8638
 
8617
8639
  this.queue( container );
8618
8640
 
8619
- this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-plus'></a>", (value, e) => {
8641
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-plus'></a>", ( value, e ) => {
8620
8642
  let mult = step ?? 1;
8621
8643
  if( e.shiftKey ) mult *= 10;
8622
- _onChange( counterText.count + mult, false, e );
8644
+ widget.set( counterText.count + mult, false, e );
8623
8645
  }, { className: "micro", skipInlineCount: true, title: "Plus" });
8624
8646
 
8625
8647
  this.clearQueue();
@@ -8632,6 +8654,7 @@ class Panel {
8632
8654
  * @param {String} name Widget name
8633
8655
  * @param {Number} data Table data
8634
8656
  * @param {*} options:
8657
+ * hideName: Don't use name as label [false]
8635
8658
  * head: Table headers (each of the headers per column)
8636
8659
  * body: Table body (data per row for each column)
8637
8660
  * rowActions: Allow to add actions per row
@@ -8646,17 +8669,17 @@ class Panel {
8646
8669
  throw( "Data is needed to create a table!" );
8647
8670
  }
8648
8671
 
8649
- let widget = this.create_widget( name, Widget.TABLE, options );
8672
+ let widget = this._createWidget( Widget.TABLE, name, null, options );
8650
8673
 
8651
8674
  widget.onGetValue = () => {
8652
8675
 
8653
8676
  };
8654
8677
 
8655
- widget.onSetValue = ( newValue, skipCallback ) => {
8678
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8656
8679
 
8657
8680
  };
8658
8681
 
8659
- let element = widget.domEl;
8682
+ const element = widget.domEl;
8660
8683
 
8661
8684
  const container = document.createElement('div');
8662
8685
  container.className = "lextable";
@@ -8844,7 +8867,8 @@ class Panel {
8844
8867
 
8845
8868
  widget.refreshTable();
8846
8869
 
8847
- if( !widget.name )
8870
+ const useNameAsLabel = !( options.hideName ?? false );
8871
+ if( !useNameAsLabel )
8848
8872
  {
8849
8873
  element.className += " noname";
8850
8874
  container.style.width = "100%";
@@ -8891,12 +8915,14 @@ class Branch {
8891
8915
  var title = document.createElement( 'div' );
8892
8916
  title.className = "lexbranchtitle";
8893
8917
 
8894
- title.innerHTML = "<a class='fa-solid fa-angle-up switch-branch-button'></a>";
8895
8918
  if( options.icon )
8896
8919
  {
8897
- title.innerHTML += "<a class='branchicon " + options.icon + "' style='margin-right: 8px; margin-bottom: -2px;'>";
8920
+ title.innerHTML = "<a class='branchicon " + options.icon + "'>";
8898
8921
  }
8899
- title.innerHTML += name || "Branch";
8922
+
8923
+ title.innerHTML += ( name || "Branch" );
8924
+
8925
+ title.innerHTML += "<a class='fa-solid fa-angle-up switch-branch-button'></a>";
8900
8926
 
8901
8927
  root.appendChild( title );
8902
8928
 
@@ -9054,7 +9080,7 @@ class Branch {
9054
9080
  for( var i = 0; i < this.widgets.length; i++ )
9055
9081
  {
9056
9082
  let widget = this.widgets[ i ];
9057
- let element = widget.domEl;
9083
+ const element = widget.domEl;
9058
9084
 
9059
9085
  if( element.children.length < 2 )
9060
9086
  {
@@ -9227,7 +9253,7 @@ class Dialog {
9227
9253
  titleDiv.innerHTML = title;
9228
9254
  titleDiv.setAttribute('draggable', false);
9229
9255
 
9230
- titleDiv.oncontextmenu = function(e) {
9256
+ titleDiv.oncontextmenu = function( e ) {
9231
9257
  e.preventDefault();
9232
9258
  e.stopPropagation();
9233
9259
 
@@ -9567,7 +9593,7 @@ class ContextMenu {
9567
9593
  }
9568
9594
  }
9569
9595
 
9570
- _adjust_position( div, margin, useAbsolute = false ) {
9596
+ _adjustPosition( div, margin, useAbsolute = false ) {
9571
9597
 
9572
9598
  let rect = div.getBoundingClientRect();
9573
9599
 
@@ -9608,7 +9634,7 @@ class ContextMenu {
9608
9634
  }
9609
9635
  }
9610
9636
 
9611
- _create_submenu( o, k, c, d ) {
9637
+ _createSubmenu( o, k, c, d ) {
9612
9638
 
9613
9639
  this.root.querySelectorAll( ".lexcontextmenu" ).forEach( cm => cm.remove() );
9614
9640
 
@@ -9620,7 +9646,7 @@ class ContextMenu {
9620
9646
  {
9621
9647
  const subitem = o[ k ][ i ];
9622
9648
  const subkey = Object.keys( subitem )[ 0 ];
9623
- this._create_entry(subitem, subkey, contextmenu, d);
9649
+ this._createEntry(subitem, subkey, contextmenu, d);
9624
9650
  }
9625
9651
 
9626
9652
  var rect = c.getBoundingClientRect();
@@ -9628,10 +9654,10 @@ class ContextMenu {
9628
9654
  contextmenu.style.marginTop = 3.5 - c.offsetHeight + "px";
9629
9655
 
9630
9656
  // Set final width
9631
- this._adjust_position( contextmenu, 6, true );
9657
+ this._adjustPosition( contextmenu, 6, true );
9632
9658
  }
9633
9659
 
9634
- _create_entry( o, k, c, d ) {
9660
+ _createEntry( o, k, c, d ) {
9635
9661
 
9636
9662
  const hasSubmenu = o[ k ].length;
9637
9663
  let entry = document.createElement('div');
@@ -9676,7 +9702,7 @@ class ContextMenu {
9676
9702
  return;
9677
9703
 
9678
9704
  if( LX.OPEN_CONTEXTMENU_ENTRY == 'click' )
9679
- this._create_submenu( o, k, entry, ++d );
9705
+ this._createSubmenu( o, k, entry, ++d );
9680
9706
  });
9681
9707
 
9682
9708
  if( !hasSubmenu )
@@ -9692,7 +9718,7 @@ class ContextMenu {
9692
9718
  if(entry.built)
9693
9719
  return;
9694
9720
  entry.built = true;
9695
- this._create_submenu( o, k, entry, ++d );
9721
+ this._createSubmenu( o, k, entry, ++d );
9696
9722
  e.stopPropagation();
9697
9723
  });
9698
9724
  }
@@ -9704,7 +9730,7 @@ class ContextMenu {
9704
9730
  }
9705
9731
 
9706
9732
  onCreate() {
9707
- doAsync( () => this._adjust_position( this.root, 6 ) );
9733
+ doAsync( () => this._adjustPosition( this.root, 6 ) );
9708
9734
  }
9709
9735
 
9710
9736
  add( path, options = {} ) {
@@ -9734,13 +9760,13 @@ class ContextMenu {
9734
9760
 
9735
9761
  if( found )
9736
9762
  {
9737
- insert( tokens[idx++], found );
9763
+ insert( tokens[ idx++ ], found );
9738
9764
  }
9739
9765
  else
9740
9766
  {
9741
9767
  let item = {};
9742
9768
  item[ token ] = [];
9743
- const nextToken = tokens[idx++];
9769
+ const nextToken = tokens[ idx++ ];
9744
9770
  // Check if last token -> add callback
9745
9771
  if( !nextToken )
9746
9772
  {
@@ -9760,13 +9786,15 @@ class ContextMenu {
9760
9786
 
9761
9787
  const setParent = _item => {
9762
9788
 
9763
- let key = Object.keys(_item)[0];
9789
+ let key = Object.keys( _item )[ 0 ];
9764
9790
  let children = _item[ key ];
9765
9791
 
9766
- if(!children.length)
9792
+ if( !children.length )
9793
+ {
9767
9794
  return;
9795
+ }
9768
9796
 
9769
- if(children.find( c => Object.keys(c)[0] == key ) == null)
9797
+ if( children.find( c => Object.keys(c)[0] == key ) == null )
9770
9798
  {
9771
9799
  const parent = {};
9772
9800
  parent[ key ] = [];
@@ -9776,14 +9804,18 @@ class ContextMenu {
9776
9804
 
9777
9805
  for( var child of _item[ key ] )
9778
9806
  {
9779
- let k = Object.keys(child)[0];
9780
- for( var i = 0; i < child[k].length; ++i )
9781
- setParent(child);
9807
+ let k = Object.keys( child )[ 0 ];
9808
+ for( var i = 0; i < child[ k ].length; ++i )
9809
+ {
9810
+ setParent( child );
9811
+ }
9782
9812
  }
9783
9813
  };
9784
9814
 
9785
9815
  for( let item of this.items )
9786
- setParent(item);
9816
+ {
9817
+ setParent( item );
9818
+ }
9787
9819
 
9788
9820
  // Create elements
9789
9821
 
@@ -9793,9 +9825,11 @@ class ContextMenu {
9793
9825
  let pKey = "eId" + getSupportedDOMName( key );
9794
9826
 
9795
9827
  // Item already created
9796
- const id = "#" + (item.id ?? pKey);
9797
- if( !this.root.querySelector(id) )
9798
- this._create_entry(item, key, this.root, -1);
9828
+ const id = "#" + ( item.id ?? pKey );
9829
+ if( !this.root.querySelector( id ) )
9830
+ {
9831
+ this._createEntry( item, key, this.root, -1 );
9832
+ }
9799
9833
  }
9800
9834
  }
9801
9835
 
@@ -11056,7 +11090,7 @@ class AssetView {
11056
11090
  itemEl.appendChild(info);
11057
11091
  }
11058
11092
 
11059
- itemEl.addEventListener('click', function(e) {
11093
+ itemEl.addEventListener('click', function( e ) {
11060
11094
  e.stopImmediatePropagation();
11061
11095
  e.stopPropagation();
11062
11096
 
@@ -11093,7 +11127,7 @@ class AssetView {
11093
11127
 
11094
11128
  if( that.contextMenu )
11095
11129
  {
11096
- itemEl.addEventListener('contextmenu', function(e) {
11130
+ itemEl.addEventListener('contextmenu', function( e ) {
11097
11131
  e.preventDefault();
11098
11132
 
11099
11133
  const multiple = that.content.querySelectorAll('.selected').length;
@@ -11118,7 +11152,7 @@ class AssetView {
11118
11152
  });
11119
11153
  }
11120
11154
 
11121
- itemEl.addEventListener("dragstart", function(e) {
11155
+ itemEl.addEventListener("dragstart", function( e ) {
11122
11156
  e.preventDefault();
11123
11157
  }, false );
11124
11158
 
@@ -11515,7 +11549,7 @@ Object.assign(LX, {
11515
11549
  script.src = url[i] + ( version ? "?version=" + version : "" );
11516
11550
  script.original_src = url[i];
11517
11551
  script.async = false;
11518
- script.onload = function(e) {
11552
+ script.onload = function( e ) {
11519
11553
  total--;
11520
11554
  loaded_scripts.push(this);
11521
11555
  if(total)