lexgui 0.1.35 → 0.1.37

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.
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  var LX = {
11
- version: "0.1.35",
11
+ version: "0.1.37",
12
12
  ready: false,
13
13
  components: [], // specific pre-build components
14
14
  signals: {} // events and triggers
@@ -25,7 +25,10 @@ LX.CURVE_MOVEOUT_CLAMP = 0;
25
25
  LX.CURVE_MOVEOUT_DELETE = 1;
26
26
 
27
27
  function clamp( num, min, max ) { return Math.min( Math.max( num, min ), max ); }
28
- function round( num, n ) { return +num.toFixed( n ); }
28
+ function round( number, precision ) { return +(( number ).toFixed( precision ?? 2 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' )); }
29
+
30
+ LX.clamp = clamp;
31
+ LX.round = round;
29
32
 
30
33
  function getSupportedDOMName( string )
31
34
  {
@@ -41,43 +44,43 @@ function has( component_name )
41
44
 
42
45
  LX.has = has;
43
46
 
44
- function getExtension(s)
47
+ function getExtension( s )
45
48
  {
46
49
  return s.includes('.') ? s.split('.').pop() : null;
47
50
  }
48
51
 
49
52
  LX.getExtension = getExtension;
50
53
 
51
- function deepCopy(o)
54
+ function deepCopy( o )
52
55
  {
53
56
  return JSON.parse(JSON.stringify(o))
54
57
  }
55
58
 
56
59
  LX.deepCopy = deepCopy;
57
60
 
58
- function setThemeColor(color_name, color)
61
+ function setThemeColor( colorName, color )
59
62
  {
60
- var r = document.querySelector(':root');
61
- r.style.setProperty("--" + color_name, color);
63
+ var r = document.querySelector( ':root' );
64
+ r.style.setProperty( '--' + colorName, color );
62
65
  }
63
66
 
64
67
  LX.setThemeColor = setThemeColor;
65
68
 
66
- function getThemeColor(color_name)
69
+ function getThemeColor( colorName )
67
70
  {
68
- var r = getComputedStyle(document.querySelector(':root'));
69
- return r.getPropertyValue("--" + color_name);
71
+ var r = getComputedStyle( document.querySelector( ':root' ) );
72
+ return r.getPropertyValue( '--' + colorName );
70
73
  }
71
74
 
72
75
  LX.getThemeColor = getThemeColor;
73
76
 
74
- function getBase64Image(img) {
75
- var canvas = document.createElement("canvas");
77
+ function getBase64Image( img ) {
78
+ var canvas = document.createElement( 'canvas' );
76
79
  canvas.width = img.width;
77
80
  canvas.height = img.height;
78
- var ctx = canvas.getContext("2d");
79
- ctx.drawImage(img, 0, 0);
80
- return canvas.toDataURL("image/png");
81
+ var ctx = canvas.getContext( '2d' );
82
+ ctx.drawImage( img, 0, 0 );
83
+ return canvas.toDataURL( 'image/png' );
81
84
  }
82
85
 
83
86
  LX.getBase64Image = getBase64Image;
@@ -109,6 +112,8 @@ function simple_guidGenerator() {
109
112
  return (S4()+"-"+S4()+"-"+S4());
110
113
  }
111
114
 
115
+ LX.guidGenerator = simple_guidGenerator;
116
+
112
117
  // Timer that works everywhere (from litegraph.js)
113
118
  if (typeof performance != "undefined") {
114
119
  LX.getTime = performance.now.bind(performance);
@@ -129,22 +134,26 @@ let ASYNC_ENABLED = true;
129
134
 
130
135
  function doAsync( fn, ms ) {
131
136
  if( ASYNC_ENABLED )
137
+ {
132
138
  setTimeout( fn, ms ?? 0 );
139
+ }
133
140
  else
141
+ {
134
142
  fn();
143
+ }
135
144
  }
136
145
 
137
146
  // Math classes
138
147
 
139
148
  class vec2 {
140
149
 
141
- constructor(x, y) {
150
+ constructor( x, y ) {
142
151
  this.x = x ?? 0;
143
- this.y = y ?? (x ?? 0);
152
+ this.y = y ?? ( x ?? 0 );
144
153
  }
145
154
 
146
- get xy() { return [ this.x, this.y]; }
147
- get yx() { return [ this.y, this.x]; }
155
+ get xy() { return [ this.x, this.y ]; }
156
+ get yx() { return [ this.y, this.x ]; }
148
157
 
149
158
  set ( x, y ) { this.x = x; this.y = y; }
150
159
  add ( v, v0 = new vec2() ) { v0.set( this.x + v.x, this.y + v.y ); return v0; }
@@ -152,6 +161,11 @@ class vec2 {
152
161
  mul ( v, v0 = new vec2() ) { if( v.constructor == Number ) { v = new vec2( v ) } v0.set( this.x * v.x, this.y * v.y ); return v0; }
153
162
  div ( v, v0 = new vec2() ) { if( v.constructor == Number ) { v = new vec2( v ) } v0.set( this.x / v.x, this.y / v.y ); return v0; }
154
163
  abs ( v0 = new vec2() ) { v0.set( Math.abs( this.x ), Math.abs( this.y ) ); return v0; }
164
+ dot ( v ) { return this.x * v.x + this.y * v.y; }
165
+ len2 () { return this.dot( this ) }
166
+ len () { return Math.sqrt( this.len2() ); }
167
+ nrm ( v0 = new vec2() ) { v0.set( this.x, this.y ); return v0.mul( 1.0 / this.len(), v0 ); }
168
+ dst ( v ) { return v.sub( this ).len(); }
155
169
  };
156
170
 
157
171
  LX.vec2 = vec2;
@@ -291,11 +305,18 @@ function create_global_searchbar( root ) {
291
305
  else
292
306
  {
293
307
  for( let c of LX.components )
294
- if( LX[c].prototype.onKeyPressed )
308
+ {
309
+ if( !LX[c] || !LX[c].prototype.onKeyPressed )
295
310
  {
296
- const instances = LX.CodeEditor.getInstances();
297
- for( let i of instances ) i.onKeyPressed( e );
311
+ continue;
298
312
  }
313
+
314
+ const instances = LX.CodeEditor.getInstances();
315
+ for( let i of instances )
316
+ {
317
+ i.onKeyPressed( e );
318
+ }
319
+ }
299
320
  }
300
321
  });
301
322
 
@@ -622,6 +643,8 @@ class IEvent {
622
643
  }
623
644
  };
624
645
 
646
+ LX.IEvent = IEvent;
647
+
625
648
  class TreeEvent {
626
649
 
627
650
  static NONE = 0;
@@ -659,16 +682,22 @@ class TreeEvent {
659
682
 
660
683
  LX.TreeEvent = TreeEvent;
661
684
 
662
- function emit( signal_name, value, target )
685
+ function emit( signalName, value, options = {} )
663
686
  {
664
- const data = LX.signals[ signal_name ];
687
+ const data = LX.signals[ signalName ];
665
688
 
666
689
  if( !data )
667
690
  return;
668
691
 
692
+ const target = options.target;
693
+
669
694
  if( target )
670
695
  {
671
- if(target[signal_name]) target[signal_name].call(target, value);
696
+ if( target[ signalName ])
697
+ {
698
+ target[ signalName ].call( target, value );
699
+ }
700
+
672
701
  return;
673
702
  }
674
703
 
@@ -676,13 +705,16 @@ function emit( signal_name, value, target )
676
705
  {
677
706
  if( obj.constructor === Widget )
678
707
  {
679
- obj.set( value );
708
+ obj.set( value, options.skipCallback ?? true );
680
709
 
681
- if(obj.options && obj.options.callback)
682
- obj.options.callback(value, data);
683
- }else
710
+ if( obj.options && obj.options.callback )
711
+ {
712
+ obj.options.callback( value, data );
713
+ }
714
+ }
715
+ else
684
716
  {
685
- obj[signal_name].call(obj, value);
717
+ obj[ signalName ].call( obj, value );
686
718
  }
687
719
  }
688
720
  }
@@ -722,12 +754,16 @@ class Area {
722
754
 
723
755
  constructor( options = {} ) {
724
756
 
725
- var root = document.createElement('div');
757
+ var root = document.createElement( 'div' );
726
758
  root.className = "lexarea";
727
- if(options.id)
759
+ if( options.id )
760
+ {
728
761
  root.id = options.id;
729
- if(options.className)
762
+ }
763
+ if( options.className )
764
+ {
730
765
  root.className += " " + options.className;
766
+ }
731
767
 
732
768
  var width = options.width || "calc( 100% )";
733
769
  var height = options.height || "100%";
@@ -735,10 +771,14 @@ class Area {
735
771
  // This has default options..
736
772
  this.setLimitBox( options.minWidth, options.minHeight, options.maxWidth, options.maxHeight );
737
773
 
738
- if(width.constructor == Number)
774
+ if( width.constructor == Number )
775
+ {
739
776
  width += "px";
740
- if(height.constructor == Number)
777
+ }
778
+ if( height.constructor == Number )
779
+ {
741
780
  height += "px";
781
+ }
742
782
 
743
783
  root.style.width = width;
744
784
  root.style.height = height;
@@ -749,27 +789,32 @@ class Area {
749
789
  this.sections = [];
750
790
  this.panels = [];
751
791
 
752
- if(!options.no_append) {
792
+ if( !options.no_append )
793
+ {
753
794
  var lexroot = document.getElementById("lexroot");
754
795
  lexroot.appendChild( this.root );
755
796
  }
756
797
 
757
798
  let overlay = options.overlay;
758
799
 
759
- if(overlay)
800
+ if( overlay )
760
801
  {
761
802
  this.root.classList.add("overlay-" + overlay);
762
803
 
763
- if(options.left) {
804
+ if( options.left )
805
+ {
764
806
  this.root.style.left = options.left;
765
807
  }
766
- if(options.right) {
808
+ else if( options.right )
809
+ {
767
810
  this.root.style.right = options.right;
768
811
  }
769
- if(options.top) {
812
+ else if( options.top )
813
+ {
770
814
  this.root.style.top = options.top;
771
815
  }
772
- if(options.bottom) {
816
+ else if( options.bottom )
817
+ {
773
818
  this.root.style.bottom = options.bottom;
774
819
  }
775
820
 
@@ -781,97 +826,99 @@ class Area {
781
826
  root.classList.add("resizeable");
782
827
  }
783
828
 
784
- if(options.resize)
829
+ if( options.resize )
785
830
  {
786
831
  this.split_bar = document.createElement("div");
787
- let type = overlay == "left" || overlay == "right" ? "horizontal" : "vertical";
832
+ let type = (overlay == "left") || (overlay == "right") ? "horizontal" : "vertical";
788
833
  this.type = overlay;;
789
834
  this.split_bar.className = "lexsplitbar " + type;
790
- if(overlay == "right") {
835
+
836
+ if( overlay == "right" )
837
+ {
791
838
  this.split_bar.style.width = LX.DEFAULT_SPLITBAR_SIZE + "px";
792
- this.split_bar.style.left = -LX.DEFAULT_SPLITBAR_SIZE/2 + "px";
839
+ this.split_bar.style.left = -(LX.DEFAULT_SPLITBAR_SIZE / 2.0) + "px";
793
840
  }
794
- else if(overlay == "left") {
841
+ else if( overlay == "left" )
842
+ {
795
843
  let size = Math.min(document.body.clientWidth - LX.DEFAULT_SPLITBAR_SIZE, this.root.clientWidth);
796
844
  this.split_bar.style.width = LX.DEFAULT_SPLITBAR_SIZE + "px";
797
- this.split_bar.style.left = size + LX.DEFAULT_SPLITBAR_SIZE/2 + "px";
845
+ this.split_bar.style.left = size + (LX.DEFAULT_SPLITBAR_SIZE / 2.0) + "px";
798
846
  }
799
- else if (overlay == "top") {
847
+ else if( overlay == "top" )
848
+ {
800
849
  let size = Math.min(document.body.clientHeight - LX.DEFAULT_SPLITBAR_SIZE, this.root.clientHeight);
801
850
  this.split_bar.style.height = LX.DEFAULT_SPLITBAR_SIZE + "px";
802
- this.split_bar.style.top = size + LX.DEFAULT_SPLITBAR_SIZE/2 + "px";
851
+ this.split_bar.style.top = size + (LX.DEFAULT_SPLITBAR_SIZE / 2.0) + "px";
803
852
  }
804
- else if(overlay == "bottom") {
853
+ else if( overlay == "bottom" )
854
+ {
805
855
  this.split_bar.style.height = LX.DEFAULT_SPLITBAR_SIZE + "px";
806
- this.split_bar.style.top = -LX.DEFAULT_SPLITBAR_SIZE/2 + "px";
856
+ this.split_bar.style.top = -(LX.DEFAULT_SPLITBAR_SIZE / 2.0) + "px";
807
857
  }
808
858
 
809
859
  this.split_bar.addEventListener("mousedown", inner_mousedown);
810
- this.root.appendChild(this.split_bar);
860
+ this.root.appendChild( this.split_bar );
811
861
 
812
862
  var that = this;
813
- var last_pos = [0,0];
863
+ var last_pos = [ 0, 0 ];
814
864
 
815
- function inner_mousedown(e)
865
+ function inner_mousedown( e )
816
866
  {
817
867
  var doc = that.root.ownerDocument;
818
- doc.addEventListener("mousemove",inner_mousemove);
819
- doc.addEventListener("mouseup",inner_mouseup);
820
- last_pos[0] = e.x;
821
- last_pos[1] = e.y;
868
+ doc.addEventListener( 'mousemove', inner_mousemove );
869
+ doc.addEventListener( 'mouseup', inner_mouseup );
870
+ last_pos[ 0 ] = e.x;
871
+ last_pos[ 1 ] = e.y;
822
872
  e.stopPropagation();
823
873
  e.preventDefault();
824
- document.body.classList.add("nocursor");
825
- that.split_bar.classList.add("nocursor");
874
+ document.body.classList.add( 'nocursor' );
875
+ that.split_bar.classList.add( 'nocursor' );
826
876
  }
827
877
 
828
- function inner_mousemove(e)
878
+ function inner_mousemove( e )
829
879
  {
830
- switch(that.type) {
880
+ switch( that.type ) {
831
881
  case "right":
832
- var dt = (last_pos[0] - e.x);
833
- var size = (that.root.offsetWidth + dt);
882
+ var dt = ( last_pos[ 0 ] - e.x );
883
+ var size = ( that.root.offsetWidth + dt );
834
884
  that.root.style.width = size + "px";
835
885
  break;
836
-
837
886
  case "left":
838
- var dt = (last_pos[0] - e.x);
887
+ var dt = ( last_pos[ 0 ] - e.x );
839
888
  var size = Math.min(document.body.clientWidth - LX.DEFAULT_SPLITBAR_SIZE, (that.root.offsetWidth - dt));
840
889
  that.root.style.width = size + "px";
841
890
  that.split_bar.style.left = size + LX.DEFAULT_SPLITBAR_SIZE/2 + "px";
842
891
  break;
843
-
844
892
  case "top":
845
- var dt = (last_pos[1] - e.y);
893
+ var dt = ( last_pos[ 1 ] - e.y );
846
894
  var size = Math.min(document.body.clientHeight - LX.DEFAULT_SPLITBAR_SIZE, (that.root.offsetHeight - dt));
847
895
  that.root.style.height = size + "px";
848
896
  that.split_bar.style.top = size + LX.DEFAULT_SPLITBAR_SIZE/2 + "px";
849
897
  break;
850
-
851
898
  case "bottom":
852
- var dt = (last_pos[1] - e.y);
853
- var size = (that.root.offsetHeight + dt);
899
+ var dt = ( last_pos[ 1 ] - e.y );
900
+ var size = ( that.root.offsetHeight + dt );
854
901
  that.root.style.height = size + "px";
855
902
  break;
856
903
  }
857
904
 
858
- last_pos[0] = e.x;
859
- last_pos[1] = e.y;
905
+ last_pos[ 0 ] = e.x;
906
+ last_pos[ 1 ] = e.y;
860
907
  e.stopPropagation();
861
908
  e.preventDefault();
862
909
 
863
910
  // Resize events
864
- if(that.onresize)
911
+ if( that.onresize )
865
912
  that.onresize( that.root.getBoundingClientRect() );
866
913
  }
867
914
 
868
- function inner_mouseup(e)
915
+ function inner_mouseup( e )
869
916
  {
870
917
  var doc = that.root.ownerDocument;
871
- doc.removeEventListener("mousemove",inner_mousemove);
872
- doc.removeEventListener("mouseup",inner_mouseup);
873
- document.body.classList.remove("nocursor");
874
- that.split_bar.classList.remove("nocursor");
918
+ doc.removeEventListener( 'mousemove', inner_mousemove );
919
+ doc.removeEventListener( 'mouseup', inner_mouseup );
920
+ document.body.classList.remove( 'nocursor' );
921
+ that.split_bar.classList.remove( 'nocursor' );
875
922
  }
876
923
  }
877
924
  }
@@ -1154,7 +1201,7 @@ class Area {
1154
1201
  this.propagateEvent("onresize");
1155
1202
  }
1156
1203
 
1157
- /**
1204
+ /**
1158
1205
  * @method extend
1159
1206
  * Hide 2nd area split
1160
1207
  */
@@ -1603,7 +1650,7 @@ class Tabs {
1603
1650
  this.classList.remove("dockingtab");
1604
1651
 
1605
1652
  // Change tabs instance
1606
- LX.emit( "@on_tab_docked", el.instance );
1653
+ LX.emit( "@on_tab_docked" );
1607
1654
  el.instance = that;
1608
1655
 
1609
1656
  // Show on drop
@@ -1675,22 +1722,26 @@ class Tabs {
1675
1722
 
1676
1723
  let isSelected = options.selected ?? false;
1677
1724
 
1678
- if( isSelected ) {
1679
- this.root.querySelectorAll('span').forEach( s => s.classList.remove('selected'));
1680
- this.area.root.querySelectorAll('.lextabcontent').forEach( c => c.style.display = 'none');
1725
+ if( isSelected )
1726
+ {
1727
+ this.root.querySelectorAll( 'span' ).forEach( s => s.classList.remove( 'selected' ) );
1728
+ this.area.root.querySelectorAll( '.lextabcontent' ).forEach( c => c.style.display = 'none' );
1681
1729
  }
1682
1730
 
1683
1731
  isSelected = !Object.keys( this.tabs ).length && !this.folding ? true : isSelected;
1684
1732
 
1685
1733
  let contentEl = content.root ? content.root : content;
1686
- contentEl.style.display = isSelected ? "block" : "none";
1687
- contentEl.classList.add("lextabcontent");
1734
+ contentEl.originalDisplay = contentEl.style.display;
1735
+ contentEl.style.display = isSelected ? contentEl.originalDisplay : "none";
1736
+ contentEl.classList.add( 'lextabcontent' );
1688
1737
 
1689
1738
  // Process icon
1690
1739
  if( options.icon )
1691
1740
  {
1692
1741
  if( options.icon.includes( 'fa-' ) ) // It's fontawesome icon...
1742
+ {
1693
1743
  options.icon = "<i class='" + options.icon + "'></i>";
1744
+ }
1694
1745
  else // an image..
1695
1746
  {
1696
1747
  const rootPath = "https://raw.githubusercontent.com/jxarco/lexgui.js/master/";
@@ -1699,26 +1750,31 @@ class Tabs {
1699
1750
  }
1700
1751
 
1701
1752
  // Create tab
1702
- let tabEl = document.createElement('span');
1703
- tabEl.dataset["name"] = name;
1704
- tabEl.className = "lexareatab" + (isSelected ? " selected" : "");
1705
- tabEl.innerHTML = (options.icon ?? "") + name;
1706
- tabEl.id = name.replace(/\s/g, '') + Tabs.TAB_ID++;
1753
+ let tabEl = document.createElement( 'span' );
1754
+ tabEl.dataset[ "name" ] = name;
1755
+ tabEl.className = "lexareatab" + ( isSelected ? " selected" : "" );
1756
+ tabEl.innerHTML = ( options.icon ?? "" ) + name;
1757
+ tabEl.id = name.replace( /\s/g, '' ) + Tabs.TAB_ID++;
1707
1758
  tabEl.title = options.title;
1708
1759
  tabEl.selected = isSelected ?? false;
1709
1760
  tabEl.fixed = options.fixed;
1710
- if(tabEl.selected)
1711
- this.selected = name;
1712
1761
  tabEl.instance = this;
1713
1762
  contentEl.id = tabEl.id + "_content";
1714
1763
 
1764
+ if( tabEl.selected )
1765
+ {
1766
+ this.selected = name;
1767
+ }
1768
+
1715
1769
  LX.addSignal( "@on_tab_docked", tabEl, function() {
1716
- if( this.parentElement.childNodes.length == 1 ){
1717
- this.parentElement.childNodes[0].click(); // single tab!!
1770
+ if( this.parentElement.childNodes.length == 1 )
1771
+ {
1772
+ this.parentElement.childNodes[ 0 ].click(); // single tab!!
1718
1773
  }
1719
1774
  } );
1720
1775
 
1721
1776
  tabEl.addEventListener("click", e => {
1777
+
1722
1778
  e.preventDefault();
1723
1779
  e.stopPropagation();
1724
1780
 
@@ -1726,25 +1782,27 @@ class Tabs {
1726
1782
  {
1727
1783
  // For folding tabs
1728
1784
  const lastValue = tabEl.selected;
1729
- tabEl.parentElement.querySelectorAll('span').forEach( s => s.selected = false );
1785
+ tabEl.parentElement.querySelectorAll( 'span' ).forEach( s => s.selected = false );
1730
1786
  tabEl.selected = !lastValue;
1731
1787
  // Manage selected
1732
- tabEl.parentElement.querySelectorAll('span').forEach( s => s.classList.remove('selected'));
1733
- tabEl.classList.toggle('selected', (this.folding && tabEl.selected));
1788
+ tabEl.parentElement.querySelectorAll( 'span' ).forEach( s => s.classList.remove( 'selected' ));
1789
+ tabEl.classList.toggle('selected', ( this.folding && tabEl.selected ));
1734
1790
  // Manage visibility
1735
- tabEl.instance.area.root.querySelectorAll('.lextabcontent').forEach( c => c.style.display = 'none');
1736
- contentEl.style.display = "block";
1791
+ tabEl.instance.area.root.querySelectorAll( '.lextabcontent' ).forEach( c => c.style.display = 'none' );
1792
+ contentEl.style.display = contentEl.originalDisplay;
1737
1793
  tabEl.instance.selected = tabEl.dataset.name;
1738
1794
  }
1739
1795
 
1740
1796
  if( this.folding )
1741
1797
  {
1742
1798
  this.folded = tabEl.selected;
1743
- this.area.root.classList.toggle('folded', !this.folded);
1799
+ this.area.root.classList.toggle( 'folded', !this.folded );
1744
1800
  }
1745
1801
 
1746
- if(options.onSelect)
1802
+ if( options.onSelect )
1803
+ {
1747
1804
  options.onSelect(e, tabEl.dataset.name);
1805
+ }
1748
1806
 
1749
1807
  if( this.thumb )
1750
1808
  {
@@ -1759,25 +1817,28 @@ class Tabs {
1759
1817
  e.preventDefault();
1760
1818
  e.stopPropagation();
1761
1819
 
1762
- if(options.onContextMenu)
1820
+ if( options.onContextMenu )
1821
+ {
1763
1822
  options.onContextMenu( e, tabEl.dataset.name );
1823
+ }
1764
1824
  });
1765
1825
 
1766
1826
  tabEl.addEventListener("mouseup", e => {
1767
1827
  e.preventDefault();
1768
1828
  e.stopPropagation();
1769
- if( e.button == 1 ) {
1770
- this.delete( tabEl.dataset["name"] );
1829
+ if( e.button == 1 )
1830
+ {
1831
+ this.delete( tabEl.dataset[ "name" ] );
1771
1832
  }
1772
1833
  });
1773
1834
 
1774
- tabEl.setAttribute('draggable', true);
1775
- tabEl.addEventListener("dragstart", function(e) {
1835
+ tabEl.setAttribute( 'draggable', true );
1836
+ tabEl.addEventListener( 'dragstart', function( e ) {
1776
1837
  if( this.parentElement.childNodes.length == 1 ){
1777
1838
  e.preventDefault();
1778
1839
  return;
1779
1840
  }
1780
- e.dataTransfer.setData( "source", e.target.id );
1841
+ e.dataTransfer.setData( 'source', e.target.id );
1781
1842
  });
1782
1843
 
1783
1844
  // Attach content
@@ -2401,6 +2462,7 @@ class Widget {
2401
2462
  static CONTENT = 20;
2402
2463
  static CUSTOM = 21;
2403
2464
  static SEPARATOR = 22;
2465
+ static KNOB = 23;
2404
2466
 
2405
2467
  static NO_CONTEXT_TYPES = [
2406
2468
  Widget.BUTTON,
@@ -2409,7 +2471,7 @@ class Widget {
2409
2471
  Widget.PROGRESS
2410
2472
  ];
2411
2473
 
2412
- constructor(name, type, options) {
2474
+ constructor( name, type, options ) {
2413
2475
  this.name = name;
2414
2476
  this.type = type;
2415
2477
  this.options = options;
@@ -2417,13 +2479,15 @@ class Widget {
2417
2479
 
2418
2480
  value() {
2419
2481
 
2420
- if(this.onGetValue)
2482
+ if( this.onGetValue )
2483
+ {
2421
2484
  return this.onGetValue();
2485
+ }
2422
2486
 
2423
- console.warn("Can't get value of " + this.typeName());
2487
+ console.warn( "Can't get value of " + this.typeName() );
2424
2488
  }
2425
2489
 
2426
- set( value, skipCallback = false ) {
2490
+ set( value, skipCallback = false, signalName = "" ) {
2427
2491
 
2428
2492
  if( this.onSetValue )
2429
2493
  return this.onSetValue( value, skipCallback );
@@ -2480,13 +2544,13 @@ class Widget {
2480
2544
  case Widget.LIST: return "List";
2481
2545
  case Widget.TAGS: return "Tags";
2482
2546
  case Widget.CURVE: return "Curve";
2547
+ case Widget.KNOB: return "Knob";
2483
2548
  case Widget.CUSTOM: return this.customName;
2484
2549
  }
2485
2550
  }
2486
2551
 
2487
2552
  refresh() {
2488
- // this.domEl.innerHTML = "";
2489
- // if( this.options.callback ) this.options.callback();
2553
+
2490
2554
  }
2491
2555
  }
2492
2556
 
@@ -3788,6 +3852,8 @@ class Panel {
3788
3852
  * placeholder: Add input placeholder
3789
3853
  * trigger: Choose onchange trigger (default, input) [default]
3790
3854
  * inputWidth: Width of the text input
3855
+ * float: Justify input text content
3856
+ * justifyName: Justify name content
3791
3857
  * fitHeight: Height adapts to text
3792
3858
  */
3793
3859
 
@@ -3825,6 +3891,7 @@ class Panel {
3825
3891
  let wValue = document.createElement( 'textarea' );
3826
3892
  wValue.value = wValue.iValue = value || "";
3827
3893
  wValue.style.width = "100%";
3894
+ wValue.style.textAlign = options.float ?? "";
3828
3895
  Object.assign( wValue.style, options.style ?? {} );
3829
3896
 
3830
3897
  if( options.disabled ?? false ) wValue.setAttribute("disabled", true);
@@ -3901,8 +3968,9 @@ class Panel {
3901
3968
  * @param {String} value Button name
3902
3969
  * @param {Function} callback Callback function on click
3903
3970
  * @param {*} options:
3904
- * icon
3905
3971
  * disabled: Make the widget disabled [false]
3972
+ * icon: Icon class to show as button value
3973
+ * img: Path to image to show as button value
3906
3974
  */
3907
3975
 
3908
3976
  addButton( name, value, callback, options = {} ) {
@@ -4521,6 +4589,7 @@ class Panel {
4521
4589
  * @param {Array} values By default values in the array
4522
4590
  * @param {Function} callback Callback function on change
4523
4591
  * @param {*} options:
4592
+ * innerValues (Array): Use dropdown mode and use values as options
4524
4593
  */
4525
4594
 
4526
4595
  addArray( name, values = [], callback, options = {} ) {
@@ -5033,6 +5102,7 @@ class Panel {
5033
5102
  * precision: The number of digits to appear after the decimal point
5034
5103
  * min, max: Min and Max values for the input
5035
5104
  * skipSlider: If there are min and max values, skip the slider
5105
+ * units: Unit as string added to the end of the value
5036
5106
  */
5037
5107
 
5038
5108
  addNumber( name, value, callback, options = {} ) {
@@ -5043,7 +5113,7 @@ class Panel {
5043
5113
  return +vecinput.value;
5044
5114
  };
5045
5115
  widget.onSetValue = ( newValue, skipCallback ) => {
5046
- vecinput.value = newValue;
5116
+ vecinput.value = round( newValue, options.precision );
5047
5117
  Panel._dispatch_event( vecinput, "change", skipCallback );
5048
5118
  };
5049
5119
 
@@ -5068,27 +5138,48 @@ class Panel {
5068
5138
  box.className = "numberbox";
5069
5139
 
5070
5140
  let vecinput = document.createElement( 'input' );
5141
+ vecinput.id = "number_" + simple_guidGenerator();
5071
5142
  vecinput.className = "vecinput";
5072
5143
  vecinput.min = options.min ?? -1e24;
5073
5144
  vecinput.max = options.max ?? 1e24;
5074
5145
  vecinput.step = options.step ?? "any";
5075
5146
  vecinput.type = "number";
5076
- vecinput.id = "number_" + simple_guidGenerator();
5077
5147
 
5078
5148
  if( value.constructor == Number )
5079
5149
  {
5080
5150
  value = clamp( value, +vecinput.min, +vecinput.max );
5081
- value = options.precision ? round( value, options.precision ) : value;
5151
+ value = round( value, options.precision );
5082
5152
  }
5083
5153
 
5084
5154
  vecinput.value = vecinput.iValue = value;
5085
5155
  box.appendChild( vecinput );
5086
5156
 
5087
- let drag_icon = document.createElement( 'a' );
5088
- drag_icon.className = "fa-solid fa-arrows-up-down drag-icon hidden";
5089
- box.appendChild( drag_icon );
5157
+ let measureRealWidth = function( value, paddingPlusMargin = 8 ) {
5158
+ var i = document.createElement( "span" );
5159
+ i.className = "lexinputmeasure";
5160
+ i.innerHTML = value;
5161
+ document.body.appendChild( i );
5162
+ var rect = i.getBoundingClientRect();
5163
+ LX.UTILS.deleteElement( i );
5164
+ return rect.width + paddingPlusMargin;
5165
+ }
5090
5166
 
5091
- if( options.disabled ) {
5167
+ if( options.units )
5168
+ {
5169
+ let unitSpan = document.createElement( 'span' );
5170
+ unitSpan.className = "lexunit";
5171
+ unitSpan.innerText = options.units;
5172
+ unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
5173
+ vecinput.unitSpan = unitSpan;
5174
+ box.appendChild( unitSpan );
5175
+ }
5176
+
5177
+ let dragIcon = document.createElement( 'a' );
5178
+ dragIcon.className = "fa-solid fa-arrows-up-down drag-icon hidden";
5179
+ box.appendChild( dragIcon );
5180
+
5181
+ if( options.disabled )
5182
+ {
5092
5183
  vecinput.disabled = true;
5093
5184
  }
5094
5185
 
@@ -5103,7 +5194,7 @@ class Panel {
5103
5194
  slider.value = value;
5104
5195
  slider.addEventListener( "input", function( e ) {
5105
5196
  let new_value = +this.valueAsNumber;
5106
- vecinput.value = ( +new_value ).toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
5197
+ vecinput.value = round( new_value, options.precision );
5107
5198
  Panel._dispatch_event( vecinput, "change" );
5108
5199
  }, false );
5109
5200
  box.appendChild( slider );
@@ -5119,7 +5210,7 @@ class Panel {
5119
5210
  if( e.shiftKey ) mult *= 10;
5120
5211
  else if( e.altKey ) mult *= 0.1;
5121
5212
  let new_value = ( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) );
5122
- this.value = ( +new_value ).toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
5213
+ this.value = round( new_value, options.precision );
5123
5214
  Panel._dispatch_event(vecinput, "change");
5124
5215
  }, { passive: false });
5125
5216
 
@@ -5132,12 +5223,20 @@ class Panel {
5132
5223
 
5133
5224
  let val = e.target.value = clamp( +e.target.valueAsNumber, +vecinput.min, +vecinput.max );
5134
5225
  val = options.precision ? round( val, options.precision ) : val;
5135
- // update slider!
5226
+
5227
+ // Update slider!
5136
5228
  if( box.querySelector( ".lexinputslider" ) )
5229
+ {
5137
5230
  box.querySelector( ".lexinputslider" ).value = val;
5231
+ }
5138
5232
 
5139
5233
  vecinput.value = val;
5140
5234
 
5235
+ if( options.units )
5236
+ {
5237
+ vecinput.unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
5238
+ }
5239
+
5141
5240
  // Reset button (default value)
5142
5241
  if( !skipCallback )
5143
5242
  {
@@ -5162,7 +5261,7 @@ class Panel {
5162
5261
  lastY = e.pageY;
5163
5262
  document.body.classList.add('nocursor');
5164
5263
  document.body.classList.add('noevents');
5165
- drag_icon.classList.remove('hidden');
5264
+ dragIcon.classList.remove('hidden');
5166
5265
  e.stopImmediatePropagation();
5167
5266
  e.stopPropagation();
5168
5267
  }
@@ -5174,8 +5273,8 @@ class Panel {
5174
5273
  if(e.shiftKey) mult *= 10;
5175
5274
  else if(e.altKey) mult *= 0.1;
5176
5275
  let new_value = (+vecinput.valueAsNumber + mult * dt);
5177
- vecinput.value = (+new_value).toFixed(4).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/,'$1');
5178
- Panel._dispatch_event(vecinput, "change");
5276
+ vecinput.value = (+new_value).toFixed( 4 ).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/,'$1');
5277
+ Panel._dispatch_event( vecinput, "change" );
5179
5278
  }
5180
5279
 
5181
5280
  lastY = e.pageY;
@@ -5189,7 +5288,7 @@ class Panel {
5189
5288
  doc.removeEventListener("mouseup",inner_mouseup);
5190
5289
  document.body.classList.remove('nocursor');
5191
5290
  document.body.classList.remove('noevents');
5192
- drag_icon.classList.add('hidden');
5291
+ dragIcon.classList.add('hidden');
5193
5292
  }
5194
5293
 
5195
5294
  container.appendChild(box);
@@ -5226,9 +5325,15 @@ class Panel {
5226
5325
  };
5227
5326
  widget.onSetValue = ( newValue, skipCallback ) => {
5228
5327
  const inputs = element.querySelectorAll( ".vecinput" );
5328
+ if( inputs.length == newValue.length )
5329
+ {
5330
+ console.error( "Input length does not match vector length." );
5331
+ return;
5332
+ }
5333
+
5229
5334
  for( var i = 0; i < inputs.length; ++i ) {
5230
5335
  let value = newValue[ i ];
5231
- inputs[ i ].value = value ?? 0;
5336
+ inputs[ i ].value = round( value, options.precision ) ?? 0;
5232
5337
  Panel._dispatch_event( inputs[ i ], "change", skipCallback );
5233
5338
  }
5234
5339
  };
@@ -5268,14 +5373,14 @@ class Panel {
5268
5373
  if( value[ i ].constructor == Number )
5269
5374
  {
5270
5375
  value[ i ] = clamp(value[ i ], +vecinput.min, +vecinput.max);
5271
- value[ i ] = options.precision ? round(value[ i ], options.precision) : value[ i ];
5376
+ value[ i ] = round( value[ i ], options.precision );
5272
5377
  }
5273
5378
 
5274
5379
  vecinput.value = vecinput.iValue = value[ i ];
5275
5380
 
5276
- let drag_icon = document.createElement( 'a' );
5277
- drag_icon.className = "fa-solid fa-arrows-up-down drag-icon hidden";
5278
- box.appendChild( drag_icon );
5381
+ let dragIcon = document.createElement( 'a' );
5382
+ dragIcon.className = "fa-solid fa-arrows-up-down drag-icon hidden";
5383
+ box.appendChild( dragIcon );
5279
5384
 
5280
5385
  if( options.disabled ) {
5281
5386
  vecinput.disabled = true;
@@ -5291,14 +5396,17 @@ class Panel {
5291
5396
  if( e.shiftKey ) mult = 10;
5292
5397
  else if( e.altKey ) mult = 0.1;
5293
5398
 
5294
- if( lock_icon.locked )
5399
+ if( locker.locked )
5295
5400
  {
5296
- for( let v of element.querySelectorAll(".vecinput") ) {
5297
- v.value = ( +v.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) ).toPrecision( 5 );
5401
+ for( let v of element.querySelectorAll(".vecinput") )
5402
+ {
5403
+ v.value = round( +v.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ), options.precision );
5298
5404
  Panel._dispatch_event(v, "change");
5299
5405
  }
5300
- } else {
5301
- this.value = ( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) ).toPrecision( 5 );
5406
+ }
5407
+ else
5408
+ {
5409
+ this.value = round( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ), options.precision );
5302
5410
  Panel._dispatch_event( vecinput, "change" );
5303
5411
  }
5304
5412
  }, { passive: false } );
@@ -5311,7 +5419,7 @@ class Panel {
5311
5419
  const skipCallback = e.detail;
5312
5420
 
5313
5421
  let val = e.target.value = clamp( e.target.value, +vecinput.min, +vecinput.max );
5314
- val = options.precision ? round( val, options.precision ) : val;
5422
+ val = round( val, options.precision );
5315
5423
 
5316
5424
  // Reset button (default value)
5317
5425
  if( !skipCallback )
@@ -5320,7 +5428,7 @@ class Panel {
5320
5428
  if( btn ) btn.style.display = val != vecinput.iValue ? "block": "none";
5321
5429
  }
5322
5430
 
5323
- if( lock_icon.locked )
5431
+ if( locker.locked )
5324
5432
  {
5325
5433
  for( let v of element.querySelectorAll( ".vecinput" ) ) {
5326
5434
  v.value = val;
@@ -5348,7 +5456,7 @@ class Panel {
5348
5456
  lastY = e.pageY;
5349
5457
  document.body.classList.add('nocursor');
5350
5458
  document.body.classList.add('noevents');
5351
- drag_icon.classList.remove('hidden');
5459
+ dragIcon.classList.remove('hidden');
5352
5460
  e.stopImmediatePropagation();
5353
5461
  e.stopPropagation();
5354
5462
  }
@@ -5360,14 +5468,14 @@ class Panel {
5360
5468
  if(e.shiftKey) mult = 10;
5361
5469
  else if(e.altKey) mult = 0.1;
5362
5470
 
5363
- if( lock_icon.locked )
5471
+ if( locker.locked )
5364
5472
  {
5365
5473
  for( let v of element.querySelectorAll(".vecinput") ) {
5366
- v.value = (+v.valueAsNumber + mult * dt).toPrecision(5);
5474
+ v.value = round( +v.valueAsNumber + mult * dt, options.precision );
5367
5475
  Panel._dispatch_event(v, "change");
5368
5476
  }
5369
5477
  } else {
5370
- vecinput.value = (+vecinput.valueAsNumber + mult * dt).toPrecision(5);
5478
+ vecinput.value = round( +vecinput.valueAsNumber + mult * dt, options.precision );
5371
5479
  Panel._dispatch_event(vecinput, "change");
5372
5480
  }
5373
5481
  }
@@ -5377,23 +5485,23 @@ class Panel {
5377
5485
  e.preventDefault();
5378
5486
  }
5379
5487
 
5380
- function inner_mouseup(e) {
5488
+ function inner_mouseup( e ) {
5381
5489
  var doc = that.root.ownerDocument;
5382
5490
  doc.removeEventListener("mousemove",inner_mousemove);
5383
5491
  doc.removeEventListener("mouseup",inner_mouseup);
5384
5492
  document.body.classList.remove('nocursor');
5385
5493
  document.body.classList.remove('noevents');
5386
- drag_icon.classList.add('hidden');
5494
+ dragIcon.classList.add('hidden');
5387
5495
  }
5388
5496
 
5389
- box.appendChild(vecinput);
5390
- container.appendChild(box);
5497
+ box.appendChild( vecinput );
5498
+ container.appendChild( box );
5391
5499
  }
5392
5500
 
5393
- let lock_icon = document.createElement('a');
5394
- lock_icon.className = "fa-solid fa-lock-open lexicon";
5395
- container.appendChild(lock_icon);
5396
- lock_icon.addEventListener("click", function(e) {
5501
+ let locker = document.createElement('a');
5502
+ locker.className = "fa-solid fa-lock-open lexicon";
5503
+ container.appendChild(locker);
5504
+ locker.addEventListener("click", function(e) {
5397
5505
  this.locked = !this.locked;
5398
5506
  if(this.locked){
5399
5507
  this.classList.add("fa-lock");
@@ -5404,7 +5512,7 @@ class Panel {
5404
5512
  }
5405
5513
  }, false);
5406
5514
 
5407
- element.appendChild(container);
5515
+ element.appendChild( container );
5408
5516
 
5409
5517
  return widget;
5410
5518
  }
@@ -5442,9 +5550,9 @@ class Panel {
5442
5550
  * @param {*} options:
5443
5551
  * min, max: Min and Max values
5444
5552
  * low, optimum, high: Low and High boundary values, Optimum point in the range
5445
- * showValue: show current value
5446
- * editable: allow edit value
5447
- * callback: function called on change value
5553
+ * showValue: Show current value
5554
+ * editable: Allow edit value
5555
+ * callback: Function called on change value
5448
5556
  */
5449
5557
 
5450
5558
  addProgress( name, value, options = {} ) {
@@ -5703,64 +5811,93 @@ class Panel {
5703
5811
 
5704
5812
  /**
5705
5813
  * @method addTabs
5706
- * @param {Array} tabs Contains objects with {name, icon, callback}
5814
+ * @param {Array} tabs Contains objects with {
5815
+ * name: Name of the tab (if icon, use as title)
5816
+ * icon: Icon to be used as the tab icon (optional)
5817
+ * onCreate: Func to be called at tab creation
5818
+ * onSelect: Func to be called on select tab (optional)
5819
+ * }
5707
5820
  * @param {*} options
5708
5821
  * vertical: Use vertical or horizontal tabs (vertical by default)
5709
5822
  * showNames: Show tab name only in horizontal tabs
5710
5823
  */
5711
5824
 
5712
5825
  addTabs( tabs, options = {} ) {
5826
+
5713
5827
  let root = this.current_branch ? this.current_branch.content : this.root;
5714
- if(!this.current_branch)
5828
+
5829
+ if( !this.current_branch )
5830
+ {
5715
5831
  console.warn("No current branch!");
5832
+ }
5716
5833
 
5717
- if(tabs.constructor != Array)
5718
- throw("Param @tabs must be an Array!");
5834
+ if( tabs.constructor != Array )
5835
+ {
5836
+ throw( "Param @tabs must be an Array!" );
5837
+ }
5719
5838
 
5720
5839
  const vertical = options.vertical ?? true;
5721
- const showNames = !vertical && (options.showNames ?? false);
5840
+ const showNames = !vertical && ( options.showNames ?? false );
5722
5841
 
5723
- let container = document.createElement('div');
5842
+ let container = document.createElement( 'div' );
5724
5843
  container.className = "lextabscontainer";
5725
- if( !vertical ) container.className += " horizontal";
5844
+ if( !vertical )
5845
+ {
5846
+ container.className += " horizontal";
5847
+ }
5726
5848
 
5727
- let tabContainer = document.createElement("div");
5728
- tabContainer.className = "tabs";
5849
+ let tabContainer = document.createElement( 'div' );
5850
+ tabContainer.className = 'tabs';
5729
5851
  container.appendChild( tabContainer );
5730
5852
  root.appendChild( container );
5731
5853
 
5732
- for( var i = 0; i < tabs.length; ++i )
5854
+ for( let i = 0; i < tabs.length; ++i )
5733
5855
  {
5734
- const tab = tabs[i];
5735
- const selected = i == 0;
5736
- let tabEl = document.createElement('div');
5737
- tabEl.className = "lextab " + (i == tabs.length - 1 ? "last" : "") + (selected ? "selected" : "");
5738
- tabEl.innerHTML = (showNames ? tab.name : "") + "<a class='" + (tab.icon || "fa fa-hashtag") + " " + (showNames ? "withname" : "") + "'></a>";
5856
+ const tab = tabs[ i ];
5857
+ console.assert( tab.name );
5858
+ const isSelected = ( i == 0 );
5859
+ let tabEl = document.createElement( 'div' );
5860
+ tabEl.className = "lextab " + (i == tabs.length - 1 ? "last" : "") + ( isSelected ? "selected" : "" );
5861
+ tabEl.innerHTML = ( showNames ? tab.name : "" ) + "<a class='" + ( tab.icon || "fa fa-hashtag" ) + " " + (showNames ? "withname" : "") + "'></a>";
5739
5862
  tabEl.title = tab.name;
5740
5863
 
5741
- let infoContainer = document.createElement("div");
5742
- infoContainer.id = tab.name.replace(/\s/g, '');
5864
+ let infoContainer = document.createElement( 'div' );
5865
+ infoContainer.id = tab.name.replace( /\s/g, '' );
5743
5866
  infoContainer.className = "widgets";
5744
- if(!selected) infoContainer.toggleAttribute('hidden', true);
5867
+
5868
+ if(!isSelected)
5869
+ {
5870
+ infoContainer.toggleAttribute('hidden', true);
5871
+ }
5872
+
5745
5873
  container.appendChild( infoContainer );
5746
5874
 
5747
- tabEl.addEventListener("click", function() {
5748
- // change selected tab
5749
- tabContainer.querySelectorAll(".lextab").forEach( e => { e.classList.remove("selected"); } );
5750
- this.classList.add("selected");
5751
- // hide all tabs content
5752
- container.querySelectorAll(".widgets").forEach( e => { e.toggleAttribute('hidden', true); } );
5753
- // show tab content
5754
- const el = container.querySelector("#" + infoContainer.id);
5755
- el.toggleAttribute('hidden');
5875
+ tabEl.addEventListener( 'click', e => {
5876
+
5877
+ // Change selected tab
5878
+ tabContainer.querySelectorAll( '.lextab' ).forEach( e => { e.classList.remove( 'selected' ); } );
5879
+ e.target.classList.add( 'selected' );
5880
+ // Hide all tabs content
5881
+ container.querySelectorAll(".widgets").forEach( e => { e.toggleAttribute( 'hidden', true ); } );
5882
+ // Show tab content
5883
+ const el = container.querySelector( '#' + infoContainer.id );
5884
+ el.toggleAttribute( 'hidden' );
5885
+
5886
+ if( tab.onSelect )
5887
+ {
5888
+ tab.onSelect( this, infoContainer );
5889
+ }
5756
5890
  });
5757
5891
 
5758
- tabContainer.appendChild(tabEl);
5892
+ tabContainer.appendChild( tabEl );
5759
5893
 
5760
- // push to tab space
5761
- this.queue( infoContainer );
5762
- tab.callback( this, infoContainer );
5763
- this.clearQueue();
5894
+ if( tab.onCreate )
5895
+ {
5896
+ // push to tab space
5897
+ this.queue( infoContainer );
5898
+ tab.onCreate( this, infoContainer );
5899
+ this.clearQueue();
5900
+ }
5764
5901
  }
5765
5902
 
5766
5903
  this.addSeparator();
@@ -5776,14 +5913,19 @@ LX.Panel = Panel;
5776
5913
  class Branch {
5777
5914
 
5778
5915
  constructor( name, options = {} ) {
5916
+
5779
5917
  this.name = name;
5780
5918
 
5781
- var root = document.createElement('div');
5919
+ var root = document.createElement( 'div' );
5782
5920
  root.className = "lexbranch";
5783
- if(options.id)
5921
+ if( options.id )
5922
+ {
5784
5923
  root.id = options.id;
5785
- if(options.className)
5924
+ }
5925
+ if( options.className )
5926
+ {
5786
5927
  root.className += " " + options.className;
5928
+ }
5787
5929
 
5788
5930
  root.style.width = "calc(100% - 7px)";
5789
5931
  root.style.margin = "0 auto";
@@ -5794,52 +5936,56 @@ class Branch {
5794
5936
  this.widgets = [];
5795
5937
 
5796
5938
  // create element
5797
- var title = document.createElement('div');
5939
+ var title = document.createElement( 'div' );
5798
5940
  title.className = "lexbranchtitle";
5799
5941
 
5800
5942
  title.innerHTML = "<a class='fa-solid fa-angle-up switch-branch-button'></a>";
5801
- if(options.icon) {
5943
+ if( options.icon )
5944
+ {
5802
5945
  title.innerHTML += "<a class='branchicon " + options.icon + "' style='margin-right: 8px; margin-bottom: -2px;'>";
5803
5946
  }
5804
5947
  title.innerHTML += name || "Branch";
5805
5948
 
5806
- root.appendChild(title);
5949
+ root.appendChild( title );
5807
5950
 
5808
- var branch_content = document.createElement('div');
5809
- branch_content.id = name.replace(/\s/g, '');
5810
- branch_content.className = "lexbranchcontent";
5811
- root.appendChild(branch_content);
5812
- this.content = branch_content;
5951
+ var branchContent = document.createElement( 'div' );
5952
+ branchContent.id = name.replace(/\s/g, '');
5953
+ branchContent.className = "lexbranchcontent";
5954
+ root.appendChild(branchContent);
5955
+ this.content = branchContent;
5813
5956
 
5814
5957
  this._addBranchSeparator();
5815
5958
 
5816
- if( options.closed ) {
5959
+ if( options.closed )
5960
+ {
5817
5961
  title.className += " closed";
5818
5962
  root.className += " closed";
5819
5963
  this.grabber.setAttribute('hidden', true);
5820
5964
  doAsync( () => {
5821
- this.content.setAttribute('hidden', true);
5822
- }, 15);
5965
+ this.content.setAttribute( 'hidden', true );
5966
+ }, 15 );
5823
5967
  }
5824
5968
 
5825
- this.onclick = function(e){
5969
+ this.onclick = function( e ) {
5826
5970
  e.stopPropagation();
5827
- this.classList.toggle('closed');
5828
- this.parentElement.classList.toggle('closed');
5971
+ this.classList.toggle( 'closed' );
5972
+ this.parentElement.classList.toggle( 'closed' );
5829
5973
 
5830
- that.content.toggleAttribute('hidden');
5831
- that.grabber.toggleAttribute('hidden');
5974
+ that.content.toggleAttribute( 'hidden' );
5975
+ that.grabber.toggleAttribute( 'hidden' );
5832
5976
 
5833
- LX.emit("@on_branch_closed", this.classList.contains("closed"), that.panel);
5977
+ LX.emit( "@on_branch_closed", this.classList.contains("closed"), { target: that.panel } );
5834
5978
  };
5835
5979
 
5836
- this.oncontextmenu = function(e) {
5980
+ this.oncontextmenu = function( e ) {
5837
5981
 
5838
5982
  e.preventDefault();
5839
5983
  e.stopPropagation();
5840
5984
 
5841
5985
  if( this.parentElement.classList.contains("dialog") )
5986
+ {
5842
5987
  return;
5988
+ }
5843
5989
 
5844
5990
  addContextMenu("Dock", e, p => {
5845
5991
  e.preventDefault();
@@ -5847,12 +5993,12 @@ class Branch {
5847
5993
  // p.add('<i class="fa-regular fa-window-maximize fa-rotate-180">', {id: 'dock_options1'});
5848
5994
  // p.add('<i class="fa-regular fa-window-maximize fa-rotate-90">', {id: 'dock_options2'});
5849
5995
  // p.add('<i class="fa-regular fa-window-maximize fa-rotate-270">', {id: 'dock_options3'});
5850
- p.add('Floating', that._on_make_floating.bind(that));
5996
+ p.add( 'Floating', that._on_make_floating.bind( that ) );
5851
5997
  }, { icon: "fa-regular fa-window-restore" });
5852
5998
  };
5853
5999
 
5854
- title.addEventListener("click", this.onclick);
5855
- title.addEventListener("contextmenu", this.oncontextmenu);
6000
+ title.addEventListener( 'click', this.onclick );
6001
+ title.addEventListener( 'contextmenu', this.oncontextmenu );
5856
6002
  }
5857
6003
 
5858
6004
  _on_make_floating() {
@@ -6291,7 +6437,8 @@ class ContextMenu {
6291
6437
  this.items = [];
6292
6438
  this.colors = {};
6293
6439
 
6294
- if(title) {
6440
+ if( title )
6441
+ {
6295
6442
  const item = {};
6296
6443
  item[ title ] = [];
6297
6444
  item[ 'className' ] = "cmtitle";
@@ -6306,7 +6453,7 @@ class ContextMenu {
6306
6453
 
6307
6454
  if(!useAbsolute)
6308
6455
  {
6309
- let width = rect.width + 36; // this has paddings
6456
+ let width = rect.width;
6310
6457
  if(window.innerWidth - rect.right < 0)
6311
6458
  div.style.left = (window.innerWidth - width - margin) + "px";
6312
6459
 
@@ -6347,7 +6494,6 @@ class ContextMenu {
6347
6494
  contextmenu.style.marginTop = 3.5 - c.offsetHeight + "px";
6348
6495
 
6349
6496
  // Set final width
6350
- // contextmenu.style.width = contextmenu.offsetWidth + "px";
6351
6497
  this._adjust_position( contextmenu, 6, true );
6352
6498
  }
6353
6499
 
@@ -6421,7 +6567,7 @@ class ContextMenu {
6421
6567
  }
6422
6568
 
6423
6569
  onCreate() {
6424
- this._adjust_position( this.root, 6 );
6570
+ doAsync( () => this._adjust_position( this.root, 6 ) );
6425
6571
  }
6426
6572
 
6427
6573
  add( path, options = {} ) {
@@ -6526,10 +6672,12 @@ LX.ContextMenu = ContextMenu;
6526
6672
  function addContextMenu( title, event, callback, options )
6527
6673
  {
6528
6674
  var menu = new ContextMenu( event, title, options );
6529
- LX.root.appendChild(menu.root);
6675
+ LX.root.appendChild( menu.root );
6530
6676
 
6531
- if(callback)
6677
+ if( callback )
6678
+ {
6532
6679
  callback( menu );
6680
+ }
6533
6681
 
6534
6682
  menu.onCreate();
6535
6683
 
@@ -6867,7 +7015,6 @@ class Curve {
6867
7015
  }
6868
7016
 
6869
7017
  redraw( options = {} ) {
6870
- console.log("REDRAW!!");
6871
7018
  this.element.redraw( options );
6872
7019
  }
6873
7020
  }