lexgui 0.1.11 → 0.1.13

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.
@@ -102,6 +102,8 @@ class CodeEditor {
102
102
 
103
103
  constructor( area, options = {} ) {
104
104
 
105
+ window.editor = this;
106
+
105
107
  CodeEditor.__instances.push( this );
106
108
 
107
109
  this.disable_edition = options.disable_edition ?? false;
@@ -160,6 +162,7 @@ class CodeEditor {
160
162
  cursor.style.top = "4px";
161
163
  cursor.position = 0;
162
164
  cursor.line = 0;
165
+ cursor.print = (function() { console.log( this.line, this.position ) }).bind(cursor);
163
166
  this.cursors.appendChild(cursor);
164
167
  }
165
168
 
@@ -183,6 +186,7 @@ class CodeEditor {
183
186
 
184
187
  // Code
185
188
 
189
+ this.useAutoComplete = options.autocomplete ?? true;
186
190
  this.highlight = options.highlight ?? 'Plain Text';
187
191
  this.onsave = options.onsave ?? ((code) => { });
188
192
  this.onrun = options.onrun ?? ((code) => { this.runScript(code) });
@@ -191,7 +195,7 @@ class CodeEditor {
191
195
  this.tabSpaces = 4;
192
196
  this.maxUndoSteps = 16;
193
197
  this.lineHeight = 20;
194
- this.charWidth = 8;//this.measureChar();
198
+ this.charWidth = 8; //this.measureChar();
195
199
  this._lastTime = null;
196
200
 
197
201
  this.languages = [
@@ -203,28 +207,31 @@ class CodeEditor {
203
207
  'End', 'Tab', 'Escape'
204
208
  ];
205
209
  this.keywords = {
206
- 'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof'],
210
+ 'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger'],
207
211
  'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
208
212
  'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
209
213
  'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
210
214
  'sampler', 'sampler_comparison', 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array', 'texture_depth_multisampled_2d',
211
215
  'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
212
- 'texture_storage_2d_array', 'texture_storage_3d'],
213
- 'JSON': []
216
+ 'texture_storage_2d_array', 'texture_storage_3d']
217
+ };
218
+ this.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
219
+ 'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite',
220
+ 'bind', 'prototype', 'length', 'assign', 'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild', 'createElement'],
221
+ 'WGSL': ['textureSample']
222
+ };
223
+ this.types = {
224
+ 'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number']
214
225
  };
215
226
  this.builtin = {
216
- 'JavaScript': ['console', 'window', 'navigator'],
217
- 'CSS': ['*', '!important'],
218
- 'GLSL': [],
219
- 'WGSL': [],
220
- 'JSON': []
227
+ 'JavaScript': ['document', 'console', 'window', 'navigator'],
228
+ 'CSS': ['*', '!important']
221
229
  };
222
- this.literals = {
223
- 'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do'],
230
+ this.statementsAndDeclarations = {
231
+ 'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import',
232
+ 'from', 'throw', 'async', 'try', 'catch'],
224
233
  'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
225
- 'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform'],
226
- 'JSON': [],
227
- 'CSS': []
234
+ 'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform']
228
235
  };
