Jump to content


Popular Content

Showing content with the highest reputation since 08/04/2014 in all areas

  1. 7 points
    Updated 5/2019 to add: Now online here: Paintball Net --- Back in the mid-90's, my brother created what he called an "action MUD". He called it "Paintball Net". PBN used ANSI text terminal commands to create a combined roll-and-scroll and animated text experience. ! (exclamation points) were trees, _ (underscores) were grass, ^ (up carets) were mountains, and more. Your "avatar" was a "Y" (uppercase y) and enemies were * (asterisks). You used MUD-like commands to move around the world and target your paintball gun and buy and sell and more. I wish I had a screenshot of what the first version looked like, but you'll just have to imagine. πŸ˜ƒ He asked me to create the GUI for the game. That was 1996, and I had just purchased Delphi 2 and wrapped up my first "learn Delphi" project. This sounded like a good next step, and "PBTerm" was born. I *do* have screenshots of that. Oh, yeah. Love those 16-color Windows graphics. πŸ˜ƒ During the years it was online, the game evolved and upgraded to look more like this: We were never going to win any awards, but we had a very devoted player community. πŸ˜ƒ Paintball Net was online from the summer of 1996 through the summer of 2000, when we took it offline to focus on other projects. The game was never huge, but we had thousands of players come through over that period. And since then, every year at least a few of the players have reached out and asked/begged/pleaded/demanded if we were going to put it back online. There really wasn't much chance of the original version going back online. The original server, written in ANSI C for Linux had proven rather fragile, and was a big part of why we took the game offline. It was taking hours every day just to keep it up and running, in addition to time spent managing the community of players. On top of that, a hard drive incident in 2008 had cost me the source code of a number of core third-party/modified components for PBTerm. This past summer, though, I realized I might be able to make the game live again using Smart Mobile Studio. I would do a straight port of the ANSI C server to Smart Pascal using Node.JS and WebSocket, and I would rebuild the PBTerm client as a browser-based client. I'm not going to say it was *easy*, but it has been a lot of fun. ANSI C converts to Pascal without a lot of painful gyrations. And Node.JS seems a LOT more flexible, stable, and powerful than the TCP sockets approach we were using before. Also, game development is a lot easier when you already own all the graphic and audio resources. πŸ˜ƒ This is the server "in action"... Recreating the client has been more complicated. 20+ years of Delphi habits had to be adapted to the new reality of SMS and a browser-based UI. But that's coming together too. It might be obvious, but I'm not targeting this game at mobile. Paintball Net needed a mouse and keyboard in 1996, it's gonna need a mouse and keyboard in 2018. Also, I've made as few modifications to the gameplay as possible. I really wanted to bring back the original as much as I could. Today I got the handful of sound effects integrated, which was easier than I expected. I'm planning to start testing soon. I just need to line up a server to use and find a few volunteers. I'm excited. This would never have happened without Smart Mobile Studio. So I figured I would share. Merry Xmas! -David
  2. 6 points

    Smart Mobile Studio 3.0 is released!

    One year of hard work, lots of changes, new features and bug fixes. But here it is! Release announcement: https://smartmobilestudio.com/2018/07/20/smart-mobile-studio-3-0-released/ Most of you have already been using beta-versions, that are really, really close to this release. The last bit was to fix the cors-problem in the Images and to add the Smart Desktop source code to the Featured Demos. We've also set up a live demo of the Desktop to showcase what Smart Mobile Studio and JavaScript can do: desktop.smartmobilestudio.com Big thanks to everybody here, who have helped by testing, suggesting improvements and reporting bugs.
  3. 5 points

    Development updates

    New update available: RTL: TW3ListMenu: Add Items-property, so items can created in Object Inspector. Add OnSelected-event. IDE: Bug fix to renaming of units Show form source instead of designer when form is selected from Project Manager
  4. 5 points

    Smart Mobile Studio 3.0.1 is released!

    Smart Mobile Studio 3.0.1 is released This is the first release since 3.0. Biggest new feature is TW3LeafletMap, which lets you use OpenStreetMap. As it does not need API keys (like TW3GoogleMaps), it’s really fast and easy to use: – Create a project – Add a TW3LeafletMap -control on the form – Set AutoCreateMap to true on the map control Changes since 3.0 8.11.2018 RTL: – EventManager: – Add procedure AllowDefaultAction, that can be called from OnClick when the default action should happen. For example: To let the virtual keyboard to pop up from OnTouch. – Bug fixes: – Native scrolling was prevented if scrolling was done from an unknown element. – Prevent an extra OnClick from being fired on mobile devices. – TW3ListView: Bug fix to resizing of items. – Bug fixes to GeoLocation. Also update the Geolocation demo. – Deprecate PhoneGapAPI’s JGeolocation functions. SmartCL.GeoLocation.pas should be used instead. – Fix slider so that OnMouseDown, OnMouseUp and OnMOuseMove can be used. – TW3TabControl Tab’s OnShow was sent twice for the first tab – SmartCL.RegEx moved to System.RegEx. Also fixed TW3RegEx.Exec to return a proper empty array instead of nil. – Bug fix to Chart: TSeriesData.Refresh now also updates the X axis – TW3Grid: – Added TW3ImageColumn – Add Alignment-property to TW3ColumnHeader – Added a new OnCellControlCreated-event, which fires when a button, toggle, progress bar or image is created. Makes it possible to change properties of the control easily. – Added support for OpenStreetMap using the Leaflet library: – New control: TW3LeafletMap – New featured demo: LeafletMap IDE/Compiler: – Fixed search path compilation issues – Relative and absolute paths are working now – Compiler is updated when search path is modified in options – $I will look for include file in the project folder first – $R supports absolute paths, wildcards, folder name extension and ($Libraries) macro – Fix exceptions in Search – Upgrade to UPX 3.95 23.7.2018 – SmartCL.Effects: Properly handle padding and margins while doing effects. 22.7.2018 – Fix to css-files for selected text in TW3Edit and TW3Memo Release notes and installers: https://smartmobilestudio.com/2018/11/08/smart-mobile-studio-3-0-1-released/ Note that you can also use SmartUpdate to keep your portable installation up to date. Instructions on using that are in the link above.
  5. 5 points

    Font Demo

    in project options add a webfont (see https://jonlennartaasenden.wordpress.com/2017/10/04/webfonts-in-smart-mobile-studio/) W3Memo1.Font.Name := 'Tangerine'; W3Memo1.Font.Style := [fsItalic]; W3Memo1.Font.Size := 48; W3Memo1.Font.Weight := 'bold'; In this case I added Font 'Tangerine' (see also https://developers.google.com/fonts/docs/getting_started)
  6. 5 points
    Daniel Eiszele

    Google Maps API

    It took quite a bit of trial and error due to some case sensitivity errors but I finally have a working google map which utilises Google's javascript API. I know this has been touched on in other topics on the forum but none of them had a full blown working example; so I include this here for those who come after! Just create a new Visual Components project and replace everything in the form1 unit with the below code. Note that you will also need to obtain an API Key from Google (https://developers.google.com/maps/documentation/javascript/) and replace the dummy one in the code below. Note that I have only included a few partial classes and objects as far as the maps API is concerned but hopefully this is enough to get newcomers started. As an aside, the reference section on the above linked URL is very helpful in getting things up and running. 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.Controls.Elements, W3C.HTML5, W3C.DOM, SmartCL.Require, System.JSON; type JMap = partial Class external; JLatLngLiteral = record Property lat : Double; Property lng : Double; end; JMapOptions = record property Zoom : Integer; property Center : JLatLngLiteral; end; JMarkerOptions = record property position : JLatLngLiteral; property map : JMap; property title : String; end; JMarker = partial class external 'google.maps.Marker' Public Constructor Create(options:JMarkerOptions); external 'Marker'; end; JMap = partial class external 'google.maps.Map' public Constructor Create(mapDiv:JElement; options : JMapOptions); external 'Map'; end; TForm1 = class(TW3Form) private {$I 'Form1:intf'} FMap : JMap; FMapDIV : TW3DIVHtmlElement; protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; procedure InitMap; end; implementation { TForm1 } procedure TForm1.InitializeForm; begin inherited; end; procedure TForm1.InitializeObject; var APIKey : String; begin inherited; {$I 'Form1:impl'} FMapDIV := TW3DIVHtmlElement.Create(Self); FMapDiv.SetBounds(5,5,450,400); //Enter a valid API Key here. It can be obtained from //https://developers.google.com/maps/documentation/javascript/ APIKey := 'YOUR API KEY HERE'; Require(['https://maps.googleapis.com/maps/api/js?key=' + APIKey],procedure() begin InitMap; end); end; procedure TForm1.Resize; begin inherited; end; procedure TForm1.InitMap; var LUluru : JLatLngLiteral; LMapOptions : JMapOptions; LMarkerOptions : JMarkerOptions; LMarker : JMarker; LMapElement : JELement; begin LUluru.lat := -25.3444; LUluru.lng := 131.0369; LMapOptions.Zoom := 13; LMapOptions.Center := LUluru; LMapElement := Document.getElementById(FMapDIV.Handle.id); FMap := JMap.Create(LMapElement,LMapOptions); LMarkerOptions.position := LUluru; LMarkerOptions.map := FMap; LMarkerOptions.title := 'Woo Hoo!'; LMarker := JMarker.Create(LMarkerOptions); end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
  7. 5 points

    Database Examples

    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
  8. 5 points

    New forum software version

    So far working fine for me - an improvement over the previous forum. BTW I am loving the activity of users and admins in the forum. Hopefully it will continue to grow.
  9. 4 points


    The unit ECMA.Promise is broken. I use this definition Using Promises in SMS unit uPromises; interface uses W3C.Console, W3C.DOM, W3C.XMLHttpRequest; type TVariantDynArray = array of Variant; JDeferred = class; TJPromiseCallback = procedure(Value: Variant); TJDeferredObject_fn = function(d: TJPromiseCallback): Variant; TJDeferredObject = procedure(resolve: TJPromiseCallback; reject: TJPromiseCallback); TJPromiseCallback_fn = function(Value: Variant): Variant; TJDeferredEventHandler = function(event: Variant): Variant; JPromise = class external 'Promise' constructor Create(fn: TJDeferredObject_fn { = nil}); overload; constructor Create(resolve: TJDeferredObject_fn; reject: TJDeferredObject_fn); overload; constructor Create(fn: TJDeferredObject); overload; function always(alwaysCallbacks: TVariantDynArray): JPromise; function done(doneCallbacks: TVariantDynArray): JPromise; overload; function done(doneCallbacks: Variant): JPromise; overload; function fail(failCallbacks: TVariantDynArray): JPromise; function progress(progressCallbacks: TVariantDynArray): JPromise; function state(): string; function &then(doneCallbacks: Variant; failCallbacks: Variant{ = undefined}; progressCallbacks: Variant { = undefined}): JPromise; external 'then'; function &then(onFulfilled: TJPromiseCallback_fn = nil): JPromise; overload; external 'then'; function &then(onFulfilled: TJPromiseCallback_fn; onRejected: TJPromiseCallback_fn): JPromise; overload; external 'then'; function &then(onFulfilled: TJPromiseCallback; onRejected: TJPromiseCallback): JPromise; overload; external 'then'; function catch(rejecTJPromiseCallback: Variant = nil): JPromise; overload; function catch(rejecTJPromiseCallback: TJPromiseCallback_fn): JPromise; overload; class function promise(target: Variant): JPromise; end; type JDeferred = class external 'Promise'(JPromise) function notify(args: TVariantDynArray): JDeferred; function notifyWith(context: Variant; args: TVariantDynArray): JDeferred; function reject(args: TVariantDynArray): JDeferred; overload; function reject(args: Variant): JDeferred; overload; function reject(args: TJDeferredEventHandler): JDeferred; overload; function rejectWith(context: Variant; args: TVariantDynArray): JDeferred; function resolve(args: TVariantDynArray): JDeferred; overload; function resolve(value: Variant = nil): JPromise; overload; function resolveWith(context: Variant; args: TVariantDynArray): JDeferred; function all(iterable: TVariantDynArray): JPromise; overload; function race(iterable: TVariantDynArray): JPromise; end; { global external functions } function Promise : JDeferred; external 'Promise' property; function queue: JPromise; external 'Promise.resolve()'; function Error(message: variant): variant; external 'Error'; function document: variant; external "document" property; function window : Variant; external 'window' property; function &typeof(obj:variant): variant; overload; external "typeof"; function wait(ms: Integer): JPromise; function getURI(url: string): Variant; function getFile(url: string): variant; //function myRequire(url: string): Variant; implementation function wait(ms: Integer): JPromise; function setTimeout(ahandler : TJPromiseCallback; aTimeout : Integer): Integer; external 'window.setTimeout'; begin result := JPromise.Create( procedure (resolve, reject: TJPromiseCallback) begin setTimeout(resolve, ms); end); end; function getURI(url: string): Variant; var request: JXMLHttpRequest; procedure p(resolve: TJPromiseCallback; reject: TJPromiseCallback); // Standard XHR to load an image procedure doOnLoad; begin // This is called even on 404 etc // so check the status if (request.status = 200) then begin // If successful, resolve the promise by passing back the request response resolve(request.response); end else begin // Otherwise reject with the status text // which will hopefully be a meaningful error reject(Error('File didn''t load successfully; error code: ' + request.statusText)); end; end; procedure doOnError; begin // Also deal with the case when the entire request fails to begin with // This is probably a network error, so reject the promise with an appropriate message reject(Error('There was a network error.')); end; Begin request := JXMLHttpRequest.Create; request.open('GET', url); // When the request loads, check whether it was successful request.addEventListener('load', @doOnLoad); // Handle network errors request.addEventListener('abort', @doOnError); // Send the request request.send(); End; begin // Create new promise with the Promise() constructor; // This has as its argument a function // with two parameters, resolve and reject Result := JPromise.Create(@p); end; function getFile(url: string): variant; begin // Create new promise with the Promise() constructor; // This has as its argument a function // with two parameters, resolve and reject Result := JPromise.create( procedure(resolve: TJPromiseCallback; reject: TJPromiseCallback) // Standard XHR to load an image var request: JXMLHttpRequest; begin request := new JXMLHttpRequest(); request.open('GET', url); // When the request loads, check whether it was successful request.onload := lambda begin // This is called even on 404 etc // so check the status if (request.status = 200) then begin // If successful, resolve the promise by passing back the request response resolve(request.response); end else begin // Otherwise reject with the status text // which will hopefully be a meaningful error reject(Error("File didn't load successfully; error code: " + request.statusText)); end; end; end; // Handle network errors request.onerror := lambda // Also deal with the case when the entire request fails to begin with // This is probably a network error, so reject the promise with an appropriate message reject(Error('There was a network error.')); end; // Send the request request.send(); end); end; /* function myRequire( url: string): Variant; function ev(win: Variant; arr: array of Variant): Variant; external 'eval.apply'; var ajax: JXMLHttpRequest; function onReady(event: JEvent): Variant; var script: Variant; begin script := ajax.response; // ?? ajax.responseText; if (ajax.readyState = 4) then begin case( (ajax.status)) of 200: begin //eval.apply( window, [script] ); ev( window, [script] ); console.log('script loaded: '+ url); end else console.log('ERROR: script not loaded: '+ url); end; end; end; begin ajax := JXMLHttpRequest.Create; ajax.open( 'GET', url, false ); // <-- the 'false' makes it synchronous ajax.onreadystatechange := @onReady; ajax.send(null); end;*/ end.
  10. 4 points


    Was looking for an easy to make wysiwyg editor. This one is based on 'document.designMode'. Mozilla: "When an HTML document has been switched to designMode, its document object exposes an execCommand method to run commands that manipulate the current editable region" IFrame's have an innate document element which can be used for that purpose. Have a form with an IFrameHtmlElement and 2 buttons ('bold' and 'italic') : procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components W3IframeHtmlElement1.handle.contentDocument.designMode := 'on'; W3IframeHtmlElement1.handle.contentDocument.body.innerHTML := 'this is some text'; W3IframeHtmlElement1.handle.focus(); end; procedure TForm1.W3Button1Click(Sender: TObject); //bold begin W3IframeHtmlElement1.handle.contentDocument.execCommand('bold', false, null); W3IframeHtmlElement1.handle.focus(); end; procedure TForm1.W3Button2Click(Sender: TObject); //italic begin W3IframeHtmlElement1.handle.contentDocument.execCommand('italic', false, null); W3IframeHtmlElement1.handle.focus(); end; Besides 'bold' and 'italic' execCommand supports a host of other edit commands as well. see https://codepen.io/chrisdavidmills/full/gzYjag/
  11. 4 points


    Google has implemented a new service : web.dev Basically this measures some indicators of any url using their LightHouse tool. These indicators are grouped into Performance, Accessibility, Best Practices and SEO I created a minimal SMS project with only a single image on a form and run the test : https://web.dev/measure and url : https://www.lynkfs.com/Experiments/lighthouse/www/ This gives scores of 95 / 27 / 77 / 89 for Performance, Accessibility, Best Practices and SEO respectively There are a couple of really simple things which will up these scores considerably. Scores were upped to 96 / 74 / 92 / 100 when doing this : Best Practices from 77 to 92 : 1) in project options/linker unclick 'generate cache manifest' (deprecated) Performance from 95 to 96 : 1) in project options/linker unclick 'store CSS as a separate file' (95 to 96) 2) add async to <script async src="lib/polyfill.custom.events.js" type="text/javascript"></script> in the index.html template SEO from 89 to 100 : 1) add meta description tag to the head of index.html <meta name="Description" content="Put your description here."> Accessability from 27 to 74 : 1) add alt attribute to images, even if only '' : W3Image1.handle.setAttribute('alt', ''); (27 to 58) 2) add lang="en" to html element in index.html template : <html lang="en"> (58 to 74) 3) tabindex=1 for TW3Display and TW3DisplayView. Should be 0 ? run test again on url : https://www.lynkfs.com/Experiments/lighthouse2/www/ to see these results All of the above could be made part of the standard settings for a new visual project
  12. 4 points

    console override

    This mechanism can be used to redefine more built-in functions. Funny. The code below redefines 'ShowMessage' to redirect its contents to a Memo : procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} W3Memo1.Text := 'ShowMessage : ' + #10; browserapi.window.alert := procedure(text:variant) begin W3Memo1.Text := W3Memo1.Text + #10 + text; end; ShowMessage('ccc'); browserapi.window.alert('ddd'); end; or combine the two and redefine 'ShowMessage' to redirect its contents to the 'Writeln' function, which in its turn is redirected to the Memo : procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} W3Memo1.Text := 'Console.log : ' + #10; //WriteLn redirects to Memo browserapi.console.log := procedure(text:variant) begin W3Memo1.Text := W3Memo1.Text + #10 + text; end; //ShowMessage redirects to WriteLn browserapi.window.alert := procedure(text:variant) begin writeln(text); end; writeln('aaa'); browserapi.console.log('bbb'); ShowMessage('ccc'); browserapi.window.alert('ddd'); end;
  13. 4 points

    Development updates

    17.11.2018 RTL: Changes to handling of Cursor: Style definitions moved from basic html elements to control styles GetCursor and SetCursor can now be overridden Bug fixes to how many controls handle cursor. Especially TW3Label. Themes: Add missing styles TW3CheckBox, TW3CheckMark, TW3RadioButton, TW3RadioToggle and TW3RadioGroup Two new backgrounds: TW3DecorativeListItemBackground and TW3TransparentBackground RTL optimizations to creation of controls, GetBoundsRect, SetBounds, MoveTo and SetSize. Bug fix to SmartCL.Graphics.pas: Changing of canvas font, size and styles did not work. Bug fix to System.DateUtils.DecodeDate. IDE: Delete key works now Search dialog and other dialogs. Bug fix: Internal Browser Window showed only a white screen if Execute was clicked while it was open. Compiler: Now(), EncodeDate() and EncodeTime() returns now the same values as Delphi and FPC All time/date -functions fixed to work with the new TDateTime-values
  14. 4 points


    I'm sad to see Jon leave his baby like this, but I do respect his decision. What I can say is, that SMS is not dying here. I've been waiting a few weeks already to push a new update, but have not been able to get any answers from Jon, if he'd also want to include some changes. I believe things look worse to you guys than they really are. A lot of RTL work and especially bug fixes was done by me during the last year and almost all the IDE improvements and bug fixes by Primoz. Jon did pretty much all the NodeJS work, so we'll miss him most there. If I look at this optimistically, Jon's donating his shares to two young and talented Delphi programmers is a very nice thing and may give them a possibility to spend more time on SMS. But hey, hang on there and lets hope that this divorce will not be too ugly.
  15. 4 points
    I'd rather do it like Delphi and Lazarus: Keycode as a var parameter and setting it to #0 would cancel. However, that would break backwards compatibility of SMS code just as any other new parameters would do. If you guys don't see it as a big problem, we could do it in a future version. Let me know what your thoughts are.
  16. 4 points

    Scroll bar bug

    This was a weird bug that required changes in SynEdit code itself. May be a Delphi bug even. But I got it fixed and it will be in 3.0
  17. 4 points
    @rshotbolt Here are some resources that might help you out. I like the first one as it explains the precedence and how each overrides the others. Playing with the design 03.06.2013 https://smartmobilestudio.com/2013/06/03/playing-with-the-design/ Smart Mobile Studio and CSS: part 1 October 9, 2017 https://jonlennartaasenden.wordpress.com/2017/10/09/smart-mobile-studio-and-css-part-1/ Smart Mobile Studio and CSS: part 2 https://jonlennartaasenden.wordpress.com/2017/10/11/smart-mobile-studio-and-css-part-2/ Smart Mobile Studio and CSS: part 3 https://jonlennartaasenden.wordpress.com/2017/10/12/smart-mobile-studio-and-css-part-3/ Smart Mobile Studio and CSS: part 4 https://jonlennartaasenden.wordpress.com/2017/10/18/smart-mobile-studio-and-css-part-4/ Themes and styles https://smartmobilestudio.com/documentation/getting-started/themes-and-styles/ Working with controls, the boxing model 05.01.2012 https://smartmobilestudio.com/2012/01/05/working-with-controls-the-boxing-model/ Hidden stylesheets with Smart Pascal August 13, 2014 https://jonlennartaasenden.wordpress.com/2014/08/13/hidden-stylesheets-with-smart-pascal/ HTML5 Attributes, learn how to trigger conditional styling with Smart Mobile Studio November 8, 2017 https://jonlennartaasenden.wordpress.com/2017/11/08/html5-attributes-learn-how-to-trigger-conditional-styling-with-smart-mobile-studio/
  18. 4 points

    databases and applications

    This is taken into account with the framework we are researching / making now. The objects you use to access data act more as "front-ends" to the mechanisms behind it, and just like the filesystem classes the operations are all async. This has the benefit of decoupling the consumer-part (i.e controls + bindings) from the producer aspect. To the consumer parts the database can be resident - or on the other side of the planet, it cares not where the data originates, only that its made available in a format that it can use. A typical middleware entity would be a node.js server that is connected to X number of databases. The client will use the framework and connect to the node.js endpoint, but neither the controls or bindings will have any clue about the data originating from a server. It will simply see a driver and issue calls to it. When the driver (or class) gets the data it will validate and dispatch it via its internal mechanisms. The nice part about this is that, a datasource can be almost anything. As long as someone implements the 3 core classes, it can be anything from a hardcoded file, local database or remote data server for that matter. I will go into more detail about these classes when we are a little closer, but you are quite right that old-school 1:1 endpoints is useless in the new paradigm
  19. 4 points
    We're getting closer to the official release. The first beta is out! https://smartmobilestudio.com/2018/02/12/smart-mobile-studio-3-0-0-beta1-released/
  20. 4 points

    Google Maps API

    the google map, defaults to the roadmap view, so I added the MapTypeId to the code i posted above just add the following to the interface section const HYBRID = 'hybrid'; ROADMAP = 'roadmap'; SATELLITE = 'satellite'; TERRAIN = 'terrain'; JMapOptions = record property Zoom : Integer; property Center : JLatLngLiteral; property mapTypeId: String; end; change the following procedure to include the MapType param procedure InitMap(AAPIKey, ATitle: String; AMapType: String; ALat, ALong: Double; AControl: TW3CustomControl); and then in the implementation section, modify the require to include the mapTypeId option Require(['https://maps.googleapis.com/maps/api/js?key=' + AAPIKey],procedure() begin ... LMapOptions.mapTypeId:= AMapType; ... end); In my case, i needed my map to default to the TERRAIN view so, i used the TERRAIN const InitMap('myApiKey', Mountain, TERRAIN, Latitude, Longitude, FMap); e.g.
  21. 4 points

    Creating a mobile app for Android and iOS

    Phonegap has a lot of plugins, that let you access your phone's features. As a quick example, here's how you can make your phone purr: Add to your Phonegap project's config.xml : <plugin name="cordova-plugin-vibration" source="npm" spec="~2.1.6" /> Cool thing about Phonegap Desktop is, that that environment automatically downloads and installs the plugins. When you run the project in Phonegap Desktop, you'll notice how the new plugin appeared in the Plugins- folder. In Smart Mobile Studio, after the Phonegap has been initialized: PhoneGap.Notification.vibrate(1000); When you deal with Phonegap, make sure that you wait for that signal, that it's been properly initialized. The example I posted above expects that the form will be initialized before Phonegap is ready. In general, you should wait for the initialization once, while your app is starting and make sure to always check before calling Phonegap: if PhoneGap.Ready then PhoneGap.Notification.vibrate(1000);
  22. 4 points

    New forum software version

    Dear Smart community! we are proud to announce that we finished updating the software of our forums, as you might have noticed. If you encounter bugs, please let us know about them so that we can take care of them as soon as possible. Stuff like clearing notifications, editing profile settings, setting profile pics should work now without problems. Wishing you all the best!
  23. 4 points

    Pagespeed ranking 98/100

    Googles PageSpeed tool (https://developers.google.com/speed/pagespeed/insights/) checks any website or webapp on various speedfactors, and returns a rating both for desktop and mobile. Most of these factors are easy to implement. Just tick the relevant minifying, packing, obfuscation boxes in the "Compiler-Code generation" section of the Project Options. The PageSpeed tool also flags un-optimised images and even optimises them to download. The more difficult option is the rule about "prioritising visible content". This is obviously difficult to do in regular SMS projects. Both the CSS and JS files generated by the compiler don't make any distinction between 'above' or 'below' the fold. To circumvent this, the following works for me : Get the generated HTML code for the first form. Easiest is execute the project in the IDE, go to the "Console section" in DevTools and type 'copy(document.body.innerHTML);' This copies the generated HTML to the clipboard. Insert this code in the HTML template in the top of the <body> section Insert the keyword 'async' in the <script> section where the main.js file is loaded This basically quickly renders the first page in HTML only and the user will see it pretty quickly. It won't do anything until the js file has loaded but that's generally ok. CSS files, unless really really big, usually don't pose a problem as they can fit into the initial congestion window (typically 14.6kB compressed) Doing this gives me a 98/100 rating both for mobile and desktop. The remaining 2% is about 'leveraging browser caching' which doesn't bother me too much. Haven't checked yet what the sms manifest file options may do to this rule.
  24. 4 points

    simple game

    Hi, there is simple game i've written for my children. Hope you enjoy this game too probably a code is not mastery but it works guessing game for children.7z
  25. 4 points

    simple game

    Either that or support this great product