Chapter 32. Browser Integration

Table of Contents

1. Scaling OpenLaszlo Applications to Browser Dimensions
2. The LzBrowser service
3. Embedding OpenLaszlo Applications
3.1. Embedding OpenLaszlo Applications in HTML pages
3.2. The <embed> tag
3.3. Detecting which flash player is running
3.4. How to specify the target
3.5. Using LzEmbed()
4. Page design and layout
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 and Global Variables
6.2. Application state
7. Supporting Multiple Resolutions
8. Communicating with Javascript
8.1. Bidirectional Communication
8.2. Passing Parameters to SOLO applications
9. Using the browser's "back" button
10. Browser Limitations

Though OpenLaszlo applications 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 features that your application can use.

1. 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.

[Note] Note

As of OpenLaszlo release 3.0, the Flash 5 target is no longer supported. Applications can be compiled for the Flash 6 or Flash 7 player. The default target is Flash 6.

2. The LzBrowser service

The LzBrowser 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 LzBrowser 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.

As you explore examples in this chapter you should take the opportunity to explore the facilities provided by this service. The Lzbrowser is always running.

3. Embedding OpenLaszlo Applications

OpenLaszlo applications are .swf files sent from the OpenLaszlo Server to be rendered in the Flash Player. They can be from the OpenLaszlo Server to client:

  • as "naked" swf

  • as swf embedded in an html "wrapper" page

  • as xml source.

See the Request Types chapter of the Deployer's Guide for discussion of the various ways to request applications.

In this section we'll look at ways to include OpenLaszlo applications in HTML pages. This means, in effect, that the HTML page invokes the Flash player, and the player runs the OpenLaszlo application.

3.1. Embedding OpenLaszlo Applications in HTML pages

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. Necessary information is included in a file called embed.js, so you need to place a reference to that file within a <script> tag in the head of the HTML file. In particular, this filed defines the function LzEmbed.

You can, of course, put this information into an HTML file "by hand", but the easier way is to use the OpenLaszlo Server to generate a "wrapper" page that contains all the necessary information. You can edit this wrapper or paste its contents into an existing HTML page The steps below show how to do this.

The easiest way cause the OpenLaszlo Server to generate the appropriate wrapper pages, for both SOLO and proxied applications, is to use the Developer's Console. Simply press the appropriate button, "Server" or "SOLO", and follow the instructions that are then displayed.

Alternatively, you can generate wrappers by using the various request types on the url you use to browse to your application:

  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.js Adjust the path if necessary for deployment.

  4. take lzEmbed method call including surrounding script tags copy that

  5. paste to wherever you want the OpenLaszlo application to appear in HTML page

  6. verify by running HTML file in your browser.

