Chapter 2. Language Preliminaries

Table of Contents

1. Overview of Syntax and Semantics
1.1. XML
1.2. JavaScript
1.3. The "dot" syntax
1.4. Case Sensitivity
1.5. Implicit "this" and "with"
1.6. The LZX DTD and Schema
2. Objects and Attributes
2.1. Objects
2.2. Attributes
3. Events and Methods
3.1. Events
3.2. Methods
3.3. Handlers
4. Constraints
4.1. Lexical and View Hierarchies
4.2. Lexical Scope
5. Data Access, Manipulation, and Binding
5.1. Data Access
5.2. Data Manipulation
5.3. Data Binding
6. Combining Tags and Script in LZX Programs
6.1. Name Mapping between Tags and Classes
6.2. How to Combine Tags and Script
6.3. When to Use Tags and When to Use Script
7. Compilation and Execution

LZX is an object-oriented, tag-based language that uses XML and JavaScript syntax to create the presentation layer of rich Internet applications. Typically these applications are compiled by the OpenLaszlo compiler. They may be deployed as standalone files, or they may be served by the OpenLaszlo Server. The specification of the language includes both a set of XML tags and a set of JavaScript APIs.

The LZX language was designed to use familiar syntaxes and naming conventions so that experienced web developers would find it easy to learn and incorporate into their programming environments. At the same time LZX introduces new concepts and capabilities that make possible more fluid and responsive user interfaces to web applications than are possible with any other technology.

A running LZX program takes place on a visual object called a canvas, which is basically a bit of screen real estate. On the canvas, autonomous boxes called views interact. These views may be nested logically and visually, and have programmable attributes, including size, position, background color, and so forth. Views may be used to contain resources, such as, for example, an image or a video, and may also be dynamically bound to any set of XML-formatted data. The attributes of any view can be set to be a function of the attributes of any other view or views, and virtually any attribute of a view can be animated — that is, set to vary over time.

The LZX view system is similar to other view systems in many ways, but its implementations of data binding, attribute-constraints, and animation distinguish it from other UI technologies.

LZX programs typically contain both declarative and procedural structures, and the language follows many naming conventions from CSS (Cascading Style Sheets). Programs written in LZX thus appear similar, on casual inspection, to DHTML applications with embedded JavaScript. LZX programs are conceptually different, however, from typical DHTML/JavaScript applications that are interpreted and rendered, or "executed," by the web browser. LZX programs, in contrast, are compiled on the server and downloaded as byte code for a target rendering engine.

In the current implementation of the OpenLaszlo platform, LZX programs are compiled in the OpenLaszlo Server and downloaded either as Flash movies (.swf files) to be executed in the Flash Player plugged into an Internet browser, or compiled to DHTML which is downloaded to be executed by the browser's JavaScript engine. It's important to understand that the Flash Player is used only as an execution/rendering engine for the generated byte code: there's nothing inherent in LZX that marries it to Flash. In particular, LZX does not employ or rely upon the Flash object model.

Similarly, because LZX programs are compiled by the OpenLaszlo Server the use of JavaScript in LZX programs is subtly different from its use in traditional web applications in which JavaScript is used to do things like communicate with the browser or generate HTML pages. Those functions are basically irrelevant in LZX applications. Thus, although the language design is rather traditional, the programming paradigm is fundamentally new.

This chapter summarizes the traditional and innovative aspects of LZX. It's an overview, not a tutorial; after reading it you will be better able to decide how to go about learning the language. Depending on your background and experience, you may determine that you need to go learn some more about XML or object-oriented programming before addressing LZX. On the other hand, if you find these concepts accessible, you may want to jump right in and begin coding, in which case we suggest starting with the tutorial: Chapter 5, OpenLaszlo Basics.

For discussion of OpenLaszlo runtime and deployment modes, see the Preface.

1. Overview of Syntax and Semantics

