lexgui 0.1.43 → 0.1.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/lexgui.js CHANGED
@@ -12,7 +12,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
12
12
  */
13
13
 
14
14
  var LX = global.LX = {
15
- version: "0.1.43",
15
+ version: "0.1.45",
16
16
  ready: false,
17
17
  components: [], // specific pre-build components
18
18
  signals: {} // events and triggers
@@ -64,6 +64,15 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
64
64
 
65
65
  LX.deepCopy = deepCopy;
66
66
 
67
+ function setTheme( colorScheme )
68
+ {
69
+ colorScheme = ( colorScheme == "light" ) ? "light" : "dark";
70
+ document.documentElement.setAttribute( "data-theme", colorScheme );
71
+ LX.emit( "@on_new_color_scheme", colorScheme );
72
+ }
73
+
74
+ LX.setTheme = setTheme;
75
+
67
76
  function setThemeColor( colorName, color )
68
77
  {
69
78
  var r = document.querySelector( ':root' );
@@ -74,8 +83,24 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
74
83
 
75
84
  function getThemeColor( colorName )
76
85
  {
77
- var r = getComputedStyle( document.querySelector( ':root' ) );
78
- return r.getPropertyValue( '--' + colorName );
86
+ const r = getComputedStyle( document.querySelector( ':root' ) );
87
+ const value = r.getPropertyValue( '--' + colorName );
88
+
89
+ if( value.includes( "light-dark" ) && window.matchMedia )
90
+ {
91
+ const currentScheme = r.getPropertyValue( "color-scheme" );
92
+
93
+ if( ( window.matchMedia( "(prefers-color-scheme: light)" ).matches ) || ( currentScheme == "light" ) )
94
+ {
95
+ return value.substring( value.indexOf( '(' ) + 1, value.indexOf( ',' ) ).replace( /\s/g, '' );
96
+ }
97
+ else
98
+ {
99
+ return value.substring( value.indexOf( ',' ) + 1, value.indexOf( ')' ) ).replace( /\s/g, '' );
100
+ }
101
+ }
102
+
103
+ return value;
79
104
  }
80
105
 
81
106
  LX.getThemeColor = getThemeColor;
@@ -132,6 +157,23 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
132
157
 
133
158
  LX.guidGenerator = simple_guidGenerator;
134
159
 
160
+ function buildTextPattern( options = {} ) {
161
+ let patterns = [];
162
+ if ( options.lowercase ) patterns.push("(?=.*[a-z])");
163
+ if ( options.uppercase ) patterns.push("(?=.*[A-Z])");
164
+ if ( options.digit ) patterns.push("(?=.*\\d)");
165
+ if ( options.specialChar ) patterns.push("(?=.*[@#$%^&+=!])");
166
+ if ( options.noSpaces ) patterns.push("(?!.*\\s)");
167
+
168
+ let minLength = options.minLength || 0;
169
+ let maxLength = options.maxLength || ""; // Empty means no max length restriction
170
+
171
+ let pattern = `^${ patterns.join("") }.{${ minLength },${ maxLength }}$`;
172
+ return options.asRegExp ? new RegExp( pattern ) : pattern;
173
+ }
174
+
175
+ LX.buildTextPattern = buildTextPattern;
176
+
135
177
  // Timer that works everywhere (from litegraph.js)
136
178
  if (typeof performance != "undefined") {
137
179
  LX.getTime = performance.now.bind(performance);
@@ -618,6 +660,11 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
618
660
  this.main_area = new Area( { id: options.id ?? 'mainarea' } );
619
661
  }
620
662
 
663
+ window.matchMedia( "(prefers-color-scheme: dark)" ).addEventListener( "change", event => {
664
+ const newColorScheme = event.matches ? "dark" : "light";
665
+ LX.emit( "@on_new_color_scheme", newColorScheme );
666
+ });
667
+
621
668
  return this.main_area;
622
669
  }
623
670
 
@@ -730,7 +777,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
730
777
  if( callback ) callback.call( this, value );
731
778
  dialog.close();
732
779
  }
733
- }, { buttonClass: "accept" });
780
+ }, { buttonClass: "primary" });
734
781
 
735
782
  p.addButton(null, "Cancel", () => {if(options.on_cancel) options.on_cancel(); dialog.close();} );
736
783
 
@@ -747,6 +794,25 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
747
794
 
748
795
  LX.prompt = prompt;
749
796
 
797
+ /**
798
+ * @method badge
799
+ * @param {String} text
800
+ * @param {String} className
801
+ * @param {*} options
802
+ * style: Style attributes to override
803
+ */
804
+
805
+ function badge( text, className, options = {} )
806
+ {
807
+ const container = document.createElement( "div" );
808
+ container.innerHTML = text;
809
+ container.className = "lexbadge " + ( className ?? "" );
810
+ Object.assign( container.style, options.style ?? {} );
811
+ return container.outerHTML;
812
+ }
813
+
814
+ LX.badge = badge;
815
+
750
816
  /*
751
817
  * Events and Signals
752
818
  */
@@ -833,7 +899,9 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
833
899
  }
834
900
  else
835
901
  {
836
- obj[ signalName ].call( obj, value );
902
+ // This is a function callback!
903
+ const fn = obj;
904
+ fn( null, value );
837
905
  }
838
906
  }
839
907
  }
@@ -2158,18 +2226,18 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2158
2226
  entry.className = "lexmenuentry";
2159
2227
  entry.id = pKey;
2160
2228
  entry.innerHTML = "<span>" + key + "</span>";
