lexgui 0.7.12 → 0.7.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.
- package/build/extensions/codeeditor.js +85 -25
- package/build/lexgui.js +80 -19
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +104 -43
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +17 -1
- package/examples/editor.html +31 -4
- package/package.json +1 -1
|
@@ -946,8 +946,11 @@ class CodeEditor
|
|
|
946
946
|
{
|
|
947
947
|
this._processSelection( cursor, e );
|
|
948
948
|
}
|
|
949
|
-
}
|
|
949
|
+
}
|
|
950
|
+
else if( !e.keepSelection )
|
|
951
|
+
{
|
|
950
952
|
this.endSelection();
|
|
953
|
+
}
|
|
951
954
|
});
|
|
952
955
|
|
|
953
956
|
this.action( 'End', false, ( ln, cursor, e ) => {
|
|
@@ -2502,7 +2505,7 @@ class CodeEditor
|
|
|
2502
2505
|
{
|
|
2503
2506
|
// Make sure we only keep the main cursor..
|
|
2504
2507
|
this._removeSecondaryCursors();
|
|
2505
|
-
this.cursorToLine( cursor, ln
|
|
2508
|
+
this.cursorToLine( cursor, ln );
|
|
2506
2509
|
this.cursorToPosition( cursor, string.length );
|
|
2507
2510
|
}
|
|
2508
2511
|
|
|
@@ -4077,7 +4080,7 @@ class CodeEditor
|
|
|
4077
4080
|
charCounter += t.length;
|
|
4078
4081
|
};
|
|
4079
4082
|
|
|
4080
|
-
let iter = lineString.matchAll(/(<!--|-->|\*\/|\/\*|::|[\[\](){}<>.,;:*"'
|
|
4083
|
+
let iter = lineString.matchAll(/(<!--|-->|\*\/|\/\*|::|[\[\](){}<>.,;:*"'`%@$!/= ])/g);
|
|
4081
4084
|
let subtokens = iter.next();
|
|
4082
4085
|
if( subtokens.value )
|
|
4083
4086
|
{
|
|
@@ -4237,13 +4240,29 @@ class CodeEditor
|
|
|
4237
4240
|
usePreviousTokenToCheckString = true;
|
|
4238
4241
|
customStringKeys['@['] = ']';
|
|
4239
4242
|
}
|
|
4243
|
+
else if( highlight == 'javascript' || highlight == 'typescript' )
|
|
4244
|
+
{
|
|
4245
|
+
customStringKeys["@`"] = "`";
|
|
4246
|
+
}
|
|
4240
4247
|
|
|
4241
4248
|
// Manage strings
|
|
4242
4249
|
this._stringEnded = false;
|
|
4243
4250
|
|
|
4244
4251
|
if( usePreviousTokenToCheckString || ( !inBlockComment && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) ) )
|
|
4245
4252
|
{
|
|
4246
|
-
const _checkIfStringEnded = t => {
|
|
4253
|
+
const _checkIfStringEnded = ( t ) => {
|
|
4254
|
+
|
|
4255
|
+
if( this._stringInterpolation )
|
|
4256
|
+
{
|
|
4257
|
+
if( token == "$" && next == "{" )
|
|
4258
|
+
{
|
|
4259
|
+
delete this._stringInterpolation;
|
|
4260
|
+
this._stringInterpolationOpened = true;
|
|
4261
|
+
this._stringEnded = true;
|
|
4262
|
+
return;
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
|
|
4247
4266
|
const idx = Object.values( customStringKeys ).indexOf( t );
|
|
4248
4267
|
this._stringEnded = (idx > -1) && (idx == Object.values(customStringKeys).indexOf( customStringKeys[ '@' + this._buildingString ] ));
|
|
4249
4268
|
};
|
|
@@ -4257,12 +4276,23 @@ class CodeEditor
|
|
|
4257
4276
|
// Start new string
|
|
4258
4277
|
this._buildingString = ( usePreviousTokenToCheckString ? ctxData.prevWithSpaces : token );
|
|
4259
4278
|
|
|
4279
|
+
if( ( highlight == 'javascript' || highlight == 'typescript' ) && token == "`" )
|
|
4280
|
+
{
|
|
4281
|
+
this._stringInterpolation = true;
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4260
4284
|
// Check if string ended in same token using next...
|
|
4261
4285
|
if( usePreviousTokenToCheckString )
|
|
4262
4286
|
{
|
|
4263
4287
|
_checkIfStringEnded( ctxData.nextWithSpaces );
|
|
4264
4288
|
}
|
|
4265
4289
|
}
|
|
4290
|
+
else if( this._stringInterpolationOpened && prev == "}" )
|
|
4291
|
+
{
|
|
4292
|
+
delete this._stringInterpolationOpened;
|
|
4293
|
+
this._stringInterpolation = true;
|
|
4294
|
+
this._buildingString = "`";
|
|
4295
|
+
}
|
|
4266
4296
|
}
|
|
4267
4297
|
|
|
4268
4298
|
// Update context data for next tests
|
|
@@ -4284,6 +4314,16 @@ class CodeEditor
|
|
|
4284
4314
|
// Get highlighting class based on language common and specific rules
|
|
4285
4315
|
let tokenClass = this._getTokenHighlighting( ctxData, highlight );
|
|
4286
4316
|
|
|
4317
|
+
if( this._stringInterpolationOpened && this._pendingString )
|
|
4318
|
+
{
|
|
4319
|
+
this._pendingString = this._pendingString.substring( 0, this._pendingString.indexOf( "$" ) );
|
|
4320
|
+
|
|
4321
|
+
if( ctxData.tokens[ tokenIndex + 1 ] == "{" )
|
|
4322
|
+
{
|
|
4323
|
+
ctxData.tokens[ tokenIndex + 1 ] = "${";
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
|
|
4287
4327
|
// We finished constructing a string
|
|
4288
4328
|
if( this._buildingString && ( this._stringEnded || isLastToken ) && !inBlockComment )
|
|
4289
4329
|
{
|
|
@@ -4671,43 +4711,57 @@ class CodeEditor
|
|
|
4671
4711
|
this.hideAutoCompleteBox();
|
|
4672
4712
|
}
|
|
4673
4713
|
|
|
4674
|
-
cursorToRight(
|
|
4714
|
+
cursorToRight( text, cursor )
|
|
4675
4715
|
{
|
|
4676
|
-
if( !
|
|
4716
|
+
if( !text || !text.length ) return;
|
|
4717
|
+
|
|
4718
|
+
const chars = text.length;
|
|
4719
|
+
const offset = chars * this.charWidth;
|
|
4677
4720
|
|
|
4678
|
-
cursor._left +=
|
|
4721
|
+
cursor._left += offset;
|
|
4679
4722
|
cursor.style.left = `calc( ${ cursor._left }px + ${ this.xPadding } )`;
|
|
4680
|
-
cursor.position
|
|
4723
|
+
cursor.position += chars;
|
|
4681
4724
|
|
|
4682
4725
|
this.restartBlink();
|
|
4683
4726
|
|
|
4684
4727
|
// Add horizontal scroll
|
|
4728
|
+
const rightMargin = this.charWidth;
|
|
4729
|
+
const cursorX = ( cursor.position * this.charWidth );
|
|
4685
4730
|
const currentScrollLeft = this.getScrollLeft();
|
|
4686
|
-
|
|
4687
|
-
|
|
4731
|
+
const viewportSizeX = this.codeScroller.clientWidth - CodeEditor.LINE_GUTTER_WIDTH; // Gutter offset
|
|
4732
|
+
const viewportX = viewportSizeX + currentScrollLeft;
|
|
4733
|
+
|
|
4734
|
+
if( cursorX >= ( viewportX - rightMargin ) )
|
|
4688
4735
|
{
|
|
4689
|
-
|
|
4736
|
+
const scroll = Math.max( cursorX - ( viewportSizeX - rightMargin ), 0 );
|
|
4737
|
+
this.setScrollLeft( scroll );
|
|
4690
4738
|
}
|
|
4691
4739
|
}
|
|
4692
4740
|
|
|
4693
|
-
cursorToLeft(
|
|
4741
|
+
cursorToLeft( text, cursor )
|
|
4694
4742
|
{
|
|
4695
|
-
if( !
|
|
4743
|
+
if( !text || !text.length ) return;
|
|
4744
|
+
|
|
4745
|
+
const chars = text.length;
|
|
4746
|
+
const offset = chars * this.charWidth;
|
|
4696
4747
|
|
|
4697
|
-
cursor._left -=
|
|
4748
|
+
cursor._left -= offset;
|
|
4698
4749
|
cursor._left = Math.max( cursor._left, 0 );
|
|
4699
4750
|
cursor.style.left = `calc( ${ cursor._left }px + ${ this.xPadding } )`;
|
|
4700
|
-
cursor.position
|
|
4751
|
+
cursor.position -= chars;
|
|
4701
4752
|
cursor.position = Math.max( cursor.position, 0 );
|
|
4753
|
+
|
|
4702
4754
|
this.restartBlink();
|
|
4703
4755
|
|
|
4704
4756
|
// Add horizontal scroll
|
|
4705
|
-
|
|
4757
|
+
const leftMargin = this.charWidth;
|
|
4758
|
+
const cursorX = ( cursor.position * this.charWidth );
|
|
4706
4759
|
const currentScrollLeft = this.getScrollLeft();
|
|
4707
|
-
|
|
4708
|
-
if(
|
|
4760
|
+
|
|
4761
|
+
if( cursorX < ( currentScrollLeft + leftMargin ) )
|
|
4709
4762
|
{
|
|
4710
|
-
|
|
4763
|
+
const scroll = Math.max( cursorX - leftMargin, 0 );
|
|
4764
|
+
this.setScrollLeft( scroll );
|
|
4711
4765
|
}
|
|
4712
4766
|
}
|
|
4713
4767
|
|
|
@@ -4759,9 +4813,13 @@ class CodeEditor
|
|
|
4759
4813
|
return;
|
|
4760
4814
|
}
|
|
4761
4815
|
|
|
4762
|
-
|
|
4816
|
+
if( reverse )
|
|
4763
4817
|
{
|
|
4764
|
-
|
|
4818
|
+
this.cursorToLeft( text, cursor )
|
|
4819
|
+
}
|
|
4820
|
+
else
|
|
4821
|
+
{
|
|
4822
|
+
this.cursorToRight( text, cursor );
|
|
4765
4823
|
}
|
|
4766
4824
|
}
|
|
4767
4825
|
|
|
@@ -4867,15 +4925,17 @@ class CodeEditor
|
|
|
4867
4925
|
if( flag & CodeEditor.CURSOR_LEFT )
|
|
4868
4926
|
{
|
|
4869
4927
|
cursor._left = 0;
|
|
4870
|
-
cursor.style.left = "calc(" +
|
|
4928
|
+
cursor.style.left = "calc(" + this.xPadding + ")";
|
|
4871
4929
|
cursor.position = 0;
|
|
4930
|
+
this.setScrollLeft( 0 );
|
|
4872
4931
|
}
|
|
4873
4932
|
|
|
4874
4933
|
if( flag & CodeEditor.CURSOR_TOP )
|
|
4875
4934
|
{
|
|
4876
4935
|
cursor._top = 0;
|
|
4877
|
-
cursor.style.top =
|
|
4936
|
+
cursor.style.top = "0px";
|
|
4878
4937
|
cursor.line = 0;
|
|
4938
|
+
this.setScrollTop( 0 )
|
|
4879
4939
|
}
|
|
4880
4940
|
}
|
|
4881
4941
|
|
|
@@ -5034,7 +5094,7 @@ class CodeEditor
|
|
|
5034
5094
|
LX.doAsync( () => {
|
|
5035
5095
|
this.codeScroller.scrollLeft = value;
|
|
5036
5096
|
this.setScrollBarValue( 'horizontal', 0 );
|
|
5037
|
-
},
|
|
5097
|
+
}, 10 );
|
|
5038
5098
|
}
|
|
5039
5099
|
|
|
5040
5100
|
setScrollTop( value )
|
|
@@ -5043,7 +5103,7 @@ class CodeEditor
|
|
|
5043
5103
|
LX.doAsync( () => {
|
|
5044
5104
|
this.codeScroller.scrollTop = value;
|
|
5045
5105
|
this.setScrollBarValue( 'vertical' );
|
|
5046
|
-
},
|
|
5106
|
+
}, 10 );
|
|
5047
5107
|
}
|
|
5048
5108
|
|
|
5049
5109
|
resize( flag = CodeEditor.RESIZE_SCROLLBAR_H_V, pMaxLength, onResize )
|
package/build/lexgui.js
CHANGED
|
@@ -14,7 +14,7 @@ console.warn( 'Script _build/lexgui.js_ is depracated and will be removed soon.
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
const LX = {
|
|
17
|
-
version: "0.7.
|
|
17
|
+
version: "0.7.13",
|
|
18
18
|
ready: false,
|
|
19
19
|
extensions: [], // Store extensions used
|
|
20
20
|
signals: {}, // Events and triggers
|
|
@@ -5329,11 +5329,12 @@ LX.guidGenerator = guidGenerator;
|
|
|
5329
5329
|
function buildTextPattern( options = {} )
|
|
5330
5330
|
{
|
|
5331
5331
|
let patterns = [];
|
|
5332
|
-
if
|
|
5333
|
-
if
|
|
5334
|
-
if
|
|
5335
|
-
if
|
|
5336
|
-
if
|
|
5332
|
+
if( options.lowercase ) patterns.push("(?=.*[a-z])");
|
|
5333
|
+
if( options.uppercase ) patterns.push("(?=.*[A-Z])");
|
|
5334
|
+
if( options.digit ) patterns.push("(?=.*\\d)");
|
|
5335
|
+
if( options.specialChar ) patterns.push("(?=.*[@#$%^&+=!])");
|
|
5336
|
+
if( options.noSpaces ) patterns.push("(?!.*\\s)");
|
|
5337
|
+
if( options.email ) patterns.push("(^[^\s@]+@[^\s@]+\.[^\s@]+$)");
|
|
5337
5338
|
|
|
5338
5339
|
let minLength = options.minLength || 0;
|
|
5339
5340
|
let maxLength = options.maxLength || ""; // Empty means no max length restriction
|
|
@@ -5344,6 +5345,45 @@ function buildTextPattern( options = {} )
|
|
|
5344
5345
|
|
|
5345
5346
|
LX.buildTextPattern = buildTextPattern;
|
|
5346
5347
|
|
|
5348
|
+
/**
|
|
5349
|
+
* Checks a value against a set of pattern requirements and returns an array
|
|
5350
|
+
* of specific error messages for all criteria that failed.
|
|
5351
|
+
* @param { String } value The string to validate.
|
|
5352
|
+
* @param { Object } pattern The pattern options
|
|
5353
|
+
* @returns { Array } An array of error messages for failed criteria.
|
|
5354
|
+
*/
|
|
5355
|
+
function validateValueAtPattern( value, pattern = {}, ...args )
|
|
5356
|
+
{
|
|
5357
|
+
const errors = [];
|
|
5358
|
+
const minLength = pattern.minLength || 0;
|
|
5359
|
+
const maxLength = pattern.maxLength; // undefined means no max limit
|
|
5360
|
+
|
|
5361
|
+
// Length requirements
|
|
5362
|
+
if( value.length < minLength ) errors.push(`Must be at least ${ minLength } characters long.`);
|
|
5363
|
+
else if( maxLength !== undefined && value.length > maxLength ) errors.push(`Must be no more than ${ maxLength } characters long.`);
|
|
5364
|
+
|
|
5365
|
+
// Check for Lowercase, Uppercase, Digits
|
|
5366
|
+
if( pattern.lowercase && !/[a-z]/.test( value ) ) errors.push( "Must contain at least one lowercase letter (a-z)." );
|
|
5367
|
+
if( pattern.uppercase && !/[A-Z]/.test( value ) ) errors.push( "Must contain at least one uppercase letter (A-Z)." );
|
|
5368
|
+
if( pattern.digit && !/\d/.test( value ) ) errors.push( "Must contain at least one number (0-9)." );
|
|
5369
|
+
|
|
5370
|
+
// Check for No Spaces (The original regex was (?!.*\s), meaning 'not followed by any character and a space')
|
|
5371
|
+
if( pattern.noSpaces && /\s/.test(value)) errors.push("Must NOT contain any spaces.");
|
|
5372
|
+
|
|
5373
|
+
// Check for Special Character (using the same set as buildTextPattern)
|
|
5374
|
+
if( pattern.specialChar && !/[@#$%^&+=!]/.test( value ) ) errors.push("Must contain at least one special character (e.g., @, #, $, %, ^, &, +, =, !).");
|
|
5375
|
+
|
|
5376
|
+
// Check email formatting
|
|
5377
|
+
if( pattern.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test( value ) ) errors.push("Must have a valid email format.");
|
|
5378
|
+
|
|
5379
|
+
// Check match to any other text word
|
|
5380
|
+
if( pattern.fieldMatchName && value !== ( args[ 0 ] ) ) errors.push(`Must match ${ pattern.fieldMatchName } field.`);
|
|
5381
|
+
|
|
5382
|
+
return errors;
|
|
5383
|
+
}
|
|
5384
|
+
|
|
5385
|
+
LX.validateValueAtPattern = validateValueAtPattern;
|
|
5386
|
+
|
|
5347
5387
|
/**
|
|
5348
5388
|
* @method makeDraggable
|
|
5349
5389
|
* @description Allows an element to be dragged
|
|
@@ -9062,7 +9102,14 @@ class TextInput extends BaseComponent
|
|
|
9062
9102
|
|
|
9063
9103
|
this.onSetValue = ( newValue, skipCallback, event ) => {
|
|
9064
9104
|
|
|
9065
|
-
|
|
9105
|
+
let skipTrigger = ( this._lastValueTriggered == newValue );
|
|
9106
|
+
|
|
9107
|
+
if( !options.ignoreValidation )
|
|
9108
|
+
{
|
|
9109
|
+
skipTrigger |= ( !this.valid( newValue ) );
|
|
9110
|
+
}
|
|
9111
|
+
|
|
9112
|
+
if( skipTrigger )
|
|
9066
9113
|
{
|
|
9067
9114
|
return;
|
|
9068
9115
|
}
|
|
@@ -9082,11 +9129,11 @@ class TextInput extends BaseComponent
|
|
|
9082
9129
|
container.style.width = options.inputWidth ?? `calc( 100% - ${ realNameWidth })`;
|
|
9083
9130
|
};
|
|
9084
9131
|
|
|
9085
|
-
this.valid = ( v ) => {
|
|
9132
|
+
this.valid = ( v, matchField ) => {
|
|
9086
9133
|
v = v ?? this.value();
|
|
9087
|
-
if(
|
|
9088
|
-
const
|
|
9089
|
-
return
|
|
9134
|
+
if( !options.pattern ) return true;
|
|
9135
|
+
const errs = LX.validateValueAtPattern( v, options.pattern, matchField );
|
|
9136
|
+
return ( errs.length == 0 );
|
|
9090
9137
|
};
|
|
9091
9138
|
|
|
9092
9139
|
let container = document.createElement( 'div' );
|
|
@@ -9115,7 +9162,7 @@ class TextInput extends BaseComponent
|
|
|
9115
9162
|
|
|
9116
9163
|
if( options.pattern )
|
|
9117
9164
|
{
|
|
9118
|
-
wValue.setAttribute( "pattern", options.pattern );
|
|
9165
|
+
wValue.setAttribute( "pattern", LX.buildTextPattern( options.pattern ) );
|
|
9119
9166
|
}
|
|
9120
9167
|
|
|
9121
9168
|
const trigger = options.trigger ?? "default";
|
|
@@ -9785,13 +9832,14 @@ class Form extends BaseComponent
|
|
|
9785
9832
|
|
|
9786
9833
|
if( entryData.constructor != Object )
|
|
9787
9834
|
{
|
|
9788
|
-
const oldValue =
|
|
9835
|
+
const oldValue = LX.deepCopy( entryData );
|
|
9789
9836
|
entryData = { value: oldValue };
|
|
9790
9837
|
data[ entry ] = entryData;
|
|
9791
9838
|
}
|
|
9792
9839
|
|
|
9793
|
-
entryData.placeholder = entryData.placeholder ?? ( entryData.label ?? `Enter ${ entry }` );
|
|
9794
9840
|
entryData.width = "100%";
|
|
9841
|
+
entryData.placeholder = entryData.placeholder ?? ( entryData.label ?? `Enter ${ entry }` );
|
|
9842
|
+
entryData.ignoreValidation = true;
|
|
9795
9843
|
|
|
9796
9844
|
if( !( options.skipLabels ?? false ) )
|
|
9797
9845
|
{
|
|
@@ -9807,14 +9855,14 @@ class Form extends BaseComponent
|
|
|
9807
9855
|
container.formData[ entry ] = entryData.constructor == Object ? entryData.value : entryData;
|
|
9808
9856
|
}
|
|
9809
9857
|
|
|
9810
|
-
const buttonContainer = LX.makeContainer( ["100%", "auto"], "flex flex-row", "", container );
|
|
9858
|
+
const buttonContainer = LX.makeContainer( ["100%", "auto"], "flex flex-row mt-2", "", container );
|
|
9811
9859
|
|
|
9812
9860
|
if( options.secondaryActionName || options.secondaryActionCallback )
|
|
9813
9861
|
{
|
|
9814
9862
|
const secondaryButton = new LX.Button( null, options.secondaryActionName ?? "Cancel", ( value, event ) => {
|
|
9815
|
-
if(
|
|
9863
|
+
if( options.secondaryActionCallback )
|
|
9816
9864
|
{
|
|
9817
|
-
|
|
9865
|
+
options.secondaryActionCallback( container.formData, event );
|
|
9818
9866
|
}
|
|
9819
9867
|
}, { width: "100%", minWidth: "0", buttonClass: options.secondaryButtonClass ?? "primary" } );
|
|
9820
9868
|
|
|
@@ -9829,9 +9877,22 @@ class Form extends BaseComponent
|
|
|
9829
9877
|
{
|
|
9830
9878
|
let entryData = data[ entry ];
|
|
9831
9879
|
|
|
9832
|
-
|
|
9880
|
+
const pattern = entryData.pattern;
|
|
9881
|
+
const matchField = pattern?.fieldMatchName ? container.formData[ pattern.fieldMatchName ] : undefined;
|
|
9882
|
+
|
|
9883
|
+
if( !entryData.textComponent.valid( undefined, matchField ) )
|
|
9833
9884
|
{
|
|
9834
|
-
|
|
9885
|
+
const err = { entry, type: "input_not_valid" };
|
|
9886
|
+
err.messages = [];
|
|
9887
|
+
if( pattern )
|
|
9888
|
+
{
|
|
9889
|
+
err.messages = LX.validateValueAtPattern(
|
|
9890
|
+
container.formData[ entry ],
|
|
9891
|
+
pattern,
|
|
9892
|
+
matchField
|
|
9893
|
+
);
|
|
9894
|
+
}
|
|
9895
|
+
errors.push( err );
|
|
9835
9896
|
}
|
|
9836
9897
|
}
|
|
9837
9898
|
|