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.
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  // Lexgui.js @jxarco
4
2
 
5
3
  /**
@@ -8,7 +6,7 @@
8
6
  */
9
7
 
10
8
  var LX = {
11
- version: "0.4.0",
9
+ version: "0.4.2",
12
10
  ready: false,
13
11
  components: [], // Specific pre-build components
14
12
  signals: {}, // Events and triggers
@@ -20,13 +18,13 @@ LX.MOUSE_LEFT_CLICK = 0;
20
18
  LX.MOUSE_MIDDLE_CLICK = 1;
21
19
  LX.MOUSE_RIGHT_CLICK = 2;
22
20
 
23
- LX.MOUSE_DOUBLE_CLICK = 2;
24
- LX.MOUSE_TRIPLE_CLICK = 3;
21
+ LX.MOUSE_DOUBLE_CLICK = 2;
22
+ LX.MOUSE_TRIPLE_CLICK = 3;
25
23
 
26
- LX.CURVE_MOVEOUT_CLAMP = 0;
24
+ LX.CURVE_MOVEOUT_CLAMP = 0;
27
25
  LX.CURVE_MOVEOUT_DELETE = 1;
28
26
 
29
- LX.DRAGGABLE_Z_INDEX = 101;
27
+ LX.DRAGGABLE_Z_INDEX = 101;
30
28
 
31
29
  function clamp( num, min, max ) { return Math.min( Math.max( num, min ), max ); }
32
30
  function round( number, precision ) { return precision == 0 ? Math.floor( number ) : +(( number ).toFixed( precision ?? 2 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' )); }
@@ -165,7 +163,6 @@ function getThemeColor( colorName )
165
163
  {
166
164
  const r = getComputedStyle( document.querySelector( ':root' ) );
167
165
  const value = r.getPropertyValue( '--' + colorName );
168
- const theme = document.documentElement.getAttribute( "data-theme" );
169
166
 
170
167
  if( value.includes( "light-dark" ) )
171
168
  {
@@ -239,7 +236,7 @@ LX.rgbToHex = rgbToHex;
239
236
  /**
240
237
  * @method measureRealWidth
241
238
  * @description Measure the pixel width of a text
242
- * @param {Object} value Text to measure
239
+ * @param {Number} value Text to measure
243
240
  * @param {Number} paddingPlusMargin Padding offset
244
241
  */
245
242
  function measureRealWidth( value, paddingPlusMargin = 8 )
@@ -439,7 +436,7 @@ function makeCollapsible( domEl, content, parent, options = { } )
439
436
  actionIcon.dataset[ "collapsed" ] = collapsed;
440
437
  actionIcon.style.marginLeft = "auto";
441
438
 
442
- actionIcon.addEventListener( "click", function(e) {
439
+ actionIcon.addEventListener( "click", function( e ) {
443
440
  e.preventDefault();
444
441
  e.stopPropagation();
445
442
  if( this.dataset[ "collapsed" ] )
@@ -570,12 +567,13 @@ LX.makeCodeSnippet = makeCodeSnippet;
570
567
  * @description Gets an SVG element using one of LX.ICONS
571
568
  * @param {String} iconName
572
569
  * @param {String} iconTitle
570
+ * @param {String} extraClass
573
571
  */
574
- function makeIcon( iconName, iconTitle )
572
+ function makeIcon( iconName, iconTitle, extraClass = "" )
575
573
  {
576
574
  const icon = document.createElement( "a" );
577
575
  icon.title = iconTitle ?? "";
578
- icon.className = "lexicon";
576
+ icon.className = "lexicon " + extraClass;
579
577
  icon.innerHTML = LX.ICONS[ iconName ] ?? "";
580
578
  return icon;
581
579
  }
@@ -786,17 +784,17 @@ function _createCommandbar( root )
786
784
  searchItem.entry_name = t;
787
785
  searchItem.callback = c;
788
786
  searchItem.item = i;
789
- searchItem.addEventListener('click', function(e) {
787
+ searchItem.addEventListener('click', function( e ) {
790
788
  this.callback.call( window, this.entry_name );
791
789
  LX.setCommandbarState( false );
792
790
  _resetBar( true );
793
791
  });
794
- searchItem.addEventListener('mouseenter', function(e) {
792
+ searchItem.addEventListener('mouseenter', function( e ) {
795
793
  commandbar.querySelectorAll(".hovered").forEach(e => e.classList.remove('hovered'));
796
794
  this.classList.add('hovered');
797
795
  hoverElId = allItems.indexOf( this );
798
796
  });
799
- searchItem.addEventListener('mouseleave', function(e) {
797
+ searchItem.addEventListener('mouseleave', function( e ) {
800
798
  this.classList.remove('hovered');
801
799
  });
802
800
  allItems.push( searchItem );
@@ -877,7 +875,7 @@ function _createCommandbar( root )
877
875
  }
878
876
  }
879
877
 
880
- input.addEventListener('input', function(e) {
878
+ input.addEventListener('input', function( e ) {
881
879
  commandbar._addElements( this.value.toLowerCase() );
882
880
  });
883
881
 
@@ -926,7 +924,19 @@ function init( options = { } )
926
924
  this.container = document.getElementById( options.container );
927
925
  }
928
926
 
929
- document.documentElement.setAttribute( "data-strictVP", ( options.strictViewport ?? true ) ? "true" : "false" );
927
+ this.usingStrictViewport = options.strictViewport ?? true;
928
+ document.documentElement.setAttribute( "data-strictVP", ( this.usingStrictViewport ) ? "true" : "false" );
929
+
930
+ if( !this.usingStrictViewport )
931
+ {
932
+ document.addEventListener( "scroll", e => {
933
+ // Get all active menuboxes
934
+ const mbs = document.body.querySelectorAll( ".lexmenubox" );
935
+ mbs.forEach( ( mb ) => {
936
+ mb._updatePosition();
937
+ } );
938
+ } );
939
+ }
930
940
 
931
941
  this.commandbar = _createCommandbar( this.container );
932
942
 
@@ -1293,7 +1303,7 @@ LX.makeContainer = makeContainer;
1293
1303
 
1294
1304
  class IEvent {
1295
1305
 
1296
- constructor(name, value, domEvent) {
1306
+ constructor( name, value, domEvent ) {
1297
1307
  this.name = name;
1298
1308
  this.value = value;
1299
1309
  this.domEvent = domEvent;
@@ -2063,7 +2073,7 @@ class Area {
2063
2073
  }
2064
2074
 
2065
2075
  // Generate DOM elements after adding all entries
2066
- sidebar._build();
2076
+ sidebar.update();
2067
2077
 
2068
2078
  LX.menubars.push( sidebar );
2069
2079
 
@@ -2366,17 +2376,17 @@ class Tabs {
2366
2376
 
2367
2377
  let that = this;
2368
2378
 
2369
- container.addEventListener("dragenter", function(e) {
2379
+ container.addEventListener("dragenter", function( e ) {
2370
2380
  e.preventDefault(); // Prevent default action (open as link for some elements)
2371
2381
  this.classList.add("dockingtab");
2372
2382
  });
2373
2383
 
2374
- container.addEventListener("dragleave", function(e) {
2384
+ container.addEventListener("dragleave", function( e ) {
2375
2385
  e.preventDefault(); // Prevent default action (open as link for some elements)
2376
2386
  this.classList.remove("dockingtab");
2377
2387
  });
2378
2388
 
2379
- container.addEventListener("drop", function(e) {
2389
+ container.addEventListener("drop", function( e ) {
2380
2390
  e.preventDefault(); // Prevent default action (open as link for some elements)
2381
2391
 
2382
2392
  const tab_id = e.dataTransfer.getData("source");
@@ -2673,6 +2683,195 @@ class Menubar {
2673
2683
  this.shorts = { };
2674
2684
  }
2675
2685
 
2686
+ _resetMenubar() {
2687
+
2688
+ // Menu entries are in the menubar..
2689
+ this.root.querySelectorAll(".lexmenuentry").forEach( _entry => {
2690
+ _entry.classList.remove( 'selected' );
2691
+ _entry.built = false;
2692
+ } );
2693
+
2694
+ // Menuboxes are in the root area!
2695
+ LX.root.querySelectorAll(".lexmenubox").forEach(e => e.remove());
2696
+
2697
+ // Next time we need to click again
2698
+ this.focused = false;
2699
+ }
2700
+
2701
+ _createSubmenu( o, k, c, d ) {
2702
+
2703
+ let menuElement = document.createElement('div');
2704
+ menuElement.className = "lexmenubox";
2705
+ menuElement.tabIndex = "0";
2706
+ c.currentMenu = menuElement;
2707
+ menuElement.parentEntry = c;
2708
+
2709
+ const isSubMenu = c.classList.contains( "lexmenuboxentry" );
2710
+ if( isSubMenu )
2711
+ {
2712
+ menuElement.dataset[ "submenu" ] = true;
2713
+ }
2714
+
2715
+ menuElement._updatePosition = () => {
2716
+
2717
+ // Remove transitions for this change..
2718
+ const transition = menuElement.style.transition;
2719
+ menuElement.style.transition = "none";
2720
+ flushCss( menuElement );
2721
+
2722
+ doAsync( () => {
2723
+ let rect = c.getBoundingClientRect();
2724
+ rect.x += document.scrollingElement.scrollLeft;
2725
+ rect.y += document.scrollingElement.scrollTop;
2726
+ menuElement.style.left = ( isSubMenu ? ( rect.x + rect.width ) : rect.x ) + "px";
2727
+ menuElement.style.top = ( isSubMenu ? rect.y : ( ( rect.y + rect.height ) ) - 4 ) + "px";
2728
+
2729
+ menuElement.style.transition = transition;
2730
+ } );
2731
+ };
2732
+
2733
+ menuElement._updatePosition();
2734
+
2735
+ doAsync( () => {
2736
+ menuElement.dataset[ "open" ] = true;
2737
+ }, 10 );
2738
+
2739
+ LX.root.appendChild( menuElement );
2740
+
2741
+ for( var i = 0; i < o[ k ].length; ++i )
2742
+ {
2743
+ const subitem = o[ k ][ i ];
2744
+ const subkey = Object.keys( subitem )[ 0 ];
2745
+ const hasSubmenu = subitem[ subkey ].length;
2746
+ const isCheckbox = subitem[ 'type' ] == 'checkbox';
2747
+ let subentry = document.createElement('div');
2748
+ subentry.className = "lexmenuboxentry";
2749
+ subentry.className += (i == o[k].length - 1 ? " last" : "") + ( subitem.disabled ? " disabled" : "" );
2750
+
2751
+ if( subkey == '' )
2752
+ {
2753
+ subentry.className = " lexseparator";
2754
+ }
2755
+ else
2756
+ {
2757
+ subentry.id = subkey;
2758
+ let subentrycont = document.createElement('div');
2759
+ subentrycont.innerHTML = "";
2760
+ subentrycont.classList = "lexmenuboxentrycontainer";
2761
+ subentry.appendChild(subentrycont);
2762
+ const icon = this.icons[ subkey ];
2763
+ if( isCheckbox )
2764
+ {
2765
+ subentrycont.innerHTML += "<input type='checkbox' >";
2766
+ }
2767
+ else if( icon )
2768
+ {
2769
+ subentrycont.innerHTML += "<a class='" + icon + " fa-sm'></a>";
2770
+ }
2771
+ else
2772
+ {
2773
+ subentrycont.innerHTML += "<a class='fa-solid fa-sm noicon'></a>";
2774
+ subentrycont.classList.add( "noicon" );
2775
+
2776
+ }
2777
+ subentrycont.innerHTML += "<div class='lexentryname'>" + subkey + "</div>";
2778
+ }
2779
+
2780
+ let checkboxInput = subentry.querySelector('input');
2781
+ if( checkboxInput )
2782
+ {
2783
+ checkboxInput.checked = subitem.checked ?? false;
2784
+ checkboxInput.addEventListener('change', e => {
2785
+ subitem.checked = checkboxInput.checked;
2786
+ const f = subitem[ 'callback' ];
2787
+ if( f )
2788
+ {
2789
+ f.call( this, subitem.checked, subkey, subentry );
2790
+ this._resetMenubar();
2791
+ }
2792
+ e.stopPropagation();
2793
+ e.stopImmediatePropagation();
2794
+ })
2795
+ }
2796
+
2797
+ menuElement.appendChild( subentry );
2798
+
2799
+ // Nothing more for separators
2800
+ if( subkey == '' )
2801
+ {
2802
+ continue;
2803
+ }
2804
+
2805
+ menuElement.addEventListener('keydown', e => {
2806
+ e.preventDefault();
2807
+ let short = this.shorts[ subkey ];
2808
+ if(!short) return;
2809
+ // check if it's a letter or other key
2810
+ short = short.length == 1 ? short.toLowerCase() : short;
2811
+ if( short == e.key )
2812
+ {
2813
+ subentry.click()
2814
+ }
2815
+ });
2816
+
2817
+ // Add callback
2818
+ subentry.addEventListener("click", e => {
2819
+ if( checkboxInput )
2820
+ {
2821
+ subitem.checked = !subitem.checked;
2822
+ }
2823
+ const f = subitem[ 'callback' ];
2824
+ if( f )
2825
+ {
2826
+ f.call( this, checkboxInput ? subitem.checked : subkey, checkboxInput ? subkey : subentry );
2827
+ this._resetMenubar();
2828
+ }
2829
+ e.stopPropagation();
2830
+ e.stopImmediatePropagation();
2831
+ });
2832
+
2833
+ // Add icon if has submenu, else check for shortcut
2834
+ if( !hasSubmenu)
2835
+ {
2836
+ if( this.shorts[ subkey ] )
2837
+ {
2838
+ let shortEl = document.createElement('div');
2839
+ shortEl.className = "lexentryshort";
2840
+ shortEl.innerText = this.shorts[ subkey ];
2841
+ subentry.appendChild( shortEl );
2842
+ }
2843
+ continue;
2844
+ }
2845
+
2846
+ let submenuIcon = document.createElement('a');
2847
+ submenuIcon.className = "fa-solid fa-angle-right fa-xs";
2848
+ subentry.appendChild( submenuIcon );
2849
+
2850
+ subentry.addEventListener("mouseover", e => {
2851
+ if( subentry.built )
2852
+ {
2853
+ return;
2854
+ }
2855
+ subentry.built = true;
2856
+ this._createSubmenu( subitem, subkey, subentry, ++d );
2857
+ e.stopPropagation();
2858
+ });
2859
+
2860
+ subentry.addEventListener("mouseleave", e => {
2861
+ if( subentry.currentMenu && ( subentry.currentMenu != e.toElement ) )
2862
+ {
2863
+ d = -1; // Reset depth
2864
+ delete subentry.built;
2865
+ subentry.currentMenu.remove();
2866
+ delete subentry.currentMenu;
2867
+ }
2868
+ });
2869
+ }
2870
+
2871
+ // Set final width
2872
+ menuElement.style.width = menuElement.offsetWidth + "px";
2873
+ }
2874
+
2676
2875
  /**
2677
2876
  * @method add
2678
2877
  * @param {Object} options:
@@ -2697,7 +2896,6 @@ class Menubar {
2697
2896
  this.shorts[ lastPath ] = options.short;
2698
2897
 
2699
2898
  let idx = 0;
2700
- let that = this;
2701
2899
 
2702
2900
  const _insertEntry = ( token, list ) => {
2703
2901
  if( token == undefined )
@@ -2776,176 +2974,11 @@ class Menubar {
2776
2974
  }
2777
2975
  }
2778
2976
 
2779
- const _resetMenubar = function() {
2780
- // Menu entries are in the menubar..
2781
- that.root.querySelectorAll(".lexmenuentry").forEach( _entry => {
2782
- _entry.classList.remove( 'selected' );
2783
- _entry.built = false;
2784
- } );
2785
- // Menuboxes are in the root area!
2786
- LX.root.querySelectorAll(".lexmenubox").forEach(e => e.remove());
2787
- // Next time we need to click again
2788
- that.focused = false;
2789
- };
2790
-
2791
- const create_submenu = function( o, k, c, d ) {
2792
-
2793
- let menuElement = document.createElement('div');
2794
- menuElement.className = "lexmenubox";
2795
- menuElement.tabIndex = "0";
2796
- c.currentMenu = menuElement;
2797
- const isSubMenu = c.classList.contains( "lexmenuboxentry" );
2798
- if( isSubMenu ) menuElement.dataset[ "submenu" ] = true;
2799
- var rect = c.getBoundingClientRect();
2800
- menuElement.style.left = ( isSubMenu ? ( rect.x + rect.width ) : rect.left ) + "px";
2801
- menuElement.style.top = ( isSubMenu ? rect.y : rect.bottom - 4 ) + "px";
2802
- rect = menuElement.getBoundingClientRect();
2803
-
2804
- doAsync( () => {
2805
- menuElement.dataset[ "open" ] = true;
2806
- }, 10 );
2807
-
2808
- LX.root.appendChild( menuElement );
2809
-
2810
- for( var i = 0; i < o[ k ].length; ++i )
2811
- {
2812
- const subitem = o[ k ][ i ];
2813
- const subkey = Object.keys( subitem )[ 0 ];
2814
- const hasSubmenu = subitem[ subkey ].length;
2815
- const isCheckbox = subitem[ 'type' ] == 'checkbox';
2816
- let subentry = document.createElement('div');
2817
- subentry.className = "lexmenuboxentry";
2818
- subentry.className += (i == o[k].length - 1 ? " last" : "") + ( subitem.disabled ? " disabled" : "" );
2819
-
2820
- if( subkey == '' )
2821
- {
2822
- subentry.className = " lexseparator";
2823
- }
2824
- else
2825
- {
2826
- subentry.id = subkey;
2827
- let subentrycont = document.createElement('div');
2828
- subentrycont.innerHTML = "";
2829
- subentrycont.classList = "lexmenuboxentrycontainer";
2830
- subentry.appendChild(subentrycont);
2831
- const icon = that.icons[ subkey ];
2832
- if( isCheckbox )
2833
- {
2834
- subentrycont.innerHTML += "<input type='checkbox' >";
2835
- }
2836
- else if( icon )
2837
- {
2838
- subentrycont.innerHTML += "<a class='" + icon + " fa-sm'></a>";
2839
- }
2840
- else
2841
- {
2842
- subentrycont.innerHTML += "<a class='fa-solid fa-sm noicon'></a>";
2843
- subentrycont.classList.add( "noicon" );
2844
-
2845
- }
2846
- subentrycont.innerHTML += "<div class='lexentryname'>" + subkey + "</div>";
2847
- }
2848
-
2849
- let checkboxInput = subentry.querySelector('input');
2850
- if( checkboxInput )
2851
- {
2852
- checkboxInput.checked = subitem.checked ?? false;
2853
- checkboxInput.addEventListener('change', e => {
2854
- subitem.checked = checkboxInput.checked;
2855
- const f = subitem[ 'callback' ];
2856
- if( f )
2857
- {
2858
- f.call( this, subitem.checked, subkey, subentry );
2859
- _resetMenubar();
2860
- }
2861
- e.stopPropagation();
2862
- e.stopImmediatePropagation();
2863
- })
2864
- }
2865
-
2866
- menuElement.appendChild( subentry );
2867
-
2868
- // Nothing more for separators
2869
- if( subkey == '' )
2870
- {
2871
- continue;
2872
- }
2873
-
2874
- menuElement.addEventListener('keydown', function(e) {
2875
- e.preventDefault();
2876
- let short = that.shorts[ subkey ];
2877
- if(!short) return;
2878
- // check if it's a letter or other key
2879
- short = short.length == 1 ? short.toLowerCase() : short;
2880
- if( short == e.key )
2881
- {
2882
- subentry.click()
2883
- }
2884
- });
2885
-
2886
- // Add callback
2887
- subentry.addEventListener("click", e => {
2888
- if( checkboxInput )
2889
- {
2890
- subitem.checked = !subitem.checked;
2891
- }
2892
- const f = subitem[ 'callback' ];
2893
- if( f )
2894
- {
2895
- f.call( this, checkboxInput ? subitem.checked : subkey, checkboxInput ? subkey : subentry );
2896
- _resetMenubar();
2897
- }
2898
- e.stopPropagation();
2899
- e.stopImmediatePropagation();
2900
- });
2901
-
2902
- // Add icon if has submenu, else check for shortcut
2903
- if( !hasSubmenu)
2904
- {
2905
- if( that.shorts[ subkey ] )
2906
- {
2907
- let shortEl = document.createElement('div');
2908
- shortEl.className = "lexentryshort";
2909
- shortEl.innerText = that.shorts[ subkey ];
2910
- subentry.appendChild( shortEl );
2911
- }
2912
- continue;
2913
- }
2914
-
2915
- let submenuIcon = document.createElement('a');
2916
- submenuIcon.className = "fa-solid fa-angle-right fa-xs";
2917
- subentry.appendChild( submenuIcon );
2918
-
2919
- subentry.addEventListener("mouseover", e => {
2920
- if( subentry.built )
2921
- {
2922
- return;
2923
- }
2924
- subentry.built = true;
2925
- create_submenu( subitem, subkey, subentry, ++d );
2926
- e.stopPropagation();
2927
- });
2928
-
2929
- subentry.addEventListener("mouseleave", (e) => {
2930
- if( subentry.currentMenu && ( subentry.currentMenu != e.toElement ) )
2931
- {
2932
- d = -1; // Reset depth
2933
- delete subentry.built;
2934
- subentry.currentMenu.remove();
2935
- delete subentry.currentMenu;
2936
- }
2937
- });
2938
- }
2939
-
2940
- // Set final width
2941
- menuElement.style.width = menuElement.offsetWidth + "px";
2942
- };
2943
-
2944
2977
  const _showEntry = () => {
2945
- _resetMenubar();
2978
+ this._resetMenubar();
2946
2979
  entry.classList.add( "selected" );
2947
2980
  entry.built = true;
2948
- create_submenu( item, key, entry, -1 );
2981
+ this._createSubmenu( item, key, entry, -1 );
2949
2982
  };
2950
2983
 
2951
2984
  entry.addEventListener("click", () => {
@@ -2976,22 +3009,22 @@ class Menubar {
2976
3009
  return;
2977
3010
  }
2978
3011
 
2979
- _resetMenubar();
3012
+ this._resetMenubar();
2980
3013
  });
2981
3014
  }
2982
3015
  }
2983
3016
 
2984
3017
  /**
2985
3018
  * @method getButton
2986
- * @param {String} title
3019
+ * @param {String} name
2987
3020
  */
2988
3021
 
2989
- getButton( title ) {
2990
- return this.buttons[ title ];
3022
+ getButton( name ) {
3023
+ return this.buttons[ name ];
2991
3024
  }
2992
3025
 
2993
3026
  /**
2994
- * @method getSubitems: recursive method to find subentries of a menu entry
3027
+ * @method getSubitems
2995
3028
  * @param {Object} item: parent item
2996
3029
  * @param {Array} tokens: split path strings
2997
3030
  */
@@ -3024,6 +3057,7 @@ class Menubar {
3024
3057
  * @param {String} path
3025
3058
  */
3026
3059
  getItem( path ) {
3060
+
3027
3061
  // process path
3028
3062
  const tokens = path.split("/");
3029
3063
 
@@ -3032,100 +3066,118 @@ class Menubar {
3032
3066
 
3033
3067
  /**
3034
3068
  * @method setButtonIcon
3035
- * @param {String} title
3069
+ * @param {String} name
3036
3070
  * @param {String} icon
3071
+ * @param {Function} callback
3072
+ * @param {Object} options
3037
3073
  */
3038
3074
 
3039
- setButtonIcon( title, icon, callback, options = {} ) {
3075
+ setButtonIcon( name, icon, callback, options = {} ) {
3076
+
3077
+ if( !name )
3078
+ {
3079
+ throw( "Set Button Name!" );
3080
+ }
3040
3081
 
3041
- const button = this.buttons[ title ];
3082
+ let button = this.buttons[ name ];
3042
3083
  if( button )
3043
3084
  {
3044
3085
  button.querySelector('a').className = "fa-solid" + " " + icon + " lexicon";
3086
+ return;
3045
3087
  }
3046
- else
3088
+
3089
+ // Otherwise, create it
3090
+ button = document.createElement('div');
3091
+ const disabled = options.disabled ?? false;
3092
+ button.className = "lexmenubutton main" + (disabled ? " disabled" : "");
3093
+ button.title = name;
3094
+ button.innerHTML = "<a class='" + icon + " lexicon'></a>";
3095
+
3096
+ if( options.float == "right" )
3047
3097
  {
3048
- let button = document.createElement('div');
3049
- const disabled = options.disabled ?? false;
3050
- button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3051
- button.title = title ?? "";
3052
- button.innerHTML = "<a class='" + icon + " lexicon' style='font-size:x-large;'></a>";
3053
- button.style.padding = "5px 10px";
3054
- button.style.maxHeight = "calc(100% - 10px)";
3055
- button.style.alignItems = "center";
3098
+ button.right = true;
3099
+ }
3056
3100
 
3057
- if( options.float == "right" )
3058
- {
3059
- button.right = true;
3060
- }
3101
+ if( this.root.lastChild && this.root.lastChild.right )
3102
+ {
3103
+ this.root.lastChild.before( button );
3104
+ }
3105
+ else if( options.float == "left" )
3106
+ {
3107
+ this.root.prepend( button );
3108
+ }
3109
+ else
3110
+ {
3111
+ this.root.appendChild( button );
3112
+ }
3061
3113
 
3062
- if( this.root.lastChild && this.root.lastChild.right )
3063
- {
3064
- this.root.lastChild.before( button );
3065
- }
3066
- else if( options.float == "left" )
3067
- {
3068
- this.root.prepend( button );
3069
- }
3070
- else
3114
+ const _b = button.querySelector('a');
3115
+ _b.addEventListener("click", (e) => {
3116
+ if( callback && !disabled )
3071
3117
  {
3072
- this.root.appendChild( button );
3118
+ callback.call( this, _b, e );
3073
3119
  }
3120
+ });
3074
3121
 
3075
- const _b = button.querySelector('a');
3076
- _b.addEventListener("click", (e) => {
3077
- if(callback && !disabled)
3078
- callback.call( this, _b, e );
3079
- });
3080
- }
3122
+ this.buttons[ name ] = button;
3081
3123
  }
3082
3124
 
3083
3125
  /**
3084
3126
  * @method setButtonImage
3085
- * @param {String} title
3127
+ * @param {String} name
3086
3128
  * @param {String} src
3129
+ * @param {Function} callback
3130
+ * @param {Object} options
3087
3131
  */
3088
3132
 
3089
- setButtonImage( title, src, callback, options = {} ) {
3090
- const button = this.buttons[ title ];
3133
+ setButtonImage( name, src, callback, options = {} ) {
3134
+
3135
+ if( !name )
3136
+ {
3137
+ throw( "Set Button Name!" );
3138
+ }
3139
+
3140
+ let button = this.buttons[ name ];
3091
3141
  if( button )
3092
3142
  {
3093
- button.querySelector('a').className = "fa-solid" + " " + icon + " lexicon";
3143
+ button.querySelector('img').src = src;
3144
+ return;
3094
3145
  }
3095
- else
3146
+
3147
+ // Otherwise, create it
3148
+ button = document.createElement('div');
3149
+ const disabled = options.disabled ?? false;
3150
+ button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3151
+ button.title = name;
3152
+ button.innerHTML = "<a><image src='" + src + "' class='lexicon' style='height:32px;'></a>";
3153
+
3154
+ if( options.float == "right" )
3096
3155
  {
3097
- let button = document.createElement('div');
3098
- const disabled = options.disabled ?? false;
3099
- button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3100
- button.title = title ?? "";
3101
- button.innerHTML = "<a><image src='" + src + "' class='lexicon' style='height:32px;'></a>";
3102
- button.style.padding = "5px";
3103
- button.style.alignItems = "center";
3156
+ button.right = true;
3157
+ }
3104
3158
 
3105
- if( options.float == "right" )
3106
- {
3107
- button.right = true;
3108
- }
3159
+ if( this.root.lastChild && this.root.lastChild.right )
3160
+ {
3161
+ this.root.lastChild.before( button );
3162
+ }
3163
+ else if( options.float == "left" )
3164
+ {
3165
+ this.root.prepend( button );
3166
+ }
3167
+ else
3168
+ {
3169
+ this.root.appendChild( button );
3170
+ }
3109
3171
 
3110
- if( this.root.lastChild && this.root.lastChild.right )
3111
- {
3112
- this.root.lastChild.before( button );
3113
- }
3114
- else if( options.float == "left" )
3115
- {
3116
- this.root.prepend( button );
3117
- }
3118
- else
3172
+ const _b = button.querySelector('a');
3173
+ _b.addEventListener("click", (e) => {
3174
+ if( callback && !disabled )
3119
3175
  {
3120
- this.root.appendChild( button );
3176
+ callback.call( this, _b, e );
3121
3177
  }
3178
+ });
3122
3179
 
3123
- const _b = button.querySelector('a');
3124
- _b.addEventListener("click", (e) => {
3125
- if(callback && !disabled)
3126
- callback.call( this, _b, e );
3127
- });
3128
- }
3180
+ this.buttons[ name ] = button;
3129
3181
  }
3130
3182
 
3131
3183
  /**
@@ -3231,20 +3283,21 @@ class SideBar {
3231
3283
 
3232
3284
  /**
3233
3285
  * @param {Object} options
3234
- * inset: TODO
3235
- * filter: TODO
3286
+ * filter: Add search bar to filter entries [false]
3236
3287
  * skipHeader: Do not use sidebar header [false]
3237
3288
  * headerImg: Image to be shown as avatar
3238
3289
  * headerIcon: Icon to be shown as avatar (from LX.ICONS)
3239
- * headerTitle
3240
- * headerSubtitle
3290
+ * headerTitle: Header title
3291
+ * headerSubtitle: Header subtitle
3292
+ * header: HTMLElement to add a custom header
3241
3293
  * skipFooter: Do not use sidebar footer [false]
3242
3294
  * footerImg: Image to be shown as avatar
3243
3295
  * footerIcon: Icon to be shown as avatar (from LX.ICONS)
3244
- * footerTitle
3245
- * footerSubtitle
3296
+ * footerTitle: Footer title
3297
+ * footerSubtitle: Footer subtitle
3298
+ * footer: HTMLElement to add a custom footer
3246
3299
  * collapsable: Sidebar can toggle between collapsed/expanded [true]
3247
- * collapseToIcons: When Sidebar collapses, icons remains visible [true]
3300
+ * collapseToIcons: When Sidebar collapses, icons remains visible [true]
3248
3301
  * onHeaderPressed: Function to call when header is pressed
3249
3302
  * onFooterPressed: Function to call when footer is pressed
3250
3303
  */
@@ -3254,12 +3307,12 @@ class SideBar {
3254
3307
  this.root = document.createElement( 'div' );
3255
3308
  this.root.className = "lexsidebar";
3256
3309
 
3257
- window.sidebar = this;
3258
-
3259
3310
  this.collapsable = options.collapsable ?? true;
3260
- this.collapseWidth = ( options.collapseToIcons ?? true ) ? "58px" : "0px";
3311
+ this._collapseWidth = ( options.collapseToIcons ?? true ) ? "58px" : "0px";
3261
3312
  this.collapsed = false;
3262
3313
 
3314
+ this.filterString = "";
3315
+
3263
3316
  doAsync( () => {
3264
3317
 
3265
3318
  this.root.parentElement.ogWidth = this.root.parentElement.style.width;
@@ -3272,64 +3325,19 @@ class SideBar {
3272
3325
  }
3273
3326
  });
3274
3327
 
3275
- }, 100 );
3276
-
3277
- // This account for header, footer and all inner paddings
3278
- let contentOffset = 32;
3328
+ }, 10 );
3279
3329
 
3280
3330
  // Header
3281
3331
  if( !( options.skipHeader ?? false ) )
3282
3332
  {
3283
- this.header = document.createElement( 'div' );
3333
+ this.header = options.header ?? this._generateDefaultHeader( options );
3334
+ console.assert( this.header.constructor === HTMLDivElement, "Use an HTMLDivElement to build your custom header" );
3284
3335
  this.header.className = "lexsidebarheader";
3285
3336
  this.root.appendChild( this.header );
3286
3337
 
3287
- this.header.addEventListener( "click", e => {
3288
- if( this.collapsed )
3289
- {
3290
- e.preventDefault();
3291
- e.stopPropagation();
3292
- this.toggleCollapsed();
3293
- }
3294
- else if( options.onHeaderPressed )
3295
- {
3296
- options.onHeaderPressed( e );
3297
- }
3298
- } );
3299
-
3300
- const avatar = document.createElement( 'span' );
3301
- avatar.className = "lexavatar";
3302
- this.header.appendChild( avatar );
3303
-
3304
- if( options.headerImage )
3305
- {
3306
- const avatarImg = document.createElement( 'img' );
3307
- avatarImg.src = options.headerImage;
3308
- avatar.appendChild( avatarImg );
3309
- }
3310
- else if( options.headerIcon )
3311
- {
3312
- const avatarIcon = LX.makeIcon( options.headerIcon );
3313
- avatar.appendChild( avatarIcon );
3314
- }
3315
-
3316
- // Info
3317
- {
3318
- const info = document.createElement( 'div' );
3319
- this.header.appendChild( info );
3320
-
3321
- const infoText = document.createElement( 'span' );
3322
- infoText.innerHTML = options.headerTitle ?? "";
3323
- info.appendChild( infoText );
3324
-
3325
- const infoSubtext = document.createElement( 'span' );
3326
- infoSubtext.innerHTML = options.headerSubtitle ?? "";
3327
- info.appendChild( infoSubtext );
3328
- }
3329
-
3330
3338
  if( this.collapsable )
3331
3339
  {
3332
- const icon = LX.makeIcon( "Sidebar", "Toggle Sidebar" );
3340
+ const icon = LX.makeIcon( "Sidebar", "Toggle Sidebar", "toggler" );
3333
3341
  this.header.appendChild( icon );
3334
3342
 
3335
3343
  icon.addEventListener( "click", (e) => {
@@ -3338,8 +3346,18 @@ class SideBar {
3338
3346
  this.toggleCollapsed();
3339
3347
  } );
3340
3348
  }
3349
+ }
3341
3350
 
3342
- contentOffset += 52;
3351
+ // Entry filter
3352
+ if( ( options.filter ?? false ) )
3353
+ {
3354
+ const panel = new Panel();
3355
+ panel.addText(null, "", (value, event) => {
3356
+ this.filterString = value;
3357
+ this.update();
3358
+ }, { placeholder: "Search...", icon: "fa-solid fa-magnifying-glass" });
3359
+ this.filter = panel.root.childNodes[ 0 ];
3360
+ this.root.appendChild( this.filter );
3343
3361
  }
3344
3362
 
3345
3363
  // Content
@@ -3352,78 +3370,143 @@ class SideBar {
3352
3370
  // Footer
3353
3371
  if( !( options.skipFooter ?? false ) )
3354
3372
  {
3355
- this.footer = document.createElement( 'div' );
3373
+ this.footer = options.footer ?? this._generateDefaultFooter( options );
3374
+ console.assert( this.footer.constructor === HTMLDivElement, "Use an HTMLDivElement to build your custom footer" );
3356
3375
  this.footer.className = "lexsidebarfooter";
3357
3376
  this.root.appendChild( this.footer );
3377
+ }
3358
3378
 
3359
- this.footer.addEventListener( "click", e => {
3360
- if( options.onFooterPressed )
3361
- {
3362
- options.onFooterPressed( e );
3363
- }
3364
- } );
3379
+ // Set width depending on header/footer
3380
+ doAsync( () => {
3381
+ // This account for header, footer and all inner paddings
3382
+ const contentOffset = 32 + ( this.header?.offsetHeight ?? 0 ) +
3383
+ ( this.filter?.offsetHeight ?? 0 ) +
3384
+ ( this.footer?.offsetHeight ?? 0 );
3385
+ this.content.style.height = `calc(100% - ${ contentOffset }px)`;
3386
+ }, 10 );
3365
3387
 
3366
- const avatar = document.createElement( 'span' );
3367
- avatar.className = "lexavatar";
3368
- this.footer.appendChild( avatar );
3388
+ this.items = [ ];
3389
+ this.icons = { };
3390
+ this.groups = { };
3391
+ }
3392
+
3393
+ _generateDefaultHeader( options ) {
3369
3394
 
3370
- if( options.footerImage )
3395
+ const header = document.createElement( 'div' );
3396
+
3397
+ header.addEventListener( "click", e => {
3398
+ if( this.collapsed )
3371
3399
  {
3372
- const avatarImg = document.createElement( 'img' );
3373
- avatarImg.src = options.footerImage;
3374
- avatar.appendChild( avatarImg );
3400
+ e.preventDefault();
3401
+ e.stopPropagation();
3402
+ this.toggleCollapsed();
3375
3403
  }
3376
- else if( options.footerIcon )
3404
+ else if( options.onHeaderPressed )
3377
3405
  {
3378
- const avatarIcon = LX.makeIcon( options.footerIcon );
3379
- avatar.appendChild( avatarIcon );
3406
+ options.onHeaderPressed( e );
3380
3407
  }
3408
+ } );
3381
3409
 
3382
- // Info
3383
- {
3384
- const info = document.createElement( 'div' );
3385
- this.footer.appendChild( info );
3410
+ const avatar = document.createElement( 'span' );
3411
+ avatar.className = "lexavatar";
3412
+ header.appendChild( avatar );
3413
+
3414
+ if( options.headerImage )
3415
+ {
3416
+ const avatarImg = document.createElement( 'img' );
3417
+ avatarImg.src = options.headerImage;
3418
+ avatar.appendChild( avatarImg );
3419
+ }
3420
+ else if( options.headerIcon )
3421
+ {
3422
+ const avatarIcon = LX.makeIcon( options.headerIcon );
3423
+ avatar.appendChild( avatarIcon );
3424
+ }
3425
+
3426
+ // Info
3427
+ {
3428
+ const info = document.createElement( 'div' );
3429
+ info.className = "infodefault";
3430
+ header.appendChild( info );
3431
+
3432
+ const infoText = document.createElement( 'span' );
3433
+ infoText.innerHTML = options.headerTitle ?? "";
3434
+ info.appendChild( infoText );
3386
3435
 
3387
- const infoText = document.createElement( 'span' );
3388
- infoText.innerHTML = options.footerTitle ?? "";
3389
- info.appendChild( infoText );
3436
+ const infoSubtext = document.createElement( 'span' );
3437
+ infoSubtext.innerHTML = options.headerSubtitle ?? "";
3438
+ info.appendChild( infoSubtext );
3439
+ }
3440
+
3441
+ return header;
3442
+ }
3443
+
3444
+ _generateDefaultFooter( options ) {
3445
+
3446
+ const footer = document.createElement( 'div' );
3390
3447
 
3391
- const infoSubtext = document.createElement( 'span' );
3392
- infoSubtext.innerHTML = options.footerSubtitle ?? "";
3393
- info.appendChild( infoSubtext );
3448
+ footer.addEventListener( "click", e => {
3449
+ if( options.onFooterPressed )
3450
+ {
3451
+ options.onFooterPressed( e );
3394
3452
  }
3453
+ } );
3395
3454
 
3396
- const icon = LX.makeIcon( "MenuArrows" );
3397
- this.footer.appendChild( icon );
3455
+ const avatar = document.createElement( 'span' );
3456
+ avatar.className = "lexavatar";
3457
+ footer.appendChild( avatar );
3398
3458
 
3399
- contentOffset += 52;
3459
+ if( options.footerImage )
3460
+ {
3461
+ const avatarImg = document.createElement( 'img' );
3462
+ avatarImg.src = options.footerImage;
3463
+ avatar.appendChild( avatarImg );
3464
+ }
3465
+ else if( options.footerIcon )
3466
+ {
3467
+ const avatarIcon = LX.makeIcon( options.footerIcon );
3468
+ avatar.appendChild( avatarIcon );
3400
3469
  }
3401
3470
 
3402
- // Set width depending on header/footer
3403
- this.content.style.height = `calc(100% - ${ contentOffset }px)`;
3471
+ // Info
3472
+ {
3473
+ const info = document.createElement( 'div' );
3474
+ info.className = "infodefault";
3475
+ footer.appendChild( info );
3404
3476
 
3405
- this.items = [ ];
3406
- this.icons = { };
3407
- this.groups = { };
3477
+ const infoText = document.createElement( 'span' );
3478
+ infoText.innerHTML = options.footerTitle ?? "";
3479
+ info.appendChild( infoText );
3480
+
3481
+ const infoSubtext = document.createElement( 'span' );
3482
+ infoSubtext.innerHTML = options.footerSubtitle ?? "";
3483
+ info.appendChild( infoSubtext );
3484
+ }
3485
+
3486
+ const icon = LX.makeIcon( "MenuArrows" );
3487
+ footer.appendChild( icon );
3488
+
3489
+ return footer;
3408
3490
  }
3409
3491
 
3410
3492
  /**
3411
3493
  * @method toggleCollapsed
3494
+ * @param {Boolean} force: Force collapsed state
3412
3495
  */
3413
3496
 
3414
- toggleCollapsed() {
3497
+ toggleCollapsed( force ) {
3415
3498
 
3416
3499
  if( !this.collapsable )
3417
3500
  {
3418
3501
  return;
3419
3502
  }
3420
3503
 
3421
- this.collapsed = !this.collapsed;
3504
+ this.collapsed = force ?? !this.collapsed;
3422
3505
 
3423
3506
  if( this.collapsed )
3424
3507
  {
3425
3508
  this.root.classList.add( "collapsing" );
3426
- this.root.parentElement.style.width = this.collapseWidth;
3509
+ this.root.parentElement.style.width = this._collapseWidth;
3427
3510
  }
3428
3511
  else
3429
3512
  {
@@ -3432,6 +3515,11 @@ class SideBar {
3432
3515
  this.root.parentElement.style.width = this.root.parentElement.ogWidth;
3433
3516
  }
3434
3517
 
3518
+ if( !this.resizeObserver )
3519
+ {
3520
+ throw( "Wait until ResizeObserver has been created!" );
3521
+ }
3522
+
3435
3523
  this.resizeObserver.observe( this.root.parentElement );
3436
3524
 
3437
3525
  doAsync( () => {
@@ -3471,9 +3559,9 @@ class SideBar {
3471
3559
  * @param {String} path
3472
3560
  * @param {Object} options:
3473
3561
  * callback: Function to call on each item
3474
- * icon: Entry icon
3475
- * collapsable: Add entry as a collapsable section
3476
3562
  * className: Add class to the entry DOM element
3563
+ * collapsable: Add entry as a collapsable section
3564
+ * icon: Entry icon
3477
3565
  */
3478
3566
 
3479
3567
  add( path, options = {} ) {
@@ -3490,7 +3578,6 @@ class SideBar {
3490
3578
  const lastPath = tokens[tokens.length - 1];
3491
3579
  this.icons[ lastPath ] = options.icon;
3492
3580
 
3493
-
3494
3581
  let idx = 0;
3495
3582
 
3496
3583
  const _insertEntry = ( token, list ) => {
@@ -3545,10 +3632,19 @@ class SideBar {
3545
3632
  if( !entry )
3546
3633
  return;
3547
3634
 
3548
- entry.domEl.click();
3635
+ entry.dom.click();
3549
3636
  }
3550
3637
 
3551
- _build() {
3638
+ update() {
3639
+
3640
+ // Reset first
3641
+
3642
+ this.content.innerHTML = "";
3643
+
3644
+ for( let item of this.items )
3645
+ {
3646
+ delete item.dom;
3647
+ }
3552
3648
 
3553
3649
  for( let item of this.items )
3554
3650
  {
@@ -3561,12 +3657,18 @@ class SideBar {
3561
3657
  }
3562
3658
 
3563
3659
  let key = Object.keys( item )[ 0 ];
3660
+
3661
+ if( this.filterString.length && !key.toLowerCase().includes( this.filterString.toLowerCase() ) )
3662
+ {
3663
+ continue;
3664
+ }
3665
+
3564
3666
  let pKey = key.replace( /\s/g, '' ).replaceAll( '.', '' );
3565
3667
  let currentGroup = null;
3566
3668
 
3567
3669
  let entry = document.createElement( 'div' );
3568
3670
  entry.className = "lexsidebarentry " + ( options.className ?? "" );
3569
- entry.id = pKey;
3671
+ entry.id = item.name = pKey;
3570
3672
 
3571
3673
  if( item.group )
3572
3674
  {
@@ -3637,7 +3739,7 @@ class SideBar {
3637
3739
 
3638
3740
  let itemDom = document.createElement( 'div' );
3639
3741
  entry.appendChild( itemDom );
3640
- item.dom = itemDom;
3742
+ item.dom = entry;
3641
3743
 
3642
3744
  if( options.type == "checkbox" )
3643
3745
  {
@@ -3693,6 +3795,8 @@ class SideBar {
3693
3795
  entry.classList.add( "selected" );
3694
3796
  });
3695
3797
 
3798
+ const isCollapsable = options.collapsable != undefined ? options.collapsable : ( options.collapsable || item[ key ].length );
3799
+
3696
3800
  if( options.action )
3697
3801
  {
3698
3802
  const actionIcon = LX.makeIcon( options.action.icon ?? "MoreHorizontal", options.action.name );
@@ -3705,7 +3809,7 @@ class SideBar {
3705
3809
  if( f ) f.call( this, key, e );
3706
3810
  } );
3707
3811
  }
3708
- else if( options.collapsable )
3812
+ else if( isCollapsable )
3709
3813
  {
3710
3814
  const collapsableContent = document.createElement( 'div' );
3711
3815
  Object.assign( collapsableContent.style, { width: "100%", display: "none" } );
@@ -3740,7 +3844,12 @@ class SideBar {
3740
3844
  let subentryContainer = document.createElement( 'div' );
3741
3845
  subentryContainer.className = "lexsidebarsubentrycontainer";
3742
3846
 
3743
- if( currentGroup )
3847
+ if( isCollapsable )
3848
+ {
3849
+ this.collapseContainer.appendChild( subentryContainer )
3850
+ delete this.collapseContainer;
3851
+ }
3852
+ else if( currentGroup )
3744
3853
  {
3745
3854
  currentGroup.appendChild( subentryContainer );
3746
3855
  }
@@ -3749,12 +3858,17 @@ class SideBar {
3749
3858
  this.content.appendChild( subentryContainer );
3750
3859
  }
3751
3860
 
3752
- for( var i = 0; i < item[ key ].length; ++i )
3861
+ for( let i = 0; i < item[ key ].length; ++i )
3753
3862
  {
3754
3863
  const subitem = item[ key ][ i ];
3755
3864
  const suboptions = subitem.options ?? {};
3756
3865
  const subkey = Object.keys( subitem )[ 0 ];
3757
3866
 
3867
+ if( this.filterString.length && !subkey.toLowerCase().includes( this.filterString.toLowerCase() ) )
3868
+ {
3869
+ continue;
3870
+ }
3871
+
3758
3872
  let subentry = document.createElement( 'div' );
3759
3873
  subentry.innerHTML = `<span>${ subkey }</span>`;
3760
3874
 
@@ -3805,31 +3919,34 @@ class Widget {
3805
3919
  static CHECKBOX = 5;
3806
3920
  static TOGGLE = 6;
3807
3921
  static RADIO = 7;
3808
- static COLOR = 8;
3809
- static RANGE = 9;
3810
- static NUMBER = 10;
3811
- static TITLE = 11;
3812
- static VECTOR = 12;
3813
- static TREE = 13;
3814
- static PROGRESS = 14;
3815
- static FILE = 15;
3816
- static LAYERS = 16;
3817
- static ARRAY = 17;
3818
- static LIST = 18;
3819
- static TAGS = 19;
3820
- static CURVE = 20;
3821
- static CARD = 21;
3822
- static IMAGE = 22;
3823
- static CONTENT = 23;
3824
- static CUSTOM = 24;
3825
- static SEPARATOR = 25;
3826
- static KNOB = 26;
3827
- static SIZE = 27;
3828
- static PAD = 28;
3829
- static FORM = 29;
3830
- static DIAL = 30;
3831
- static COUNTER = 31;
3832
- static TABLE = 32;
3922
+ static BUTTONS = 8;
3923
+ static COLOR = 9;
3924
+ static RANGE = 10;
3925
+ static NUMBER = 11;
3926
+ static TITLE = 12;
3927
+ static VECTOR = 13;
3928
+ static TREE = 14;
3929
+ static PROGRESS = 15;
3930
+ static FILE = 16;
3931
+ static LAYERS = 17;
3932
+ static ARRAY = 18;
3933
+ static LIST = 19;
3934
+ static TAGS = 20;
3935
+ static CURVE = 21;
3936
+ static CARD = 22;
3937
+ static IMAGE = 23;
3938
+ static CONTENT = 24;
3939
+ static CUSTOM = 25;
3940
+ static SEPARATOR = 26;
3941
+ static KNOB = 27;
3942
+ static SIZE = 28;
3943
+ static PAD = 29;
3944
+ static FORM = 30;
3945
+ static DIAL = 31;
3946
+ static COUNTER = 32;
3947
+ static TABLE = 33;
3948
+ static TABS = 34;
3949
+ static BLANK = 35;
3833
3950
 
3834
3951
  static NO_CONTEXT_TYPES = [
3835
3952
  Widget.BUTTON,
@@ -3838,10 +3955,11 @@ class Widget {
3838
3955
  Widget.PROGRESS
3839
3956
  ];
3840
3957
 
3841
- constructor( name, type, options ) {
3958
+ constructor( name, type, value, options ) {
3842
3959
  this.name = name;
3843
3960
  this.type = type;
3844
3961
  this.options = options;
3962
+ this._initialValue = value;
3845
3963
  }
3846
3964
 
3847
3965
  value() {
@@ -3854,22 +3972,31 @@ class Widget {
3854
3972
  console.warn( "Can't get value of " + this.typeName() );
3855
3973
  }
3856
3974
 
3857
- set( value, skipCallback = false, signalName = "" ) {
3975
+ set( value, skipCallback, event ) {
3858
3976
 
3859
3977
  if( this.onSetValue )
3860
3978
  {
3861
- return this.onSetValue( value, skipCallback );
3979
+ let resetButton = this.domEl.querySelector( ".lexwidgetname .lexicon" );
3980
+ if( resetButton )
3981
+ {
3982
+ resetButton.style.display = ( value != this.value() ? "block" : "none" );
3983
+ resetButton.style.display = ( value != this._initialValue ? "block" : "none" );
3984
+ }
3985
+
3986
+ return this.onSetValue( value, skipCallback ?? false, event );
3862
3987
  }
3863
3988
 
3864
3989
  console.warn("Can't set value of " + this.typeName());
3865
3990
  }
3866
3991
 
3867
- oncontextmenu(e) {
3992
+ oncontextmenu( e ) {
3868
3993
 
3869
- if( Widget.NO_CONTEXT_TYPES.includes(this.type) )
3994
+ if( Widget.NO_CONTEXT_TYPES.includes( this.type ) )
3995
+ {
3870
3996
  return;
3997
+ }
3871
3998
 
3872
- addContextMenu(this.typeName(), e, c => {
3999
+ addContextMenu( this.typeName(), e, c => {
3873
4000
  c.add("Copy", () => { this.copy() });
3874
4001
  c.add("Paste", { disabled: !this._can_paste(), callback: () => { this.paste() } } );
3875
4002
  });
@@ -3926,6 +4053,8 @@ class Widget {
3926
4053
  case Widget.DIAL: return "Dial";
3927
4054
  case Widget.COUNTER: return "Counter";
3928
4055
  case Widget.TABLE: return "Table";
4056
+ case Widget.TABS: return "Tabs";
4057
+ case Widget.BLANK: return "Blank";
3929
4058
  case Widget.CUSTOM: return this.customName;
3930
4059
  }
3931
4060
 
@@ -3939,26 +4068,31 @@ class Widget {
3939
4068
 
3940
4069
  LX.Widget = Widget;
3941
4070
 
3942
- function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
4071
+ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
3943
4072
  {
3944
4073
  let custom_idx = simple_guidGenerator();
3945
4074
 
3946
- Panel.prototype[ 'add' + custom_widget_name ] = function( name, instance, callback ) {
4075
+ Panel.prototype[ 'add' + customWidgetName ] = function( name, instance, callback ) {
3947
4076
 
3948
- let widget = this.create_widget(name, Widget.CUSTOM, options);
3949
- widget.customName = custom_widget_name;
4077
+ let widget = this._createWidget( Widget.CUSTOM, name, null, options );
4078
+ widget.customName = customWidgetName;
3950
4079
  widget.customIdx = custom_idx;
4080
+
3951
4081
  widget.onGetValue = () => {
3952
4082
  return instance;
3953
4083
  };
3954
- widget.onSetValue = ( newValue, skipCallback ) => {
4084
+
4085
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
3955
4086
  instance = newValue;
3956
4087
  refresh_widget();
3957
4088
  element.querySelector( ".lexcustomitems" ).toggleAttribute( 'hidden', false );
3958
- if( !skipCallback ) this._trigger( new IEvent( name, instance, null ), callback );
4089
+ if( !skipCallback )
4090
+ {
4091
+ this._trigger( new IEvent( name, instance, event ), callback );
4092
+ }
3959
4093
  };
3960
4094
 
3961
- let element = widget.domEl;
4095
+ const element = widget.domEl;
3962
4096
  element.style.flexWrap = "wrap";
3963
4097
 
3964
4098
  let container, custom_widgets;
@@ -3968,8 +4102,10 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
3968
4102
 
3969
4103
  const refresh_widget = () => {
3970
4104
 
3971
- if(instance)
4105
+ if( instance )
4106
+ {
3972
4107
  widget.instance = instance = Object.assign(deepCopy(default_instance), instance);
4108
+ }
3973
4109
 
3974
4110
  if(container) container.remove();
3975
4111
  if(custom_widgets) custom_widgets.remove();
@@ -3981,7 +4117,7 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
3981
4117
  this.queue(container);
3982
4118
 
3983
4119
  let buttonName = "<a class='fa-solid " + (options.icon ?? "fa-cube") + "' style='float:left'></a>";
3984
- buttonName += custom_widget_name + (!instance ? " [empty]" : "");
4120
+ buttonName += customWidgetName + (!instance ? " [empty]" : "");
3985
4121
  // Add alwayis icon to keep spacing right
3986
4122
  buttonName += "<a class='fa-solid " + (instance ? "fa-bars-staggered" : " ") + " menu' style='float:right; width:5%;'></a>";
3987
4123
 
@@ -3993,7 +4129,7 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
3993
4129
  else
3994
4130
  {
3995
4131
  addContextMenu(null, event, c => {
3996
- c.add("New " + custom_widget_name, () => {
4132
+ c.add("New " + customWidgetName, () => {
3997
4133
  instance = {};
3998
4134
  refresh_widget();
3999
4135
  element.querySelector(".lexcustomitems").toggleAttribute('hidden', false);
@@ -4105,10 +4241,10 @@ class NodeTree {
4105
4241
  _create_item( parent, node, level = 0, selectedId ) {
4106
4242
 
4107
4243
  const that = this;
4108
- const node_filter_input = this.domEl.querySelector( "#lexnodetree_filter" );
4244
+ const nodeFilterInput = this.domEl.querySelector( "#lexnodetree_filter" );
4109
4245
 
4110
4246
  node.children = node.children ?? [];
4111
- if( node_filter_input && !node.id.includes( node_filter_input.value ) || (selectedId != undefined) && selectedId != node.id )
4247
+ if( nodeFilterInput && !node.id.includes( nodeFilterInput.value ) || (selectedId != undefined) && selectedId != node.id )
4112
4248
  {
4113
4249
  for( var i = 0; i < node.children.length; ++i )
4114
4250
  {
@@ -4348,7 +4484,7 @@ class NodeTree {
4348
4484
  const nameInput = document.createElement( "input" );
4349
4485
  nameInput.toggleAttribute( "hidden", !node.rename );
4350
4486
  nameInput.value = node.id;
4351
- item.appendChild(nameInput);
4487
+ item.appendChild( nameInput );
4352
4488
 
4353
4489
  if( node.rename )
4354
4490
  {
@@ -4356,7 +4492,7 @@ class NodeTree {
4356
4492
  nameInput.focus();
4357
4493
  }
4358
4494
 
4359
- nameInput.addEventListener("keyup", function(e){
4495
+ nameInput.addEventListener("keyup", function( e ) {
4360
4496
  if( e.key == "Enter" )
4361
4497
  {
4362
4498
  this.value = this.value.replace(/\s/g, '_');
@@ -4379,7 +4515,7 @@ class NodeTree {
4379
4515
  }
4380
4516
  });
4381
4517
 
4382
- nameInput.addEventListener("blur", function(e){
4518
+ nameInput.addEventListener("blur", function( e ) {
4383
4519
  delete node.rename;
4384
4520
  that.refresh();
4385
4521
  });
@@ -4387,7 +4523,7 @@ class NodeTree {
4387
4523
  if( this.options.draggable ?? true )
4388
4524
  {
4389
4525
  // Drag nodes
4390
- if(parent) // Root doesn't move!
4526
+ if( parent ) // Root doesn't move!
4391
4527
  {
4392
4528
  item.addEventListener("dragstart", e => {
4393
4529
  window.__tree_node_dragged = node;
@@ -4454,7 +4590,7 @@ class NodeTree {
4454
4590
  // Show/hide children
4455
4591
  if( isParent )
4456
4592
  {
4457
- item.querySelector('a.hierarchy').addEventListener("click", function(e) {
4593
+ item.querySelector('a.hierarchy').addEventListener("click", function( e ) {
4458
4594
 
4459
4595
  handled = true;
4460
4596
  e.stopImmediatePropagation();
@@ -4472,40 +4608,44 @@ class NodeTree {
4472
4608
 
4473
4609
  // Add button icons
4474
4610
 
4611
+ const inputContainer = document.createElement( "div" );
4612
+ item.appendChild( inputContainer );
4613
+
4614
+ if( node.actions )
4615
+ {
4616
+ for( let i = 0; i < node.actions.length; ++i )
4617
+ {
4618
+ let a = node.actions[ i ];
4619
+ let actionEl = document.createElement('a');
4620
+ actionEl.className = "lexicon " + a.icon;
4621
+ actionEl.title = a.name;
4622
+ actionEl.addEventListener("click", function( e ) {
4623
+ a.callback( node, actionEl );
4624
+ e.stopPropagation();
4625
+ });
4626
+
4627
+ inputContainer.appendChild( actionEl );
4628
+ }
4629
+ }
4630
+
4475
4631
  if( !node.skipVisibility ?? false )
4476
4632
  {
4477
- let visibility = document.createElement('a');
4478
- visibility.className = "itemicon fa-solid fa-eye" + (!node.visible ? "-slash" : "");
4633
+ let visibility = document.createElement( 'a' );
4634
+ visibility.className = "lexicon fa-solid fa-eye" + ( !node.visible ? "-slash" : "" );
4479
4635
  visibility.title = "Toggle visible";
4480
- visibility.addEventListener("click", function(e) {
4636
+ visibility.addEventListener("click", function( e ) {
4481
4637
  e.stopPropagation();
4482
4638
  node.visible = node.visible === undefined ? false : !node.visible;
4483
- this.className = "itemicon fa-solid fa-eye" + (!node.visible ? "-slash" : "");
4639
+ this.className = "lexicon fa-solid fa-eye" + ( !node.visible ? "-slash" : "" );
4484
4640
  // Trigger visibility event
4485
4641
  if( that.onevent )
4486
4642
  {
4487
- const event = new TreeEvent(TreeEvent.NODE_VISIBILITY, node, node.visible);
4643
+ const event = new TreeEvent( TreeEvent.NODE_VISIBILITY, node, node.visible );
4488
4644
  that.onevent( event );
4489
4645
  }
4490
4646
  });
4491
4647
 
4492
- item.appendChild(visibility);
4493
- }
4494
-
4495
- if( node.actions )
4496
- {
4497
- for( var i = 0; i < node.actions.length; ++i )
4498
- {
4499
- let a = node.actions[i];
4500
- var actionEl = document.createElement('a');
4501
- actionEl.className = "itemicon " + a.icon;
4502
- actionEl.title = a.name;
4503
- actionEl.addEventListener("click", function(e) {
4504
- a.callback(node, actionEl);
4505
- e.stopPropagation();
4506
- });
4507
- item.appendChild(actionEl);
4508
- }
4648
+ inputContainer.appendChild( visibility );
4509
4649
  }
4510
4650
 
4511
4651
  if( selectedId != undefined && node.id == selectedId )
@@ -4566,28 +4706,35 @@ class Panel {
4566
4706
  */
4567
4707
 
4568
4708
  constructor( options = {} ) {
4709
+
4569
4710
  var root = document.createElement('div');
4570
4711
  root.className = "lexpanel";
4712
+
4571
4713
  if( options.id )
4714
+ {
4572
4715
  root.id = options.id;
4716
+ }
4717
+
4573
4718
  if( options.className )
4719
+ {
4574
4720
  root.className += " " + options.className;
4721
+ }
4575
4722
 
4576
4723
  root.style.width = options.width || "calc( 100% - 6px )";
4577
4724
  root.style.height = options.height || "100%";
4578
- Object.assign(root.style, options.style ?? {});
4725
+ Object.assign( root.style, options.style ?? {} );
4579
4726
 
4580
4727
  this._inline_widgets_left = -1;
4581
4728
  this._inline_queued_container = null;
4582
4729
 
4583
4730
  this.root = root;
4584
4731
 
4585
- this.onevent = (e => {});
4732
+ this.onevent = ( e => {} );
4586
4733
 
4587
4734
  // branches
4588
- this.branch_open = false;
4735
+ this._branchOpen = false;
4589
4736
  this.branches = [];
4590
- this.current_branch = null;
4737
+ this._currentBranch = null;
4591
4738
  this.widgets = {};
4592
4739
  this._queue = []; // Append widgets in other locations
4593
4740
  }
@@ -4628,13 +4775,9 @@ class Panel {
4628
4775
 
4629
4776
  attach( content ) {
4630
4777
 
4631
- if(!content)
4632
- throw("no content to attach");
4633
-
4778
+ console.assert( content, "No content to attach!" );
4634
4779
  content.parent = this;
4635
- let element = content.root ? content.root : content;
4636
- //this.root.style.maxHeight = "800px"; // limit size when attaching stuff from outside
4637
- this.root.appendChild( element );
4780
+ this.root.appendChild( content.root ? content.root : content );
4638
4781
  }
4639
4782
 
4640
4783
  /**
@@ -4643,18 +4786,18 @@ class Panel {
4643
4786
 
4644
4787
  clear() {
4645
4788
 
4646
- this.branch_open = false;
4789
+ this._branchOpen = false;
4647
4790
  this.branches = [];
4648
- this.current_branch = null;
4791
+ this._currentBranch = null;
4649
4792
 
4650
4793
  for( let w in this.widgets )
4651
4794
  {
4652
- if( this.widgets[w].options && this.widgets[w].options.signal )
4795
+ if( this.widgets[ w ].options && this.widgets[ w ].options.signal )
4653
4796
  {
4654
- const signal = this.widgets[w].options.signal;
4797
+ const signal = this.widgets[ w ].options.signal;
4655
4798
  for( let i = 0; i < LX.signals[signal].length; i++ )
4656
4799
  {
4657
- if( LX.signals[signal][i] == this.widgets[w] )
4800
+ if( LX.signals[signal][i] == this.widgets[ w ] )
4658
4801
  {
4659
4802
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
4660
4803
  }
@@ -4666,7 +4809,7 @@ class Panel {
4666
4809
  {
4667
4810
  for( let w = 0; w < this.signals.length; w++ )
4668
4811
  {
4669
- let widget = Object.values(this.signals[w])[0];
4812
+ let widget = Object.values(this.signals[ w ])[0];
4670
4813
  let signal = widget.options.signal;
4671
4814
  for( let i = 0; i < LX.signals[signal].length; i++ )
4672
4815
  {
@@ -4691,7 +4834,7 @@ class Panel {
4691
4834
  sameLine( number ) {
4692
4835
 
4693
4836
  this._inline_queued_container = this.queuedContainer;
4694
- this._inline_widgets_left = number || Infinity;
4837
+ this._inline_widgets_left = ( number || Infinity );
4695
4838
  }
4696
4839
 
4697
4840
  /**
@@ -4701,7 +4844,7 @@ class Panel {
4701
4844
 
4702
4845
  endLine( justifyContent ) {
4703
4846
 
4704
- if( this._inline_widgets_left == -1)
4847
+ if( this._inline_widgets_left == -1 )
4705
4848
  {
4706
4849
  console.warn("No pending widgets to be inlined!");
4707
4850
  return;
@@ -4723,27 +4866,37 @@ class Panel {
4723
4866
  // Push all elements single element or Array[element, container]
4724
4867
  for( let item of this._inlineWidgets )
4725
4868
  {
4726
- const is_pair = item.constructor == Array;
4869
+ const isPair = ( item.constructor == Array );
4727
4870
 
4728
- if(is_pair)
4871
+ if( isPair )
4729
4872
  {
4730
4873
  // eg. an array, inline items appended later to
4731
- if( this._inline_queued_container)
4732
- this._inlineContainer.appendChild( item[0] );
4874
+ if( this._inline_queued_container )
4875
+ {
4876
+ this._inlineContainer.appendChild( item[ 0 ] );
4877
+ }
4733
4878
  // eg. a dropdown, item is appended to parent, not to inline cont.
4734
4879
  else
4735
- item[1].appendChild(item[0]);
4880
+ {
4881
+ item[ 1 ].appendChild( item[ 0 ] );
4882
+ }
4736
4883
  }
4737
4884
  else
4885
+ {
4738
4886
  this._inlineContainer.appendChild( item );
4887
+ }
4739
4888
  }
4740
4889
 
4741
- if(!this._inline_queued_container)
4890
+ if( !this._inline_queued_container )
4742
4891
  {
4743
- if( this.current_branch)
4744
- this.current_branch.content.appendChild( this._inlineContainer );
4892
+ if( this._currentBranch )
4893
+ {
4894
+ this._currentBranch.content.appendChild( this._inlineContainer );
4895
+ }
4745
4896
  else
4897
+ {
4746
4898
  this.root.appendChild( this._inlineContainer );
4899
+ }
4747
4900
  }
4748
4901
  else
4749
4902
  {
@@ -4767,20 +4920,24 @@ class Panel {
4767
4920
 
4768
4921
  branch( name, options = {} ) {
4769
4922
 
4770
- if( this.branch_open )
4923
+ if( this._branchOpen )
4924
+ {
4771
4925
  this.merge();
4926
+ }
4772
4927
 
4773
4928
  // Create new branch
4774
- var branch = new Branch(name, options);
4929
+ var branch = new Branch( name, options );
4775
4930
  branch.panel = this;
4776
4931
 
4777
4932
  // Declare new open
4778
- this.branch_open = true;
4779
- this.current_branch = branch;
4933
+ this._branchOpen = true;
4934
+ this._currentBranch = branch;
4780
4935
 
4781
4936
  // Append to panel
4782
- if( this.branches.length == 0)
4937
+ if( this.branches.length == 0 )
4938
+ {
4783
4939
  branch.root.classList.add('first');
4940
+ }
4784
4941
 
4785
4942
  // This is the last!
4786
4943
  this.root.querySelectorAll(".lexbranch.last").forEach( e => { e.classList.remove("last"); } );
@@ -4792,16 +4949,15 @@ class Panel {
4792
4949
  // Add widget filter
4793
4950
  if( options.filter )
4794
4951
  {
4795
- this._addFilter( options.filter, {callback: this._searchWidgets.bind(this, branch.name)} );
4952
+ this._addFilter( options.filter, { callback: this._searchWidgets.bind( this, branch.name ) } );
4796
4953
  }
4797
4954
 
4798
4955
  return branch;
4799
4956
  }
4800
4957
 
4801
4958
  merge() {
4802
-
4803
- this.branch_open = false;
4804
- this.current_branch = null;
4959
+ this._branchOpen = false;
4960
+ this._currentBranch = null;
4805
4961
  }
4806
4962
 
4807
4963
  _pick( arg, def ) {
@@ -4827,9 +4983,16 @@ class Panel {
4827
4983
  Panel Widgets
4828
4984
  */
4829
4985
 
4830
- create_widget( name, type, options = {} ) {
4986
+ _createWidget( type, name, value, options = {} ) {
4831
4987
 
4832
- let widget = new Widget( name, type, options );
4988
+ if( !LX.CREATED_INSTANCES )
4989
+ {
4990
+ LX.CREATED_INSTANCES = [];
4991
+ }
4992
+
4993
+ LX.CREATED_INSTANCES[ type ] = true;
4994
+
4995
+ let widget = new Widget( name, type, value, options );
4833
4996
 
4834
4997
  let element = document.createElement( 'div' );
4835
4998
  element.className = "lexwidget";
@@ -4843,7 +5006,7 @@ class Panel {
4843
5006
 
4844
5007
  if( type != Widget.TITLE )
4845
5008
  {
4846
- element.style.width = "calc(100% - " + (this.current_branch || type == Widget.FILE ? 10 : 20) + "px)";
5009
+ element.style.width = "calc(100% - " + (this._currentBranch || type == Widget.FILE || type == Widget.TREE ? 10 : 20) + "px)";
4847
5010
 
4848
5011
  if( options.width )
4849
5012
  {
@@ -4865,7 +5028,7 @@ class Panel {
4865
5028
 
4866
5029
  if( name != undefined )
4867
5030
  {
4868
- if( !(options.hideName ?? false) )
5031
+ if( !( options.hideName ?? false ) )
4869
5032
  {
4870
5033
  let domName = document.createElement( 'div' );
4871
5034
  domName.className = "lexwidgetname";
@@ -4873,10 +5036,10 @@ class Panel {
4873
5036
  {
4874
5037
  domName.classList.add( "float-" + options.justifyName );
4875
5038
  }
4876
- domName.innerHTML = name || "";
5039
+ domName.innerHTML = name;
4877
5040
  domName.title = options.title ?? domName.innerHTML;
4878
5041
  domName.style.width = options.nameWidth || LX.DEFAULT_NAME_WIDTH;
4879
- element.appendChild(domName);
5042
+ element.appendChild( domName );
4880
5043
  element.domName = domName;
4881
5044
 
4882
5045
  // Copy-paste info
@@ -4884,10 +5047,23 @@ class Panel {
4884
5047
  e.preventDefault();
4885
5048
  widget.oncontextmenu( e );
4886
5049
  });
5050
+
5051
+ if( !( options.skipReset ?? false ) && ( value != null ) )
5052
+ {
5053
+ Panel._add_reset_property( domName, function( e ) {
5054
+ widget.set( widget._initialValue, false, e );
5055
+ // Og value, don't show it
5056
+ this.style.display = "none";
5057
+ });
5058
+ }
4887
5059
  }
4888
5060
 
4889
5061
  this.widgets[ name ] = widget;
4890
5062
  }
5063
+ else
5064
+ {
5065
+ options.hideName = true;
5066
+ }
4891
5067
 
4892
5068
  if( options.signal )
4893
5069
  {
@@ -4914,13 +5090,13 @@ class Panel {
4914
5090
  }
4915
5091
  else if( !this.queuedContainer )
4916
5092
  {
4917
- if( this.current_branch )
5093
+ if( this._currentBranch )
4918
5094
  {
4919
5095
  if( !options.skipWidget )
4920
5096
  {
4921
- this.current_branch.widgets.push( widget );
5097
+ this._currentBranch.widgets.push( widget );
4922
5098
  }
4923
- this.current_branch.content.appendChild( el );
5099
+ this._currentBranch.content.appendChild( el );
4924
5100
  }
4925
5101
  else
4926
5102
  {
@@ -4981,30 +5157,32 @@ class Panel {
4981
5157
  options.skipWidget = options.skipWidget ?? true;
4982
5158
  options.skipInlineCount = true;
4983
5159
 
4984
- let widget = this.create_widget(null, Widget.TEXT, options);
4985
- let element = widget.domEl;
5160
+ let widget = this._createWidget( Widget.TEXT, null, null, options );
5161
+ const element = widget.domEl;
4986
5162
  element.className += " lexfilter noname";
4987
5163
 
4988
5164
  let input = document.createElement('input');
4989
5165
  input.className = 'lexinput-filter';
4990
- input.setAttribute("placeholder", options.placeholder);
5166
+ input.setAttribute( "placeholder", options.placeholder );
4991
5167
  input.style.width = "calc( 100% - 17px )";
4992
5168
  input.value = options.filterValue || "";
4993
5169
 
4994
5170
  let searchIcon = document.createElement('a');
4995
5171
  searchIcon.className = "fa-solid fa-magnifying-glass";
4996
- element.appendChild(searchIcon);
4997
- element.appendChild(input);
5172
+ element.appendChild( searchIcon );
5173
+ element.appendChild( input );
4998
5174
 
4999
5175
  input.addEventListener("input", (e) => {
5000
- if(options.callback)
5001
- options.callback(input.value, e);
5176
+ if( options.callback )
5177
+ {
5178
+ options.callback( input.value, e );
5179
+ }
5002
5180
  });
5003
5181
 
5004
5182
  return element;
5005
5183
  }
5006
5184
 
5007
- _searchWidgets(branchName, value) {
5185
+ _searchWidgets( branchName, value ) {
5008
5186
 
5009
5187
  for( let b of this.branches )
5010
5188
  {
@@ -5078,10 +5256,14 @@ class Panel {
5078
5256
  _trigger( event, callback ) {
5079
5257
 
5080
5258
  if( callback )
5259
+ {
5081
5260
  callback.call( this, event.value, event.domEvent, event.name );
5261
+ }
5082
5262
 
5083
5263
  if( this.onevent )
5264
+ {
5084
5265
  this.onevent.call( this, event );
5266
+ }
5085
5267
  }
5086
5268
 
5087
5269
  /**
@@ -5096,7 +5278,7 @@ class Panel {
5096
5278
  return this.branches.find( b => b.name == name );
5097
5279
  }
5098
5280
 
5099
- return this.current_branch;
5281
+ return this._currentBranch;
5100
5282
  }
5101
5283
 
5102
5284
  /**
@@ -5106,9 +5288,9 @@ class Panel {
5106
5288
 
5107
5289
  queue( domEl ) {
5108
5290
 
5109
- if( !domEl && this.current_branch)
5291
+ if( !domEl && this._currentBranch)
5110
5292
  {
5111
- domEl = this.current_branch.root;
5293
+ domEl = this._currentBranch.root;
5112
5294
  }
5113
5295
 
5114
5296
  if( this.queuedContainer )
@@ -5141,12 +5323,14 @@ class Panel {
5141
5323
 
5142
5324
  addBlank( height = 8, width ) {
5143
5325
 
5144
- let widget = this.create_widget(null, Widget.addBlank);
5326
+ let widget = this._createWidget( Widget.BLANK );
5145
5327
  widget.domEl.className += " blank";
5146
5328
  widget.domEl.style.height = height + "px";
5147
5329
 
5148
- if(width)
5330
+ if( width )
5331
+ {
5149
5332
  widget.domEl.style.width = width;
5333
+ }
5150
5334
 
5151
5335
  return widget;
5152
5336
  }
@@ -5164,13 +5348,12 @@ class Panel {
5164
5348
 
5165
5349
  addTitle( name, options = {} ) {
5166
5350
 
5167
- if( !name )
5168
- {
5169
- throw( "Can't create Title without text!" );
5170
- }
5351
+ console.assert( name, "Can't create Title Widget without text!" );
5171
5352
 
5172
- let widget = this.create_widget( null, Widget.TITLE, options );
5173
- let element = widget.domEl;
5353
+ // Note: Titles are not registered in Panel.widgets by now
5354
+ let widget = this._createWidget( Widget.TITLE, null, null, options );
5355
+
5356
+ const element = widget.domEl;
5174
5357
  element.className = "lextitle";
5175
5358
 
5176
5359
  if( options.icon )
@@ -5181,7 +5364,7 @@ class Panel {
5181
5364
  element.appendChild( icon );
5182
5365
  }
5183
5366
 
5184
- let text = document.createElement( "span");
5367
+ let text = document.createElement( "span" );
5185
5368
  text.innerText = name;
5186
5369
  element.appendChild( text );
5187
5370
 
@@ -5207,6 +5390,7 @@ class Panel {
5207
5390
  * @param {String} value Text value
5208
5391
  * @param {Function} callback Callback function on change
5209
5392
  * @param {*} options:
5393
+ * hideName: Don't use name as label [false]
5210
5394
  * disabled: Make the widget disabled [false]
5211
5395
  * required: Make the input required
5212
5396
  * placeholder: Add input placeholder
@@ -5220,33 +5404,43 @@ class Panel {
5220
5404
 
5221
5405
  addText( name, value, callback, options = {} ) {
5222
5406
 
5223
- let widget = this.create_widget( name, Widget.TEXT, options );
5407
+ let widget = this._createWidget( Widget.TEXT, name, String( value ), options );
5224
5408
 
5225
5409
  widget.onGetValue = () => {
5226
- return wValue.value;
5410
+ return value;
5227
5411
  };
5228
5412
 
5229
- widget.onSetValue = ( newValue, skipCallback ) => {
5230
- this.disabled ? wValue.innerText = newValue : wValue.value = newValue;
5231
- Panel._dispatch_event( wValue, "focusout", skipCallback );
5413
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5414
+
5415
+ if( !widget.valid( newValue ) || ( this._lastValueTriggered == newValue ) )
5416
+ {
5417
+ return;
5418
+ }
5419
+
5420
+ this._lastValueTriggered = value = newValue;
5421
+
5422
+ if( options.disabled )
5423
+ {
5424
+ wValue.innerText = newValue;
5425
+ }
5426
+ else
5427
+ {
5428
+ wValue.value = newValue;
5429
+ }
5430
+
5431
+ if( !skipCallback )
5432
+ {
5433
+ this._trigger( new IEvent( name, newValue, event ), callback );
5434
+ }
5232
5435
  };
5233
5436
 
5234
- widget.valid = () => {
5235
- if( wValue.pattern == "" ) { return true; }
5437
+ widget.valid = ( v ) => {
5438
+ if( !v.length || wValue.pattern == "" ) return true;
5236
5439
  const regexp = new RegExp( wValue.pattern );
5237
- return regexp.test( wValue.value );
5440
+ return regexp.test( v );
5238
5441
  };
5239
5442
 
5240
- let element = widget.domEl;
5241
-
5242
- // Add reset functionality
5243
- if( widget.name && !( options.skipReset ?? false ) ) {
5244
- Panel._add_reset_property( element.domName, function() {
5245
- wValue.value = wValue.iValue;
5246
- this.style.display = "none";
5247
- Panel._dispatch_event( wValue, "focusout" );
5248
- } );
5249
- }
5443
+ const element = widget.domEl;
5250
5444
 
5251
5445
  // Add widget value
5252
5446
 
@@ -5269,7 +5463,7 @@ class Panel {
5269
5463
  wValue.type = options.type || "";
5270
5464
  wValue.value = wValue.iValue = value || "";
5271
5465
  wValue.style.width = "100%";
5272
- wValue.style.textAlign = options.float ?? "";
5466
+ wValue.style.textAlign = ( options.float ?? "" );
5273
5467
 
5274
5468
  wValue.setAttribute( "placeholder", options.placeholder ?? "" );
5275
5469
 
@@ -5283,39 +5477,25 @@ class Panel {
5283
5477
  wValue.setAttribute( "pattern", options.pattern );
5284
5478
  }
5285
5479
 
5286
- var resolve = ( function( val, event ) {
5287
-
5288
- if( !widget.valid() )
5289
- {
5290
- return;
5291
- }
5292
-
5293
- const skipCallback = event.detail;
5294
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5295
- if( btn ) btn.style.display = ( val != wValue.iValue ? "block" : "none" );
5296
- if( !skipCallback )
5297
- {
5298
- this._trigger( new IEvent( name, val, event ), callback );
5299
- }
5300
-
5301
- }).bind( this );
5480
+ const trigger = options.trigger ?? "default";
5302
5481
 
5303
- const trigger = options.trigger ?? 'default';
5304
-
5305
- if( trigger == 'default' )
5482
+ if( trigger == "default" )
5306
5483
  {
5307
5484
  wValue.addEventListener( "keyup", function( e ){
5308
- if(e.key == 'Enter')
5309
- resolve( e.target.value, e );
5485
+ if( e.key == "Enter" )
5486
+ {
5487
+ wValue.blur();
5488
+ }
5310
5489
  });
5490
+
5311
5491
  wValue.addEventListener( "focusout", function( e ){
5312
- resolve( e.target.value, e );
5492
+ widget.set( e.target.value, false, e );
5313
5493
  });
5314
5494
  }
5315
- else if( trigger == 'input' )
5495
+ else if( trigger == "input" )
5316
5496
  {
5317
5497
  wValue.addEventListener("input", function( e ){
5318
- resolve( e.target.value, e );
5498
+ widget.set( e.target.value, false, e );
5319
5499
  });
5320
5500
  }
5321
5501
 
@@ -5331,14 +5511,17 @@ class Panel {
5331
5511
  container.appendChild( icon );
5332
5512
  }
5333
5513
 
5334
- } else
5514
+ }
5515
+ else
5335
5516
  {
5336
5517
  wValue = document.createElement( options.url ? 'a' : 'div' );
5518
+
5337
5519
  if( options.url )
5338
5520
  {
5339
5521
  wValue.href = options.url;
5340
5522
  wValue.target = "_blank";
5341
5523
  }
5524
+
5342
5525
  const icon = options.warning ? '<i class="fa-solid fa-triangle-exclamation"></i>' : '';
5343
5526
  wValue.innerHTML = ( icon + value ) || "";
5344
5527
  wValue.style.width = "100%";
@@ -5351,7 +5534,8 @@ class Panel {
5351
5534
  element.appendChild( container );
5352
5535
 
5353
5536
  // Remove branch padding and margins
5354
- if( !widget.name )
5537
+ const useNameAsLabel = !( options.hideName ?? false );
5538
+ if( !useNameAsLabel )
5355
5539
  {
5356
5540
  element.className += " noname";
5357
5541
  container.style.width = "100%";
@@ -5365,7 +5549,8 @@ class Panel {
5365
5549
  * @param {String} name Widget name
5366
5550
  * @param {String} value Text Area value
5367
5551
  * @param {Function} callback Callback function on change
5368
- * @param {*} options:
5552
+ * @param {Object} options:
5553
+ * hideName: Don't use name as label [false]
5369
5554
  * disabled: Make the widget disabled [false]
5370
5555
  * placeholder: Add input placeholder
5371
5556
  * trigger: Choose onchange trigger (default, input) [default]
@@ -5377,82 +5562,76 @@ class Panel {
5377
5562
 
5378
5563
  addTextArea( name, value, callback, options = {} ) {
5379
5564
 
5380
- let widget = this.create_widget( name, Widget.TEXTAREA, options );
5565
+ let widget = this._createWidget( Widget.TEXTAREA, name, value, options );
5381
5566
 
5382
5567
  widget.onGetValue = () => {
5383
- return wValue.value;
5384
- };
5385
- widget.onSetValue = ( newValue, skipCallback ) => {
5386
- wValue.value = newValue;
5387
- Panel._dispatch_event( wValue, "focusout", skipCallback );
5568
+ return value;
5388
5569
  };
5389
5570
 
5390
- let element = widget.domEl;
5571
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5391
5572
 
5392
- // Add reset functionality
5393
- if( widget.name && !( options.skipReset ?? false ) ) {
5394
- Panel._add_reset_property( element.domName, function() {
5395
- wValue.value = wValue.iValue;
5396
- this.style.display = "none";
5397
- Panel._dispatch_event( wValue, "focusout" );
5398
- });
5399
- }
5573
+ wValue.value = value = newValue;
5574
+
5575
+ if( !skipCallback )
5576
+ {
5577
+ this._trigger( new IEvent( name, newValue, event ), callback );
5578
+ }
5579
+ };
5580
+
5581
+ const element = widget.domEl;
5400
5582
 
5401
5583
  // Add widget value
5402
5584
 
5403
- let container = document.createElement( 'div' );
5585
+ let container = document.createElement( "div" );
5404
5586
  container.className = "lextextarea";
5405
5587
  container.style.width = options.inputWidth || "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + " )";
5406
5588
  container.style.height = options.height;
5407
5589
  container.style.display = "flex";
5408
5590
 
5409
- let wValue = document.createElement( 'textarea' );
5591
+ let wValue = document.createElement( "textarea" );
5410
5592
  wValue.value = wValue.iValue = value || "";
5411
5593
  wValue.style.width = "100%";
5412
5594
  wValue.style.textAlign = options.float ?? "";
5413
5595
  Object.assign( wValue.style, options.style ?? {} );
5414
5596
 
5415
- if( options.disabled ?? false ) wValue.setAttribute("disabled", true);
5416
- if( options.placeholder ) wValue.setAttribute("placeholder", options.placeholder);
5597
+ if( options.disabled ?? false ) wValue.setAttribute( "disabled", true );
5598
+ if( options.placeholder ) wValue.setAttribute( "placeholder", options.placeholder );
5417
5599
 
5418
- var resolve = (function( val, event ) {
5419
- const skipCallback = event.detail;
5420
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5421
- if( btn ) btn.style.display = ( val != wValue.iValue ? "block" : "none" );
5422
- if( !skipCallback ) this._trigger( new IEvent( name, val, event ), callback );
5423
- }).bind(this);
5600
+ const trigger = options.trigger ?? "default";
5424
5601
 
5425
- const trigger = options.trigger ?? 'default';
5426
-
5427
- if(trigger == 'default')
5602
+ if( trigger == "default" )
5428
5603
  {
5429
- wValue.addEventListener("keyup", function(e){
5430
- if(e.key == 'Enter')
5431
- resolve(e.target.value, e);
5604
+ wValue.addEventListener("keyup", function( e ) {
5605
+ if( e.key == "Enter" )
5606
+ {
5607
+ wValue.blur();
5608
+ }
5432
5609
  });
5433
- wValue.addEventListener("focusout", function(e){
5434
- resolve(e.target.value, e);
5610
+
5611
+ wValue.addEventListener("focusout", function( e ) {
5612
+ widget.set( e.target.value, false, e );
5435
5613
  });
5436
5614
  }
5437
- else if(trigger == 'input')
5615
+ else if( trigger == "input" )
5438
5616
  {
5439
- wValue.addEventListener("input", function(e){
5440
- resolve(e.target.value, e);
5617
+ wValue.addEventListener("input", function( e ) {
5618
+ widget.set( e.target.value, false, e );
5441
5619
  });
5442
5620
  }
5443
5621
 
5444
- if(options.icon)
5622
+ if( options.icon )
5445
5623
  {
5446
5624
  let icon = document.createElement('a');
5447
5625
  icon.className = "inputicon " + options.icon;
5448
- container.appendChild(icon);
5626
+ container.appendChild( icon );
5449
5627
  }
5450
5628
 
5451
- container.appendChild(wValue);
5452
- element.appendChild(container);
5629
+ container.appendChild( wValue );
5630
+ element.appendChild( container );
5453
5631
 
5454
5632
  // Remove branch padding and margins
5455
- if( !widget.name )
5633
+ const useNameAsLabel = !( options.hideName ?? false );
5634
+ if( !useNameAsLabel )
5456
5635
  {
5457
5636
  element.className += " noname";
5458
5637
  container.style.width = "100%";
@@ -5465,7 +5644,7 @@ class Panel {
5465
5644
  // Update height depending on the content
5466
5645
  wValue.style.height = wValue.scrollHeight + "px";
5467
5646
  }
5468
- }, 10);
5647
+ }, 10 );
5469
5648
 
5470
5649
  return widget;
5471
5650
  }
@@ -5473,6 +5652,7 @@ class Panel {
5473
5652
  /**
5474
5653
  * @method addLabel
5475
5654
  * @param {String} value Information string
5655
+ * @param {Object} options Text options
5476
5656
  */
5477
5657
 
5478
5658
  addLabel( value, options = {} ) {
@@ -5487,6 +5667,7 @@ class Panel {
5487
5667
  * @param {String} value Button name
5488
5668
  * @param {Function} callback Callback function on click
5489
5669
  * @param {*} options:
5670
+ * hideName: Don't use name as label [false]
5490
5671
  * disabled: Make the widget disabled [false]
5491
5672
  * icon: Icon class to show as button value
5492
5673
  * img: Path to image to show as button value
@@ -5495,19 +5676,19 @@ class Panel {
5495
5676
 
5496
5677
  addButton( name, value, callback, options = {} ) {
5497
5678
 
5498
- let widget = this.create_widget( name, Widget.BUTTON, options );
5679
+ let widget = this._createWidget( Widget.BUTTON, name, null, options );
5499
5680
 
5500
5681
  widget.onGetValue = () => {
5501
5682
  return wValue.innerText;
5502
5683
  };
5503
5684
 
5504
- widget.onSetValue = ( newValue, skipCallback ) => {
5685
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5505
5686
  wValue.innerHTML =
5506
- (options.icon ? "<a class='" + options.icon + "'></a>" :
5507
- ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (newValue || "") + "</span>" ));
5687
+ ( options.icon ? "<a class='" + options.icon + "'></a>" :
5688
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + ( newValue || "" ) + "</span>" ) );
5508
5689
  };
5509
5690
 
5510
- let element = widget.domEl;
5691
+ const element = widget.domEl;
5511
5692
 
5512
5693
  var wValue = document.createElement( 'button' );
5513
5694
  wValue.title = options.title ?? "";
@@ -5519,22 +5700,22 @@ class Panel {
5519
5700
  }
5520
5701
 
5521
5702
  wValue.innerHTML =
5522
- (options.icon ? "<a class='" + options.icon + "'></a>" :
5523
- ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (value || "") + "</span>" ));
5703
+ ( options.icon ? "<a class='" + options.icon + "'></a>" :
5704
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + ( value || "" ) + "</span>" ) );
5524
5705
 
5525
- wValue.style.width = "calc( 100% - " + (options.nameWidth ?? LX.DEFAULT_NAME_WIDTH) + ")";
5706
+ wValue.style.width = "calc( 100% - " + ( options.nameWidth ?? LX.DEFAULT_NAME_WIDTH ) + ")";
5526
5707
 
5527
5708
  if( options.disabled )
5528
5709
  {
5529
5710
  wValue.setAttribute( "disabled", true );
5530
5711
  }
5531
5712
 
5532
- wValue.addEventListener("click", e => {
5713
+ wValue.addEventListener( "click", e => {
5533
5714
  if( options.selectable )
5534
5715
  {
5535
5716
  if( options.parent )
5536
5717
  {
5537
- options.parent.querySelectorAll(".lexbutton.selected").forEach( e => { if(e == wValue) return; e.classList.remove("selected") } );
5718
+ options.parent.querySelectorAll(".lexbutton.selected").forEach( e => { if( e == wValue ) return; e.classList.remove( "selected" ) } );
5538
5719
  }
5539
5720
 
5540
5721
  wValue.classList.toggle('selected');
@@ -5545,8 +5726,9 @@ class Panel {
5545
5726
 
5546
5727
  element.appendChild( wValue );
5547
5728
 
5548
- // Remove branch padding and margins
5549
- if( !widget.name )
5729
+ // Remove branch padding and
5730
+ const useNameAsLabel = !( options.hideName ?? false ) && !( options.icon || options.img );
5731
+ if( !useNameAsLabel )
5550
5732
  {
5551
5733
  wValue.className += " noname";
5552
5734
  wValue.style.width = "100%";
@@ -5560,6 +5742,7 @@ class Panel {
5560
5742
  * @param {String} name Widget name
5561
5743
  * @param {Array} values Each of the {value, callback, selected, disabled} items
5562
5744
  * @param {*} options:
5745
+ * hideName: Don't use name as label [false]
5563
5746
  * float: Justify content (left, center, right) [center]
5564
5747
  * @legacy selected: Selected item by default by value
5565
5748
  * noSelection: Buttons can be clicked, but they are not selectable
@@ -5568,8 +5751,8 @@ class Panel {
5568
5751
 
5569
5752
  addComboButtons( name, values, options = {} ) {
5570
5753
 
5571
- let widget = this.create_widget( name, Widget.BUTTON, options );
5572
- let element = widget.domEl;
5754
+ let widget = this._createWidget( Widget.BUTTONS, name, null, options );
5755
+ const element = widget.domEl;
5573
5756
 
5574
5757
  let that = this;
5575
5758
  let container = document.createElement('div');
@@ -5638,7 +5821,8 @@ class Panel {
5638
5821
  }
5639
5822
 
5640
5823
  // Remove branch padding and margins
5641
- if( !widget.name )
5824
+ const useNameAsLabel = !( options.hideName ?? false );
5825
+ if( !useNameAsLabel )
5642
5826
  {
5643
5827
  element.className += " noname";
5644
5828
  container.style.width = "100%";
@@ -5654,17 +5838,19 @@ class Panel {
5654
5838
  * @method addCard
5655
5839
  * @param {String} name Card Name
5656
5840
  * @param {*} options:
5657
- * title: title if any
5658
- * text: card text if any
5659
- * src: url of the image if any
5841
+ * text: Card text
5842
+ * link: Card link
5843
+ * title: Card dom title
5844
+ * src: url of the image
5660
5845
  * callback (Function): function to call on click
5661
5846
  */
5662
5847
 
5663
5848
  addCard( name, options = {} ) {
5664
5849
 
5665
5850
  options.hideName = true;
5666
- let widget = this.create_widget(name, Widget.CARD, options);
5667
- let element = widget.domEl;
5851
+
5852
+ let widget = this._createWidget( Widget.CARD, name, null, options );
5853
+ const element = widget.domEl;
5668
5854
 
5669
5855
  let container = document.createElement('div');
5670
5856
  container.className = "lexcard";
@@ -5676,7 +5862,7 @@ class Panel {
5676
5862
  img.src = options.img;
5677
5863
  container.appendChild(img);
5678
5864
 
5679
- if(options.link != undefined)
5865
+ if( options.link != undefined )
5680
5866
  {
5681
5867
  img.style.cursor = "pointer";
5682
5868
  img.addEventListener('click', function() {
@@ -5686,29 +5872,30 @@ class Panel {
5686
5872
  }
5687
5873
  }
5688
5874
 
5689
- let name_el = document.createElement('span');
5690
- name_el.innerText = name;
5875
+ let cardNameDom = document.createElement('span');
5876
+ cardNameDom.innerText = name;
5691
5877
 
5692
- if(options.link != undefined)
5878
+ if( options.link != undefined )
5693
5879
  {
5694
- let link_el = document.createElement('a');
5695
- link_el.innerText = name;
5696
- link_el.href = options.link;
5697
- link_el.target = options.target ?? "";
5698
- name_el.innerText = "";
5699
- name_el.appendChild(link_el);
5880
+ let cardLinkDom = document.createElement( 'a' );
5881
+ cardLinkDom.innerText = name;
5882
+ cardLinkDom.href = options.link;
5883
+ cardLinkDom.target = options.target ?? "";
5884
+ cardNameDom.innerText = "";
5885
+ cardNameDom.appendChild( cardLinkDom );
5700
5886
  }
5701
5887
 
5702
- container.appendChild(name_el);
5888
+ container.appendChild( cardNameDom );
5703
5889
 
5704
- if( options.callback ) {
5890
+ if( options.callback )
5891
+ {
5705
5892
  container.style.cursor = "pointer";
5706
- container.addEventListener("click", (e) => {
5707
- this._trigger( new IEvent(name, null, e), options.callback );
5893
+ container.addEventListener("click", ( e ) => {
5894
+ this._trigger( new IEvent( name, null, e ), options.callback );
5708
5895
  });
5709
5896
  }
5710
5897
 
5711
- element.appendChild(container);
5898
+ element.appendChild( container );
5712
5899
 
5713
5900
  return widget;
5714
5901
  }
@@ -5733,13 +5920,13 @@ class Panel {
5733
5920
  // Always hide name for this one
5734
5921
  options.hideName = true;
5735
5922
 
5736
- let widget = this.create_widget( name, Widget.FORM, options );
5923
+ let widget = this._createWidget( Widget.FORM, name, null, options );
5737
5924
 
5738
5925
  widget.onGetValue = () => {
5739
5926
  return container.formData;
5740
5927
  };
5741
5928
 
5742
- widget.onSetValue = ( newValue, skipCallback ) => {
5929
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5743
5930
  container.formData = newValue;
5744
5931
  const entries = container.querySelectorAll( ".lexwidget" );
5745
5932
  for( let i = 0; i < entries.length; ++i )
@@ -5758,7 +5945,7 @@ class Panel {
5758
5945
 
5759
5946
  // Add widget value
5760
5947
 
5761
- let element = widget.domEl;
5948
+ const element = widget.domEl;
5762
5949
 
5763
5950
  let container = document.createElement( 'div' );
5764
5951
  container.className = "lexformdata";
@@ -5812,31 +5999,29 @@ class Panel {
5812
5999
 
5813
6000
  element.appendChild( container );
5814
6001
 
5815
- if( !widget.name || options.hideName )
5816
- {
5817
- element.className += " noname";
5818
- container.style.width = "100%";
5819
- }
6002
+ // Form does not never use label
6003
+ element.className += " noname";
6004
+ container.style.width = "100%";
5820
6005
 
5821
6006
  return widget;
5822
6007
  }
5823
6008
 
5824
6009
  /**
5825
6010
  * @method addContent
6011
+ * @param {String} name Widget name
5826
6012
  * @param {HTMLElement/String} element
6013
+ * @param {Object} options
5827
6014
  */
5828
6015
 
5829
- addContent( element, options = {} ) {
6016
+ addContent( name, element, options = {} ) {
5830
6017
 
5831
- if( !element )
5832
- {
5833
- return;
5834
- }
6018
+ console.assert( element, "Empty content!" );
5835
6019
 
5836
6020
  if( element.constructor == String )
5837
6021
  {
5838
6022
  const tmp = document.createElement( "div" );
5839
6023
  tmp.innerHTML = element;
6024
+
5840
6025
  if( tmp.childElementCount > 1 )
5841
6026
  {
5842
6027
  element = tmp;
@@ -5847,27 +6032,28 @@ class Panel {
5847
6032
  }
5848
6033
  }
5849
6034
 
5850
- let widget = this.create_widget( null, Widget.CONTENT, options );
6035
+ options.hideName = true;
6036
+
6037
+ let widget = this._createWidget( Widget.CONTENT, name, null, options );
5851
6038
  widget.domEl.appendChild( element );
6039
+
5852
6040
  return widget;
5853
6041
  }
5854
6042
 
5855
6043
  /**
5856
6044
  * @method addImage
6045
+ * @param {String} name Widget name
5857
6046
  * @param {String} url Image Url
5858
6047
  * @param {*} options
6048
+ * hideName: Don't use name as label [false]
5859
6049
  */
5860
6050
 
5861
- async addImage( url, options = {} ) {
6051
+ async addImage( name, url, options = {} ) {
5862
6052
 
5863
- if( !url )
5864
- {
5865
- return;
5866
- }
6053
+ console.assert( url, "Empty src/url for Image!" );
5867
6054
 
5868
- options.hideName = true;
5869
- let widget = this.create_widget( null, Widget.IMAGE, options );
5870
- let element = widget.domEl;
6055
+ let widget = this._createWidget( Widget.IMAGE, name, null, options );
6056
+ const element = widget.domEl;
5871
6057
 
5872
6058
  let container = document.createElement( 'div' );
5873
6059
  container.className = "leximage";
@@ -5882,6 +6068,7 @@ class Panel {
5882
6068
  }
5883
6069
 
5884
6070
  await img.decode();
6071
+
5885
6072
  container.appendChild( img );
5886
6073
  element.appendChild( container );
5887
6074
 
@@ -5895,6 +6082,7 @@ class Panel {
5895
6082
  * @param {String} value Select by default option
5896
6083
  * @param {Function} callback Callback function on change
5897
6084
  * @param {*} options:
6085
+ * hideName: Don't use name as label [false]
5898
6086
  * filter: Add a search bar to the widget [false]
5899
6087
  * disabled: Make the widget disabled [false]
5900
6088
  * skipReset: Don't add the reset value button when value changes
@@ -5904,33 +6092,24 @@ class Panel {
5904
6092
 
5905
6093
  addDropdown( name, values, value, callback, options = {} ) {
5906
6094
 
5907
- let widget = this.create_widget( name, Widget.DROPDOWN, options );
6095
+ let widget = this._createWidget( Widget.DROPDOWN, name, value, options );
5908
6096
 
5909
6097
  widget.onGetValue = () => {
5910
6098
  return element.querySelector( "li.selected" ).getAttribute( 'value' );
5911
6099
  };
5912
6100
 
5913
- widget.onSetValue = ( newValue, skipCallback ) => {
5914
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5915
- if( btn ) btn.style.display = ( newValue != wValue.iValue ? "block" : "none" );
6101
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5916
6102
  value = newValue;
5917
6103
  list.querySelectorAll( 'li' ).forEach( e => { if( e.getAttribute('value') == value ) e.click() } );
5918
- if( !skipCallback ) this._trigger( new IEvent( name, value, null ), callback );
6104
+ if( !skipCallback )
6105
+ {
6106
+ this._trigger( new IEvent( name, value, event ), callback );
6107
+ }
5919
6108
  };
5920
6109
 
5921
- let element = widget.domEl;
6110
+ const element = widget.domEl;
5922
6111
  let that = this;
5923
6112
 
5924
- // Add reset functionality
5925
- if(widget.name && !( options.skipReset ?? false ))
5926
- {
5927
- Panel._add_reset_property( element.domName, function() {
5928
- value = wValue.iValue;
5929
- list.querySelectorAll( 'li' ).forEach( e => { if( e.getAttribute('value') == value ) e.click() } );
5930
- this.style.display = "none";
5931
- });
5932
- }
5933
-
5934
6113
  let container = document.createElement( 'div' );
5935
6114
  container.className = "lexdropdown";
5936
6115
  container.style.width = options.inputWidth || "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
@@ -6139,16 +6318,19 @@ class Panel {
6139
6318
 
6140
6319
  li.addEventListener( "click", e => {
6141
6320
  listDialog.close();
6321
+
6142
6322
  const currentSelected = element.querySelector( ".lexoptions .selected" );
6143
- if(currentSelected) currentSelected.classList.remove( "selected" );
6323
+ if(currentSelected)
6324
+ {
6325
+ currentSelected.classList.remove( "selected" );
6326
+ }
6327
+
6144
6328
  value = e.currentTarget.getAttribute( "value" );
6145
6329
  e.currentTarget.toggleAttribute( "hidden", false );
6146
6330
  e.currentTarget.classList.add( "selected" );
6147
- selectedOption.refresh(value);
6331
+ selectedOption.refresh( value );
6148
6332
 
6149
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6150
- if( btn ) btn.style.display = (value != wValue.iValue ? "block" : "none");
6151
- that._trigger( new IEvent( name, value, null ), callback );
6333
+ widget.set( value, false, e );
6152
6334
 
6153
6335
  // Reset filter
6154
6336
  if( filter )
@@ -6205,7 +6387,8 @@ class Panel {
6205
6387
  element.appendChild( container );
6206
6388
 
6207
6389
  // Remove branch padding and margins
6208
- if( !widget.name )
6390
+ const useNameAsLabel = !( options.hideName ?? false );
6391
+ if( !useNameAsLabel )
6209
6392
  {
6210
6393
  element.className += " noname";
6211
6394
  container.style.width = "100%";
@@ -6232,39 +6415,25 @@ class Panel {
6232
6415
 
6233
6416
  addCurve( name, values, callback, options = {} ) {
6234
6417
 
6235
- if( !name )
6236
- {
6237
- throw( "Set Widget Name!" );
6238
- }
6418
+ let defaultValues = JSON.parse( JSON.stringify( values ) );
6239
6419
 
6240
6420
  let that = this;
6241
- let widget = this.create_widget( name, Widget.CURVE, options );
6421
+ let widget = this._createWidget( Widget.CURVE, name, defaultValues, options );
6242
6422
 
6243
6423
  widget.onGetValue = () => {
6244
6424
  return JSON.parse(JSON.stringify( curveInstance.element.value ));
6245
6425
  };
6246
6426
 
6247
- widget.onSetValue = ( newValue, skipCallback ) => {
6248
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6249
- if( btn ) btn.style.display = ( newValue != curveInstance.element.value ? "block" : "none" );
6427
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6250
6428
  curveInstance.element.value = JSON.parse( JSON.stringify( newValue ) );
6251
6429
  curveInstance.redraw();
6252
- if( !skipCallback ) that._trigger( new IEvent( name, curveInstance.element.value, null ), callback );
6430
+ if( !skipCallback )
6431
+ {
6432
+ that._trigger( new IEvent( name, curveInstance.element.value, event ), callback );
6433
+ }
6253
6434
  };
6254
6435
 
6255
- let element = widget.domEl;
6256
- let defaultValues = JSON.parse( JSON.stringify( values ) );
6257
-
6258
- // Add reset functionality
6259
- if( !(options.skipReset ?? false) )
6260
- {
6261
- Panel._add_reset_property(element.domName, function(e) {
6262
- this.style.display = "none";
6263
- curveInstance.element.value = JSON.parse( JSON.stringify( defaultValues ) );
6264
- curveInstance.redraw();
6265
- that._trigger( new IEvent( name, curveInstance.element.value, e ), callback );
6266
- });
6267
- }
6436
+ const element = widget.domEl;
6268
6437
 
6269
6438
  // Add widget value
6270
6439
 
@@ -6273,9 +6442,7 @@ class Panel {
6273
6442
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6274
6443
 
6275
6444
  options.callback = (v, e) => {
6276
- let btn = element.querySelector(".lexwidgetname .lexicon");
6277
- if(btn) btn.style.display = (v != defaultValues ? "block" : "none");
6278
- that._trigger( new IEvent(name, v, e), callback );
6445
+ that._trigger( new IEvent( name, v, e ), callback );
6279
6446
  };
6280
6447
 
6281
6448
  options.name = name;
@@ -6314,34 +6481,25 @@ class Panel {
6314
6481
 
6315
6482
  addDial( name, values, callback, options = {} ) {
6316
6483
 
6484
+ let defaultValues = JSON.parse( JSON.stringify( values ) );
6485
+
6317
6486
  let that = this;
6318
- let widget = this.create_widget(name, Widget.DIAL, options);
6487
+ let widget = this._createWidget( Widget.DIAL, name, defaultValues, options );
6319
6488
 
6320
6489
  widget.onGetValue = () => {
6321
- return JSON.parse(JSON.stringify(curveInstance.element.value));
6490
+ return JSON.parse( JSON.stringify( curveInstance.element.value ) );
6322
6491
  };
6323
6492
 
6324
- widget.onSetValue = ( newValue, skipCallback ) => {
6325
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6326
- if( btn ) btn.style.display = ( newValue != curveInstance.element.value ? "block" : "none" );
6493
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6327
6494
  curveInstance.element.value = JSON.parse( JSON.stringify( newValue ) );
6328
6495
  curveInstance.redraw();
6329
- if( !skipCallback ) that._trigger( new IEvent( name, curveInstance.element.value, null ), callback );
6496
+ if( !skipCallback )
6497
+ {
6498
+ that._trigger( new IEvent( name, curveInstance.element.value, event ), callback );
6499
+ }
6330
6500
  };
6331
6501
 
6332
- let element = widget.domEl;
6333
- let defaultValues = JSON.parse( JSON.stringify( values ) );
6334
-
6335
- // Add reset functionality
6336
- if( widget.name && !(options.skipReset ?? false) )
6337
- {
6338
- Panel._add_reset_property(element.domName, function(e) {
6339
- this.style.display = "none";
6340
- curveInstance.element.value = JSON.parse( JSON.stringify( defaultValues ) );
6341
- curveInstance.redraw();
6342
- that._trigger( new IEvent( name, curveInstance.element.value, e ), callback );
6343
- });
6344
- }
6502
+ const element = widget.domEl;
6345
6503
 
6346
6504
  // Add widget value
6347
6505
 
@@ -6349,10 +6507,8 @@ class Panel {
6349
6507
  container.className = "lexcurve";
6350
6508
  container.style.width = widget.name ? "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")" : '100%';
6351
6509
 
6352
- options.callback = (v, e) => {
6353
- let btn = element.querySelector(".lexwidgetname .lexicon");
6354
- if(btn) btn.style.display = (v != defaultValues ? "block" : "none");
6355
- that._trigger( new IEvent(name, v, e), callback );
6510
+ options.callback = ( v, e ) => {
6511
+ that._trigger( new IEvent( name, v, e ), callback );
6356
6512
  };
6357
6513
 
6358
6514
  options.name = name;
@@ -6381,42 +6537,32 @@ class Panel {
6381
6537
  * @param {String} name Widget name
6382
6538
  * @param {Number} value Flag value by default option
6383
6539
  * @param {Function} callback Callback function on change
6384
- * @param {*} options:
6540
+ * @param {Object} options:
6385
6541
  */
6386
6542
 
6387
6543
  addLayers( name, value, callback, options = {} ) {
6388
6544
 
6389
- if( !name )
6390
- {
6391
- throw("Set Widget Name!");
6392
- }
6393
-
6394
6545
  let that = this;
6395
- let widget = this.create_widget(name, Widget.LAYERS, options);
6546
+ let widget = this._createWidget( Widget.LAYERS, name, value, options );
6547
+
6396
6548
  widget.onGetValue = () => {
6397
6549
  return element.value;
6398
6550
  };
6399
- widget.onSetValue = ( newValue, skipCallback ) => {
6400
- let btn = element.querySelector(".lexwidgetname .lexicon");
6401
- if(btn) btn.style.display = (newValue != defaultValue ? "block" : "none");
6551
+
6552
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6402
6553
  value = element.value = newValue;
6403
6554
  setLayers();
6404
- if( !skipCallback ) that._trigger( new IEvent(name, value), callback );
6555
+ if( !skipCallback )
6556
+ {
6557
+ that._trigger( new IEvent(name, value, event), callback );
6558
+ }
6405
6559
  };
6406
6560
 
6407
- let element = widget.domEl;
6408
-
6409
- // Add reset functionality
6410
- Panel._add_reset_property(element.domName, function(e) {
6411
- this.style.display = "none";
6412
- value = element.value = defaultValue;
6413
- setLayers();
6414
- that._trigger( new IEvent(name, value, e), callback );
6415
- });
6561
+ const element = widget.domEl;
6416
6562
 
6417
6563
  // Add widget value
6418
6564
 
6419
- var container = document.createElement('div');
6565
+ var container = document.createElement( "div" );
6420
6566
  container.className = "lexlayers";
6421
6567
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6422
6568
 
@@ -6428,6 +6574,7 @@ class Panel {
6428
6574
 
6429
6575
  let binary = value.toString( 2 );
6430
6576
  let nbits = binary.length;
6577
+
6431
6578
  // fill zeros
6432
6579
  for( var i = 0; i < (16 - nbits); ++i )
6433
6580
  {
@@ -6441,33 +6588,28 @@ class Panel {
6441
6588
  if( value != undefined )
6442
6589
  {
6443
6590
  const valueBit = binary[ 16 - bit - 1 ];
6444
- if(valueBit != undefined && valueBit == '1')
6591
+ if( valueBit != undefined && valueBit == '1' )
6592
+ {
6445
6593
  layer.classList.add('selected');
6594
+ }
6446
6595
  }
6447
6596
  layer.innerText = bit + 1;
6448
6597
  layer.title = "Bit " + bit + ", value " + (1 << bit);
6449
6598
  container.appendChild( layer );
6450
6599
 
6451
6600
  layer.addEventListener("click", e => {
6452
-
6453
6601
  e.stopPropagation();
6454
6602
  e.stopImmediatePropagation();
6455
6603
  e.target.classList.toggle('selected');
6456
6604
  value ^= ( 1 << bit );
6457
- element.value = value;
6458
-
6459
- let btn = element.querySelector(".lexwidgetname .lexicon");
6460
- if(btn) btn.style.display = (value != defaultValue ? "block" : "none");
6461
-
6462
- this._trigger( new IEvent(name, value, e), callback );
6605
+ widget.set( value, false, e );
6463
6606
  });
6464
6607
  }
6465
-
6466
6608
  };
6467
6609
 
6468
6610
  setLayers();
6469
6611
 
6470
- element.appendChild(container);
6612
+ element.appendChild( container );
6471
6613
 
6472
6614
  return widget;
6473
6615
  }
@@ -6483,12 +6625,8 @@ class Panel {
6483
6625
 
6484
6626
  addArray( name, values = [], callback, options = {} ) {
6485
6627
 
6486
- if( !name )
6487
- {
6488
- throw( "Set Widget Name!" );
6489
- }
6628
+ let widget = this._createWidget( Widget.ARRAY, name, null, options );
6490
6629
 
6491
- let widget = this.create_widget(name, Widget.ARRAY, options);
6492
6630
  widget.onGetValue = () => {
6493
6631
  let array_inputs = element.querySelectorAll("input");
6494
6632
  let values = [];
@@ -6496,13 +6634,17 @@ class Panel {
6496
6634
  values.push( v.value );
6497
6635
  return values;
6498
6636
  };
6499
- widget.onSetValue = ( newValue, skipCallback ) => {
6637
+
6638
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6500
6639
  values = newValue;
6501
6640
  updateItems();
6502
- if( !skipCallback ) this._trigger( new IEvent(name, values, null), callback );
6641
+ if( !skipCallback )
6642
+ {
6643
+ this._trigger( new IEvent( name, values, event ), callback );
6644
+ }
6503
6645
  };
6504
6646
 
6505
- let element = widget.domEl;
6647
+ const element = widget.domEl;
6506
6648
  element.style.flexWrap = "wrap";
6507
6649
 
6508
6650
  // Add dropdown array button
@@ -6515,10 +6657,10 @@ class Panel {
6515
6657
 
6516
6658
  this.queue( container );
6517
6659
 
6518
- const angle_down = `<a class='fa-solid fa-angle-down' style='float:right; margin-right: 3px;'></a>`;
6660
+ const angleDown = `<a class='fa-solid fa-angle-down' style='float:right; margin-right: 3px;'></a>`;
6519
6661
 
6520
6662
  let buttonName = "Array (size " + values.length + ")";
6521
- buttonName += angle_down;
6663
+ buttonName += angleDown;
6522
6664
  this.addButton(null, buttonName, () => {
6523
6665
  element.querySelector(".lexarrayitems").toggleAttribute('hidden');
6524
6666
  }, { buttonClass: 'array' });
@@ -6527,24 +6669,24 @@ class Panel {
6527
6669
 
6528
6670
  // Show elements
6529
6671
 
6530
- let array_items = document.createElement('div');
6531
- array_items.className = "lexarrayitems";
6532
- array_items.toggleAttribute('hidden', true);
6672
+ let arrayItems = document.createElement( "div" );
6673
+ arrayItems.className = "lexarrayitems";
6674
+ arrayItems.toggleAttribute( "hidden", true );
6533
6675
 
6534
- element.appendChild(container);
6535
- element.appendChild(array_items);
6676
+ element.appendChild( container );
6677
+ element.appendChild( arrayItems );
6536
6678
 
6537
6679
  const updateItems = () => {
6538
6680
 
6539
6681
  // Update num items
6540
6682
  let buttonEl = element.querySelector(".lexbutton.array span");
6541
6683
  buttonEl.innerHTML = "Array (size " + values.length + ")";
6542
- buttonEl.innerHTML += angle_down;
6684
+ buttonEl.innerHTML += angleDown;
6543
6685
 
6544
6686
  // Update inputs
6545
- array_items.innerHTML = "";
6687
+ arrayItems.innerHTML = "";
6546
6688
 
6547
- this.queue( array_items );
6689
+ this.queue( arrayItems );
6548
6690
 
6549
6691
  for( let i = 0; i < values.length; ++i )
6550
6692
  {
@@ -6590,7 +6732,7 @@ class Panel {
6590
6732
  this._trigger( new IEvent(name, values, event), callback );
6591
6733
  }, { buttonClass: 'array' });
6592
6734
 
6593
- // Stop pushing to array_items
6735
+ // Stop pushing to arrayItems
6594
6736
  this.clearQueue();
6595
6737
  };
6596
6738
 
@@ -6606,22 +6748,30 @@ class Panel {
6606
6748
  * @param {String} value Selected list value
6607
6749
  * @param {Function} callback Callback function on change
6608
6750
  * @param {*} options:
6751
+ * hideName: Don't use name as label [false]
6609
6752
  */
6610
6753
 
6611
6754
  addList( name, values, value, callback, options = {} ) {
6612
6755
 
6613
- let widget = this.create_widget( name, Widget.LIST, options );
6756
+ let widget = this._createWidget( Widget.LIST, name, value, options );
6614
6757
 
6615
6758
  widget.onGetValue = () => {
6616
6759
  return value;
6617
6760
  };
6618
- widget.onSetValue = ( newValue, skipCallback ) => {
6761
+
6762
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6619
6763
  listContainer.querySelectorAll( '.lexlistitem' ).forEach( e => e.classList.remove( 'selected' ) );
6620
6764
  const idx = values.indexOf( newValue );
6621
- if( idx == -1 ) return;
6765
+ if( idx == -1 )
6766
+ {
6767
+ return;
6768
+ }
6622
6769
  listContainer.children[ idx ].classList.toggle( 'selected' );
6623
6770
  value = newValue;
6624
- if( !skipCallback ) this._trigger( new IEvent( name, newValue ), callback );
6771
+ if( !skipCallback )
6772
+ {
6773
+ this._trigger( new IEvent( name, newValue, event ), callback );
6774
+ }
6625
6775
  };
6626
6776
 
6627
6777
  widget.updateValues = ( newValues ) => {
@@ -6655,7 +6805,7 @@ class Panel {
6655
6805
  }
6656
6806
  };
6657
6807
 
6658
- let element = widget.domEl;
6808
+ const element = widget.domEl;
6659
6809
 
6660
6810
  // Show list
6661
6811
 
@@ -6666,7 +6816,8 @@ class Panel {
6666
6816
  widget.updateValues( values );
6667
6817
 
6668
6818
  // Remove branch padding and margins
6669
- if( !widget.name )
6819
+ const useNameAsLabel = !( options.hideName ?? false );
6820
+ if( !useNameAsLabel )
6670
6821
  {
6671
6822
  element.className += " noname";
6672
6823
  listContainer.style.width = "100%";
@@ -6683,46 +6834,39 @@ class Panel {
6683
6834
  * @param {String} value Comma separated tags
6684
6835
  * @param {Function} callback Callback function on change
6685
6836
  * @param {*} options:
6837
+ * hideName: Don't use name as label [false]
6686
6838
  */
6687
6839
 
6688
6840
  addTags( name, value, callback, options = {} ) {
6689
6841
 
6690
6842
  value = value.replace( /\s/g, '' ).split( ',' );
6843
+
6691
6844
  let defaultValue = [].concat( value );
6692
- let widget = this.create_widget( name, Widget.TAGS, options );
6845
+ let widget = this._createWidget( Widget.TAGS, name, defaultValue, options );
6693
6846
 
6694
6847
  widget.onGetValue = () => {
6695
6848
  return [].concat( value );
6696
6849
  };
6697
- widget.onSetValue = ( newValue, skipCallback ) => {
6850
+
6851
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6698
6852
  value = [].concat( newValue );
6699
- create_tags();
6700
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6701
- if( btn ) btn.style.display = ( newValue != defaultValue ? "block" : "none" );
6702
- if( !skipCallback ) that._trigger( new IEvent( name, value ), callback );
6853
+ _generateTags();
6854
+ if( !skipCallback )
6855
+ {
6856
+ that._trigger( new IEvent( name, value, event ), callback );
6857
+ }
6703
6858
  };
6704
6859
 
6705
- let element = widget.domEl;
6860
+ const element = widget.domEl;
6706
6861
  let that = this;
6707
6862
 
6708
- // Add reset functionality
6709
- if(widget.name)
6710
- {
6711
- Panel._add_reset_property(element.domName, function(e) {
6712
- this.style.display = "none";
6713
- value = [].concat(defaultValue);
6714
- create_tags();
6715
- that._trigger( new IEvent(name, value, e), callback );
6716
- });
6717
- }
6718
-
6719
6863
  // Show tags
6720
6864
 
6721
6865
  const tagsContainer = document.createElement('div');
6722
6866
  tagsContainer.className = "lextags";
6723
6867
  tagsContainer.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6724
6868
 
6725
- const create_tags = () => {
6869
+ const _generateTags = () => {
6726
6870
 
6727
6871
  tagsContainer.innerHTML = "";
6728
6872
 
@@ -6740,9 +6884,7 @@ class Panel {
6740
6884
  removeButton.addEventListener( 'click', e => {
6741
6885
  tag.remove();
6742
6886
  value.splice( value.indexOf( tagName ), 1 );
6743
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6744
- if( btn ) btn.style.display = ( value != defaultValue ? "block" : "none" );
6745
- that._trigger( new IEvent( name, value, e ), callback );
6887
+ widget.set( value, false, e );
6746
6888
  } );
6747
6889
 
6748
6890
  tagsContainer.appendChild( tag );
@@ -6761,20 +6903,18 @@ class Panel {
6761
6903
  if( !val.length || value.indexOf( val ) > -1 )
6762
6904
  return;
6763
6905
  value.push( val );
6764
- create_tags();
6765
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6766
- if(btn) btn.style.display = "block";
6767
- that._trigger( new IEvent( name, value, e ), callback );
6906
+ widget.set( value, false, e );
6768
6907
  }
6769
6908
  };
6770
6909
 
6771
6910
  tagInput.focus();
6772
6911
  }
6773
6912
 
6774
- create_tags();
6913
+ _generateTags();
6775
6914
 
6776
6915
  // Remove branch padding and margins
6777
- if( !widget.name )
6916
+ const useNameAsLabel = !( options.hideName ?? false );
6917
+ if( !useNameAsLabel )
6778
6918
  {
6779
6919
  element.className += " noname";
6780
6920
  tagsContainer.style.width = "100%";
@@ -6804,44 +6944,44 @@ class Panel {
6804
6944
  throw( "Set Widget Name or at least a label!" );
6805
6945
  }
6806
6946
 
6807
- let widget = this.create_widget( name, Widget.CHECKBOX, options );
6947
+ let widget = this._createWidget( Widget.CHECKBOX, name, value, options );
6808
6948
 
6809
6949
  widget.onGetValue = () => {
6810
- return checkbox.checked;
6950
+ return value;
6811
6951
  };
6812
6952
 
6813
- widget.onSetValue = ( newValue, skipCallback ) => {
6814
- if( checkbox.checked !== newValue )
6953
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6954
+
6955
+ if( newValue == value )
6956
+ {
6957
+ return;
6958
+ }
6959
+
6960
+ checkbox.checked = value = newValue;
6961
+
6962
+ // Update suboptions menu
6963
+ element.querySelector( ".lexcheckboxsubmenu" )?.toggleAttribute( 'hidden', !newValue );
6964
+
6965
+ if( !skipCallback )
6815
6966
  {
6816
- checkbox.checked = newValue;
6817
- Panel._dispatch_event( checkbox, "change", skipCallback );
6967
+ this._trigger( new IEvent( name, newValue, event ), callback );
6818
6968
  }
6819
6969
  };
6820
6970
 
6821
- let element = widget.domEl;
6822
-
6823
- // Add reset functionality
6824
- if( name )
6825
- {
6826
- Panel._add_reset_property( element.domName, function() {
6827
- checkbox.checked = !checkbox.checked;
6828
- Panel._dispatch_event( checkbox, "change" );
6829
- });
6830
- }
6971
+ const element = widget.domEl;
6831
6972
 
6832
6973
  // Add widget value
6833
6974
 
6834
- var container = document.createElement('div');
6975
+ var container = document.createElement( "div" );
6835
6976
  container.className = "lexcheckboxcont";
6836
6977
 
6837
- let checkbox = document.createElement('input');
6978
+ let checkbox = document.createElement( "input" );
6838
6979
  checkbox.type = "checkbox";
6839
6980
  checkbox.className = "lexcheckbox " + ( options.className ?? "" );
6840
6981
  checkbox.checked = value;
6841
- checkbox.iValue = value;
6842
6982
  checkbox.disabled = options.disabled ?? false;
6843
6983
 
6844
- let valueName = document.createElement( 'span' );
6984
+ let valueName = document.createElement( "span" );
6845
6985
  valueName.className = "checkboxtext";
6846
6986
  valueName.innerHTML = options.label ?? "On";
6847
6987
 
@@ -6849,21 +6989,7 @@ class Panel {
6849
6989
  container.appendChild( valueName );
6850
6990
 
6851
6991
  checkbox.addEventListener( "change" , e => {
6852
-
6853
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
6854
-
6855
- // Reset button (default value)
6856
- if( !skipCallback )
6857
- {
6858
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6859
- if( btn ) btn.style.display = checkbox.checked != checkbox.iValue ? "block": "none";
6860
- }
6861
-
6862
- // Open suboptions
6863
- let submenu = element.querySelector( ".lexcheckboxsubmenu" );
6864
- if( submenu ) submenu.toggleAttribute( 'hidden', !checkbox.checked );
6865
-
6866
- if( !skipCallback ) this._trigger( new IEvent( name, checkbox.checked, e ), callback );
6992
+ widget.set( checkbox.checked, false, e );
6867
6993
  });
6868
6994
 
6869
6995
  element.appendChild( container );
@@ -6871,9 +6997,9 @@ class Panel {
6871
6997
  if( options.suboptions )
6872
6998
  {
6873
6999
  element.style.flexWrap = "wrap";
6874
- let suboptions = document.createElement('div');
7000
+ let suboptions = document.createElement( "div" );
6875
7001
  suboptions.className = "lexcheckboxsubmenu";
6876
- suboptions.toggleAttribute( 'hidden', !checkbox.checked );
7002
+ suboptions.toggleAttribute( "hidden", !checkbox.checked );
6877
7003
 
6878
7004
  this.queue( suboptions );
6879
7005
  options.suboptions.call(this, this);
@@ -6898,32 +7024,31 @@ class Panel {
6898
7024
 
6899
7025
  addToggle( name, value, callback, options = {} ) {
6900
7026
 
6901
- if( !name )
6902
- {
6903
- throw( "Set Widget Name!" );
6904
- }
6905
-
6906
- let widget = this.create_widget( name, Widget.TOGGLE, options );
7027
+ let widget = this._createWidget( Widget.TOGGLE, name, value, options );
6907
7028
 
6908
7029
  widget.onGetValue = () => {
6909
7030
  return toggle.checked;
6910
7031
  };
6911
7032
 
6912
- widget.onSetValue = ( newValue, skipCallback ) => {
6913
- if( toggle.checked !== newValue )
7033
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7034
+
7035
+ if( newValue == value )
6914
7036
  {
6915
- toggle.checked = newValue;
6916
- Panel._dispatch_event( toggle, "change", skipCallback );
7037
+ return;
6917
7038
  }
6918
- };
6919
7039
 
6920
- let element = widget.domEl;
7040
+ toggle.checked = value = newValue;
6921
7041
 
6922
- // Add reset functionality
6923
- Panel._add_reset_property( element.domName, function() {
6924
- toggle.checked = !toggle.checked;
6925
- Panel._dispatch_event( toggle, "change" );
6926
- });
7042
+ // Update suboptions menu
7043
+ element.querySelector( ".lextogglesubmenu" )?.toggleAttribute( 'hidden', !newValue );
7044
+
7045
+ if( !skipCallback )
7046
+ {
7047
+ this._trigger( new IEvent( name, newValue, event ), callback );
7048
+ }
7049
+ };
7050
+
7051
+ const element = widget.domEl;
6927
7052
 
6928
7053
  // Add widget value
6929
7054
 
@@ -6945,21 +7070,7 @@ class Panel {
6945
7070
  container.appendChild( valueName );
6946
7071
 
6947
7072
  toggle.addEventListener( "change" , e => {
6948
-
6949
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
6950
-
6951
- // Reset button (default value)
6952
- if( !skipCallback )
6953
- {
6954
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6955
- if( btn ) btn.style.display = toggle.checked != toggle.iValue ? "block": "none";
6956
- }
6957
-
6958
- // Open suboptions
6959
- let submenu = element.querySelector( ".lextogglesubmenu" );
6960
- if( submenu ) submenu.toggleAttribute( 'hidden', !toggle.checked );
6961
-
6962
- if( !skipCallback ) this._trigger( new IEvent( name, toggle.checked, e ), callback );
7073
+ widget.set( toggle.checked, false, e );
6963
7074
  });
6964
7075
 
6965
7076
  element.appendChild( container );
@@ -6983,43 +7094,43 @@ class Panel {
6983
7094
 
6984
7095
  /**
6985
7096
  * @method addRadioGroup
7097
+ * @param {String} name Widget name
6986
7098
  * @param {String} label Radio label
6987
7099
  * @param {Array} values Radio options
6988
7100
  * @param {Function} callback Callback function on change
6989
7101
  * @param {*} options:
6990
7102
  * disabled: Make the widget disabled [false]
6991
7103
  * className: Customize colors
7104
+ * selected: Index of the default selected option
6992
7105
  */
6993
7106
 
6994
- addRadioGroup( label, values, callback, options = {} ) {
7107
+ addRadioGroup( name, label, values, callback, options = {} ) {
6995
7108
 
6996
- let widget = this.create_widget( null, Widget.RADIO, options );
7109
+ let widget = this._createWidget( Widget.RADIO, name, null, options );
7110
+ let currentIndex = null;
6997
7111
 
6998
7112
  widget.onGetValue = () => {
6999
7113
  const items = container.querySelectorAll( 'button' );
7000
- for( let i = 0; i < items.length; ++i )
7001
- {
7002
- const optionItem = items[ i ];
7003
- if( optionItem.checked )
7004
- {
7005
- return [ i, values[ i ] ];
7006
- }
7007
- }
7114
+ return currentIndex ? [ currentIndex, items[ currentIndex ] ] : undefined;
7008
7115
  };
7009
7116
 
7010
- widget.onSetValue = ( newValue, skipCallback ) => {
7117
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7118
+ console.assert( newValue.constructor == Number, "RadioGroup _value_ must be an Array index!" );
7119
+
7011
7120
  const items = container.querySelectorAll( 'button' );
7012
- for( let i = 0; i < items.length; ++i )
7121
+ items.forEach( b => { b.checked = false; b.classList.remove( "checked" ) } );
7122
+
7123
+ const optionItem = items[ newValue ];
7124
+ optionItem.checked = !optionItem.checked;
7125
+ optionItem.classList.toggle( "checked" );
7126
+
7127
+ if( !skipCallback )
7013
7128
  {
7014
- const optionItem = items[ i ];
7015
- if( newValue == i )
7016
- {
7017
- Panel._dispatch_event( optionItem, "click", skipCallback );
7018
- }
7129
+ that._trigger( new IEvent( null, [ newValue, values[ newValue ] ], event ), callback );
7019
7130
  }
7020
7131
  };
7021
7132
 
7022
- let element = widget.domEl;
7133
+ const element = widget.domEl;
7023
7134
 
7024
7135
  // Add widget value
7025
7136
  var container = document.createElement( 'div' );
@@ -7042,18 +7153,12 @@ class Panel {
7042
7153
  optionButton.disabled = options.disabled ?? false;
7043
7154
  optionItem.appendChild( optionButton );
7044
7155
 
7045
- optionButton.addEventListener( "click", function( e ) {
7046
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
7047
- container.querySelectorAll( 'button' ).forEach( e => { e.checked = false; e.classList.remove( "checked" ) } );
7048
- this.checked = !this.checked;
7049
- this.classList.toggle( "checked" );
7050
- if( !skipCallback ) that._trigger( new IEvent( null, [ i, values[ i ] ], e ), callback );
7156
+ optionButton.addEventListener( "click", ( e ) => {
7157
+ widget.set( i, false, e );
7051
7158
  } );
7052
7159
 
7053
- {
7054
- const checkedSpan = document.createElement( 'span' );
7055
- optionButton.appendChild( checkedSpan );
7056
- }
7160
+ const checkedSpan = document.createElement( 'span' );
7161
+ optionButton.appendChild( checkedSpan );
7057
7162
 
7058
7163
  const optionLabel = document.createElement( 'span' );
7059
7164
  optionLabel.innerHTML = values[ i ];
@@ -7062,8 +7167,9 @@ class Panel {
7062
7167
 
7063
7168
  if( options.selected )
7064
7169
  {
7065
- console.assert( options.selected.constructor == Number );
7066
- widget.set( options.selected, true );
7170
+ console.assert( options.selected.constructor == Number, "RadioGroup _selected_ must be an Array index!" );
7171
+ currentIndex = options.selected;
7172
+ widget.set( currentIndex, true );
7067
7173
  }
7068
7174
 
7069
7175
  element.appendChild( container );
@@ -7083,30 +7189,36 @@ class Panel {
7083
7189
 
7084
7190
  addColor( name, value, callback, options = {} ) {
7085
7191
 
7086
- if( !name )
7087
- {
7088
- throw( "Set Widget Name!" );
7089
- }
7192
+ value = ( value.constructor === Array ) ? rgbToHex( value ) : value;
7090
7193
 
7091
- let widget = this.create_widget( name, Widget.COLOR, options );
7194
+ let widget = this._createWidget( Widget.COLOR, name, value, options );
7092
7195
 
7093
7196
  widget.onGetValue = () => {
7094
- return color.value;
7095
- };
7096
- widget.onSetValue = ( newValue, skipCallback ) => {
7097
- color.value = newValue;
7098
- Panel._dispatch_event( color, "input", skipCallback );
7197
+ return value;
7099
7198
  };
7100
7199
 
7101
- let element = widget.domEl;
7102
- let change_from_input = false;
7200
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7103
7201
 
7104
- // Add reset functionality
7105
- Panel._add_reset_property( element.domName, function() {
7106
- this.style.display = "none";
7107
- color.value = color.iValue;
7108
- Panel._dispatch_event( color, "input" );
7109
- });
7202
+ if( color.useRGB )
7203
+ {
7204
+ newValue = hexToRgb( newValue );
7205
+ }
7206
+
7207
+ // Means it was called from the color input listener, not the text
7208
+ if( event )
7209
+ {
7210
+ textWidget.set( newValue, true, event );
7211
+ }
7212
+
7213
+ color.value = value = newValue;
7214
+
7215
+ if( !skipCallback )
7216
+ {
7217
+ this._trigger( new IEvent( name, newValue, event ), callback );
7218
+ }
7219
+ };
7220
+
7221
+ const element = widget.domEl;
7110
7222
 
7111
7223
  // Add widget value
7112
7224
 
@@ -7120,7 +7232,7 @@ class Panel {
7120
7232
  color.className = "colorinput";
7121
7233
  color.id = "color" + simple_guidGenerator();
7122
7234
  color.useRGB = options.useRGB ?? false;
7123
- color.value = color.iValue = value.constructor === Array ? rgbToHex( value ) : value;
7235
+ color.value = color.iValue = value;
7124
7236
 
7125
7237
  if( options.disabled )
7126
7238
  {
@@ -7128,38 +7240,18 @@ class Panel {
7128
7240
  }
7129
7241
 
7130
7242
  color.addEventListener( "input", e => {
7131
- let val = e.target.value;
7132
-
7133
- const skipCallback = e.detail;
7134
-
7135
- // Change value (always hex)
7136
- if( !change_from_input )
7137
- text_widget.set( val );
7138
-
7139
- // Reset button (default value)
7140
- if( !skipCallback )
7141
- {
7142
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7143
- if( btn ) btn.style.display = val != color.iValue ? "block": "none";
7144
- }
7145
-
7146
- if( color.useRGB )
7147
- val = hexToRgb( val );
7148
-
7149
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7243
+ widget.set( e.target.value, false, e );
7150
7244
  }, false );
7151
7245
 
7152
7246
  container.appendChild( color );
7153
7247
 
7154
7248
  this.queue( container );
7155
7249
 
7156
- const text_widget = this.addText( null, color.value, v => {
7157
- change_from_input = true;
7250
+ const textWidget = this.addText( null, color.value, v => {
7158
7251
  widget.set( v );
7159
- change_from_input = false;
7160
7252
  }, { width: "calc( 100% - 32px )"});
7161
7253
 
7162
- text_widget.domEl.style.marginLeft = "4px";
7254
+ textWidget.domEl.style.marginLeft = "4px";
7163
7255
 
7164
7256
  this.clearQueue();
7165
7257
 
@@ -7174,6 +7266,7 @@ class Panel {
7174
7266
  * @param {Number} value Default number value
7175
7267
  * @param {Function} callback Callback function on change
7176
7268
  * @param {*} options:
7269
+ * hideName: Don't use name as label [false]
7177
7270
  * className: Extra classes to customize style
7178
7271
  * disabled: Make the widget disabled [false]
7179
7272
  * left: The slider goes to the left instead of the right
@@ -7184,28 +7277,28 @@ class Panel {
7184
7277
 
7185
7278
  addRange( name, value, callback, options = {} ) {
7186
7279
 
7187
- let widget = this.create_widget( name, Widget.RANGE, options );
7280
+ let widget = this._createWidget( Widget.RANGE, name, value, options );
7188
7281
 
7189
7282
  widget.onGetValue = () => {
7190
- return +slider.value;
7283
+ return value;
7191
7284
  };
7192
7285
 
7193
- widget.onSetValue = ( newValue, skipCallback ) => {
7194
- slider.value = newValue;
7195
- Panel._dispatch_event( slider, "input", skipCallback );
7196
- };
7286
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7287
+
7288
+ if( isNaN( newValue ) )
7289
+ {
7290
+ return;
7291
+ }
7197
7292
 
7198
- let element = widget.domEl;
7293
+ slider.value = value = clamp( +newValue, +slider.min, +slider.max );
7199
7294
 
7200
- // add reset functionality
7201
- if( widget.name )
7202
- {
7203
- Panel._add_reset_property( element.domName, function() {
7204
- this.style.display = "none";
7205
- slider.value = slider.iValue;
7206
- Panel._dispatch_event( slider, "input" );
7207
- });
7208
- }
7295
+ if( !skipCallback )
7296
+ {
7297
+ this._trigger( new IEvent( name, options.left ? ( ( +slider.max ) - value + ( +slider.min ) ) : value, event ), callback );
7298
+ }
7299
+ };
7300
+
7301
+ const element = widget.domEl;
7209
7302
 
7210
7303
  // add widget value
7211
7304
 
@@ -7233,30 +7326,7 @@ class Panel {
7233
7326
  }
7234
7327
 
7235
7328
  slider.addEventListener( "input", e => {
7236
-
7237
- if( isNaN( e.target.valueAsNumber ) )
7238
- {
7239
- return;
7240
- }
7241
-
7242
- const skipCallback = e.detail;
7243
-
7244
- let val = e.target.value = clamp( +e.target.valueAsNumber, +slider.min, +slider.max );
7245
- slider.value = val;
7246
-
7247
- // Reset button (default value)
7248
- if( !skipCallback )
7249
- {
7250
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7251
- if( btn ) btn.style.display = val != slider.iValue ? "block": "none";
7252
- }
7253
-
7254
- if( options.left )
7255
- {
7256
- val = ( +slider.max ) - val + ( +slider.min );
7257
- }
7258
-
7259
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7329
+ widget.set( e.target.valueAsNumber, false, e );
7260
7330
  }, { passive: false });
7261
7331
 
7262
7332
  slider.addEventListener( "mousedown", function( e ) {
@@ -7289,8 +7359,8 @@ class Panel {
7289
7359
  container.appendChild( slider );
7290
7360
  element.appendChild( container );
7291
7361
 
7292
- // Remove branch padding and margins
7293
- if( !widget.name )
7362
+ const useNameAsLabel = !( options.hideName ?? false );
7363
+ if( !useNameAsLabel )
7294
7364
  {
7295
7365
  element.className += " noname";
7296
7366
  container.style.width = "100%";
@@ -7305,6 +7375,7 @@ class Panel {
7305
7375
  * @param {Number} value Default number value
7306
7376
  * @param {Function} callback Callback function on change
7307
7377
  * @param {*} options:
7378
+ * hideName: Don't use name as label [false]
7308
7379
  * disabled: Make the widget disabled [false]
7309
7380
  * step: Step of the input
7310
7381
  * precision: The number of digits to appear after the decimal point
@@ -7317,28 +7388,40 @@ class Panel {
7317
7388
 
7318
7389
  addNumber( name, value, callback, options = {} ) {
7319
7390
 
7320
- let widget = this.create_widget( name, Widget.NUMBER, options );
7391
+ let widget = this._createWidget( Widget.NUMBER, name, value, options );
7321
7392
 
7322
7393
  widget.onGetValue = () => {
7323
- return +vecinput.value;
7394
+ return value;
7324
7395
  };
7325
7396
 
7326
- widget.onSetValue = ( newValue, skipCallback ) => {
7327
- vecinput.value = round( newValue, options.precision );
7328
- Panel._dispatch_event( vecinput, "change", skipCallback );
7329
- };
7397
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7330
7398
 
7331
- let element = widget.domEl;
7399
+ if( isNaN( newValue ) )
7400
+ {
7401
+ return;
7402
+ }
7332
7403
 
7333
- // add reset functionality
7334
- if( widget.name )
7335
- {
7336
- Panel._add_reset_property( element.domName, function() {
7337
- this.style.display = "none";
7338
- vecinput.value = vecinput.iValue;
7339
- Panel._dispatch_event( vecinput, "change" );
7340
- });
7341
- }
7404
+ value = clamp( +newValue, +vecinput.min, +vecinput.max );
7405
+ vecinput.value = value = round( value, options.precision );
7406
+
7407
+ // Update slider!
7408
+ if( box.querySelector( ".lexinputslider" ) )
7409
+ {
7410
+ box.querySelector( ".lexinputslider" ).value = value;
7411
+ }
7412
+
7413
+ if( options.units )
7414
+ {
7415
+ vecinput.unitSpan.style.left = measureRealWidth( value ) + "px";
7416
+ }
7417
+
7418
+ if( !skipCallback )
7419
+ {
7420
+ this._trigger( new IEvent( name, value, event ), callback );
7421
+ }
7422
+ };
7423
+
7424
+ const element = widget.domEl;
7342
7425
 
7343
7426
  // add widget value
7344
7427
 
@@ -7397,9 +7480,7 @@ class Panel {
7397
7480
  slider.value = value;
7398
7481
 
7399
7482
  slider.addEventListener( "input", function( e ) {
7400
- let new_value = +this.valueAsNumber;
7401
- vecinput.value = round( new_value, options.precision );
7402
- Panel._dispatch_event( vecinput, "change" );
7483
+ widget.set( this.valueAsNumber, false, e );
7403
7484
  }, false );
7404
7485
 
7405
7486
  slider.addEventListener( "mousedown", function( e ) {
@@ -7424,13 +7505,13 @@ class Panel {
7424
7505
  vecinput.max = slider.max = newMax ?? vecinput.max;
7425
7506
  vecinput.step = newStep ?? vecinput.step;
7426
7507
  slider.step = newStep ?? slider.step;
7427
- Panel._dispatch_event( vecinput, "change", true );
7508
+ widget.set( value, true );
7428
7509
  };
7429
7510
  }
7430
7511
 
7431
7512
  vecinput.addEventListener( "input", function( e ) {
7432
- let new_value = +this.valueAsNumber;
7433
- vecinput.value = round( new_value, options.precision );
7513
+ value = +this.valueAsNumber;
7514
+ value = round( value, options.precision );
7434
7515
  if( options.units )
7435
7516
  {
7436
7517
  vecinput.unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
@@ -7446,44 +7527,12 @@ class Panel {
7446
7527
  let mult = options.step ?? 1;
7447
7528
  if( e.shiftKey ) mult *= 10;
7448
7529
  else if( e.altKey ) mult *= 0.1;
7449
- let new_value = ( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) );
7450
- this.value = round( new_value, options.precision );
7451
- Panel._dispatch_event(vecinput, "change");
7530
+ value = ( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) );
7531
+ widget.set( value, false, e );
7452
7532
  }, { passive: false });
7453
7533
 
7454
- vecinput.addEventListener( "change", e => {
7455
-
7456
- if( isNaN( e.target.valueAsNumber ) )
7457
- {
7458
- return;
7459
- }
7460
-
7461
- const skipCallback = e.detail;
7462
-
7463
- let val = e.target.value = clamp( +e.target.valueAsNumber, +vecinput.min, +vecinput.max );
7464
- val = options.precision ? round( val, options.precision ) : val;
7465
-
7466
- // Update slider!
7467
- if( box.querySelector( ".lexinputslider" ) )
7468
- {
7469
- box.querySelector( ".lexinputslider" ).value = val;
7470
- }
7471
-
7472
- vecinput.value = val;
7473
-
7474
- if( options.units )
7475
- {
7476
- vecinput.unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
7477
- }
7478
-
7479
- // Reset button (default value)
7480
- if( !skipCallback )
7481
- {
7482
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7483
- if( btn ) btn.style.display = val != vecinput.iValue ? "block": "none";
7484
- }
7485
-
7486
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7534
+ vecinput.addEventListener( "change", function( e ) {
7535
+ widget.set( this.valueAsNumber, false, e );
7487
7536
  }, { passive: false });
7488
7537
 
7489
7538
  // Add drag input
@@ -7527,9 +7576,9 @@ class Panel {
7527
7576
  let mult = options.step ?? 1;
7528
7577
  if( e.shiftKey ) mult *= 10;
7529
7578
  else if( e.altKey ) mult *= 0.1;
7530
- let new_value = ( +vecinput.valueAsNumber + mult * dt );
7531
- vecinput.value = ( +new_value ).toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
7532
- Panel._dispatch_event( vecinput, "change" );
7579
+ value = ( +vecinput.valueAsNumber + mult * dt );
7580
+ widget.set( value, false, e );
7581
+ // vecinput.value = ( +new_value ).toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
7533
7582
  }
7534
7583
 
7535
7584
  e.stopPropagation();
@@ -7559,7 +7608,8 @@ class Panel {
7559
7608
  element.appendChild( container );
7560
7609
 
7561
7610
  // Remove branch padding and margins
7562
- if( !widget.name )
7611
+ const useNameAsLabel = !( options.hideName ?? false );
7612
+ if( !useNameAsLabel )
7563
7613
  {
7564
7614
  element.className += " noname";
7565
7615
  container.style.width = "100%";
@@ -7570,17 +7620,12 @@ class Panel {
7570
7620
 
7571
7621
  static VECTOR_COMPONENTS = { 0: 'x', 1: 'y', 2: 'z', 3: 'w' };
7572
7622
 
7573
- _add_vector( num_components, name, value, callback, options = {} ) {
7623
+ _add_vector( numComponents, name, value, callback, options = {} ) {
7574
7624
 
7575
- num_components = clamp( num_components, 2, 4 );
7576
- value = value ?? new Array( num_components ).fill( 0 );
7625
+ numComponents = clamp( numComponents, 2, 4 );
7626
+ value = value ?? new Array( numComponents ).fill( 0 );
7577
7627
 
7578
- if( !name )
7579
- {
7580
- throw( "Set Widget Name!" );
7581
- }
7582
-
7583
- let widget = this.create_widget( name, Widget.VECTOR, options );
7628
+ let widget = this._createWidget( Widget.VECTOR, name, [].concat( value ), options );
7584
7629
 
7585
7630
  widget.onGetValue = () => {
7586
7631
  let inputs = element.querySelectorAll( "input" );
@@ -7592,33 +7637,30 @@ class Panel {
7592
7637
  return value;
7593
7638
  };
7594
7639
 
7595
- widget.onSetValue = ( newValue, skipCallback ) => {
7596
- const inputs = element.querySelectorAll( ".vecinput" );
7597
- if( inputs.length == newValue.length )
7640
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7641
+
7642
+ if( vectorInputs.length != newValue.length )
7598
7643
  {
7599
7644
  console.error( "Input length does not match vector length." );
7600
7645
  return;
7601
7646
  }
7602
7647
 
7603
- for( let i = 0; i < inputs.length; ++i )
7648
+ for( let i = 0; i < vectorInputs.length; ++i )
7604
7649
  {
7605
7650
  let value = newValue[ i ];
7606
- inputs[ i ].value = round( value, options.precision ) ?? 0;
7607
- Panel._dispatch_event( inputs[ i ], "change", skipCallback );
7651
+ value = clamp( value, +vectorInputs[ i ].min, +vectorInputs[ i ].max );
7652
+ value = round( value, options.precision ) ?? 0;
7653
+ vectorInputs[ i ].value = newValue[ i ] = value;
7608
7654
  }
7609
- };
7610
7655
 
7611
- let element = widget.domEl;
7612
-
7613
- // Add reset functionality
7614
- Panel._add_reset_property( element.domName, function() {
7615
- this.style.display = "none";
7616
- for( let v of element.querySelectorAll( ".vecinput" ) )
7656
+ if( !skipCallback )
7617
7657
  {
7618
- v.value = v.iValue;
7619
- Panel._dispatch_event( v, "change" );
7658
+ this._trigger( new IEvent( name, newValue, event ), callback );
7620
7659
  }
7621
- });
7660
+ };
7661
+
7662
+ const element = widget.domEl;
7663
+ const vectorInputs = [];
7622
7664
 
7623
7665
  // Add widget value
7624
7666
 
@@ -7626,20 +7668,21 @@ class Panel {
7626
7668
  container.className = "lexvector";
7627
7669
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
7628
7670
 
7629
- for( let i = 0; i < num_components; ++i )
7671
+ for( let i = 0; i < numComponents; ++i )
7630
7672
  {
7631
7673
  let box = document.createElement( 'div' );
7632
7674
  box.className = "vecbox";
7633
7675
  box.innerHTML = "<span class='" + Panel.VECTOR_COMPONENTS[ i ] + "'></span>";
7634
7676
 
7635
7677
  let vecinput = document.createElement( 'input' );
7636
- vecinput.className = "vecinput v" + num_components;
7678
+ vecinput.className = "vecinput v" + numComponents;
7637
7679
  vecinput.min = options.min ?? -1e24;
7638
7680
  vecinput.max = options.max ?? 1e24;
7639
7681
  vecinput.step = options.step ?? "any";
7640
7682
  vecinput.type = "number";
7641
- vecinput.id = "vec" + num_components + "_" + simple_guidGenerator();
7683
+ vecinput.id = "vec" + numComponents + "_" + simple_guidGenerator();
7642
7684
  vecinput.idx = i;
7685
+ vectorInputs[ i ] = vecinput;
7643
7686
 
7644
7687
  if( value[ i ].constructor == Number )
7645
7688
  {
@@ -7689,21 +7732,12 @@ class Panel {
7689
7732
  return;
7690
7733
  }
7691
7734
 
7692
- const skipCallback = e.detail;
7693
-
7694
- let val = e.target.value = clamp( e.target.value, +vecinput.min, +vecinput.max );
7735
+ let val = clamp( e.target.value, +vecinput.min, +vecinput.max );
7695
7736
  val = round( val, options.precision );
7696
7737
 
7697
- // Reset button (default value)
7698
- if( !skipCallback )
7699
- {
7700
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7701
- if( btn ) btn.style.display = val != vecinput.iValue ? "block" : "none";
7702
- }
7703
-
7704
7738
  if( locker.locked )
7705
7739
  {
7706
- for( let v of element.querySelectorAll( ".vecinput" ) )
7740
+ for( let v of vectorInputs )
7707
7741
  {
7708
7742
  v.value = val;
7709
7743
  value[ v.idx ] = val;
@@ -7715,7 +7749,7 @@ class Panel {
7715
7749
  value[ e.target.idx ] = val;
7716
7750
  }
7717
7751
 
7718
- if( !skipCallback ) this._trigger( new IEvent( name, value, e ), callback );
7752
+ widget.set( value, false, e );
7719
7753
  }, false );
7720
7754
 
7721
7755
  // Add drag input
@@ -7806,17 +7840,14 @@ class Panel {
7806
7840
  if( options.min !== undefined || options.max !== undefined )
7807
7841
  {
7808
7842
  widget.setLimits = ( newMin, newMax, newStep ) => {
7809
- const inputs = element.querySelectorAll(".vecinput");
7810
- for( let v of inputs )
7843
+ for( let v of vectorInputs )
7811
7844
  {
7812
7845
  v.min = newMin ?? v.min;
7813
7846
  v.max = newMax ?? v.max;
7814
7847
  v.step = newStep ?? v.step;
7815
- Panel._dispatch_event( v, "change", true );
7816
7848
  }
7817
7849
 
7818
- // To call onChange callback
7819
- this._trigger( new IEvent( name, value ), callback );
7850
+ widget.set( value, true );
7820
7851
  };
7821
7852
  }
7822
7853
 
@@ -7877,13 +7908,14 @@ class Panel {
7877
7908
  * @param {Number} value Default number value
7878
7909
  * @param {Function} callback Callback function on change
7879
7910
  * @param {*} options:
7911
+ * hideName: Don't use name as label [false]
7880
7912
  * disabled: Make the widget disabled [false]
7881
7913
  * units: Unit as string added to the end of the value
7882
7914
  */
7883
7915
 
7884
7916
  addSize( name, value, callback, options = {} ) {
7885
7917
 
7886
- let widget = this.create_widget( name, Widget.SIZE, options );
7918
+ let widget = this._createWidget( Widget.SIZE, name, value, options );
7887
7919
 
7888
7920
  widget.onGetValue = () => {
7889
7921
  const value = [];
@@ -7894,14 +7926,14 @@ class Panel {
7894
7926
  return value;
7895
7927
  };
7896
7928
 
7897
- widget.onSetValue = ( newValue, skipCallback ) => {
7929
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7898
7930
  for( let i = 0; i < element.dimensions.length; ++i )
7899
7931
  {
7900
7932
  element.dimensions[ i ].set( newValue[ i ], skipCallback );
7901
7933
  }
7902
7934
  };
7903
7935
 
7904
- let element = widget.domEl;
7936
+ const element = widget.domEl;
7905
7937
 
7906
7938
  this.queue( element );
7907
7939
 
@@ -7974,7 +8006,8 @@ class Panel {
7974
8006
  }
7975
8007
 
7976
8008
  // Remove branch padding and margins
7977
- if( !widget.name )
8009
+ const useNameAsLabel = !( options.hideName ?? false );
8010
+ if( !useNameAsLabel )
7978
8011
  {
7979
8012
  element.className += " noname";
7980
8013
  container.style.width = "100%";
@@ -7998,18 +8031,13 @@ class Panel {
7998
8031
 
7999
8032
  addPad( name, value, callback, options = {} ) {
8000
8033
 
8001
- if( !name )
8002
- {
8003
- throw( "Set Widget Name!" );
8004
- }
8005
-
8006
- let widget = this.create_widget( name, Widget.PAD, options );
8034
+ let widget = this._createWidget( Widget.PAD, name, null, options );
8007
8035
 
8008
8036
  widget.onGetValue = () => {
8009
8037
  return thumb.value.xy;
8010
8038
  };
8011
8039
 
8012
- widget.onSetValue = ( newValue, skipCallback ) => {
8040
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8013
8041
  thumb.value.set( newValue[ 0 ], newValue[ 1 ] );
8014
8042
  _updateValue( thumb.value );
8015
8043
  if( !skipCallback )
@@ -8018,7 +8046,7 @@ class Panel {
8018
8046
  }
8019
8047
  };
8020
8048
 
8021
- let element = widget.domEl;
8049
+ const element = widget.domEl;
8022
8050
 
8023
8051
  var container = document.createElement( 'div' );
8024
8052
  container.className = "lexpad";
@@ -8123,17 +8151,13 @@ class Panel {
8123
8151
 
8124
8152
  addProgress( name, value, options = {} ) {
8125
8153
 
8126
- if( !name )
8127
- {
8128
- throw("Set Widget Name!");
8129
- }
8130
-
8131
- let widget = this.create_widget( name, Widget.PROGRESS, options );
8154
+ let widget = this._createWidget( Widget.PROGRESS, name, value, options );
8132
8155
 
8133
8156
  widget.onGetValue = () => {
8134
8157
  return progress.value;
8135
8158
  };
8136
- widget.onSetValue = ( newValue, skipCallback ) => {
8159
+
8160
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8137
8161
  element.querySelector("meter").value = newValue;
8138
8162
  _updateColor();
8139
8163
  if( element.querySelector("span") )
@@ -8142,7 +8166,7 @@ class Panel {
8142
8166
  }
8143
8167
  };
8144
8168
 
8145
- let element = widget.domEl;
8169
+ const element = widget.domEl;
8146
8170
 
8147
8171
  var container = document.createElement('div');
8148
8172
  container.className = "lexprogress";
@@ -8264,13 +8288,8 @@ class Panel {
8264
8288
 
8265
8289
  addFile( name, callback, options = { } ) {
8266
8290
 
8267
- if( !name )
8268
- {
8269
- throw( "Set Widget Name!" );
8270
- }
8271
-
8272
- let widget = this.create_widget( name, Widget.FILE, options );
8273
- let element = widget.domEl;
8291
+ let widget = this._createWidget( Widget.FILE, name, null, options );
8292
+ const element = widget.domEl;
8274
8293
 
8275
8294
  let local = options.local ?? true;
8276
8295
  let type = options.type ?? 'text';
@@ -8356,20 +8375,28 @@ class Panel {
8356
8375
 
8357
8376
  addTree( name, data, options = {} ) {
8358
8377
 
8378
+ options.hideName = true;
8379
+
8380
+ const widget = this._createWidget( Widget.TREE, name, null, options );
8381
+ const element = widget.domEl;
8382
+
8359
8383
  let container = document.createElement('div');
8360
8384
  container.className = "lextree";
8385
+ element.appendChild( container );
8361
8386
 
8362
8387
  if( name )
8363
8388
  {
8364
8389
  let title = document.createElement('span');
8365
8390
  title.innerHTML = name;
8366
- container.appendChild(title);
8391
+ container.appendChild( title );
8367
8392
  }
8368
8393
 
8369
8394
  let toolsDiv = document.createElement('div');
8370
8395
  toolsDiv.className = "lextreetools";
8371
- if(!name)
8396
+ if( !name )
8397
+ {
8372
8398
  toolsDiv.className += " notitle";
8399
+ }
8373
8400
 
8374
8401
  // Tree icons
8375
8402
  if( options.icons )
@@ -8380,7 +8407,7 @@ class Panel {
8380
8407
  iconEl.title = data.name;
8381
8408
  iconEl.className = "lexicon " + data.icon;
8382
8409
  iconEl.addEventListener("click", data.callback);
8383
- toolsDiv.appendChild(iconEl);
8410
+ toolsDiv.appendChild( iconEl );
8384
8411
  }
8385
8412
  }
8386
8413
 
@@ -8388,35 +8415,36 @@ class Panel {
8388
8415
 
8389
8416
  options.filter = options.filter ?? true;
8390
8417
 
8391
- let node_filter_input = null;
8392
- if(options.filter)
8418
+ let nodeFilterInput = null;
8419
+ if( options.filter )
8393
8420
  {
8394
- node_filter_input = document.createElement('input');
8395
- node_filter_input.id = "lexnodetree_filter";
8396
- node_filter_input.setAttribute("placeholder", "Filter..");
8397
- node_filter_input.style.width = "calc( 100% - 17px )";
8398
- node_filter_input.addEventListener('input', function(){
8421
+ nodeFilterInput = document.createElement('input');
8422
+ nodeFilterInput.id = "lexnodetree_filter";
8423
+ nodeFilterInput.setAttribute("placeholder", "Filter..");
8424
+ nodeFilterInput.style.width = "calc( 100% - 17px )";
8425
+ nodeFilterInput.addEventListener('input', function(){
8399
8426
  nodeTree.refresh();
8400
8427
  });
8401
8428
 
8402
8429
  let searchIcon = document.createElement('a');
8403
8430
  searchIcon.className = "lexicon fa-solid fa-magnifying-glass";
8404
- toolsDiv.appendChild(node_filter_input);
8405
- toolsDiv.appendChild(searchIcon);
8431
+ toolsDiv.appendChild( nodeFilterInput );
8432
+ toolsDiv.appendChild( searchIcon );
8406
8433
  }
8407
8434
 
8408
- if(options.icons || options.filter)
8409
- container.appendChild(toolsDiv);
8435
+ if( options.icons || options.filter )
8436
+ {
8437
+ container.appendChild( toolsDiv );
8438
+ }
8410
8439
 
8411
8440
  // Tree
8412
8441
 
8413
8442
  let list = document.createElement('ul');
8414
- list.addEventListener("contextmenu", function(e) {
8443
+ list.addEventListener("contextmenu", function( e ) {
8415
8444
  e.preventDefault();
8416
8445
  });
8417
8446
 
8418
- container.appendChild(list);
8419
- this.root.appendChild(container);
8447
+ container.appendChild( list );
8420
8448
 
8421
8449
  const nodeTree = new NodeTree( container, data, options );
8422
8450
  return nodeTree;
@@ -8430,13 +8458,14 @@ class Panel {
8430
8458
 
8431
8459
  var element = document.createElement('div');
8432
8460
  element.className = "lexseparator";
8433
- let widget = new Widget( null, Widget.SEPARATOR );
8461
+
8462
+ let widget = new Widget( Widget.SEPARATOR );
8434
8463
  widget.domEl = element;
8435
8464
 
8436
- if( this.current_branch )
8465
+ if( this._currentBranch )
8437
8466
  {
8438
- this.current_branch.content.appendChild( element );
8439
- this.current_branch.widgets.push( widget );
8467
+ this._currentBranch.content.appendChild( element );
8468
+ this._currentBranch.widgets.push( widget );
8440
8469
  }
8441
8470
  else
8442
8471
  {
@@ -8459,12 +8488,8 @@ class Panel {
8459
8488
 
8460
8489
  addTabs( tabs, options = {} ) {
8461
8490
 
8462
- let root = this.current_branch ? this.current_branch.content : this.root;
8463
-
8464
- if( !this.current_branch )
8465
- {
8466
- console.warn("No current branch!");
8467
- }
8491
+ const widget = this._createWidget( Widget.TABS, null, null, options );
8492
+ const element = widget.domEl;
8468
8493
 
8469
8494
  if( tabs.constructor != Array )
8470
8495
  {
@@ -8484,7 +8509,7 @@ class Panel {
8484
8509
  let tabContainer = document.createElement( 'div' );
8485
8510
  tabContainer.className = 'tabs';
8486
8511
  container.appendChild( tabContainer );
8487
- root.appendChild( container );
8512
+ element.appendChild( container );
8488
8513
 
8489
8514
  for( let i = 0; i < tabs.length; ++i )
8490
8515
  {
@@ -8508,10 +8533,9 @@ class Panel {
8508
8533
  container.appendChild( infoContainer );
8509
8534
 
8510
8535
  tabEl.addEventListener( 'click', e => {
8511
-
8512
8536
  // Change selected tab
8513
8537
  tabContainer.querySelectorAll( '.lextab' ).forEach( e => { e.classList.remove( 'selected' ); } );
8514
- e.target.classList.add( 'selected' );
8538
+ tabEl.classList.add( 'selected' );
8515
8539
  // Hide all tabs content
8516
8540
  container.querySelectorAll(".widgets").forEach( e => { e.toggleAttribute( 'hidden', true ); } );
8517
8541
  // Show tab content
@@ -8552,42 +8576,38 @@ class Panel {
8552
8576
 
8553
8577
  addCounter( name, value, callback, options = { } ) {
8554
8578
 
8555
- let widget = this.create_widget( name, Widget.COUNTER, options );
8579
+ let widget = this._createWidget( Widget.COUNTER, name, value, options );
8556
8580
 
8557
8581
  widget.onGetValue = () => {
8558
8582
  return counterText.count;
8559
8583
  };
8560
8584
 
8561
- widget.onSetValue = ( newValue, skipCallback ) => {
8562
- _onChange( newValue, skipCallback );
8585
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8586
+ newValue = clamp( newValue, min, max );
8587
+ counterText.count = newValue;
8588
+ counterText.innerHTML = newValue;
8589
+ if( !skipCallback )
8590
+ {
8591
+ this._trigger( new IEvent( name, newValue, event ), callback );
8592
+ }
8563
8593
  };
8564
8594
 
8565
- let element = widget.domEl;
8595
+ const element = widget.domEl;
8566
8596
 
8567
8597
  const min = options.min ?? 0;
8568
8598
  const max = options.max ?? 100;
8569
8599
  const step = options.step ?? 1;
8570
8600
 
8571
- const _onChange = ( value, skipCallback, event ) => {
8572
- value = clamp( value, min, max );
8573
- counterText.count = value;
8574
- counterText.innerHTML = value;
8575
- if( !skipCallback )
8576
- {
8577
- this._trigger( new IEvent( name, value, event ), callback );
8578
- }
8579
- }
8580
-
8581
8601
  const container = document.createElement( 'div' );
8582
8602
  container.className = "lexcounter";
8583
8603
  element.appendChild( container );
8584
8604
 
8585
8605
  this.queue( container );
8586
8606
 
8587
- this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-minus'></a>", (value, e) => {
8607
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-minus'></a>", ( value, e ) => {
8588
8608
  let mult = step ?? 1;
8589
8609
  if( e.shiftKey ) mult *= 10;
8590
- _onChange( counterText.count - mult, false, e );
8610
+ widget.set( counterText.count - mult, false, e );
8591
8611
  }, { className: "micro", skipInlineCount: true, title: "Minus" });
8592
8612
 
8593
8613
  this.clearQueue();
@@ -8612,10 +8632,10 @@ class Panel {
8612
8632
 
8613
8633
  this.queue( container );
8614
8634
 
8615
- this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-plus'></a>", (value, e) => {
8635
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-plus'></a>", ( value, e ) => {
8616
8636
  let mult = step ?? 1;
8617
8637
  if( e.shiftKey ) mult *= 10;
8618
- _onChange( counterText.count + mult, false, e );
8638
+ widget.set( counterText.count + mult, false, e );
8619
8639
  }, { className: "micro", skipInlineCount: true, title: "Plus" });
8620
8640
 
8621
8641
  this.clearQueue();
@@ -8628,6 +8648,7 @@ class Panel {
8628
8648
  * @param {String} name Widget name
8629
8649
  * @param {Number} data Table data
8630
8650
  * @param {*} options:
8651
+ * hideName: Don't use name as label [false]
8631
8652
  * head: Table headers (each of the headers per column)
8632
8653
  * body: Table body (data per row for each column)
8633
8654
  * rowActions: Allow to add actions per row
@@ -8642,17 +8663,17 @@ class Panel {
8642
8663
  throw( "Data is needed to create a table!" );
8643
8664
  }
8644
8665
 
8645
- let widget = this.create_widget( name, Widget.TABLE, options );
8666
+ let widget = this._createWidget( Widget.TABLE, name, null, options );
8646
8667
 
8647
8668
  widget.onGetValue = () => {
8648
8669
 
8649
8670
  };
8650
8671
 
8651
- widget.onSetValue = ( newValue, skipCallback ) => {
8672
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8652
8673
 
8653
8674
  };
8654
8675
 
8655
- let element = widget.domEl;
8676
+ const element = widget.domEl;
8656
8677
 
8657
8678
  const container = document.createElement('div');
8658
8679
  container.className = "lextable";
@@ -8840,7 +8861,8 @@ class Panel {
8840
8861
 
8841
8862
  widget.refreshTable();
8842
8863
 
8843
- if( !widget.name )
8864
+ const useNameAsLabel = !( options.hideName ?? false );
8865
+ if( !useNameAsLabel )
8844
8866
  {
8845
8867
  element.className += " noname";
8846
8868
  container.style.width = "100%";
@@ -8887,12 +8909,14 @@ class Branch {
8887
8909
  var title = document.createElement( 'div' );
8888
8910
  title.className = "lexbranchtitle";
8889
8911
 
8890
- title.innerHTML = "<a class='fa-solid fa-angle-up switch-branch-button'></a>";
8891
8912
  if( options.icon )
8892
8913
  {
8893
- title.innerHTML += "<a class='branchicon " + options.icon + "' style='margin-right: 8px; margin-bottom: -2px;'>";
8914
+ title.innerHTML = "<a class='branchicon " + options.icon + "'>";
8894
8915
  }
8895
- title.innerHTML += name || "Branch";
8916
+
8917
+ title.innerHTML += ( name || "Branch" );
8918
+
8919
+ title.innerHTML += "<a class='fa-solid fa-angle-up switch-branch-button'></a>";
8896
8920
 
8897
8921
  root.appendChild( title );
8898
8922
 
@@ -9050,7 +9074,7 @@ class Branch {
9050
9074
  for( var i = 0; i < this.widgets.length; i++ )
9051
9075
  {
9052
9076
  let widget = this.widgets[ i ];
9053
- let element = widget.domEl;
9077
+ const element = widget.domEl;
9054
9078
 
9055
9079
  if( element.children.length < 2 )
9056
9080
  {
@@ -9223,7 +9247,7 @@ class Dialog {
9223
9247
  titleDiv.innerHTML = title;
9224
9248
  titleDiv.setAttribute('draggable', false);
9225
9249
 
9226
- titleDiv.oncontextmenu = function(e) {
9250
+ titleDiv.oncontextmenu = function( e ) {
9227
9251
  e.preventDefault();
9228
9252
  e.stopPropagation();
9229
9253
 
@@ -9563,7 +9587,7 @@ class ContextMenu {
9563
9587
  }
9564
9588
  }
9565
9589
 
9566
- _adjust_position( div, margin, useAbsolute = false ) {
9590
+ _adjustPosition( div, margin, useAbsolute = false ) {
9567
9591
 
9568
9592
  let rect = div.getBoundingClientRect();
9569
9593
 
@@ -9604,7 +9628,7 @@ class ContextMenu {
9604
9628
  }
9605
9629
  }
9606
9630
 
9607
- _create_submenu( o, k, c, d ) {
9631
+ _createSubmenu( o, k, c, d ) {
9608
9632
 
9609
9633
  this.root.querySelectorAll( ".lexcontextmenu" ).forEach( cm => cm.remove() );
9610
9634
 
@@ -9616,7 +9640,7 @@ class ContextMenu {
9616
9640
  {
9617
9641
  const subitem = o[ k ][ i ];
9618
9642
  const subkey = Object.keys( subitem )[ 0 ];
9619
- this._create_entry(subitem, subkey, contextmenu, d);
9643
+ this._createEntry(subitem, subkey, contextmenu, d);
9620
9644
  }
9621
9645
 
9622
9646
  var rect = c.getBoundingClientRect();
@@ -9624,10 +9648,10 @@ class ContextMenu {
9624
9648
  contextmenu.style.marginTop = 3.5 - c.offsetHeight + "px";
9625
9649
 
9626
9650
  // Set final width
9627
- this._adjust_position( contextmenu, 6, true );
9651
+ this._adjustPosition( contextmenu, 6, true );
9628
9652
  }
9629
9653
 
9630
- _create_entry( o, k, c, d ) {
9654
+ _createEntry( o, k, c, d ) {
9631
9655
 
9632
9656
  const hasSubmenu = o[ k ].length;
9633
9657
  let entry = document.createElement('div');
@@ -9672,7 +9696,7 @@ class ContextMenu {
9672
9696
  return;
9673
9697
 
9674
9698
  if( LX.OPEN_CONTEXTMENU_ENTRY == 'click' )
9675
- this._create_submenu( o, k, entry, ++d );
9699
+ this._createSubmenu( o, k, entry, ++d );
9676
9700
  });
9677
9701
 
9678
9702
  if( !hasSubmenu )
@@ -9688,7 +9712,7 @@ class ContextMenu {
9688
9712
  if(entry.built)
9689
9713
  return;
9690
9714
  entry.built = true;
9691
- this._create_submenu( o, k, entry, ++d );
9715
+ this._createSubmenu( o, k, entry, ++d );
9692
9716
  e.stopPropagation();
9693
9717
  });
9694
9718
  }
@@ -9700,7 +9724,7 @@ class ContextMenu {
9700
9724
  }
9701
9725
 
9702
9726
  onCreate() {
9703
- doAsync( () => this._adjust_position( this.root, 6 ) );
9727
+ doAsync( () => this._adjustPosition( this.root, 6 ) );
9704
9728
  }
9705
9729
 
9706
9730
  add( path, options = {} ) {
@@ -9730,13 +9754,13 @@ class ContextMenu {
9730
9754
 
9731
9755
  if( found )
9732
9756
  {
9733
- insert( tokens[idx++], found );
9757
+ insert( tokens[ idx++ ], found );
9734
9758
  }
9735
9759
  else
9736
9760
  {
9737
9761
  let item = {};
9738
9762
  item[ token ] = [];
9739
- const nextToken = tokens[idx++];
9763
+ const nextToken = tokens[ idx++ ];
9740
9764
  // Check if last token -> add callback
9741
9765
  if( !nextToken )
9742
9766
  {
@@ -9756,13 +9780,15 @@ class ContextMenu {
9756
9780
 
9757
9781
  const setParent = _item => {
9758
9782
 
9759
- let key = Object.keys(_item)[0];
9783
+ let key = Object.keys( _item )[ 0 ];
9760
9784
  let children = _item[ key ];
9761
9785
 
9762
- if(!children.length)
9786
+ if( !children.length )
9787
+ {
9763
9788
  return;
9789
+ }
9764
9790
 
9765
- if(children.find( c => Object.keys(c)[0] == key ) == null)
9791
+ if( children.find( c => Object.keys(c)[0] == key ) == null )
9766
9792
  {
9767
9793
  const parent = {};
9768
9794
  parent[ key ] = [];
@@ -9772,14 +9798,18 @@ class ContextMenu {
9772
9798
 
9773
9799
  for( var child of _item[ key ] )
9774
9800
  {
9775
- let k = Object.keys(child)[0];
9776
- for( var i = 0; i < child[k].length; ++i )
9777
- setParent(child);
9801
+ let k = Object.keys( child )[ 0 ];
9802
+ for( var i = 0; i < child[ k ].length; ++i )
9803
+ {
9804
+ setParent( child );
9805
+ }
9778
9806
  }
9779
9807
  };
9780
9808
 
9781
9809
  for( let item of this.items )
9782
- setParent(item);
9810
+ {
9811
+ setParent( item );
9812
+ }
9783
9813
 
9784
9814
  // Create elements
9785
9815
 
@@ -9789,9 +9819,11 @@ class ContextMenu {
9789
9819
  let pKey = "eId" + getSupportedDOMName( key );
9790
9820
 
9791
9821
  // Item already created
9792
- const id = "#" + (item.id ?? pKey);
9793
- if( !this.root.querySelector(id) )
9794
- this._create_entry(item, key, this.root, -1);
9822
+ const id = "#" + ( item.id ?? pKey );
9823
+ if( !this.root.querySelector( id ) )
9824
+ {
9825
+ this._createEntry( item, key, this.root, -1 );
9826
+ }
9795
9827
  }
9796
9828
  }
9797
9829
 
@@ -11052,7 +11084,7 @@ class AssetView {
11052
11084
  itemEl.appendChild(info);
11053
11085
  }
11054
11086
 
11055
- itemEl.addEventListener('click', function(e) {
11087
+ itemEl.addEventListener('click', function( e ) {
11056
11088
  e.stopImmediatePropagation();
11057
11089
  e.stopPropagation();
11058
11090
 
@@ -11089,7 +11121,7 @@ class AssetView {
11089
11121
 
11090
11122
  if( that.contextMenu )
11091
11123
  {
11092
- itemEl.addEventListener('contextmenu', function(e) {
11124
+ itemEl.addEventListener('contextmenu', function( e ) {
11093
11125
  e.preventDefault();
11094
11126
 
11095
11127
  const multiple = that.content.querySelectorAll('.selected').length;
@@ -11114,7 +11146,7 @@ class AssetView {
11114
11146
  });
11115
11147
  }
11116
11148
 
11117
- itemEl.addEventListener("dragstart", function(e) {
11149
+ itemEl.addEventListener("dragstart", function( e ) {
11118
11150
  e.preventDefault();
11119
11151
  }, false );
11120
11152
 
@@ -11511,7 +11543,7 @@ Object.assign(LX, {
11511
11543
  script.src = url[i] + ( version ? "?version=" + version : "" );
11512
11544
  script.original_src = url[i];
11513
11545
  script.async = false;
11514
- script.onload = function(e) {
11546
+ script.onload = function( e ) {
11515
11547
  total--;
11516
11548
  loaded_scripts.push(this);
11517
11549
  if(total)