Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by lynkfs

  1. The 'Open Recent' project list is now properly updated too 👍
  2. lynkfs

    css styling

    Browsers are certainly talented as multi-language processors. They have to understand html, css, javascript, svg and lately web-assembly as a fifth language. XML and Json are format specifiers, but could be borderline categorised as languages as well. Smart elevates javascript to great heights, but sort of leaves the other ones at the periphery. Especially css. Css can be used for a) component styling including colours, fonts etc, b) visual layout of pages/forms and reacting responsively to screen size and orientation and c) interactivity in various forms. Some of this overlaps with the js domain. Smart basically uses css for a) but not for b) or c). Looking at various css frameworks out there, I would categorise these as on one end of the scale there are many UI kit frameworks where the emphasis is on components - buttons, accordions, cards etc. Sometimes as css stand-alone files, sometimes in conjunction with js. Most of these also have functionality for layout purposes, usually based on flexbox or internal grid structures. on the other end of the scale there are frameworks which forego the component focus, but instead provide a large number of elementary styling functions, which can be applied to any component. and many which occupy a space in between This post is about the open source Tailwind framework, belonging in the second category above, just to see how that would work in the Smart environment. Tailwind comes in 2 flavours : a static framework which can be accessed locally or from a cdn, and a more dynamic version. The latter can be tailored dynamically for any specific project and basically needs to be part of the build toolchain. To keep it simple I used the static version. Change the res/app.css reference to the tailwind css file (either just replace it or change the reference in the index.html file). Now any component can be made to use this styling. Because this is not more (or less) than a list of individual styling functions, the use boils down to defining what you want. Tell a story as in "give me a button with a green background, which turns to blue on hover, has rounded corners and white text and a shadow effect which makes it seem floating". In code : W3Button1.handle.className := "bg-green-500 hover:bg-blue-700 text-white rounded shadow-2xl"; The second button in the demo project (see below) has a different story (className sequence), which gives it a totally different look and feel. I kind of like this approach, it works very well with any component, thus also with the built in Smart components. It is a bit of a drag specifying these className sequences by hand, so it would be nice if this would be supported in the IDE. There are after all only a small number of categories of these elementary styling bits : backgrounds, typography, borders, spacing, sizing, svg, interactivity and effects. And a relatively limited list of functions per category (bg-green-500 means a green background, 500 is the fifth shade in the green colour scheme. So there will be a bg-green-600, a bg-blue-200 etc). Doable I think. Apart from these styling functions, Tailwind also has a number of layout functions. Smart has its own layout mechanism, however the Tailwind functions can be used as well. See the 'card' component in the demo. Demo and source
  3. lynkfs

    css styling

    Various ways to do this. Probably easiest is to download the latest tailwind css file from https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css and store it in the lib folder and use the $R directive initialization Forms.RegisterForm({$I %FILE%}, TForm1); {$R 'tailwind.css'} This will automatically add a line like this to your index template file : <link rel="stylesheet" href="lib/tailwind.css"> Alternatively, either add a custom template in the Project Manager and add the css file link manually to it, or load the css file in code Apart from any $R directive, projectsource is the same as the official demo with the 5 one-liners added
  4. lynkfs

    css styling

    One of the plusses of the (opensource) tailwind css library is that it does not have any pre-made components like buttons etc. Instead, any element can be styled using one or more of it's css 'functions'. So this also works for any of the standard SMS components. As an example, see here the new login demo (Featured Demos / Forms & Components / LoginDialog), but styled a bit differently Demo (see hover and focus effects) Usage is quite intuitive : - Give the LoginForm a white background and rounded corners - Display the 'Please login' label in dark grey using a small bold font - Render the user name editbox with a shadow, rounded corners, dark grey text, a 3px lead and a shadow outline on hover and on focus etc in code, just 5 one-liners : procedure TLoginForm.InitializeForm; begin ... self.handle.className := "bg-white rounded"; //dialog W3Label1.handle.className := "block text-gray-700 text-sm font-bold"; //title EditUserName.handle.className := "shadow appearance-none border rounded w-full px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline hover:outline-none hover:shadow-outline"; EditPassWord.handle.className := "shadow appearance-none border border-red-500 rounded w-full px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"; LogIn.handle.className := "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded focus:outline-none focus:shadow-outline"; //button end; ( instead of manipulating the elements className directly by it's handle, use the rtl's built in TagStyle functions : self.TagStyle.Clear; self.TagStyle.Add("bg-white rounded");
  5. lynkfs

    TXMLDocument in FPC

    there are also some browser built-in functions to handle (xml) parsing
  6. lynkfs

    Float 16

    There are many untapped gems in the rtl. Memory processing is one of them (at least for me it is).
  7. lynkfs

    Accessing W3IFrameHTMLElement URL Clicks

    set <element>.NativeScrolling to true
  8. lynkfs


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


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

    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(; 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.
  11. lynkfs

    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
  12. 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)
  13. lynkfs

    Waiting for controls to be ready

    could the 'readyexecute' method be repurposed so that code changes to existing projects could be avoided ?
  14. lynkfs


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


    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.
  16. lynkfs

    kodi grid

    @lennart posted a reference to a scrollable grid in Delphi Developer. See post here. Since he produced this grid in Smart Pascal, it might be of interest to forum members. This grid basically takes items of irregular dimensions and determines how to best fit those items in rows/columns. The code he posted doesn't work in my (latest) version of Smart, but with a few tweaks here and there I got it going. At least for strings that is. Icons/images are not fully implemented in the source he posted, but I'll take another look. Won't be too difficult. I like the marching ants selection. See demo. kodi := TWbIconView.Create(self); kodi.setbounds(10,10,500,500); //by way of demo : just split any sentence into words and use these as variable length cell items var s : string := #'This grid basically takes items of irregular dimensions and determines how to best fit those items in rows/columns.'; var a : array of string := StrSplit(s,' '); for var i := 0 to a.length-1 do begin kodi.AddText(a[i]); end;
  17. lynkfs

    Combobox items work like "read-only" mode

    don't use the items object : procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components W3ComboBox1.add('Line 1'); W3ComboBox1.add('Line 2'); W3ComboBox1.add('Line 3'); end; procedure TForm1.W3Button1Click(Sender: TObject); begin W3ComboBox1.Clear; end;
  18. lynkfs


    edited Been a while since I had a look at Googles Tensorflow deep learning library (js version) In the meantime this library has expanded quite a bit, most notably it enables using pre-built models for a variety of tasks. This one takes any image and detects up to 90 categories of objects in the image (persons, dogs, traffic lights etc) The library correctly identifies 2 objects of type "person" in the above image (image 620*408 pixels, bbox coordinates x,y,width,height) Usage is incredibly simple : load the libraries, display an image in the browser and issue a detect command var Script := document.createElement('script'); Script.src := 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs'; console.log('init tensorflow'); document.head.appendChild(Script); Script.onload := procedure begin console.log('tensorflow loaded'); var Script := document.createElement('script'); Script.src := 'https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd'; console.log('init coco-ssd'); document.head.appendChild(Script); Script.onload := procedure begin console.log('coco-ssd loaded'); var Image1 := TW3Image.Create(Self); Image1.SetBounds(0, 0, 620, 408); Image1.src := 'puydesancy.jpg'; var img := Image1.handle; asm //quick and dirty : cocoSsd.load().then(model => { // detect objects in the image. model.detect(@img).then(predictions => { console.log('Predictions: ', predictions); }); }); end; end; end; (should have checked for image1.onload). Anyway, demo here. Works from server only due to cors constraints, otherwise results in 'tainted image' errors. edited and another random image
  19. lynkfs


    it is a bit slow timing for this example : start: 0.0849609375ms init tensorflow : 0.936767578125ms tensorflow loaded : 1017.56591796875ms init coco-ssd : 1017.623779296875ms coco-ssd loaded : 1421.75390625ms get image : 1422.751708984375ms Predictions: 45869.56982421875ms Firefox has the same sort of response times, Edge doesn't support console timers. It is basically the time to load the mobilenet model itself from the google servers, which is responsible for the large timegap between get image and the resulting predictions. GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/model.json" GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/group1-shard5of5" GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/group1-shard4of5" GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/group1-shard3of5" GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/group1-shard2of5" GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/group1-shard1of5" It should be possible to cache all of these files, or store them on a SBC
  20. lynkfs


    I recently revisited the console object, available in all browsers, and discovered some newbies I didn't know about. The methods and properties below may be sometimes useful for debugging purposes. The console object is tied to the global window object, so access either by browserapi.window.console.log('whatever'); or define a reference to the window object : var window external 'window': variant; and call like window.console.dirxml(self); Some console methods are wrapped in SmartCL.System (like writeln ea) and SmartCL.Console The console.dirxml() above gives an xml representation of any object, which is way better than the 'object Object' result of a console.log(some object) instruction For performance testing, console also offers native timers console.time() console.timeLog() console.timeEnd() The above respectively initiate, report and destroy a named timer. The maximum nr of concurrent timers is 10,000 (!) To see how this works I created a stock new visual project with a single form and no visual controls. A timer was inserted at the beginning of the generated js projectfile, at the initialization of the form and at the ApplicationStarting, InitializeForm, InitializeObject and Resize events. Results : console.timeLog top of main.js : 0.0009765625 ms Initialization 10.574951171875 ms applicationstarting 17.234130859375 ms InitializeObject 21.23095703125 ms ReSize 62.385986328125 ms ReSize 101.25 ms InitializeForm 225.994140625 ms ReSize 226.757080078125 ms Wondering about the multiple ReSize calls and the relatively large time lag between InitializeObject and InitializeForm. There is another feature built into browsers re performance : the window.performance object with its own api's The most useful methods of this object are performance.now(), which gives a high res timestamp, and performance.toJSON() which gives a json representation of the attributes of the performance object Below the 'console.timer' and 'performance.now' results of a minimum project with 1 form and no visual controls in the native framework : console.timeLog performance.now() top of main.js : 0.001953125 ms / 118.28000005334616 timestamps Initialization 2.16186523437 ms / 120.43999996967614 InitializeForm 3.61083984375 ms / 121.92000006325543 InitializeObject 4.32080078125 ms / 122.65000003390014 and some values for the attributes of the performance object itself (console.log(window.JSON.stringify(window.performance.toJSON()));) timeOrigin : 1564383720413.3972 timestamps navigationStart: 1564383720413 redirectStart: 0 redirectEnd: 0 fetchStart: 1564383720414 domainLookupStart: 1564383720418 domainLookupEnd: 1564383720418 connectStart: 1564383720418 connectEnd: 1564383720418 secureConnectionStart: 0 requestStart: 1564383720418 responseStart: 1564383720423 responseEnd: 1564383720426 unloadEventStart: 1564383720432 unloadEventEnd: 1564383720432 domLoading: 1564383720435 domInteractive: 0 domContentLoadedEventStart:0 domContentLoadedEventEnd: 0 domComplete: 0 loadEventStart: 0 loadEventEnd: 0
  21. lynkfs

    Waiting for controls to be ready

    Some interesting decisions to be made. side note : the native/shoestring rtl uses MutationObservers exclusively, which worked well for me. Interested to see what the speed difference is
  22. lynkfs

    Responsive Design

    Responsive design in essence consists of 2 measures : style all components as best as possible depending on screen size (larger buttons on mobile, proportional font sizing etc) tweak the layout of forms so that it works best on any given screen size 1) Screen size dependent styling can be done in code and/or is done as other frameworks do by including media queries in the stylesheet. Media queries basically activate different parts of a stylesheet depending on the actual screen size. Smart doesn't implement this in its shipped stylesheets, but there is no reason it can't be done. Just a bit of work. 2) Tweaking the form layout in Smart is possible by doing it in code depending on screensize and orientation - a bit of a pain really using the Layout Manager using some css grid framework or ui kit. For website development I've used the Frow framework quite a bit, but there are many others. using anchors and the CSS grid (which is basically the topic of this post) The new anchors are fantastic, but don't really help in responsive design. Browser window resizing doesn't proportionally resize anchored components. That is unless they are dimensioned depending on screen size beforehand : var W3Panel0 : TW3Panel := TW3Panel.Create(self); W3Panel0.Left := trunc(browserapi.window.innerWidth*0.05); W3Panel0.Top := trunc(browserapi.window.innerHeight*0.05); W3Panel0.Width := trunc(browserapi.window.innerWidth*0.9); W3Panel0.Height := trunc(browserapi.window.innerHeight*0.10); W3Panel0.Anchors := [akLeft, akRight, akTop]; // var W3Panel1 : TW3Panel := TW3Panel.Create(self); W3Panel1.Left := trunc(browserapi.window.innerWidth*0.05); W3Panel1.Top := trunc(browserapi.window.innerHeight*0.15); W3Panel1.Width := trunc(browserapi.window.innerWidth*0.9); W3Panel1.Height := trunc(browserapi.window.innerHeight*0.70); W3Panel1.Anchors := [akLeft, akRight, akTop, akBottom]; W3Panel1.NativeScrolling := true; // var W3Panel2 : TW3Panel := TW3Panel.Create(self); W3Panel2.Left := trunc(browserapi.window.innerWidth*0.05); W3Panel2.Top := trunc(browserapi.window.innerHeight*0.85); W3Panel2.Width := trunc(browserapi.window.innerWidth*0.9); W3Panel2.Height := trunc(browserapi.window.innerHeight*0.10); W3Panel2.Anchors := [akLeft, akRight, akBottom]; As an example the code above produces a header (always on top), a footer (always on the bottom) and a scrollable body inbetween with some proportional margins. The good thing is it works well on all screen sizes, and even on orientation changes, without having to code anything else. Instead of using a grid system with media queries, or doing it in code, it is also possible to use the built-in css grid to get a responsive design. Much easier, see article. If I just plonk the css and html of the first example in that article into the structure above, this is how it ends up looking : Demo Resize browser window to see this in effect (or see it in the responsinator) Not the end-all of all possible responsive design options, but it may fit the bill sometimes. Note : the built-in chrome browser doesn't understand the css grid, so execute from file or server.
  23. lynkfs

    Waiting for controls to be ready

    To make this work using MutationObservers I would probably do something like this: demo, project Essentially the TestPanel is put here under the surveillance of a MutationObserver, which triggers when all changes to the panel have been applied (including the original sizing). After that it is ready to accept the label etc. procedure TTestPanel.InitializeObject; begin inherited; WriteLn('InitializeObject called -> Creating Label'); self.observe; self.handle.addEventListener('readytogo', procedure(e:variant) begin writeln('heard readytogo'); var Lab:=TW3Label.Create(Self); Lab.Caption:='Can you see me?'; Lab.SetBounds(10,10,150,30); WriteLn('InitializeObject done'); end); end; The code in this demo works, but is a bit ugly. It would be better to include the observe method standard in a constructor, put it in the TControlHandle rather than the TPanel, rewrite the eventlistener similar to the standard ReadyExecute method etc. But hey, demo. As a matter of fact, using the standard ReadyExecute method instead of MutationObservers works here as well. I think the latter is a bit better though as observers don't rely on timeouts and a single observer does handle multiple changes. /* self.Handle.ReadyExecute(procedure() begin var Lab:=TW3Label.Create(Self); Lab.Caption:='Can you see me?'; Lab.SetBounds(10,10,150,30); WriteLn('InitializeObject done'); end); */ Maybe the rtl could be made to handle all child inserts in this manner
  24. lynkfs

    file system

    The original html5 spec had a file api which allowed to read and write files clientside (the File Directories and System Api). This was only fully implemented by Chrome at one point, but even then Chrome needed to be started with a switch (-allow-file-access-from-files). Today clientside storage is limited to cookies, local/session storage, indexedDB and the Cache API. While I understand the reasons for limiting file handling on the client, it is sometimes annoying. Fortunately some pretty creative solutions have emerged. One of them is Filer. This is a nodejs fs filesystem look-a-like which uses indexedDB for storage, and runs in the browser. Basically it allows for a complete filesystem clientside. Not bad at all. To continue stacking : Filer is stacked on top of indexedDB. Stack nohost on top of Filer and you have a web fileserver (based on service workers) in the browser. Amazing. Will certainly try this out.
  25. lynkfs

    file system

    Scripts need to be loaded once only. I usually do that in the Form.InitializeForm section, but you can do it anywhere (as long as the script is loaded before use) procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components var Script := browserapi.document.createElement('script'); Script.src := 'res/filer.js'; browserapi.document.head.appendChild(Script); Script.onload := procedure begin writeln('script loaded'); ... the asynchronous onload event triggers after the script has loaded Alternatively you can use the $R directive