Jump to content

lynkfs

Moderators
  • Content Count

    502
  • Joined

  • Last visited

  • Days Won

    39

lynkfs last won the day on November 7

lynkfs had the most liked content!

About lynkfs

  • Rank

Profile Information

  • Gender
    Male
  • Location
    Australia

Recent Profile Visitors

610 profile views
  1. I saw that SmartCL.RegEx is moved to System.RegEx. This reminded me of VerbalExpressions, which I came across a while ago VerbalExpressions is basically a library which creates regex strings using functional programming /^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$/ The above regular expression string checks for correctly formatted url's, and in 'regular english' would be something like the url must start with either http or https followed by :// and optionally www. and then anything but a space This can be replaced by a set of function calls : // Create an example of how to test for correctly formed URLs const tester = VerEx() .startOfLine() .then('http') .maybe('s') .then('://') .maybe('www.') .anythingBut(' ') .endOfLine(); // Create an example URL const testMe = 'https://www.google.com'; // Use RegExp object's native test() function if (tester.test(testMe)) { alert('We have a correct URL'); // This output will fire } else { alert('The URL is incorrect'); } which is much more readable The library is available for quite a few languages, including javascript, so could be included as a native lib. However the below code is based on a c# implementation, which was translated into Delphi, and now translated for use in SMS. Bit of a roundabout way. Anyway, here is the code unit VerbalExpressions; {* * ---------------------------------------------------------------------------- * "THE VODKA-WARE LICENSE" (Revision 42): * <tim@bandenkrieg.hacked.jp> wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a vodka in return. Tim Schumacher * ---------------------------------------------------------------------------- https://www.exceptionnotfound.net/use-verbalexpressions-to-create-readable-regexs-in-c/ https://github.com/enko/DelphiVerbalExpressions updated for use in Smart Pascal by LynkFS *} interface uses SmartCL.System, System.RegEx; type VerbalExpressionException = class(Exception); TVerbalExpression = class private FstrSource : string; FStrPrefix : string; FstrSuffix : string; FstrModifier : string; function Add(astrValue : string) : TVerbalExpression; function getRegEx : TW3RegEx; public function AnyOf(astrValue : string) : TVerbalExpression; function Any(astrValue : string) : TVerbalExpression; function Range(astrValue : array of string) : TVerbalExpression; function RepeatPrevious(astrValue : integer) : TVerbalExpression; function Anything : TVerbalExpression; function Sanitize(astrValue : string) : string; function Multiple(astrValue : string) : TVerbalExpression; function StartOfLine(aboolEnable : boolean = True) : TVerbalExpression; function EndOfLine(aboolEnable : boolean = True) : TVerbalExpression; function _Then(astrValue : string) : TVerbalExpression; function Find(astrValue : string) : TVerbalExpression; function Maybe(astrValue : string) : TVerbalExpression; function AnythingBut(astrValue : string) : TVerbalExpression; function _Or(astrValue : string) : TVerbalExpression; function Something : TVerbalExpression; function SomethingBut(astrValue : string) : TVerbalExpression; function LineBreak : TVerbalExpression; function br : TVerbalExpression; function tab : TVerbalExpression; function word : TVerbalExpression; function AddModifier(astrModifier : string) : TVerbalExpression; function RemoveModifier(astrModifier : string) : TVerbalExpression; function WithAnyCase(aboolEnable : boolean = true) : TVerbalExpression; function StopAtFirst(aboolEnable : boolean = true) : TVerbalExpression; function SearchOneLine(aboolEnable : boolean = true) : TVerbalExpression; function AsString : string; function Clear : TVerbalExpression; function Test(astrValue : string) : boolean; property RegEx : TW3RegEx read getRegEx; end; implementation { TVerbalExpression } function TVerbalExpression.Add(astrValue: string): TVerbalExpression; begin Result := self; FstrSource := FstrSource + astrValue; end; function TVerbalExpression.AddModifier(astrModifier: string): TVerbalExpression; begin if (Pos(astrModifier,FstrModifier) = -1) then FstrModifier := FstrModifier + astrModifier; Result := self; end; function TVerbalExpression.Any(astrValue: string): TVerbalExpression; begin Result := AnyOf(astrValue); end; function TVerbalExpression.AnyOf(astrValue: string): TVerbalExpression; begin Result := Add('['+ astrValue +']'); end; function TVerbalExpression.Anything: TVerbalExpression; begin Result := Add('(.*)'); end; function TVerbalExpression.AsString: string; begin Result := FstrSource; end; function TVerbalExpression.br: TVerbalExpression; begin Result := LineBreak; end; function TVerbalExpression.Clear: TVerbalExpression; begin Result := self; FstrSource := ''; FStrPrefix := ''; FstrSuffix := ''; FstrModifier := 'gm'; end; function TVerbalExpression.getRegEx: TW3RegEx; begin Result := TW3RegEx.Create(FstrSource); end; function TVerbalExpression.LineBreak: TVerbalExpression; begin Result := Add('(\n|(\r\n))'); end; function TVerbalExpression.Multiple(astrValue: string): TVerbalExpression; begin astrValue := sanitize(astrValue); if (not ((Copy(astrValue,Length(astrValue)-1,1) = '+') or (Copy(astrValue,Length(astrValue)-1,1) = '*'))) then astrValue := astrValue + '+'; Result := Add(astrValue); end; function TVerbalExpression.Range(astrValue: array of string): TVerbalExpression; var LintCounter: Integer; LstrValue : string; begin if (Length(astrValue) mod 2) <> 0 then raise VerbalExpressionException.Create('Number of args must be even'); LstrValue := '['; LintCounter := 0; while LintCounter < Length(astrValue) do begin LstrValue := LstrValue + astrValue[LintCounter] + '-' + astrValue[LintCounter+1]; Inc(LintCounter,2); end; LstrValue := LstrValue + ']'; Result := Add(LstrValue); end; function TVerbalExpression.RepeatPrevious(astrValue: integer): TVerbalExpression; var LstrValue : string; begin LStrValue := '{' + inttostr(astrValue) + '}'; Result := Add(LstrValue); end; function TVerbalExpression.RemoveModifier( astrModifier: string): TVerbalExpression; begin // FstrModifier := StringReplace(FstrModifier,astrModifier,'',[rfReplaceAll]); //<============ //replaced with code between //// lines //// function Replace(Dest, SubStr, Str: string): string; var Position: Integer; begin Position:=Pos(SubStr, Dest); Delete(Dest, Position, Length(SubStr)); Insert(Str, Dest, Position); Result:=Dest; end; FstrModifier := Replace(FstrModifier,astrModifier,''); //// Result := Self; end; function TVerbalExpression.Sanitize(astrValue: string): string; begin // Result := TW3RegEx.Escape(astrValue); //<=============== //replaced with code between //// lines //// var myStr := ''; asm var escapeRegExp; (function () { // Referring to the table here: // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/regexp // these characters should be escaped // \ ^ $ * + ? . ( ) | { } [ ] // These characters only have special meaning inside of brackets // they do not need to be escaped, but they MAY be escaped // without any adverse effects (to the best of my knowledge and casual testing) // : ! , = // my test "~!@#$%^&*(){}[]`/=?+\|-_;:'\",<.>".match(/[\#]/g) var specials = [ // order matters for these "-" , "[" , "]" // order doesn't matter for any of these , "/" , "{" , "}" , "(" , ")" , "*" , "+" , "?" , "." , "\\" , "^" , "$" , "|" ] // I choose to escape every character with '\' // even though only some strictly require it when inside of [] , regex = RegExp('[' + specials.join('\\') + ']', 'g') ; escapeRegExp = function (str) { return str.replace(regex, "\\$&"); }; }()); @myStr = escapeRegExp(@astrValue); end; //// Result := myStr; end; function TVerbalExpression.SearchOneLine( aboolEnable: boolean): TVerbalExpression; begin if aboolEnable then Result := AddModifier('m') else Result := RemoveModifier('m'); end; function TVerbalExpression.Something: TVerbalExpression; begin Result := Add('(.+)'); end; function TVerbalExpression.SomethingBut(astrValue: string): TVerbalExpression; begin Result := Add('([^'+ Sanitize(astrValue) +']+)'); end; function TVerbalExpression.StartOfLine(aboolEnable : boolean = True) : TVerbalExpression; begin if aboolEnable then FStrPrefix := '^' else FStrPrefix := ''; Result := self; end; function TVerbalExpression.StopAtFirst(aboolEnable: boolean = true): TVerbalExpression; begin if aboolEnable then Result := AddModifier('g') else Result := RemoveModifier('g'); end; function TVerbalExpression.tab: TVerbalExpression; begin Result := Add('\t') end; function TVerbalExpression.Test(astrValue: string): boolean; begin // Result := RegEx.Match(astrValue).Success; //<=============== //replaced with code between //// lines //// Result := RegEx.Test(astrValue); //// end; function TVerbalExpression.WithAnyCase(aboolEnable: boolean = true): TVerbalExpression; begin if aboolEnable then Result := AddModifier('i') else Result := RemoveModifier('i'); end; function TVerbalExpression.word: TVerbalExpression; begin Result := Add('\w+'); end; function TVerbalExpression.EndOfLine(aboolEnable : boolean = True) : TVerbalExpression; begin if aboolEnable then FstrSuffix := '$' else FstrSuffix := ''; Result := self; end; function TVerbalExpression.Find(astrValue: string): TVerbalExpression; begin Result := _Then(astrValue); end; function TVerbalExpression._Or(astrValue: string): TVerbalExpression; begin if (Pos('(',FstrPrefix) = -1) then begin FStrPrefix := FStrPrefix + '('; end; if (Pos(')',FstrSuffix) = -1) then begin FstrSuffix := FstrSuffix + ')'; end; Add(')|('); if Length(astrValue) > 0 then Add(Sanitize(astrValue)); Result := self; end; function TVerbalExpression._Then(astrValue : string) : TVerbalExpression; begin Result := Add('('+ Sanitize(astrValue) +')'); end; function TVerbalExpression.Maybe(astrValue : string) : TVerbalExpression; begin Result := Add('('+ Sanitize(astrValue) +')?'); end; function TVerbalExpression.AnythingBut(astrValue : string) : TVerbalExpression; begin Result := Add('([^'+ Sanitize(astrValue) +']*)'); end; end. and usage like this with example check for url, email address and phone nr unit Form1; interface uses SmartCL.System, SmartCL.Graphics, SmartCL.Components, SmartCL.Forms, SmartCL.Fonts, SmartCL.Borders, SmartCL.Application, VerbalExpressions; type TForm1 = class(TW3Form) private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; urlExp : TVerbalExpression; emailExp : TVerbalExpression; phoneExp : TVerbalExpression; end; implementation { TForm1 } procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} /* rules for checking simple URLs: -The URL must start with either "http" or "https". -The URL must then have "://". -The URL can then have anything following "://", as long as it is isn't a space. */ urlExp := TVerbalExpression.Create .StartOfLine() ._Then('http') .Maybe('s') ._Then('://') .Maybe('www.') .anythingBut(' ') .endOfLine(); writeln('regexp url : ' + urlExp.AsString); var url := "https://github.com"; if urlExp.Test(url) then writeln(url + ' is valid') else writeln(url + ' is invalid'); /* rules for checking email addresses: -The email may start with any text, followed by an '@' symbol. -After the '@', the email may contain any text (except a blank space), followed by a '.' -After the '.', the email address may contain any text (except a blank space). */ emailExp := TVerbalExpression.Create .StartOfLine() .Anything() ._Then("@") .AnythingBut(" ") ._Then(".") .AnythingBut(" ") .EndOfLine(); writeln('regexp email : ' + emailExp.asString); var email := "test@example.com"; if emailExp.Test(email) then writeln(email + ' is valid') else writeln(email + ' is invalid'); email := "test@example"; if emailExp.Test(email) then writeln(email + ' is valid') else writeln(email + ' is invalid'); /* rules for checking (australian) phone nrs: -The phone number may start with "(". -The phone number must then have 2 digits, each of which are in the range 0-9. -The phone number may then have ")". -Following the optional ")", the phone number may also have a space. -Following the optional space, the phone number must have 4 digits, each in the range 0-9. -Following this set of digits, the phone number may optionally include a dash ("-") or a space. -Following the optional dash or space, the phone number must have 4 digits, each in the range 0-9. */ phoneExp := TVerbalExpression.Create .StartOfLine() .Maybe("(") .Range(['0', '9']) .RepeatPrevious(2) .Maybe(")") .Maybe(" ") .Range(['0', '9']) .RepeatPrevious(4) .Maybe(" ") .Range(['0', '9']) .RepeatPrevious(4) .EndOfLine(); writeln('regexp au-phone : ' + phoneExp.asString); var phone := "(02) 2093 9118"; if phoneExp.Test(phone) then writeln(phone + ' is valid') else writeln(phone + ' is invalid'); phone := "022093911"; if phoneExp.Test(phone) then writeln(phone + ' is valid') else writeln(phone + ' is invalid'); end; procedure TForm1.Resize; begin inherited; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
  2. lynkfs

    Resize method called 3 times ?

    The multiple calls to ReSize have been a 'feature' from the beginning, but I don't know exactly why this happens. The Smart Mobile Studio release page (https://smartmobilestudio.com/category/news/) says : 28.9.2017 : Improvements to Resizing: – Minimize number of resizes during form create. – Trigger only one resize during device flip. so obviously there can be multiple resize events on startup. Not sure what your business case is here, but personally I use ReSize mainly for device flip purposes. If your purpose is to DoSomething after an element or a form has been completely initialised in the DOM, you could use ReadyExecute Handle.ReadyExecute( procedure () begin DoSomething; end); or even when it involves doing this on a repaint Handle.ReadyExecute( Procedure () begin BrowserAPI.window.requestAnimationFrame( procedure () Begin DoSomething; end); end); not in the ReSize proc but in the control setup
  3. lynkfs

    forum login

    partly solved : login in with member name rather than email account works
  4. Wondering if these posts would help at all : These posts cover a couple of node-usage scenarios. These scenarios start with vanilla js which is then transformed into object pascal I'm not a node expert but this approach did work for me
  5. lynkfs

    forum login

    Lately having some problems with the forum login Basically every time I log in to the forum it doesn't recognise my password, so have to get a new one As long as I keep the session open, there's no problem - until I try to log in again. Annoying
  6. lynkfs

    double tap

    I needed to catch mobile double-tap events. There is no such thing as a double-tap event, therefore I used the code below to translate double taps (rapid consecutive touchstarts) into double-clicks. Which works fine. var window external 'window': variant; var Twice : boolean := false; procedure tapHandler(e: variant); implementation procedure tapHandler(e: variant); begin //replaces double taps (consecutive touchstart events) into dblclicks if not Twice then begin Twice := true; Window.setTimeout( procedure() begin Twice := false; end, 300 ); end else begin e.preventDefault(); var event: variant; asm @event = new Event('dblclick'); end; e.target.dispatchEvent(event); end; end; //usage: replace double taps (consecutive touchstart events) into dblclicks <element>.handle.addEventListener('touchstart', procedure(e:variant) begin tapHandler(e); end); Question : does the regular rtl event manager cover double-taps ?
  7. lynkfs

    Calendar Control

    sometimes (always ?) when copying / pasting code from a forum post into the ide, some invisible and unwanted characters are transferred as well. The compiler doesn't like that and gives weird error messages. The only way to rectify is to delete and retype offending lines. That is probably what happened when you got "***** Syntax Error: Unexpected Statement"
  8. lynkfs

    Calendar Control

    custom template : right-click somewhere in the Project Manager pane and select 'add template', go to your SMS 'Templates' directory and select 'default.html' this will add a 'custom template' unit in the Project Manager pane then add the 3 jquery lines to that unit : <!DOCTYPE html> <html<?pas if Project.Options.Linker.GenerateAppCacheManifest then Print(' manifest="app.manifest"');?>> <head> <?pas=Project.MetaData.AsTags?> <?pas=Compiler.ManifestTag?> <?pas=Compiler.IconLinkTags?> <?pas=Project.WebFonts?> <title><?pas=Project.Title?></title> <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> <script src="https://code.jquery.com/jquery-1.12.4.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <?pas PrintLn(Compiler.ThemeTag); for var s in Compiler.Resources("text/css") do PrintLn('<link rel="stylesheet" href="'+s+'">'); for var s in Compiler.Resources("text/javascript") do PrintLn('<script src="'+s+'" type="text/javascript"></script>'); // NOTE: This template is not completed and thus more or less identical to // the simple.html template. Future versions will feature more advanced // scripts to fine control the generation of tags. // // While this version should work reliably, you do not have control over // the bootloader mechanism in this file. Please create a custom template // for this case and replace the code below with a custom bootloader ?> <?pas=Compiler.InlineScriptTag?> </head> <body><?pas=Compiler.LinkedScriptTag?></body> </html> form1 unit : unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.EditBox; //, SmartCL.CSS.Classes; type TForm1 = class(TW3Form) private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; MyBox : TW3EditBox; end; function jQuery(aTagObj: TW3TagObj): Variant; function jQueryExternal(v: Variant): Variant; external '$'; implementation { TForm1 } function jQuery(aTagObj: TW3TagObj): Variant; begin result := jQueryExternal(aTagObj.handle); end; procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components MyBox := TW3EditBox.Create(self); MyBox.Text := 'Date'; MyBox.SetBounds(20,20,200,40); var props : Variant := TVariant.Create; props['dateFormat'] := 'dd/mm/yy'; jQuery( MyBox ).datepicker(props); end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} end; procedure TForm1.Resize; begin inherited; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
  9. lynkfs

    Calendar Control

    I suppose marrying third-party controls means living with it (up to a point) In this case jQuery offers themeRoller, which generates a theme. The resulting css spec can then be included Otherwise individual control classes can be overridden. JQuery style : jQueryExternal(browserapi.Document.getElementById("ui-datepicker-div")).addClass( "my-ui-widget-header" ); or RTL style TW3TagStyle.AddClassToControl(browserapi.Document.getElementById("ui-datepicker-div"), "my-ui-widget-header"); or browserapi.Document.getElementById("ui-datepicker-div").setAttribute("style", "background:red; border: 1px solid blue;"); (btw : link to older post )
  10. lynkfs

    Calendar Control

    a quicky calendar : function jQuery(aTagObj: TW3TagObj): Variant; function jQueryExternal(v: Variant): Variant; external '$'; implementation { TForm1 } function jQuery(aTagObj: TW3TagObj): Variant; begin result := jQueryExternal(aTagObj.handle); end; procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components MyBox := TW3EditBox.Create(self); MyBox.Text := 'Date'; MyBox.SetBounds(20,20,200,40); var props : Variant := TVariant.Create; props['dateFormat'] := 'dd/mm/yy'; jQuery( MyBox ).datepicker(props); // or alternatively // jQuery( MyBox ).datepicker(); // jQuery( MyBox ).datepicker("option", "dateFormat", "dd/mm/yy"); end; and add this to your custom template <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> <script src="https://code.jquery.com/jquery-1.12.4.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> there are many options for this widget demo here (based on older post)
  11. lynkfs

    forum search problem

    there seems to be a problem with the search facility on the forum : Something went wrong. Please try again. Error code: EX-1
  12. lynkfs

    no-code

    pushing as much as possible towards the holy grail we'll see...
  13. lynkfs

    no-code

    I had an idea of how to approach the concept of a 'no-code' / 'lo-code' framework, which involved a modelling exercise done from different viewpoints. The idea was to base this on a graph-based domain model and some defined aspect-models derived from that. The following link contains bit of a write-up : preamble, framework basics, domain model, data model, process model, beginnings of workflow model. Event-, processor- and objectives model unfinished. demo showing how the design efforts of these models might look like (in broad terms) demo of how the generated app from these models could look like Sometimes an idea can only be validated by making it work. Or if it doesn't work out then obviously it was a bad idea to begin with. Not making a final call yet as I haven't finished this proof of concept (only about half ready), but so far no reason to abandon. Comments welcome
  14. lynkfs

    Yikes!!!

    Pity Jon quit from Smart but I'm sure he has made the choice he did in his best interests. I would like to say thank you to Jon for the huge investment he made in developing Smart Mobile Studio and the support and r&d of same. I hitched along since version 0.x and have enjoyed it ever since. Warm regards
  15. every time you add a row, fill the tagid of the row with the row number. TagId is a field (string) present in all component descendants in which you can save things. Then in an onclick handler read that field to retrieve the clicked on rownr. (MyGrid.Row[MyGrid.LastAddedRow] as TW3CustomControl).TagId := inttostr(MyGrid.LastAddedRow); (MyGrid.Row[MyGrid.LastAddedRow] as TW3CustomControl).onClick := procedure(sender:TObject)  begin browserapi.window.alert((Sender as TW3CustomControl).TagId); end; (Alternatively you can use the data- attribute as temporary storage).
×