Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by lennart

  1. lennart


    Its not a "bug", the $ postfix / prefix means its a managed variable/field. Write a normal streamer or implement a ToJSON() mechanism. The mess of raw JS is what we are trying to get away from
  2. Sorry for the delay we are working non-stop on the next update, so its hectic! First, let me explain a bit where we are, so we can pick the right storage mechanism: The past months we have focused solely on the visual aspect of the RTL. We had to start somewhere and since the visual aspect is what most people care about first, that seemed like the best place. So as you have no doubt seen, we have gone through the RTL with a fine tooth comb, removing things that didnt work, rewriting controls from scratch and testing each part to make sure that everything works. But that is just 1/3 of the path we are on. We are now moving into the non-visual aspects of the RTL. Here we have things like databases, non-visual controls (TComponent in Delphi and Lazarus), filesystems and ultimately node.js - which by nature is UI less and runs on the server (or the shell). We have also brushed up Phonegap and removed the wrongs we found there. The Phonegap API has not stood still and have evolved, so it was due for an overhaul. So that is a quick "status". We are completing 1/3 of the planned system, which covers the visual aspects. DB typically falls under "non-visual", with bindings back to visual later. DB for browsers, what is what? Modern browsers actually have 3 DB engines built into them: WebSQL IndexDB Microsoft Access (*) (*) Only supported by Microsoft and relies on COM (so not for mobile). It must be mentioned that WebSQL, which is the only really good option here, has been deprecated. But it wont vanish tomorrow. The W3C (world wide web consortium) is a bit like the vatican; they move in decades and code will linger in the codebase for ages before it changes. So I wouldnt worry too much about using WebSQL. Secondly, there are shim's for everything these days. A "shim" is a JS re-implementation of something. What a shim does is check if a feature is supported, and if it's not then the shim installs itself and delivers the missing functionality. In other words, you can safely use websql and just download a shim if suddenly google or firefox lacks it. Here is a nice shim for websql: https://github.com/agershun/WebSQLShim Then there is IndexDB. This is a no-sql database and is designed to store JS structures (or objects) in a typical name-value pair style. I think JS people like this a lot since they can easily stuff raw JS objects into it, using a string key. And while it does have its uses, its not a table/row/column solutions. And finally there is access. Which is a pure Microsoft solution, only runs in Edge or Internet Explorer and requires the onslaught of COM. This might have changed in the later versions of edge but either way - this is Microsoft only. So I doubt that is interesting. Thinking outside the box, Smart approach Facing this typical JavaScript mess (sorry but it's true, the browser wars were not kind to JS) I decided we needed to become independent. Browsers change all the time and JS have little standards to speak of compared to Delphi, C# or C++; So coming from Delphi JS will be hell to work with unless you do a long and serious study. Our role is in many ways to "tame" JS and force it to behave inside object pascal's rules. So we needed some structure, something that only change when we allow it! So that people can write programs without being afraid that it will vanish in six months. So we created two solutions: TW3Dataset for simple, single table work SQLite compiled from C to JavaScript Note: Some bugs sadly crept into TW3Dataset, but these have been fixed. So the next update will remedy the situation profoundly. The second option, SQLite, is awesome. It is a pure, 1:1 compile from the original C code, so it contains everything WebSQL has "and then some". The reason it havent been wrapped more rigidly is because it's destined to become a part of our "DataSnap" like API later. Like mentioned above we are on a journey here, and we have to deal with things in the right order. So each DB engine will be isolated in a "driver" like class and then register with a common API. That way people can use classes like TDatabase, TQuery, TStatement and similar high-level components to access any database they like. IndexDB is the oddball in all of this, so we might write a shim/proxy to leverage that. Picking an engine OK, with a situation rapport out of the way, lets pick a database. Our options are thus: WebSQL SQLite TW3Dataset Since TW3Dataset needs the update, I have excluded that. SQLite is awesome but could perhaps need more solid wrappers. So for this I will use WebSQL since that has more clearly defined classes. Picking a storage point Browsers only have cache storage. This is basically a sandboxed file that is saved in the cache folder. Under phonegap this is likewise sandboxed and stored in the "yourname.app/cache" folder if im not mistaken (not sure about that one, please check the Phonegap docs if you need the absolute path). But you dont need a path to load or save into the cache. The browser maps your data into the file-region automatically. Also worth mentioning: When you run in the browser you have a limit of 10-15 megabytes (depends on the browser) for cache storage. When you link the project with Phonegap this limit goes away and you have the same storage rights as native applications. So you dont need to worry about running out of space if you plan to link with phonegap. For pure browser work, you are not expected to make huge databases - but rather cache info before shipping that to a server. A bit like what you would use TClientDataset for under Delphi. But ok, let's use normal cache storage for now. WebSQL is excellent because this will store itself automatically! You dont need to do anything in particular to load or save it. It all boils down to you creating or dropping the database. DB example Right! So fire up Smart and create a new visual project. Our first business is to create the database object and I typically put that into the application class. That way it can be accessed by any form in the program. A nice helper function is also good to have. Here is how it looks so far: unit Unit1; interface uses Pseudo.CreateForms, System.Types, SmartCL.System, SmartCL.Components, SmartCL.Forms, SmartCL.Application, SmartCL.DbSql, Form1; type TApplication = class(TW3CustomApplication) private FDatabase: TW3WebSQLDatabase; public procedure ApplicationStarting; override; published Property Database: TW3WebSQLDatabase read FDatabase; end; // global easy-access function to DB layer function Datastore: TW3WebSQLDatabase; implementation function Datastore: TW3WebSQLDatabase; begin Result := TApplication(Application).database; end; procedure TApplication.ApplicationStarting; begin //setup database engine FDatabase:=TW3WebSQLDatabase.Create; FDatabase.DBName:={$I 'app:name'}; FDatabase.DBDescription:='Database for ' + {$I 'app:name'}; FDatabase.DBSize:=(1024 * 1024) * 4; inherited; end; end. This creates a websql database that can hold 4 megabytes of data. Which in browser terms is huge (I mean, you are not doing your taxes here are you). Then there is the main form and actually making something happen. So drop a TW3Button on the form and set the title to "Create Database". We then need some code to create a table and insert some records. For brewity here is the whole code for Form1: unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.DbSql, SmartCL.Controls.Button; type TForm1 = class(TW3Form) procedure W3Button1Click(Sender: TObject); private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; end; implementation { TForm1 } uses unit1; procedure TForm1.W3Button1Click(Sender: TObject); var Transaction: TW3WebSQLCustomTransaction; begin // activate database try if not Datastore.Active then Datastore.Active := True; except on e: exception do begin writeln("Failed to activate database: "); writeln(e.message); exit; end; end; // Get new write transaction if Datastore.GetWriteTransaction(Transaction) then begin // Create our table try Transaction.Execute("create table if not exists customers (id integer primary key asc, name string);",[]); finally if Transaction.LastFailed then WriteLn(Transaction.LastError); Transaction.Free; end; end else raise Exception.Create('Failed to create write transaction error'); // Get new write transaction if Datastore.GetWriteTransaction(Transaction) then begin try // Populate the table with some records for var x := 1 to 10 do begin var MyData := 'John Doe #' + x.ToString(); Transaction.Execute("insert into customers (name) values (?);",[MyData]); end; finally if Transaction.LastFailed then WriteLn(Transaction.LastError); Transaction.Free; end; end else raise Exception.Create('Failed to create write transaction error'); // Get a new read transaction if DataStore.GetReadTransaction(Transaction) then begin // Setup the OnSuccess event handler. Transaction.OnSuccess := procedure (Sender: TObject) begin var Reader := TW3WebSQLReadTransaction(Sender); if Reader.Dataset <> nil then begin var Dataset := Reader.Dataset; for var x := 0 to Dataset.rows.Length - 1 do begin var Row := Dataset.rows.item(x); WriteLn(Row['name']); end; end; // All done, kill the transaction Reader.free; end; // Setup the OnFailed event handler TransAction.OnFailed := procedure (Sender: TObject) begin var Reader := TW3WebSQLReadTransaction(Sender); writelnF("Read transaction failed: %s", [reader.LastError]); // Kill the transaction Reader.free; end; // OK lets execute the SQL Query! try Transaction.Execute("select * from customers;",[]); finally if Transaction.LastFailed then WriteLn(Transaction.LastError); end; end; end; procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} end; procedure TForm1.Resize; begin inherited; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end. Now you are going to notice something -- namely that the database is persistent (!) So if you run this example 10 times, it will actually create 10 new records every time. The key here is the SQL that creates the database: Transaction.Execute("create table if not exists customers (id integer primary key asc, name string);",[]); This basically says "Create this database UNLESS it already exists". Manual storage with SQLite I am bit pressed for time right now. I will be away from the office for about a month pending surgery. I will be busy with forum duty etc. as much as I can, but sadly a secondary example with SQLite will have to wait a little. But I hope this one has given you an easy path to store things in the cache. Also remember: You can look at the data using the Browser Devtools I will do an article on SQLite when time allows
  3. lennart


    Strange, no idea how you get the $ postfixing there (which means its a managed variable or object, it has nothing to do with obfuscation -- it just means smart handles the life-cycle of that variable or entity). Are you using inline variables to hold this or properly defined variables? There is a subtle but huge difference. But as EWB points out, you need to build it up like you would any array. And I would add some functions for each record to simplify managing them. Like "AddParam(bla bla bla)" and stuff like that. Just a quick test i did now to see what this was about: JC = record id : integer; external 'id'; name : string; external 'name'; age : integer; external 'age'; end; JB = record field1 : string; params : Array of JC; end; JA = record fields : array of JB; end; Then i just wrote some code in a button onClick handler: procedure Form1.W3Button1OnClick(Sender: TObject) var head: JA; row : JB; begin row.field1 := 'first'; head.fields.add(row); asm @raw = JSON.stringify(@head); end; showmessage(raw); end; The code above, when it reaches the showmessage call, produces clean JSON. No postfixing. The postfix is added to all Smart "managed" variables, but records should be excluded from that. But (!) JavaScript work by references, so seem to me you managed to fall between two chairs and when you assigned values to the data, JS produces references to the values rather than the values itself (a bit like pointers can behave under COM Delphi coding). Here is the result I got: {"fields":[{"field1":"first"}]} Now, mapping to existing data, which i presume you are doing (?), where you have a wad of JSON you load in, parse to an object structure -- and then i presume you want a ready to go array structure from that? Well, i did something similar in the memory-filesystem unit if you look there, but as a general rule i read in the data into a variant, then typecast and traverse that, copying over the data i need. Then just null the variant to get the GC to clean up. I also have a node.js websocket server that sends info as JSON on my network, and designed the structures to be simple and easy to transport. Using a typical envelope record to make identification easy, then attaching the actual message as base64 inside: type TNetworkPacket = record Identifier: string; //GUID ClientId: string; //GUID envelopeId: string; //ID of the contained type envelope: string; // Contained type, base64 encoded end; // Then i can do simple lookups for the handler when i get a package // Locate a handler for this JSON packet var handler := __dispatch[envelopeid]; // Did we support this message? Ok, dispatch if assigned(handler) then handler(Socket, Packet.Envelope):
  4. Or just .. uses System.JSON; type TMyType = class Firstname: string; LastName: string; age: integer; end; var data := TMyType.Create; var textdata: string := ''; data.firstname :='Jon'; data.lastname :='Aasenden'; data.age := 44; // Turn object to json text asm @textdata = JSON.stringify(@data); end; // Turn JSON Text into object again asm @data = JSON.parse(@TextData); end; Javascript objects are by default serializable. But not objects that inherits from TObject. Names etc. of fields in a pure "class" without inheritance are protected from compiler changes. So you can safely do lookups etc. It compiles to a pure JS structure. Also look at classes in system.structure.pas, system.structure.json .. the structure classes provides abstraction and can do storage in most formats, including binary, with ease. The idea is to have multiple back-end formats (json, xml, binary.. whatever) that would work with the same front-end persistance
  5. That looks like Delphi code? Second snippet there? You have to look at the Smart classes and the RO imported classes and method-names. If you look at the generated code after importing a RODL file, you will notice: (* This codegen depends on RemObjectsSDK.js: file must be in "SmartMobileStudio\Libraries\" folder * Usage: * var channel := TROHTTPClientChannel.Create("http://localhost:8099/JSON"); * var msg := TROJSONMessage.Create(); * var service := TNewService.Create(channel, msg); * service.Sum(1, 2, * procedure(aResult: Integer) * begin * ShowMessage('Result = ' + IntToStr(aResult) ); * end, * ROErrorCallback); *) Note: in the above imported RODL file, the remote service is just called "NewService". You have to import the RODL file for your service stack. Also make sure that you have a JSON channel on your native server. You might want to look at our classes for dealing with memory and binary data, like System.Memory.Buffer. System.Memory.Allocation etc. since they will simplify working with raw data. The unit System.Types.Convert is also very important, it has a class called TDataType that allows you to encode between instrinsic types, memory and byte-arrays.
  6. requestAnimationFrame() doesnt always exist under some rendering engines, especially kiosk systems and embedded web-views. Which means it will fall back on a setTimeOut shim anyways. Things are the way they are for a reason.
  7. As for WinXP, im sorry but that is not a system we can support. Please consider upgrading your machine
  8. By the way, I spent an hour writing you yesterday, just in case you missed it:
  9. Just to make sure there is no missunderstanding: This is a bytecode assembler, disassembler and runtime implemented in Smart pascal. It is not a "smart to bytecode" compiler where you can take a smart project and generate bytecodes. Although i could probably create a bootstrap that injects your smart code into a single .exe file -- but that already exists with NodeWebkit (google it). What you get in these units is a runtime engine that uses assembly close to 68k and arm, but where variables is a known factor. So moving data between registers and variables is valid. Allocating variables on the fly is likewise allowed. The next iteration of the project is to add class and instance awareness to the virtual cpu, so that classes can be defined, members defined and instances created and managed. Then you essentially have Java but one you can shape and do whatever you like with. This is very low-level stuff and to make use of it you need a high-level parser that assembles the binary LDEF files (read: parses whatever language you have made, then uses the assembler to emit bytecodes). The cool part is that i have also implemented the same in Delphi, so it can run native there or via FPC just about anywhere. The ASM instructions are designed to be easy to port to asm.js, which is a huge topic i cant cover here -- just wanted to clear up any missunderstanding! But if you are into languages and perhaps have wanted to create your own lingo, then this demonstrates basic principles. I have avoided too much binary for the "cpu" factors to make it readable and understandable. A faster version can easily be implemented at a later date -- its a journey
  10. Did you remember to run the update program? It seems to me like you have either been extremely unfortunate and hit every possible problem in existance, or that you have some old files lingering that were not updated. Backup the RTL folder, run the update and make sure you get the fresh units. There is no way that amount of errors can occur without something being terribly wrong with the installation
  11. One thing that can be a bit of a challenge when coming from Delphi or FPC, is that Javascript is 100% ASYNC by nature. This means that from the time that you call the constructor, to the time that the element has been successfully created, styled and injected into the DOM - 1000 other things can happen in between. This is true for everything, even loading third party libraries, css files, images; everything is async. There is no such method in our core RTL that you describe (CreateRootElement), this is a part of Chrome itself. So unless you are using a third party library like jquery or something that doesnt synchronize with our RTL - this is too little to go on to properly help you. But when you run your program, click the buttons in the following order, check the "pause on caught exception" (note: there is a deliberate exception when the program starts checking for a feature, but this is handled so just click the blue arrow to continue). Below you see how the callstack looks for an exception i intentially created. From the event being issued, delegated, dispatched and handled. But i doubt this is where your problem stems from. I think this has to do with synchronization and that you are expecting code to execute linear. Hence your code crash because whatever you are trying to access is either not finished yet, fully loaded or in the process or being created. It is a typical thing to experience when coming from Delphi or FPC, because you are used to linear execution. Here are a few pointers on how to synchronize your code and what to override. Because there are 3 substancial differences (InitializeObject, FinalizeObject and ObjectReady. More about those as we move along). When you first look at examples etc. you will notice calls like TW3Dispatch.Execute() which is often used to create small timed delays. A bit like TTimer in Delphi but wrapped into a single method. The same class, TW3Dispatch also has other handy methods. You will find it in System.Time.pas and SmartCL.Time.pas. Smart supports partial classes, which means the content of classes can be finished and/or added to in different units (much like C#, C++ and Java has; with emphasis on Mono C#). This is very effective when building multi target frameworks. The second thing to explore is the use of callbacks when writing code that should run in a purely async environment. This can take some getting used to, but once its in your fingers you will wonder how you ever lived without it (or at least parts of it). So learning to properly synchronize things is the key here. This is why you never override the constructor in Smart, you override InitializeObject() and FinalizeObject() instead which ensures a level of synchronized execution. For example, let's say you are loading a file right? Since JS is async that means whatever Load() routine you use (even direct access to the filesystem under node.js) will finish and exit before any data is available. This can be bewildering to say the least. Until you start to make some observations on how it's best handled. Loading files can be done in many ways, but for browser apps -using the methods in TW3Storage is the easiest (in SmartCL.FileUtils.pas). Here is an example of how you could use callbacks in your own classes to better deal with async IO: // First lets define a callback. Just like events they dont need "of object" postfix // or "reference to" prefixing. Its fairly straight forward stuff. // Note: The procedure we define is actually "TStdCallback" in System.Types.pas type TMyDataClassReadyCB = procedure(Success: boolean); // OK now lets imagine we have a class for loading some text data. It takes text and builds // an object model from this. If the object model is loaded and valid then Resize() will // do layout and show the data. So let's define a loading method that takes our // callback as a parameter: procedure TMyDataClass.LoadDataFromURI(const URI: string; const CB: TMyDataClassReadyCB); begin // We let TW3Storage do the dirtywork. Note that there are several overloaded // methods for loading data in the class. This one loads any file and // returns it as text, but you can also get the data as a TStream (have a peek at the class) TW3Storage.LoadFile(URI, procedure (const FromUrl: string; const TextData: string; const Success: boolean) begin // Loaded ok? Then keep the data if Success then SetRawData(TextData); // Regardless of outcome, always call back! if assigned(CB) then CB(Success); end); end; // When calling the method from the UI, say a button-click, we can be more // flexible with how we handle control states. The callback will fire // regardless of success or failure procedure TForm1.BtnLoadDataOnClick(Sender: TObject) begin // Disable load button and listbox while we are loading. // Also set the cursor to a progress animation BtnLoadData.enabled := false; lstDataView.enabled := false; Cursor := crProgress; // Now use our loading method FMyData.LoadDataFromURI('res/defaultdata.txt', procedure (Success: boolean) begin // Loading is done! Regardless if it failed or not we adjust // the UI's state back to how it was. With one exception, // namely that we let our list remain disabled if no data is present Cursor := crDefault; BtnLoadData.enabled := true; lstDataView.enabled := Success; // Use an inline variable to remember if our list // held any data and was cleared var HadVisibleData := lstDataView.Items.Count > 0; // OK let's build a data-model if we got some data to work with. // If the load failed then clear the list. If the load failed the list // has already been disabled (see above) so we just need a clear() call. // Javascript is laid back, i prefer to be more strict. lstDataView.BeginUpdate(); try // Flush our list if it had content if HadVisibleData then lstDataView.Items.Clear(); // Any data loaded? Build an object model from it if Success then buildDataModel(); finally // Mark the list as needing a Resize() call if we either // cleared old data, loaded new data -or both! if HadVisibleData or Success then lstDataView.AddToComponentState([csResizing]); // End update, it's all good lstDataView.EndUpdate(); end; end); end; One of the root classes for visual controls, TW3TagContainer, has a method you want to investigate. It will save you a lot of time: // Does a check if the child elements are in ready-state function QueryChildrenReady: boolean; virtual; This method will recursively check that all child controls have been created and have their csReady flag set in the ComponentState value-set. I find this to be very handy when writing large and complex visual controls where you need to know 100% that children are ready before you access them. The way JS works is that even though the constructor is finished and the element created --the prototype can still be under construction in the background. It is very rare to encounter this in our new RTL, but it can occur if you expect controls to be ready after a call to Create(). There is also a method to synchronize resize calls. This is quite handy to add in the ObjectReady() method. All visual elements have an ObjectReady() method that is called after the element has been injected into the DOM. This executes just between the time when an element is finished, but before it's visually presented. If you want an immediate resize when all child controls are created and ready, calling ResizeWhenReady() is the ticket. procedure TW3MovableControl.ResizeWhenReady; Next there is type helpers. Handles for example come in many shapes, and most of them (like THandle, TControlHandle, TGradientHandle, TCSSStyleHandle) have a helper class with low-level methods. Sadly these are not always shown in the code suggestion dialog, but that will be improved on in future updates. Now TControlHandle has a method called ReadyExecute(), and as the name implies this sets a callback that fires when the element has been created and been injected into the DOM. But this is low-level so we are not talking about the whole object pascal custom-control, but rather the raw HTML element that the control manages. It should be noted that overriding ObjectReady() is the proper way to do things, but sometimes you have to "bend the rules". While a bit esoteric looking calling a method on a handle, this is perfectly valid: procedure TSomeControl.InitializeObject; begin inherited; ... Handle.ReadyExecute( procedure () begin // Do something at once when the element is injected into the DOM. end); end; I think these will help you synchronize your code when porting from Delphi or FPC. In 99% of cases where people experience problems with porting over code, it comes down to synchronization. The JavaScript Virtual Machine simply doesnt follow conventional behavior and proper use of callback handlers, events and delayed execution is the way things work. The bonus is that you are not boxed into a "fake" linear environment (which could be achieved), communicating with JS libraries and mapping directly to existing JS objects is 1:1 rather than resorting to elaborate interface files. Paradoxical as it may sound, with correct use you can easily produce code that runs faster or en-par with the tools you are used to. We have node.js code in our lab that actually out-performs native, monolithic code. When you add clustering and domain level programming to that - the possebilities are near endless. Have a peek at this demo for example: http://quartexhq.myasustor.com/applications/particles/ We use TW3Dispatch.RequestAnimationFrame() to ensure a steady 60fps rendering, but it runs fine at 120fps on relatively modest PC specs. So its a matter of going with the flow and learn the subtle differences.
  12. Click on project options -> Compiler -> and set hints to a lower rate
  13. You can load and save to normal streams, then push that into the cache (for example). The filesystem has been there for some time, but we have deprecated it since it only comes into play with phonegap really. Uploading and catching files is done easier using standard html5 methods. But we have a baseclass and cache filesystem implementation, and will soon have node and phonegap wrapped too -- then loading and saving to real devices will be very simple. We have to finish the visual parts of SMS first before we dig into the non visual. But look at the storage units! It is very simple to base64 encode a stream and save it to the cache
  14. Adding an "async keyword" for something as trivial as this seem like overkill to me, but i do see your point. There is nothing particularly useful imho and by that i mean - javascript is async by nature. It executes single threaded, with events popping in and out of existance in a manner that is hard to control. It can be controlled, but not without resorting to hacks that might look cool there and then - but will require constant maintainance for each change in the runtime engine. There are a few JS libraries that does this on github, so if you need this type of behavior then perhaps wrapping that is a better solution. There is nothing in the way of adding a "workbegins/update/ends" style mechanism per see -- but a lose "async" prefix is not on our menu. At least not in this form. What you have here can be replicated with 3 calls: beginwork (show dialog), create panels and wait until verified, close dialog and perform re-entry. We have avoided this type of coding for many reasons, first of all that users should have full control over how they wish to deal with async workload. If we forced an async closure, we would also have to compliment that with a re-entry mechanism so that code continues post return. This leads to much heavier code since a procedure must be parted. In Smart you could achieve the same with a little work, with something like: async( procedure () begin // create panels here end); So if you write a simple procedure: procedure TMyClass.Someproc; begin async(somestuff); proc2; proc3; end; The cleanest way to compile that would be: function TMyClassSomeProc() { async(somestuff, function () { proc2(); proc3(); } } This avoid a typical re-entry you would have in assembler, and just executes "the rest" of the procedure when the async finishes. Now try to debug that if you have 10-15 async's going in 3 different contexts! Which as you probably notice, is the exact same as using TW3Dispatch! Except that is cleaner and doesnt force the codegen to emit magic But ofcourse, you will have to define the re-entry yourself. Or use of the many libraries that are out there. Our goal is to provide a compatible, ad-hoc, no-nonsense pascal based system that follows suit. We could have added a ton of features, and we have been tempted, but for now at least this is not on the menu. In the future we will no doubt add other, more helpful features, like generics instead.
  15. As an experiment in compiler tech, I have written a compiler and bytecode runtime in smart pascal. While not for everyone, it shows you how to write a parser/tokenizer, assembler and disassembler, and finally a bytecode runtime. This is very handy if you aspire to create.. perhaps a basic language for the web, AI speach where text is compiled into assembly, game engines .. the list is endless. Anyways, I will include a working assembler / compiler, linker, parser and runtime as a demo in one of our updates (not the next, but most probably the one after that). If you compile and run the compiled code through kripken's JS to LLVM bitcode converter - you have a system that runs en-par with native software (asm.js) in the browser. I will write a short tutorial with the basics you need to know. It runs much faster than vanilla JS and generates code that can easily run under C or Delphi. It's pretty cool! Brilliant for writing larger and more elaborate systems. It is in many ways close to Java 1.0 or the initial CIL (common language runtime).
  16. Easy fix: Select the theme file you want, then check the "use custom css". This creates a clone of the last selected theme and gives you a css file in the IDE. Search for ".TW3Image" and look at the css -- im fairly sure you will find a border set there. Remove that, then copy & paste the text into a new textfile and save that under the Themes folder. Now reboot smart and the theme should be available from the normal list -- call it "Ubuntu Ext" for instance. Not a permanent solution, but good enough until the next update. You can also set the border style manually via: w3image1.handle.style['border'] := '0px none'; If that works, then we know its hardcoded as a part of the theme. Or, create a new class that inherits from TW3Image, override "StyleTagObj" and just call inherited + ThemeReset() + handle.style['border'] .... that should kill it until i issue a permanent fix
  17. The SQLLite engine we ship is compiled to JS from C. So i would just Base64 encode the data and stuff it in the cache. For mobile apps (phonegap) you should work directly with the filesystem, and honestly I would use WebSQL (which is just a wrapper around SQLite inside webkit/moz). Later when we have the node.js DB framework you can write a small server to deal with data storage, and just use SQLite to cache up changes that are uploaded to the server when it connects.
  18. IndexDB is the "universal" DB for browsers. Although names will vary. Phonegap doesnt have any size limit so both sqlite etc. has no restrictions on mobile devices, nor websql or indexdb for that matter. But vanilla cache is bound to the whims of the vendor. The cache was never meant for pure DB storage. It is meant for small amounts of cache'd data to be sent off at a later time. And yes, DB is very much coming. We want to finish the visual parts first, then we begin on the non-visual ascpects. This includes unified storage (filesystem drivers) for node.js, browser, cache and zip files. We will no doubt add azure and other devices later (dropbox, amazon etc. etc), but we have to do things in the right order. LevelDB is just someone's framework that uses IndexDB as a boat so to speak. The problem with indexDB is that it completely breaks with traditional row/column based data storage. It has not queries like we are used to and belongs to the "nosql" family or databases. So we have to make an exception for that. But when making mobile apps, just keep in mind that there is NO restriction on your SQLite / WebSQL database size. That is only when you work with normal desktop browsers.
  19. In that case it is a part of the image class and cannot be removed. This is a mistake and i would have to edit the code to see what is going on. Some of the controls have look/feel that is hardcoded into the stylesheet - and the border is one of them. But i will have a look at this pronto and get back to you!
  20. You probably dont need to convert the stream into a string. It depends on where you are storing it. The Cache storage classes will transform binary data automatically for you. But yes, that looks good!
  21. This has been raised before. The image border is simply a part of the theme. Each theme will have properties that reflect the platform they represent. If you dont like a style you can call ThemeReset() on the control to get rid of it. // Completely reset theme and styling W3Image1.ThemeReset(); ThemeReset removes the theming styles, but TW3Image can also have some residual settings. In this case, just blank out the StyleClass property completely: W3Image1.ThemeReset(); w3Image1.StyleClass :=''; If you want your own style, I would suggest just using the GlobalStyleSheet() instance and add your CSS there. Having an extra CSS file just for small potatoes is overkill. You can get the global stylesheet (which is a sheet created on demand and used by our effects and other sub systems): // Get global stylesheet var styles := TW3StyleSheet.GlobalStyleSheet(); // Append our css snippet (Note: Only add once, good for unit Initialization section) styles.append(#".MyImageStyle { /* Add your css here } "); // And apply our custom style, perhaps a nice border, no border, gradient background W3Image1.StyleClass := '.MyImageStyle'; That should do the trick!
  22. On a back-note, if you ever see $ in names, 90% of the time that means you are dumping out managed values. The RTL uses different names inside the code, than the data you get when you extract something from that code. Like you noticed, the rows are "values" as an array, and you can iterate from there. This is probably not so intuitive -- i cant wait until we get to the DB stage where we have a framework to build from. A bit like what DBExpress etc. did for delphi, where you have a fixed number of classes that represents various bits. I think the websql wrapper came out quite nice, but it can be better.
  23. Had a closer look at yes, this unit is not used at all and should be disregarded. It was never finished and as you see - it implements binary storage for version-info. So that unit should be disregarded completely and we will have it removed asap.
  24. First, what unit are you using? There was a mistake earlier and two units were included that holds this info. The first is in the system namespace. the idea was to have TW3CustomApplication start in the System namespace, and then all application types (including node) would have a dedicated application and version object. From what you describe it would seen that a {$I 'app:info'} is missing on our behalf. I will look into this later today! Thanks for letting us know!
  25. lennart

    Compiler woes

    This sounds very strange. Typically when odd errors like this surface, it has to do with timing or that some code has been executed at the wrong time. Have you compiled with the JS as a separate file or is it included in the html? We basically need an example that we can copy & paste in to re-create the problem, otherwise we are searching blind
  • Create New...