At FoxInCloud we see the Web as just another User Interface (UI) device. And no one should ever need to rewrite an application for another UI device, not even rewrite the UI, just do whatever provides a similar UI on the new device.

From this stand point, a desktop Application only needs be adapted to: (1) display the UI inside a browser and (2) run on a Web server.

This post discusses the 3 areas where code needs adaptation, and how FoxInCloud Adaptation Assistant (FAA) and FoxInCloud Application Server (FAS) handle them.

It provides a broad view on why and how adaptations are needed, and will hopefully ring a bell when you later meet such a situation while developing.


A - Map VFP and Browser event models

The event models implemented in VFP and Browsers (HTML DOM events to be precise) are mostly similar, though with some slight differences such as:

  • VFP events that don’t exist in HTML: eg. .Validate(), .When(), .DropDown(), …
  • HTML events bubble, VFP events don’t: by default in HTML, an event occurred in a contained element also triggers on all parent containers up to the document (equivalent for the form);
  • Fast events such as .MouseOver() and .InteractiveChange() can’t be timely processed on the server and require some browser-based code instead.

How FoxInCloud handles Events

1- Choose who processes the event: Browser, Server, or both

When running FAA step 2, after replacing native classes (such as Textbox, Combobox, etc.) by classes derived from FoxInCloud (such as xxxTxt, xxxCbo, etc.) FAA adds at the beginning of each Event Method:

IF (TYPE('m.thisForm.wlHTMLgen') == 'L' AND m.thisForm.wlHTMLgen)
  RETURN <value>
ENDIF

First time it needs a Form, FAS:

  1. sets m.thisForm.wlHTMLgen = .T.
  2. runs each event methods in all member objects of the form
  3. decides what to do based on the <value> returned by the RETURN <value> instruction above:

    • .T.: (default) run VFP event code on server
    • .F.: ignore event 1
    • string: assume JavaScript to be run on the browser

If necessary, thisForm.wcScriptClientServer() and thisForm.wcScriptServerClient() makes the event be processed by the browser and the server in either order.

» FoxInCloud Live Tutorial Sample

FoxInCloud Live Tutorial Sample: modal form

2- Handle event

Based on the above value, FAS creates a JavaScript event handler invoking FoxInCloud.js, a standard JavaScript module that any FoxInCloud Web page loads by default.

FoxInCloud.js collects whatever value from the form controls, processes the event, takes care of cancelling bubbling and/or default behavior such as context menu on .RightClick() and, if required, sends event to server using AJAX, for processing by the VFP app.

» FoxInCloud Live Tutorial Sample

FoxInCloud Live Tutorial Sample: events form


B - Replace local peripherals by the Browser

When running on the desktop, an app can directly address peripherals on the user’s machine: screen, printer(s), storage, etc.; when running on a server, app can only address the server’s resources, and reach the client’s machine through a protocol named ‘HTTP’, acronym for ‘HyperText Transfer Protocol’.

As its name says, ‘HTTP’ can only transfer text, without any ability to ‘discuss’ with user’s screen, printer or storage directly.

Furthermore, the server can only respond to an HTTP request from the browser, in other words it can’t initiate a message to the browser 2.

As a consequence, every piece of code where your application delivers a result to the user – either on display, printer of file – must be replaced by an equivalent mechanism going through the Browser.

How FoxInCloud Handles Peripherals

With FAA replacing the native classes by classes derived from FoxInCloud’s aw.vcx ‘base classes’, FoxInCloud exposes classes, methods and procedures that replace the VFP native command and functions addressing the peripherals. These methods and functions run different code whether the app. is in desktop mode or web mode:

  • desktop mode: execute regular VFP code
  • web mode: execute browser aware code
1- Display
  • DO FORM ...[thisForm.]wForm()
  • MessageBox()[thisForm.]wMessageBox()
  • etc.

FAA replaces these instructions automatically.

» FoxInCloud Live Tutorial Sample

FoxInCloud Live Tutorial Sample: modal form

2- File
  • PutFile()thisForm.wFileSaveAs()
  • GetFile() | GetPict()xxxImgGetFile | xxxImgGetPict class

» FoxInCloud Live Tutorial Sample

FoxInCloud Live Tutorial Sample: file upload form

3- Printer

Printing requires generating a PDF report and send it to the browser using wFileSaveAs()

As code depends on the PDF generator you prefer (eg. XFRX, FoxyPreviewer, FRX2Any, etc.), this adaptation is manual for now 3.

» FoxInCloud Live Tutorial Sample

FoxInCloud Live Tutorial Sample: reporting form


C - Address multiple users

When running on the desktop, an Application addresses one single user at a time; when running on a server conversely, the same application can address any user having the Web Application’s URL.

Because the Web the application is shared by a number of users, it must ensure that serving a user does not affect the other users:

  • never stops or hangs
  • cleans up the place after each user action
1- Split Questions and Answers into separate processes

One easy way to hang an application is waiting for an answer from the user:

procedure Click
...
IF MessageBox("Question", 4) = 6 && application hangs here
  <process user's choice>
ENDIF
ENDPROC

