lexgui 0.1.45 → 0.2.0

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.
@@ -237,6 +237,8 @@ class CodeEditor {
237
237
  static CODE_MIN_FONT_SIZE = 9;
238
238
  static CODE_MAX_FONT_SIZE = 22;
239
239
 
240
+ static LINE_GUTTER_WIDTH = 48;
241
+
240
242
  /**
241
243
  * @param {*} options
242
244
  * name:
@@ -313,6 +315,9 @@ class CodeEditor {
313
315
  this.base_area = area;
314
316
  this.area = new LX.Area( { className: "lexcodeeditor", height: "100%", skipAppend: true } );
315
317
 
318
+ this.skipCodeInfo = options.skipInfo ?? false;
319
+ this.disableEdition = options.disableEdition ?? false;
320
+
316
321
  this.tabs = this.area.addTabs( { onclose: (name) => {
317
322
  delete this.openedTabs[ name ];
318
323
  if( Object.keys( this.openedTabs ).length < 2 )
@@ -322,12 +327,15 @@ class CodeEditor {
322
327
  }
323
328
  } } );
324
329
 
325
- this.tabs.root.addEventListener( 'dblclick', (e) => {
326
- if( options.allowAddScripts ?? true ) {
327
- e.preventDefault();
328
- this.addTab("unnamed.js", true);
329
- }
330
- } );
330
+ if( !this.disableEdition )
331
+ {
332
+ this.tabs.root.addEventListener( 'dblclick', (e) => {
333
+ if( options.allowAddScripts ?? true ) {
334
+ e.preventDefault();
335
+ this.addTab("unnamed.js", true);
336
+ }
337
+ } );
338
+ }
331
339
 
332
340
  // Full editor
333
341
  area.root.classList.add('codebasearea');
@@ -339,15 +347,16 @@ class CodeEditor {
339
347
  this.root.tabIndex = -1;
340
348
  area.attach( this.root );
341
349
 
342
- this.skipCodeInfo = options.skipInfo ?? false;
343
- this.disableEdition = options.disableEdition ?? false;
344
-
345
350
  if( !this.disableEdition )
346
351
  {
347
352
  this.root.addEventListener( 'keydown', this.processKey.bind( this) );
348
353
  this.root.addEventListener( 'focus', this.processFocus.bind( this, true ) );
349
354
  this.root.addEventListener( 'focusout', this.processFocus.bind( this, false ) );
350
355
  }
356
+ else
357
+ {
358
+ this.root.classList.add( "disabled" );
359
+ }
351
360
 
352
361
  this.root.addEventListener( 'mousedown', this.processMouse.bind(this) );
353
362
  this.root.addEventListener( 'mouseup', this.processMouse.bind(this) );
@@ -373,7 +382,7 @@ class CodeEditor {
373
382
  this.selections = {};
374
383
 
375
384
  // Css char synchronization
376
- this.xPadding = "48px";
385
+ this.xPadding = CodeEditor.LINE_GUTTER_WIDTH + "px";
377
386
 
378
387
  // Add main cursor
379
388
  this._addCursor( 0, 0, true, true );
@@ -383,57 +392,60 @@ class CodeEditor {
383
392
  this.codeScroller = this.tabs.area.root;
384
393
  this.firstLineInViewport = 0;
385
394
  this.lineScrollMargin = new LX.vec2( 20, 20 ); // [ mUp, mDown ]
386
- window.scroller = this.codeScroller;
387
395
 
388
396
  let lastScrollTopValue = -1;
389
- this.codeScroller.addEventListener( 'scroll', e => {
390
397
 
391
- if( this._discardScroll )
392
- {
393
- this._discardScroll = false;
394
- return;
395
- }
398
+ if( !this.disableEdition )
399
+ {
400
+ this.codeScroller.addEventListener( 'scroll', e => {
401
+
402
+ if( this._discardScroll )
403
+ {
404
+ this._discardScroll = false;
405
+ return;
406
+ }
396
407
 
397
- this.setScrollBarValue( 'vertical' );
408
+ this.setScrollBarValue( 'vertical' );
398
409
 
399
- const scrollTop = this.getScrollTop();
410
+ const scrollTop = this.getScrollTop();
400
411
 
401
- // Scroll down...
402
- if( scrollTop > lastScrollTopValue )
403
- {
404
- if( this.visibleLinesViewport.y < (this.code.lines.length - 1) )
412
+ // Scroll down...
413
+ if( scrollTop > lastScrollTopValue )
405
414
  {
406
- const totalLinesInViewport = ((this.codeScroller.offsetHeight - 36) / this.lineHeight)|0;
407
- const scrollDownBoundary =
408
- ( Math.max( this.visibleLinesViewport.y - totalLinesInViewport, 0 ) - 1 ) * this.lineHeight;
415
+ if( this.visibleLinesViewport.y < (this.code.lines.length - 1) )
416
+ {
417
+ const totalLinesInViewport = ((this.codeScroller.offsetHeight - 36) / this.lineHeight)|0;
418
+ const scrollDownBoundary =
419
+ ( Math.max( this.visibleLinesViewport.y - totalLinesInViewport, 0 ) - 1 ) * this.lineHeight;
409
420
 
410
- if( scrollTop >= scrollDownBoundary )
421
+ if( scrollTop >= scrollDownBoundary )
422
+ this.processLines( CodeEditor.UPDATE_VISIBLE_LINES );
423
+ }
424
+ }
425
+ // Scroll up...
426
+ else
427
+ {
428
+ const scrollUpBoundary = parseInt( this.code.style.top );
429
+ if( scrollTop < scrollUpBoundary )
411
430
  this.processLines( CodeEditor.UPDATE_VISIBLE_LINES );
412
431
  }
413
- }
414
- // Scroll up...
415
- else
416
- {
417
- const scrollUpBoundary = parseInt( this.code.style.top );
418
- if( scrollTop < scrollUpBoundary )
419
- this.processLines( CodeEditor.UPDATE_VISIBLE_LINES );
420
- }
421
432
 
422
- lastScrollTopValue = scrollTop;
423
- });
433
+ lastScrollTopValue = scrollTop;
434
+ });
424
435
 
425
- this.codeScroller.addEventListener( 'wheel', e => {
426
- if( e.ctrlKey )
427
- {
428
- e.preventDefault();
429
- ( e.deltaY > 0.0 ? this._decreaseFontSize() : this._increaseFontSize() );
430
- }
431
- else
432
- {
433
- const dX = ( e.deltaY > 0.0 ? 10.0 : -10.0 ) * ( e.shiftKey ? 1.0 : 0.0 );
434
- if( dX != 0.0 ) this.setScrollBarValue( 'horizontal', dX );
435
- }
436
- });
436
+ this.codeScroller.addEventListener( 'wheel', e => {
437
+ if( e.ctrlKey )
438
+ {
439
+ e.preventDefault();
440
+ ( e.deltaY > 0.0 ? this._decreaseFontSize() : this._increaseFontSize() );
441
+ }
442
+ else
443
+ {
444
+ const dX = ( e.deltaY > 0.0 ? 10.0 : -10.0 ) * ( e.shiftKey ? 1.0 : 0.0 );
445
+ if( dX != 0.0 ) this.setScrollBarValue( 'horizontal', dX );
446
+ }
447
+ });
448
+ }
437
449
  }
438
450
 
439
451
  // This is only the container, line numbers are in the same line div
@@ -455,59 +467,62 @@ class CodeEditor {
455
467
  area.attach( this.hScrollbar.root );
456
468
  }
457
469
 
458
- // Add autocomplete box
470
+ if( !this.disableEdition )
459
471
  {
460
- var box = document.createElement( 'div' );
461
- box.className = "autocomplete";
462
- this.autocomplete = box;
463
- this.tabs.area.attach( box );
472
+ // Add autocomplete box
473
+ {
474
+ var box = document.createElement( 'div' );
475
+ box.className = "autocomplete";
476
+ this.autocomplete = box;
477
+ this.tabs.area.attach( box );
464
478
 
465
- this.isAutoCompleteActive = false;
466
- }
479
+ this.isAutoCompleteActive = false;
480
+ }
467
481
 
468
- // Add search box
469
- {
470
- var box = document.createElement( 'div' );
471
- box.className = "searchbox";
482
+ // Add search box
483
+ {
484
+ var box = document.createElement( 'div' );
485
+ box.className = "searchbox";
472
486
 
473
- var searchPanel = new LX.Panel();
474
- box.appendChild( searchPanel.root );
487
+ var searchPanel = new LX.Panel();
488
+ box.appendChild( searchPanel.root );
475
489
 
476
- searchPanel.sameLine( 4 );
477
- searchPanel.addText( null, "", null, { placeholder: "Find" } );
478
- searchPanel.addButton( null, "up", () => this.search( null, true ), { className: 'micro', icon: "fa fa-arrow-up" } );
479
- searchPanel.addButton( null, "down", () => this.search(), { className: 'micro', icon: "fa fa-arrow-down" } );
480
- searchPanel.addButton( null, "x", this.hideSearchBox.bind( this ), { className: 'micro', icon: "fa fa-xmark" } );
490
+ searchPanel.sameLine( 4 );
491
+ searchPanel.addText( null, "", null, { placeholder: "Find" } );
492
+ searchPanel.addButton( null, "up", () => this.search( null, true ), { className: 'micro', icon: "fa fa-arrow-up" } );
493
+ searchPanel.addButton( null, "down", () => this.search(), { className: 'micro', icon: "fa fa-arrow-down" } );
494
+ searchPanel.addButton( null, "x", this.hideSearchBox.bind( this ), { className: 'micro', icon: "fa fa-xmark" } );
481
495
 
482
- box.querySelector( 'input' ).addEventListener( 'keyup', e => {
483
- if( e.key == 'Escape' ) this.hideSearchBox();
484
- else if( e.key == 'Enter' ) this.search( e.target.value, !!e.shiftKey );
485
- } );
496
+ box.querySelector( 'input' ).addEventListener( 'keyup', e => {
497
+ if( e.key == 'Escape' ) this.hideSearchBox();
498
+ else if( e.key == 'Enter' ) this.search( e.target.value, !!e.shiftKey );
499
+ } );
486
500
 
487
- this.searchbox = box;
488
- this.tabs.area.attach( box );
489
- }
501
+ this.searchbox = box;
502
+ this.tabs.area.attach( box );
503
+ }
490
504
 
491
- // Add search LINE box
492
- {
493
- var box = document.createElement( 'div' );
494
- box.className = "searchbox gotoline";
505
+ // Add search LINE box
506
+ {
507
+ var box = document.createElement( 'div' );
508
+ box.className = "searchbox gotoline";
495
509
 
496
- var searchPanel = new LX.Panel();
497
- box.appendChild( searchPanel.root );
510
+ var searchPanel = new LX.Panel();
511
+ box.appendChild( searchPanel.root );
498
512
 
499
- searchPanel.addText( null, "", ( value, event ) => {
500
- input.value = ":" + value.replaceAll( ':', '' );
501
- this.goToLine( input.value.slice( 1 ) );
502
- }, { placeholder: "Go to line", trigger: "input" } );
513
+ searchPanel.addText( null, "", ( value, event ) => {
514
+ input.value = ":" + value.replaceAll( ':', '' );
515
+ this.goToLine( input.value.slice( 1 ) );
516
+ }, { placeholder: "Go to line", trigger: "input" } );
503
517
 
504
- let input = box.querySelector( 'input' );
505
- input.addEventListener( 'keyup', e => {
506
- if( e.key == 'Escape' ) this.hideSearchLineBox();
507
- } );
518
+ let input = box.querySelector( 'input' );
519
+ input.addEventListener( 'keyup', e => {
520
+ if( e.key == 'Escape' ) this.hideSearchLineBox();
521
+ } );
508
522
 
509
- this.searchlinebox = box;
510
- this.tabs.area.attach( box );
523
+ this.searchlinebox = box;
524
+ this.tabs.area.attach( box );
525
+ }
511
526
  }
512
527
 
513
528
  // Add code-sizer
@@ -1059,7 +1074,7 @@ class CodeEditor {
1059
1074
  this.addTab("+", false, "New File");
1060
1075
  }
1061
1076
 
1062
- this.addTab( options.name || "untitled", true, options.title, { language: "CSS" } );
1077
+ this.addTab( options.name || "untitled", true, options.title, { language: "Plain Text" } );
1063
1078
 
1064
1079
  // Create inspector panel
1065
1080
  let panel = this._createPanelInfo();
@@ -1609,6 +1624,11 @@ class CodeEditor {
1609
1624
 
1610
1625
  _onSelectTab( isNewTabButton, event, name ) {
1611
1626
 
1627
+ if( this.disableEdition )
1628
+ {
1629
+ return;
1630
+ }
1631
+
1612
1632
  if( isNewTabButton )
1613
1633
  {
1614
1634
  this._onNewTab( event );
@@ -1657,7 +1677,9 @@ class CodeEditor {
1657
1677
  }, 0 );
1658
1678
 
1659
1679
  if( repeats > 0 )
1680
+ {
1660
1681
  name = name.split( '.' ).join( '_' + repeats + '.' );
1682
+ }
1661
1683
 
1662
1684
  const isNewTabButton = ( name === '+' );
1663
1685
 
@@ -2435,10 +2457,11 @@ class CodeEditor {
2435
2457
 
2436
2458
  // We are out of the viewport and max length is different? Resize scrollbars...
2437
2459
  const maxLineLength = this.getMaxLineLength();
2438
- const numViewportChars = Math.floor( this.codeScroller.clientWidth / this.charWidth );
2460
+ const numViewportChars = Math.floor( ( this.codeScroller.clientWidth - CodeEditor.LINE_GUTTER_WIDTH ) / this.charWidth );
2439
2461
  if( maxLineLength >= numViewportChars && maxLineLength != this._lastMaxLineLength )
2440
2462
  {
2441
2463
  this.resize( maxLineLength );
2464
+ this.setScrollLeft( 1e4 );
2442
2465
  }
2443
2466
 
2444
2467
  // Manage autocomplete
@@ -3369,7 +3392,9 @@ class CodeEditor {
3369
3392
 
3370
3393
  // I think it's not necessary but...
3371
3394
  if( this.disableEdition )
3395
+ {
3372
3396
  return;
3397
+ }
3373
3398
 
3374
3399
  // Some selections don't depend on mouse up..
3375
3400
  if( cursor.selection ) cursor.selection.invertIfNecessary();
@@ -3455,7 +3480,7 @@ class CodeEditor {
3455
3480
  // Add horizontal scroll
3456
3481
 
3457
3482
  doAsync(() => {
3458
- var viewportSizeX = ( this.codeScroller.clientWidth + this.getScrollLeft() ) - 48; // Gutter offset
3483
+ var viewportSizeX = ( this.codeScroller.clientWidth + this.getScrollLeft() ) - CodeEditor.LINE_GUTTER_WIDTH; // Gutter offset
3459
3484
  if( (cursor.position * this.charWidth) >= viewportSizeX )
3460
3485
  this.setScrollLeft( this.getScrollLeft() + this.charWidth );
3461
3486
  });
@@ -3712,7 +3737,7 @@ class CodeEditor {
3712
3737
 
3713
3738
  // Update max viewport
3714
3739
  const maxLineLength = pMaxLength ?? this.getMaxLineLength();
3715
- const scrollWidth = maxLineLength * this.charWidth;
3740
+ const scrollWidth = maxLineLength * this.charWidth + CodeEditor.LINE_GUTTER_WIDTH;
3716
3741
  const scrollHeight = this.code.lines.length * this.lineHeight;
3717
3742
 
3718
3743
  this._lastMaxLineLength = maxLineLength;
@@ -3727,9 +3752,9 @@ class CodeEditor {
3727
3752
 
3728
3753
  resizeScrollBars() {
3729
3754
 
3730
- const totalLinesInViewport = ((this.codeScroller.offsetHeight - 36) / this.lineHeight)|0;
3755
+ const totalLinesInViewport = ((this.codeScroller.offsetHeight) / this.lineHeight)|0;
3731
3756
 
3732
- if( totalLinesInViewport > this.code.lines.length )
3757
+ if( totalLinesInViewport >= this.code.lines.length )
3733
3758
  {
3734
3759
  this.codeScroller.classList.remove( 'with-vscrollbar' );
3735
3760
  this.vScrollbar.root.classList.add( 'scrollbar-unused' );
@@ -3742,10 +3767,10 @@ class CodeEditor {
3742
3767
  this.vScrollbar.thumb.style.height = (this.vScrollbar.thumb.size * 100.0) + "%";
3743
3768
  }
3744
3769
 
3745
- const numViewportChars = Math.floor( this.codeScroller.clientWidth / this.charWidth );
3770
+ const numViewportChars = Math.floor( ( this.codeScroller.clientWidth - CodeEditor.LINE_GUTTER_WIDTH ) / this.charWidth );
3746
3771
  const maxLineLength = this._lastMaxLineLength;
3747
3772
 
3748
- if( numViewportChars > maxLineLength )
3773
+ if( numViewportChars >= maxLineLength )
3749
3774
  {
3750
3775
  this.codeScroller.classList.remove( 'with-hscrollbar' );
3751
3776
  this.hScrollbar.root.classList.add( 'scrollbar-unused' );
@@ -4032,7 +4057,7 @@ class CodeEditor {
4032
4057
  // Show box
4033
4058
  this.autocomplete.classList.toggle('show', true);
4034
4059
  this.autocomplete.classList.toggle('no-scrollbar', !(this.autocomplete.scrollHeight > this.autocomplete.offsetHeight));
4035
- this.autocomplete.style.left = (cursor._left + 48 - this.getScrollLeft()) + "px";
4060
+ this.autocomplete.style.left = (cursor._left + CodeEditor.LINE_GUTTER_WIDTH - this.getScrollLeft()) + "px";
4036
4061
  this.autocomplete.style.top = (cursor._top + 28 + this.lineHeight - this.getScrollTop()) + "px";
4037
4062
 
4038
4063
  this.isAutoCompleteActive = true;
@@ -4040,6 +4065,11 @@ class CodeEditor {
4040
4065
 
4041
4066
  hideAutoCompleteBox() {
4042
4067
 
4068
+ if( !this.autocomplete )
4069
+ {
4070
+ return;
4071
+ }
4072
+
4043
4073
  const isActive = this.isAutoCompleteActive;
4044
4074
  this.isAutoCompleteActive = false;
4045
4075
  this.autocomplete.classList.remove( 'show' );