Consider the following simple OpenLaszlo application. (For now, pay no attention to the onreset method; we'll see it used later.):

Example 32.1. Hello Grace!

<canvas height="200" debug="true" proxied="false">
  <attribute name="reset"/>
  <handler name="onreset">
    redview.unfade.dostart()
    Debug.write('Reset Button clicked')
  </handler>
  <debug y="100"/>
     <view name="redview" bgcolor="red" height="30" width="150" clip="true">
       <animator name="fade" attribute="opacity" to=".10" duration="2000"/>
       <animator name="unfade" attribute="opacity" to="1" duration="2000" start="false"/>
       <text x="15"> Hello Grace!
         <animator name="wrapper" attribute="x" to="150" duration="2000"/>
       </text>
     </view>
</canvas>

The wrapper page returned with by invoking it with the ?lzt=html type returns the following wrapper:

Example 32.2. Simple wrapper page

<!DOCTYPE html
  PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   
      <link rel="SHORTCUT ICON" href="http://www.openlaszlo.org/favicon.ico">
      <title>OpenLaszlo Application</title><style type="text/css">
      html, body { margin: 0; padding: 0; height: 100%; }
      body { background-color: #ffffff; }
    </style><script src="/lps-dev/lps/includes/embed.js" type="text/javascript"></script></head>
   <body><script type="text/javascript">
          lzEmbed({url: 'graceie.lzx?lzt=swf', bgcolor: '#ffffff', width: '500', height: '200'});
        </script></body>

</html>

Later on in this chapter we'll show how to modify this wrapper to enable more sophisticated applications.

3.2. The <embed> tag

To embed OpenLaszlo applications in HTML pages, you use <embed> tag. The mechanics of this can be handled automatically as we have seen above. For your reference, here's a sample embed object:

Example 32.3. The embed tag

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
        codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,79,0"
        width="705"
        height="80">
  <param name="movie" value="OpenLaszloapplication.lzx?lzt=swf">
  <param name="quality" value="high">
  <param name="scale" value="noscale">
  <param name="salign" value="LT">
  <param name="menu" value="false">
  <param name="bgcolor" value="#394660">
  <embed src="OpenLaszloapplication.lzx?lzt=swf" 
         scale="noscale" 
         salign="lt" 
         width="705" 
         height="80" 
         bgcolor="#394660" 
         type="application/x-shockwave-flash" 
         pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash">
  </embed>
</object>

3.3. Detecting which flash player is running

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

This page that will look something like the following:

Example 32.4. Flash player version-detecting wrapper

<!DOCTYPE html
  PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   
      <link rel="SHORTCUT ICON" href="http://www.laszlosystems.com/favicon.ico">
      <title>OpenLaszlo Application</title><style type="text/css">
      html, body { margin: 0; padding: 0; height: 100%; }
      body { background-color: #ffffff; }
    </style><script language="JavaScript1.1" src="/lps-dev/lps/includes/vbembed.js" type="text/javascript"></script><script src="/lps-dev/lps/includes/embed.js" type="text/javascript"></script></head>
   <body><script type="text/javascript">
          actualVersion = detectFlash();
          requiredVersion = 6;
          if (isIE && isWin || actualVersion >= requiredVersion) {
            lzEmbed({url: 'graceie.lzx?lzt=swf', bgcolor: '#ffffff', width: '500', height: '200'}, requiredVersion);
          } else {
            document.write('This application requires Flash player ' + requiredVersion + '. <a href="http://www.macromedia.com/go/getflashplayer" target="fpupgrade">Click here</a> to upgrade.');
          }
        </script></body>

</html>

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.

3.3.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

3.3.2. Features available only in Flash 7 or later

The following features are available only in the Flash 7 or later environments:

  • Support for the html <img> tag

  • Support for context ("right-click") menus

3.4. How to specify the target

The query parameterlzr identifies which version is to be issued. Use lzr=swf6, lzr=swf7, or lzr=swf8 accordingly.

Alternatively you can compile your applications using the lzc command. Use the --runtime= argument to specify the target, for example,

        lzc myfile.lzx --runtime=swf7

3.5. Using LzEmbed()

LZX provides a number of helpful javascript routines to simplify the process of embedding LZX applications into your web pages. The most common of these is lzEmbed, which easily specifies the application to include and any request parameters that your application relies on. LzEmbed is in /3.3.3/lps/includes/embed.js.

Example 32.5. Using LzEmbed

<script type="text/javascript">
lzEmbed({url: 'header.lzx?lzt=swf', bgcolor: '#394660', width: '705', height: '80'});
</script>

4. Page design and layout

<div> tags can be used to place applications into various places on your page, for example to align your application in the center of a region, use:

Example 32.6. Using <div> tags to align applications

<div align="center">
<script type="text/javascript">
  lzEmbed({url: 'header.lzx?lzt=swf', bgcolor: '#394660', width: '705', height: '80'});
</script>
</div>

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 32.7. 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 LzBrowser.LoadURL() to load pages in the target frame.

Example 32.8. target in loadURL

LzBrowser.loadURL('http://www.laszlosystems.com','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 32.9. 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 32.10. new Blank window

<canvas height="27" >
  <button onclick="LzBrowser.loadURL('http://www.laszlosystems.com','_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 32.11. new Blank window

<canvas height="56" >
  <simplelayout/>
  <button onclick="LzBrowser.loadURL('http://www.openlaszlo.org','newOpenLaszloWindow');" text="OpenLaszlo Homepage"/>
  <button onclick="LzBrowser.loadURL('http://www.mylaszlo.com','newOpenLaszloWindow');" text="myLaszlo.com"/>
</canvas>

6. Passing startup Data to Embedded Applications

Any query parameters that you add to the end of your url to load the application get passed into the application as globals.

Example 32.12. Passing query parameters to LZX application

<script type="text/javascript">
lzEmbed({url: 'header.lzx?lzt=swf&globalVar=foo', bgcolor: '#394660', width: '705', height: '80'});
</script>     

6.1. Query Parameters and Global Variables

Once the data has been passed in you can simply access it as a global variable from within the OpenLaszlo application.

Example 32.13. Accessing parameters

<canvas debug="true" oninit="Debug.write(globalVar)"/>       

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 32.14. Setting defaults

<canvas debug="true" width="200" height="200">
  <script>
    var innerVar = typeof(global.globalVar) != 'undefined' ? global.globalVar : 'bar';
    Debug.write(global.innerVar);
  </script>
</canvas>

The above application is requested without a globalVar query parameter. The debugger therefore displays the default value bar. If this application is requested with the globalVar=foo query parameter, it will display the default value 'bar'. 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 32.15. Restoring state

<canvas width="300" height="250">
  <script>
    var opened = typeof(global.tab) != 'undefined' ? global.tab : 'one';
  </script>
  <tabslider width="150" x="10" y="10"
             height="200"
             spacing="2" slideduration="300" oninit="this[global.opened].setAttribute('selected',true)">
    <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 LzEmbed().

7. Supporting Multiple Resolutions

Most web surfers have discovered the merits of high resolution displays. Some, though, still view the web using very low resolutions. OpenLaszlo applications can be written in a way that allows for multiple resolution versions of the same application to coexist using the same codebase.

Begin by creating your application in a library file instead of a canvas, but use $once{} constraints to size the visual elements relative to the canvas.

Example 32.16. Multi-canvas application library

mainApplication.lzx:
<library>
  <tabslider width="$once{canvas.width - 20}" x="10" y="10"
             height="$once{canvas.height - 20}"
             spacing="2" slideduration="300">
    <tabelement name="one" text="Tabelement One"/>
    <tabelement name="two" text="Tabelement Two"/>
    <tabelement name="three" text="Tabelement Three"/>
  </tabslider>
</library>

Then create multiple files, one for each canvas area you wish to support.

Example 32.17. Multi-canvas application files

mainApplication640.lzx:
<canvas width="640" height="480">
  <include href="mainApplication.lzx"/>
</canvas>

mainApplication800.lzx:
<canvas width="800" height="600">
  <include href="mainApplication.lzx"/>
</canvas>

mainApplication1024.lzx:
<canvas width="1024" height="768">
  <include href="mainApplication.lzx"/>
</canvas>

mainApplication1280.lzx:
<canvas width="1280" height="1024">
  <include href="mainApplication.lzx"/>
</canvas>

For real world deployments, you will want to set the size of your canvas to account for screen area occupied by browser controls. Selecting the correct application size requires some javascript to pick the right application when the visitor comes to the page. This example keys off the screen width which will help you determine with high accuracy the height of the client as well.

Example 32.18. Using LzEmbed() to Selecting the Correct Canvas

<script type="text/javascript">
var screenW = screen.width;
var screenH =0;
switch (true) {
  case (screenW >= 1280):
    screenW = 1280;
    screenH = 1024;
    break;
  case (screenW >= 1024):
    screenW = 1024;
    screenH = 768;
    break;
  case (screenW >= 800):
    screenW = 800;
    screenH = 600;
    break;
  default:
    screenW = 640;
    screenH = 480;
    break;         
}
lzEmbed({url: 'mainApplication'+screenW+'.lzx?lzt=swf', bgcolor: '#394660', width: screenW, height: screenH});        
</script>  

8. Communicating with Javascript

It is possible to pass initialization arguments in by adding arguments to the lzEmbed() call in the wrapper HTML or by adding them to the query string when you request your app from the server.

If you define attributes on the canvas with a corresponding name and no value, these attributes will be set when your application starts up.

For example, adding this in the canvas of your app:

        <attribute name="foo" />

Allows the value of foo to be passed in via lzEmbed in the HTML, e.g.:


    lzEmbed({url: 'grad_test.lzx?lzt=swf&foo=bar', bgcolor: '#ffffff', width: '800', height: '600'});

would cause the foo attribute to contain 'bar' when the application starts up.

8.1. Bidirectional Communication

The LzHistoryservice allows you to establish bidirectional communication between the browser and the OpenLaszlo application. When you deploy your OpenLaszlo application with the the default wrapper,it includes the javascript functions that enable this.

After the application has been loaded, the methods of communication are as follows:

  • From OpenLaszlo application to browser, use the loadURL() of the LzBrowser service

  • From browser to the OpenLaszlo application, use LzSetCanvasAttribute()

Any attributes that are declared on the canvas are visible to the browser javascript. You use the function lzSetCanvasAttribute to pass information from the browser to the application.

Here's a wrapper page that uses these methods to communicate with the "Hello Grace" example from the beginning of this chapter. When you click the button, it sets the global variable called 'reset' in the OpenLaszlo application.

Example 32.19. bidirectional application wrapper


<!DOCTYPE html
  PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   
      <link rel="SHORTCUT ICON" href="http://www.laszlosystems.com/favicon.ico">
      <title>OpenLaszlo Application</title><style type="text/css">
      html, body { margin: 0; padding: 0; height: 100%; }
      body { background-color: #ffffff; }
    </style><script src="/lps-dev/lps/includes/embed.js" type="text/javascript"></script></head>
   <body><script type="text/javascript">
            lzEmbed({url: 'graceie.lzx?lzt=swf&__lzhistconn='+top.connuid+'&__lzhisturl=' + escape('/lps-dev/lps/includes/h.html?h='), bgcolor: '#ffffff', width: '500', height: '300'});
            lzHistEmbed('/lps-dev');
        </script>
    <form>
    <input type="button" onclick="lzSetCanvasAttribute('reset', true)" value="Reset"/>
</form>
</body>
</html>

8.1.1. Example: building a URL

In this example, notice how the LzBrowser.loadURL()() method is used to create a URL based on user input. This URL is then communicated to browser, where a javascript function is used to unpack it and fill in a form. The wrapper page is shown immediately below.

<canvas width="200" height="400">
    <attribute name="appear" type="number" value="0" />
    <handler name="onappear">
        win.show.doStart();
    </handler>

    <window id="win" title="Create and Account" 
            y="-300"
            width="180" height="300">
        <animator name="show" attribute="y" from="-300" to="50" 
                  start="false" relative="false" duration="500" />
        <animator name="hide" attribute="y" from="50" to="-300" 
                  start="false" relative="false" duration="500" />
        <simplelayout axis="y" spacing="4" />
         
        <state name="question" apply="true">
            <text multiline="true" width="160" align="center">
                It looks like you don't have an account. Would you 
                like to create one now?
            </text>
            <button>Go
                <handler name="onclick">
                    parent.question.remove();   
                    parent.signup.apply();
                </handler>
            </button>
        </state>
        <state name="signup">
            <text>Name:</text>
            <edittext />
            <text>Email:</text>
            <edittext />
            <text>Username:</text>
            <edittext name="uname"/>
            <text>Password:</text>
            <edittext name="pass" />
            <button>Create Account
                <handler name="onclick">
                    var s = "javascript:fillFormValues('"
                          + parent.uname.getText()
                          + "', '" + parent.pass.getText()
                          + "')";
                    LzBrowser.loadURL(s);
                    parent.hide.doStart();
                </handler>
            </button>
        </state>
    </window>    
</canvas>

Here is a sample wrapper that was generated by

  1. running the program with the ?lzt=history query

  2. saving the wrapper by copying from "view source" and pasting to .html file

  3. editing the wrapper with the logic for the button and unpacking name and password

Example 32.20. Wrapper for login example application

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>Login Form</title>
    <script type="text/javascript" src="/lps-3.0b2/lps/includes/embed.js"></script>
    <script>
        function fillFormValues( uname, pass ) {
            document.forms[0].u.value = uname;
            document.forms[0].p.value = pass;
        }
    </script>
</head>
<body>
<h1>Login Form</h1>

<table>
<tr>
    <td width="200">
        <p>
            Please enter your username and password below. If you don't have a username and password, you will be prompted to create an account.
        </p>
        <form name="f" action="" onsubmit="lzSetCanvasAttribute('appear', 1); return false; return false">
            Username:<br>
            <input name="u" type="text"><br>
            Password<br>
            <input name="p" type="password"><br>
            <input type="submit" value="Log In">
        </form>
    </td>
    <td>
    
            <script type="text/javascript">
lzEmbed({url: 'createaccount.lzx?lzt=swf&__lzhistconn='+top.connuid+'&__lzhisturl=' + escape('/lps-3.0b2/lps/includes/h.html?h='), bgcolor: '#ffffff', width: '200', height: '400'});
            lzHistEmbed('/lps-3.0b2');
          </script>
    </td>
</tr>
</table>

</body>
</html>

8.2. Passing Parameters to SOLO applications

If you are deploying a SOLO application and wish to pass parameters down to the application from the base URL, you need to make some modifications to the stock html wrapper page that the server provides.

Here is an lzEmbed line that passes all of the query parameters down to the Laszlo app undamaged:

lzEmbed({url: 'main.lzx.swf?'+window.location.search.substring(1), bgcolor: '#ffffff', width: '100%', height: '100%'});

The thing that's different is the alteration to main.lzx.swf? from main.lzx?lzt=swf and the addition of '+window.location.search.substring(1)'

9. Using the browser's "back" button

The LzHistory 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:lzSetCanvasAttribute(attributeName, value)

to set a canvas attribute and send an event. The following example shows the way the LzHistory service works to move among states. To see the program in action, copy this code to your applications directory.

Example 32.21. Communicating with Back button

<canvas width="1400" height="600" debug="true">
  <debug width="500" height="500" fontsize="12" x="450"/>

  <!-- 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="selected" type="boolean" />
    <text y="20" x="4" fontsize="16" text="${parent.title}"/>
    <handler name="onclick">
      this.setAttribute('selected', true);
      main.selectBlob(this);
    </handler>

    <method name="select" args="val">
      if (val) {
      // draw a border to select
        this.lineStyle = 0x000000;
        this.lineWidth = 4;
        moveTo(0,0);
        lineTo(0,100);
        lineTo(100,100);
        lineTo(100,0);
        lineTo(0,0);
        stroke();
      } else {
          clear();
      }
    </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 bgcolor="#ffcccc" id="s1" title="START"/>
    <blob bgcolor="#ccccff" id="s2" title="STATE_2"/>
    <blob bgcolor="#ccffcc" id="s3" title="STATE_3"/>
    <blob bgcolor="#cccccc" id="s4" title="END"/>

    <method name="unselectAll">
      s1.select(false);
      s2.select(false);
      s3.select(false);
      s4.select(false);
    </method>

    <method name="selectBlob" args="blob">
       // save the old state
     LzHistory.next();
      setAttribute('mystate', blob.title);
      unselectAll();
      blob.select(true);
      recordState(blob.title);
    </method>

    <handler name="oninit">
      unselectAll();
      recordState("START");
      selectBlob(s1);
    </handler>

    <handler name="onmystate">
      Debug.write("got onmystate event: ", this.mystate);
      gotoState(this.mystate);
    </handler>

    <method name="recordState" args="newstate">
      this.mystate = newstate;
      LzHistory.save(this,'mystate', this.mystate);
      Debug.write("recordState", newstate)
    </method>

    <method name="gotoState" args="ns">
      unselectAll();
      if (ns == "START") {
          s1.select(true);
      } else if (ns == "STATE_2") {
          s2.select(true);
      } else if (ns == "STATE_3") {
          s3.select(true);
      } else if (ns == "END") {
          s4.select(true);
      }
    </method>
  </view>

</canvas>

10. 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.

<canvas height="130" debug="true">
  <command oninit="this.setKeys(['Control','n'])"
           onselect="Debug.write('the [Ctrl-n] key combination was pressed');" />
</canvas>