Jump to content


Popular Content

Showing content with the highest reputation since 05/20/2019 in Posts

  1. 4 points

    menu component

    Probably not the most inspiring topic, but just sharing a menu component I needed for some project. There are quite a few css based menu components around, but I wanted to have a pure smart one. This one has an unlimited number of submenus and is instantiated as a hamburger menu on a toolbar Hamburger := TCHMenu.Create(self); //nodes : id,parent,description,procedure Hamburger.Add('root','','Hamburger menu'); //root Hamburger.Add( 'projects','root','Projects'); Hamburger.Add( 'project0','projects','New project'); Hamburger.Add( 'project1','projects','Open project'); Hamburger.Add( 'project2','projects','Delete project'); Hamburger.Add( 'designers','root','Designer'); Hamburger.Add( 'designer0','designers','Domain info',clickproc); //<== execproc Hamburger.Add( 'designer1','designers','Data development'); ... Hamburger.Add( 'designer5','designers','Goal Metrics'); //some test cases below Hamburger.Add( 'test','project0','test3'); // test level 3 Hamburger.Add( 'designer5','designers','Goal Metrics'); //test on double entries Hamburger.Add( 'test0','root','test',procedure begin writeln('clickproc'); end); //test on no children Hamburger.Add( 'designer6','designers','extra test'); //test on out of order demo and project files on the .../Menu subdir
  2. 3 points

    fetching files

    There are afaik 3 ways to read external files directly from the client : using ajax / xmlhttprequest using the filereader using the fetch api 1: get xmlhttprequest var FHttp := TW3HttpRequest.Create; FHttp.OnDataReady := lambda writeln(FHttp.responsetext); end; FHttp.open('GET','res/textfile.txt'); FHttp.send(); 2: file input W3EditBox1.InputType := itFile; W3EditBox1.Handle.ReadyExecute( procedure () begin W3EditBox1.OnChanged := procedure(sender:TObject) begin var reader: variant; asm @reader = new FileReader(); end; reader.onload := lambda writeln(reader.result); end; reader.readAsText(W3EditBox1.handle.files[0]); end; end); 3: fetch api var window external 'window': variant; window.fetch('res/textfile.txt') .then(function(response:variant):variant begin result := response.text(); end) .then(procedure(myText:variant) begin writeln(myText); end); these fetch calls return a promise and the promise returns a response The fetch api is a mechanism which can replace the usual ajax calls. This api is primarily designed to act on network events. (see this post), but works for simple file fetching as well 4: Variations of the above, see f.i. this one using webworkers var FileReader : variant := new JObject; asm @FileReader = new Worker('filereader.js'); end; FileReader.onmessage := procedure(e: variant) begin writeln(e.data); end; FileReader.postMessage('res/textfile.txt'); //read file through webworker (filereader.js) demo / project (demo output is to console : ctrl+shift+i)
  3. 2 points

    Promises revisited

    Promises are a javascript construct enabling async processes to be written in a synchronous manner (sort of). The status quo at this stage : - Promises are part of javascript since Ecmascript 6 - Promises are enabled in all major browsers - Promises work in node.js too (require('promise')) - Promises are not used in the rtl, or described in Smart documentation - There have been some posts on the forum re implementing promises (the best one : ) - The ecma.promise unit in the rtl is based on an older, incompatible, version of the current spec The subject of this post is to try and make the ecma.promise unit usable again and provide some examples A promise is a way to combine an asynchronous action, its possible outcomes and the actions associated with these outcomes in a single construct. Reading through the API documents (see bottom of this post), the basic syntax to create a promise is : var promise : variant := new JPromise(resolver) where resolver is a function with as arguments a resolve and reject function, which resolves the promise or rejects it on error. The return value of the resolver itself is ignored. (so we might as well type it as a procedure rather than a function) The above is covered by type TPromiseCallback = function(Value: Variant): Variant; JPromise = class external 'Promise' public constructor Create(resolver: procedure(resolve,reject: TPromiseCallback)); end; The most important functions of the Promise object are the "then" and the "catch" functions. Basically these are used to process actions when the promise is either resolved or rejected. So we need to add at least this to the type info type JPromise = class external 'Promise' public ... function &then(onFulFilled: TPromiseCallback): Variant; function catch(onRejected: TPromiseCallback): Variant; end; Some examples The most quoted example is an external httprequest for some data (1), wait for the request to come back and see if it was succesfull or not (2) and perform the necessary actions (3) or (4). function example1(url:string):variant; begin var q : variant := new JPromise(procedure(resolve,reject: TPromiseCallback) begin var FHttp := TW3HttpRequest.Create; FHttp.GET(url); //1 do something asynchronous FHttp.ondataready := lambda resolve(FHttp.responseText); end; //2 resolve (and pass 'responseText' on to the then function) FHttp.onerror := lambda reject(FHttp.statusText); end; reject (and pass 'statusText' on to the catch function) end) .then(function(x:variant):variant begin writeln(x); end) //3 do something when succesful .catch(function(x:variant):variant begin writeln(x); end); //4 do something when unsuccesfull result := q; end; Example1('res/textfile.txt'); Really not that different from a normal ajax call : procedure example2(url:string); begin procedure OnDataReady(x: variant); begin writeln(x) end; //3 procedure OnError(x: variant); begin writeln(x) end; //4 var FHttp := TW3HttpRequest.Create; FHttp.GET(url); //1 FHttp.ondataready := lambda OnDataReady(FHttp.responseText) end; //2 FHttp.onerror := lambda OnError(FHttp.statusText) end; end; Example2('res/textfile.txt'); The differences being here that promises allow all code in the one construct. The other main advantage is that a single promise can be accessed multiple times and at multiple code locations: Suppose we have a promise which reads an accountname from a db, and the accountname is say displayed on a window title and a report. In both places this can be simply referred to as AccountName = AccountPromise.then(*function*) without having to check if data is actually available. The promise api also provides a shortcut to create a promise in the resolved or rejected state immediately without going through its constructor : Promise.resolve('resolved'); Promise.reject('rejected'); For this we need access to the global Promise object, and have to add the next line : type ... var Promise external 'Promise': variant; A possible use is if we have a process consisting of multiple steps, and we want to use the chaining mechanism of the "then" functions of the promise object to tie these steps together : Promise.resolve('life') //life .then(function(x:variant):variant begin result := x + "'s"; //life's end) .then(function(x:variant):variant begin writeln(x + ' good'); //life's good end); The two remaining api functions are promise = Promise.all(array or set) promise = Promise.race(array or set) These functions are initially typed as JPromise = class external 'Promise' public ... function all(Iterable: variant): Variant; function race(Iterable: variant): Variant; end; Both can be used when running asynchronous tasks in parallel. Suppose there are say 3 tasks running concurrently which execute a query to Google, Bing and Yahoo The "all" function returns a promise when all 3 promises have been resolved, and the "race" function as soon as one of the promises has been resolved Promise.all(requests:array[0..2] of string) .then(function(outcome:variant):variant begin writeln('All ' + requests.length + ' query outcomes received'); end); .catch(function(error:variant):variant begin writeln('Error occurred ' + error); end); Adding it all up, the typed info as per below works perfectly fine for the type of examples listed above type TPromiseCallback = function(Value: Variant): Variant; JPromise = class external 'Promise' public constructor Create(resolver: procedure(resolve,reject: TPromiseCallback)); function &then(onFulFilled: TPromiseCallback): Variant; function catch(onRejected: TPromiseCallback): Variant; function all(Iterable: variant): Variant; function race(Iterable: variant): Variant; end; var Promise external 'Promise': variant; To complicate matters a bit, and usually unnecessarily so : The function header of the "then" function above is the most used format. "Then" is however actually a function with 2 arguments, both of which are optional. Which means that the possible formats of this function are function &then: Variant; overload; function &then(onFulFilled: TPromiseCallback): Variant; overload; function &then(onFulfilled, onRejected: TPromiseCallback): Variant; overload; Mozilla : "If one or both arguments are omitted or are provided non-functions, then they will be missing the handler(s), but will not generate any errors. If the Promise that then is called on adopts a state (fulfillment or rejection) for which then has no handler, a new Promise is created with no additional handlers, simply adopting the final state of the original Promise on which then was called". My thoughts exactly () By the way this also means that catch(onRejected) is the same as &then(undefined, onRejected); Another (usually unnessary) detail is that the static resolve function (Promise.resolve) comes in different flavours as well : Promise.resolve(promise); Returns promise (only if promise.constructor == Promise) Promise.resolve(thenable); Make a new promise from the thenable. A thenable is promise-like in as far as it has a `then() method. Promise.resolve(obj); Make a promise that fulfills to obj. in this situation. This however is handled by the reference to the global Promise object, and doesn't require any changes to the typed info Finally, Mozilla also lists a "finally" function, which is not described in other api documents API documentation http://definitelytyped.org/docs/es6-promises--es6-promises/classes/promise.html https://tc39.github.io/ecma262/#sec-promise-constructor https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise https://promisesaplus.com/ https://developers.google.com/web/fundamentals/primers/promises#promise-api-reference https://github.com/tildeio/rsvp.js Note: In principal there are 3 ways to extract and type the functions, procedures and variables from any API : - find an API declaration file and convert this programmatically into object Pascal syntax - find a reference to a global object representing the API methods (if available) - read through the API specs The first option would involve finding a relevant API declaration file (f.i. this one) and convert this into pascal syntax. The results of using ts2pas (see posts) have been not very 'promising' The second option, when using promises in a browser environment, is to simply forget about typing, but have a reference to the global window object, and reference the promise object from there : var window external 'window': variant; ... var promise := window.Promise.resolve('something'); promise.then(function(x:variant):variant begin writeln(x); end); The post above has used the last option (read the API docs) Surprisingly, it is actually somewhat difficult to find the latest/most comprehensive/most agreed upon spec. The promise api has gone through various iterations, starting from an idea to external libraries (Q, when, rsvp.js) and finally becoming part of the javascript language in Ecmascript 6. Even then there have been some changes . Please let me know if you know of relevant use cases for this API requiring other changes to the ECMA.Promise unit
  4. 2 points

    Editing "Color" as a property

    We are working on replacing the Object Inspector with a better one.
  5. 2 points

    tree walking

    Modern browsers have an enormous number of built-in functions, sometimes very handy These ones, having to do with tree handling, came up the other day : domParsing, nodeIteration and treeWalking The built in domParser function takes a tree in html/dom or xml format : <catalog> <book id="bk101"> <author>Gambardella, Matthew</author> <title>XML Developer''s Guide</title> <genre>Computer</genre> <price>44.95</price> <publish_date>2000-10-01</publish_date> <description>An in-depth look at creating applications with XML.</description> </book> </catalog> which can be used as in var oDOM: variant := new JObject; asm var oParser = new DOMParser(); @oDOM = oParser.parseFromString(@theaboveXMLstring, "application/xml"); end; writeln(oDOM.documentElement.nodeName); // root (catalog) writeln(oDOM.children[0].children[0].attributes[0].name); // id writeln(oDOM.children[0].children[0].attributes[0].value); // bk101 writeln(oDOM.children.item(0).innerHTML); To traverse these types of trees, there are also the following functions available : nodeIterator and treeWalker These functions are largely similar. The nodeIterator : var nodeIterator : variant := new JObject; nodeIterator := oDOM.createNodeIterator( oDOM.documentElement,-1); // NodeFilter.SHOW_ELEMENT : -1 or 0xFFFFFFFF var currentNode := nodeIterator.nextNode(); while currentNode <> null do begin // null = eof // get rid of whitespace text nodes if currentNode.nodeName <> '#text' then begin writeln(currentNode.nodeName); writeln(currentNode.innerHTML); end; currentNode := nodeIterator.nextNode(); end;
  6. 2 points

    Node.JS Game Loop

    Here is my Smart Mobile Studio adaptation of the Node.JS "node-gameloop" module. My primary modification was some code to make the *next* interval line up with the desired number of ms per tick. I also tightened the window for choosing SetTimeout vs SetImmediate. I needed this to provide the beating heart of Paintball Net's simulation engine. Which, sure, only beats 10 times per second, but I wanted it to be as accurate as possible. 🙂 -David unit UPBNGameGameLoop; interface uses System.Types, System.Types.Convert, System.Time, System.Streams, System.Reader, System.Writer, System.Device.Storage, SmartNJ.System, SmartNJ.Streams, SmartNJ.Device.Storage, SmartNJ.Application, NodeJS.Core; var GameLoopActive: boolean; type TGameLoopCallback = procedure(delta: float); procedure StartGameLoop(update: TGameLoopCallback; msInterval: integer); procedure StopGameLoop; implementation procedure SetImmediate(const Entrypoint: TProcedureRef); external 'setImmediate'; const Seconds2Nano = 1e9; Nano2Seconds = 1 / Seconds2Nano; MS2Nano = 1e6; Nano2MS = 1 / MS2Nano; var _loopProc: TGameLoopCallback; _loopLengthHR: float; _loopPrevHR, _loopTargetHR: float; function GetHRTime: float; var hrTime: Variant; begin // I need a more up-to-date node to use the bigint() function. // asm // @Result = process.hrtime.bigint(); // end; asm @hrTime = process.hrtime(); end; Result := (hrTime[0] * Seconds2Nano) + hrtime[1]; end; procedure DoGameLoop; var now, delta: float; remaining: integer; begin if GameLoopActive then begin now := GetHRTime; if now >= _loopTargetHR then begin delta := now - _loopPrevHR; _loopPrevHR := now; _loopTargetHR := now + _loopLengthHR; if delta > _loopLengthHR then begin _loopTargetHR -= (delta - _loopLengthHR); end; _loopProc(delta * Nano2Seconds); end; if GameLoopActive then begin remaining := Trunc((_loopTargetHR - GetHRTime) * Nano2MS); if remaining >= 4 {16} then SetTimeout(DoGameLoop, remaining) else SetImmediate(DoGameLoop); end; end; end; procedure StartGameLoop(update: TGameLoopCallback; msInterval: integer); begin if not GameLoopActive then begin GameLoopActive := True; _loopLengthHR := msInterval * MS2Nano; _loopProc := update; _loopPrevHR := GetHRTime; _loopTargetHR := _loopPrevHR; SetImmediate(DoGameLoop); end; end; procedure StopGameLoop; begin GameLoopActive := False; _loopProc := nil; end; end.
  7. 2 points

    My SMS Project: Paintball Net Revival

    I was eleven years old and had just mailed off a crinkly ten dollar bill folded inside a handwritten message explaining my love for this game. A few days would pass until there it was, my first level up. Huge fan, no game has ever come close to the caliber of paintball net. I’m — we — are much older now but the game hasn’t aged a day. Thank you David for resurrecting the legend !
  8. 1 point

    Page Loading Issues with iPhone Browser

    That seems to have fixed it. Thanks! -David
  9. 1 point
    Thank you for looking into this (and applying a fix). Below is just some additional data in case you ever encounter some version of it again: The only way I've been able to somewhat reliably trigger it happening, is by jumping back and forth between two demo projects (not sure if it matters which ones, I've been using the Archimedes and PolyShape canvas demos) - opening via Windows Explorer - and some random combination of: closing tabs with "close all others" / "close all to the right" double clicking forms/units/project files in the project tree ctrl-clicking RTL unit names from within the editor occasionally running the project ... and then exiting the IDE by closing the window, before opening the other project But I haven't been able to make it happen truly deterministically. It takes a few cycles of the above, alternating between the two projects, with no obvious trigger. I had the thought that maybe I was opening a new project before the old one finished closing entirely, since I had been going through all the demos somewhat rapidly. But attempting to open/close them as fast as possible doesn't appear any more likely to trigger it. ¯\_(ツ)_/¯
  10. 1 point

    Another GumRoad License Problem

    Many thanks for the fast turnaround - everything working fine with the new license code you sent via email. Best Regards, KultiVator
  11. 1 point

    components lose name - little IDE bug

    Thank you. I'll add this to my work list.
  12. 1 point
    Sent 3 bug reports [with subject "Bugreport (IDE crash on double clicking forms/units)"]. Let me know if you need any other information.
  13. 1 point

    Another GumRoad License Problem

    @KultiVator A license was generated and sent in the morning of May 30th. Can you please check if the license happens to have gone to your spam folder? I'll forward you the e-mail containing the license also now. Edit: I also sent you a private message here with the license key
  14. 1 point

    TIniFile Question

    omg, sorry i suppose i found the problem. ini := TIniFile.Create; ini.loadFromUrl('res/apps.ini', procedure() begin writeln(ini.ReadString('0','Url','')) end); ini.free Ini.free is executed before the value has been loaded. Sorry for the circumstances.