Home > JavaScript, Object Pascal, Smart Mobile Studio > Sneak peek at the new Smart RTL

Sneak peek at the new Smart RTL

November 4, 2016 Leave a comment Go to comments

There are many new and powerful features in the upcoming release of Smart Mobile Studio. And one of the smallest, yet most powerful and useful features – is the ability to be notified when a value or property of an object is altered.

So far, TVariant has just been a helper class for dealing with typical tasks. Well, perhaps not just typical because under Smart Pascal, variants can map directly to a javascript object (of any type) and allows you direct access to its prototype.

In the next update TVariant has a few new members, most notably: watch() and unwatch().

As their name imply they allow you to be notified whenever a single property, or indeed – a property representing a child object, is altered. It may sound trivial but it’s actually one of those features that lays the foundation for responsive, data-aware controls. But you can use it for many other things as well, like keeping tab’s on async calls that alters some value. Wouldnt it be cool to be notified when a picture has finished loading? Well, TW3Image has events for that, but being able to achieve the same results with different means is cool. Well, now you can!

Here is how you can use it:

  // create an empty javascript object
  var MyValue: Variant;
  MyValue := TVariant.CreateObject();
  MyValue.NewProperty := 12; // add a property and set a value

  // We want to know when the object changes
  TVariant.Watch(MyValue, "NewProperty", procedure ()
    begin
      showmessage("You changed the value!");
    end);

  // Wait 2 seconds and then change the value
  TW3Dispatch.Execute( procedure ()
    begin
      MyValue.NewProperty := 24;
    end, 2000);

The above code produces, as expected, a message dialog with the text “You changed the value!” 2 seconds after. So the moment “MyValue.NewProperty := 24” is called, the underlying mechanism picks that up and informs you of it through an anonymous call. Removing a watch is equally simple:

  // Remove the watchdog callback from the javascript prototype
  TVariant.UnWatch(MyValue, "Newproperty");

As of writing only Firefox supports this natively: but dont worry! We use the now universal and standard polyfill written by Eli Grey. So this polyfill is compiled into your application regardless. Its essentially now a standard part of the RTL. We have tested it on iOS, Android, Chrome, Internet Explorer, Spartan and even node.js and it performs brilliantly with little or no penalty with regards to speed. The polyfill simply injects itself as a new getter/setter and keeps track of the old, that’s how it can intercept values without causing problems with the intrinsic mechanisms in the JSVM. For those interested, here is the polyfill:

/*
 * object.watch polyfill
 *
 * 2012-04-03
 *
 * By Eli Grey, http://eligrey.com
 * Public Domain.
 * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
 */

// object.watch
if (!Object.prototype.watch) {
	Object.defineProperty(Object.prototype, "watch", {
		  enumerable: false
		, configurable: true
		, writable: false
		, value: function (prop, handler) {
			var
			  oldval = this[prop]
			, newval = oldval
			, getter = function () {
				return newval;
			}
			, setter = function (val) {
				oldval = newval;
				return newval = handler.call(this, prop, oldval, val);
			}
			;
			
			if (delete this[prop]) { // can't watch constants
				Object.defineProperty(this, prop, {
					  get: getter
					, set: setter
					, enumerable: true
					, configurable: true
				});
			}
		}
	});
}

// object.unwatch
if (!Object.prototype.unwatch) {
	Object.defineProperty(Object.prototype, "unwatch", {
		  enumerable: false
		, configurable: true
		, writable: false
		, value: function (prop) {
			var val = this[prop];
			delete this[prop]; // remove accessors
			this[prop] = val;
		}
	});
}

TW3Dispatch

Did you notice that I used TW3Dispatch.Execute() rather than the old w3_callback() function? Indeed. Part of the cleanup we are doing is not just about new and fancy code. Its also about removing duplicate code (which we had a lot of) and to organize as much as possible into classes or namespaces.