2161
- if(options.position == "left") {
2162
- this.root.prepend( entry );
2163
- }
2164
- else {
2165
- if(options.position == "right")
2166
- entry.right = true;
2167
- if(this.root.lastChild && this.root.lastChild.right) {
2168
- this.root.lastChild.before( entry );
2169
- }
2170
- else {
2171
- this.root.appendChild( entry );
2172
- }
2229
+ if(options.position == "left") {
2230
+ this.root.prepend( entry );
2231
+ }
2232
+ else {
2233
+ if(options.position == "right")
2234
+ entry.right = true;
2235
+ if(this.root.lastChild && this.root.lastChild.right) {
2236
+ this.root.lastChild.before( entry );
2237
+ }
2238
+ else {
2239
+ this.root.appendChild( entry );
2240
+ }
2173
2241
  }
2174
2242
 
2175
2243
  const create_submenu = function( o, k, c, d ) {
@@ -2390,16 +2458,16 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2390
2458
  button.style.maxHeight = "calc(100% - 10px)";
2391
2459
  button.style.alignItems = "center";
2392
2460
 
2393
- if(options.float == "right")
2394
- button.right = true;
2395
- if(this.root.lastChild && this.root.lastChild.right) {
2396
- this.root.lastChild.before( button );
2461
+ if(options.float == "right")
2462
+ button.right = true;
2463
+ if(this.root.lastChild && this.root.lastChild.right) {
2464
+ this.root.lastChild.before( button );
2397
2465
  }
2398
2466
  else if(options.float == "left") {
2399
2467
  this.root.prepend(button);
2400
- }
2401
- else {
2402
- this.root.appendChild( button );
2468
+ }
2469
+ else {
2470
+ this.root.appendChild( button );
2403
2471
  }
2404
2472
 
2405
2473
  const _b = button.querySelector('a');
@@ -2431,16 +2499,16 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2431
2499
  button.style.padding = "5px";
2432
2500
  button.style.alignItems = "center";
2433
2501
 
2434
- if(options.float == "right")
2435
- button.right = true;
2436
- if(this.root.lastChild && this.root.lastChild.right) {
2437
- this.root.lastChild.before( button );
2438
- }
2502
+ if(options.float == "right")
2503
+ button.right = true;
2504
+ if(this.root.lastChild && this.root.lastChild.right) {
2505
+ this.root.lastChild.before( button );
2506
+ }
2439
2507
  else if(options.float == "left") {
2440
2508
  this.root.prepend(button);
2441
2509
  }
2442
- else {
2443
- this.root.appendChild( button );
2510
+ else {
2511
+ this.root.appendChild( button );
2444
2512
  }
2445
2513
 
2446
2514
  const _b = button.querySelector('a');
@@ -2460,44 +2528,75 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2460
2528
 
2461
2529
  addButtons( buttons, options = {} ) {
2462
2530
 
2463
- if(!buttons)
2464
- throw("No buttons to add!");
2531
+ if( !buttons )
2532
+ {
2533
+ throw( "No buttons to add!" );
2534
+ }
2465
2535
 
2466
- if(!this.buttonContainer)
2536
+ if( !this.buttonContainer )
2467
2537
  {
2468
- this.buttonContainer = document.createElement('div');
2538
+ this.buttonContainer = document.createElement( "div" );
2469
2539
  this.buttonContainer.className = "lexmenubuttons";
2470
- this.buttonContainer.classList.add(options.float ?? 'center');
2471
- if(options.position == "right")
2472
- this.buttonContainer.right = true;
2473
- if(this.root.lastChild && this.root.lastChild.right) {
2474
- this.root.lastChild.before( this.buttonContainer );
2475
- }
2476
- else {
2477
- this.root.appendChild( this.buttonContainer );
2540
+ this.buttonContainer.classList.add( options.float ?? "center" );
2541
+
2542
+ if( options.position == "right" )
2543
+ {
2544
+ this.buttonContainer.right = true;
2545
+ }
2546
+
2547
+ if( this.root.lastChild && this.root.lastChild.right )
2548
+ {
2549
+ this.root.lastChild.before( this.buttonContainer );
2550
+ }
2551
+ else
2552
+ {
2553
+ this.root.appendChild( this.buttonContainer );
2478
2554
  }
2479
2555
  }
2480
2556
 
2481
2557
  for( let i = 0; i < buttons.length; ++i )
2482
2558
  {
2483
- let data = buttons[i];
2484
- let button = document.createElement('div');
2559
+ let data = buttons[ i ];
2560
+ let button = document.createElement( "label" );
2485
2561
  const title = data.title;
2486
2562
  let disabled = data.disabled ?? false;
2487
2563
  button.className = "lexmenubutton" + (disabled ? " disabled" : "");
2488
2564
  button.title = title ?? "";
2489
- button.innerHTML = "<a class='" + data.icon + " lexicon'></a>";
2490
2565
  this.buttonContainer.appendChild( button );
2491
2566
 
2492
- const _b = button.querySelector('a');
2493
- _b.addEventListener("click", (e) => {
2494
- disabled = e.target.parentElement.classList.contains("disabled");
2495
- if(data.callback && !disabled)
2496
- data.callback.call( this, _b, e );
2567
+ const icon = document.createElement( "a" );
2568
+ icon.className = data.icon + " lexicon";
2569
+ button.appendChild( icon );
2570
+
2571
+ let trigger = icon;
2572
+
2573
+ if( data.swap )
2574
+ {
2575
+ button.classList.add( "swap" );
2576
+ icon.classList.add( "swap-off" );
2577
+
2578
+ const input = document.createElement( "input" );
2579
+ input.type = "checkbox";
2580
+ button.prepend( input );
2581
+ trigger = input;
2582
+
2583
+ const swapIcon = document.createElement( "a" );
2584
+ swapIcon.className = data.swap + " swap-on lexicon";
2585
+ button.appendChild( swapIcon );
2586
+ }
2587
+
2588
+ trigger.addEventListener("click", e => {
2589
+ if( data.callback && !disabled )
2590
+ {
2591
+ const swapInput = button.querySelector( "input" );
2592
+ data.callback.call( this, e, swapInput?.checked );
2593
+ }
2497
2594
  });
2498
2595
 
2499
- if(title)
2596
+ if( title )
2597
+ {
2500
2598
  this.buttons[ title ] = button;
2599
+ }
2501
2600
  }
2502
2601
  }
2503
2602
  };
@@ -2628,27 +2727,30 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2628
2727
  static BUTTON = 3;
2629
2728
  static DROPDOWN = 4;
2630
2729
  static CHECKBOX = 5;
2631
- static COLOR = 6;
2632
- static NUMBER = 7;
2633
- static TITLE = 8;
2634
- static VECTOR = 9;
2635
- static TREE = 10;
2636
- static PROGRESS = 11;
2637
- static FILE = 12;
2638
- static LAYERS = 13;
2639
- static ARRAY = 14;
2640
- static LIST = 15;
2641
- static TAGS = 16;
2642
- static CURVE = 17;
2643
- static CARD = 18;
2644
- static IMAGE = 19;
2645
- static CONTENT = 20;
2646
- static CUSTOM = 21;
2647
- static SEPARATOR = 22;
2648
- static KNOB = 23;
2649
- static SIZE = 24;
2650
- static PAD = 25;
2651
- static FORM = 26;
2730
+ static TOGGLE = 6;
2731
+ static COLOR = 7;
2732
+ static NUMBER = 8;
2733
+ static TITLE = 9;
2734
+ static VECTOR = 10;
2735
+ static TREE = 11;
2736
+ static PROGRESS = 12;
2737
+ static FILE = 13;
2738
+ static LAYERS = 14;
2739
+ static ARRAY = 15;
2740
+ static LIST = 16;
2741
+ static TAGS = 17;
2742
+ static CURVE = 18;
2743
+ static CARD = 19;
2744
+ static IMAGE = 20;
2745
+ static CONTENT = 21;
2746
+ static CUSTOM = 22;
2747
+ static SEPARATOR = 23;
2748
+ static KNOB = 24;
2749
+ static SIZE = 25;
2750
+ static PAD = 26;
2751
+ static FORM = 27;
2752
+ static DIAL = 28;
2753
+ static COUNTER = 29;
2652
2754
 
2653
2755
  static NO_CONTEXT_TYPES = [
2654
2756
  Widget.BUTTON,
@@ -2721,6 +2823,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2721
2823
  case Widget.BUTTON: return "Button";
2722
2824
  case Widget.DROPDOWN: return "Dropdown";
2723
2825
  case Widget.CHECKBOX: return "Checkbox";
2826
+ case Widget.TOGGLE: return "Toggle";
2724
2827
  case Widget.COLOR: return "Color";
2725
2828
  case Widget.NUMBER: return "Number";
2726
2829
  case Widget.VECTOR: return "Vector";
@@ -2736,8 +2839,12 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2736
2839
  case Widget.SIZE: return "Size";
2737
2840
  case Widget.PAD: return "Pad";
2738
2841
  case Widget.FORM: return "Form";
2842
+ case Widget.DIAL: return "Dial";
2843
+ case Widget.COUNTER: return "Counter";
2739
2844
  case Widget.CUSTOM: return this.customName;
2740
2845
  }
2846
+
2847
+ console.error( `Unknown Widget type: ${ this.type }` );
2741
2848
  }
2742
2849
 
2743
2850
  refresh() {
@@ -3909,41 +4016,50 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3909
4016
  /**
3910
4017
  * @method addTitle
3911
4018
  * @param {String} name Title name
4019
+ * @param {*} options:
4020
+ * link: Href in case title is an hyperlink
4021
+ * target: Target name of the iframe (if any)
4022
+ * icon: FA class of the icon (if any)
4023
+ * iconColor: Color of title icon (if any)
4024
+ * style: CSS to override
3912
4025
  */
3913
4026
 
3914
4027
  addTitle( name, options = {} ) {
3915
4028
 
3916
- if(!name) {
3917
- throw("Set Widget Name!");
4029
+ if( !name )
4030
+ {
4031
+ throw( "Can't create Title without text!" );
3918
4032
  }
3919
4033
 
3920
- let widget = this.create_widget(null, Widget.TITLE, options);
4034
+ let widget = this.create_widget( null, Widget.TITLE, options );
3921
4035
  let element = widget.domEl;
3922
4036
  element.className = "lextitle";
3923
4037
 
3924
- if(options.icon) {
3925
- let icon = document.createElement('a');
4038
+ if( options.icon )
4039
+ {
4040
+ let icon = document.createElement( 'a' );
3926
4041
  icon.className = options.icon;
3927
- icon.style.color = options.icon_color || "";
3928
- element.appendChild(icon);
4042
+ icon.style.color = options.iconColor || "";
4043
+ element.appendChild( icon );
3929
4044
  }
3930
4045
 
3931
- let text = document.createElement('span');
4046
+ let text = document.createElement( "span");
3932
4047
  text.innerText = name;
3933
- element.appendChild(text);
4048
+ element.appendChild( text );
3934
4049
 
3935
- Object.assign(element.style, options.style ?? {});
4050
+ Object.assign( element.style, options.style ?? {} );
3936
4051
 
3937
- if(options.link != undefined)
4052
+ if( options.link != undefined )
3938
4053
  {
3939
- let link_el = document.createElement('a');
3940
- link_el.innerText = name;
3941
- link_el.href = options.link;
3942
- link_el.target = options.target ?? "";
3943
- link_el.className = "lextitle link";
3944
- Object.assign(link_el.style, options.style ?? {});
3945
- element.replaceWith(link_el);
4054
+ let linkDom = document.createElement('a');
4055
+ linkDom.innerText = name;
4056
+ linkDom.href = options.link;
4057
+ linkDom.target = options.target ?? "";
4058
+ linkDom.className = "lextitle link";
4059
+ Object.assign( linkDom.style, options.style ?? {} );
4060
+ element.replaceWith( linkDom );
3946
4061
  }
4062
+
3947
4063
  return element;
3948
4064
  }
3949
4065
 
@@ -3954,7 +4070,9 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3954
4070
  * @param {Function} callback Callback function on change
3955
4071
  * @param {*} options:
3956
4072
  * disabled: Make the widget disabled [false]
4073
+ * required: Make the input required
3957
4074
  * placeholder: Add input placeholder
4075
+ * pattern: Regular expression that value must match
3958
4076
  * trigger: Choose onchange trigger (default, input) [default]
3959
4077
  * inputWidth: Width of the text input
3960
4078
  * skipReset: Don't add the reset value button when value changes
@@ -3969,11 +4087,18 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3969
4087
  widget.onGetValue = () => {
3970
4088
  return wValue.value;
3971
4089
  };
4090
+
3972
4091
  widget.onSetValue = ( newValue, skipCallback ) => {
3973
4092
  this.disabled ? wValue.innerText = newValue : wValue.value = newValue;
3974
4093
  Panel._dispatch_event( wValue, "focusout", skipCallback );
3975
4094
  };
3976
4095
 
4096
+ widget.valid = () => {
4097
+ if( wValue.pattern == "" ) { return true; }
4098
+ const regexp = new RegExp( wValue.pattern );
4099
+ return regexp.test( wValue.value );
4100
+ };
4101
+
3977
4102
  let element = widget.domEl;
3978
4103
 
3979
4104
  // Add reset functionality
@@ -3992,6 +4117,11 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3992
4117
  container.style.width = options.inputWidth || "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + " )";
3993
4118
  container.style.display = "flex";
3994
4119
 
4120
+ if( options.textClass )
4121
+ {
4122
+ container.classList.add( options.textClass );
4123
+ }
4124
+
3995
4125
  this.disabled = ( options.disabled || options.warning ) ?? ( options.url ? true : false );
3996
4126
  let wValue = null;
3997
4127
 
@@ -4003,14 +4133,33 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4003
4133
  wValue.style.width = "100%";
4004
4134
  wValue.style.textAlign = options.float ?? "";
4005
4135
 
4006
- if( options.placeholder )
4007
- wValue.setAttribute( "placeholder", options.placeholder );
4136
+ wValue.setAttribute( "placeholder", options.placeholder ?? "" );
4137
+
4138
+ if( options.required )
4139
+ {
4140
+ wValue.setAttribute( "required", options.required );
4141
+ }
4142
+
4143
+ if( options.pattern )
4144
+ {
4145
+ wValue.setAttribute( "pattern", options.pattern );
4146
+ }
4008
4147
 
4009
4148
  var resolve = ( function( val, event ) {
4149
+
4150
+ if( !widget.valid() )
4151
+ {
4152
+ return;
4153
+ }
4154
+
4010
4155
  const skipCallback = event.detail;
4011
4156
  let btn = element.querySelector( ".lexwidgetname .lexicon" );
4012
4157
  if( btn ) btn.style.display = ( val != wValue.iValue ? "block" : "none" );
4013
- if( !skipCallback ) this._trigger( new IEvent( name, val, event ), callback );
4158
+ if( !skipCallback )
4159
+ {
4160
+ this._trigger( new IEvent( name, val, event ), callback );
4161
+ }
4162
+
4014
4163
  }).bind( this );
4015
4164
 
4016
4165
  const trigger = options.trigger ?? 'default';
@@ -4222,18 +4371,13 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4222
4371
 
4223
4372
  var wValue = document.createElement( 'button' );
4224
4373
  wValue.title = options.title ?? "";
4225
- wValue.className = "lexbutton";
4374
+ wValue.className = "lexbutton " + ( options.buttonClass ?? "" );
4226
4375
 
4227
4376
  if( options.selected )
4228
4377
  {
4229
4378
  wValue.classList.add( "selected" );
4230
4379
  }
4231
4380
 
4232
- if( options.buttonClass )
4233
- {
4234
- wValue.classList.add( options.buttonClass );
4235
- }
4236
-
4237
4381
  wValue.innerHTML =
4238
4382
  (options.icon ? "<a class='" + options.icon + "'></a>" :
4239
4383
  ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (value || "") + "</span>" ));
@@ -4457,20 +4601,44 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4457
4601
 
4458
4602
  for( let entry in data )
4459
4603
  {
4460
- const entryData = data[ entry ];
4461
- this.addText( entry, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
4604
+ let entryData = data[ entry ];
4605
+
4606
+ if( entryData.constructor != Object )
4607
+ {
4608
+ entryData = { };
4609
+ }
4610
+
4611
+ entryData.placeholder = entryData.placeholder ?? entry;
4612
+ entryData.width = "calc(100% - 10px)";
4613
+
4614
+ this.addLabel( entry, { textClass: "formlabel" } );
4615
+
4616
+ entryData.textWidget = this.addText( null, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
4462
4617
  container.formData[ entry ] = value;
4463
4618
  }, entryData );
4464
4619
 
4465
4620
  container.formData[ entry ] = entryData.constructor == Object ? entryData.value : entryData;
4466
4621
  }
4467
4622
 
4623
+ this.addBlank( );
4624
+
4468
4625
  this.addButton( null, options.actionName ?? "Submit", ( value, event ) => {
4626
+
4627
+ for( let entry in data )
4628
+ {
4629
+ let entryData = data[ entry ];
4630
+
4631
+ if( !entryData.textWidget.valid() )
4632
+ {
4633
+ return;
4634
+ }
4635
+ }
4636
+
4469
4637
  if( callback )
4470
4638
  {
4471
4639
  callback( container.formData, event );
4472
4640
  }
4473
- } );
4641
+ }, { buttonClass: "primary", width: "calc(100% - 10px)" } );
4474
4642
 
4475
4643
  this.clearQueue();
4476
4644
 
@@ -4486,16 +4654,32 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4486
4654
 
4487
4655
  /**
4488
4656
  * @method addContent
4489
- * @param {HTMLElement} element
4657
+ * @param {HTMLElement/String} element
4490
4658
  */
4491
4659
 
4492
4660
  addContent( element, options = {} ) {
4493
4661
 
4494
4662
  if( !element )
4495
- return;
4663
+ {
4664
+ return;
4665
+ }
4666
+
4667
+ if( element.constructor == String )
4668
+ {
4669
+ const tmp = document.createElement( "div" );
4670
+ tmp.innerHTML = element;
4671
+ if( tmp.childElementCount > 1 )
4672
+ {
4673
+ element = tmp;
4674
+ }
4675
+ else
4676
+ {
4677
+ element = tmp.firstElementChild;
4678
+ }
4679
+ }
4496
4680
 
4497
- let widget = this.create_widget(null, Widget.CONTENT, options);
4498
- widget.domEl.appendChild(element);
4681
+ let widget = this.create_widget( null, Widget.CONTENT, options );
4682
+ widget.domEl.appendChild( element );
4499
4683
  return widget;
4500
4684
  }
4501
4685
 
@@ -4600,7 +4784,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4600
4784
  const iString = String( i );
4601
4785
  maxWidth = Math.max( iString.length, maxWidth );
4602
4786
  }
4603
- return maxWidth * 9;
4787
+ return Math.max( maxWidth * 10, 80 );
4604
4788
  };
4605
4789
 
4606
4790
  let selectedOption = this.addButton( null, buttonName, ( value, event ) => {
@@ -4609,13 +4793,35 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4609
4793
  delete list.unfocus_event;
4610
4794
  return;
4611
4795
  }
4612
- const topPosition = selectedOption.getBoundingClientRect().y;
4613
- list.style.top = (topPosition + selectedOption.offsetHeight) + 'px';
4796
+
4797
+ list.toggleAttribute( "hidden" );
4798
+ list.classList.remove( "place-above" );
4799
+
4800
+ const listHeight = 26 * values.length;
4801
+ const rect = selectedOption.getBoundingClientRect();
4802
+ const topPosition = rect.y;
4803
+
4804
+ let maxY = window.innerHeight;
4805
+
4806
+ if( this.mainContainer )
4807
+ {
4808
+ const parentRect = this.mainContainer.getBoundingClientRect();
4809
+ maxY = parentRect.y + parentRect.height;
4810
+ }
4811
+
4812
+ list.style.top = ( topPosition + selectedOption.offsetHeight ) + 'px';
4813
+
4814
+ const showAbove = ( topPosition + listHeight ) > maxY;
4815
+ if( showAbove )
4816
+ {
4817
+ list.style.top = ( topPosition - listHeight ) + 'px';
4818
+ list.classList.add( "place-above" );
4819
+ }
4820
+
4614
4821
  list.style.width = (event.currentTarget.clientWidth) + 'px';
4615
4822
  list.style.minWidth = (_getMaxListWidth()) + 'px';
4616
- list.toggleAttribute('hidden');
4617
4823
  list.focus();
4618
- }, { buttonClass: 'array', skipInlineCount: true });
4824
+ }, { buttonClass: "array", skipInlineCount: true });
4619
4825
 