In LZX, XML tags are used to create JavaScript objects, and JavaScript is used inside of LZX programs to manipulate objects created by tags. In most cases, anything that can be done with a tag can be accomplished in JavaScript, and vice versa. However, this equivalence is not universally true, and moreover one technique is virtually always vastly superior to another in any situation. Learning LZX basically comes down to learning the tags and APIs; mastering the language requires developing a subtle understanding of how the procedural and declarative approaches differ, and learning how and when to use each.

LZX strictly adheres to XML and JavaScript syntax

The following sections offer a brief refresher on the two kinds of LZX syntax. See below for a discussion of how JavaScript and XML syntaxes play together in typical LZX programs.

1.1. XML

XML, the eXtensible Markup Language, is a W3C standard for encoding data. You will need a general familiarity with XML concepts in order to write programs in the LZX language for two reasons: in the first place, most of the functionality of LZX is implemented in an XML tag set. LZX programs themselves are valid XML documents; LZX programs that are not valid XML simply will not compile. Secondly, LZX programs only operate on data encapsulated in XML. See Section 3, “XML Characters in Script” for a description of how to use XML in script.

If you understand how tags and attributes are represented in XML, are comfortable with the concepts of roots and nodes, and know how nesting works you probably know enough to get started with LZX. For more information and links to any number of online books and tutorials about XML, visit the W3C web site.

Another distinction between XML and JavaScript is that in XML the type names are lowercase ("string", "number"), while in JavaScript they're initial-capped, for example String and Number. The XML type names are used in <attribute name="foo" type="string"/>; they're lowercase for compatibility with the XML Schema Description datatypes.

1.1.1. XML and HTML

If you have experience with HTML but not XML, you'll find many similarities. Here's some notes on how XML differs from the HTML, for people who are only familiar with HTML. These apply to all XML; they may particularly catch people up with respect to text markup, which uses tags (<p>, <i>, <br>) with the same names and meanings as HTML tags:

  • Case matters. <b> is different from <B>. (<b> exists in LZX as a tag for marking up bold text in a <text> element. <B> does not exist.)

  • Attribute values must be quoted using " or '. <view width=100> is invalid XML; use <view width='100'> or <view width="100"> instead.

  • Empty elements must be closed. <br> is valid HTML; in XML use <br></br> or <br/> instead.

1.1.2. Namespaces

OpenLaszlo applications can be written with a namespace:

  <canvas xmlns="http://www.laszlosystems.com/2003/05/lzx">...</canvas>

or without:

  <canvas>...</canvas>

