Jump to content

lynkfs

Moderators
  • Content Count

    740
  • Joined

  • Last visited

  • Days Won

    146

Reputation Activity

  1. Like
    lynkfs reacted to jarto in Smart Mobile Studio 3.9.1 (1st Alpha release) is available   
    There's a small update available. It adds a possibility to change TW3ListBox line text.
  2. Like
    lynkfs reacted to sektor in browser-ui-state lib bindings   
    Decided to start working with SMS again, but I needed better tooling, so I started creating them. 
    I have also decided to share some of them.
    Here are bindings for the browser-ui-state library:
    Bindings for the browser-ui-state lib
    and a small demo showing you how to both the library and the bindings
     
    Cheers
  3. Thanks
    lynkfs got a reaction from IgorSavkic in Redefining language elements   
    I've always loved the basic data-structures as described in this link.
    Since I needed a double linked list the other day, I transcribed all of the major basic structures from js into object pascal :
    Array Hash Table Set Singly Linked List Doubly Linked List Stack Queue Tree Graph  Source and test-files here
  4. Like
    lynkfs reacted to jarto in Smart Mobile Studio 3.9.1 (1st Alpha release) is available   
    A new update is available:
    IDE:
    Fix compilation without saving, when SMS is run from Program Files Change embedded browser to always use localhost This lets us use Chrome's Powerful Features through http RTL:
    Add RemoteUrl as a property for TNJWebSocketServer Bug fixes and improvements to TW3Slider Works now with mobile devices Set default max value to 100
  5. Like
    lynkfs got a reaction from jarto in touch-action in CSS   
    This looks like a non-solvable problem.
    Apparently Apple changes his/her mind often how to implement this in the various mobile iOS Safari versions and the OS hardware event checking seems to take precedence over html processing.
    If you change the <body> part of the index.html file to this
    <body>  <script type="text/javascript">    /* This prevents the window being moved by touches,       to give the impression of a native app */     document.ontouchmove = function(e) { e.preventDefault(); } document.addEventListener("touchstart", event => {window.alert("touch event starting"); event.preventDefault();     if(event.touches.length > 1) {         window.alert("zooming happening");         event.preventDefault();     } }, {passive: false});       </script>     <script type="text/javascript" src="main.js"></script> </body> then the problem goes away. However its a sledgehammer solution as it captures all touch events.
    Depending on what touch events you need, you may be able to re-enable f.i. touch scrolling on selected elements, or channel the above from the document body and all of its children to specific elements only
     
     
  6. Like
    lynkfs got a reaction from Tim Koscielski in routing   
    Routing is the link between specific url's and specific functionality in an app, so f.i.
    https://<server>/index.html           starts up your app in the browser as per normal https://<server>/login                    executes a specific view (f.i a login form) Smart per default produces single page app's, without routing functionality
    There are a couple of strategies which can be followed to implement routing (and which will work to some extent) :
    use # formatted urls. 
    Browsers have a 'hashchange' event built in, which fires every time a url changes, as long as it contains a '#'
    This can be used to implement routing.
    Without going into the specifics, the end-result will end up looking like
    https://<server>/index.html#                  for the main form https://<server>/index.html#/login         for the login form See example here. Not exactly what I want
    Use url-parameters 
    https://<server>/index.html                       as per normal https://<server>/index.html?form=login   for the login form Not liking this syntax either
    Do something crazy like setting up a mutation observer on the url
    Do the routing not on the client but go server-side. 
    This involves programming a dedicated server (php, node) which intercepts all url requests and generates output which then needs to be rendered client-side.
    While not impossible, it doesn't really gel with Smart's infrastructure
    Divide up the app in multiple sub-projects, so that there is only 1 form per sub-project.
    This is actually not a bad idea, and I've done that a couple of times. It just requires some development organisation.
    Or use the browsers history object. 
    This works almost out of the box and has the advantages that it also includes the browsers back-button.
    https://<server>/index.html              as per normal https://<server>/login                       for the login form  The problem with the last entry is that when a user refreshes the /login page, or goes directly to it, a 404 page-not-found error will be generated 
    This can be intercepted by modifying some server setting, usually entries in the .htaccess file.
    Not something I like to do as it introduces another dependency.
    The demo here is built using the history object and eliminates the 404 page-not-found problem.
    It has the following routing structure :
    Form1 :  /index.html
    Form2 : /Topic-01.html
    Form3 : /Topic-02.html
    Switching back and forth between these forms and url's works fine both in code or using the browsers back-button. Also refreshing or navigating directly (by typing in the page url in the address bar) works fine.
    My satisfaction level with this approach is 90%. There are a couple of things I don't like, but can live with. Might be able to improve on them later on.
    Salient code
    procedure TApplication.ApplicationStarting; begin   browserapi.window.onpopstate := procedure(e:variant)   begin     case browserapi.window.location.pathname of       '/Experiments/routing/www/' :              Application.GoToForm('Form1');       '/Experiments/routing/www/index.html' :    Application.GoToForm('Form1');       '/Experiments/routing/www/Topic-01.html' : Application.GoToForm('Form2');       '/Experiments/routing/www/Topic-02.html' : Application.GoToForm('Form3');     end;   end;   TW3Dispatch.WaitFor([FormByName('Form1'),FormByName('Form2'),FormByName('Form3')], procedure ()   begin     case browserapi.window.location.pathname of       '/Experiments/routing/www/Topic-01.html' :           FormByName('Form1').OnActivate := lambda Application.GoToForm('Form2'); end;       '/Experiments/routing/www/Topic-02.html' :           FormByName('Form1').OnActivate := lambda Application.GoToForm('Form3'); end;     end;   end);  
  7. Thanks
    lynkfs got a reaction from IgorSavkic in routing   
    Routing is the link between specific url's and specific functionality in an app, so f.i.
    https://<server>/index.html           starts up your app in the browser as per normal https://<server>/login                    executes a specific view (f.i a login form) Smart per default produces single page app's, without routing functionality
    There are a couple of strategies which can be followed to implement routing (and which will work to some extent) :
    use # formatted urls. 
    Browsers have a 'hashchange' event built in, which fires every time a url changes, as long as it contains a '#'
    This can be used to implement routing.
    Without going into the specifics, the end-result will end up looking like
    https://<server>/index.html#                  for the main form https://<server>/index.html#/login         for the login form See example here. Not exactly what I want
    Use url-parameters 
    https://<server>/index.html                       as per normal https://<server>/index.html?form=login   for the login form Not liking this syntax either
    Do something crazy like setting up a mutation observer on the url
    Do the routing not on the client but go server-side. 
    This involves programming a dedicated server (php, node) which intercepts all url requests and generates output which then needs to be rendered client-side.
    While not impossible, it doesn't really gel with Smart's infrastructure
    Divide up the app in multiple sub-projects, so that there is only 1 form per sub-project.
    This is actually not a bad idea, and I've done that a couple of times. It just requires some development organisation.
    Or use the browsers history object. 
    This works almost out of the box and has the advantages that it also includes the browsers back-button.
    https://<server>/index.html              as per normal https://<server>/login                       for the login form  The problem with the last entry is that when a user refreshes the /login page, or goes directly to it, a 404 page-not-found error will be generated 
    This can be intercepted by modifying some server setting, usually entries in the .htaccess file.
    Not something I like to do as it introduces another dependency.
    The demo here is built using the history object and eliminates the 404 page-not-found problem.
    It has the following routing structure :
    Form1 :  /index.html
    Form2 : /Topic-01.html
    Form3 : /Topic-02.html
    Switching back and forth between these forms and url's works fine both in code or using the browsers back-button. Also refreshing or navigating directly (by typing in the page url in the address bar) works fine.
    My satisfaction level with this approach is 90%. There are a couple of things I don't like, but can live with. Might be able to improve on them later on.
    Salient code
    procedure TApplication.ApplicationStarting; begin   browserapi.window.onpopstate := procedure(e:variant)   begin     case browserapi.window.location.pathname of       '/Experiments/routing/www/' :              Application.GoToForm('Form1');       '/Experiments/routing/www/index.html' :    Application.GoToForm('Form1');       '/Experiments/routing/www/Topic-01.html' : Application.GoToForm('Form2');       '/Experiments/routing/www/Topic-02.html' : Application.GoToForm('Form3');     end;   end;   TW3Dispatch.WaitFor([FormByName('Form1'),FormByName('Form2'),FormByName('Form3')], procedure ()   begin     case browserapi.window.location.pathname of       '/Experiments/routing/www/Topic-01.html' :           FormByName('Form1').OnActivate := lambda Application.GoToForm('Form2'); end;       '/Experiments/routing/www/Topic-02.html' :           FormByName('Form1').OnActivate := lambda Application.GoToForm('Form3'); end;     end;   end);  
  8. Like
    lynkfs got a reaction from Tim Koscielski in Facebook or Google OAuth   
    Works from server only, the one specified in the credentials page
    var document external 'document': variant; var console  external 'console':  variant; var gapi     external 'gapi':     variant; implementation { TForm1 } procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components   var Script := document.createElement('script');   Script.src := 'https://apis.google.com/js/api.js';   Script.setAttribute('async','');   Script.setAttribute('defer','');   document.head.appendChild(Script);   Script.onload := procedure   begin     writeln('loaded');     //     // Load the API client and auth2 library     gapi.load('auth2', procedure     begin       var mypromise : variant;       mypromise := gapi.auth2.init(class         clientId = '..........s0m9qrp8u2m6dcivi8p09tu5se.apps.googleusercontent.com';       end); mypromise.then(procedure begin console.log(mypromise.isSignedIn.get()); end, procedure begin console.log('error'); end);     end);   end; end; logs 'loaded' and 'true'
    your other link does specify useful functions which can be added to the above
     
     
  9. Like
    lynkfs reacted to jarto in Issues with Timezones in FormatDateTime? Panel Transparency?   
    Fixed now in the Alpha branch. Thank you @Tim Koscielski
  10. Like
    lynkfs got a reaction from IElite in SEO   
    Search Engine Optimisation - SEO
    Over the years I have made quite a few websites with Smart, but eventually stopped doing that.
    Reason is that Smart produces single page applications (or multiple if you like) and SEO for these type of apps is essentially impossible.
    Search engine crawlers look for html, which is what feeds their indexing effort. This is completely absent in the standard index.html file, so essentially they see an empty page.
    Google's googlebot is supposed to be able to execute the js bit during its crawling, but I find the indexing results below par. 
    The accepted standard solution is to use serverside rendering. Basically if someone accesses a page in the browser, a piece of server side code is fired up which determines if the user is a webcrawler or a person. In case of a crawler it serves some html code, in case of a user it lets the browser handle the js code.
    Sounds like cloaking-101, but amazingly Google is ok with that.
    The crux of course is where the 'html code' in the sentence above comes from. The process of constructing that is a bit messy and involves the use of Google or third party products (Puppeteer, headless Chrome, dataflowkit, prerender.io ea)
    A more simple way is to pre-render client-side : add the static html bit of all forms in the index.html file.
    Something like
    <!DOCTYPE html> <html> <head>     ... standard tags </head> <body> <div id="Temp1" class="TW3Display" data-okey="OBJ4" style="z-index: 1; visibility: visible; display: inline-block; overflow: hidden; left: 0px; top: 0px; position: absolute; min-width: 100%; min-height: 100%;"><div id="Component2" class="TW3DisplayView" data-okey="OBJ3" style="z-index: 1; visibility: visible; display: inline-block; overflow: hidden; left: 0px; top: 0px; position: absolute; width: 1440px; height: 488px;"> <div id="Component5" class="TW3CustomForm" data-ok .... ... standard body script (document.ontouchmove)   <script type="text/javascript" src="main.js"></script> </body></html>  
    and delete this Temp1 element as the first action in the app's project to avoid having it all doubled up
    There are some disadvantages associated with this approach too, but at least the crawler will have some html to base its indexing on

    It would be nice if somehow this could be incorporated in the build process ...
     
     
  11. Like
    lynkfs reacted to jarto in SSO (Single-Sign-On) via Facebook using TW3FacebookLogin   
    TW3FacebookLogin is a non-visual component, which is available in the Designer's Cloud-tab. It lets you add Single-Sign-On to your web page using Facebook credentials.
    To use this, you need an AppID, which you can obtain by registering at https://developers.facebook.com
    When you register, you need to specify the App Domains where you're going to host your web app. So, if you plan to have it at https://myserver.example.com/mywebapp/index.html, then you need to register myserver.example.com as App Domain.
    Then add TW3FacebookLogin to your form and set the necessary properties and events: Properties:
    ApiVersion: Version of FB api. Default is v8.0 AppId: The AppId you get when you register your app at Facebook. AutoLogin: If true, your web page will automatically call Login, which pops up Facebook's login form Enabled: When this is set to True, the component loads Facebook's SDK and checks if you're already logged in Permissions: What information to ask for from Facebook. Leave it blank if the basic profile information is enough. https://developers.facebook.com/docs/permissions/reference SDK: The Facebook SDK to load. This default value is usually ok. Events:
    OnLogin: Is called when the Login is successful OnLogout: Is called when user logs off Methods:
    Login: Opens Facebook's login form (or logs the user automatically in if you're already in logged into Facebook) Logout Useful properties after login:
    UserName UserEmail UserId UserToken Most of the time you only need to:
    Set AppID Set Enabled to True, which will automatically load Facebook's SDK Set the OnLogin- and OnLogout-events In OnLogin, get the name, email, UserId and token. Call Login (for example, from a Button's or Image's OnClick-handler) And when you test, make sure to do it with a browser from the registered URI. Don't expect this to work from SMS's console.
    Finally, a few words about security. Do not blindly trust the information that you receive. As this all happens in the client, you can not automatically trust UserName and UserEmail. So, when you do add this kind of SSO-support to log into a backend server, pass on the UserToken to the backend server, which should separately call Facebook's services to verify the token.
    Note! When writing this, there is a problem with Facebook's API and Firefox browser. This problem is not related to Smart Mobile Studio at all, but affects any Facebook logins in Firefox. Refer to these URLs for more information:
    https://support.mozilla.org/en-US/questions/1130053
    https://stackoverflow.com/questions/33855626/firefox-blocking-facebook-js
  12. Like
    lynkfs got a reaction from IgorSavkic in font choice   
    Styling on the web is really messy, and takes a lot of effort to get it right. 
    As a matter of personal interest, I'm collecting as many 'design rules' underpinning good styling as I can.
    Like :
    In the typography area, I came across this site. It uses machine learning to identify font-families which work well together.
    It uses Google fonts as its domain, and outputs a header, sub-header and text font (similar to h1, h2 and p)
    I really like its recommendations (usually using the 'similar' setting)
     

  13. Like
    lynkfs got a reaction from jarto in font choice   
    Styling on the web is really messy, and takes a lot of effort to get it right. 
    As a matter of personal interest, I'm collecting as many 'design rules' underpinning good styling as I can.
    Like :
    In the typography area, I came across this site. It uses machine learning to identify font-families which work well together.
    It uses Google fonts as its domain, and outputs a header, sub-header and text font (similar to h1, h2 and p)
    I really like its recommendations (usually using the 'similar' setting)
     

  14. Like
    lynkfs got a reaction from jarto in Facebook or Google OAuth   
    Works from server only, the one specified in the credentials page
    var document external 'document': variant; var console  external 'console':  variant; var gapi     external 'gapi':     variant; implementation { TForm1 } procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components   var Script := document.createElement('script');   Script.src := 'https://apis.google.com/js/api.js';   Script.setAttribute('async','');   Script.setAttribute('defer','');   document.head.appendChild(Script);   Script.onload := procedure   begin     writeln('loaded');     //     // Load the API client and auth2 library     gapi.load('auth2', procedure     begin       var mypromise : variant;       mypromise := gapi.auth2.init(class         clientId = '..........s0m9qrp8u2m6dcivi8p09tu5se.apps.googleusercontent.com';       end); mypromise.then(procedure begin console.log(mypromise.isSignedIn.get()); end, procedure begin console.log('error'); end);     end);   end; end; logs 'loaded' and 'true'
    your other link does specify useful functions which can be added to the above
     
     
  15. Like
    lynkfs got a reaction from Daniel Eiszele in Facebook or Google OAuth   
    Works from server only, the one specified in the credentials page
    var document external 'document': variant; var console  external 'console':  variant; var gapi     external 'gapi':     variant; implementation { TForm1 } procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components   var Script := document.createElement('script');   Script.src := 'https://apis.google.com/js/api.js';   Script.setAttribute('async','');   Script.setAttribute('defer','');   document.head.appendChild(Script);   Script.onload := procedure   begin     writeln('loaded');     //     // Load the API client and auth2 library     gapi.load('auth2', procedure     begin       var mypromise : variant;       mypromise := gapi.auth2.init(class         clientId = '..........s0m9qrp8u2m6dcivi8p09tu5se.apps.googleusercontent.com';       end); mypromise.then(procedure begin console.log(mypromise.isSignedIn.get()); end, procedure begin console.log('error'); end);     end);   end; end; logs 'loaded' and 'true'
    your other link does specify useful functions which can be added to the above
     
     
  16. Like
    lynkfs reacted to jarto in Facebook or Google OAuth   
    I thought I'd test this. When I try to compile, I get an error in FaceBook.SDK.pas:
    Syntax Error: Class "JFBUIParams" isn't defined completely [line: 15, column: 35, file: FaceBook.SDK] Edit: Got the SDK to compile by changing into this:
    JFBUIParams = class external end;  
  17. Like
    lynkfs got a reaction from jarto in Integrating Paypal Payment Script   
    Your code should work, except for the script placement.
    The below code is pretty much the same as yours, just embedded in a button click handler (which you might have done too)
        var W3Button : TW3Button := TW3Button.Create(W3Panel);     W3Button.SetBounds(580,-10+160*(cartitems.length+1),200,40);     W3Button.Caption := 'Make Payment';     W3Button.OnClick := procedure(sender:TObject)     begin       var PayPal : TW3Panel := TW3Panel.Create(W3Panel);       Paypal.SetBounds(140,100 + 160*(cartitems.length+1),400,800); //position below cart        Paypal.StyleClass := 'DIV'; //removes all smart's style info       Paypal.handle.id := 'paypal-button-container';       Paypal.NativeScrolling := true;       var paypalamount : string := StrAfter(currencytotal,'A$');       asm         paypal.Buttons({           createOrder: function(data, actions) {             // This function sets up the details of the transaction, including the amount and line item details.             return actions.order.create({               purchase_units: [{                 amount: {                   value: @paypalamount                 }               }]             });           },           onApprove: function(data, actions) {             // This function captures the funds from the transaction.             return actions.order.capture().then(function(details) {               // This function shows a transaction success message to your buyer.               alert('Transaction completed by ' + details.payer.name.given_name);             });           }         }).render('#paypal-button-container');         //This function displays Smart Payment Buttons on your web page.       end;
    as for the script, put it in the body of your custom template
    <body>     <script src="https://www.paypal.com/sdk/js?client-id=xxx&currency=AUD">
     
  18. Like
    lynkfs reacted to jarto in Smart Mobile Studio 3.9.1 (1st Alpha release) is available   
    A new update is available:
    IDE: When a property is changed, the IDE adds the required unit into uses clauses. RTL: Bug fix to TRESTCall: Call OnRemove only if it is assigned.
  19. Like
    lynkfs got a reaction from jarto in percentage based positioning   
    Percentage based positioning has the advantage of having a 'responsive' page layout with little effort.
    Responsive in this respect means that page layouts will scale seamlessly on devices which may have slightly different pixel dimensions horizontally and/or vertically.
    So instead of having an editbox on absolute positioning 100,100,400,35 in pixel terms, it is sometimes easier to define all or some of these in percentages. Conveniently 10%-left is interpreted by the browser as starting on 10% of the viewport width, 10%-top starts at 10% of the viewport height etc.
    Mixing pixels and percentages works fine too
    MyEditBox.handle.style.left := '10%'; MyEditBox.handle.style.top := '12px'; //MyEditBox.left := 12; This is all still based on absolute positioning, which is the default in Smart.
    An alternative approach, with the same outcome, is to define dimensions at runtime based on a formula.
    MyEditBox.handle.style.left := 'calc(10vw - 20px)'  resolves in a left positioning of 10% of the viewport-width minus 20 pixels : 28 pixels on a 480 pixels wide phone
    Note that the minus sign needs to be enclosed in spaces or this won't work
    This can be also used to scale f.i. font size depending on device capabilities :
    MyEditBox.handle.style['font-size'] := 'calc(16px + 1vw)'  which has the effect of a font size which grows by 1 pixel for every 100 pixels of viewport width. 
    It can get a bit funny on orientation change
     
    an all-percentage example page with some random text and images, based on these principles, looks like this , which scales nicely from mobile to desktop (resize browser window).
  20. Like
    lynkfs got a reaction from Czar in panel.zoom and firefox   
    zoom is not supported in firefox (https://caniuse.com/#search=zoom) and opera
    The workaround is to use the "transform" css function
    W3Panel1.handle.style.transform := "scale(1.5)"; You can scale to different values for x and y as in "scale(2, 3)"
  21. Like
    lynkfs reacted to jarto in Update   
    Hi all,
    as you probably have noticed, I've been very quiet lately. When COVID-19 spread around the world, the restrictions hit my personal life pretty hard. In UAE, all the schools were closed very early and remained closed all the way to the end of the semester. There was also a strict lock down and lots of restrictions. Doing a lock down with three small children and having to home school them basically meant that my chances of spending many hours of time on development went to nil.
    Luckily it seems like the schools will open in 3 weeks, which should give me a possibility to concentrate on development again. My plan is to get 3.0 out then.
  22. Like
    lynkfs reacted to warleyalex in Dynamic Allocation of Records   
    FRepliedSearchCriteria.SetLength(20);
        console.log(
          JSON.Stringify(Variant(FRepliedSearchCriteria))
          );
  23. Like
    lynkfs reacted to Daniel Eiszele in the future of coding   
    With great power comes great responsibility!
  24. Like
    lynkfs got a reaction from Czar in the future of coding   
    Read this article , at least till the paragraph "and now for the fun part". 
    Mindblowing.
    Then, after that, have a look at this 
  25. Like
    lynkfs got a reaction from jarto in the future of coding   
    Read this article , at least till the paragraph "and now for the fun part". 
    Mindblowing.
    Then, after that, have a look at this 
×
×
  • Create New...