TW3Dispatch is a class that deals with delayed execution of code. It is stored in the new System.Time.pas unit and is marked as a partial class. Here is what the basis looks like under System.Time.pas:

  TW3DispatchHandle = THandle;

  (* This class is a thin wrapper around the essential JavaScript callback
     and time-scheduling methods (for delayed or repeated execution of code).
     It is isolated here to protect the user against changes in future revisions
     of javascript, and also different environments that has not yet
     reached the market. This gives us space to adapt to changes without
     affecting your codebase *)
  TW3Dispatch = partial class
    class function Execute(const EntryPoint:TProcedureRef;
          const WaitForInMs: integer): TW3DispatchHandle;overload;

    class procedure CancelExecute(const Handle: TW3DispatchHandle);

    class procedure RepeatExecute(const Entrypoint: TProcedureRef;
            const RepeatCount: integer;
            const IntervalInMs: integer);

    class function SetTimeOut(const Entrypoint: TProcedureRef;
      const WaitForInMs: integer): TW3DispatchHandle;

    class procedure ClearTimeOut(const Handle: TW3DispatchHandle);

    class function SetInterval(const Entrypoint: TProcedureRef;
      const IntervalDelayInMS: integer): TW3DispatchHandle;

    class procedure ClearInterval(const Handle: TW3DispatchHandle);

    class function JsNow: JDate;

    class function Ticks: integer;
    class function TicksOf(const Present: TDateTime): integer;
    class function TicksBetween(const Past, Future: TDateTime): integer;
  end;

The reason the class is marked as partial, is because depending on the framework you are using (SmartNJ for node.js, or SmartCL for visual applications), TW3Dispatch is expanded accordingly. If we move over to the SmartCL RTL folder and look at the file SmartCL.System.pas (yes, the same units exist in the different namespaces), TW3Dispatch gains the following, DOM related functionality:

  (* TW3Dispatch is defined in the System.Time unit.
     We complete the partial class here with DOM spesific functionality *)
  TW3Dispatch = partial class
  public
    class function Ready:Boolean;
    class procedure ExecuteDocumentReady(const OnReady:TProcedureRef);
    class function RequestAnimationFrame(const Entrypoint: TProcedureRef): TW3DispatchHandle;virtual;
    class procedure CancelRequestAnimationFrame(const Reference: TW3DispatchHandle);virtual;
  end;

The Ready() function is a handy one, it tells you if the browser has finished loading associated scripts and resources and that the document object model is, well, ready to be used. But while that is handy (especially in Form.Initialization() and similar spots that can be called while the DOM is not yet finished), ExecuteDocumentReady() makes it almost fun to write controls and get the startup of your application synchronized.

As you probably know JavaScript is ASYNC by nature. This means that any attempt to force it to behave like traditional object pascal is a gamble at best. Earlier you had to make due with a timer callback to give you some delay before performing a graphics, but now you can use ExecuteDocumentReady() to make sure everything is in order before you execute that special piece of code. It takes a single anonymous procedure, like this:

  TW3Dispatch.ExecuteDocumentReady( procedure ()
    begin
      showmessage("Its now safe to modify the DOM!");
    end);

