Remote data persistence using cookies

Thinfinity VirtualUI: Remote data persistence using cookiesIn early May we started with a series of brief and simple, yet useful articles to help you take better advantage of the existing possibilities that Thinfinity® VirtualUI™ provides.

In the present note we will see a way to store remote data in the browser to be later retrieved from the application, when needed. Continue reading

Browser within the browser: breaking the Matryoshka effect.

When the browsers are nested.
image01

It has become increasingly common to expect desktop applications to be capable of integrating to the web to incorporate available external information. There are several ways this could be achieved: by consuming web services to get the raw data; or, by simply including a browser component within the application, in the form of a panel. In this second case, we’ll be capable of navigating to sites displaying up-to-date information or external multimedia content, such as maps, videos, images and sound —as an example, visit Fish Facts, a live demo that shows basic GUI virtualization capabilities as well as an integration with the web browser and external web resources.

Once we transform these applications with Thinfinity® VirtualUI™, and we invoke them from the web, an effect similar to the famous Russian Nested Dolls case occurs: we run from the browser an application that contains within itself another browser. In short, we surf to an application that internally re-surfs. This is not bad in itself, but we can take advantage of already being in the web to access the final resource directly from the main browser. By doing this we will avoid a roundabout that, among other things, would unnecessarily increase the consumption of resources.

Let’s take a look at the diagrams of the same application running from the desktop…

Matryoshka-image1

… and from the Web:

Matryoshka-image2

As we can see, when we run the application from the desktop we must necessarily navigate through an embedded Webbrowser component. The application is the one that connects to the desired web address, and this webpage is seen in an internal panel, within the application.

But once we load the application from a browser we would experience the aforementioned effect. As a result, the application browses on its own, and the visible result of that surfing is re-transferred to our browser.

If we transfer that navigation to the only browser we need to use, we will access the intended resource without delay or additional bandwidth, processing, or time consumption.

image00

The demo application

The application we use in the example (whose complete source can be found here) attempts to break this browser-within-the-browser effect, simply by using the internal browser only when the application is accessed from the desktop. If the application is being accessed from the web, instead of using that component, the app would load the desired page in an iframe created on the fly. To achieve this integration (which requires a little bit more interaction between the application and the browser) we will add a jsRO object, which will control the remote iframe from the application.

The first addition to our application will be the adding of VirtualUI and its initialization:

using System;
using System.Windows.Forms;

namespace matryoshka
{
   static class Program
   {
       /// <summary>
       /// The main entry point for the application.
       /// </summary>
       [STAThread]
       static void Main()
       {
           new Cybele.Thinfinity.VirtualUI().Start();
           Application.EnableVisualStyles();
           Application.SetCompatibleTextRenderingDefault(false);
           Application.Run(new Form1());
       }
   }
}

Let’s now see what we have to add to the main form code, which it only has a panel where to enter the browsing URL and another one where the WebBrowser component is located.

The following is the application code before adding the changes:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace matryoshka
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            // Sets Google Maps URL to run in the embedded browser
            txtURL.Text = "https://maps.google.com";
            Navigate();
        }

        private void btnGo_Click(object sender, EventArgs e)
        {
            Navigate();
        }

        private void txtURL_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (Convert.ToInt32(e.KeyChar) == 13)
            {
                Navigate();
            }
        }

        private void Navigate()
        {
            if (!txtURL.Text.Equals(""))
            {
                if (!txtURL.Text.StartsWith("http"))
                {
                    txtURL.Text = "http://" + txtURL.Text;
                }
                webBrowser1.Navigate(new Uri(txtURL.Text));
            }
            else {
                webBrowser1.Navigate(new Uri("about:_blank"));
            }
        }
    }
}

As a first step, we will add to the class the objects in the Cybele.Thinfinity library:

using System;
using System.Drawing;
using System.Windows.Forms;
using Cybele.Thinfinity;

namespace matryoshka
{
    public partial class Form1 : Form
    {

        private VirtualUI vui = new VirtualUI();
        private IJSObject mRemoteLayout = null;

