Jump to content

lynkfs

Moderators
  • Content Count

    743
  • Joined

  • Last visited

  • Days Won

    146

Reputation Activity

  1. Like
    lynkfs reacted to jarto in New Visual Designer for Smart Mobile Studio   
    This week has been a bit slow as I've been sick. However, here's a little pic of the Menu Designer

  2. Like
    lynkfs got a reaction from IElite in KaTex   
    and this works too
      var html : variant;   asm   @html = katex.renderToString("c = \\pm\\sqrt{a^3 + b^3}", {     throwOnError: false });   end;   W3Panel1.InnerHTML := html;  
  3. Like
    lynkfs got a reaction from IElite in KaTex   
    A quick solution :
    have a form with a panel and a button
    in the custom template add these lines
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.css" integrity="sha384-bsHo4/LA+lkZv61JspMDQB9QP1TtO4IgOf2yYS+J6VdAYLVyx1c3XKcsHh0Vy8Ws" crossorigin="anonymous"> <script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.js" integrity="sha384-4z8mjH4yIpuK9dIQGR1JwbrfYsStrNK6MP+2Enhue4eyo0XlBDXOIPc8b6ZU0ajz" crossorigin="anonymous"></script>  
    in the onclick handler of the button add this code
    procedure TForm1.W3Button1Click(Sender: TObject); begin   var katexelement : variant;   katexelement := W3Panel1.handle;   asm   katex.render("c = \\pm\\sqrt{a^2 + b^2}", @katexelement, {     throwOnError: false   });   end; end;  
    which results in this :
     

     
    This code can be improved a bit.
    F.i. I like to load the js library under program control, so at least you know when it is ready
     
  4. Like
    lynkfs reacted to warleyalex in KaTex   
    //------ typescript definition -------
    /** Documentation: https://katex.org/docs/options.html */
    export interface KatexOptions {
        /**
         * If `true`, math will be rendered in display mode
         * (math in display style and center math on page)
         *
         * If `false`, math will be rendered in inline mode
         * @default false
         */
        displayMode?: boolean;
        /**
         * If `true`, KaTeX will throw a `ParseError` when
         * it encounters an unsupported command or invalid LaTex
         *
         * If `false`, KaTeX will render unsupported commands as
         * text, and render invalid LaTeX as its source code with
         * hover text giving the error, in color given by errorColor
         * @default true
         */
        throwOnError?: boolean;
        /**
         * A Color string given in format `#XXX` or `#XXXXXX`
         */
        errorColor?: string;
        /**
         * A collection of custom macros.
         *
         * See `src/macros.js` for its usage
         */
        macros?: any;
        /**
         * If `true`, `\color` will work like LaTeX's `\textcolor`
         * and takes 2 arguments
         *
         * If `false`, `\color` will work like LaTeX's `\color`
         * and takes 1 argument
         *
         * In both cases, `\textcolor` works as in LaTeX
         *
         * @default false
         */
        colorIsTextColor?: boolean;
        /**
         * All user-specified sizes will be caped to `maxSize` ems
         *
         * If set to Infinity, users can make elements and space
         * arbitrarily large
         *
         * @default Infinity
         */
        maxSize?: number;
        /**
         * Limit the number of macro expansions to specified number
         *
         * If set to `Infinity`, marco expander will try to fully expand
         * as in LaTex
         *
         * @default 1000
         */
        maxExpand?: number;
        /**
         * Allowed protocols in `\href`
         *
         * Use `_relative` to allow relative urls
         *
         * Use `*` to allow all protocols
         */
        allowedProtocols?: string[];
        /**
         * If `false` or `"ignore"`, allow features that make
         * writing in LaTex convenient but not supported by LaTex
         *
         * If `true` or `"error"`, throw an error for such transgressions
         *
         * If `"warn"`, warn about behavior via `console.warn`
         *
         * @default "warn"
         */
        strict?: boolean | string | Function;
    }
    export class ParseError implements Error {
        constructor(message: string, lexer: any, position: number);
        name: string;
        message: string;
        position: number;
    }
    /**
     * Renders a TeX expression into the specified DOM element
     * @param tex A TeX expression
     * @param element The DOM element to render into
     * @param options KaTeX options
     */
    export function render(tex: string, element: HTMLElement, options?: KatexOptions): void;
    /**
     * Renders a TeX expression into an HTML string
     * @param tex A TeX expression
     * @param options KaTeX options
     */
    export function renderToString(tex: string, options?: KatexOptions): string;
    //----------------
    // ====>- pascal external class definition  <====
    unit katex;
    interface
    uses 
      System.Types, W3C.HTML5, W3C.DOM;
    type
      TJSKatexOptions = class external 'Object'
        displayMode: Boolean; // nullable
        throwOnError: Boolean; // nullable
        errorColor: String; // nullable
        macros: Variant; // nullable
        colorIsTextColor: Boolean; // nullable
        maxSize: Float; // nullable
        maxExpand: Float; // nullable
        allowedProtocols: TStrArray; // nullable
        &strict: Variant; {Boolean or String or JFunction} // nullable
      end;
    type
      TJSKatex = class external 'katex'
      public
        class procedure render(tex: String; element: JHTMLElement); overload; external 'render';
        class procedure render(tex: String; element: JHTMLElement; options: TJSKatexOptions); overload; external 'render';
        class function renderToString(tex: String): String; overload; external 'renderToString';
        class function renderToString(tex: String; options: TJSKatexOptions): String; overload;external 'renderToString';
      end;
    type
      TJSParseError = class external 'katex.ParseError'
        constructor Create(const msg: String; lexer: Variant; position: Float);
        name: String;
        &message: String;
        position: Float;
      end;
    implementation
    end.
    //---------------------------------------------------------------------
  5. Like
    lynkfs reacted to jarto in SMS and the Ionic Framework   
    Hi Thomas,
    Thank you for an interesting idea. At the moment we are working on the new Visual Designer. Once it's finished, it's way easier to create beautiful and functional PWA apps without having to create all by code. So, the plan is:
    Finish the Designer and have it support all current Visual components. (90% ready) Add non-visual components to the component palette Release a version Once we have that out, we'll be able to plan our next steps.
  6. Like
    lynkfs got a reaction from jarto in KaTex   
    and this works too
      var html : variant;   asm   @html = katex.renderToString("c = \\pm\\sqrt{a^3 + b^3}", {     throwOnError: false });   end;   W3Panel1.InnerHTML := html;  
  7. Like
    lynkfs got a reaction from jarto in KaTex   
    A quick solution :
    have a form with a panel and a button
    in the custom template add these lines
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.css" integrity="sha384-bsHo4/LA+lkZv61JspMDQB9QP1TtO4IgOf2yYS+J6VdAYLVyx1c3XKcsHh0Vy8Ws" crossorigin="anonymous"> <script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.js" integrity="sha384-4z8mjH4yIpuK9dIQGR1JwbrfYsStrNK6MP+2Enhue4eyo0XlBDXOIPc8b6ZU0ajz" crossorigin="anonymous"></script>  
    in the onclick handler of the button add this code
    procedure TForm1.W3Button1Click(Sender: TObject); begin   var katexelement : variant;   katexelement := W3Panel1.handle;   asm   katex.render("c = \\pm\\sqrt{a^2 + b^2}", @katexelement, {     throwOnError: false   });   end; end;  
    which results in this :
     

     
    This code can be improved a bit.
    F.i. I like to load the js library under program control, so at least you know when it is ready
     
  8. Like
    lynkfs got a reaction from Czar in KaTex   
    and this works too
      var html : variant;   asm   @html = katex.renderToString("c = \\pm\\sqrt{a^3 + b^3}", {     throwOnError: false });   end;   W3Panel1.InnerHTML := html;  
  9. Like
    lynkfs got a reaction from Czar in KaTex   
    A quick solution :
    have a form with a panel and a button
    in the custom template add these lines
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.css" integrity="sha384-bsHo4/LA+lkZv61JspMDQB9QP1TtO4IgOf2yYS+J6VdAYLVyx1c3XKcsHh0Vy8Ws" crossorigin="anonymous"> <script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.js" integrity="sha384-4z8mjH4yIpuK9dIQGR1JwbrfYsStrNK6MP+2Enhue4eyo0XlBDXOIPc8b6ZU0ajz" crossorigin="anonymous"></script>  
    in the onclick handler of the button add this code
    procedure TForm1.W3Button1Click(Sender: TObject); begin   var katexelement : variant;   katexelement := W3Panel1.handle;   asm   katex.render("c = \\pm\\sqrt{a^2 + b^2}", @katexelement, {     throwOnError: false   });   end; end;  
    which results in this :
     

     
    This code can be improved a bit.
    F.i. I like to load the js library under program control, so at least you know when it is ready
     
  10. Like
    lynkfs reacted to jarto in New Visual Designer for Smart Mobile Studio   
    You know the feeling when everything makes sense and everything is easy and you get a lot done? That's where I've been today.
    I cleaned up the IDE code and removed the old designer and the live preview-code, which never worked. Then Snap to grid and export to code and XML. The old designer's popup menu had many functions, but apparently half of them were never finished. So, while there is an undo there, it never worked. Same thing with aligns.
    Now I'm working on cut/copy/paste. While testing, I noticed that the old designer does not check for duplicate names, if you paste a component with child components. Also, you can't paste into another child component, like another panel. Weird thing is also, that the clipboard is cleaned after every paste. And even weirder is, that the old IDE is not using a global clipboard. Wonder why?
    So I ended up fixing those bugs in the old designer, as I get to use most of the underlying functionality with the new designer as well. Also wrote a static TW3URL-class for nice handling of url parameters.
    Life is so good in the zone 🙂
  11. Like
    lynkfs reacted to jarto in New Visual Designer for Smart Mobile Studio   
    Let me also write about the progress I'm making with the designer. I got it running inside embedded Chrome in the IDE last night. I'm very excited with everything you can do with this new designer and what it lets us do in the future.
    Let's start with a small geeky detail. Look at those dots in the form. They are the standard 8px grid that Delphi and Lazarus also have. Two for-loops plotting dots on an html5-canvas? No, no, no, this is made with pure CSS and you can use this same code in your own apps as well:
    DesignerForm.Handle.style['background'] := 'linear-gradient(90deg, #EFEBE7 7px, transparent 1%), linear-gradient(#EFEBE7 7px, transparent 1%), #000000'; DesignerForm.Handle.style['background-size'] := '8px 8px'; DesignerForm.Handle.style['background-position'] := '1px 1px'; What is lovely is that you can see what every property does. Like you can see in the image above, I changed the ThemeBorder for the label to flat. So, you can change the theme border and background for every control and see what it looks like. Set AlphaBlend to True and adjust Opacity and you'll see the result. All the other properties are also supported: Color, Angle, BorderRadius, you name it. And it's really cool that you can change functional properties and see what the control did. Fill in the Items of a ListBox and you can see how they are drawn.
    This one will also unlock the secrets of TW3EditBox. Look at the picture below. The slider down there is just a TW3EditBox with itRange as InputType. The panel with the angle has three sub panels and are using Align-properties to position themselves. Inside the middle panel is a TW3ListBox where the background Opacity is adjusted. Hence, you see the blue color from the background getting through. If the background had been an image, you'd see it as well.
    I've been testing and drawing forms for a while now and it's really great fun. I hope I can get this finished soon so I can share the fun with you guys too.

     
  12. Like
    lynkfs reacted to jarto in New Visual Designer for Smart Mobile Studio   
  13. Like
    lynkfs reacted to jarto in TW3StringGrid and TW3Grid Issues wtih Scrolling in Firefox   
    Found the reason. The browser specific CSS transformation for Firefox is wrong. There are two ways I can fix this: I can simply fix the Firefox transformation or then remove the browser specific CSS and go with generic CSS.
    It may be a better idea to change all to use generic CSS. It may break some really old browsers, but modern ones are going to stop supporting these soon anyway.
    Gonna fix this today and push an update in the evening.
  14. Like
    lynkfs got a reaction from Tim Koscielski in HTML programming   
    A bit of a weird topic name for a product which elevates Object Pascal using javascript.
    I could have named it 'Web Components' (as I did a while ago) or maybe better 'Api Programming'.
    There are a number of organisations which look after the web api's. W3C is the most known one, but there are others looking after specific parts (WhatWG, Khronos etc.). What they have in common is that they publish the web api's they're looking after in a specific formal format (WebIDL).
    Smart has transformed these specifications in corresponding pascal classes, contained in the APIs directory in the RTL. These classes basically map directly onto functions and data available in the browser itself, and using them is sort of similar to for instance incorporating Microsoft Office functions in a Delphi program (although technically very different from using COM).
    The RTL uses some of these api's under the hood, this post is about a mini-rtl if you like consisting of just 1 component.
    I use this approach a lot for non-form based projects, single-page-apps (or multiple) and websites.
    Demo of a semi-static SPA 
    The inheritance chain of the DOM looks like
    - EventTarget
      - Node
        - Element
          - HTMLElement
    with all the various html elements underneath : HTMLDivElement for a <div>, HTMLAnchorElement for a <a> etc.
    The code below wraps the HTMLElement, but allows its constructor to create any of the specialised sub-elements.
    unit JElement; interface uses W3C.HTML5, W3C.DOM, W3C.CSS; type   THTMLElement = class   private     FHTMLElement: JHTMLElement;   public     constructor Create(element: String; parent: THTMLElement; className: String; innerHTML : String);  end; implementation { THTMLElement } constructor THTMLElement.Create(element: String; parent: THTMLElement; className: String; innerHTML : String); begin   // cache element and set innerHTML and className   FHTMLElement := JHTMLElement(Document.createElement(element));   FHTMLElement.innerHTML := innerHTML;   FHTMLElement.className := ClassName;   If parent = nil     then document.body.appendChild(FHTMLElement)     else parent.FHTMLElement.appendchild(FHTMLElement); end; HTMLElements have as specifiers 'attributes' and 'properties'. Properties are any of the values in the 'style' attribute : in the html expression style="left=10px; color=white;" href="#" both 'left' and 'color' are properties, and both 'style' and 'href' are attributes.
    So we need to add the setters for these parameters :
    Procedure THTMLElement.SetProperty(s1: String; S2: String); begin   var FElementStyle := JElementCSSInlineStyle(FHTMLElement).style;   FElementStyle.setProperty(S1, S2); end; Procedure THTMLElement.SetAttribute(S1: String; S2: String); begin   FHTMLElement.setAttribute(S1, S2); end; The first part of the demo app is an image, which fills the complete viewport on any device and orientation, a slightly darker tinted bar on top, and a button in the bar linking to an email form further down.
    This header can be constructed as per below :
    unit JHeader; interface uses JHTMLElement, smartcl.system; type   THeader = class(THTMLElement)   public     constructor Create(props: Variant); end; implementation { THeader } constructor THeader.Create(props: Variant); begin   //hero image   var hdr0  := THTMLElement.Create('div', nil, 'div');       hdr0.setProperty('width', '100%');       hdr0.setProperty('height', '100%');       hdr0.setProperty('min-height', '100vh');       hdr0.setProperty('background-image','url('+props['header-img']+')');       hdr0.setProperty('background-size','cover');     //top bar 100px     var hdr11  := THTMLElement.Create('div', hdr0, 'div');         hdr11.setProperty('width', '100%');         hdr11.setProperty('height', '100px');         hdr11.setProperty('background-color','rgb(21,21,21,0.2)');       //contact button       var hdr21  := THTMLElement.Create('a', hdr11, 'div', 'Contact');           hdr21.SetBounds(20,30,75,35);           hdr21.setProperty('color', 'white');           hdr21.SetAttribute('type', 'button');           hdr21.setAttribute('href', '#contact');           hdr21.SetProperty('background-color', 'rgb(21,21,21,0.2)'); which can be instantiated as in (with a variable for the image)
    //THeader   props['header-img'] := 'assets/img/blueprint.jpg';   THeader.Create(props); and which on execution time resolves in
    <div class="div" style="width: 100%; height: 100%; min-height: 100vh; background-image: url(http://127.0.0.1:8090/assets/img/blueprint.jpg); background-size: cover;">   <div class="div" style="width: 100%; height: 100px;">     <a class="div" type="button" href="#contact" style="visibility: visible; display: inline-block; position: absolute; overflow: auto; left: 20px; top: 30px; width: 75px; height: 35px; color: white;">       Contact     </a>   </div> </div>
    Hence the title of this post : HTML programming.
    The advantage of this approach is that once there are a number of these multi-purpose constructs like THeader available, making up another webpage is as easy as providing values for constructor parameters and stringing them together.
    At some stage I created the constructs listed here
    They need to be updated and streamlined a bit.
      
  15. Like
    lynkfs reacted to jarto in WebWorker and InitAnimationFrameShim   
    Try editing SmartCL.Time.pas and remove the call to InitAnimationFrameShim in the initialization -section in end of the file.
  16. Like
    lynkfs got a reaction from Daniel Eiszele in HTML programming   
    A bit of a weird topic name for a product which elevates Object Pascal using javascript.
    I could have named it 'Web Components' (as I did a while ago) or maybe better 'Api Programming'.
    There are a number of organisations which look after the web api's. W3C is the most known one, but there are others looking after specific parts (WhatWG, Khronos etc.). What they have in common is that they publish the web api's they're looking after in a specific formal format (WebIDL).
    Smart has transformed these specifications in corresponding pascal classes, contained in the APIs directory in the RTL. These classes basically map directly onto functions and data available in the browser itself, and using them is sort of similar to for instance incorporating Microsoft Office functions in a Delphi program (although technically very different from using COM).
    The RTL uses some of these api's under the hood, this post is about a mini-rtl if you like consisting of just 1 component.
    I use this approach a lot for non-form based projects, single-page-apps (or multiple) and websites.
    Demo of a semi-static SPA 
    The inheritance chain of the DOM looks like
    - EventTarget
      - Node
        - Element
          - HTMLElement
    with all the various html elements underneath : HTMLDivElement for a <div>, HTMLAnchorElement for a <a> etc.
    The code below wraps the HTMLElement, but allows its constructor to create any of the specialised sub-elements.
    unit JElement; interface uses W3C.HTML5, W3C.DOM, W3C.CSS; type   THTMLElement = class   private     FHTMLElement: JHTMLElement;   public     constructor Create(element: String; parent: THTMLElement; className: String; innerHTML : String);  end; implementation { THTMLElement } constructor THTMLElement.Create(element: String; parent: THTMLElement; className: String; innerHTML : String); begin   // cache element and set innerHTML and className   FHTMLElement := JHTMLElement(Document.createElement(element));   FHTMLElement.innerHTML := innerHTML;   FHTMLElement.className := ClassName;   If parent = nil     then document.body.appendChild(FHTMLElement)     else parent.FHTMLElement.appendchild(FHTMLElement); end; HTMLElements have as specifiers 'attributes' and 'properties'. Properties are any of the values in the 'style' attribute : in the html expression style="left=10px; color=white;" href="#" both 'left' and 'color' are properties, and both 'style' and 'href' are attributes.
    So we need to add the setters for these parameters :
    Procedure THTMLElement.SetProperty(s1: String; S2: String); begin   var FElementStyle := JElementCSSInlineStyle(FHTMLElement).style;   FElementStyle.setProperty(S1, S2); end; Procedure THTMLElement.SetAttribute(S1: String; S2: String); begin   FHTMLElement.setAttribute(S1, S2); end; The first part of the demo app is an image, which fills the complete viewport on any device and orientation, a slightly darker tinted bar on top, and a button in the bar linking to an email form further down.
    This header can be constructed as per below :
    unit JHeader; interface uses JHTMLElement, smartcl.system; type   THeader = class(THTMLElement)   public     constructor Create(props: Variant); end; implementation { THeader } constructor THeader.Create(props: Variant); begin   //hero image   var hdr0  := THTMLElement.Create('div', nil, 'div');       hdr0.setProperty('width', '100%');       hdr0.setProperty('height', '100%');       hdr0.setProperty('min-height', '100vh');       hdr0.setProperty('background-image','url('+props['header-img']+')');       hdr0.setProperty('background-size','cover');     //top bar 100px     var hdr11  := THTMLElement.Create('div', hdr0, 'div');         hdr11.setProperty('width', '100%');         hdr11.setProperty('height', '100px');         hdr11.setProperty('background-color','rgb(21,21,21,0.2)');       //contact button       var hdr21  := THTMLElement.Create('a', hdr11, 'div', 'Contact');           hdr21.SetBounds(20,30,75,35);           hdr21.setProperty('color', 'white');           hdr21.SetAttribute('type', 'button');           hdr21.setAttribute('href', '#contact');           hdr21.SetProperty('background-color', 'rgb(21,21,21,0.2)'); which can be instantiated as in (with a variable for the image)
    //THeader   props['header-img'] := 'assets/img/blueprint.jpg';   THeader.Create(props); and which on execution time resolves in
    <div class="div" style="width: 100%; height: 100%; min-height: 100vh; background-image: url(http://127.0.0.1:8090/assets/img/blueprint.jpg); background-size: cover;">   <div class="div" style="width: 100%; height: 100px;">     <a class="div" type="button" href="#contact" style="visibility: visible; display: inline-block; position: absolute; overflow: auto; left: 20px; top: 30px; width: 75px; height: 35px; color: white;">       Contact     </a>   </div> </div>
    Hence the title of this post : HTML programming.
    The advantage of this approach is that once there are a number of these multi-purpose constructs like THeader available, making up another webpage is as easy as providing values for constructor parameters and stringing them together.
    At some stage I created the constructs listed here
    They need to be updated and streamlined a bit.
      
  17. Like
    lynkfs got a reaction from IgorSavkic in HTML programming   
    A bit of a weird topic name for a product which elevates Object Pascal using javascript.
    I could have named it 'Web Components' (as I did a while ago) or maybe better 'Api Programming'.
    There are a number of organisations which look after the web api's. W3C is the most known one, but there are others looking after specific parts (WhatWG, Khronos etc.). What they have in common is that they publish the web api's they're looking after in a specific formal format (WebIDL).
    Smart has transformed these specifications in corresponding pascal classes, contained in the APIs directory in the RTL. These classes basically map directly onto functions and data available in the browser itself, and using them is sort of similar to for instance incorporating Microsoft Office functions in a Delphi program (although technically very different from using COM).
    The RTL uses some of these api's under the hood, this post is about a mini-rtl if you like consisting of just 1 component.
    I use this approach a lot for non-form based projects, single-page-apps (or multiple) and websites.
    Demo of a semi-static SPA 
    The inheritance chain of the DOM looks like
    - EventTarget
      - Node
        - Element
          - HTMLElement
    with all the various html elements underneath : HTMLDivElement for a <div>, HTMLAnchorElement for a <a> etc.
    The code below wraps the HTMLElement, but allows its constructor to create any of the specialised sub-elements.
    unit JElement; interface uses W3C.HTML5, W3C.DOM, W3C.CSS; type   THTMLElement = class   private     FHTMLElement: JHTMLElement;   public     constructor Create(element: String; parent: THTMLElement; className: String; innerHTML : String);  end; implementation { THTMLElement } constructor THTMLElement.Create(element: String; parent: THTMLElement; className: String; innerHTML : String); begin   // cache element and set innerHTML and className   FHTMLElement := JHTMLElement(Document.createElement(element));   FHTMLElement.innerHTML := innerHTML;   FHTMLElement.className := ClassName;   If parent = nil     then document.body.appendChild(FHTMLElement)     else parent.FHTMLElement.appendchild(FHTMLElement); end; HTMLElements have as specifiers 'attributes' and 'properties'. Properties are any of the values in the 'style' attribute : in the html expression style="left=10px; color=white;" href="#" both 'left' and 'color' are properties, and both 'style' and 'href' are attributes.
    So we need to add the setters for these parameters :
    Procedure THTMLElement.SetProperty(s1: String; S2: String); begin   var FElementStyle := JElementCSSInlineStyle(FHTMLElement).style;   FElementStyle.setProperty(S1, S2); end; Procedure THTMLElement.SetAttribute(S1: String; S2: String); begin   FHTMLElement.setAttribute(S1, S2); end; The first part of the demo app is an image, which fills the complete viewport on any device and orientation, a slightly darker tinted bar on top, and a button in the bar linking to an email form further down.
    This header can be constructed as per below :
    unit JHeader; interface uses JHTMLElement, smartcl.system; type   THeader = class(THTMLElement)   public     constructor Create(props: Variant); end; implementation { THeader } constructor THeader.Create(props: Variant); begin   //hero image   var hdr0  := THTMLElement.Create('div', nil, 'div');       hdr0.setProperty('width', '100%');       hdr0.setProperty('height', '100%');       hdr0.setProperty('min-height', '100vh');       hdr0.setProperty('background-image','url('+props['header-img']+')');       hdr0.setProperty('background-size','cover');     //top bar 100px     var hdr11  := THTMLElement.Create('div', hdr0, 'div');         hdr11.setProperty('width', '100%');         hdr11.setProperty('height', '100px');         hdr11.setProperty('background-color','rgb(21,21,21,0.2)');       //contact button       var hdr21  := THTMLElement.Create('a', hdr11, 'div', 'Contact');           hdr21.SetBounds(20,30,75,35);           hdr21.setProperty('color', 'white');           hdr21.SetAttribute('type', 'button');           hdr21.setAttribute('href', '#contact');           hdr21.SetProperty('background-color', 'rgb(21,21,21,0.2)'); which can be instantiated as in (with a variable for the image)
    //THeader   props['header-img'] := 'assets/img/blueprint.jpg';   THeader.Create(props); and which on execution time resolves in
    <div class="div" style="width: 100%; height: 100%; min-height: 100vh; background-image: url(http://127.0.0.1:8090/assets/img/blueprint.jpg); background-size: cover;">   <div class="div" style="width: 100%; height: 100px;">     <a class="div" type="button" href="#contact" style="visibility: visible; display: inline-block; position: absolute; overflow: auto; left: 20px; top: 30px; width: 75px; height: 35px; color: white;">       Contact     </a>   </div> </div>
    Hence the title of this post : HTML programming.
    The advantage of this approach is that once there are a number of these multi-purpose constructs like THeader available, making up another webpage is as easy as providing values for constructor parameters and stringing them together.
    At some stage I created the constructs listed here
    They need to be updated and streamlined a bit.
      
  18. Like
    lynkfs got a reaction from jarto in HTML programming   
    A bit of a weird topic name for a product which elevates Object Pascal using javascript.
    I could have named it 'Web Components' (as I did a while ago) or maybe better 'Api Programming'.
    There are a number of organisations which look after the web api's. W3C is the most known one, but there are others looking after specific parts (WhatWG, Khronos etc.). What they have in common is that they publish the web api's they're looking after in a specific formal format (WebIDL).
    Smart has transformed these specifications in corresponding pascal classes, contained in the APIs directory in the RTL. These classes basically map directly onto functions and data available in the browser itself, and using them is sort of similar to for instance incorporating Microsoft Office functions in a Delphi program (although technically very different from using COM).
    The RTL uses some of these api's under the hood, this post is about a mini-rtl if you like consisting of just 1 component.
    I use this approach a lot for non-form based projects, single-page-apps (or multiple) and websites.
    Demo of a semi-static SPA 
    The inheritance chain of the DOM looks like
    - EventTarget
      - Node
        - Element
          - HTMLElement
    with all the various html elements underneath : HTMLDivElement for a <div>, HTMLAnchorElement for a <a> etc.
    The code below wraps the HTMLElement, but allows its constructor to create any of the specialised sub-elements.
    unit JElement; interface uses W3C.HTML5, W3C.DOM, W3C.CSS; type   THTMLElement = class   private     FHTMLElement: JHTMLElement;   public     constructor Create(element: String; parent: THTMLElement; className: String; innerHTML : String);  end; implementation { THTMLElement } constructor THTMLElement.Create(element: String; parent: THTMLElement; className: String; innerHTML : String); begin   // cache element and set innerHTML and className   FHTMLElement := JHTMLElement(Document.createElement(element));   FHTMLElement.innerHTML := innerHTML;   FHTMLElement.className := ClassName;   If parent = nil     then document.body.appendChild(FHTMLElement)     else parent.FHTMLElement.appendchild(FHTMLElement); end; HTMLElements have as specifiers 'attributes' and 'properties'. Properties are any of the values in the 'style' attribute : in the html expression style="left=10px; color=white;" href="#" both 'left' and 'color' are properties, and both 'style' and 'href' are attributes.
    So we need to add the setters for these parameters :
    Procedure THTMLElement.SetProperty(s1: String; S2: String); begin   var FElementStyle := JElementCSSInlineStyle(FHTMLElement).style;   FElementStyle.setProperty(S1, S2); end; Procedure THTMLElement.SetAttribute(S1: String; S2: String); begin   FHTMLElement.setAttribute(S1, S2); end; The first part of the demo app is an image, which fills the complete viewport on any device and orientation, a slightly darker tinted bar on top, and a button in the bar linking to an email form further down.
    This header can be constructed as per below :
    unit JHeader; interface uses JHTMLElement, smartcl.system; type   THeader = class(THTMLElement)   public     constructor Create(props: Variant); end; implementation { THeader } constructor THeader.Create(props: Variant); begin   //hero image   var hdr0  := THTMLElement.Create('div', nil, 'div');       hdr0.setProperty('width', '100%');       hdr0.setProperty('height', '100%');       hdr0.setProperty('min-height', '100vh');       hdr0.setProperty('background-image','url('+props['header-img']+')');       hdr0.setProperty('background-size','cover');     //top bar 100px     var hdr11  := THTMLElement.Create('div', hdr0, 'div');         hdr11.setProperty('width', '100%');         hdr11.setProperty('height', '100px');         hdr11.setProperty('background-color','rgb(21,21,21,0.2)');       //contact button       var hdr21  := THTMLElement.Create('a', hdr11, 'div', 'Contact');           hdr21.SetBounds(20,30,75,35);           hdr21.setProperty('color', 'white');           hdr21.SetAttribute('type', 'button');           hdr21.setAttribute('href', '#contact');           hdr21.SetProperty('background-color', 'rgb(21,21,21,0.2)'); which can be instantiated as in (with a variable for the image)
    //THeader   props['header-img'] := 'assets/img/blueprint.jpg';   THeader.Create(props); and which on execution time resolves in
    <div class="div" style="width: 100%; height: 100%; min-height: 100vh; background-image: url(http://127.0.0.1:8090/assets/img/blueprint.jpg); background-size: cover;">   <div class="div" style="width: 100%; height: 100px;">     <a class="div" type="button" href="#contact" style="visibility: visible; display: inline-block; position: absolute; overflow: auto; left: 20px; top: 30px; width: 75px; height: 35px; color: white;">       Contact     </a>   </div> </div>
    Hence the title of this post : HTML programming.
    The advantage of this approach is that once there are a number of these multi-purpose constructs like THeader available, making up another webpage is as easy as providing values for constructor parameters and stringing them together.
    At some stage I created the constructs listed here
    They need to be updated and streamlined a bit.
      
  19. Like
    lynkfs got a reaction from DavidRM in HTML programming   
    A bit of a weird topic name for a product which elevates Object Pascal using javascript.
    I could have named it 'Web Components' (as I did a while ago) or maybe better 'Api Programming'.
    There are a number of organisations which look after the web api's. W3C is the most known one, but there are others looking after specific parts (WhatWG, Khronos etc.). What they have in common is that they publish the web api's they're looking after in a specific formal format (WebIDL).
    Smart has transformed these specifications in corresponding pascal classes, contained in the APIs directory in the RTL. These classes basically map directly onto functions and data available in the browser itself, and using them is sort of similar to for instance incorporating Microsoft Office functions in a Delphi program (although technically very different from using COM).
    The RTL uses some of these api's under the hood, this post is about a mini-rtl if you like consisting of just 1 component.
    I use this approach a lot for non-form based projects, single-page-apps (or multiple) and websites.
    Demo of a semi-static SPA 
    The inheritance chain of the DOM looks like
    - EventTarget
      - Node
        - Element
          - HTMLElement
    with all the various html elements underneath : HTMLDivElement for a <div>, HTMLAnchorElement for a <a> etc.
    The code below wraps the HTMLElement, but allows its constructor to create any of the specialised sub-elements.
    unit JElement; interface uses W3C.HTML5, W3C.DOM, W3C.CSS; type   THTMLElement = class   private     FHTMLElement: JHTMLElement;   public     constructor Create(element: String; parent: THTMLElement; className: String; innerHTML : String);  end; implementation { THTMLElement } constructor THTMLElement.Create(element: String; parent: THTMLElement; className: String; innerHTML : String); begin   // cache element and set innerHTML and className   FHTMLElement := JHTMLElement(Document.createElement(element));   FHTMLElement.innerHTML := innerHTML;   FHTMLElement.className := ClassName;   If parent = nil     then document.body.appendChild(FHTMLElement)     else parent.FHTMLElement.appendchild(FHTMLElement); end; HTMLElements have as specifiers 'attributes' and 'properties'. Properties are any of the values in the 'style' attribute : in the html expression style="left=10px; color=white;" href="#" both 'left' and 'color' are properties, and both 'style' and 'href' are attributes.
    So we need to add the setters for these parameters :
    Procedure THTMLElement.SetProperty(s1: String; S2: String); begin   var FElementStyle := JElementCSSInlineStyle(FHTMLElement).style;   FElementStyle.setProperty(S1, S2); end; Procedure THTMLElement.SetAttribute(S1: String; S2: String); begin   FHTMLElement.setAttribute(S1, S2); end; The first part of the demo app is an image, which fills the complete viewport on any device and orientation, a slightly darker tinted bar on top, and a button in the bar linking to an email form further down.
    This header can be constructed as per below :
    unit JHeader; interface uses JHTMLElement, smartcl.system; type   THeader = class(THTMLElement)   public     constructor Create(props: Variant); end; implementation { THeader } constructor THeader.Create(props: Variant); begin   //hero image   var hdr0  := THTMLElement.Create('div', nil, 'div');       hdr0.setProperty('width', '100%');       hdr0.setProperty('height', '100%');       hdr0.setProperty('min-height', '100vh');       hdr0.setProperty('background-image','url('+props['header-img']+')');       hdr0.setProperty('background-size','cover');     //top bar 100px     var hdr11  := THTMLElement.Create('div', hdr0, 'div');         hdr11.setProperty('width', '100%');         hdr11.setProperty('height', '100px');         hdr11.setProperty('background-color','rgb(21,21,21,0.2)');       //contact button       var hdr21  := THTMLElement.Create('a', hdr11, 'div', 'Contact');           hdr21.SetBounds(20,30,75,35);           hdr21.setProperty('color', 'white');           hdr21.SetAttribute('type', 'button');           hdr21.setAttribute('href', '#contact');           hdr21.SetProperty('background-color', 'rgb(21,21,21,0.2)'); which can be instantiated as in (with a variable for the image)
    //THeader   props['header-img'] := 'assets/img/blueprint.jpg';   THeader.Create(props); and which on execution time resolves in
    <div class="div" style="width: 100%; height: 100%; min-height: 100vh; background-image: url(http://127.0.0.1:8090/assets/img/blueprint.jpg); background-size: cover;">   <div class="div" style="width: 100%; height: 100px;">     <a class="div" type="button" href="#contact" style="visibility: visible; display: inline-block; position: absolute; overflow: auto; left: 20px; top: 30px; width: 75px; height: 35px; color: white;">       Contact     </a>   </div> </div>
    Hence the title of this post : HTML programming.
    The advantage of this approach is that once there are a number of these multi-purpose constructs like THeader available, making up another webpage is as easy as providing values for constructor parameters and stringing them together.
    At some stage I created the constructs listed here
    They need to be updated and streamlined a bit.
      
  20. Like
    lynkfs got a reaction from IElite in Expand Combobox using code   
    Basically the <select> element where the combobox is based on, is under control of the browser. And can't be manipulated from pascal/js.
    There is a hack (there is a hack for everything) to fake it :
    procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components var W3ComboBox1 : Tw3ComboBox := TW3ComboBox.Create(self); W3ComboBox1.SetBounds(50,70,250,30); W3ComboBox1.Add('line 1'); W3ComboBox1.Add('line 2'); W3ComboBox1.Add('line 3'); W3ComboBox1.handle.style['opacity'] := 0; var W3Panel1 : TW3Panel := TW3Panel.Create(self); W3Panel1.SetBounds(50,70,250,30); W3Panel1.handle.style['z-index'] := 0; W3Panel1.handle.setAttribute('class',''); //get rid of all styling var W3Button1 : TW3Button := TW3Button.Create(W3Panel1); W3Button1.Caption := 'MyButton'; W3Button1.SetBounds(0,0,250,30); W3ComboBox1.OnChanged := procedure (Sender: TObject) begin W3Button1.Caption := W3ComboBox1.Items[W3ComboBox1.SelectedIndex]; end; end;  
    works on Chrome and other browsers. Works on mobile too
    Demo
    Wouldn't recommend it though ....
    edited :
    as per @IElite suggestion: it would also be possible to make the combobox invisible, read its items on the click of a button and display them in a listbox or memo.
    Better, in my opinion and if really necessary, is to create a dedicated control which does exactly what you want, based on a listbox rather than on a <select> htmlelement
     
  21. Like
    lynkfs reacted to jarto in Creating a mobile app for Android and iOS   
    Took some time, but I was able to create a test apk and install it on my phone.
    First of all, set up your folders the way I did in https://forums.smartmobilestudio.com/topic/4524-creating-a-mobile-app-for-android-and-ios/?do=findComment&comment=22461
    That will let you have one folder for all the Phonegap-related stuff and another folder for your SMS app.
    Here's my config.xml, which should be in the phonegap-project's folder:
    <?xml version='1.0' encoding='utf-8'?> <widget id="com.smartmobilestudio.phonegaptest" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:gap="http://phonegap.com/ns/1.0"> <name>PhonegapTest</name> <description> Push sample application that receives push notifications. </description> <author email="youremail@example.com" href="https://www.smartmobilestudio.com"> Smart Mobile Studio </author> <preference name="phonegap-version" value="cli-9.0.0" /> <preference name="android-minSdkVersion" value="19" /> <preference name="android-targetSdkVersion" value="28" /> <content src="index.html" /> <preference name="DisallowOverscroll" value="true" /> <preference name="StatusBarOverlaysWebView" value="false" /> <preference name="StatusBarBackgroundColor" value="#ee6e73" /> <preference name="StatusBarStyle" value="blacktranslucent" /> <plugin name="com.ourcodeworld.plugins.Filebrowser" spec="https://github.com/ourcodeworld/cordova-ourcodeworld-filebrowser.git" /> <plugin name="cordova-plugin-geolocation" /> <plugin name="cordova-plugin-device" /> <plugin name="cordova-plugin-vibration" /> <plugin name="cordova-plugin-file" /> <plugin name="cordova-plugin-statusbar" source="npm" /> <plugin name="phonegap-plugin-push" source="npm" spec="~2.2.3" /> <plugin name="cordova-plugin-console" source="npm" /> <plugin name="cordova-plugin-dialogs" source="npm" /> <plugin name="cordova-plugin-whitelist" source="npm" /> <platform name="android"> <resource-file src="google-services.json" target="app/google-services.json" /> </platform> <platform name="android"> <icon density="ldpi" src="www/res/icon/android/drawable-ldpi-icon.png" /> <icon density="mdpi" src="www/res/icon/android/drawable-mdpi-icon.png" /> <icon density="hdpi" src="www/res/icon/android/drawable-hdpi-icon.png" /> <icon density="xhdpi" src="www/res/icon/android/drawable-xhdpi-icon.png" /> <icon density="xxhdpi" src="www/res/icon/android/drawable-xxhdpi-icon.png" /> <icon density="xxxhdpi" src="www/res/icon/android/drawable-xxxhdpi-icon.png" /> <splash density="land-ldpi" src="www/res/screen/android/drawable-land-ldpi-screen.png" /> <splash density="land-mdpi" src="www/res/screen/android/drawable-land-mdpi-screen.png" /> <splash density="land-hdpi" src="www/res/screen/android/drawable-land-hdpi-screen.png" /> <splash density="land-xhdpi" src="www/res/screen/android/drawable-land-xhdpi-screen.png" /> <splash density="land-xxhdpi" src="www/res/screen/android/drawable-land-xxhdpi-screen.png" /> <splash density="land-xxxhdpi" src="www/res/screen/android/drawable-land-xxxhdpi-screen.png" /> <splash density="port-ldpi" src="www/res/screen/android/drawable-port-ldpi-screen.png" /> <splash density="port-mdpi" src="www/res/screen/android/drawable-port-mdpi-screen.png" /> <splash density="port-hdpi" src="www/res/screen/android/drawable-port-hdpi-screen.png" /> <splash density="port-xhdpi" src="www/res/screen/android/drawable-port-xhdpi-screen.png" /> <splash density="port-xxhdpi" src="www/res/screen/android/drawable-port-xxhdpi-screen.png" /> <splash density="port-xxxhdpi" src="www/res/screen/android/drawable-port-xxxhdpi-screen.png" /> </platform> <platform name="ios"> <icon height="57" platform="ios" src="www/res/icon/ios/icon.png" width="57" /> <icon height="114" platform="ios" src="www/res/icon/ios/icon@2x.png" width="114" /> <icon height="40" platform="ios" src="www/res/icon/ios/icon-40.png" width="40" /> <icon height="80" platform="ios" src="www/res/icon/ios/icon-40@2x.png" width="80" /> <icon height="50" platform="ios" src="www/res/icon/ios/icon-50.png" width="50" /> <icon height="100" platform="ios" src="www/res/icon/ios/icon-50@2x.png" width="100" /> <icon height="60" platform="ios" src="www/res/icon/ios/icon-60.png" width="60" /> <icon height="120" platform="ios" src="www/res/icon/ios/icon-60@2x.png" width="120" /> <icon height="180" platform="ios" src="www/res/icon/ios/icon-60@3x.png" width="180" /> <icon height="72" platform="ios" src="www/res/icon/ios/icon-72.png" width="72" /> <icon height="144" platform="ios" src="www/res/icon/ios/icon-72@2x.png" width="144" /> <icon height="76" platform="ios" src="www/res/icon/ios/icon-76.png" width="76" /> <icon height="152" platform="ios" src="www/res/icon/ios/icon-76@2x.png" width="152" /> <icon height="29" platform="ios" src="www/res/icon/ios/icon-small.png" width="29" /> <icon height="58" platform="ios" src="www/res/icon/ios/icon-small@2x.png" width="58" /> <icon height="87" platform="ios" src="www/res/icon/ios/icon-small@3x.png" width="87" /> <splash height="1136" platform="ios" src="www/res/screen/ios/Default-568h@2x~iphone.png" width="640" /> <splash height="1334" platform="ios" src="www/res/screen/ios/Default-667h.png" width="750" /> <splash height="2208" platform="ios" src="www/res/screen/ios/Default-736h.png" width="1242" /> <splash height="1242" platform="ios" src="www/res/screen/ios/Default-Landscape-736h.png" width="2208" /> <splash height="1536" platform="ios" src="www/res/screen/ios/Default-Landscape@2x~ipad.png" width="2048" /> <splash height="768" platform="ios" src="www/res/screen/ios/Default-Landscape~ipad.png" width="1024" /> <splash height="2048" platform="ios" src="www/res/screen/ios/Default-Portrait@2x~ipad.png" width="1536" /> <splash height="1024" platform="ios" src="www/res/screen/ios/Default-Portrait~ipad.png" width="768" /> <splash height="960" platform="ios" src="www/res/screen/ios/Default@2x~iphone.png" width="640" /> <splash height="480" platform="ios" src="www/res/screen/ios/Default~iphone.png" width="320" /> </platform> <platform name="wp8"> <icon height="99" platform="wp8" src="www/res/icon/wp8/ApplicationIcon.png" width="99" /> <icon height="159" platform="wp8" src="www/res/icon/wp8/Background.png" width="159" /> <splash height="1280" platform="wp8" src="www/res/screen/wp8/screen-portrait.jpg" width="768" /> </platform> <platform name="windows"> <icon height="150" platform="windows" src="www/res/icon/windows/Square150x150Logo.scale-100.png" width="150" /> <icon height="30" platform="windows" src="www/res/icon/windows/Square30x30Logo.scale-100.png" width="30" /> <icon height="50" platform="windows" src="www/res/icon/windows/StoreLogo.scale-100.png" width="50" /> <splash height="300" platform="windows" src="www/res/screen/windows/SplashScreen.scale-100.png" width="620" /> <icon height="120" platform="windows" src="www/res/icon/windows/StoreLogo.scale-240.png" width="120" /> <icon height="44" platform="windows" src="www/res/icon/windows/Square44x44Logo.scale-100.png" width="44" /> <icon height="106" platform="windows" src="www/res/icon/windows/Square44x44Logo.scale-240.png" width="106" /> <icon height="70" platform="windows" src="www/res/icon/windows/Square70x70Logo.scale-100.png" width="70" /> <icon height="71" platform="windows" src="www/res/icon/windows/Square71x71Logo.scale-100.png" width="71" /> <icon height="170" platform="windows" src="www/res/icon/windows/Square71x71Logo.scale-240.png" width="170" /> <icon height="360" platform="windows" src="www/res/icon/windows/Square150x150Logo.scale-240.png" width="360" /> <icon height="310" platform="windows" src="www/res/icon/windows/Square310x310Logo.scale-100.png" width="310" /> <icon height="150" platform="windows" src="www/res/icon/windows/Wide310x150Logo.scale-100.png" width="310" /> <icon height="360" platform="windows" src="www/res/icon/windows/Wide310x150Logo.scale-240.png" width="744" /> <splash height="1920" platform="windows" src="www/res/screen/windows/SplashScreenPhone.scale-240.png" width="1152" /> </platform> <access origin="*" /> <allow-intent href="http://*/*" /> <allow-intent href="https://*/*" /> <allow-intent href="tel:*" /> <allow-intent href="sms:*" /> <allow-intent href="mailto:*" /> <allow-intent href="geo:*" /> <platform name="android"> <allow-intent href="market:*" /> </platform> <platform name="ios"> <allow-intent href="itms:*" /> <allow-intent href="itms-apps:*" /> </platform> </widget> You also have to register your app with FCM using the advice in the link that @lynkfsjust provided. Then download the google-services.json file and save it in the folder where your config.xml is.
    Lastly, when you are installing the apk on your phone, you may need to disable Play Protect in the Play Store.
    Here's the source code I'm currently using for testing. Note, that the parts that use the file dialogs is not pretty at all. You can simply ignore those parts and the FileBrowser-plugin when you make your own implementation.
    unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, System.json, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Button, SmartCL.Controls.Label, SmartCL.Controls.EditBox, PhoneGapApi, SmartCL.Controls.Memo, SmartCL.Controls.Image, System.Device.Storage, SmartCL.Device.Storage, System.IOUtils, System.Streams, System.Writer, SmartCL.Dialogs, SmartCL.Geolocation; type TForm1 = class(TW3Form) private {$I 'Form1:intf'} procedure AddToLog(const Value: String); procedure FileDialogError(data: Variant); procedure InitPushNotifications(Sender: TObject); procedure PhoneGapReady; procedure PushInitialized(data: Variant); procedure PushReceived(data: Variant); procedure PushError(data: Variant); procedure OpenSaveDialog(Sender: TObject); procedure SavePushToken(NewFileName: String); protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; end; implementation uses PGStripe; { TForm1 } procedure TForm1.AddToLog(const Value: String); begin LogMemo.Add(Value+#13#10); end; procedure TForm1.InitPushNotifications(Sender: TObject); begin var Id := SenderId.Text; PhoneGap.InitPushNotifications(Id, @PushInitialized, @PushReceived, @PushError); end; procedure TForm1.InitializeForm; begin inherited; AddToLog('Waiting for deviceready in PhoneGap'); SenderId.Enabled := False; InitPush.Enabled := False; SaveToken.Enabled := False; PhoneGap.OnReady(PhoneGapReady); end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} end; procedure TForm1.PhoneGapReady; begin LogMemo.Clear; AddToLog('uuid: '+PhoneGap.Device.uuid); AddToLog('version: '+PhoneGap.Device.version); AddToLog('model: '+PhoneGap.Device.model); AddToLog('manufacturer: '+PhoneGap.Device.manufacturer); AddToLog('platform: '+PhoneGap.Device.platform); AddToLog('cordova: '+PhoneGap.Device.cordova); AddToLog('serial: '+PhoneGap.Device.serial); //Where are we? var GL:=TW3GeoLocation.Create; GL.OnPosition:=procedure (Sender: TObject; Position: TW3Position) begin AddToLog(''); AddToLog('Got position!'); AddToLog(' Time: '+FormatDateTime('dd.mm.yyyy hh:nn:ss.zzz',Position.Timestamp)); AddToLog(' Lat: '+Variant(Position.Coords.Latitude)); AddToLog(' Lng: '+Variant(Position.Coords.Longitude)); AddToLog(' Alt: '+Variant(Position.Coords.Altitude)); AddToLog(' Acc: '+Variant(Position.Coords.Accuracy)); AddToLog(' AltAcc: '+Variant(Position.Coords.AltitudeAccuracy)); AddToLog(' Head: '+Variant(Position.Coords.Heading)); AddToLog(' Speed: '+Variant(Position.Coords.Speed)); AddToLog(''); end; GL.OnPositionError:=procedure (Sender: TObject; Msg: TW3PositionError) begin AddToLog('Got an error'); AddToLog('Message: '+Msg.ErrorMessage); end; GL.QueryCurrentPosition; SenderId.Enabled := True; InitPush.Enabled := True; InitPush.OnClick := @InitPushNotifications; PhoneGap.Notification.vibrate(1000); end; procedure TForm1.PushInitialized(data: Variant); begin AddToLog('Push init ok'); AddToLog('Token: '+PhoneGap.RegistrationId); SenderId.Enabled := False; InitPush.Enabled := False; SaveToken.Enabled := True; SaveToken.OnClick := @OpenSaveDialog; end; procedure TForm1.PushReceived(data: Variant); begin AddToLog('Push: '+data.message); end; procedure TForm1.PushError(data: Variant); begin AddToLog('Push error: '+data.message); end; procedure TForm1.OpenSaveDialog(Sender: TObject); // Uses https://github.com/ourcodeworld/cordova-ourcodeworld-filebrowser procedure DoSave(data: Variant); begin SavePushToken(data); end; begin asm window.OurCodeWorld.Filebrowser.createFileDialog({ success: function(data) { @DoSave(data); }, error: function(data) { @FileDialogError(data); } }); end; end; procedure TForm1.FileDialogError(data: Variant); begin AddToLog("Error calling createFileDialog: "+data.message); end; procedure TForm1.SavePushToken(NewFileName: String); var NewFilePath: String; procedure fail(err: Variant); begin AddToLog('Error saving push notification token'); end; procedure FileWriterCreated(FileWriter: Variant); begin var TokenToWrite := PhoneGap.RegistrationId; asm FileWriter.onerror = @fail; FileWriter.write(@TokenToWrite); end; end; procedure FileCreated(fileEntry: Variant); begin AddToLog('File created'); asm fileEntry.createWriter(@FileWriterCreated); end; end; procedure onDirectorySuccess(dirEntry: Variant); begin AddToLog('Got directory access'); asm dirEntry.getFile(NewFileName, {create: true, exclusive: false}, @FileCreated, @fail); end; end; begin AddToLog('Saving push notification token'); //Split NewFileName to path and file for var a:=Length(NewFileName) downto 1 do if NewFileName[a]='/' then begin NewFilePath := copy(NewFileName,1,a); NewFileName := copy(NewFileName,a+1,Length(NewFileName)); break; end; asm window.resolveLocalFileSystemURL(@NewFilePath, @onDirectorySuccess); end; end; procedure TForm1.Resize; begin var h:=ClientHeight; if h=0 then exit; var w:=ClientWidth; SaveToken.SetBounds(0,h-40,w,40); dec(h,40); InitPush.SetBounds(0,h-40,w,40); dec(h,40); SenderId.SetBounds(0,h-32,w,32); dec(h,32); LogMemo.SetBounds(0,0,w,h); inherited; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.  
  22. Like
    lynkfs got a reaction from jarto in Creating a mobile app for Android and iOS   
    This link is from feb 2019 (nasa related), so may work : https://www.jakenherman.com/articles/2019-02/push-notifi-cordova-firebase
    (there are more links as a result of googling 'cordova push notifications plugin 2019', which may be of relevance)
  23. Like
    lynkfs got a reaction from jorn in Learning Smart Mobile Studio   
    Look at Primož Gabrijelčič's book on SMS, available on https://leanpub.com/asmartbook
    Very valuable and I still consult it on a regular basis
    There is some info on http://www.pp4s.co.uk/main/smart-gettingstarted.html as well
  24. Like
    lynkfs got a reaction from jarto in bluetooth   
    For this app I used the HOP-H58 bluetooth printer. It comes with bluetooth and usb connectivity, and even a socket for a cash drawer.
    At an unbeatable price of $26 USD delivered (!), this gives me a mobile POS (invoice print) solution, where only the printer and a mobile phone is needed. 
    Amazing.
  25. Like
    lynkfs got a reaction from jarto in bluetooth   
    How to connect webapp to a bluetooth enabled printer.
    Basically there are 2 avenues to do that : a) going native and b) the web bluetooth api
    Going native
    This involves using PhoneGap or Cordova, plus installing a plugin to make the bluetooth capacities of the mobile target platform available in the js world.
    I found this plugin on github, a cordova plugin, so used cordova rather than phonegap.
    The install sequence for a clean install of everything looks something like
    see https://cordova.apache.org/docs/en/latest/guide/cli/ install node install npm install cordova : npm install -g cordova install pre-requisites : see https://cordova.apache.org/docs/en/latest/guide/platforms/android/index.html#requirements-and-support java development kit (jdk) gradle android studio / android sdk set environment and path variables JAVA_HOME ANDROID_HOME PATH create app structure create app : cordova create bluetoothprinter com.smartmobilestudio.bluetoothprinter BluetoothPrinter install plugin cordova plugin add https://github.com/srehanuddin/Cordova-Plugin-Bluetooth-Printer.git create remainder of app add platform : cordova platform add android create SMS project copy SMS project output into the cordova app structure (www directory) build app : cordova build android install apk file on android Amazingly this all works out.
    Some gotcha's :
    - environment and path variables don't allow spaces, so for instance .../Program Files/... has to be shortened to .../Progra~1/...
    - cordova now requires plugins to have their own package.json file. The selected plugin doesn't have that, so I had to download the git repository, create a package.json file and install the plugin from local (cordova plugin add cordova-plugin-bluetooth-printer --save --searchpath ../plugin)
    - the SMS project needs these entries in its template index file
      <script type="text/javascript" charset="utf-8" src="cordova.js"></script>   <script type="text/javascript" charset="utf-8" src="BluetoothPrinter.js"></script> and compile may end up in errors. However the cordova build process takes care of that and produces a viable apk file.
    It is a bit of a process to go through, but works out well in the end.
     
×
×
  • Create New...