If there is no namespace, the compiler defaults it to the LZX namespace (http://www.laszlosystems.com/2003/05/lzx).

[Note] Note

A namespace has the same syntax as an URL, but it is not an URL; namespaces are really just hierarchical unique id's. In the above examples, the namespace doesn't point to anything in particular on the Laszlo Systems website. For more on namespaces, see the W3C specification.

1.2. JavaScript

JavaScript is a language originally written at Netscape by Brendan Eich for incorporation in the Netscape 2.0 browser. It was instantly successful and widely adopted in other browsers, and to preserve its emerging value as a standard, the European Computer Manufacturer's Association (ECMA) codified the language as ECMAScript and now maintains control of its evolution. Although there may be subtle differences between any implementation of JavaScript and the language formally specified by the standards body, in colloquial usage the terms JavaScript and ECMAScript are often used interchangeably. While perhaps a little more accurate to say that "ECMAScript" refers to the pure language while "JavaScript" means both the language and the associated libraries that are available on most browsers, in this book we follow general usage and use the terms loosely, trusting that it will be clear from the context what we're talking about. The term "script" refers to any (procedural) code written in JavaScript.

LZX incorporates a partial implementation of the ECMA-262 Edition 3 specification. See Section 2, “Differences between ECMA-262 and LZX” for more information.

Depending on your background, you may find certain aspects of LZX familiar or foreign. For example, if you have experience with Java but not JavaScript, you will need to be aware of key differences in the languages.

JavaScript's approach to object-oriented programming lacks the rigor of Java's. There are no packages or interfaces, for example, nor is it possible to finalize classes. Finally, the behavior of local and global variables in JavaScript is sometimes surprising to Java programmers.

Conversely, if you are an experienced JavaScript programmer you may have to "unlearn" certain assumptions, in particular with regard to the availability of certain libraries and functions. For example, while the Math RegExp librarys are present in LZX, literal RegExp for regular expressions is not. Moreover, LZX has a more complete object-oriented programming model than does simple JavaScript. That is, LZX has classes, inheritance, and mixins.

If you have experience with neither Java nor JavaScript you may want to first work through a JavaScript tutorial like the ones available at the MDN Doc Center before delving too deeply into LZX.

1.3. The "dot" syntax

LZX employs the "dot" (period) syntax to indicate relationships between objects and their members. Consider the expression

something.other

When read as JavaScript, something refers to an object, and other refers to a property of that object, where a property might be, say, a method. Now consider the following LZX code fragment:

<view name="beatles"> 
  <view name="george"/> 
</view>

in this case it may sometimes be convenient to refer to the interior view, "george" as

beatles.george

in which case george is a "child" of beatles .

As will be discussed further below, LZX affords various ways to define attributes of, and methods on, objects or classes. For example, the following code samples (which create a view named myview and set its background color to red) are equivalent:

JavaScript:

myview = new lz.view;
myview.setAttribute ("bgcolor", "red");

XML tag:

<view name="myview" bgcolor="red"/>

and in both cases the background color of myview could be accessed by subsequent code as myview.bgcolor . The "dot" convention thus provides a convenient way of referring to objects regardless of how they came into existence — that is, whether by declarative tag or procedural code.

1.4. Case Sensitivity

OpenLaszlo is entirely case-sensitive. This means that whenever you use a variable it must be in the case in which it was defined.

1.5. Implicit "this" and "with"

LZX has a behavior that is more Java-like than JavaScript-like. 'implicit this' is a term we use to describe the behavior of free references in LZX methods and handlers. In LZX classes, the object bound to `this` is implicitly 'in scope' in all methods and handlers, as it is in Java (this is _not_ the case in JavaScript). We added this feature to LZX because we felt it led to more intuitive and compact code.

What this means is that in any method or handler in a class you can refer to the class attributes by name directly, without the prefix `this.`. (Hence the nickname 'implicit this'.) A concrete example:

<class name="foo">
   <attribute name="attr" value="42" />

   <method name="implicitAttrValue">
     return attr;
   </method>

   <method name="explicitAttrValue">
     return this.attr;
   </method>
</class>

The two methods will return the same value.

You should not rely on "implicit this" on the left-hand side (LHS) of an assignment expression. Doing so is dangerous because unless the property already exists in `this`, you will write a global. Consider:

<class name="bar">
   <attribute name="attr" />

   <method name="implicitSetAttrValue">
     attr = 7;
   </method>

   <method name="explicitSetAttrValue">
     this.attr = 7;
   </method>
</class>

Because attr is not initialized, the implicit method may not find attr in the instance and will set the global variable attr instead. You should always explicitly use this on the left-hand side of any assignment. The compiler can help you find errors like this: If you compile your application with lzc - DwarnGlobalAssignments, the compiler will print a warning for every global assignment that your program makes. If you intend to make a global assignment, you can silence the warning by explicitly using `global.attr =`. For a complete description see Chapter 49, Understanding Compilation.

`with` is a JavaScript primitive that can be used to establish scope. Free variables in the body of a `with` will be looked up first in the object that is the argument (and then in whatever the enclosing scope is).

1.6. The LZX DTD and Schema

An XML schema defines the LZX tag set and can be used to configure an editor. The DTD is also available for the curious, although there is no need for you to be aware of it for programming purposes.

The LZX schema is used by the OpenLaszlo Compiler to ensure that LZX programs are formally correct. For example, the schema specifies what attributes can be included in an opening <view> tag. If your program contains a <view> tag that includes an attribute not defined by the schema, it will compile with a warning.

LZX allows you to define your own tags. User defined tags do not get incorporated into the schema that is available to your editor, although they are used in the internal schema that the compiler uses to test that program's validity.

2. Objects and Attributes

LZX incorporates the standard object-oriented programming concepts of inheritance, encapsulation, and polymorphism. In general, a tag in an OpenLaszlo program corresponds to an object that is an instance of the class of that name. For example, the <view> tag corresponds to an lz.view object.

To a first approximation, then, LZX can be described as a rule-based declarative language for manipulating visual (JavaScript) objects called views, where rules are expressed as constraints on the values of the attributes of those objects.

The following paragraphs summarize some of the key object-oriented aspects of the LZX language. These ideas are examined at greater length elsewhere in this Guide, particularly in Chapter 33, Extending Classes.

2.1. Objects

An object is a data type that contains named pieces of data. Depending on context, a named datum might be called a property or an attribute of that object. For example, each view object has attributes, such as height, width, horizontal position, vertical position, and so forth. Values are generally assigned to the attributes of objects when the objects are created; attributes that you do not specifically set are assigned default values.

You can create new kinds of LZX objects by using the <class> tag. Each new class you create must be given a name and the name of a class that it's "extending." The newly created objects inherit all the properties of the class you extended, plus any additional properties you may define. For example consider the trivial case

<class name="myview" extends="view"/>

In this case you have defined a new kind of object called a myview that has all the properties of a view. Appendix A, Understanding Instantiation discusses in depth when and how objects are defined in your code and built by the compiler at runtime.

2.2. Attributes

In LZX, the word "attribute" has two related but subtly different meanings, one syntactical and one semantic. In the XML, syntactical, sense, an attribute is a named value associated with an XML element and specified in that element's opening tag. Thus in the XML tag

<boss demeanor="friendly"/>

demeanor is an attribute of the tag boss . This meaning of "attribute" applies whenever the context is XML structure. Note that the value assigned to an attribute is enclosed in double quotation marks.

Because LZX tags correspond to JavaScript classes, "attribute" takes on the additional semantic weight of property of a JavaScript object. Thus the LZX tag

<view height="20" width="30"/>

causes the creation of a view object with the specified values for the attributes height and width.

The <attribute> tag can be used to set JavaScript attributes of objects. For example,

 
<view name="myview"> 
  <attribute name="height" value="20"/> 
  <attribute name="width" value="30"/> 
</view>  

is equivalent, in LZX, to the earlier one-line version. Thus height is an attribute of the view, in the semantic sense, even though it is not in the XML sense of being contained in the opening tag. height is also an attribute, in the XML sense, of the first <attribute> tag. Its value can be referenced by script as myview.height.

You also use the <attribute> tag to define new attributes for classes you create. For example

<class name="froboz" extends="view"> 
  <attribute name="whatnot" value="17"/> 
</class>

Defines a new kind of view object, froboz, that has the attributes that it inherited from view, plus the new attribute named whatnot.

We have seen that attributes can be set, that is, assigned values, in LZX tags. It is also possible to set attribute values in script using the setAttribute() setAttribute() method. Additionally, values of attributes can be read, or "gotten" in script (but not in tags) using the . operator.

Assume the existence of a view named johnny. This view may have been created by a tag or in script; how the view came into existence does not matter.

The JavaScript code to set the height of this view to 100 pixels would be:

johnny.setAttribute("height", 100);

and to read the value the height would be

johnny.height;

Every time that an attribute is set, that is, every time the value of an attribute changes, an object called an event is generated. The next section discusses what events are, and how they work in LZX programs.

3. Events and Methods

3.1. Events

Events are the mechanism by which objects communicate with each other when something changes. For example, an event might be generated when a mouse button is clicked, or when data arrives from a server, or when a view has been constructed.

In LZX programs, events are not broadcast, but rather they are communicated in a point-to-point fashion using delegates, which are basically function pointers that are referenced when events happen. This implementation increases flexibility and reduces the overhead of using events. However, for the purposes of the discussion in this chapter we're going to ignore delegates and speak of events in a slightly less rigorous manner, saying, for example when such-and-such an event occurs, thus and such happens, leaving aside for now an explanation of how it happens.

Views have defined events, as listed on the entry for <view> in the LZX Reference Manual. Many of these events that deal with user input, such as onblur, onclick, onkeydown, will be familiar to JavaScript programmers. Other events, such as onheight and onopacity, pertain to views' visible attributes. Finally, the oninit and onconstruct events are related to the creation of the instances of view objects. Similarly, other system-defined LZX objects such as Datasets (see below) have events associated with them.

Events and attributes often work as pairs, and in fact, the default behavior of the setAttribute() method is to set the named property and send the event called "onx" + property. For instance, when a view changes its x (horizontal) position, it sends the event onx with the new value for its x property. This means that in addition to system-defined events, there exists an event for each attribute you define.

When an event happens, control is transferred to its associated event-handler (if one is defined). Events can be sent with a single argument, which usually conveys information about the property that changed.

Elsewhere in this Guide we discuss how events are implemented in LZX, and how the event architecture bears upon program design. In particular, Chapter 30, Delegates discusses the relationship between events and delegates.

3.2. Methods

In LZX, a method is a JavaScript function associated with a particular object.

Functions are invoked using the () operator. Thus,

<view name="dog">   
  <method name="bark"> 
    <!-- some JavaScript code --> 
  </method> 
</view>

Defines a function that is executed when invoked by name, as in

dog.bark();

In JavaScript, the this keyword is used to refer to the object through which the function was invoked.

3.3. Handlers

A handler is like a method. But whereas a method is invoked by its name, a handler must be associated with a particular event. The handler script will be executed when the referenced view receives an event with this name. For example,

<view> 
  <handler name="onclick"> 
    <!-- some JavaScript code --> 
  </handler> 
</view>

defines a function that is executed when the view is clicked on. Consider

 
<view> 
  <method name="bark"> 
    <!-- some JavaScript code --> 
  </method> 
  <handler name="onclick"> 
    this.bark()  
  </handler > 
</view>

When the view is clicked on, the onclick event causes the execution of the handler() which in turn invokes the method named bark(). Event handlers are often identified in an opening tag, as in

<view onclick="clickHandler()"> 
  <method name="clickHandler"> 
    <!-- some JavaScript code --> 
  </method> 
</view> 

There are three general categories of methods and handler:

  • "On init" methods that are invoked when their parent object is created;

  • handlers that are invoked when their parent object receives a specified event;

  • named methods that are explicitly invoked by other methods.

Note that you can define a method using conventional JavaScript syntax, but in LZX the preferred way to declare a method is with the <method> tag.

Also note that in LZX, unlike many other object-oriented systems, you can override a method in an instance of an object. This topic is covered in Chapter 33, Extending Classes.

4. Constraints

In LZX, a constraint is an attribute whose value is a function of other attribute values. The syntax for coding a constraint is

$when{expression}

where:

  • $ is the token indicating a constraint

  • when is an optional compiler directive: immediately, once, or always. $always{expression} can be abbreviated to ${expression}

  • { and } are tokens delimiting the expression to be evaluated

  • expression is a JavaScript expression

As we have seen above, whenever the value of an attribute changes, its on event is generated. Because a constraint is an attribute whose value is dependent upon the values of one or more other attribute(s), the value of the constraint is recalculated whenever it receives the on event for the attributes on which it depends.

Consider

<view name="someView" 
      width="${someAttribute + someOtherAttribute}" 
 />

The value of someView.width is recomputed whenever an onsomeAttribute or onsomeOtherAttribute event occurred.

So for example

<view name="beatles" width="${this.paul.width + 28}"> 
  <view name="paul" onclick="clickhandler()" > 
    <!-- clickhandler method here to increase paul's width based on user clicking mouse --> 
  </view> 
</view>

The width of beatles will increase or decrease as a function of paul's width; the expression this.paul.width + 28 is a constraint.

When the user clicks on the paul view, the clickhandler will adjust the size of the paul view. This change will be reported to the ${this.paul.width + 28} constraint, which will then adjust the beatles view to the width of the paul view plus an additional 28 pixels. All of these steps are invisible to the user because they occur instantly.

This, of course is a trivial example, but it serves to make the point that in declaring the structure of your objects in LZX you also declare the rules by which they will relate to each other. Constraints are a fundamental concept in LZX programming, and learning to "think in LZX" is a mostly a matter of learning to properly model your system's behavior in terms of the constraints on its constituent parts. Chapter 27, Constraints covers constraints in depth.

4.1. Lexical and View Hierarchies

An LZX application is expressed as a hierarchy of objects, usually visual objects, all of which are contained in a single object called the Canvas. Recall that LZX programs are XML documents, the Canvas is the root element. The simplest LZX program is thus:

<canvas/>

This program compiles and executes, but has no output. As the simplest visual object is the View, a minimal LZX program would look something like:

<canvas>
  <view>
    <text> Hello World!</text> 
  </view> 
</canvas>

This code clearly defines a hierarchy of three objects. We can make their visual relationship more visible by giving the canvas and view sizes and background colors:

In this simple case, the lexical hierarchy in the code corresponds to the visual hierarchy in the canvas. In fact, a <text> object is an instance of a class derived from <view> . The typical LZX program repeats this pattern on a larger scale: the canvas contains views which contain other views, and so forth. Classes are used to replicate view groupings; components such as buttons, windows, input fields and sliders are examples of classes built from views.

LZX affords a variety of ways to simplify the relationships among views. For example, there are several categories of layouts that handle the "housekeeping" of placing views in relationship to each other. These are described in Chapter 17, Layout and Design.

However, the relationship between the textual hierarchy in the code and the visual hierarchy on the canvas is not always as neat as in the example above. In particular, LZX's powerful data binding semantics make it possible for a single <view> tag in the text to cause the creation of an arbitrary number of instances of view objects. In such cases it becomes very important to have a precise way of talking about complex relationships among objects. Chapter 26, Views covers this topic in depth.

4.2. Lexical Scope

In LZX the concepts of local and global namespaces, or scopes, are basically the same as in JavaScript. Having said that, it should be pointed out that JavaScript follows rules that are sometimes surprising to Java Programmers.

In JavaScript, all variables are global unless they are preceded by the keyword var.

Thus

a = 7; // defines a global variable a

and

var a = 7 // defines a local variable a

This syntax means, for example, that an assignment in a method definition can set an instance of a global variable:

for (a = 0; a <n; a++);

Creates a global variable named a, or changes the value of this variable if it already exists. What the programmer meant to write was

for (var a = 0; a <n; a++);

In LZX, the name attribute is local and the id attribute is global. Thus

<canvas> 
  <view name="john" id="grandfather"> 
    <view name="john" id="father"> 
      <view name="john" id="son"/> 
    </view> 
  </view> 
</canvas>

is a valid name scheme. The innermost view can be referenced by canvas.john.john.john or simply son.

As will be discussed below, functions created using the <script> tag can be accessed from anywhere in the program.

5. Data Access, Manipulation, and Binding

LZX is designed to make it easy to write data-driven applications in which the values of data sources define the appearance and actions of the program. It does this through tags and APIs that allow you to get access to data over http, manipulate XML data in memory, and, significantly, bind the data hierarchy to the view hierarchy.

The following paragraphs summarize these key features of the LZX data-handling architecture. Chapter 36, Data, XML, and XPath discusses this subject in depth.

5.1. Data Access

LZX programs manipulate XML-formatted data, which may be

  • embedded in the program text,

  • read in from a source when the program is compiled, or

  • read in from a source when the program is running.

XML sources are stored as objects called datasets. The <dataset> tag has attributes that allow you to, for example, control caching on the client and server, include or exclude http headers, queue requests, and so forth. Objects created by <dataset> are called lz.datasets. Methods on lz.datasets allow you to, for example, get and set query strings, parameters and so forth.

5.2.  Data Manipulation

LZX employs datapointers, which are objects that represent pointers to nodes in datasets, to locate and manipulate content. Datapointers support a subset of XPath, which is a W3C standard specification for identifying paths of an XML document, or in the case of LZX, datasets. XPath uses a notation similar to the UNIX file-system to refer to nodes within a dataset. Datapointers can be repositioned using both procedural calls such as selectNext() and by running an XPath request using setXPath().

Because it incorporates sophisticated pattern matching, XPath notation is extremely concise and powerful. A single XPath expression can represent an arbitrarily large number of XML elements. Using methods such as addNode(), setNodeName() setXpath(), and selectParent(), you can build and manipulate XML structures.

5.3. Data Binding

LZX provides a unique way of merging any arbitrarily shaped data hierarchy with any display hierarchy; this capability is called data binding. It is implemented in such a way that the data context of a child in the display hierarchy is implicitly the data context of its parent. Moreover, it is possible to instruct the system to create an arbitrary bit of view hierarchy to represent each element in a set of selected data.

The way this is done is by binding views to datapointers. A datapath is a special case of datapointer that explicitly marries the data hierarchy to the view hierarchy, as in, for example:

<view name="bob" datapath="testdata:/*">

where testdata refers to a dataset defined earlier in the program.

If this sounds a little abstract, well, it is. Therefore we'll keep the discussion short here and defer longer explanations to Chapter 37, Data Access and Binding; you may also want to examine some of the examples on http://openlaszlo.org/showcase to get a feel for what can be done when applications are truly data-driven.

The key thing to understand is that while other languages and technologies have implemented merge algorithms that may appear similar on the surface, LZX's data binding is novel in the creation of program objects that retain a live connection with the entities of the data source.

6. Combining Tags and Script in LZX Programs

As mentioned earlier, virtually all nontrivial LZX programs contain both (XML) tags and script. Tags are used declaratively, that is, to define objects and their attributes. Script is used procedurally, that is, to explicitly define a series of steps. Although the two kinds of code are liberally intermixed within LZX — for example, script can appear within tags — each syntax locally maintains its integrity.

So for example, within declarative LZX code, comments are denoted

<!-- XML comment -->

while within JavaScript, comments are denoted

// JavaScript comment

Thus LZX is similar to an alloy of two metals that do not chemically combine.

Because the declarative and procedural portions of a program can be so intertwined, it can be a little tricky, at first, to recognize them within a program. However, once you gain a little experience and begin to grasp the underlying logic of LZX you will find that you hardly notice the alternating syntaxes. The following paragraphs explain how and why to employ the two "flavors" of LZX. Consult the documentation for your IDE or text editor to learn how to use the LZX DTD or schema to give visual cues that indicate what portions of the program are in each syntax.

6.1. Name Mapping between Tags and Classes

The preferred normalized form of class names is lz.[tagname] where [tagname] is the name of a tag. So for example a if you created a class called "bob":

<class name="bob"/>

then from the point of view of JavaScript, this would be an lz.bob object.

In earlier versions of LZX (before OpenLaszlo 4), there was an asymmetric mapping between tag and class names, often of the form LzFoo <—> <foo>, as in the correspondence between, say the class name lz.view and the tag name <view>. (Notice in lz.view the mixed case, and the absence of the period between lz and the tag name.) Also there was a distinction between LFC classes and user-created classes. The new lz.foo form is consistent across LFC classes and user-created classes. The old forms will still work, and they appear throughout this documentation and in example code. As of OpenLaszlo 5.0, however, the old forms will be deprecated; it would be a good practice to adopt the new form in your code.

6.2. How to Combine Tags and Script

Let's start by making a distinction between what is syntactically allowable and what is meaningful.

6.2.1. What's Allowable

Remember that all LZX programs are well-formed XML files. That means that all portions of the program, including embedded JavaScript, must conform to XML rules. Therefore where JavaScript uses characters that are meaningful to XML, such as the left angle bracket <, you must make sure that those characters don't confuse the XML parser. You can do that in either of two ways:

  • by explicitly escaping delimiter characters with an entity reference. (For example, the entity reference for the left angle bracket is &lt;).

  • by using the XML CDATA construct to define a block of character data.

This is the sum total of rules for making sure that XML does not trip on JavaScript.

6.2.2. What's Meaningful

Although the admixture of two different sets of language rules in one language does create opportunities for confusion, it's fairly easy to recognize how LZX programs are structured, and what kind of code goes where. There are only a few contexts in which script code can appear in LZX programs. After you learn to recognize these contexts you are unlikely to be confused about what syntax applies. JavaScript is used:

  • between an opening and closing <script> and </script> tag;

  • between an opening and closing <method> and </method> tag;

  • between an opening and closing <handler> and </handler> tag;

  • with the double-quoted right hand value of an assignment statement within certain tags, such as oninit="script expression" .

6.3. When to Use Tags and When to Use Script

As we said earlier, most things that you can do in LZX can be done either with XML tags or JavaScript APIs, and mastering LZX requires developing a subtle understanding of how and when to use each. You will find, in general, that tags are best for computations that can be done at compile-time — such as laying out the canvas — and script is best for run-time things, such as responding to user input. But in order for you to make any use of that information you need to understand what is done at compile time and what is done at run time, and much of that is under your control, and dependent on the problem you're trying to solve.

In other words, there is no simple set of unambiguous rules that tell you when to use tags versus when to use script. But there are, however, design patterns common to all well-made LZX programs.

Remember, LZX is primarily a language for manipulating visual objects called views. So the question of when to use tags versus script is usually asked in the context of the creation of views and manipulation of their attributes. Script can be used for other things, such as global functions, but in those instances the need to write procedural code (i.e., script) is usually clear-cut. The finesse part has to do with manipulating views and their attributes.

For example, a simple two-word constraint might express a relationship between views that would require thirty lines of code to express. Most of the time the constraint is the better programming solution. But not always.

Although there are no absolutes, there are some general principles that define best practice in LZX development:

  • Use tags when that is the only option.

  • Use JavaScript when that is the only option.

  • If something can be done with either tags or script, use tags (unless there is a good reason not to).

Each of these principles is described briefly below.

6.3.1. Use tags when that is the only option

There are certain tags that perform functions that cannot be done using script. For example, the root node (and enclosing tag) of every LZX program is <canvas>. Every LZX program begins with <canvas> and ends with </canvas>; there is no alternative structure using script. Similarly there are no script equivalents for <method>, <attribute>, <resource> , and several other tags. Moreover, within certain tag definitions there are certain attributes that can only be set in the tag.

6.3.2. Use JavaScript when that is the only option.

There are several JavaScript APIs that perform functions that cannot be done using tags. For example, lz.event and similar APIs perform operations that cannot be done using tags. Similarly, there are certain attributes of objects that can only be referenced by script, even for objects that were created with tags. For example, consider

<view name="franklin">

There is an attribute, franklin.subviews, that can be accessed by script; it is not possible to set or access that attribute in a tag.

6.3.3. If something can be done with either tags or script, use tags.

In the large number of cases where it is possible to do something using either tags or views, it is generally better to use tags. For example you can create a new view called "sam" using tags

<view name="sam">

or script

sam = new lz.view();

When you use the tag syntax you can quite naturally create hierarchies of nested subviews, define attributes as constraints, and lay out your code in a way that helps you conceptualize the placement of views on the canvas. Achieving any of these results in pure JavaScript would be a colossal pain and negate much of the benefit of the language. Learning to think in LZX means learning to think in terms of views that act nearly autonomously according to the constraints you establish at their creation.

6.3.4. Unless there is a good reason not to.

Sometimes it's better to write procedural code instead of declarative code. This may become necessary, for example, to achieve optimal performance: multiply-constrained systems can sometimes become CPU bound. Other times procedural code may make your program's behavior easier to understand: complex rule-based view systems sometimes become inscrutable.

7. Compilation and Execution

LZX programs are compiled by the OpenLaszlo Compiler, downloaded as byte code or DHTML, and executed on the client. In writing your program you can make trade-offs between compilation performance, download and startup up time, and runtime performance.

As in other scripting languages such as Perl and Python, LZX programs execute in approximately linear order. That is to say, if you were to write

<view name="outside"> 
  <view name="inside"/> 
</view>

Then inside would be built before outside. However in many cases you may wish to control the order in which objects are built, or initialized. LZX gives you fine grained control over initialization and instantiation of views.