        public Form1()
        ...

This demo application loads a Google Map. To be run embedded in an iframe, Google Maps needs a different URL from the one used on a full page. We know that it will run embedded in the iframe (using VirtualUI) and directly when loaded into the internal browser.

public Form1()
{
    InitializeComponent();
    if (vui.Active)
    {
        // Code executed only when the application runs with Thinfinity VirtualUI
        // Sets Google Maps URL to run embedded in an iframe
        txtURL.Text = "https://www.google.com/maps/embed";
    }
    else
    {
        //* Code executed only when the application runs from desktop 
        // Sets Google Maps URL to run in the embedded browser
        txtURL.Text = "https://maps.google.com";
    }
}

Additionally, we must also make some changes to Navigate( ):

private void Navigate()
{
    string sURL = "";
    if (txtURL.Text.Equals("")){
        sURL = "about:_blank";
    } 
    else
    {
        sURL = txtURL.Text;
        if (!sURL.StartsWith("http"))
        {
            sURL = "http://" + sURL;
        }
    }
    if (vui.Active) {
        mRemoteLayout.Properties["url"].AsString = sURL;
    }
    else {
        webBrowser1.Navigate(new Uri(sURL));
    }
} 

The last (but not least important) addition is the one concerning the jsRO object management: the first method, for its instantiation and definition of their getters; the other one, for the later updating of the iframe’s bounds.

private void Form1_Shown(object sender, EventArgs e)
{
    // Creates jsRO object for iframe control
    mRemoteLayout = new JSObject("layout");
    mRemoteLayout.Properties.Add("windowId").AsString =
                String.Format("virtualui_canvas_{0}", Handle);
    mRemoteLayout.Properties.Add("url").AsString = txtURL.Text;
    mRemoteLayout.Properties.Add("bounds")
        .OnGet(new JSBinding(        // Adds a "getter" to bounds
            delegate(IJSObject AObj, IJSProperty AProp)
            {
                // Returns a JSON object
                Point p = pnlNav.PointToScreen(new Point(0, 0));
                p.Offset(-Left, -Top);
                AProp.AsJSON = "{" + String.Format("\"left\":{0}, \"top\":{1}, \"width\":{2}, \"height\":{3}",
                     p.X, p.Y, pnlNav.Width, pnlNav.Height) + "}";
            }));
    mRemoteLayout.ApplyModel();
}

private void pnlNav_Resize(object sender, EventArgs e)
{
    // Updates all mRemoteLayout's properties "getters"
    if (mRemoteLayout != null)
    {
        mRemoteLayout.ApplyChanges();
    }
}

We can now introduce the additional information that must be included, at javascript level, to the HTML page.

What follows is the base page to which we will be adding the functionality:

 

We will now create three global variables for the VirtualUI and jsRO objects, and for the iframe created on the fly.

<script type="text/javascript">
var virtualUI = null;
var jsro = null;
var nav = null;

$(document).ready(function () {
    ...

We will add three functions intended to create the iframe and keep it updated:

...
$(document).ready(function () {

    var navigate = function(url) {
        nav.src = url;
    }

    var applyBounds = function(bounds) {
        nav.style.left = bounds.left + "px";
        nav.style.top = bounds.top + "px";
        nav.style.width = bounds.width + "px";
        nav.style.height = bounds.height + "px";
    }

    var createNav = function () {
        nav = document.createElement("iframe");
        nav.id = "nav";
        nav.style.position = "absolute";
        nav.style.display = "none";
        nav.style.zIndex = 2;
        nav.style.border = "none";
    }
    ...

We will now instantiate the VirtualUI object, configure its events, and then connect to the application:

var createNav = function () {
   ...
   ...
}

virtualUI = new Thinfinity.VirtualUI();

virtualUI.onError = function (errorMsg) {
    alert("Application load failed");
};

virtualUI.onLoading = function () {
    console.log((virtualUI.devMode) ? "Waiting for application..." : "Loading...");
};

virtualUI.onClose = function (url) {
    if ((typeof url != 'undefined') && (url != '') && (url != null)) {
        if ((virtualUI.devMode != true) || (window.top.opener)) {
           window.top.close();
        }
        window.top.location.href = url;
        return;
    }
    if (virtualUI.devMode) { location.reload(); }
    if ((virtualUI.devMode != true) || (window.opener)) { window.close(); }
    if ((window.top == window) && (document.referrer) && (location.href != document.referrer)) {
        location.href = document.referrer;
    } 
    else {
        if (nav) { nav.style.display = "none"; }
        alert("Application closed");
    }
};

// -- Connect to server...
virtualUI.connect();

Finally, we will create the jsRO object instance. Please note that it’s in the creation of the layout jsRO model (defined in the application) where the iframe is injected into the application window. The other two events are used to keep both the URL and the inserted iframe bounds updated:

...
virtualUI.connect();

// Defining Javascript Remote Objects Elements
jsro = new Thinfinity.JsRO();

jsro.on('model:layout', 'created', function (obj) {
    layout = jsro.model.layout;
    if (nav == null) {
        createNav();
        document.getElementById(layout.windowId).appendChild(nav);
        nav.style.display = "inline-block";
    }
    applyBounds(layout.bounds);
});

jsro.on('model:layout.url', 'changed', function (obj) {
    navigate(obj.value);
});

jsro.on('model:layout.bounds', 'changed', function (obj) {
    applyBounds(layout.bounds);
});

With this procedure, we’ve been able to break the “Matryoshka Effect”. From now on, the embedded browser will be used from the desktop, while the external one will be used from the web —but always being controlled by the remote application.

 

Running the application from the browser

To see the end result, we must first add the application to the list of registered apps in the Thinfinity VirtualUI Server and configure its home page, making sure it points to the page modified by us in this tutorial.

Once this is done, we can access the application from the browser. If we are running the application from the Thinfinity VirtualUI Development Lab, we must change the virtual path’ address so as to match the one defined in the configuration —in the VirtualUI’s manager.

 

How to manage and customize the homepage of a Thinfinity VirtualUI application

How to customize the homepage of a Thinfinity VirtualUI applicationWe continue providing hints and tips with the goal of helping you take full advantage of Thinfinity® VirtualUI™’s existing possibilities.

In the present note we will explain how to manage and customize the homepage of a Thinfinity VirtualUI application.

If we take a look at Thinfinity VirtualUI’s installation folder (by default the folder is “C:\Program Files\Thinfinity\VirtualUI\dev\web”) we will find, in the dev/web subfolder, a file labeled app.html. This file (actually, an html page) loads all of VirtualUI’s web-side components so that our application can be controlled from the browser.

The file’s default content is the following:

<!DOCTYPE html>
<html>
<head>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
   <meta name="apple-mobile-web-app-capable" content="yes" />
   <meta name="apple-mobile-web-app-status-bar-style" content="black" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, target-densityDpi=device-dpi" />
   <title>Thinfinity VirtualUI</title>
   <link rel="apple-touch-icon" href="images/icon.png" />
   <link rel="stylesheet" type="text/css" href="css/splash.css"/>
   <link rel="stylesheet" type="text/css" href="css/thinfinity.virtualui.css" />
   <link rel="stylesheet" type="text/css" href="css/print.dialog.css" />
   <script src="virtualui.sdk.min.js" type="text/javascript"></script>
   <script src="js/splash.js" type="text/javascript"></script>
   <script src="js/app.js" type="text/javascript"></script>
   <script src="https://www.google.com/cloudprint/client/cpgadget.js" type="text/javascript"></script>
</head>
<body style="background-color: #f2f2f2;">
     <div id="virtualui" style="position:absolute;display:none;">
     </div>
</body>
</html>

 
This page holds the basic content needed for VirtualUI to work, but it is nothing more than the default and basic page. We will now show how we can turn it into something different. This will be a small but very important first step to reach a full web-integration of our application.

 

Modifying the homepage

Modifying the application’s homepage is quite simple. All we have to do is the following:

The first step would be to create a copy of app.html and rename it to myapp.html.

After that, we should open the VirtualUI Server Manager. On the Applications tab we must select the application item, and then open the configuration dialog by clicking on Edit:

We must now set the home page, pointing to the new file, as shown in the image below:

Customizing a VirtualUI appplication homepage

We are now ready to modify the page and to see the changes from the browser. We could certainly change the basic layout of the page: its settings, for example; or select another color, or insert an image in the application background. More importantly, we can change the out of the box event handlers defined in the app.js file:

var virtualUI = null;
var splash = null;

$(document).ready(function () {
   splash = new Splash();
   virtualUI = new Thinfinity.VirtualUI();
   virtualUI.onError = function (errorMsg) {
       splash.show("Application load failed", errorMsg || "", false, false);
   };
   virtualUI.onLoading = function () {
       splash.show(
          (virtualUI.devMode)? "Waiting for application..." : "Loading...",
          "", true, false);
   };
   virtualUI.onShow = function () {
       splash.hide();
   };
   virtualUI.onClose = function (url) {
       // -- The url argument is used to allow to back
       // -- to specific page, when a virtualPath is used
       // -- and url is not null.
       if ((typeof url != 'undefined') && (url != '') && (url != null)) {
           // tries to close the window.top
           if ((virtualUI.devMode != true) || (window.top.opener)) {
               window.top.close();
           }
           window.top.location.href = url;
           return;
       }
       // if in devMode, reload the page
       if (virtualUI.devMode) {
           location.reload();
       }
       // tries to close the window/tab
       if ((virtualUI.devMode != true) || (window.opener)) {
           window.close();
       }
       // else returns to the calling page
       if ((window.top == window) && (document.referrer) && (location.href != document.referrer)) {
           location.href = document.referrer;
       } // else shows the splash
       else {
           splash.show("Application closed", "", false, false);
       }
   };
   // -- Connect to server...
   virtualUI.connect();
});

As we can see, handling the published events of VirtualUI API allows us to control each session step, from the connection to the disconnection. Each .on… event handler can be modified to do what we want. Therefore, we could customize the message, or reshape it to be displayed in a different way, simply by replacing the splash object.

 

Example: Tailoring the error handling

In the following example we will change the error handling, redirecting the message to a  new custom error page:

Create a copy of app.js and rename it to myapp.js.

Modify the link to the app.js file into myapp.html:

<script src="js/myapp.js" type="text/javascript"></script>

Edit the new myapp.js and modify the onError event handler:

var virtualUI = null;
var splash = null;

$(document).ready(function () {
  ...
  ...
  virtualUI.onError = function (errorMsg) {
     location.href = "oops.html?msg=" + encodeURIComponent(errorMsg || "");
  };
  ...
  ...
  // -- Connect to server...
  virtualUI.connect();
});

Finally, create the oops.html with the code below and add it to the web folder:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Thinfinity VirtualUI Demo - Oops!</title>
<style>
.errmsg {  width: 100%; height: 100%; text-align: center; }
.errmsg h1 { font-size: 8em; }
.errmsg h2 { font-size: 2em; color: #808080; }
.errmsg h3 { font-size: 1.5em; }
.errmsg h4 { font-size: 1.5em; color: #808080; }
</style>
<script type="text/javascript">
function getmsg() {
  var regexS = "[\\?&]msg=([^&#]*)";
  var regex = new RegExp(regexS);
  var results = regex.exec(window.location.href);
  delete regex;
  document.write((results) ? decodeURIComponent(results[1]) : "Unspecified error");
  document.close();
}
</script>
</head>
<body>
<div class="errmsg">
<h1>Oops!</h1>
<h2>An error occurs when connecting to the application:</h2>
<h3><script>getmsg()</script></h3>
<h4>Sorry, please try again later :(</h4>
</div>
</body>
</html>

This example can be almost trivial, but knowing how to replace or modify the app.html page can help you provide your end users a custom-made experience of your VirtualUI application.

 

Change browser behavior using ‘ClientSettings’

ClientSettings is an additional interface variable for Thinfinity VirtualUI LibraryIn our last post we started with a new notes and tips series aimed at helping developers take full advantage of Thinfinity® VirtualUI™’s existing possibilities.

In the present note, we will focus on ClientSettings.

ClientSettings is an additional interface available at the Thinfinity VirtualUI Library that allows developers to remotely and programmatically configure particular browser settings. These settings are related to the cursor visibility and some specific touch action behaviors.

We can find, in the table below, a detailed explanation of the ClientSettings interface’s current properties:

Continue reading