229
236
  this.symbols = {
230
237
  'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
@@ -256,6 +263,8 @@ class CodeEditor {
256
263
  this.code.lines[ln] = sliceChar( this.code.lines[ln], cursor.position - 1 );
257
264
  this.cursorToLeft( letter );
258
265
  this.processLine(ln);
266
+ if( this.useAutoComplete )
267
+ this.showAutoCompleteBox( 'foo', cursor );
259
268
  }
260
269
  else if(this.code.lines[ln - 1] != undefined) {
261
270
  this.lineUp();
@@ -317,16 +326,17 @@ class CodeEditor {
317
326
  this._refreshCodeInfo(cursor.line + 1, cursor.position);
318
327
  this.code.scrollLeft = 0;
319
328
 
320
- if( !e.shiftKey || e.cancelShift )
321
- return;
322
-
323
- // Get last selection range
324
- if(this.selection)
329
+ if( e.shiftKey && !e.cancelShift )
330
+ {
331
+ // Get last selection range
332
+ if(this.selection)
325
333
  last_pos += this.selection.chars;
326
334
 
327
- this.startSelection(cursor);
328
- var string = this.code.lines[ln].substring(idx, last_pos);
329
- this.selection.selectInline(idx, cursor.line, this.measureString(string));
335
+ this.startSelection(cursor);
336
+ var string = this.code.lines[ln].substring(idx, last_pos);
337
+ this.selection.selectInline(idx, cursor.line, this.measureString(string));
338
+ } else
339
+ this.endSelection();
330
340
  });
331
341
 
332
342
  this.action('End', false, ( ln, cursor, e ) => {
@@ -337,7 +347,8 @@ class CodeEditor {
337
347
  if(!this.selection)
338
348
  this.startSelection(cursor);
339
349
  this.selection.selectInline(cursor.position, cursor.line, this.measureString(string));
340
- }
350
+ } else
351
+ this.endSelection();
341
352
 
342
353
  this.resetCursorPos( CodeEditor.CURSOR_LEFT );
343
354
  this.cursorToString( cursor, this.code.lines[ln] );
@@ -394,11 +405,18 @@ class CodeEditor {
394
405
  if( e.shiftKey ) {
395
406
  if(!this.selection)
396
407
  this.startSelection(cursor);
397
- this.selection.fromX = cursor.position;
398
- this.selection.toY = this.selection.toY > 0 ? this.selection.toY - 1 : 0;
399
- this.processSelection(null, true);
400
- this.cursorToPosition(cursor, this.selection.fromX);
408
+
409
+ this.selection.toY = (this.selection.toY > 0) ? (this.selection.toY - 1) : 0;
401
410
  this.cursorToLine(cursor, this.selection.toY);
411
+
412
+ var letter = this.getCharAtPos( cursor );
413
+ if(!letter) {
414
+ this.selection.toX = (this.code.lines[cursor.line].length - 1);
415
+ this.cursorToPosition(cursor, this.selection.toX);
416
+ }
417
+
418
+ this.processSelection(null, true);
419
+
402
420
  } else {
403
421
  this.endSelection();
404
422
  this.lineUp();
@@ -422,11 +440,17 @@ class CodeEditor {
422
440
  if( e.shiftKey ) {
423
441
  if(!this.selection)
424
442
  this.startSelection(cursor);
425
- this.selection.toX = cursor.position;
443
+
426
444
  this.selection.toY = this.selection.toY < this.code.lines.length - 1 ? this.selection.toY + 1 : this.code.lines.length - 1;
427
- this.processSelection(null, true);
428
- this.cursorToPosition(cursor, this.selection.toX);
429
445
  this.cursorToLine(cursor, this.selection.toY);
446
+
447
+ var letter = this.getCharAtPos( cursor );
448
+ if(!letter) {
449
+ this.selection.toX = Math.max(this.code.lines[cursor.line].length - 1, 0);
450
+ this.cursorToPosition(cursor, this.selection.toX);
451
+ }
452
+
453
+ this.processSelection(null, true);
430
454
  } else {
431
455
 
432
456
  if( this.code.lines[ ln + 1 ] == undefined )
@@ -478,7 +502,11 @@ class CodeEditor {
478
502
  this.processSelection(null, true);
479
503
  }
480
504
  else {
481
- if(!this.selection) this.cursorToLeft( letter, cursor );
505
+ if(!this.selection) {
506
+ this.cursorToLeft( letter, cursor );
507
+ if( this.useAutoComplete && this.isAutoCompleteActive )
508
+ this.showAutoCompleteBox( 'foo', cursor );
509
+ }
482
510
  else {
483
511
  this.selection.invertIfNecessary();
484
512
  this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
@@ -537,7 +565,11 @@ class CodeEditor {
537
565
  this.cursorToRight( letter, cursor );
538
566
  this.processSelection(null, keep_range);
539
567
  }else{
540
- if(!this.selection) this.cursorToRight( letter, cursor );
568
+ if(!this.selection) {
569
+ this.cursorToRight( letter, cursor );
570
+ if( this.useAutoComplete && this.isAutoCompleteActive )
571
+ this.showAutoCompleteBox( 'foo', cursor );
572
+ }
541
573
  else
542
574
  {
543
575
  this.selection.invertIfNecessary();
@@ -553,6 +585,7 @@ class CodeEditor {
553
585
  this.lineDown( cursor );
554
586
  e.cancelShift = true;
555
587
  this.actions['Home'].callback(cursor.line, cursor, e);
588
+ this.hideAutoCompleteBox();
556
589
 
557
590
  if( e.shiftKey ) {
558
591
  if(!this.selection) this.startSelection(cursor);
@@ -909,10 +942,26 @@ class CodeEditor {
909
942
  {
910
943
  if( mouse_pos[0] > this.code.scrollWidth || mouse_pos[1] > this.code.scrollHeight )
911
944
  return; // Scrollbar click
945
+
946
+ // Left click only...
947
+ if( e.button === 2 )
948
+ {
949
+ this.processClick(e);
950
+
951
+ this.canOpenContextMenu = !this.selection;
952
+
953
+ if( this.selection )
954
+ {
955
+ this.canOpenContextMenu |= (cursor.line >= this.selection.fromY && cursor.line <= this.selection.toY
956
+ && cursor.position >= this.selection.fromX && cursor.position <= this.selection.toX);
957
+ if( this.canOpenContextMenu )
958
+ return;
959
+ }
960
+ }
961
+
912
962
  this.lastMouseDown = LX.getTime();
913
963
  this.state.selectingText = true;
914
964
  this.endSelection();
915
- return;
916
965
  }
917
966
 
918
967
  else if( e.type == 'mouseup' )
@@ -955,15 +1004,26 @@ class CodeEditor {
955
1004
  }
956
1005
  }
957
1006
 
958
- else if ( e.type == 'contextmenu' ) {
959
- e.preventDefault()
960
- LX.addContextMenu( "Format code", e, m => {
961
- m.add( "JSON", () => {
1007
+ else if ( e.type == 'contextmenu' )
1008
+ {
1009
+ e.preventDefault();
1010
+
1011
+ if( !this.canOpenContextMenu )
1012
+ return;
1013
+
1014
+ LX.addContextMenu( null, e, m => {
1015
+ m.add( "Format/JSON", () => {
962
1016
  let json = this.toJSONFormat(this.getText());
963
1017
  this.code.lines = json.split("\n");
964
1018
  this.processLines();
965
1019
  } );
1020
+ m.add( "" )
1021
+ m.add( "Cut", () => { this._cutContent(); } );
1022
+ m.add( "Copy", () => { this._copyContent(); } );
1023
+ m.add( "Paste", () => { this._pasteContent(); } );
966
1024
  });
1025
+
1026
+ this.canOpenContextMenu = false;
967
1027
  }
968
1028
  }
969
1029
 
@@ -1134,27 +1194,7 @@ class CodeEditor {
1134
1194
  this.cursorToLine(cursor, this.selection.toY);
1135
1195
  break;
1136
1196
  case 'c': // copy
1137
- let text_to_copy = "";
1138
- if( !this.selection ) {
1139
- text_to_copy = "\n" + this.code.lines[cursor.line];
1140
- }
1141
- else {
1142
- const separator = "_NEWLINE_";
1143
- let code = this.code.lines.join(separator);
1144
-
1145
- // Get linear start index
1146
- let index = 0;
1147
-
1148
- for(let i = 0; i <= this.selection.fromY; i++)
1149
- index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[i].length);
1150
-
1151
- index += this.selection.fromY * separator.length;
1152
- const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
1153
- const text = code.substr(index, num_chars);
1154
- const lines = text.split(separator);
1155
- text_to_copy = lines.join('\n');
1156
- }
1157
- navigator.clipboard.writeText(text_to_copy).then(() => console.log("Successfully copied"), (err) => console.error("Error"));
1197
+ this._copyContent();
1158
1198
  return;
1159
1199
  case 'd': // duplicate line
1160
1200
  e.preventDefault();
@@ -1167,13 +1207,10 @@ class CodeEditor {
1167
1207
  this.onsave( this.getText() );
1168
1208
  return;
1169
1209
  case 'v': // paste
1170
- let text = await navigator.clipboard.readText();
1171
- this.appendText(text);
1210
+ this._pasteContent();
1172
1211
  return;
1173
1212
  case 'x': // cut line
1174
- const to_copy = this.code.lines.splice(lidx, 1)[0];
1175
- this.processLines(lidx);
1176
- navigator.clipboard.writeText(to_copy + '\n');
1213
+ this._cutContent();
1177
1214
  return;
1178
1215
  case 'z': // undo
1179
1216
  if(!this.code.undoSteps.length)
@@ -1263,6 +1300,7 @@ class CodeEditor {
1263
1300
  if( this.selection )
1264
1301
  {
1265
1302
  this.actions['Backspace'].callback(lidx, cursor, e);
1303
+ lidx = cursor.line; // Update this, since it's from the old code
1266
1304
  }
1267
1305
 
1268
1306
  // Append key
@@ -1275,21 +1313,107 @@ class CodeEditor {
1275
1313
 
1276
1314
  this.cursorToRight( key );
1277
1315
 
1278
- // Other special char cases...
1316
+ // Some custom cases for auto key pair (), {}, "", '', ...
1279
1317
 
1280
- if( key == '{' )
1318
+ const pairKeys = ["\"", "'", "(", "{"];
1319
+ if( pairKeys.indexOf( key ) > -1 && !this.wasKeyPaired )
1281
1320
  {
1282
- this.root.dispatchEvent(new KeyboardEvent('keydown', {'key': '}'}));
1321
+ // Find pair key
1322
+ let pair = key;
1323
+ switch(key)
1324
+ {
1325
+ case "'":
1326
+ case "\"":
1327
+ break;
1328
+ case "(": pair = ")"; break;
1329
+ case "{": pair = "}"; break;
1330
+ }
1331
+
1332
+ // Make sure to detect later that the key is paired automatically to avoid loops...
1333
+ this.wasKeyPaired = true;
1334
+
1335
+ this.root.dispatchEvent(new KeyboardEvent('keydown', { 'key': pair }));
1283
1336
  this.cursorToLeft( key, cursor );
1284
- return; // It will be processed with the above event
1337
+ return;
1285
1338
  }
1286
1339
 
1340
+ // Once here we can pair keys again
1341
+ delete this.wasKeyPaired;
1342
+
1287
1343
  // Update only the current line, since it's only an appended key
1288
1344
  this.processLine( lidx );
1289
1345
 
1290
1346
  // Manage autocomplete
1291
1347
 
1292
- this.showAutoCompleteBox( key, cursor );
1348
+ if( this.useAutoComplete )
1349
+ this.showAutoCompleteBox( key, cursor );
1350
+ }
1351
+
1352
+ async _pasteContent() {
1353
+ let text = await navigator.clipboard.readText();
1354
+ this.appendText(text);
1355
+ }
1356
+
1357
+ async _copyContent() {
1358
+
1359
+ let cursor = this.cursors.children[0];
1360
+ let text_to_copy = "";
1361
+
1362
+ if( !this.selection ) {
1363
+ text_to_copy = "\n" + this.code.lines[cursor.line];
1364
+ }
1365
+ else {
1366
+ const separator = "_NEWLINE_";
1367
+ let code = this.code.lines.join(separator);
1368
+
1369
+ // Get linear start index
1370
+ let index = 0;
1371
+
1372
+ for(let i = 0; i <= this.selection.fromY; i++)
1373
+ index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[i].length);
1374
+
1375
+ index += this.selection.fromY * separator.length;
1376
+ const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
1377
+ const text = code.substr(index, num_chars);
1378
+ const lines = text.split(separator);
1379
+ text_to_copy = lines.join('\n');
1380
+ }
1381
+
1382
+ navigator.clipboard.writeText(text_to_copy).then(() => console.log("Successfully copied"), (err) => console.error("Error"));
1383
+ }
1384
+
1385
+ async _cutContent() {
1386
+
1387
+ let cursor = this.cursors.children[0];
1388
+ let lidx = cursor.line;
1389
+ let text_to_cut = "";
1390
+
1391
+ if( !this.selection ) {
1392
+ text_to_cut = "\n" + this.code.lines[cursor.line];
1393
+ this.code.lines.splice(lidx, 1);
1394
+ this.processLines(lidx);
1395
+ this.resetCursorPos( CodeEditor.CURSOR_LEFT );
1396
+ }
1397
+ else {
1398
+ const separator = "_NEWLINE_";
1399
+ let code = this.code.lines.join(separator);
1400
+
1401
+ // Get linear start index
1402
+ let index = 0;
1403
+
1404
+ for(let i = 0; i <= this.selection.fromY; i++)
1405
+ index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[i].length);
1406
+
1407
+ index += this.selection.fromY * separator.length;
1408
+ const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
1409
+ const text = code.substr(index, num_chars);
1410
+ const lines = text.split(separator);
1411
+ text_to_cut = lines.join('\n');
1412
+
1413
+ this.deleteSelection( cursor );
1414
+ }
1415
+
1416
+ navigator.clipboard.writeText(text_to_cut).then(() => console.log("Successfully cut"), (err) => console.error("Error"));
1293
1417
  }
1294
1418
 
1295
1419
  action( key, deleteSelection, fn ) {
@@ -1421,6 +1545,11 @@ class CodeEditor {
1421
1545
  }
1422
1546
  }
1423
1547
 
1548
+ _mustHightlightWord( token, kindArray ) {
1549
+
1550
+ return kindArray[this.highlight] && kindArray[this.highlight].indexOf(token) > -1;
1551
+ }
1552
+
1424
1553
  processToken(token, linespan, prev, next) {
1425
1554
 
1426
1555
  let sString = false;
@@ -1447,16 +1576,16 @@ class CodeEditor {
1447
1576
  else if( this._building_string )
1448
1577
  span.classList.add("cm-str");
1449
1578
 
1450
- else if( this.keywords[this.highlight] && this.keywords[this.highlight].indexOf(token) > -1 )
1579
+ else if( this._mustHightlightWord( token, this.keywords ) )
1451
1580
  span.classList.add("cm-kwd");
1452
1581
 
1453
- else if( this.builtin[this.highlight] && this.builtin[this.highlight].indexOf(token) > -1 )
1582
+ else if( this._mustHightlightWord( token, this.builtin ) )
1454
1583
  span.classList.add("cm-bln");
1455
1584
 
1456
- else if( this.literals[this.highlight] && this.literals[this.highlight].indexOf(token) > -1 )
1457
- span.classList.add("cm-lit");
1585
+ else if( this._mustHightlightWord( token, this.statementsAndDeclarations ) )
1586
+ span.classList.add("cm-std");
1458
1587
 
1459
- else if( this.symbols[this.highlight] && this.symbols[this.highlight].indexOf(token) > -1 )
1588
+ else if( this._mustHightlightWord( token, this.symbols ) )
1460
1589
  span.classList.add("cm-sym");
1461
1590
 
1462
1591
  else if( token.substr(0, 2) == '//' )
@@ -1515,6 +1644,10 @@ class CodeEditor {
1515
1644
 
1516
1645
  isType( token, prev, next ) {
1517
1646
 
1647
+ // Common case
1648
+ if( this._mustHightlightWord( token, this.types ) )
1649
+ return true;
1650
+
1518
1651
  if( this.highlight == 'JavaScript' )
1519
1652
  {
1520
1653
  return (prev == 'class' && next == '{') || (prev == 'new' && next == '(');
@@ -1533,7 +1666,7 @@ class CodeEditor {
1533
1666
 
1534
1667
  if( !this.selection || (this.selection.fromY != this.selection.toY) )
1535
1668
  return false;
1536
-
1669
+
1537
1670
  const _lastLeft = cursor._left;
1538
1671
 
1539
1672
  // Insert first..
@@ -1544,8 +1677,7 @@ class CodeEditor {
1544
1677
  ].join('');
1545
1678
 
1546
1679
  // Go to the end of the word
1547
- this.cursorToString(cursor,
1548
- this.code.lines[lidx].slice(this.selection.fromX, this.selection.toX + 1));
1680
+ this.cursorToPosition(cursor, this.selection.toX + 1);
1549
1681
 
1550
1682
  // Change next key?
1551
1683
  switch(key)
@@ -1878,7 +2010,7 @@ class CodeEditor {
1878
2010
 
1879
2011
  runScript( code ) {
1880
2012
  var script = document.createElement('script');
1881
- script.type = 'text/javascript';
2013
+ script.type = 'module';
1882
2014
  script.innerHTML = code;
1883
2015
  // script.src = url[i] + ( version ? "?version=" + version : "" );
1884
2016
  script.async = false;
@@ -1921,23 +2053,27 @@ class CodeEditor {
1921
2053
 
1922
2054
  showAutoCompleteBox( key, cursor ) {
1923
2055
 
1924
- // Delimiter.. no autocomplete for this case
1925
- if( key == ' ' )
1926
- return;
1927
-
1928
2056
  const [word, start, end] = this.getWordAtPos( cursor, -1 );
1929
- if(!word.length)
1930
- return;
2057
+ if(key == ' ' || !word.length) {
2058
+ this.hideAutoCompleteBox();
2059
+ return;
2060
+ }
1931
2061
 
1932
2062
  this.autocomplete.innerHTML = ""; // Clear all suggestions
1933
2063
 
1934
2064
  let suggestions = [];
1935
2065
 
1936
2066
  // Add language special keys...
1937
- suggestions = suggestions.concat( this.keywords[ this.highlight ] );
1938
- suggestions = suggestions.concat( this.literals[ this.highlight ] );
1939
- suggestions = suggestions.concat( this.builtin[ this.highlight ] );
1940
- suggestions = suggestions.concat( Object.keys(this.code.tokens).filter( a => a != word ) );
2067
+ suggestions = suggestions.concat(
2068
+ this.builtin[ this.highlight ] ?? [],
2069
+ this.keywords[ this.highlight ] ?? [],
2070
+ this.statementsAndDeclarations[ this.highlight ] ?? [],
2071
+ this.types[ this.highlight ] ?? [],
2072
+ this.utils[ this.highlight ] ?? []
2073
+ );
2074
+
2075
+ // Add words in current tab plus remove current word
2076
+ // suggestions = suggestions.concat( Object.keys(this.code.tokens).filter( a => a != word ) );
1941
2077
 
1942
2078
  // Remove single chars and duplicates...
1943
2079
  suggestions = suggestions.filter( (value, index) => value.length > 1 && suggestions.indexOf(value) === index );
@@ -1953,6 +2089,17 @@ class CodeEditor {
1953
2089
  var pre = document.createElement('pre');
1954
2090
  this.autocomplete.appendChild(pre);
1955
2091
 
2092
+ var icon = document.createElement('a');
2093
+
2094
+ if( this._mustHightlightWord( s, this.utils ) )
2095
+ icon.className = "fa fa-cube";
2096
+ else if( this._mustHightlightWord( s, this.types ) )
2097
+ icon.className = "fa fa-code";
2098
+ else
2099
+ icon.className = "fa fa-font";
2100
+
2101
+ pre.appendChild(icon);
2102
+
1956
2103
  pre.addEventListener( 'click', () => {
1957
2104
  this.autoCompleteWord( cursor, s );
1958
2105
  } );
@@ -2033,13 +2180,13 @@ class CodeEditor {
2033
2180
  for( let childSpan of child.childNodes )
2034
2181
  word += childSpan.innerHTML;
2035
2182
 
2036
- return [word, i ]; // Get text of the span inside the 'pre' element
2183
+ return [ word, i ]; // Get text of the span inside the 'pre' element
2037
2184
  }
2038
2185
  }
2039
2186
  }
2040
2187
 
2041
2188
  moveArrowSelectedAutoComplete( dir ) {
2042
-
2189
+
2043
2190
  if( !this.isAutoCompleteActive )
2044
2191
  return;
2045
2192
 
@@ -2052,7 +2199,7 @@ class CodeEditor {
2052
2199
  if( (idx + offset) < 0 ) return;
2053
2200
  }
2054
2201
 
2055
- this.autocomplete.scrollTop += offset * 20;
2202
+ this.autocomplete.scrollTop += offset * 18;
2056
2203
 
2057
2204
  // Remove selected from the current word and add it to the next one
2058
2205
  this.autocomplete.childNodes[ idx ].classList.remove('selected');
@@ -787,7 +787,7 @@ class Timeline {
787
787
  for(var i = this.tracksDrawn.length - 1; i >= 0; --i)
788
788
  {
789
789
  var t = this.tracksDrawn[i];
790
- if( localY >= t[1] && localY < (t[1] + t[2]) )
790
+ if( t[1] >= this.topMargin && localY >= t[1] && localY < (t[1] + t[2]) )
791
791
  {
792
792
  track = t[0];
793
793
  break;
@@ -820,10 +820,6 @@ class Timeline {
820
820
  const discard = this.movingKeys || (LX.UTILS.getTime() - this.clickTime) > 420; // ms
821
821
  this.movingKeys ? innerSetTime( this.currentTime ) : 0;
822
822
 
823
- if(e.button == 0 && this.grabbing && this.onClipMoved && this.lastClipsSelected.length){
824
- this.onClipMoved(this.lastClipsSelected);
825
- }
826
-
827
823
  this.grabbing_timeline = false;
828
824
  this.grabbing = false;
829
825
  this.grabbingScroll = false;
package/build/lexgui.css CHANGED
@@ -2900,6 +2900,14 @@ ul.lexassetscontent {
2900
2900
  margin: 0;
2901
2901
  pointer-events: unset;
2902
2902
  cursor: default;
2903
+ height: 18px;
2904
+ }
2905
+
2906
+ .lexcodeeditor .autocomplete pre a {
2907
+ font-size: 11px;
2908
+ margin-top: 4px;
2909
+ margin-right: 6px;
2910
+ margin-left: 2px;
2903
2911
  }
2904
2912
 
2905
2913
  .lexcodeeditor .autocomplete pre:hover {
@@ -2912,7 +2920,6 @@ ul.lexassetscontent {
2912
2920
  }
2913
2921
 
2914
2922
  .lexcodeeditor .autocomplete pre span {
2915
- margin: 2px 0px;
2916
2923
  cursor: inherit;
2917
2924
  }
2918
2925
 
@@ -2928,7 +2935,7 @@ ul.lexassetscontent {
2928
2935
  .cm-kwd { color: #218cce; } /* keyword */
2929
2936
  .cm-com { color: #5cab5a; } /* comment */
2930
2937
  .cm-typ { color: #36c0b0; } /* type */
2931
- .cm-lit { color: #cf6dcf; } /* literal */
2938
+ .cm-std { color: #cf6dcf; } /* statements & declarations */
2932
2939
  .cm-bln { color: inherit; } /* builtin */
2933
2940
  .cm-dec { color: #b1ce9b; } /* decimal */
2934
2941
  .cm-sym { color: #e7ded2; } /* symbol */
@@ -2938,7 +2945,7 @@ ul.lexassetscontent {
2938
2945
  .cm-kwd.css { color: #e8be53; } /* keyword */
2939
2946
  .cm-com.css { color: #5cab5a; } /* comment */
2940
2947
  .cm-typ.css { color: #b7c3ec; } /* type */
2941
- .cm-lit.css { color: #cf6dcf; } /* literal */
2948
+ .cm-std.css { color: #cf6dcf; } /* statements & declarations */
2942
2949
  .cm-bln.css { color: #2194ce; } /* builtin */
2943
2950
  .cm-dec.css { color: #b1ce9b; } /* decimal */
2944
2951
  .cm-sym.css { color: #f9d620; } /* symbol */
@@ -2948,7 +2955,7 @@ ul.lexassetscontent {
2948
2955
  .cm-kwd.json { color: inherit; } /* keyword */
2949
2956
  .cm-com.json { color: inherit; } /* comment */
2950
2957
  .cm-typ.json { color: inherit; } /* type */
2951
- .cm-lit.json { color: inherit; } /* literal */
2958
+ .cm-std.json { color: inherit; } /* statements & declarations */
2952
2959
  .cm-bln.json { color: inherit; } /* builtin */
2953
2960
  .cm-dec.json { color: #b1ce9b; } /* decimal */
2954
2961
  .cm-sym.json { color: #cf6dcf; } /* symbol */
@@ -2958,7 +2965,7 @@ ul.lexassetscontent {
2958
2965
  .cm-kwd.glsl { color: #2194ce; } /* keyword */
2959
2966
  .cm-com.glsl { color: #5cab5a; } /* comment */
2960
2967
  .cm-typ.glsl { color: #36c0b0; } /* type */
2961
- .cm-lit.glsl { color: #cf6dcf; } /* literal */
2968
+ .cm-std.glsl { color: #cf6dcf; } /* statements & declarations */
2962
2969
  .cm-bln.glsl { color: #cfc159; } /* builtin */
2963
2970
  .cm-dec.glsl { color: #b1ce9b; } /* decimal */
2964
2971
  .cm-sym.glsl { color: #f9cb20; } /* symbol */
@@ -2968,14 +2975,14 @@ ul.lexassetscontent {
2968
2975
  .cm-kwd.wgsl { color: #2194ce; } /* keyword */
2969
2976
  .cm-com.wgsl { color: #5cab5a; } /* comment */
2970
2977
  .cm-typ.wgsl { color: #36c0b0; } /* type */
2971
- .cm-lit.wgsl { color: #cf6dcf; } /* literal */
2978
+ .cm-std.wgsl { color: #cf6dcf; } /* statements & declarations */
2972
2979
  .cm-bln.wgsl { color: #cfc159; } /* builtin */
2973
2980
  .cm-dec.wgsl { color: #b1ce9b; } /* decimal */
2974
2981
  .cm-sym.wgsl { color: #f9cb20; } /* symbol */
2975
2982
  .cm-mtd.wgsl { color: #e0cc68; } /* method */
2976
2983
 
2977
2984
  /* plain color */
2978
- .cm-str.plaintext, .cm-kwd.plaintext, .cm-com.plaintext, .cm-typ.plaintext, .cm-lit.plaintext,
2985
+ .cm-str.plaintext, .cm-kwd.plaintext, .cm-com.plaintext, .cm-typ.plaintext, .cm-std.plaintext,
2979
2986
  .cm-bln.plaintext, .cm-dec.plaintext, .cm-sym.plaintext, .cm-mtd.plaintext { color: inherit; }
2980
2987
 
2981
2988
 
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.11",
15
+ version: "0.1.13",
16
16
  ready: false,
17
17
  components: [], // specific pre-build components
18
18
  signals: {} // events and triggers
@@ -143,7 +143,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
143
143
 
144
144
  // Other utils
145
145
 
146
- function set_as_draggable(domEl) {
146
+ function makeDraggable( domEl, targetClass ) {
147
147
 
148
148
  let offsetX;
149
149
  let offsetY;
@@ -153,7 +153,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
153
153
  domEl.addEventListener("mousedown", function(e) {
154
154
  // e.stopPropagation();
155
155
  // e.stopImmediatePropagation();
156
- currentTarget = e.target.classList.contains('lexdialogtitle') ? e.target : null;
156
+ currentTarget = (e.target.classList.contains(targetClass) || !targetClass) ? e.target : null;
157
157
  });
158
158
 
159
159
  domEl.addEventListener("dragstart", function(e) {
@@ -169,12 +169,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
169
169
  const rect = e.target.getBoundingClientRect();
170
170
  offsetX = e.clientX - rect.x;
171
171
  offsetY = e.clientY - rect.y;
172
- e.dataTransfer.setData('branch_title', e.target.querySelector(".lexdialogtitle").innerText);
173
- e.dataTransfer.setData('dialog_id', e.target.id);
174
-
175
172
  document.addEventListener("mousemove", moveFunc );
176
- console.log("wefwef");
177
-
178
173
  }, false);
179
174
 
180
175
  const moveFunc = (e) => {
@@ -194,6 +189,8 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
194
189
  }
195
190
  });
196
191
  }
192
+
193
+ LX.makeDraggable = makeDraggable;
197
194
 
198
195
  function create_global_searchbar( root ) {
199
196
 
@@ -3838,6 +3835,8 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3838
3835
  list_options.style.height = "calc(100% - 25px)";
3839
3836
 
3840
3837
  filter.addEventListener('focusout', function(e) {
3838
+ if (e.relatedTarget && e.relatedTarget.tagName == "UL" && e.relatedTarget.classList.contains("lexoptions"))
3839
+ return;
3841
3840
  list.toggleAttribute('hidden', true);
3842
3841
  });
3843
3842
  }
@@ -5626,7 +5625,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5626
5625
  this.title = titleDiv;
5627
5626
 
5628
5627
  if(draggable)
5629
- set_as_draggable(root);
5628
+ makeDraggable( root, 'lexdialogtitle' );
5630
5629
 
5631
5630
  // Process position and size
5632
5631
  if(size.length && typeof(size[0]) != "string")
@@ -6418,6 +6417,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6418
6417
  this.skip_preview = options.skip_preview ?? false;
6419
6418
  this.only_folders = options.only_folders ?? true;
6420
6419
  this.preview_actions = options.preview_actions ?? [];
6420
+ this.context_menu = options.context_menu ?? [];
6421
6421
 
6422
6422
  if( !this.skip_browser )
6423
6423
  {
@@ -6804,23 +6804,26 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
6804
6804
  }
6805
6805
  });
6806
6806
 
6807
- itemEl.addEventListener('contextmenu', function(e) {
6808
- e.preventDefault();
6809
-
6810
- const multiple = that.content.querySelectorAll('.selected').length;
6811
-
6812
- LX.addContextMenu( multiple > 1 ? (multiple + " selected") :
6813
- is_folder ? item.id : item.type, e, m => {
6814
- if(multiple <= 1)
6815
- m.add("Rename");
6816
- if( !is_folder )
6817
- m.add("Clone", that._cloneItem.bind(that, item));
6818
- if(multiple <= 1)
6819
- m.add("Properties");
6820
- m.add("");
6821
- m.add("Delete", that._deleteItem.bind(that, item));
6807
+ if( that.context_menu )
6808
+ {
6809
+ itemEl.addEventListener('contextmenu', function(e) {
6810
+ e.preventDefault();
6811
+
6812
+ const multiple = that.content.querySelectorAll('.selected').length;
6813
+
6814
+ LX.addContextMenu( multiple > 1 ? (multiple + " selected") :
6815
+ is_folder ? item.id : item.type, e, m => {
6816
+ if(multiple <= 1)
6817
+ m.add("Rename");
6818
+ if( !is_folder )
6819
+ m.add("Clone", that._clone_item.bind(that, item));
6820
+ if(multiple <= 1)
6821
+ m.add("Properties");
6822
+ m.add("");
6823
+ m.add("Delete", that._delete_item.bind(that, item));
6824
+ });
6822
6825
  });
6823
- });
6826
+ }
6824
6827
 
6825
6828
  itemEl.addEventListener("dragstart", function(e) {
6826
6829
  e.preventDefault();
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  var LX = {
11
- version: "0.1.11",
11
+ version: "0.1.13",
12
12
  ready: false,
13
13
  components: [], // specific pre-build components
14
14
  signals: {} // events and triggers
@@ -139,7 +139,7 @@ LX.vec2 = vec2;
139
139
 
140
140
  // Other utils
141
141
 
142
- function set_as_draggable(domEl) {
142
+ function makeDraggable( domEl, targetClass ) {
143
143
 
144
144
  let offsetX;
145
145
  let offsetY;
@@ -149,7 +149,7 @@ function set_as_draggable(domEl) {
149
149
  domEl.addEventListener("mousedown", function(e) {
150
150
  // e.stopPropagation();
151
151
  // e.stopImmediatePropagation();
152
- currentTarget = e.target.classList.contains('lexdialogtitle') ? e.target : null;
152
+ currentTarget = (e.target.classList.contains(targetClass) || !targetClass) ? e.target : null;
153
153
  });
154
154
 
155
155
  domEl.addEventListener("dragstart", function(e) {
@@ -165,12 +165,7 @@ function set_as_draggable(domEl) {
165
165
  const rect = e.target.getBoundingClientRect();
166
166
  offsetX = e.clientX - rect.x;
167
167
  offsetY = e.clientY - rect.y;
168
- e.dataTransfer.setData('branch_title', e.target.querySelector(".lexdialogtitle").innerText);
169
- e.dataTransfer.setData('dialog_id', e.target.id);
170
-
171
168
  document.addEventListener("mousemove", moveFunc );
172
- console.log("wefwef");
173
-
174
169
  }, false);
175
170
 
176
171
  const moveFunc = (e) => {
@@ -191,6 +186,8 @@ function set_as_draggable(domEl) {
191
186
  });
192
187
  }
193
188
 
189
+ LX.makeDraggable = makeDraggable;
190
+
194
191
  function create_global_searchbar( root ) {
195
192
 
196
193
  let global_search = document.createElement("div");
@@ -3834,6 +3831,8 @@ class Panel {
3834
3831
  list_options.style.height = "calc(100% - 25px)";
3835
3832
 
3836
3833
  filter.addEventListener('focusout', function(e) {
3834
+ if (e.relatedTarget && e.relatedTarget.tagName == "UL" && e.relatedTarget.classList.contains("lexoptions"))
3835
+ return;
3837
3836
  list.toggleAttribute('hidden', true);
3838
3837
  });
3839
3838
  }
@@ -5622,7 +5621,7 @@ class Dialog {
5622
5621
  this.title = titleDiv;
5623
5622
 
5624
5623
  if(draggable)
5625
- set_as_draggable(root);
5624
+ makeDraggable( root, 'lexdialogtitle' );
5626
5625
 
5627
5626
  // Process position and size
5628
5627
  if(size.length && typeof(size[0]) != "string")
@@ -6414,6 +6413,7 @@ class AssetView {
6414
6413
  this.skip_preview = options.skip_preview ?? false;
6415
6414
  this.only_folders = options.only_folders ?? true;
6416
6415
  this.preview_actions = options.preview_actions ?? [];
6416
+ this.context_menu = options.context_menu ?? [];
6417
6417
 
6418
6418
  if( !this.skip_browser )
6419
6419
  {
@@ -6800,23 +6800,26 @@ class AssetView {
6800
6800
  }
6801
6801
  });
6802
6802
 
6803
- itemEl.addEventListener('contextmenu', function(e) {
6804
- e.preventDefault();
6803
+ if( that.context_menu )
6804
+ {
6805
+ itemEl.addEventListener('contextmenu', function(e) {
6806
+ e.preventDefault();
6805
6807
 
6806
- const multiple = that.content.querySelectorAll('.selected').length;
6807
-
6808
- LX.addContextMenu( multiple > 1 ? (multiple + " selected") :
6809
- is_folder ? item.id : item.type, e, m => {
6810
- if(multiple <= 1)
6811
- m.add("Rename");
6812
- if( !is_folder )
6813
- m.add("Clone", that._cloneItem.bind(that, item));
6814
- if(multiple <= 1)
6815
- m.add("Properties");
6816
- m.add("");
6817
- m.add("Delete", that._deleteItem.bind(that, item));
6808
+ const multiple = that.content.querySelectorAll('.selected').length;
6809
+
6810
+ LX.addContextMenu( multiple > 1 ? (multiple + " selected") :
6811
+ is_folder ? item.id : item.type, e, m => {
6812
+ if(multiple <= 1)
6813
+ m.add("Rename");
6814
+ if( !is_folder )
6815
+ m.add("Clone", that._clone_item.bind(that, item));
6816
+ if(multiple <= 1)
6817
+ m.add("Properties");
6818
+ m.add("");
6819
+ m.add("Delete", that._delete_item.bind(that, item));
6820
+ });
6818
6821
  });
6819
- });
6822
+ }
6820
6823
 
6821
6824
  itemEl.addEventListener("dragstart", function(e) {
6822
6825
  e.preventDefault();
@@ -42,25 +42,23 @@
42
42
  };
43
43
 
44
44
  let editor = new LX.CodeEditor(rightArea, {
45
- // allow_add_scripts: false
45
+ // allow_add_scripts: false,
46
+ // autocomplete: false
46
47
  });
47
48
 
48
- editor.loadFile( "../data/test.json" );
49
- editor.loadFile( "../data/style.css" );
50
49
  editor.loadFile( "../data/script.js" );
51
50
 
51
+ var ctx = canvas.getContext("2d");
52
+ ctx.fillStyle = "#b7a9b1";
53
+ ctx.font = "48px Monospace";
54
+ ctx.strokeStyle = "#ff1999";
55
+
52
56
  function loop(dt) {
53
57
 
54
58
  var ctx = canvas.getContext("2d");
55
59
 
56
- ctx.fillStyle = "#b7a9b1";
57
60
  ctx.fillRect(0, 0, canvas.width, canvas.height);
58
-
59
- ctx.font = "48px Monospace";
60
- ctx.fillStyle = "#ff1999";
61
-
62
- const text = "Lexgui.js @jxarco";
63
- ctx.fillText(text, 300, 300);
61
+ ctx.strokeText("Lexgui.js @jxarco", 200, 300);
64
62
 
65
63
  requestAnimationFrame(loop);
66
64
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lexgui",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "JS library to create web graphical user interfaces",
5
5
  "type": "module",
6
6
  "main": "./build/lexgui.js",