Chapter 35. Browser Integration

Table of Contents

1. Using the Developer Toolbar
2. A model of Browser/LZX application interaction
2.1. The "wrapper page"
2.2. Applications Compiled to SWF
2.3. Applications Compiled to DHTML
3. The lz.embed Class
4. Placing the LZX application in the HTML page
4.1. Placing LZX applications within <div>s
4.2. Detecting which flash player is running
5. Integrating with Frames
5.1. Frame Sets
5.2. Inline Frames
5.3. Popping and reusing browser windows
6. Passing startup Data to Embedded Applications
6.1. Query Parameters
6.2. Application state
7. Using the browser's "back" button
8. Scaling OpenLaszlo Applications to Browser Dimensions
9. Browser Limitations

OpenLaszlo applications run inside web browsers, and web browsers are designed to render HTML pages. This means that each compiled OpenLaszlo application has a relationship to the HTML page that delivers it to the browser. Depending on whether the OL application was compiled to SWF or to DHTML, the mechanics of this relationship differ. However, at an abstract level the model is pretty much the same for all runtimes: an OpenLaszlo application resides within a web page that is executed by a browser; that browser has a "JavaScript" engine. The OpenLaszlo application can make requests for the JavaScript engine to execute, and the surrounding HTML page can include JavaScript code that makes requests of the OpenLaszlo application.

This chapter discusses the way that OpenLaszlo Applications relate to the HTML pages in which they're delivered, and ways in which the OpenLaszlo application and browser JavaScript engine can communicate with each other.

1. Using the Developer Toolbar

In this chapter we'll be talking about the structure of HTML pages that contain LZX applications. Keep in mind that if you compile your applications using the Developer Toolbar, HTML pages of the proper structure are generated automatically. Situations may arise where you may need to customized these pages or even generate pages from scratch, but in many cases, the automatically generated page is all you need. Simply press the appropriate button, "Server" or "SOLO", and follow the instructions that are then displayed.

2. A model of Browser/LZX application interaction

Before going into the actual mechanics, let's take a closer look at concepts and terminology.

2.1. The "wrapper page"

As we said above, web browsers display HTML pages. This means that OpenLaszlo applications are delivered to browsers by means of HTML pages. Sometimes those HTML pages contain nothing visible to the user other than the LZX application itself. In such cases the HTML page is essentially invisible, and the fact that there's an HTML page there at all doesn't really matter to the developer or to the user of the application. Such HTML pages, whose only function is to present the OpenLaszlo application to the browser, are sometimes called "wrapper pages."

Depending on which target runtime they're compiled for, OpenLaszlo applications have different relationships with the JavaScript engine of the browser in which they run. To a rough approximation, LZX applications compiled to SWF are foreign objects embedded into an HTML page, and you must use a formal protocol to get across this logical barrier between the LZX application and the JavaScript engine. Applications compiled to DHTML become just a part of the HTML page that contains them. From the point of view of the browser, there is no difference between the OpenLaszlo application and the surrounding page.

But although some of the implementation details differ for the different runtime targets, the OpenLaszlo APIs are, wherever possible, consistent, so that you use the same programming model regardless of the target runtime. So, for example, there is a method called lz.embed.swf() for embedding SWF OL applications into the HTML page; this generates an HTML <embed> tag. Similarly, there is an lz.embed.dhtml() for placing an OL application compiled to DHTML within a web page. In this case there is no <embed> generated or required, but the methods have similar names because they perform analogous functions.

2.2. Applications Compiled to SWF

[Warning]
[SWF]

Applications compiled to SWF are executed by the Flash player and must make use of the lz.Browser service in order to "escape" to the browser's JavaScript environment.

Any attributes that are declared on the canvas are visible to the browser JavaScript. To go "the other way", from the browser "into" the OpenLaszlo application, the lz.embed['lzapp'].getCanvasAttribute() and lz.embed['lzapp'].setCanvasAttribute() methods are used, where 'lzapp' denotes the application ID.

These are discussed below.

Although OpenLaszlo applications deployed on the Flash runtime are not dependent on the browser or operating system for their look or behavior, there are some important limitations that the browser container places on the application. There are also a number of Flash Player features that your application can use which are not available in applications compiled to DHTML.

OpenLaszlo applications compiled to SWF can be sent from the OpenLaszlo Server to client in any of several forms:

  • as "naked" swf

  • as swf embedded in an html "wrapper" page

  • as xml source

2.2.1. The lz.Browser service

[Warning]
[SWF]

SWF only: The features described in this section only work in applications compiled to SWF. They do not work in applications compiled to other runtimes.