Now the amount of changes and alterations to the RTL is in the hundreds, so explaining them all here is beyond the scope of this post. But here are a few more classes that makes your life easier:

  TW3MouseCursor = static class
  public
    class function  CursorByName(const CursorName: string): TCursor;
    class function  NameByCursor(const Cursor: TCursor): String;
    class function  GetCursorFromElement(const Handle: TControlHandle): TCursor;
    class procedure SetCursorForElement
      (const Handle: TControlHandle; const Cursor: TCursor);
  end;

  TW3DOMDeviceCapabilities = class(TW3CustomDeviceCapabilities)
  protected
    function GetMouseSupport: boolean;override;
    function GetTouchSupport: boolean;override;
    function GetGamePadSupport: boolean;override;
    function GetKeyboardSupported: boolean;override;
    function GetDevicePixelRatio: float;virtual;
    function GetDisplayPixelsPerInch: TPixelsPerInch;virtual;
  public
    property  DevicePixelRatio: float read GetDevicePixelRatio;
    property  DisplayPixelsPerInch: TPixelsPerInch read GetDisplayPixelsPerInch;
  end;

  TW3TweenEngine = class
  private
    FTimer:    TW3Timer;
    FValues:   Array of TW3TweenElement;
    FActive:   boolean;
    FPartial:  boolean;
    FInterval: integer;
    FLookup:   TW3ObjDictionary;
    procedure  SetInterval(const Value: integer);
  protected
    procedure HandleSyncUpdate;virtual;
    procedure HandleUpdateTimer(Sender: TObject);virtual;
    function  Update(const Item: TW3TweenElement):double;virtual;
  protected
    property  Dictionary:TW3ObjDictionary read FLookup;
    procedure TweenStarted(const Item:TW3TweenElement);virtual;
    procedure TweenComplete(const Item:TW3TweenElement);virtual;
    procedure TweenPaused(const Item:TW3TweenElement);virtual;
    procedure TweenResumed(const Item:TW3TweenElement);virtual;
  public
    function  ObjectOf(const Id: string):TW3TweenElement;
    function  IndexOf(Id: string): integer;

    property  Active: boolean read ( FActive );
    property  Item[const index: integer]:TW3TweenElement read (FValues[index]);
    property  Tween[const Id: string]:TW3TweenElement read ObjectOf;default;
    property  Count:Integer read ( FValues.Length );
    property  Interval: integer read FInterval write SetInterval;

    property  SyncRefresh: boolean;
    property  IgnoreOscillate: boolean;

    function  Add(Id: string): TW3TweenElement;overload;
    function  Add(Id: string; const StartValue, Distance,Duration: double;
              const EaseObject: TW3TweenEase;
              const Behavior: TW3TweenBehavior): TW3TweenElement;overload;
    function  Add(const Instance: TW3TweenElement): TW3TweenElement;overload;

    procedure Delete(index: integer);overload;
    procedure Delete(Id: string);overload;
    procedure Delete(const IdList: TStrArray);overload;

    procedure Clear;overload;

    procedure Execute;overload;
    procedure Execute(const Finished: TProcedureRef);overload;
    procedure Execute(const TweenObjects: Array of TW3TweenElement);overload;

    procedure Pause(const Index: integer);overload;
    procedure Pause(const Tween: TW3TweenElement);overload;
    procedure Pause(const Objs: array of TW3TweenElement);overload;
    procedure Pause(const Ids: array of string);overload;
    procedure Pause;overload;

    procedure Resume(const index: integer);overload;
    procedure Resume(const Tween: TW3TweenElement);overload;
    procedure Resume(const Objs: array of TW3TweenElement);overload;
    procedure Resume(const Ids: array of String);overload;
    procedure Resume;overload;

    procedure Cancel;overload;

    class function TimeCode: double;

    constructor Create;virtual;
    destructor Destroy;Override;
  published
    property OnPartial:  TW3TweenPartialEvent;
    property OnUpdated:  TW3TweenUpdatedEvent;
    property OnFinished: TW3TweenFinishedEvent;
    property OnStarted:  TW3TweenStartedEvent;
  end;

  TMutationObserver = Class(TObject)
  public
    FObserving:   Boolean;
    FHandle:      THandle;
    FTarget:      THandle;
  protected
    procedure     CBMutationChange(mutationRecordsList:variant);virtual;
  public
    Property      OnDisconnect:TNotifyEvent;
    Property      OnConnect:TNotifyEvent;
    property      OnChanged:TMutationObserverEvent;
    Property      Handle:THandle read FHandle;
    Property      TargetHandle:THandle read FTarget;
    Property      Options:TMutationObserverOptions;
    Property      Observing:Boolean read FObserving;
    Procedure     Observe(targetHandle:THandle);
    Procedure     Disconnect;
    Constructor   Create;virtual;
    Destructor    Destroy;Override;
  end;

  TW3StyleSheet = Class(TObject)
  private
    FHandle:    THandle;
  protected
    function    GetSheet: THandle;
    function    GetRules: THandle;
    function    GetCount: integer;
    function    GetItem(const Index: integer): string;
  public
    Property    Sheet: THandle read GetSheet;
    Property    Handle: THandle read FHandle;

    function    Add(RuleName: string; const aRules: string): string;overload;
    Procedure   Add(const aRuleText: string);overload;

    Property    Count:Integer read GetCount;
    Property    Items[const Index: integer]: string read GetItem;

    class procedure AddClassToElement(const aElement:THandle;const aName:String);
    class procedure RemoveClassFromElement(const aElement:THandle;const aName:String);
    class function  FindClassInElement(const aElement:THandle;const aName:String):Boolean;

    Constructor Create;virtual;
    Destructor  Destroy;Override;
  End;


  TW3CustomAsset = class(TW3OwnedObject, IW3AssetAccess)
  private
    FState:       TW3AssetState;
    FCanceled:    boolean;
    FIdentifier:  TW3AssetIdentifier;
  protected
    procedure BeforeAssetIO;virtual;
    procedure AfterAssetIO;virtual;
    procedure FailedAssetIO;virtual;
    procedure ResetData;virtual;
    procedure SetState(const NewState:TW3AssetState);
    procedure SetIdentifier(const NewIdentifier:TW3AssetIdentifier);
    procedure SetData(const Value: variant);
  protected

    function  ManagerAccess: IW3AssetManagerAccess;

    procedure PerformIO(const ThisURI: String;
      OnReady: TW3AssetReadyEvent;
      OnFailed: TW3AssetIOFailureEvent);virtual;

  public
    Property  Owner: TW3AssetManager read ( TW3AssetManager(inherited Owner) );
    Property  URI: String;
    Property  Data: TW3AssetData;
    Property  State: TW3AssetState read FState;
    property  Identifier: TW3AssetIdentifier read FIdentifier;

    procedure Execute;virtual;
    procedure Cancel;virtual;

    constructor Create(const AOwner: TW3AssetManager);reintroduce;
    destructor  Destroy;override;

  published
    Property  OnBeforeIO: TW3AssetBeforeIOEvent;
    property  OnExecuting: TW3AssetIOEvent;
    property  OnAssetReady: TW3AssetReadyEvent;
    property  OnAssetFailed: TW3AssetIOFailureEvent;
  end;

  TW3AssetManager = class(TObject, IW3AssetManagerAccess)
  private
    FAssets:  array of TW3CustomAsset;
    FRemaining: integer;
    FState:     TW3AssetManagerState;
  protected
    procedure   AssetReady(const Asset:TW3CustomAsset);virtual;
    procedure   AssetFailed(const Asset:TW3CustomAsset);virtual;
    function    GetAssetById(const AssetId: TW3AssetIdentifier): TW3CustomAsset;
  public
    property    Active: boolean read ( (FRemaining>0) and (FState=msLoading) );
    property    State: TW3AssetManagerState read FState;
    Property    RemainingToLoad: Integer read FRemaining;
    property    ReadyToUse: integer read (FAssets.Count - FRemaining);
    property    Assets[const Id:string]:TW3CustomAsset read GetAssetById;
    property    Items[const AssetIndex: integer]:TW3CustomAsset read (FAssets[AssetIndex]);
    property    Count: integer read (FAssets.Count);

    function    Add(const AssetObject: TW3CustomAsset): TW3CustomAsset;

    procedure   LoadAllAssets;
    procedure   Cancel;
    procedure   Reset;
    procedure   Clear;

    destructor  Destroy;override;
  end;