To avoid hanging the app. waiting for the answer – and this is the same for any Web Application – code needs be arranged as follows:

  1. user fires an event
  2. server asks a question and provides to client an address where to send the answer
  3. client sends the answer at the address specified
  4. answer is processed by code at the address specified

Using FoxInCloud, the above code becomes:

procedure Click
...
thisForm.MessageBox("wFormCallBack", "Question", 4) && code hangs in desktop mode only, not in Web mode
ENDPROC && Question is the last instruction in method

procedure wFormCallBack && Standard method provided by FoxInCloud 'base classes'
LPARAMETERS tuUserChoice
IF tuUserChoice = 6
  <process user's choice>
ENDIF

» FoxInCloud Live Tutorial Sample

Because the callback response processing code (above moved to .wFormCallBack()) can have dependencies outside the original method (such as a ‘private’ variable), picking a call-back method and moving the code is a manual adaptation; FAA identifies the methods requiring your attention.

2- Stop instructions execute in desktop mode only

Any instruction that stops application execution must be conditioned by desktop mode:

IF wlLAN() && function provided by FoxInCloud under General Public License
  QUIT
ENDIF
3- Save and restore the user’s state

Because a user action should not be influenced by what the previous user has done, the app should be able to swap context from one user to another.

FoxInCloud saves and restores the full user context automatically, provided 2 manual adaptations are performed:

  1. List the native properties (such as .Visible, .Enabled) affected by user action into a property that every object inherits from its FoxInCloud base class 4:
    .wcPropSave = "Visible, Enabled"
  2. Requery the parameterized views using thisForm.wViewParmSet() inherited from FoxInCloud base class;
    eg. with 3 parameters:
thisForm.wViewParmSet('viewAlias', 'parm1Name', parm1Value)
thisForm.wViewParmSet('viewAlias', 'parm2Name', parm2Value)
thisForm.wViewParmSet('viewAlias', 'parm3Name', parm3Value, .T.)
&& adding .T. on last call runs Requery('viewAlias')

FoxInCloud handles all other user state bits automatically.

» FoxInCloud Live Tutorial Sample

FoxInCloud Live Tutorial Sample: data updating form

4- Setup the full App environment at Server Startup

When running on the desktop, your application goes through predefined steps that you can easily predict: start, login form, menu, formA, formB (modal), back to formA, etc.

Conversely, because browser and server execution are asynchronous, user actions can come in any order, especially after a server restart: some user actions can affect the formB in the example above, while the calling formA is not yet running.

For this reason it is required that all application-wide settings like SET PROCEDURE, SET PATH, PUBLIC, etc., are made right at server startup rather than throughout the application code.

To help this adaptation, FAA step 1 identifies the corresponding instructions in the application code and step 3 builds an environment setting class xxxSets where you can safely move them.

The Desktop and Web versions of the Application should share this environment class:

modify command DesktopMain.prg

...
local oEnv
oEnv = newObject('xxxSets', 'xxxSets.prg')
...
do form mainForm
read events

» FoxInCloud Live Tutorial Sample

FoxInCloud Live Tutorial Sample: desktop main program

modify command xxxServer.prg && generated by FAA step 3

cAppSetsLib = 'xxxSets.prg'
cAppSets = 'xxxSets'

» FoxInCloud Live Tutorial Sample

FoxInCloud Live Tutorial Sample: Web Server class


Pretty simple, isn’t it?

When you take a fresh new look at what really makes a difference between a Desktop and a Web application, you can easily isolate a handful of specific areas where an adaptation is needed, while most of the application code can run unchanged in both environments.

And you soon realize that complex MVC and REST frameWorks 5 that current Web Technologies tend to present as ‘mainstream’ are just another attempt to make what desktop applications have (wrongly) done for decades: maintain the user state on the browser, with code mixed up with the UI layer.

Another caveat of these technologies is that most of the application code 6 is in a readable form that anyone can download and copy.

Hopefully this post has helped you understand that:

  • You can have the same code running on the desktop and the Web: test new functionalities on the desktop and later deploy on the Web, get desktop developers work on the Web Application rapidly;
  • Reasons for code adaptations are pretty simple to understand and keep in mind, and easy to practise in later app development;
  • FoxInCloud keeps all the sensitive code on the server, protected from your competitors;
  • The Web can be simple and fun!

  1. Typical use case is a ‘fast event’ such as .MouseEnter() or .MouseLeave() that you can override using a CSS ‘pseudo-class’ such as :hover 

  2. Even WebSockets (allowing full duplex communication between Browser and Server) require an initial handshake from the client browser 

  3. We plan to add standard functions for the most popular PDf generator: XFRX, FoxyPreviewer and FRX2Any 

  4. At design time or in .Init(), .Load() for forms 

  5. MVC client-side FW: jQuery, Bootstrap, angularJS, emberJS, ReactJS, vueJS, etc. – REST server-side F/W: asp.net, ruby-on-rails, django, telerik, flask, expressJS 

  6. Except the database layer 



Watch FoxInCloud Marketing Videos :: Watch FoxInCloud Technical Videos :: Stay tuned on FoxInCloud Roadmap :: Learn how to use FoxInCloud :: Download FoxInCloud Adaptation Assistant for free :: Download FoxInCloud Web Application Studio for free