The lz.Browser service provides access to the browser and player environment. It includes methods to load URLs in the browser and check the version of the player. Furthermore, by using the lz.Browser service together with other OpenLaszlo functions described below, you can build applications that pass information and control between the OpenLaszlo application and the JavaScript of the browser.

The lz.Browser service is always running.

2.3. Applications Compiled to DHTML

[Warning]
[DHTML]

DHTML only: The features described in this section only work in applications compiled to DHTML. They do not work in applications compiled to other runtimes.

Applications that are compiled for DHTML run within the browser's JavaScript environment. They are essentially compiled into the DOM of that page.

Each LZX application has exactly one <canvas> element, which, when compiled to DHTML, becomes an object. When you embed an LZX application compiled to DHTML into an HTML page, it just becomes part of that page's DOM, and you can address it directly. The "foo" object of the LZX application becomes canvas.foo of the DOM. If there is a swf embedded in that (DHTML) Openlaszlo application, you can still use the mechanism for talking to SWF applications to reach it.

For DHTML, you can call canvas JavaScript directly because it is loaded in the browser. That is to say, unlike in the case of communicating with SWFs, you do not need to use getCanvasAttribute() or setCanvasAttribute(), nor do you need to use the lz.Browser service.

2.3.1. Embedding the LFC for DHTML applications

[Warning]
[DHTML]

DHTML only: The features described in this section only work in applications compiled to DHTML. They do not work in applications compiled to other runtimes.

When you compile an application to SWF, the compiler includes the Laszlo Foundation Class, or LFC in the resulting file. This does not happen for applications compiled to DHTML, therefore the LFC must be downloaded explicitly. To make the LFC available to DHTML applications, include the following call in the page head:

<script type="text/javascript">
    lz.embed.lfc('/lps-5.0.x/lps/includes/lfc/LFCdhtml.js', '/lps-5.0.x/'); 
</script>

3. The lz.embed Class

For both applications compiled to SWF and applications compiled to DHTML, the methods used for bidirectional communication with the browser are in the class lz.embed.

Unlike other OpenLaszlo classes which you use to build LZX applications, the lz.embed is designed to be incorporated into the HTML page in which your LZX application runs. That is to say, your LZX application is designed to be executed in the <body> section of the page, but the lz.embed must be include in the <head> section of the page.

The lz.embed class is defined in

    lps/includes/source/embednew.js

The source for embednew.js is shipped compressed as

    lps/includes/embed-compressed.js.

The comments/API documentation are in the source file and the class is documented in the Reference Manual.

To have access to the lz.embed class, your application must include a line like

<script type="text/javascript"
    src="/lps-5.0.x/lps/includes/embed-compressed.js"></script>

If you generate wrappers using the Developer Toolbar, the correct <script> tag will be generated for you automatically.

Remember, if you compile your application using the Developer Toolbar, the wrapper pages generated include the proper calls to the lz.embed class. It "just works" and you don't have to do anything special. If you craft your HTML pages "by hand", of course, you will need to make sure that you incorporate the lz.embed class.

4. Placing the LZX application in the HTML page

When you place a OpenLaszlo application inside an HTML page, that page has to have some way to know where to place the application, and how to handle it. That is, you must inform the browser that the embedded OpenLaszlo application is actually a SWF file, and so forth.

In the <html><head> of an HTML document that embeds an OpenLaszlo application compiled to SWF, add this line:

Example 35.1. The lz Class

<script src="/lps-5.0.x/lps/includes/embed-compressed.js" language="JavaScript" type="text/javascript"></script>

[Warning]
[SWF]

At the location within the <html><body> where the application is to be embeded, add this line:

Example 35.2. Placing the LZX (SWF) application within an HTML page

<script language="JavaScript" type="text/javascript">
  lz.embed.swf({url: url + '?lzt=swf&lzr=swf8',
    bgcolor: '#000000', width: '100%', height: '100%'});
</script>

where the url matches the URI that the application is served from, and the other properties match the attributes of the application's canvas.

[Warning]
[DHTML]

At the location within the <html><body> where the application is to be embeded, add this line:

Example 35.3. Placing the LZX (DHTML) application within an HTML page

<script language="JavaScript" type="text/javascript">
  lz.embed.dhtml({url: url + '?lzt=object&lzr=dhtml',
    bgcolor: '#000000', width: '100%', height: '100%'});
</script>

where the url matches the URI that the application is served from, and the other properties match the attributes of the application's canvas.

4.1. Placing LZX applications within <div>s

The optional appenddivid property can be passed in to lz.embed.swf() or lz.embed.dhtml() method to specify the id of a div whose contents should be replaced with the LZX application. If a div with that ID doesn't exist, one will be written into the document where lz.embed.{swf,dhtml}() is called. If appenddivid is not specified, the div written will be based on the id property + 'Container'.