4620
4826
  this.clearQueue();
4621
4827
 
@@ -4843,6 +5049,86 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4843
5049
  return widget;
4844
5050
  }
4845
5051
 
5052
+ /**
5053
+ * @method addDial
5054
+ * @param {String} name Widget name
5055
+ * @param {Array of Array} values Array of 2N Arrays of each value of the dial
5056
+ * @param {Function} callback Callback function on change
5057
+ * @param {*} options:
5058
+ * skipReset: Don't add the reset value button when value changes
5059
+ * bgColor: Widget background color
5060
+ * pointsColor: Curve points color
5061
+ * lineColor: Curve line color
5062
+ * noOverlap: Points do not overlap, replacing themselves if necessary
5063
+ * allowAddValues: Support adding values on click
5064
+ * smooth: Curve smoothness
5065
+ * moveOutAction: Clamp or delete points moved out of the curve (LX.CURVE_MOVEOUT_CLAMP, LX.CURVE_MOVEOUT_DELETE)
5066
+ */
5067
+
5068
+ addDial( name, values, callback, options = {} ) {
5069
+
5070
+ let that = this;
5071
+ let widget = this.create_widget(name, Widget.DIAL, options);
5072
+
5073
+ widget.onGetValue = () => {
5074
+ return JSON.parse(JSON.stringify(curveInstance.element.value));
5075
+ };
5076
+
5077
+ widget.onSetValue = ( newValue, skipCallback ) => {
5078
+ let btn = element.querySelector( ".lexwidgetname .lexicon" );
5079
+ if( btn ) btn.style.display = ( newValue != curveInstance.element.value ? "block" : "none" );
5080
+ curveInstance.element.value = JSON.parse( JSON.stringify( newValue ) );
5081
+ curveInstance.redraw();
5082
+ if( !skipCallback ) that._trigger( new IEvent( name, curveInstance.element.value, null ), callback );
5083
+ };
5084
+
5085
+ let element = widget.domEl;
5086
+ let defaultValues = JSON.parse( JSON.stringify( values ) );
5087
+
5088
+ // Add reset functionality
5089
+ if( widget.name && !(options.skipReset ?? false) )
5090
+ {
5091
+ Panel._add_reset_property(element.domName, function(e) {
5092
+ this.style.display = "none";
5093
+ curveInstance.element.value = JSON.parse( JSON.stringify( defaultValues ) );
5094
+ curveInstance.redraw();
5095
+ that._trigger( new IEvent( name, curveInstance.element.value, e ), callback );
5096
+ });
5097
+ }
5098
+
5099
+ // Add widget value
5100
+
5101
+ var container = document.createElement( 'div' );
5102
+ container.className = "lexcurve";
5103
+ container.style.width = widget.name ? "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")" : '100%';
5104
+
5105
+ options.callback = (v, e) => {
5106
+ let btn = element.querySelector(".lexwidgetname .lexicon");
5107
+ if(btn) btn.style.display = (v != defaultValues ? "block" : "none");
5108
+ that._trigger( new IEvent(name, v, e), callback );
5109
+ };
5110
+
5111
+ options.name = name;
5112
+
5113
+ let curveInstance = new Dial( this, values, options );
5114
+ container.appendChild( curveInstance.element );
5115
+ element.appendChild( container );
5116
+
5117
+ // Resize
5118
+ widget.onresize = curveInstance.redraw.bind( curveInstance );
5119
+ widget.curveInstance = curveInstance;
5120
+
5121
+ doAsync(() => {
5122
+ curveInstance.element.style.height = curveInstance.element.offsetWidth + "px";
5123
+ curveInstance.canvas.width = curveInstance.element.offsetWidth;
5124
+ container.style.width = curveInstance.element.offsetWidth + "px";
5125
+ curveInstance.canvas.height = curveInstance.canvas.width;
5126
+ curveInstance.redraw();
5127
+ });
5128
+
5129
+ return widget;
5130
+ }
5131
+
4846
5132
  /**
4847
5133
  * @method addLayers
4848
5134
  * @param {String} name Widget name
@@ -5181,40 +5467,45 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5181
5467
 
5182
5468
  // Show tags
5183
5469
 
5184
- let tags_container = document.createElement('div');
5185
- tags_container.className = "lextags";
5186
- tags_container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
5470
+ const tagsContainer = document.createElement('div');
5471
+ tagsContainer.className = "lextags";
5472
+ tagsContainer.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
5187
5473
 
5188
5474
  const create_tags = () => {
5189
5475
 
5190
- tags_container.innerHTML = "";
5476
+ tagsContainer.innerHTML = "";
5191
5477
 
5192
5478
  for( let i = 0; i < value.length; ++i )
5193
5479
  {
5194
- let tag_name = value[i];
5195
- let tag = document.createElement('span');
5480
+ const tagName = value[i];
5481
+ const tag = document.createElement('span');
5196
5482
  tag.className = "lextag";
5197
- tag.innerHTML = tag_name;
5483
+ tag.innerHTML = tagName;
5484
+
5485
+ const removeButton = document.createElement('a');
5486
+ removeButton.className = "lextagrmb fa-solid fa-xmark lexicon";
5487
+ tag.appendChild( removeButton );
5198
5488
 
5199
- tag.addEventListener('click', function( e ) {
5200
- this.remove();
5201
- value.splice( value.indexOf( tag_name ), 1 );
5489
+ removeButton.addEventListener( 'click', e => {
5490
+ tag.remove();
5491
+ value.splice( value.indexOf( tagName ), 1 );
5202
5492
  let btn = element.querySelector( ".lexwidgetname .lexicon" );
5203
5493
  if( btn ) btn.style.display = ( value != defaultValue ? "block" : "none" );
5204
5494
  that._trigger( new IEvent( name, value, e ), callback );
5205
- });
5495
+ } );
5206
5496
 
5207
- tags_container.appendChild( tag );
5497
+ tagsContainer.appendChild( tag );
5208
5498
  }
5209
5499
 
5210
- let tag_input = document.createElement( 'input' );
5211
- tag_input.value = "";
5212
- tag_input.placeholder = "Tag...";
5213
- tags_container.insertChildAtIndex( tag_input, 0 );
5500
+ let tagInput = document.createElement( 'input' );
5501
+ tagInput.value = "";
5502
+ tagInput.placeholder = "Add tag...";
5503
+ tagsContainer.appendChild( tagInput );
5214
5504
 
5215
- tag_input.onkeydown = function( e ) {
5505
+ tagInput.onkeydown = function( e ) {
5216
5506
  const val = this.value.replace(/\s/g, '');
5217
- if( e.key == ' ') {
5507
+ if( e.key == ' ' || e.key == 'Enter' )
5508
+ {
5218
5509
  e.preventDefault();
5219
5510
  if( !val.length || value.indexOf( val ) > -1 )
5220
5511
  return;
@@ -5226,18 +5517,19 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5226
5517
  }
5227
5518
  };
5228
5519
 
5229
- tag_input.focus();
5520
+ tagInput.focus();
5230
5521
  }
5231
5522
 
5232
5523
  create_tags();
5233
5524
 
5234
5525
  // Remove branch padding and margins
5235
- if(!widget.name) {
5526
+ if( !widget.name )
5527
+ {
5236
5528
  element.className += " noname";
5237
- tags_container.style.width = "100%";
5529
+ tagsContainer.style.width = "100%";
5238
5530
  }
5239
5531
 
5240
- element.appendChild(tags_container);
5532
+ element.appendChild( tagsContainer );
5241
5533
 
5242
5534
  return widget;
5243
5535
  }
@@ -5250,29 +5542,36 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5250
5542
  * @param {*} options:
5251
5543
  * disabled: Make the widget disabled [false]
5252
5544
  * suboptions: Callback to add widgets in case of TRUE value
5545
+ * className: Customize colors
5253
5546
  */
5254
5547
 
5255
5548
  addCheckbox( name, value, callback, options = {} ) {
5256
5549
 
5257
- if( !name ) {
5550
+ if( !name )
5551
+ {
5258
5552
  throw( "Set Widget Name!" );
5259
5553
  }
5260
5554
 
5261
5555
  let widget = this.create_widget( name, Widget.CHECKBOX, options );
5262
5556
 
5263
5557
  widget.onGetValue = () => {
5264
- return flag.value;
5558
+ return checkbox.checked;
5265
5559
  };
5560
+
5266
5561
  widget.onSetValue = ( newValue, skipCallback ) => {
5267
- if( flag.value !== newValue )
5268
- Panel._dispatch_event( toggle, "click", skipCallback );
5562
+ if( checkbox.checked !== newValue )
5563
+ {
5564
+ checkbox.checked = newValue;
5565
+ Panel._dispatch_event( checkbox, "change", skipCallback );
5566
+ }
5269
5567
  };
5270
5568
 
5271
5569
  let element = widget.domEl;
5272
5570
 
5273
5571
  // Add reset functionality
5274
5572
  Panel._add_reset_property( element.domName, function() {
5275
- Panel._dispatch_event( toggle, "click" );
5573
+ checkbox.checked = !checkbox.checked;
5574
+ Panel._dispatch_event( checkbox, "change" );
5276
5575
  });
5277
5576
 
5278
5577
  // Add widget value
@@ -5280,55 +5579,36 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5280
5579
  var container = document.createElement('div');
5281
5580
  container.className = "lexcheckboxcont";
5282
5581
 
5283
- let toggle = document.createElement('span');
5284
- toggle.className = "lexcheckbox";
5582
+ let checkbox = document.createElement('input');
5583
+ checkbox.type = "checkbox";
5584
+ checkbox.className = "lexcheckbox " + ( options.className ?? "" );
5585
+ checkbox.checked = value;
5586
+ checkbox.iValue = value;
5587
+ checkbox.disabled = options.disabled ?? false;
5285
5588
 
5286
- let flag = document.createElement('span');
5287
- flag.value = flag.iValue = value || false;
5288
- flag.className = "checkbox " + (flag.value ? "on" : "");
5289
- flag.id = "checkbox"+simple_guidGenerator();
5290
- flag.innerHTML = "<a class='fa-solid fa-check' style='display: " + (flag.value ? "block" : "none") + "'></a>";
5589
+ let valueName = document.createElement( 'span' );
5590
+ valueName.className = "checkboxtext";
5591
+ valueName.innerHTML = "On";
5291
5592
 
5292
- if( options.disabled ) {
5293
- flag.disabled = true;
5294
- toggle.className += " disabled";
5295
- }
5296
-
5297
- toggle.appendChild( flag );
5298
-
5299
- let value_name = document.createElement( 'span' );
5300
- value_name.id = "checkboxtext";
5301
- value_name.innerHTML = "On";
5302
-
5303
- container.appendChild( toggle );
5304
- container.appendChild( value_name );
5305
-
5306
- toggle.addEventListener( "click" , e => {
5307
-
5308
- let flag = toggle.querySelector( ".checkbox" );
5309
- if( flag.disabled )
5310
- return;
5311
-
5312
- const skipCallback = ( e.detail.constructor == Number ? null : e.detail );
5593
+ container.appendChild( checkbox );
5594
+ container.appendChild( valueName );
5313
5595
 
5314
- let check = toggle.querySelector( ".checkbox a" );
5596
+ checkbox.addEventListener( "change" , e => {
5315
5597
 
5316
- flag.value = !flag.value;
5317
- flag.className = "checkbox " + ( flag.value ? "on" : "" );
5318
- check.style.display = flag.value ? "block" : "none";
5598
+ const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
5319
5599
 
5320
5600
  // Reset button (default value)
5321
5601
  if( !skipCallback )
5322
5602
  {
5323
5603
  let btn = element.querySelector( ".lexwidgetname .lexicon" );
5324
- if( btn ) btn.style.display = flag.value != flag.iValue ? "block": "none";
5604
+ if( btn ) btn.style.display = checkbox.checked != checkbox.iValue ? "block": "none";
5325
5605
  }
5326
5606
 
5327
5607
  // Open suboptions
5328
5608
  let submenu = element.querySelector( ".lexcheckboxsubmenu" );
5329
- if( submenu ) submenu.toggleAttribute( 'hidden', !flag.value );
5609
+ if( submenu ) submenu.toggleAttribute( 'hidden', !checkbox.checked );
5330
5610
 
5331
- if( !skipCallback ) this._trigger( new IEvent( name, flag.value, e ), callback );
5611
+ if( !skipCallback ) this._trigger( new IEvent( name, checkbox.checked, e ), callback );
5332
5612
  });
5333
5613
 
5334
5614
  element.appendChild( container );
@@ -5338,58 +5618,154 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5338
5618
  element.style.flexWrap = "wrap";
5339
5619
  let suboptions = document.createElement('div');
5340
5620
  suboptions.className = "lexcheckboxsubmenu";
5341
- suboptions.toggleAttribute('hidden', !flag.value);
5621
+ suboptions.toggleAttribute( 'hidden', !checkbox.checked );
5342
5622
 
5343
5623
  this.queue( suboptions );
5344
5624
  options.suboptions.call(this, this);
5345
5625
  this.clearQueue();
5346
5626
 
5347
- element.appendChild(suboptions);
5627
+ element.appendChild( suboptions );
5348
5628
  }
5349
5629
 
5350
5630
  return widget;
5351
5631
  }
5352
5632
 
5353
5633
  /**
5354
- * @method addColor
5634
+ * @method addToggle
5355
5635
  * @param {String} name Widget name
5356
- * @param {String} value Default color (hex)
5636
+ * @param {Boolean} value Value of the checkbox
5357
5637
  * @param {Function} callback Callback function on change
5358
5638
  * @param {*} options:
5359
5639
  * disabled: Make the widget disabled [false]
5360
- * useRGB: The callback returns color as Array (r, g, b) and not hex [false]
5640
+ * suboptions: Callback to add widgets in case of TRUE value
5641
+ * className: Customize colors
5361
5642
  */
5362
5643
 
5363
- addColor( name, value, callback, options = {} ) {
5644
+ addToggle( name, value, callback, options = {} ) {
5364
5645
 
5365
- if( !name ) {
5646
+ if( !name )
5647
+ {
5366
5648
  throw( "Set Widget Name!" );
5367
5649
  }
5368
5650
 
5369
- let widget = this.create_widget( name, Widget.COLOR, options );
5651
+ let widget = this.create_widget( name, Widget.TOGGLE, options );
5370
5652
 
5371
5653
  widget.onGetValue = () => {
5372
- return color.value;
5654
+ return toggle.checked;
5373
5655
  };
5656
+
5374
5657
  widget.onSetValue = ( newValue, skipCallback ) => {
5375
- color.value = newValue;
5376
- Panel._dispatch_event( color, "input", skipCallback );
5658
+ if( toggle.checked !== newValue )
5659
+ {
5660
+ toggle.checked = newValue;
5661
+ Panel._dispatch_event( toggle, "change", skipCallback );
5662
+ }
5377
5663
  };
5378
5664
 
5379
5665
  let element = widget.domEl;
5380
- let change_from_input = false;
5381
5666
 
5382
5667
  // Add reset functionality
5383
5668
  Panel._add_reset_property( element.domName, function() {
5384
- this.style.display = "none";
5385
- color.value = color.iValue;
5386
- Panel._dispatch_event( color, "input" );
5669
+ toggle.checked = !toggle.checked;
5670
+ Panel._dispatch_event( toggle, "change" );
5387
5671
  });
5388
5672
 
5389
5673
  // Add widget value
5390
5674
 
5391
- var container = document.createElement( 'span' );
5392
- container.className = "lexcolor";
5675
+ var container = document.createElement('div');
5676
+ container.className = "lextogglecont";
5677
+
5678
+ let toggle = document.createElement('input');
5679
+ toggle.type = "checkbox";
5680
+ toggle.className = "lextoggle " + ( options.className ?? "" );
5681
+ toggle.checked = value;
5682
+ toggle.iValue = value;
5683
+ toggle.disabled = options.disabled ?? false;
5684
+
5685
+ let valueName = document.createElement( 'span' );
5686
+ valueName.className = "toggletext";
5687
+ valueName.innerHTML = "On";
5688
+
5689
+ container.appendChild( toggle );
5690
+ container.appendChild( valueName );
5691
+
5692
+ toggle.addEventListener( "change" , e => {
5693
+
5694
+ const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
5695
+
5696
+ // Reset button (default value)
5697
+ if( !skipCallback )
5698
+ {
5699
+ let btn = element.querySelector( ".lexwidgetname .lexicon" );
5700
+ if( btn ) btn.style.display = toggle.checked != toggle.iValue ? "block": "none";
5701
+ }
5702
+
5703
+ // Open suboptions
5704
+ let submenu = element.querySelector( ".lextogglesubmenu" );
5705
+ if( submenu ) submenu.toggleAttribute( 'hidden', !toggle.checked );
5706
+
5707
+ if( !skipCallback ) this._trigger( new IEvent( name, toggle.checked, e ), callback );
5708
+ });
5709
+
5710
+ element.appendChild( container );
5711
+
5712
+ if( options.suboptions )
5713
+ {
5714
+ element.style.flexWrap = "wrap";
5715
+ let suboptions = document.createElement('div');
5716
+ suboptions.className = "lextogglesubmenu";
5717
+ suboptions.toggleAttribute( 'hidden', !toggle.checked );
5718
+
5719
+ this.queue( suboptions );
5720
+ options.suboptions.call(this, this);
5721
+ this.clearQueue();
5722
+
5723
+ element.appendChild( suboptions );
5724
+ }
5725
+
5726
+ return widget;
5727
+ }
5728
+
5729
+ /**
5730
+ * @method addColor
5731
+ * @param {String} name Widget name
5732
+ * @param {String} value Default color (hex)
5733
+ * @param {Function} callback Callback function on change
5734
+ * @param {*} options:
5735
+ * disabled: Make the widget disabled [false]
5736
+ * useRGB: The callback returns color as Array (r, g, b) and not hex [false]
5737
+ */
5738
+
5739
+ addColor( name, value, callback, options = {} ) {
5740
+
5741
+ if( !name ) {
5742
+ throw( "Set Widget Name!" );
5743
+ }
5744
+
5745
+ let widget = this.create_widget( name, Widget.COLOR, options );
5746
+
5747
+ widget.onGetValue = () => {
5748
+ return color.value;
5749
+ };
5750
+ widget.onSetValue = ( newValue, skipCallback ) => {
5751
+ color.value = newValue;
5752
+ Panel._dispatch_event( color, "input", skipCallback );
5753
+ };
5754
+
5755
+ let element = widget.domEl;
5756
+ let change_from_input = false;
5757
+
5758
+ // Add reset functionality
5759
+ Panel._add_reset_property( element.domName, function() {
5760
+ this.style.display = "none";
5761
+ color.value = color.iValue;
5762
+ Panel._dispatch_event( color, "input" );
5763
+ });
5764
+
5765
+ // Add widget value
5766
+
5767
+ var container = document.createElement( 'span' );
5768
+ container.className = "lexcolor";
5393
5769
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
5394
5770
 
5395
5771
  let color = document.createElement( 'input' );
@@ -5573,7 +5949,14 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5573
5949
  };
5574
5950
  }
5575
5951
 
5576
- // Add wheel input
5952
+ vecinput.addEventListener( "input", function( e ) {
5953
+ let new_value = +this.valueAsNumber;
5954
+ vecinput.value = round( new_value, options.precision );
5955
+ if( options.units )
5956
+ {
5957
+ vecinput.unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
5958
+ }
5959
+ }, false );
5577
5960
 
5578
5961
  vecinput.addEventListener( "wheel", function( e ) {
5579
5962
  e.preventDefault();
@@ -6267,10 +6650,14 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6267
6650
  widget.onGetValue = () => {
6268
6651
  return progress.value;
6269
6652
  };
6653
+
6270
6654
  widget.onSetValue = ( newValue, skipCallback ) => {
6271
6655
  element.querySelector("meter").value = newValue;
6656
+ _updateColor();
6272
6657
  if( element.querySelector("span") )
6658
+ {
6273
6659
  element.querySelector("span").innerText = newValue;
6660
+ }
6274
6661
  };
6275
6662
 
6276
6663
  let element = widget.domEl;
@@ -6287,14 +6674,26 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6287
6674
  progress.step = "any";
6288
6675
  progress.min = options.min ?? 0;
6289
6676
  progress.max = options.max ?? 1;
6677
+ progress.low = options.low ?? progress.low;
6678
+ progress.high = options.high ?? progress.high;
6679
+ progress.optimum = options.optimum ?? progress.optimum;
6290
6680
  progress.value = value;
6291
6681
 
6292
- if( options.low )
6293
- progress.low = options.low;
6294
- if( options.high )
6295
- progress.high = options.high;
6296
- if( options.optimum )
6297
- progress.optimum = options.optimum;
6682
+ const _updateColor = () => {
6683
+
6684
+ let backgroundColor = LX.getThemeColor( "global-selected" );
6685
+
6686
+ if( progress.low != undefined && progress.value < progress.low )
6687
+ {
6688
+ backgroundColor = LX.getThemeColor( "global-color-error" );
6689
+ }
6690
+ else if( progress.high != undefined && progress.value < progress.high )
6691
+ {
6692
+ backgroundColor = LX.getThemeColor( "global-color-warning" );
6693
+ }
6694
+
6695
+ progress.style.background = `color-mix(in oklab, ${backgroundColor} 20%, transparent)`;
6696
+ };
6298
6697
 
6299
6698
  container.appendChild( progress );
6300
6699
  element.appendChild( container );
@@ -6318,7 +6717,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6318
6717
  progress.classList.add( "editable" );
6319
6718
  progress.addEventListener( "mousedown", inner_mousedown );
6320
6719
 
6321
- var that = this;
6720
+ const that = this;
6322
6721
 
6323
6722
  function inner_mousedown( e )
6324
6723
  {
@@ -6326,24 +6725,28 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6326
6725
  doc.addEventListener( 'mousemove', inner_mousemove );
6327
6726
  doc.addEventListener( 'mouseup', inner_mouseup );
6328
6727
  document.body.classList.add( 'noevents' );
6728
+ progress.classList.add( "grabbing" );
6329
6729
  e.stopImmediatePropagation();
6330
6730
  e.stopPropagation();
6731
+
6732
+ const rect = progress.getBoundingClientRect();
6733
+ const newValue = round( remapRange( e.offsetX, 0, rect.width, progress.min, progress.max ) );
6734
+ that.setValue( name, newValue );
6331
6735
  }
6332
6736
 
6333
6737
  function inner_mousemove( e )
6334
6738
  {
6335
- let dt = -e.movementX;
6739
+ let dt = e.movementX;
6336
6740
 
6337
6741
  if ( dt != 0 )
6338
6742
  {
6339
- let v = that.getValue( name, value );
6340
- v += e.movementX / 100;
6341
- v = round( v );
6342
- that.setValue( name, v );
6743
+ const rect = progress.getBoundingClientRect();
6744
+ const newValue = round( remapRange( e.offsetX - rect.x, 0, rect.width, progress.min, progress.max ) );
6745
+ that.setValue( name, newValue );
6343
6746
 
6344
6747
  if( options.callback )
6345
6748
  {
6346
- options.callback( v, e );
6749
+ options.callback( newValue, e );
6347
6750
  }
6348
6751
  }
6349
6752
 
@@ -6357,9 +6760,12 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6357
6760
  doc.removeEventListener( 'mousemove', inner_mousemove );
6358
6761
  doc.removeEventListener( 'mouseup', inner_mouseup );
6359
6762
  document.body.classList.remove( 'noevents' );
6763
+ progress.classList.remove( "grabbing" );
6360
6764
  }
6361
6765
  }
6362
6766
 
6767
+ _updateColor();
6768
+
6363
6769
  return widget;
6364
6770
  }
6365
6771
 
@@ -6369,6 +6775,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6369
6775
  * @param {Function} callback Callback function on change
6370
6776
  * @param {*} options:
6371
6777
  * local: Ask for local file
6778
+ * disabled: Make the widget disabled [false]
6372
6779
  * read: Return the file itself (False) or the contents (True)
6373
6780
  * type: type to read as [text (Default), buffer, bin, url]
6374
6781
  */
@@ -6389,8 +6796,10 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6389
6796
 
6390
6797
  // Create hidden input
6391
6798
  let input = document.createElement( 'input' );
6799
+ input.className = "lexfileinput";
6392
6800
  input.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + " - 10%)";
6393
6801
  input.type = 'file';
6802
+ input.disabled = options.disabled ?? false;
6394
6803
 
6395
6804
  if( options.placeholder )
6396
6805
  {
@@ -6636,6 +7045,91 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6636
7045
 
6637
7046
  this.addSeparator();
6638
7047
  }
7048
+
7049
+ /**
7050
+ * @method addCounter
7051
+ * @param {String} name Widget name
7052
+ * @param {Number} value Counter value
7053
+ * @param {Function} callback Callback function on change
7054
+ * @param {*} options:
7055
+ * disabled: Make the widget disabled [false]
7056
+ * min, max: Min and Max values
7057
+ * step: Step for adding/substracting
7058
+ * label: Text to show below the counter
7059
+ */
7060
+
7061
+ addCounter( name, value, callback, options = { } ) {
7062
+
7063
+ let widget = this.create_widget( name, Widget.COUNTER, options );
7064
+
7065
+ widget.onGetValue = () => {
7066
+ return counterText.count;
7067
+ };
7068
+
7069
+ widget.onSetValue = ( newValue, skipCallback ) => {
7070
+ _onChange( newValue, skipCallback );
7071
+ };
7072
+
7073
+ let element = widget.domEl;
7074
+
7075
+ const min = options.min ?? 0;
7076
+ const max = options.max ?? 100;
7077
+ const step = options.step ?? 1;
7078
+
7079
+ const _onChange = ( value, skipCallback, event ) => {
7080
+ value = clamp( value, min, max );
7081
+ counterText.count = value;
7082
+ counterText.innerHTML = value;
7083
+ if( !skipCallback )
7084
+ {
7085
+ this._trigger( new IEvent( name, value, event ), callback );
7086
+ }
7087
+ }
7088
+
7089
+ const container = document.createElement( 'div' );
7090
+ container.className = "lexcounter";
7091
+ element.appendChild( container );
7092
+
7093
+ this.queue( container );
7094
+
7095
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-minus'></a>", (value, e) => {
7096
+ let mult = step ?? 1;
7097
+ if( e.shiftKey ) mult *= 10;
7098
+ _onChange( counterText.count - mult, false, e );
7099
+ }, { className: "micro", skipInlineCount: true, title: "Minus" });
7100
+
7101
+ this.clearQueue();
7102
+
7103
+ const containerBox = document.createElement( 'div' );
7104
+ containerBox.className = "lexcounterbox";
7105
+ container.appendChild( containerBox );
7106
+
7107
+ const counterText = document.createElement( 'span' );
7108
+ counterText.className = "lexcountervalue";
7109
+ counterText.innerHTML = value;
7110
+ counterText.count = value;
7111
+ containerBox.appendChild( counterText );
7112
+
7113
+ if( options.label )
7114
+ {
7115
+ const counterLabel = document.createElement( 'span' );
7116
+ counterLabel.className = "lexcounterlabel";
7117
+ counterLabel.innerHTML = options.label;
7118
+ containerBox.appendChild( counterLabel );
7119
+ }
7120
+
7121
+ this.queue( container );
7122
+
7123
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-plus'></a>", (value, e) => {
7124
+ let mult = step ?? 1;
7125
+ if( e.shiftKey ) mult *= 10;
7126
+ _onChange( counterText.count + mult, false, e );
7127
+ }, { className: "micro", skipInlineCount: true, title: "Plus" });
7128
+
7129
+ this.clearQueue();
7130
+
7131
+ return widget;
7132
+ }
6639
7133
  }
6640
7134
 
6641
7135
  LX.Panel = Panel;
@@ -6871,6 +7365,104 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6871
7365
 
6872
7366
  LX.Branch = Branch;
6873
7367
 
7368
+ /**
7369
+ * @class Footer
7370
+ */
7371
+
7372
+ class Footer {
7373
+ /**
7374
+ * @param {*} options:
7375
+ * columns: Array with data per column { title, items: [ { title, link } ] }
7376
+ * credits: html string
7377
+ * socials: Array with data per item { title, link, iconHtml }
7378
+ */
7379
+ constructor( options = {} ) {
7380
+
7381
+ const root = document.createElement( "footer" );
7382
+ root.className = "lexfooter";
7383
+
7384
+ const wrapper = document.createElement( "div" );
7385
+ wrapper.className = "wrapper";
7386
+ root.appendChild( wrapper );
7387
+
7388
+ if( options.columns && options.columns.constructor == Array )
7389
+ {
7390
+ const cols = document.createElement( "div" );
7391
+ cols.className = "columns";
7392
+ cols.style.gridTemplateColumns = "1fr ".repeat( options.columns.length );
7393
+ wrapper.appendChild( cols );
7394
+
7395
+ for( let col of options.columns )
7396
+ {
7397
+ const colDom = document.createElement( "div" );
7398
+ colDom.className = "col";
7399
+ cols.appendChild( colDom );
7400
+
7401
+ const colTitle = document.createElement( "h2" );
7402
+ colTitle.innerHTML = col.title;
7403
+ colDom.appendChild( colTitle );
7404
+
7405
+ if( !col.items || !col.items.length )
7406
+ {
7407
+ continue;
7408
+ }
7409
+
7410
+ const itemListDom = document.createElement( "ul" );
7411
+ colDom.appendChild( itemListDom );
7412
+
7413
+ for( let item of col.items )
7414
+ {
7415
+ const itemDom = document.createElement( "li" );
7416
+ itemDom.innerHTML = `<a class="" href="${ item.link }">${ item.title }</a>`;
7417
+ itemListDom.appendChild( itemDom );
7418
+ }
7419
+ }
7420
+ }
7421
+
7422
+ if( options.credits || options.socials )
7423
+ {
7424
+ const hr = document.createElement( "hr" );
7425
+ wrapper.appendChild( hr );
7426
+
7427
+ const creditsSocials = document.createElement( "div" );
7428
+ creditsSocials.className = "credits-and-socials";
7429
+ wrapper.appendChild( creditsSocials );
7430
+
7431
+ if( options.credits )
7432
+ {
7433
+ const credits = document.createElement( "p" );
7434
+ credits.innerHTML = options.credits;
7435
+ creditsSocials.appendChild( credits );
7436
+ }
7437
+
7438
+ if( options.socials )
7439
+ {
7440
+ const socials = document.createElement( "div" );
7441
+ socials.className = "social";
7442
+
7443
+ for( let social of options.socials )
7444
+ {
7445
+ const itemDom = document.createElement( "a" );
7446
+ itemDom.title = social.title;
7447
+ itemDom.innerHTML = social.icon;
7448
+ itemDom.href = social.link;
7449
+ itemDom.target = "_blank";
7450
+ socials.appendChild( itemDom );
7451
+ }
7452
+
7453
+ creditsSocials.appendChild( socials );
7454
+ }
7455
+ }
7456
+
7457
+ // Append directly to body
7458
+ const parent = options.parent ?? document.body;
7459
+ parent.appendChild( root );
7460
+ }
7461
+
7462
+ }
7463
+
7464
+ LX.Footer = Footer;
7465
+
6874
7466
  /**
6875
7467
  * @class Dialog
6876
7468
  */
@@ -7203,8 +7795,8 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
7203
7795
 
7204
7796
  this.root = document.createElement('div');
7205
7797
  this.root.className = "lexcontextmenubox";
7206
- this.root.style.left = (event.x - 48) + "px";
7207
- this.root.style.top = (event.y - 8) + "px";
7798
+ this.root.style.left = (event.x - 48 + document.scrollingElement.scrollLeft) + "px";
7799
+ this.root.style.top = (event.y - 8 + document.scrollingElement.scrollTop) + "px";
7208
7800
 
7209
7801
  this.root.addEventListener("mouseleave", function() {
7210
7802
  this.remove();
@@ -7493,7 +8085,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
7493
8085
  element.style.minWidth = "50px";
7494
8086
  element.style.minHeight = "20px";
7495
8087
 
7496
- element.bgcolor = options.bgColor || LX.getThemeColor( "global-dark-background" );
8088
+ element.bgcolor = options.bgColor || LX.getThemeColor( "global-intense-background" );
7497
8089
  element.pointscolor = options.pointsColor || LX.getThemeColor( "global-selected-light" );
7498
8090
  element.linecolor = options.lineColor || "#555";
7499
8091
  element.value = value || [];
@@ -7508,6 +8100,12 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
7508
8100
  element.smooth = (options.smooth && typeof( options.smooth ) == 'number' ? options.smooth : 0.3) || false;
7509
8101
  element.move_out = options.moveOutAction ?? LX.CURVE_MOVEOUT_DELETE;
7510
8102
 
8103
+ LX.addSignal( "@on_new_color_scheme", (el, value) => {
8104
+ element.bgcolor = options.bgColor || LX.getThemeColor( "global-intense-background" );
8105
+ element.pointscolor = options.pointsColor || LX.getThemeColor( "global-selected-light" );
8106
+ this.redraw();
8107
+ } );
8108
+
7511
8109
  this.element = element;
7512
8110
 
7513
8111
  let canvas = document.createElement( "canvas" );
@@ -7811,6 +8409,335 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
7811
8409
 
7812
8410
  LX.Curve = Curve;
7813
8411
 
8412
+ /**
8413
+ * @class Dial
8414
+ */
8415
+
8416
+ class Dial {
8417
+
8418
+ constructor( panel, value, options = {} ) {
8419
+
8420
+ let element = document.createElement( "div" );
8421
+ element.className = "dial " + ( options.className ? options.className : "" );
8422
+ element.style.width = element.style.height = options.size || "100%";
8423
+ element.style.minWidth = element.style.minHeight = "50px";
8424
+
8425
+ element.bgcolor = options.bgColor || LX.getThemeColor( "global-dark-background" );
8426
+ element.pointscolor = options.pointsColor || LX.getThemeColor( "global-selected-light" );
8427
+ element.linecolor = options.lineColor || "#555";
8428
+ element.value = value || [];
8429
+ element.xrange = options.xrange || [ 0, 1 ]; // min, max
8430
+ element.yrange = options.yrange || [ 0, 1 ]; // min, max
8431
+ element.defaulty = options.defaulty != null ? options.defaulty : 0.0;
8432
+ element.no_overlap = options.noOverlap || false;
8433
+ element.show_samples = options.showSamples || 0;
8434
+ element.allow_add_values = options.allowAddValues ?? true;
8435
+ element.draggable_x = options.draggableX ?? true;
8436
+ element.draggable_y = options.draggableY ?? true;
8437
+ element.smooth = (options.smooth && typeof( options.smooth ) == 'number' ? options.smooth : 0.3) || false;
8438
+ element.move_out = options.moveOutAction ?? LX.CURVE_MOVEOUT_DELETE;
8439
+
8440
+ this.element = element;
8441
+
8442
+ let canvas = document.createElement( "canvas" );
8443
+ canvas.width = canvas.height = options.size || 200;
8444
+ element.appendChild( canvas );
8445
+ this.canvas = canvas;
8446
+
8447
+ element.addEventListener( "mousedown", onmousedown );
8448
+
8449
+ element.getValueAt = function( x ) {
8450
+
8451
+ if( x < element.xrange[ 0 ] || x > element.xrange[ 1 ] )
8452
+ {
8453
+ return element.defaulty;
8454
+ }
8455
+
8456
+ var last = [ element.xrange[ 0 ], element.defaulty ];
8457
+ var f = 0;
8458
+ for( var i = 0; i < element.value.length; i += 1 )
8459
+ {
8460
+ var v = element.value[ i ];
8461
+ if( x == v[ 0 ] ) return v[ 1 ];
8462
+ if( x < v[ 0 ] )
8463
+ {
8464
+ f = ( x - last[ 0 ] ) / (v[ 0 ] - last[ 0 ]);
8465
+ return last[ 1 ] * ( 1 - f ) + v[ 1 ] * f;
8466
+ }
8467
+
8468
+ last = v;
8469
+ }
8470
+
8471
+ v = [ element.xrange[ 1 ], element.defaulty ];
8472
+ f = (x - last[ 0 ]) / (v[ 0 ] - last[ 0 ]);
8473
+ return last[ 1 ] * ( 1 - f ) + v[ 1 ] * f;
8474
+ }
8475
+
8476
+ element.resample = function( samples ) {
8477
+
8478
+ var r = [];
8479
+ var dx = (element.xrange[1] - element.xrange[ 0 ]) / samples;
8480
+ for(var i = element.xrange[0]; i <= element.xrange[1]; i += dx)
8481
+ {
8482
+ r.push( element.getValueAt(i) );
8483
+ }
8484
+ return r;
8485
+ }
8486
+
8487
+ element.addValue = function(v) {
8488
+
8489
+ for(var i = 0; i < element.value; i++) {
8490
+ var value = element.value[i];
8491
+ if(value[0] < v[0]) continue;
8492
+ element.value.splice(i,0,v);
8493
+ redraw();
8494
+ return;
8495
+ }
8496
+
8497
+ element.value.push(v);
8498
+ redraw();
8499
+ }
8500
+
8501
+ //value to canvas
8502
+ function convert(v, r) {
8503
+
8504
+ Math.pow(v[0],2)
8505
+ return [ canvas.width * ( v[0] - element.xrange[0])/ (element.xrange[1]),
8506
+ canvas.height * (v[1] - element.yrange[0])/ (element.yrange[1])];
8507
+ }
8508
+
8509
+ //canvas to value
8510
+ function unconvert(v) {
8511
+ return [(v[0] * element.xrange[1] / canvas.width + element.xrange[0]),
8512
+ (v[1] * element.yrange[1] / canvas.height + element.yrange[0])];
8513
+ }
8514
+
8515
+ var selected = -1;
8516
+
8517
+ element.redraw = function( o = {} ) {
8518
+
8519
+ if( o.value ) element.value = o.value;
8520
+ if( o.xrange ) element.xrange = o.xrange;
8521
+ if( o.yrange ) element.yrange = o.yrange;
8522
+ if( o.smooth ) element.smooth = o.smooth;
8523
+ var rect = canvas.parentElement.getBoundingClientRect();
8524
+ if( canvas.parentElement.parentElement ) rect = canvas.parentElement.parentElement.getBoundingClientRect();
8525
+ if( rect && canvas.width != rect.width && rect.width && rect.width < 1000 )
8526
+ {
8527
+ canvas.width = rect.width;
8528
+ }
8529
+
8530
+ var ctx = canvas.getContext( "2d" );
8531
+ ctx.setTransform( 1, 0, 0, 1, 0, 0 );
8532
+ ctx.translate( 0, canvas.height );
8533
+ ctx.scale( 1, -1 );
8534
+
8535
+ ctx.fillStyle = element.bgcolor;
8536
+ ctx.fillRect(0,0,canvas.width,canvas.height);
8537
+
8538
+ ctx.strokeStyle = element.linecolor;
8539
+ ctx.beginPath();
8540
+
8541
+ //draw line
8542
+ var pos = convert([ element.xrange[ 0 ],element.defaulty ]);
8543
+ ctx.moveTo( pos[ 0 ], pos[ 1 ] );
8544
+ let values = [pos[ 0 ], pos[ 1 ]];
8545
+
8546
+ for(var i in element.value) {
8547
+ var value = element.value[i];
8548
+ pos = convert(value);
8549
+ values.push(pos[ 0 ]);
8550
+ values.push(pos[ 1 ]);
8551
+
8552
+ }
8553
+
8554
+ pos = convert([ element.xrange[ 1 ], element.defaulty ]);
8555
+ values.push(pos[ 0 ]);
8556
+ values.push(pos[ 1 ]);
8557
+
8558
+ // Draw points
8559
+ const center = [0,0];
8560
+ pos = convert(center)
8561
+ ctx.fillStyle = "gray";
8562
+ ctx.beginPath();
8563
+ ctx.arc( pos[ 0 ], pos[ 1 ], 3, 0, Math.PI * 2);
8564
+ ctx.fill();
8565
+
8566
+ for( var i = 0; i < element.value.length; i += 1 ) {
8567
+ var value = element.value[ i ];
8568
+ pos = convert( value );
8569
+ if( selected == i )
8570
+ ctx.fillStyle = "white";
8571
+ else
8572
+ ctx.fillStyle = element.pointscolor;
8573
+ ctx.beginPath();
8574
+ ctx.arc( pos[ 0 ], pos[ 1 ], selected == i ? 4 : 3, 0, Math.PI * 2);
8575
+ ctx.fill();
8576
+ }
8577
+
8578
+ if(element.show_samples) {
8579
+ var samples = element.resample(element.show_samples);
8580
+ ctx.fillStyle = "#888";
8581
+ for(var i = 0; i < samples.length; i += 1)
8582
+ {
8583
+ var value = [ i * ((element.xrange[ 1 ] - element.xrange[ 0 ]) / element.show_samples) + element.xrange[ 0 ], samples[ i ] ];
8584
+ pos = convert(value);
8585
+ ctx.beginPath();
8586
+ ctx.arc( pos[ 0 ], pos[ 1 ], 2, 0, Math.PI * 2);
8587
+ ctx.fill();
8588
+ }
8589
+ }
8590
+ }
8591
+
8592
+ var last_mouse = [ 0, 0 ];
8593
+
8594
+ function onmousedown( e ) {
8595
+ document.addEventListener( "mousemove", onmousemove );
8596
+ document.addEventListener( "mouseup", onmouseup );
8597
+
8598
+ var rect = canvas.getBoundingClientRect();
8599
+ var mousex = e.clientX - rect.left;
8600
+ var mousey = e.clientY - rect.top;
8601
+
8602
+ selected = computeSelected( mousex, canvas.height - mousey );
8603
+
8604
+ if( e.button == LX.MOUSE_LEFT_CLICK && selected == -1 && element.allow_add_values ) {
8605
+ var v = unconvert([ mousex, canvas.height - mousey ]);
8606
+ element.value.push( v );
8607
+ sortValues();
8608
+ selected = element.value.indexOf( v );
8609
+ }
8610
+
8611
+ last_mouse = [ mousex, mousey ];
8612
+ element.redraw();
8613
+ e.preventDefault();
8614
+ e.stopPropagation();
8615
+ }
8616
+
8617
+ function onmousemove( e ) {
8618
+
8619
+ var rect = canvas.getBoundingClientRect();
8620
+ var mousex = e.clientX - rect.left;
8621
+ var mousey = e.clientY - rect.top;
8622
+
8623
+ if( mousex < 0 ) mousex = 0;
8624
+ else if( mousex > canvas.width ) mousex = canvas.width;
8625
+ if( mousey < 0 ) mousey = 0;
8626
+ else if( mousey > canvas.height ) mousey = canvas.height;
8627
+
8628
+ // Dragging to remove
8629
+ const currentMouseDiff = [ e.clientX - rect.left, e.clientY - rect.top ];
8630
+ if( selected != -1 && distance( currentMouseDiff, [ mousex, mousey ] ) > canvas.height * 0.5 )
8631
+ {
8632
+ if( element.move_out == LX.CURVE_MOVEOUT_DELETE)
8633
+ {
8634
+ element.value.splice( selected, 1 );
8635
+ }
8636
+ else
8637
+ {
8638
+ const d = [ currentMouseDiff[ 0 ] - mousex, currentMouseDiff[ 1 ] - mousey ];
8639
+ let value = element.value[ selected ];
8640
+ value[ 0 ] = ( d[ 0 ] == 0.0 ) ? value[ 0 ] : ( d[ 0 ] < 0.0 ? element.xrange[ 0 ] : element.xrange[ 1 ] );
8641
+ value[ 1 ] = ( d[ 1 ] == 0.0 ) ? value[ 1 ] : ( d[ 1 ] < 0.0 ? element.yrange[ 1 ] : element.yrange[ 0 ] );
8642
+ }
8643
+
8644
+ onmouseup( e );
8645
+ return;
8646
+ }
8647
+
8648
+ var dx = element.draggable_x ? last_mouse[ 0 ] - mousex : 0;
8649
+ var dy = element.draggable_y ? last_mouse[ 1 ] - mousey : 0;
8650
+ var delta = unconvert([ -dx, dy ]);
8651
+
8652
+ if( selected != -1 ) {
8653
+ var minx = element.xrange[ 0 ];
8654
+ var maxx = element.xrange[ 1 ];
8655
+
8656
+ if( element.no_overlap )
8657
+ {
8658
+ if( selected > 0) minx = element.value[ selected - 1 ][ 0 ];
8659
+ if( selected < ( element.value.length - 1 ) ) maxx = element.value[ selected + 1 ][ 0 ];
8660
+ }
8661
+
8662
+ var v = element.value[selected];
8663
+ v[ 0 ] += delta[ 0 ];
8664
+ v[ 1 ] += delta[ 1 ];
8665
+ if(v[ 0 ] < minx) v[ 0 ] = minx;
8666
+ else if(v[ 0 ] > maxx) v[ 0 ] = maxx;
8667
+ if(v[ 1 ] < element.yrange[ 0 ]) v[ 1 ] = element.yrange[ 0 ];
8668
+ else if(v[ 1 ] > element.yrange[ 1 ]) v[ 1 ] = element.yrange[ 1 ];
8669
+ }
8670
+
8671
+ sortValues();
8672
+ element.redraw();
8673
+ last_mouse[ 0 ] = mousex;
8674
+ last_mouse[ 1 ] = mousey;
8675
+ onchange( e );
8676
+
8677
+ e.preventDefault();
8678
+ e.stopPropagation();
8679
+ }
8680
+
8681
+ function onmouseup( e ) {
8682
+ selected = -1;
8683
+ element.redraw();
8684
+ document.removeEventListener("mousemove", onmousemove);
8685
+ document.removeEventListener("mouseup", onmouseup);
8686
+ onchange(e);
8687
+ e.preventDefault();
8688
+ e.stopPropagation();
8689
+ }
8690
+
8691
+ function onchange( e ) {
8692
+ if( options.callback )
8693
+ options.callback.call( element, element.value, e );
8694
+ }
8695
+
8696
+ function distance(a,b) { return Math.sqrt( Math.pow(b[0]-a[0],2) + Math.pow(b[1]-a[1],2) ); };
8697
+
8698
+ function computeSelected( x, y ) {
8699
+
8700
+ var minDistance = 100000;
8701
+ var maxDistance = 8; //pixels
8702
+ var selected = -1;
8703
+ for( var i = 0; i < element.value.length; i++ )
8704
+ {
8705
+ var value = element.value[ i ];
8706
+ var pos = convert( value );
8707
+ var dist = distance( [ x,y ], pos );
8708
+ if( dist < minDistance && dist < maxDistance )
8709
+ {
8710
+ minDistance = dist;
8711
+ selected = i;
8712
+ }
8713
+ }
8714
+ return selected;
8715
+ }
8716
+
8717
+ function sortValues() {
8718
+ var v = null;
8719
+ if( selected != -1 )
8720
+ {
8721
+ v = element.value[ selected ];
8722
+ }
8723
+ element.value.sort(function( a,b ) { return a[ 0 ] - b[ 0 ]; });
8724
+ if( v )
8725
+ {
8726
+ selected = element.value.indexOf( v );
8727
+ }
8728
+ }
8729
+
8730
+ element.redraw();
8731
+ return this;
8732
+ }
8733
+
8734
+ redraw( options = {} ) {
8735
+ this.element.redraw( options );
8736
+ }
8737
+ }
8738
+
8739
+ LX.Dial = Dial;
8740
+
7814
8741
  class AssetViewEvent {
7815
8742
 
7816
8743
  static NONE = 0;
@@ -7886,6 +8813,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
7886
8813
  this.skipPreview = options.skipPreview ?? false;
7887
8814
  this.useNativeTitle = options.useNativeTitle ?? false;
7888
8815
  this.onlyFolders = options.onlyFolders ?? true;
8816
+ this.allowMultipleSelection = options.allowMultipleSelection ?? false;
7889
8817
  this.previewActions = options.previewActions ?? [];
7890
8818
  this.contextMenu = options.contextMenu ?? [];
7891
8819
  this.onRefreshContent = options.onRefreshContent;
@@ -8284,15 +9212,13 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
8284
9212
  itemEl.title = type + ": " + item.id;
8285
9213
  }
8286
9214
 
8287
- if( item.selected != undefined )
9215
+ if( that.allowMultipleSelection )
8288
9216
  {
8289
- let span = document.createElement('span');
8290
- span.className = "lexcheckbox";
8291
- let checkbox_input = document.createElement('input');
8292
- checkbox_input.type = "checkbox";
8293
- checkbox_input.className = "checkbox";
8294
- checkbox_input.checked = item.selected;
8295
- checkbox_input.addEventListener('change', ( e, v ) => {
9217
+ let checkbox = document.createElement( 'input' );
9218
+ checkbox.type = "checkbox";
9219
+ checkbox.className = "lexcheckbox";
9220
+ checkbox.checked = item.selected;
9221
+ checkbox.addEventListener('change', ( e, v ) => {
8296
9222
  item.selected = !item.selected;
8297
9223
  if( that.onevent )
8298
9224
  {
@@ -8302,10 +9228,9 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
8302
9228
  }
8303
9229
  e.stopPropagation();
8304
9230
  e.stopImmediatePropagation();
8305
- })
8306
- span.appendChild(checkbox_input);
8307
- itemEl.appendChild(span);
9231
+ });
8308
9232
 
9233
+ itemEl.appendChild( checkbox );
8309
9234
  }
8310
9235
 
8311
9236
  let title = document.createElement('span');
@@ -8371,6 +9296,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
8371
9296
  }
8372
9297
 
8373
9298
  this.classList.add('selected');
9299
+ that.selectedItem = item;
8374
9300
 
8375
9301
  if( !that.skipPreview )
8376
9302
  {
@@ -8682,7 +9608,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
8682
9608
  //request.mimeType = "text/plain; charset=x-user-defined";
8683
9609
  dataType = "arraybuffer";
8684
9610
  request.mimeType = "application/octet-stream";
8685
- }
9611
+ }
8686
9612
 
8687
9613
  //regular case, use AJAX call
8688
9614
  var xhr = new XMLHttpRequest();