Jump to content

lynkfs

Moderators
  • Content Count

    743
  • Joined

  • Last visited

  • Days Won

    146

Posts posted by lynkfs

  1. Something like this

      var Script : variant := browserapi.document.createElement('script');
      Script.src := '<your script statements>';
      browserapi.document.head.appendChild(Script);
      Script.onload := procedure
      begin
        writeln('script loaded');
      end;

    you can put <the script statements> as javascript in an asm block or alternatively pascalify them :

    var atOptions : variant := new JObject;
    atOptions.key := '000000';
    etc

     

  2. The listbox items map to the <input type=text> html element, which are single line items (EditBox)

    A multi line item would have to map to say a <textarea> element (Memo)

    In this case you would have to roll your own listbox component, which generates html like below

     

    Capture.JPG

     

    You can delve a bit in the SmartCl.Controls.ListBox unit to see how this component has been put together.

    For instance Height can be set by coding W3ListBox.ItemHeight := 60;

  3. There is no expiration date on local storage

    However, users can manipulate local storage themselves, or the browser can decide to purge when it needs memory, so there is no guarantee it will always exist.

    Local storage is also specific per protocol, so http:// and https:// point to different storage objects. Also storage in private browsing mode gets handled with specific rules.

    You can set an expiration yourself if necessary though

     

  4. 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

     

     

  5. The standard generated index.html should prevent what you're experiencing :

        <meta name="viewport" content="width=device-width, maximum-scale=1.0, initial-scale=1.0, user-scalable=no"/>

    At least on Android and iPad it does.

    However there have been problems reported in mobile Safari (

         https://stackoverflow.com/questions/37808180/disable-viewport-zooming-ios-10-safari
         https://stackoverflow.com/questions/10614481/disable-double-tap-zoom-option-in-browser-on-touch-devices

    If your problem is indeed linked to mobile/Safari on iPhones, then you can also disable zoom in the settings, or use a 3 finger tap :(

    At the moment I don't have an Apple mobile to test the solutions in the links above, but will do soon as I get access

     

  6. 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. 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 ...
     

     

  8. Apparently the signature of the promise.then function (executor function) in the compiled js file needs to be exactly like this :

    promise.then(function () {
       //console.log('success');      
    }, function () {
       //console.log('error');     
    })

    As long as the compiler ejects this structure, it works fine

    Probably other team members could shed some more light on this ?

  9. 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)

     

    Capture.jpg

  10. @jarto unearthed an interesting problem to do with promises

    Basically the <promise>.then function has as parameters 2 call back functions. 
    The first one will be executed on success, the second if there is an error.

    mypromise.then(procedure begin ...success... end, procedure begin ...error... end)

    This works fine. 

    However defining these callbacks as regular external procedures and substituting these in the promise function call goes awfully wrong :

      procedure SuccessProc;
      begin
        writeln('success');
      end;
    
      mypromise.then(SuccessProc, ErrorProc);

    This construct triggers BOTH of the async callbacks

    Obviously not good.
    This is either a subtle compiler problem or an elusive scope issue


    A middle ground approach though works again as expected :

          SuccessProc :=  function(value:variant) : variant begin writeln('success'); end;
          ErrorProc   :=  function(value:variant) : variant begin writeln('error'); end;
    
          mypromise.then(procedure(Value: Variant) begin SuccessProc(Value) end, procedure(err: Variant) begin ErrorProc(err) end);

    (here using the rtl-api-ecma-Ecma.promise unit) 

  11. 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

     

     

  12. I've just compiled some large regular projects and some based on my own rtl  no problems

    so maybe your projects have some special dependencies, or ...

    just to be sure, how I tested this :

    - navigated to the smsc directory
    - opened cmd window
    - smsc "G:\SMS3.9.1\Projects\animate5\animate5.sproj"

    tested with fully developed projects

    (Not sure, but don't think the ide itself uses smsc)

  13. The syntax is

    Usage: smsc [@optionFile] [options] project_name[.sproj|.pas] [options]
    
      -output-name=<fileName>                 Output file name
      -resource-path=<paths>                  Resource directories
      -unit-path=<paths>                      Unit directories
      -verbosity=none|normal|verbose          Verbosity level
      -hints=disabled|normal|strict|pedantic  Hints level
      -stage=compile|codegen|output           Last compile stage
      -defines=<defines>                      Custom conditional defines
      -search-path=<paths>                    Search path
      -closures=yes|no                        Enable/disable closures
      -code-packing=yes|no                    Enable/disable code packing
      -check-assertions=yes|no                Enable/disable assertion checks
      -check-condition=yes|no                 Enable/disable condition checking
      -check-instance=yes|no                  Enable/disable instance checking
      -check-loop=yes|no                      Enable/disable loop checking
      -check-range=yes|no                     Enable/disable range checking
      -compress-css=yes|no                    Enable/disable CSS compression
      -devirtualization=yes|no                Enable/disable de-virtualization
      -emit-locations=yes|no                  Enable/disable source location
      -emit-cache-manifest=yes|no             Enable/disable app-cache manifest
      -emit-chrome-manifest=yes|no            Enable/disable chrome app manifest
      -emit-source-map=yes|no                 Enable/disable source map
      -inline=yes|no                          Enable/disable code inlining
      -obfuscation=yes|no                     Enable/disable obfuscation
      -optimization=yes|no                    Enable/disable compiler optimization
      -rtti=yes|no                            Enable/disable RTTI
      -smart-linking=yes|no                   Enable/disable smart linking
      -logo=yes|no                            Show/hide logo

    I must admit I have not used it much

    but out of the box this works for me

    smsc "C:\Users\....\MyProjects\window\window.sproj" 
    -output-name="C:\Users\....\MyProjects\window\window.js"

    or even without the -output-name parameter it compiles and links into whatever is specified in the project file (like '\www\main.js')

  14. do something like this (max control)

      var Script := browserapi.document.createElement('script');
      Script.src := 'https://.......your script url ........';
    
      browserapi.document.head.appendChild(Script);
      Script.onload := procedure
      begin
        showmessage('loaded');
        ... do your thing ...
      end;

    this can be part of a button click handler
     

     

  15. 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">


     

×
×
  • Create New...