Alternatively, you can generate wrappers by using the various request types on the url you use to browse to your application, as explained in Chapter 49, Understanding Compilation:

  1. Invoke the lzx application with request type "?lzt=html". This generates the "wrapper."

  2. Using your browser's "View source" function, copy the contents of the wrapper and place in a new file with a .html extension.

  3. In the head of the HTML file, you will see the script tag that includes embed-compressed.js Adjust the path if necessary for deployment.

  4. Paste to wherever you want the OpenLaszlo application to appear in HTML page

  5. Verify by running HTML file in your browser.

4.2. Detecting which flash player is running

The default wrapper page contains logic to detect the version of flash running on the client.

Depending on which version is running, you may wish to use some LZX features that run in some, but not all, versions of the Flash Player.

4.2.1. Version detection

OpenLaszlo uses client-side player detection. If the user either a) doesn't have the Flash Player plug-in or b) has an older version, they'll be prompted to download a fresh version. This feature is included in the default HTML wrappers for lzt=html and the SOLO deployment wizard

5. Integrating with Frames

Frames are a very handy way to use OpenLaszlo applications within a page. If you are using a OpenLaszlo application for your navigation or as a widget in your page you might consider using frames to format your layout.

5.1. Frame Sets

When laying out a page that will use OpenLaszlo applications in different places, you can use a frameset like this:

Example 35.4. example frameset

<html>
  <frameset cols="128,*">
    <frame name="navBar" src="myOpenLaszloNav.lzx?lzt=html"/>
    <frame name="contentArea" src="myOpenLaszloContent.lzx?lzt=html"/>
  </frameset>
</html>

Inside your application use the target parameter of lz.Browser.loadURL() to load pages in the target frame.

Example 35.5. target in loadURL

lz.Browser.loadURL('http://www.openlaszlo.org','contentArea');

5.2. Inline Frames

Like framesets, OpenLaszlo applications can be embedded within an "inline frame" or <iframe> which can be more flexible for your layout and easier to use. Like regular frames, inline frames can be named for later reference by the OpenLaszlo application.

Example 35.6. iframes and applications

<html>
  <body>
    <h1>Here is a header</h1>
    <div align="center">
      <iframe src="myOpenLaszloApplication.lzx?lzt=html" width="200"
        height="200" frameborder="0" name="inlineApplication"
        scrolling="no"/>
    </div>
  </body>
</html>

Bear in mind that the iframe will contain a canvas, and unless you want the browser to provide scrollbars, you should set the width and height equal to that of your canvas plus any margin you may have included.

5.3. Popping and reusing browser windows

The same way you would target content to a frame, you can target the loadURL to new window.

Example 35.7. new blank browser window

<canvas width="100%" height="27">
  <button onclick="lz.Browser.loadURL('http://www.openlaszlo.org','_blank');" text="Click Me!"/>
</canvas>

This mechanism can also be used to name the window allowing subsequent loads to occur in the same window

Example 35.8. reusing new browswer window

<canvas width="100%" height="56">
  <simplelayout/>
  <button onclick="lz.Browser.loadURL('http://www.openlaszlo.org','newOpenLaszloWindow');" text="OpenLaszlo Homepage"/>
  <button onclick="lz.Browser.loadURL('http://forum.openlaszlo.org','newOpenLaszloWindow');" text="OpenLaszlo Forum"/>
</canvas>

6. Passing startup Data to Embedded Applications

Any query parameters that you add to the end of your url to load the application are available through the lz.Browser.getInitArg() method.

Example 35.9. Passing query parameters to LZX application

<script type="text/javascript">
  // pass 'myData' with value 'myValue' to the application
  lz.embed.swf({url: 'mainApplication.lzx?lzt=swf&myData=myValue', bgcolor: '#394660', width: 1024, height: 768});
</script>

6.1. Query Parameters

Once the data has been passed in you can simply access it by calling getInitArg() from within the OpenLaszlo application.

Example 35.10. Accessing parameters

<canvas debug="true"
    oninit="Debug.debug("%w", lz.Browser.getInitArg('myData'))"/>

In many cases you will have full control of the HTML content that is embedding your application. In many other cases you won't be able to depend on the existence of those query parameters so it is wise to provide sane defaults for those values:

Example 35.11. setting defaults

<canvas debug="true" width="100%" height="200">
  <attribute name="onceData" value="$once{lz.Browser.getInitArg('myData')}"/>
  <handler name="oninit">
    var innerVar = lz.Browser.getInitArg('myData');
    if (innerVar == null) innerVar = 'bar'; // default to 'bar'
    Debug.debug("%w", canvas.onceData);
    Debug.debug("%w", innerVar);
  </handler>