Then we have codecs for dealing with ciphers and conversion in a unified way, and much, much more. But you probably want to see the new node.js high level classes right? Well here is little taste

  TW3NodeFileSystem = class(TW3CustomFileSystem)
  public
    function Examine(const FullPath: String;var Files: TStrArray):boolean; override;
    function Rename(const OldPath, NewPath: String): boolean;override;
    function FileExists(const Filename: string): boolean;override;
    function DirectoryExists(const FullPath: string): boolean;override;
    function QueryFilePermissions(const FullPath: string;var Permissions:TW3FilePermissionMask): boolean;override;
    function CreateDirectory(const FullPath: string; Permissions: TW3FilePermissionMask): boolean;override;
    function DeleteDirectory(const FullPath: string): boolean;override;

    function QueryFilesize(const Filename: string;var Size:integer):Boolean;override;
    procedure LoadBinaryFile(const Filename: string; var Binary: TBinaryData);override;
    procedure LoadTextFile(const Filename: string; var Text: string);override;
    procedure LoadStreamFile(const Filename: string; var Stream: TStream);override;
    procedure SaveBinaryFile(const Filename: string; const Binary: TBinaryData);override;
    procedure SaveTextFile(const Filename: string; const Text: string);override;
    procedure SaveStreamFile(const Filename: string; const Stream: TStream);override;

    procedure ExamineA(const FullPath: String; const CallBack: TW3FileSystemExamineHandler);
    procedure RenameA(const OldPath, NewPath: String; const CallBack: TW3FileSystemErrorHandler);
    procedure FileExistsA(const Filename: string; const Callback: TW3FileSystemExistsHandler);
    procedure DirectoryExistsA(const FullPath: string; const Callback: TW3FileSystemExistsHandler);
    procedure CreateDirectoryA(const FullPath: string; Permissions: TW3FilePermissionMask; const CallBack: TW3FileSystemErrorHandler);
    procedure DeleteDirectoryA(const FullPath: string; const CallBack: TW3FileSystemErrorHandler);
  end;

  TNJServerAfterStartedEvent = procedure (sender: TObject);
  TNJServerAfterStoppedEvent = procedure (sender: TObject);
  TNJServerBeforeStartEvent = procedure (sender: TObject);
  TNJServerBeforeStopEvent = procedure (sender: TObject);

  TNJHandleBasedClass = class(TObject)
  private
    FHandle:  THandle;
  protected
    procedure SetHandle(const NewHandle: THandle); virtual;
    function  GetHandle: THandle; virtual;
  public
    property Handle: THandle read GetHandle;
  end;

  TNJServerChildClass = class(TNJHandleBasedClass)
  private
    FParent: TNJCustomServer;
  public
    property Server: TNJCustomServer read FParent;
    constructor Create(Server: TNJCustomServer); virtual;
  end;

  TNJCustomServerRequest = class(TNJServerChildClass)
  end;

  TNJCustomServerResponse = class(TNJServerChildClass)
  end;

  TNJCustomServer = class(TObject)
  private
    FHandle:  THandle;
    FActive:  boolean;
    FPort:    integer;
  protected
    procedure BeforeStart;virtual;
    procedure AfterStart;virtual;
    procedure BeforeStop;virtual;
    procedure AfterStop;virtual;
  protected
    procedure StartServer;virtual;
    procedure StopServer;virtual;
    function  GetActive: boolean;virtual;
    procedure SetActive(const Value: boolean);virtual;
    function  GetHandle: THandle; virtual;
    procedure SetHandle(const Value: THandle); virtual;
    function  GetPort: integer;virtual;
    procedure SetPort(const Value: integer);virtual;
  public
    property  OnAfterServerStarted: TNJServerAfterStartedEvent;
    property  OnAfterServerStopped: TNJServerAfterStoppedEvent;
    property  OnBeforeServerStarted: TNJServerBeforeStartEvent;
    property  OnBeforeServerStopped: TNJServerBeforeStopEvent;

    property  Active: boolean read GetActive write SetActive;
    property  Port: integer read GetPort write SetPort;

    procedure Start;virtual;
    procedure Stop;virtual;
  end;

  // Base exception for HTTP server(s)
  ENJHttpServerError = class(ENJServerError);

  // Http request-object
  TNJHttpRequest = class(TNJCustomServerRequest)
  private
    FEncoding:  string;
  protected
    function    GetHeaders: TJsonObject;virtual;
    function    GetTrailers: TJsonObject;virtual;

    procedure   SetEncoding(const Value: string); virtual;
    function    GetEncoding: string; virtual;
  public
    property    Socket: JNodeSocket read ( JServerRequest(Handle).connection );
    property    Encoding: string read GetEncoding write SetEncoding;
    property    &Method: string read ( Handle.&method );
    property    Url: string read ( Handle.url );
    property    Headers: TJsonObject read GetHeaders;
    property    Trailers: TJsonObject read GetTrailers;
    property    HttpVersion: string read ( Handle. httpVersion );

    procedure   Pause; virtual;
    procedure   Resume; virtual;

    constructor Create(const Server: TNJCustomServer;
                const RequestObject: JServerRequest); reintroduce; virtual;
  end;

  // Http response-object
  TNJHttpResponse = class(TNJCustomServerRequest)
  protected
    function  GetSendDate: boolean; virtual;
    procedure SetSendDate(const Value: boolean);virtual;
  public
    property  StatusCode: integer read (Handle.statusCode) write (Handle.statusCode);
    property  SendDate: boolean read GetSendDate write SetSendDate;

    function  GetHeader(const Name: string): string;
    procedure SetHeader(const Name: string; const Value: string);
    procedure RemoveHeader(const Name: string);

    function  Write(const Text: string): boolean;overload;
    function  Write(const Text: string; const Encoding: string): boolean;overload;
    procedure Write(const Stream: TStream); overload;
    procedure Write(const Buffer: TBinaryData); overload;

    procedure &End(const Buffer: TBinaryData); overload;
    procedure &End(const Data: variant); overload;
    procedure &End(const Data: variant; const Encoding: string); overload;
    procedure &End(const Stream: TStream);overload;
    procedure &End(const Stream: TStream; const Encoding: string); overload;

    constructor Create(const Server: TNJCustomServer;
                const ResponseObject: JServerResponse); reintroduce; virtual;
  end;

  TNJHTTPServerRequestEvent = procedure (Sender: TObject;
    const Request: TNJHttpRequest;
    const Response: TNJHttpResponse);

  TNJHTTPServer = class(TNJCustomServer)
  private
    procedure InternalSetActive(const Value: boolean);
  protected
    procedure Dispatch(request: JServerRequest;
              response: JServerResponse); virtual;
  protected
    procedure SetActive(const Value: boolean);override;
    procedure StartServer;override;
    procedure StopServer;override;
  public
    property  Instance: JServer read ( JServer(GetHandle));
  published
    property  OnRequest: TNJHTTPServerRequestEvent;
  end;

  TNJWebSocketSocket = class(TObject)
  private
    FSocket:  JWsSocket;
    FServer:  TNJWebSocketServer;
  protected
    function  GetReadyState: JWsReadyState; virtual;
    function  GetSocketName: string; virtual;
    function  GetSocketRequest: TNJHttpRequest; virtual;
  public
    property  ReadyState: JWsReadyState read GetReadyState;
    property  Name: string read GetSocketName;
    property  Server: TNJWebSocketServer read FServer;
    property  Request: TNJHttpRequest read GetSocketRequest;

    property  OnSocketClose: TNJWebSocketSocketNotifyEvent;
    property  OnSocketOpen: TNJWebSocketSocketNotifyEvent;
    property  OnMessage: TNJWebSocketSocketMessageEvent;

    procedure Emit(EventName: string; Data: variant; Attempts: integer);

    constructor Create(const Server: TNJWebSocketServer;
                const WsSocket: JWsSocket); virtual;
    destructor Destroy;override;
  end;

  TNJWebSocketServerTextMessageEvent = procedure (const Sender: TNJWebSocketServer; const Info: TNJWebsocketMessageInfo);
  TNJWebSocketServerBinaryMessageEvent = procedure (const Sender: TNJWebSocketServer; const Info: TNJWebsocketMessageInfo);
  TNJWebSocketServerClientConnected = procedure (const Sender: TNJWebSocketServer; const Socket: JWsSocket);
  TNJWebSocketServerClientDisconnected = procedure (const Sender: TNJWebSocketServer; const Socket: JWsSocket);

  TNJWebSocketOnHandler = procedure (const Socket: JWsSocket; const Data: variant);

  TNJWebSocketServer = class(TNJCustomServer)
  private
    FOnConnected: TNJWebSocketServerClientConnected;
    FOnDisconnected: TNJWebSocketServerClientDisconnected;
    FOnTextMessage: TNJWebSocketServerTextMessageEvent;
    FOnBinMessage: TNJWebSocketServerBinaryMessageEvent;
    FTrack: boolean;
    FPath: string;
    procedure InternalSetActive(const Value: boolean);
  protected
    procedure SetPath(URLPath: string); virtual;
    procedure SetTracking(Value: boolean); virtual;
  protected
    procedure Dispatch(const Data: TNJWebsocketMessageInfo); virtual;
    procedure SetActive(const Value: boolean);override;
    procedure StartServer;override;
    procedure StopServer;override;
  public
    property  Path: string read FPath write SetPath;
    property  ClientTracking: boolean read FTrack write SetTracking;
    procedure Emit(EventName: string; Data: variant; Attempts: integer);
    procedure On(EventName: string; CallBack: TNJWebSocketOnHandler);
  published
    property OnClientConnected: TNJWebSocketServerClientConnected read FOnConnected write FOnConnected;
    property OnClientDisconnected: TNJWebSocketServerClientDisconnected read FOnDisconnected write FOnDisconnected;
    property OnTextMessage: TNJWebSocketServerTextMessageEvent read FOnTextMessage write FOnTextMessage;
    property OnBinMessage: TNJWebSocketServerBinaryMessageEvent read FOnBinMessage write FOnBinMessage;
  end;

I could do this all day. So yeah, there is going to be a few changes. A lot of changes. And you will be amazed at how much cool stuff you can do! While I love all the cool new stuff thats going into SmartCL (the visual application types) I must admit that working with node.js and IOT is what really gives me a kick these days. When it takes less than 1 minute to write a server that can scale, execute in fork or cluster mode and daisy-chained – all of it while making use of the latest technology and API’s — then you have some firepower to play with.

So stay tuned for more, much more 🙂

Advertisements
  1. November 5, 2016 at 9:55 am

    There’s a component “TIxObjectInspector” (raize.com/DevTools/Inspex/Features.asp) which allows you can scan one or more objects and automatically populate the control’s Items collection from the properties of the inspected objects. It would be super awesome if we could inspect all published properties of objects and other data types directly in SMS the object inspector.

    • November 5, 2016 at 4:29 pm

      The challenge is that the objects we are to inspect, are not delphi. Enumerating properties that stem from a delphi object in delphi is super easy. We have to inspect javascript objects in another process, which is not running at the time, and present that through an inspector control. So its a lot more complex than you think. In many ways it would be easier to code both the editor and inspector in javascript itself, since then we would have full access to the RTTI rather than having to transport the data between the process and the ide

  1. No trackbacks yet.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: