lexgui 0.1.44 → 0.1.46

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.44",
15
+ version: "0.1.46",
16
16
  ready: false,
17
17
  components: [], // specific pre-build components
18
18
  signals: {} // events and triggers
@@ -36,34 +36,118 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
36
36
  LX.round = round;
37
37
  LX.remapRange = remapRange;
38
38
 
39
- function getSupportedDOMName( string )
39
+ // Timer that works everywhere (from litegraph.js)
40
+ if ( typeof performance != "undefined" )
41
+ {
42
+ LX.getTime = performance.now.bind( performance );
43
+ }
44
+ else if( typeof Date != "undefined" && Date.now )
45
+ {
46
+ LX.getTime = Date.now.bind( Date );
47
+ }
48
+ else if ( typeof process != "undefined" )
49
+ {
50
+ LX.getTime = function() {
51
+ var t = process.hrtime();
52
+ return t[ 0 ] * 0.001 + t[ 1 ] * 1e-6;
53
+ };
54
+ }
55
+ else
56
+ {
57
+ LX.getTime = function() {
58
+ return new Date().getTime();
59
+ };
60
+ }
61
+
62
+ let ASYNC_ENABLED = true;
63
+
64
+ /**
65
+ * @method doAsync
66
+ * @description Call a function asynchronously
67
+ * @param {Function} fn Function to call
68
+ * @param {Number} ms Time to wait until calling the function (in milliseconds)
69
+ */
70
+ function doAsync( fn, ms ) {
71
+ if( ASYNC_ENABLED )
72
+ {
73
+ setTimeout( fn, ms ?? 0 );
74
+ }
75
+ else
76
+ {
77
+ fn();
78
+ }
79
+ }
80
+
81
+ LX.doAsync = doAsync;
82
+
83
+ /**
84
+ * @method getSupportedDOMName
85
+ * @description Convert a text string to a valid DOM name
86
+ * @param {String} text Original text
87
+ */
88
+ function getSupportedDOMName( text )
40
89
  {
41
- return string.replace(/\s/g, '').replaceAll('@', '_').replaceAll('+', '_plus_').replaceAll('.', '');
90
+ return text.replace(/\s/g, '').replaceAll('@', '_').replaceAll('+', '_plus_').replaceAll('.', '');
42
91
  }
43
92
 
44
93
  LX.getSupportedDOMName = getSupportedDOMName;
45
94
 
46
- function has( component_name )
95
+ /**
96
+ * @method has
97
+ * @description Ask if LexGUI is using a specific component
98
+ * @param {String} componentName Name of the LexGUI component
99
+ */
100
+ function has( componentName )
47
101
  {
48
- return (LX.components.indexOf( component_name ) > -1);
102
+ return ( LX.components.indexOf( componentName ) > -1 );
49
103
  }
50
104
 
51
105
  LX.has = has;
52
106
 
53
- function getExtension( s )
107
+ /**
108
+ * @method getExtension
109
+ * @description Get a extension from a path/url/filename
110
+ * @param {String} name
111
+ */
112
+ function getExtension( name )
54
113
  {
55
- return s.includes('.') ? s.split('.').pop() : null;
114
+ return name.includes('.') ? name.split('.').pop() : null;
56
115
  }
57
116
 
58
117
  LX.getExtension = getExtension;
59
118
 
60
- function deepCopy( o )
119
+ /**
120
+ * @method deepCopy
121
+ * @description Create a deep copy with no references from an object
122
+ * @param {Object} obj
123
+ */
124
+ function deepCopy( obj )
61
125
  {
62
- return JSON.parse(JSON.stringify(o))
126
+ return JSON.parse( JSON.stringify( obj ) )
63
127
  }
64
128
 
65
129
  LX.deepCopy = deepCopy;
66
130
 
131
+ /**
132
+ * @method setTheme
133
+ * @description Set dark or light theme
134
+ * @param {String} colorScheme Name of the scheme
135
+ */
136
+ function setTheme( colorScheme )
137
+ {
138
+ colorScheme = ( colorScheme == "light" ) ? "light" : "dark";
139
+ document.documentElement.setAttribute( "data-theme", colorScheme );
140
+ LX.emit( "@on_new_color_scheme", colorScheme );
141
+ }
142
+
143
+ LX.setTheme = setTheme;
144
+
145
+ /**
146
+ * @method setThemeColor
147
+ * @description Sets a new value for one of the main theme variables
148
+ * @param {String} colorName Name of the theme variable
149
+ * @param {String} color Color in rgba/hex
150
+ */
67
151
  function setThemeColor( colorName, color )
68
152
  {
69
153
  var r = document.querySelector( ':root' );
@@ -72,6 +156,11 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
72
156
 
73
157
  LX.setThemeColor = setThemeColor;
74
158
 
159
+ /**
160
+ * @method getThemeColor
161
+ * @description Get the value for one of the main theme variables
162
+ * @param {String} colorName Name of the theme variable
163
+ */
75
164
  function getThemeColor( colorName )
76
165
  {
77
166
  const r = getComputedStyle( document.querySelector( ':root' ) );
@@ -79,7 +168,9 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
79
168
 
80
169
  if( value.includes( "light-dark" ) && window.matchMedia )
81
170
  {
82
- if( window.matchMedia( "(prefers-color-scheme: light)" ).matches )
171
+ const currentScheme = r.getPropertyValue( "color-scheme" );
172
+
173
+ if( ( window.matchMedia( "(prefers-color-scheme: light)" ).matches ) || ( currentScheme == "light" ) )
83
174
  {
84
175
  return value.substring( value.indexOf( '(' ) + 1, value.indexOf( ',' ) ).replace( /\s/g, '' );
85
176
  }
@@ -94,7 +185,13 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
94
185
 
95
186
  LX.getThemeColor = getThemeColor;
96
187
 
97
- function getBase64Image( img ) {
188
+ /**
189
+ * @method getBase64Image
190
+ * @description Convert an image to a base64 string
191
+ * @param {Image} img
192
+ */
193
+ function getBase64Image( img )
194
+ {
98
195
  var canvas = document.createElement( 'canvas' );
99
196
  canvas.width = img.width;
100
197
  canvas.height = img.height;
@@ -105,7 +202,13 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
105
202
 
106
203
  LX.getBase64Image = getBase64Image;
107
204
 
108
- function hexToRgb( hexStr ) {
205
+ /**
206
+ * @method hexToRgb
207
+ * @description Convert a hexadecimal string to a valid RGB color array
208
+ * @param {String} hexStr Hexadecimal color
209
+ */
210
+ function hexToRgb( hexStr )
211
+ {
109
212
  const red = parseInt( hexStr.substring( 1, 3 ), 16 ) / 255;
110
213
  const green = parseInt( hexStr.substring( 3, 5 ), 16 ) / 255;
111
214
  const blue = parseInt( hexStr.substring( 5, 7 ), 16 ) / 255;
@@ -114,7 +217,13 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
114
217
 
115
218
  LX.hexToRgb = hexToRgb;
116
219
 
117
- function rgbToHex( rgb ) {
220
+ /**
221
+ * @method rgbToHex
222
+ * @description Convert a RGB color array to a hexadecimal string
223
+ * @param {Array} rgb Array containing R, G, B, A*
224
+ */
225
+ function rgbToHex( rgb )
226
+ {
118
227
  let hex = "#";
119
228
  for( let c of rgb ) {
120
229
  c = Math.floor( c * 255 );
@@ -125,7 +234,14 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
125
234
 
126
235
  LX.rgbToHex = rgbToHex;
127
236
 
128
- function measureRealWidth( value, paddingPlusMargin = 8 ) {
237
+ /**
238
+ * @method measureRealWidth
239
+ * @description Measure the pixel width of a text
240
+ * @param {Object} value Text to measure
241
+ * @param {Number} paddingPlusMargin Padding offset
242
+ */
243
+ function measureRealWidth( value, paddingPlusMargin = 8 )
244
+ {
129
245
  var i = document.createElement( "span" );
130
246
  i.className = "lexinputmeasure";
131
247
  i.innerHTML = value;
@@ -137,76 +253,54 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
137
253
 
138
254
  LX.measureRealWidth = measureRealWidth;
139
255
 
140
- function simple_guidGenerator() {
256
+ /**
257
+ * @method simple_guidGenerator
258
+ * @description Get a random unique id
259
+ */
260
+ function simple_guidGenerator()
261
+ {
141
262
  var S4 = function() {
142
- return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
263
+ return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
143
264
  };
144
265
  return (S4()+"-"+S4()+"-"+S4());
145
266
  }
146
267
 
147
268
  LX.guidGenerator = simple_guidGenerator;
148
269
 
149
- // Timer that works everywhere (from litegraph.js)
150
- if (typeof performance != "undefined") {
151
- LX.getTime = performance.now.bind(performance);
152
- } else if (typeof Date != "undefined" && Date.now) {
153
- LX.getTime = Date.now.bind(Date);
154
- } else if (typeof process != "undefined") {
155
- LX.getTime = function() {
156
- var t = process.hrtime();
157
- return t[0] * 0.001 + t[1] * 1e-6;
158
- };
159
- } else {
160
- LX.getTime = function getTime() {
161
- return new Date().getTime();
162
- };
163
- }
164
-
165
- let ASYNC_ENABLED = true;
166
-
167
- function doAsync( fn, ms ) {
168
- if( ASYNC_ENABLED )
169
- {
170
- setTimeout( fn, ms ?? 0 );
171
- }
172
- else
173
- {
174
- fn();
175
- }
270
+ /**
271
+ * @method buildTextPattern
272
+ * @description Create a validation pattern using specific options
273
+ * @param {Object} options
274
+ * lowercase (Boolean): Text must contain a lowercase char
275
+ * uppercase (Boolean): Text must contain an uppercase char
276
+ * digit (Boolean): Text must contain a digit
277
+ * specialChar (Boolean): Text must contain a special char
278
+ * noSpaces (Boolean): Do not allow spaces in text
279
+ * minLength (Number): Text minimum length
280
+ * maxLength (Number): Text maximum length
281
+ * asRegExp (Boolean): Return pattern as Regular Expression instance
282
+ */
283
+ function buildTextPattern( options = {} )
284
+ {
285
+ let patterns = [];
286
+ if ( options.lowercase ) patterns.push("(?=.*[a-z])");
287
+ if ( options.uppercase ) patterns.push("(?=.*[A-Z])");
288
+ if ( options.digit ) patterns.push("(?=.*\\d)");
289
+ if ( options.specialChar ) patterns.push("(?=.*[@#$%^&+=!])");
290
+ if ( options.noSpaces ) patterns.push("(?!.*\\s)");
291
+
292
+ let minLength = options.minLength || 0;
293
+ let maxLength = options.maxLength || ""; // Empty means no max length restriction
294
+
295
+ let pattern = `^${ patterns.join("") }.{${ minLength },${ maxLength }}$`;
296
+ return options.asRegExp ? new RegExp( pattern ) : pattern;
176
297
  }
177
298
 
178
- // Math classes
179
-
180
- class vec2 {
181
-
182
- constructor( x, y ) {
183
- this.x = x ?? 0;
184
- this.y = y ?? ( x ?? 0 );
185
- }
186
-
187
- get xy() { return [ this.x, this.y ]; }
188
- get yx() { return [ this.y, this.x ]; }
189
-
190
- set ( x, y ) { this.x = x; this.y = y; }
191
- add ( v, v0 = new vec2() ) { v0.set( this.x + v.x, this.y + v.y ); return v0; }
192
- sub ( v, v0 = new vec2() ) { v0.set( this.x - v.x, this.y - v.y ); return v0; }
193
- 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; }
194
- 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; }
195
- abs ( v0 = new vec2() ) { v0.set( Math.abs( this.x ), Math.abs( this.y ) ); return v0; }
196
- dot ( v ) { return this.x * v.x + this.y * v.y; }
197
- len2 () { return this.dot( this ) }
198
- len () { return Math.sqrt( this.len2() ); }
199
- nrm ( v0 = new vec2() ) { v0.set( this.x, this.y ); return v0.mul( 1.0 / this.len(), v0 ); }
200
- dst ( v ) { return v.sub( this ).len(); }
201
- clp ( min, max, v0 = new vec2() ) { v0.set( clamp( this.x, min, max ), clamp( this.y, min, max ) ); return v0; }
202
- };
203
-
204
- LX.vec2 = vec2;
205
-
206
- // Other utils
299
+ LX.buildTextPattern = buildTextPattern;
207
300
 
208
301
  /**
209
302
  * @method makeDraggable
303
+ * @description Allow an element to be dragged
210
304
  * @param {Element} domEl
211
305
  * @param {Object} options
212
306
  * autoAdjust (Bool): Sets in a correct position at the beggining
@@ -214,8 +308,8 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
214
308
  * onMove (Function): Called each move event
215
309
  * onDragStart (Function): Called when drag event starts
216
310
  */
217
- function makeDraggable( domEl, options = { } ) {
218
-
311
+ function makeDraggable( domEl, options = { } )
312
+ {
219
313
  let offsetX = 0;
220
314
  let offsetY = 0;
221
315
  let currentTarget = null;
@@ -236,7 +330,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
236
330
  // Initial adjustment
237
331
  if( options.autoAdjust )
238
332
  {
239
- _computePosition( null, parseInt( domEl.style.left ), parseInt( domEl.style.top ) )
333
+ _computePosition( null, parseInt( domEl.style.left ), parseInt( domEl.style.top ) )
240
334
  }
241
335
 
242
336
  let id = LX.UTILS.uidGenerator();
@@ -305,7 +399,131 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
305
399
 
306
400
  LX.makeDraggable = makeDraggable;
307
401
 
308
- function create_global_searchbar( root ) {
402
+ /**
403
+ * @method makeCodeSnippet
404
+ * @description Create a code snippet in a specific language
405
+ * @param {String} code
406
+ * @param {Array} size
407
+ * @param {Object} options
408
+ * language (String):
409
+ * windowMode (Boolean):
410
+ * lineNumbers (Boolean):
411
+ * tabName (String):
412
+ */
413
+ function makeCodeSnippet( code, size, options = { } )
414
+ {
415
+ if( !LX.has('CodeEditor') )
416
+ {
417
+ console.error( "Import the CodeEditor component to create snippets!" );
418
+ return;
419
+ }
420
+
421
+ const snippet = document.createElement( "div" );
422
+ snippet.className = "lexcodesnippet";
423
+ snippet.style.width = size[ 0 ];
424
+ snippet.style.height = size[ 1 ];
425
+ const area = new Area( { noAppend: true } );
426
+ let editor = new LX.CodeEditor( area, {
427
+ skipInfo: true,
428
+ disableEdition: true,
429
+ allowAddScripts: false,
430
+ name: options.tabName,
431
+ // showTab: options.showTab ?? true,
432
+ // lineNumbers: options.lineNumbers ?? true
433
+ } );
434
+ editor.setText( code, options.language ?? "Plain Text" );
435
+
436
+ if( options.linesAdded )
437
+ {
438
+ const code = editor.root.querySelector( ".code" );
439
+ for( let l of options.linesAdded )
440
+ {
441
+ if( l.constructor == Number )
442
+ {
443
+ code.childNodes[ l - 1 ].classList.add( "added" );
444
+ }
445
+ else if( l.constructor == Array ) // It's a range
446
+ {
447
+ for( let i = ( l[0] - 1 ); i <= ( l[1] - 1 ); i++ )
448
+ {
449
+ code.childNodes[ i ].classList.add( "added" );
450
+ }
451
+ }
452
+ }
453
+ }
454
+
455
+ if( options.linesRemoved )
456
+ {
457
+ const code = editor.root.querySelector( ".code" );
458
+ for( let l of options.linesRemoved )
459
+ {
460
+ if( l.constructor == Number )
461
+ {
462
+ code.childNodes[ l - 1 ].classList.add( "removed" );
463
+ }
464
+ else if( l.constructor == Array ) // It's a range
465
+ {
466
+ for( let i = ( l[0] - 1 ); i <= ( l[1] - 1 ); i++ )
467
+ {
468
+ code.childNodes[ i ].classList.add( "removed" );
469
+ }
470
+ }
471
+ }
472
+ }
473
+
474
+ if( options.windowMode )
475
+ {
476
+ const windowActionButtons = document.createElement( "div" );
477
+ windowActionButtons.className = "lexwindowbuttons";
478
+ const aButton = document.createElement( "span" );
479
+ aButton.style.background = "#ee4f50";
480
+ const bButton = document.createElement( "span" );
481
+ bButton.style.background = "#f5b720";
482
+ const cButton = document.createElement( "span" );
483
+ cButton.style.background = "#53ca29";
484
+ windowActionButtons.appendChild( aButton );
485
+ windowActionButtons.appendChild( bButton );
486
+ windowActionButtons.appendChild( cButton );
487
+ const tabs = editor.root.querySelector( ".lexareatabs" );
488
+ tabs.prepend( windowActionButtons );
489
+ }
490
+
491
+ snippet.appendChild( area.root );
492
+ return snippet;
493
+ }
494
+
495
+ LX.makeCodeSnippet = makeCodeSnippet;
496
+
497
+ // Math classes
498
+
499
+ class vec2 {
500
+
501
+ constructor( x, y ) {
502
+ this.x = x ?? 0;
503
+ this.y = y ?? ( x ?? 0 );
504
+ }
505
+
506
+ get xy() { return [ this.x, this.y ]; }
507
+ get yx() { return [ this.y, this.x ]; }
508
+
509
+ set ( x, y ) { this.x = x; this.y = y; }
510
+ add ( v, v0 = new vec2() ) { v0.set( this.x + v.x, this.y + v.y ); return v0; }
511
+ sub ( v, v0 = new vec2() ) { v0.set( this.x - v.x, this.y - v.y ); return v0; }
512
+ 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; }
513
+ 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; }
514
+ abs ( v0 = new vec2() ) { v0.set( Math.abs( this.x ), Math.abs( this.y ) ); return v0; }
515
+ dot ( v ) { return this.x * v.x + this.y * v.y; }
516
+ len2 () { return this.dot( this ) }
517
+ len () { return Math.sqrt( this.len2() ); }
518
+ nrm ( v0 = new vec2() ) { v0.set( this.x, this.y ); return v0.mul( 1.0 / this.len(), v0 ); }
519
+ dst ( v ) { return v.sub( this ).len(); }
520
+ clp ( min, max, v0 = new vec2() ) { v0.set( clamp( this.x, min, max ), clamp( this.y, min, max ) ); return v0; }
521
+ };
522
+
523
+ LX.vec2 = vec2;
524
+
525
+ function create_global_searchbar( root )
526
+ {
309
527
 
310
528
  let globalSearch = document.createElement("div");
311
529
  globalSearch.id = "global-search";
@@ -749,7 +967,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
749
967
  if( callback ) callback.call( this, value );
750
968
  dialog.close();
751
969
  }
752
- }, { buttonClass: "accept" });
970
+ }, { buttonClass: "primary" });
753
971
 
754
972
  p.addButton(null, "Cancel", () => {if(options.on_cancel) options.on_cancel(); dialog.close();} );
755
973
 
@@ -2198,18 +2416,18 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2198
2416
  entry.className = "lexmenuentry";
2199
2417
  entry.id = pKey;
2200
2418
  entry.innerHTML = "<span>" + key + "</span>";
2201
- if(options.position == "left") {
2202
- this.root.prepend( entry );
2203
- }
2204
- else {
2205
- if(options.position == "right")
2206
- entry.right = true;
2207
- if(this.root.lastChild && this.root.lastChild.right) {
2208
- this.root.lastChild.before( entry );
2209
- }
2210
- else {
2211
- this.root.appendChild( entry );
2212
- }
2419
+ if(options.position == "left") {
2420
+ this.root.prepend( entry );
2421
+ }
2422
+ else {
2423
+ if(options.position == "right")
2424
+ entry.right = true;
2425
+ if(this.root.lastChild && this.root.lastChild.right) {
2426
+ this.root.lastChild.before( entry );
2427
+ }
2428
+ else {
2429
+ this.root.appendChild( entry );
2430
+ }
2213
2431
  }
2214
2432
 
2215
2433
  const create_submenu = function( o, k, c, d ) {
@@ -2430,16 +2648,16 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2430
2648
  button.style.maxHeight = "calc(100% - 10px)";
2431
2649
  button.style.alignItems = "center";
2432
2650
 
2433
- if(options.float == "right")
2434
- button.right = true;
2435
- if(this.root.lastChild && this.root.lastChild.right) {
2436
- this.root.lastChild.before( button );
2651
+ if(options.float == "right")
2652
+ button.right = true;
2653
+ if(this.root.lastChild && this.root.lastChild.right) {
2654
+ this.root.lastChild.before( button );
2437
2655
  }
2438
2656
  else if(options.float == "left") {
2439
2657
  this.root.prepend(button);
2440
- }
2441
- else {
2442
- this.root.appendChild( button );
2658
+ }
2659
+ else {
2660
+ this.root.appendChild( button );
2443
2661
  }
2444
2662
 
2445
2663
  const _b = button.querySelector('a');
@@ -2471,16 +2689,16 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2471
2689
  button.style.padding = "5px";
2472
2690
  button.style.alignItems = "center";
2473
2691
 
2474
- if(options.float == "right")
2475
- button.right = true;
2476
- if(this.root.lastChild && this.root.lastChild.right) {
2477
- this.root.lastChild.before( button );
2478
- }
2692
+ if(options.float == "right")
2693
+ button.right = true;
2694
+ if(this.root.lastChild && this.root.lastChild.right) {
2695
+ this.root.lastChild.before( button );
2696
+ }
2479
2697
  else if(options.float == "left") {
2480
2698
  this.root.prepend(button);
2481
2699
  }
2482
- else {
2483
- this.root.appendChild( button );
2700
+ else {
2701
+ this.root.appendChild( button );
2484
2702
  }
2485
2703
 
2486
2704
  const _b = button.querySelector('a');
@@ -2500,44 +2718,86 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2500
2718
 
2501
2719
  addButtons( buttons, options = {} ) {
2502
2720
 
2503
- if(!buttons)
2504
- throw("No buttons to add!");
2721
+ if( !buttons )
2722
+ {
2723
+ throw( "No buttons to add!" );
2724
+ }
2505
2725
 
2506
- if(!this.buttonContainer)
2726
+ if( !this.buttonContainer )
2507
2727
  {
2508
- this.buttonContainer = document.createElement('div');
2728
+ this.buttonContainer = document.createElement( "div" );
2509
2729
  this.buttonContainer.className = "lexmenubuttons";
2510
- this.buttonContainer.classList.add(options.float ?? 'center');
2511
- if(options.position == "right")
2512
- this.buttonContainer.right = true;
2513
- if(this.root.lastChild && this.root.lastChild.right) {
2514
- this.root.lastChild.before( this.buttonContainer );
2515
- }
2516
- else {
2517
- this.root.appendChild( this.buttonContainer );
2730
+ this.buttonContainer.classList.add( options.float ?? "center" );
2731
+
2732
+ if( options.position == "right" )
2733
+ {
2734
+ this.buttonContainer.right = true;
2735
+ }
2736
+
2737
+ if( this.root.lastChild && this.root.lastChild.right )
2738
+ {
2739
+ this.root.lastChild.before( this.buttonContainer );
2740
+ }
2741
+ else
2742
+ {
2743
+ this.root.appendChild( this.buttonContainer );
2518
2744
  }
2519
2745
  }
2520
2746
 
2521
2747
  for( let i = 0; i < buttons.length; ++i )
2522
2748
  {
2523
- let data = buttons[i];
2524
- let button = document.createElement('div');
2749
+ let data = buttons[ i ];
2750
+ let button = document.createElement( "label" );
2525
2751
  const title = data.title;
2526
2752
  let disabled = data.disabled ?? false;
2527
2753
  button.className = "lexmenubutton" + (disabled ? " disabled" : "");
2528
2754
  button.title = title ?? "";
2529
- button.innerHTML = "<a class='" + data.icon + " lexicon'></a>";
2530
2755
  this.buttonContainer.appendChild( button );
2531
2756
 
2532
- const _b = button.querySelector('a');
2533
- _b.addEventListener("click", (e) => {
2534
- disabled = e.target.parentElement.classList.contains("disabled");
2535
- if(data.callback && !disabled)
2536
- data.callback.call( this, _b, e );
2757
+ const icon = document.createElement( "a" );
2758
+ icon.className = data.icon + " lexicon";
2759
+ button.appendChild( icon );
2760
+
2761
+ let trigger = icon;
2762
+
2763
+ if( data.swap )
2764
+ {
2765
+ button.classList.add( "swap" );
2766
+ icon.classList.add( "swap-off" );
2767
+
2768
+ const input = document.createElement( "input" );
2769
+ input.type = "checkbox";
2770
+ button.prepend( input );
2771
+ trigger = input;
2772
+
2773
+ const swapIcon = document.createElement( "a" );
2774
+ swapIcon.className = data.swap + " swap-on lexicon";
2775
+ button.appendChild( swapIcon );
2776
+
2777
+ button.swap = function() {
2778
+ const swapInput = this.querySelector( "input" );
2779
+ swapInput.checked = !swapInput.checked;
2780
+ };
2781
+
2782
+ // Set if swap has to be performed
2783
+ button.setState = function( v ) {
2784
+ const swapInput = this.querySelector( "input" );
2785
+ swapInput.checked = v;
2786
+ };
2787
+ }
2788
+
2789
+ trigger.addEventListener("click", e => {
2790
+ if( data.callback && !disabled )
2791
+ {
2792
+ const swapInput = button.querySelector( "input" );
2793
+ data.callback.call( this, e, swapInput?.checked );
2794
+ }
2537
2795
  });
2538
2796
 
2539
- if(title)
2797
+ if( title )
2798
+ {
2540
2799
  this.buttons[ title ] = button;
2800
+ }
2541
2801
  }
2542
2802
  }
2543
2803
  };
@@ -2691,6 +2951,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2691
2951
  static PAD = 26;
2692
2952
  static FORM = 27;
2693
2953
  static DIAL = 28;
2954
+ static COUNTER = 29;
2694
2955
 
2695
2956
  static NO_CONTEXT_TYPES = [
2696
2957
  Widget.BUTTON,
@@ -2780,6 +3041,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2780
3041
  case Widget.PAD: return "Pad";
2781
3042
  case Widget.FORM: return "Form";
2782
3043
  case Widget.DIAL: return "Dial";
3044
+ case Widget.COUNTER: return "Counter";
2783
3045
  case Widget.CUSTOM: return this.customName;
2784
3046
  }
2785
3047
 
@@ -4009,7 +4271,9 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4009
4271
  * @param {Function} callback Callback function on change
4010
4272
  * @param {*} options:
4011
4273
  * disabled: Make the widget disabled [false]
4274
+ * required: Make the input required
4012
4275
  * placeholder: Add input placeholder
4276
+ * pattern: Regular expression that value must match
4013
4277
  * trigger: Choose onchange trigger (default, input) [default]
4014
4278
  * inputWidth: Width of the text input
4015
4279
  * skipReset: Don't add the reset value button when value changes
@@ -4024,11 +4288,18 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4024
4288
  widget.onGetValue = () => {
4025
4289
  return wValue.value;
4026
4290
  };
4291
+
4027
4292
  widget.onSetValue = ( newValue, skipCallback ) => {
4028
4293
  this.disabled ? wValue.innerText = newValue : wValue.value = newValue;
4029
4294
  Panel._dispatch_event( wValue, "focusout", skipCallback );
4030
4295
  };
4031
4296
 
4297
+ widget.valid = () => {
4298
+ if( wValue.pattern == "" ) { return true; }
4299
+ const regexp = new RegExp( wValue.pattern );
4300
+ return regexp.test( wValue.value );
4301
+ };
4302
+
4032
4303
  let element = widget.domEl;
4033
4304
 
4034
4305
  // Add reset functionality
@@ -4063,14 +4334,33 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4063
4334
  wValue.style.width = "100%";
4064
4335
  wValue.style.textAlign = options.float ?? "";
4065
4336
 
4066
- if( options.placeholder )
4067
- wValue.setAttribute( "placeholder", options.placeholder );
4337
+ wValue.setAttribute( "placeholder", options.placeholder ?? "" );
4338
+
4339
+ if( options.required )
4340
+ {
4341
+ wValue.setAttribute( "required", options.required );
4342
+ }
4343
+
4344
+ if( options.pattern )
4345
+ {
4346
+ wValue.setAttribute( "pattern", options.pattern );
4347
+ }
4068
4348
 
4069
4349
  var resolve = ( function( val, event ) {
4350
+
4351
+ if( !widget.valid() )
4352
+ {
4353
+ return;
4354
+ }
4355
+
4070
4356
  const skipCallback = event.detail;
4071
4357
  let btn = element.querySelector( ".lexwidgetname .lexicon" );
4072
4358
  if( btn ) btn.style.display = ( val != wValue.iValue ? "block" : "none" );
4073
- if( !skipCallback ) this._trigger( new IEvent( name, val, event ), callback );
4359
+ if( !skipCallback )
4360
+ {
4361
+ this._trigger( new IEvent( name, val, event ), callback );
4362
+ }
4363
+
4074
4364
  }).bind( this );
4075
4365
 
4076
4366
  const trigger = options.trigger ?? 'default';
@@ -4282,18 +4572,13 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4282
4572
 
4283
4573
  var wValue = document.createElement( 'button' );
4284
4574
  wValue.title = options.title ?? "";
4285
- wValue.className = "lexbutton";
4575
+ wValue.className = "lexbutton " + ( options.buttonClass ?? "" );
4286
4576
 
4287
4577
  if( options.selected )
4288
4578
  {
4289
4579
  wValue.classList.add( "selected" );
4290
4580
  }
4291
4581
 
4292
- if( options.buttonClass )
4293
- {
4294
- wValue.classList.add( options.buttonClass );
4295
- }
4296
-
4297
4582
  wValue.innerHTML =
4298
4583
  (options.icon ? "<a class='" + options.icon + "'></a>" :
4299
4584
  ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (value || "") + "</span>" ));
@@ -4529,7 +4814,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4529
4814
 
4530
4815
  this.addLabel( entry, { textClass: "formlabel" } );
4531
4816
 
4532
- this.addText( entry, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
4817
+ entryData.textWidget = this.addText( null, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
4533
4818
  container.formData[ entry ] = value;
4534
4819
  }, entryData );
4535
4820
 
@@ -4539,11 +4824,22 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4539
4824
  this.addBlank( );
4540
4825
 
4541
4826
  this.addButton( null, options.actionName ?? "Submit", ( value, event ) => {
4827
+
4828
+ for( let entry in data )
4829
+ {
4830
+ let entryData = data[ entry ];
4831
+
4832
+ if( !entryData.textWidget.valid() )
4833
+ {
4834
+ return;
4835
+ }
4836
+ }
4837
+
4542
4838
  if( callback )
4543
4839
  {
4544
4840
  callback( container.formData, event );
4545
4841
  }
4546
- }, { buttonClass: "accept", width: "calc(100% - 10px)" } );
4842
+ }, { buttonClass: "primary", width: "calc(100% - 10px)" } );
4547
4843
 
4548
4844
  this.clearQueue();
4549
4845
 
@@ -4698,13 +4994,36 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4698
4994
  delete list.unfocus_event;
4699
4995
  return;
4700
4996
  }
4701
- const topPosition = selectedOption.getBoundingClientRect().y;
4702
- list.style.top = (topPosition + selectedOption.offsetHeight) + 'px';
4997
+
4998
+ list.toggleAttribute( "hidden" );
4999
+ list.classList.remove( "place-above" );
5000
+
5001
+ const listHeight = 26 * values.length;
5002
+ const rect = selectedOption.getBoundingClientRect();
5003
+ const topPosition = rect.y;
5004
+
5005
+ let maxY = window.innerHeight;
5006
+ let overflowContainer = list.getParentArea();
5007
+
5008
+ if( overflowContainer )
5009
+ {
5010
+ const parentRect = overflowContainer.getBoundingClientRect();
5011
+ maxY = parentRect.y + parentRect.height;
5012
+ }
5013
+
5014
+ list.style.top = ( topPosition + selectedOption.offsetHeight ) + 'px';
5015
+
5016
+ const showAbove = ( topPosition + listHeight ) > maxY;
5017
+ if( showAbove )
5018
+ {
5019
+ list.style.top = ( topPosition - listHeight ) + 'px';
5020
+ list.classList.add( "place-above" );
5021
+ }
5022
+
4703
5023
  list.style.width = (event.currentTarget.clientWidth) + 'px';
4704
5024
  list.style.minWidth = (_getMaxListWidth()) + 'px';
4705
- list.toggleAttribute('hidden');
4706
5025
  list.focus();
4707
- }, { buttonClass: 'array', skipInlineCount: true });
5026
+ }, { buttonClass: "array", skipInlineCount: true });
4708
5027
 
4709
5028
  this.clearQueue();
4710
5029
 
@@ -5350,40 +5669,45 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5350
5669
 
5351
5670
  // Show tags
5352
5671
 
5353
- let tags_container = document.createElement('div');
5354
- tags_container.className = "lextags";
5355
- tags_container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
5672
+ const tagsContainer = document.createElement('div');
5673
+ tagsContainer.className = "lextags";
5674
+ tagsContainer.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
5356
5675
 
5357
5676
  const create_tags = () => {
5358
5677
 
5359
- tags_container.innerHTML = "";
5678
+ tagsContainer.innerHTML = "";
5360
5679
 
5361
5680
  for( let i = 0; i < value.length; ++i )
5362
5681
  {
5363
- let tag_name = value[i];
5364
- let tag = document.createElement('span');
5682
+ const tagName = value[i];
5683
+ const tag = document.createElement('span');
5365
5684
  tag.className = "lextag";
5366
- tag.innerHTML = tag_name;
5685
+ tag.innerHTML = tagName;
5686
+
5687
+ const removeButton = document.createElement('a');
5688
+ removeButton.className = "lextagrmb fa-solid fa-xmark lexicon";
5689
+ tag.appendChild( removeButton );
5367
5690
 
5368
- tag.addEventListener('click', function( e ) {
5369
- this.remove();
5370
- value.splice( value.indexOf( tag_name ), 1 );
5691
+ removeButton.addEventListener( 'click', e => {
5692
+ tag.remove();
5693
+ value.splice( value.indexOf( tagName ), 1 );
5371
5694
  let btn = element.querySelector( ".lexwidgetname .lexicon" );
5372
5695
  if( btn ) btn.style.display = ( value != defaultValue ? "block" : "none" );
5373
5696
  that._trigger( new IEvent( name, value, e ), callback );
5374
- });
5697
+ } );
5375
5698
 
5376
- tags_container.appendChild( tag );
5699
+ tagsContainer.appendChild( tag );
5377
5700
  }
5378
5701
 
5379
- let tag_input = document.createElement( 'input' );
5380
- tag_input.value = "";
5381
- tag_input.placeholder = "Tag...";
5382
- tags_container.insertChildAtIndex( tag_input, 0 );
5702
+ let tagInput = document.createElement( 'input' );
5703
+ tagInput.value = "";
5704
+ tagInput.placeholder = "Add tag...";
5705
+ tagsContainer.appendChild( tagInput );
5383
5706
 
5384
- tag_input.onkeydown = function( e ) {
5707
+ tagInput.onkeydown = function( e ) {
5385
5708
  const val = this.value.replace(/\s/g, '');
5386
- if( e.key == ' ') {
5709
+ if( e.key == ' ' || e.key == 'Enter' )
5710
+ {
5387
5711
  e.preventDefault();
5388
5712
  if( !val.length || value.indexOf( val ) > -1 )
5389
5713
  return;
@@ -5395,18 +5719,19 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5395
5719
  }
5396
5720
  };
5397
5721
 
5398
- tag_input.focus();
5722
+ tagInput.focus();
5399
5723
  }
5400
5724
 
5401
5725
  create_tags();
5402
5726
 
5403
5727
  // Remove branch padding and margins
5404
- if(!widget.name) {
5728
+ if( !widget.name )
5729
+ {
5405
5730
  element.className += " noname";
5406
- tags_container.style.width = "100%";
5731
+ tagsContainer.style.width = "100%";
5407
5732
  }
5408
5733
 
5409
- element.appendChild(tags_container);
5734
+ element.appendChild( tagsContainer );
5410
5735
 
5411
5736
  return widget;
5412
5737
  }
@@ -6594,7 +6919,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6594
6919
  progress.classList.add( "editable" );
6595
6920
  progress.addEventListener( "mousedown", inner_mousedown );
6596
6921
 
6597
- var that = this;
6922
+ const that = this;
6598
6923
 
6599
6924
  function inner_mousedown( e )
6600
6925
  {
@@ -6602,24 +6927,28 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6602
6927
  doc.addEventListener( 'mousemove', inner_mousemove );
6603
6928
  doc.addEventListener( 'mouseup', inner_mouseup );
6604
6929
  document.body.classList.add( 'noevents' );
6930
+ progress.classList.add( "grabbing" );
6605
6931
  e.stopImmediatePropagation();
6606
6932
  e.stopPropagation();
6933
+
6934
+ const rect = progress.getBoundingClientRect();
6935
+ const newValue = round( remapRange( e.offsetX, 0, rect.width, progress.min, progress.max ) );
6936
+ that.setValue( name, newValue );
6607
6937
  }
6608
6938
 
6609
6939
  function inner_mousemove( e )
6610
6940
  {
6611
- let dt = -e.movementX;
6941
+ let dt = e.movementX;
6612
6942
 
6613
6943
  if ( dt != 0 )
6614
6944
  {
6615
- let v = that.getValue( name, value );
6616
- v += e.movementX / 100;
6617
- v = round( v );
6618
- that.setValue( name, v );
6945
+ const rect = progress.getBoundingClientRect();
6946
+ const newValue = round( remapRange( e.offsetX - rect.x, 0, rect.width, progress.min, progress.max ) );
6947
+ that.setValue( name, newValue );
6619
6948
 
6620
6949
  if( options.callback )
6621
6950
  {
6622
- options.callback( v, e );
6951
+ options.callback( newValue, e );
6623
6952
  }
6624
6953
  }
6625
6954
 
@@ -6633,6 +6962,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6633
6962
  doc.removeEventListener( 'mousemove', inner_mousemove );
6634
6963
  doc.removeEventListener( 'mouseup', inner_mouseup );
6635
6964
  document.body.classList.remove( 'noevents' );
6965
+ progress.classList.remove( "grabbing" );
6636
6966
  }
6637
6967
  }
6638
6968
 
@@ -6917,6 +7247,91 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6917
7247
 
6918
7248
  this.addSeparator();
6919
7249
  }
7250
+
7251
+ /**
7252
+ * @method addCounter
7253
+ * @param {String} name Widget name
7254
+ * @param {Number} value Counter value
7255
+ * @param {Function} callback Callback function on change
7256
+ * @param {*} options:
7257
+ * disabled: Make the widget disabled [false]
7258
+ * min, max: Min and Max values
7259
+ * step: Step for adding/substracting
7260
+ * label: Text to show below the counter
7261
+ */
7262
+
7263
+ addCounter( name, value, callback, options = { } ) {
7264
+
7265
+ let widget = this.create_widget( name, Widget.COUNTER, options );
7266
+
7267
+ widget.onGetValue = () => {
7268
+ return counterText.count;
7269
+ };
7270
+
7271
+ widget.onSetValue = ( newValue, skipCallback ) => {
7272
+ _onChange( newValue, skipCallback );
7273
+ };
7274
+
7275
+ let element = widget.domEl;
7276
+
7277
+ const min = options.min ?? 0;
7278
+ const max = options.max ?? 100;
7279
+ const step = options.step ?? 1;
7280
+
7281
+ const _onChange = ( value, skipCallback, event ) => {
7282
+ value = clamp( value, min, max );
7283
+ counterText.count = value;
7284
+ counterText.innerHTML = value;
7285
+ if( !skipCallback )
7286
+ {
7287
+ this._trigger( new IEvent( name, value, event ), callback );
7288
+ }
7289
+ }
7290
+
7291
+ const container = document.createElement( 'div' );
7292
+ container.className = "lexcounter";
7293
+ element.appendChild( container );
7294
+
7295
+ this.queue( container );
7296
+
7297
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-minus'></a>", (value, e) => {
7298
+ let mult = step ?? 1;
7299
+ if( e.shiftKey ) mult *= 10;
7300
+ _onChange( counterText.count - mult, false, e );
7301
+ }, { className: "micro", skipInlineCount: true, title: "Minus" });
7302
+
7303
+ this.clearQueue();
7304
+
7305
+ const containerBox = document.createElement( 'div' );
7306
+ containerBox.className = "lexcounterbox";
7307
+ container.appendChild( containerBox );
7308
+
7309
+ const counterText = document.createElement( 'span' );
7310
+ counterText.className = "lexcountervalue";
7311
+ counterText.innerHTML = value;
7312
+ counterText.count = value;
7313
+ containerBox.appendChild( counterText );
7314
+
7315
+ if( options.label )
7316
+ {
7317
+ const counterLabel = document.createElement( 'span' );
7318
+ counterLabel.className = "lexcounterlabel";
7319
+ counterLabel.innerHTML = options.label;
7320
+ containerBox.appendChild( counterLabel );
7321
+ }
7322
+
7323
+ this.queue( container );
7324
+
7325
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-plus'></a>", (value, e) => {
7326
+ let mult = step ?? 1;
7327
+ if( e.shiftKey ) mult *= 10;
7328
+ _onChange( counterText.count + mult, false, e );
7329
+ }, { className: "micro", skipInlineCount: true, title: "Plus" });
7330
+
7331
+ this.clearQueue();
7332
+
7333
+ return widget;
7334
+ }
6920
7335
  }
6921
7336
 
6922
7337
  LX.Panel = Panel;
@@ -7152,6 +7567,104 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
7152
7567
 
7153
7568
  LX.Branch = Branch;
7154
7569
 
7570
+ /**
7571
+ * @class Footer
7572
+ */
7573
+
7574
+ class Footer {
7575
+ /**
7576
+ * @param {*} options:
7577
+ * columns: Array with data per column { title, items: [ { title, link } ] }
7578
+ * credits: html string
7579
+ * socials: Array with data per item { title, link, iconHtml }
7580
+ */
7581
+ constructor( options = {} ) {
7582
+
7583
+ const root = document.createElement( "footer" );
7584
+ root.className = "lexfooter";
7585
+
7586
+ const wrapper = document.createElement( "div" );
7587
+ wrapper.className = "wrapper";
7588
+ root.appendChild( wrapper );
7589
+
7590
+ if( options.columns && options.columns.constructor == Array )
7591
+ {
7592
+ const cols = document.createElement( "div" );
7593
+ cols.className = "columns";
7594
+ cols.style.gridTemplateColumns = "1fr ".repeat( options.columns.length );
7595
+ wrapper.appendChild( cols );
7596
+
7597
+ for( let col of options.columns )
7598
+ {
7599
+ const colDom = document.createElement( "div" );
7600
+ colDom.className = "col";
7601
+ cols.appendChild( colDom );
7602
+
7603
+ const colTitle = document.createElement( "h2" );
7604
+ colTitle.innerHTML = col.title;
7605
+ colDom.appendChild( colTitle );
7606
+
7607
+ if( !col.items || !col.items.length )
7608
+ {
7609
+ continue;
7610
+ }
7611
+
7612
+ const itemListDom = document.createElement( "ul" );
7613
+ colDom.appendChild( itemListDom );
7614
+
7615
+ for( let item of col.items )
7616
+ {
7617
+ const itemDom = document.createElement( "li" );
7618
+ itemDom.innerHTML = `<a class="" href="${ item.link }">${ item.title }</a>`;
7619
+ itemListDom.appendChild( itemDom );
7620
+ }
7621
+ }
7622
+ }
7623
+
7624
+ if( options.credits || options.socials )
7625
+ {
7626
+ const hr = document.createElement( "hr" );
7627
+ wrapper.appendChild( hr );
7628
+
7629
+ const creditsSocials = document.createElement( "div" );
7630
+ creditsSocials.className = "credits-and-socials";
7631
+ wrapper.appendChild( creditsSocials );
7632
+
7633
+ if( options.credits )
7634
+ {
7635
+ const credits = document.createElement( "p" );
7636
+ credits.innerHTML = options.credits;
7637
+ creditsSocials.appendChild( credits );
7638
+ }
7639
+
7640
+ if( options.socials )
7641
+ {
7642
+ const socials = document.createElement( "div" );
7643
+ socials.className = "social";
7644
+
7645
+ for( let social of options.socials )
7646
+ {
7647
+ const itemDom = document.createElement( "a" );
7648
+ itemDom.title = social.title;
7649
+ itemDom.innerHTML = social.icon;
7650
+ itemDom.href = social.link;
7651
+ itemDom.target = "_blank";
7652
+ socials.appendChild( itemDom );
7653
+ }
7654
+
7655
+ creditsSocials.appendChild( socials );
7656
+ }
7657
+ }
7658
+
7659
+ // Append directly to body
7660
+ const parent = options.parent ?? document.body;
7661
+ parent.appendChild( root );
7662
+ }
7663
+
7664
+ }
7665
+
7666
+ LX.Footer = Footer;
7667
+
7155
7668
  /**
7156
7669
  * @class Dialog
7157
7670
  */
@@ -9297,7 +9810,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
9297
9810
  //request.mimeType = "text/plain; charset=x-user-defined";
9298
9811
  dataType = "arraybuffer";
9299
9812
  request.mimeType = "application/octet-stream";
9300
- }
9813
+ }
9301
9814
 
9302
9815
  //regular case, use AJAX call
9303
9816
  var xhr = new XMLHttpRequest();
@@ -9538,6 +10051,14 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
9538
10051
  }
9539
10052
  }
9540
10053
 
10054
+ Element.prototype.getParentArea = function() {
10055
+ let parent = this.parentElement;
10056
+ while( parent ) {
10057
+ if( parent.classList.contains( "lexarea" ) ) { return parent; }
10058
+ parent = parent.parentElement;
10059
+ }
10060
+ }
10061
+
9541
10062
  LX.UTILS = {
9542
10063
  getTime() { return new Date().getTime() },
9543
10064
  compareThreshold( v, p, n, t ) { return Math.abs(v - p) >= t || Math.abs(v - n) >= t },