</canvas>

The above application is requested without a myData query parameter. The debugger therefore displays the default value bar. If this application is requested with the myData=foo query parameter, it will display the value foo. Click here to see the application in a popup window with the query parameter applied.

6.2. Application state

Using query parameters, you can craft your application to start up in a specific state. The default behavior of a tabslider is to have none of the tabelements opened at startup. The following application changes that default to open to the tab specified on the query parameter, and if one isn't provided, open to the first element.

Example 35.12. setting defaults

<canvas width="100%" height="250">
  <tabslider width="150" x="10" y="10" height="200" spacing="2" slideduration="300">
    <handler name="oninit">
      var opened = lz.Browser.getInitArg('tab') || 'one';
      this[opened].setAttribute('selected', true);
    </handler>
    <tabelement name="one" text="Tabelement One"/>
    <tabelement name="two" text="Tabelement Two"/>
    <tabelement name="three" text="Tabelement Three"/>
  </tabslider>
</canvas>

Click here to see this application in a popup with a different initial state applied. This is useful when creating links from outside the application, or for creating a bookmark for an application. Another thing to do is read browser cookies from JavaScript and pass in the parameters when calling the application.

7. Using the browser's "back" button

The lz.History service allows you to preserve the state of your application, and then use the "back" button of the browser to move among such states.

The default wrapper page enables the back button and browser-to-LZX communication. Once you've got this, you can call the browser javascript method

javascript:lz.embed.setCanvasAttribute(attributeName, value)

to set a canvas attribute and send an event. The following example shows the way the lz.History service works to move among states. To see the program in action, follow this link to open a popup window.

Example 35.13. history enabled application

<canvas history="true">
  <!-- Click on blob START, STATE_2, STATE_3, END, then click browser back button -->

  <!-- a blob you can click on to select -->
  <class name="blob" extends="drawview" width="100" height="100">
    <attribute name="title" type="text"/>
    <attribute name="lineStyle" value="black" type="color"/>
    <attribute name="lineWidth" value="4" type="number"/>
    <text align="center" valign="middle" fontsize="16" text="${parent.title}"/>
    <handler name="onclick">
      main.selectBlob(this);
    </handler>
    <method name="select" args="val">
      clear();
      rect(2, 2, 96, 96, 5);
      if (val) {
        // draw a border to select
        stroke();
      }
      fill();
    </method>
  </class>

  <!-- A view with four states -->
  <view id="main" x="10" y="10" layout="axis: x; spacing: 10">
    <attribute name="mystate" type="string" value="START"/>
    <blob fillStyle="#ffcccc" name="s1" title="START"/>
    <blob fillStyle="#ccccff" name="s2" title="STATE_2"/>
    <blob fillStyle="#ccffcc" name="s3" title="STATE_3"/>
    <blob fillStyle="#cccccc" name="s4" title="END"/>

    <method name="selectBlob" args="blob">
      var oldstate = this.mystate;
      var newstate = blob.title;
      if (oldstate != newstate) {
        // save the old state
        lz.History.save('main', 'mystate', oldstate);
        // create new history context
        lz.History.next();

        setAttribute('mystate', newstate);
      }
    </method>

    <!-- update blobs' appearance -->
    <handler name="oninit" method="gotoState"/>
    <handler name="onmystate" method="gotoState"/>
    <method name="gotoState" args="ignore=null"><![CDATA[
      for (var i = subviews.length - 1; i >= 0; --i) {
        var blob = subviews[i];
        blob.select(blob.title == this.mystate);
      }
    ]]></method>
  </view>
</canvas>

8. Scaling OpenLaszlo Applications to Browser Dimensions

You can make the canvas of the OpenLaszlo application either a fixed size, or you can make it expand or contract as the browser window expands or contracts. To make the canvas an absolute size, set its height and width with integer values, for example, width="40" height="40". To have the canvas scale, simply express the height and width as percentages.

9. Browser Limitations

Intercepting keystrokes can pose some challenges. The keys that are intercepted varies from browser to browser, but anything that the browser defines will not be passed to your OpenLaszlo application. Mozilla Firefox seems to do this correctly, but many browsers won't pass alt and ctrl key combinations down to the application. The application below should demonstrate this, [Ctrl-n] in many browsers is the command you use to open a new browser window. In Mozilla Firefox, you can get the debugger to print the right message, but only if your mouse is floating over the application.

Example 35.14. Intercepting keystrokes

<canvas height="130" debug="true">
    <command key="$once{['Control','n']}" onselect="Debug.debug('the [Ctrl-n] key combination was pressed');"/>
</canvas>