Table of Contents
LZX applications can include procedural code, (see Chapter 2, Language Preliminaries) as well as declarative XML tags.
Within an LZX program, script can appear
between <script>
tags
between <method>
tags
between <handler>
tags
between quotation marks as an assigned value for an attribute
The quickest way to get a script running is to put it in
<script>
tags. Code in script tags is executed
immediately. <script>
tags are only embeddable in the
<canvas>
.
<canvas
width
="100%
" height
="50
">
<text
id
="sometext
"/>
<script
>
sometext.setAttribute("text", "hello world");
</script
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
We can now use our knowledge of JavaScript to build on this foundation:
<canvas
width
="100%
" height
="100
">
<simplelayout
axis
="y
" spacing
="5
"/>
<text
id
="sometext
"/>
<text
id
="moretext
"/>
<script
>
var somestring = "Hello, World!";
sometext.addText(somestring);
var first = 4;
var second = 3;
var result = first + second;
moretext.addText(result);
</script
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
If you've never worked with JavaScript before, you may be surprised by some subtle features of the language. For example, consider this slight expansion of the preceding program:
<canvas
width
="100%
" height
="100
">
<simplelayout
axis
="y
" spacing
="5
"/>
<text
id
="sometext
"/>
<text
id
="badtext
"/>
<text
id
="goodtext
"/>
<text
id
="bettertext
"/>
<script
>
var first = 4;
var second = 3;
var result = first + second;
sometext.setAttribute("text", result);
badtext.setAttribute("text", first + " plus " + second + " is " + first + second);
goodtext.setAttribute("text", first + " plus " + second + " is " + (first + second));
bettertext.format("%s plus %s is %s", first, second, first + second);
</script
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Everything there is fine, except the "badtext.setAttribute( … );
"
line. Four plus three should be 7, as in the line immediately above it, right? What
happened is that we concatenated numbers to strings ((..." is " + first +
second)
). The next line shows one way to fix this problem. The line after
that shows a better way.
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.
XML has two reserved characters <
and
&
which introduce, respectively, tags and entities. When
you need to use either of these characters as data, rather than their reserved program
usage, you must escape them by writing them as the entities
<
and &
. (Some
syntax-directed editors will work better if you also escape >
by writing >
, but that is not required by XML.)
The bodies of, for example <method>
,
<setter>
, <handler>
and the values of tag properties (for example,
<attribute>
's name
,
type
, value
properties, etc.), are interpreted as
Javascript by the compiler, but they must still be valid XML text. The most common error
is to say something like:
<method name="..."> if (a < b && ok) { ... } </method>
This is invalid XML, but it is easy to do because <
and
&
are so common in Javascript expressions. If there are
only a few occurrences in your method body, you can escape them individually:
<method name="..."> if (a < b && ok) { ... } </method>
but that sure makes your Javascript hard to read. Fortunately, has another method for
specifying long stretches of text data that should not be parsed as XML: the
CDATA
directive. The alternative way to write your method is:
<method name="..."><![CDATA[ if (a < b && ok) { ... } ]]></method>
This clearly demarcates your Javascript and you don't have to escape individual
characters. As a rule of thumb, though, most programmers avoid using
CDATA
unless it is necessary.
OpenLaszlo documentation is not intended to provide a complete reference for JavaScript. Later chapters do explain some advanced topics in scripting, but we recommend that you have a JavaScript reference handy while writing LZX.
Because XML and Javascript have two different comment syntaxes, you must take care to use the correct syntax in each situation:
<!-- You can use XML syntax comment here --> /* You usually can use Javascript comment syntax here, but it is HIGHLY discouraged */ <method name="..."> <!-- You can use XML syntax comment here --> /* You can also use Javascript comment syntax here */ <![CDATA[ // You MUST use Javascript comment syntax here, an XML comment would look // like a funky Javascript expression "lessthan, not, predecrement" if (a < b && ok) { ... } ]]> </method>
There is one exception to the above, and the reason for highly discouraging using Javascript comment syntax outside of method bodies: If you declare a class to have an attribute named `text` of type `text`, this tells the compiler you want the body of instances of your class to be the initial value of the `text` attribute. For example, the button class does this so you can say:
<button>Click Me!</button>
And "Click Me!" becomes the text
of the button. The compiler
treats this as if you had said:
<button text="Click Me!"/>
The compiler will warn you if you say:
<button text="One thing">Another</button>
because it doesn't know which you mean to be the value of the text
attribute.
The gotcha is when you define the <button> class. If you say:
<class name="button"> <attribute name="text" type="text" value="Unnamed Button"/> // Javascript comment <method name="..."> ... </method> </class>
The compiler sees the '// Javascript comment' as the body of the
<class> and will issue a warning. It can't tell if you want the button
text to be 'Unnamed Button' or '// Javascript comment'. It is for this reason that you
are discouraged from using Javascript comments outside of
<method>
, <setter>
and <handler>
bodies.
You can write functions in <script>
tags.
<canvas
height
="100
" width
="100%
">
<text
id
="sometext
"/>
<script
>
function writeAWord(someword) {
sometext.setAttribute("text", someword);
}
writeAWord("HELLO!");
</script
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Functions are global to the LZX document, and they follow the same scope rules as JavaScript.
Methods are in some ways similar to functions. They contain blocks of code between
<method>
tags, and are associated with particular
classes.
Let's explore methods with a simple example of a window. The
<window>
element is actually a view, as we saw before.
There are a few methods that apply to it. The window
class
extends the lz.view
class. This means that
windows inherit all the attributes and methods of views.
<canvas
height
="200
" width
="100%
">
<window
x
="20
" y
="20
" width
="150
" title
="Simple Window
" resizable
="true
">
<button
text
="My button
" onclick
="this.parent.setAttribute('title', 'You clicked it');
"/>
</window
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Let's break this statement apart:
onclick="this.parent.setAttribute('title', 'You clicked it');"
First, there's the
onclick=
Like all of the on[event] attributes, this one takes JavaScript that will be run in the context of the object when the event is received.
The next part:
this.parent
is a reference to an object. In JavaScript, the scope is generally global unless you say
otherwise. That means that any class or instance methods or variables must be preceded by
the keyword this
. As for the 'parent' part: Let's start by saying that
the lzx viewsystem always assigns each view a variable 'parent' which points to that view's
hierarchical parent. View hierarchies are discussed in detail in Chapter 26, Views
Now we're going to call a method. With very few exceptions, tags in an lzx file
correspond to run-time objects of the view system. Using xml, we can configure those
objects with attributes and child nodes. Using script, we can call their APIs. From the
documentation, we know that the <window>
has a
setAttribute()
method that will change the window title to whatever
string you give it.
The last thing to note is the use of single quotes inside the function call.
Remember that window
extends the lz.view
class.
That means that each window has all the attributes of a
<view>
. Here's an example of how to use script to
manipulate some of those assets.
<canvas
width
="100%
" height
="200
">
<window
x
="100
" y
="30
" title
="Window 2
" name
="windowTwo
">
<text
>This is the second window.
</text
>
</window
>
<window
x
="20
" y
="20
" width
="150
" title
="Simple Window
">
<button
text
="My button
" onclick
="this.parent.parent.windowTwo.setAttribute('x', 200)
"/>
</window
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
We're just building on the previous example here. Instead of addressing the parent
view of the button, we are going two up, then one down.
this.parent.parent
refers to the canvas, and we point to Window 2 by
using its name
(windowTwo).
We are also using the setAttribute()
method, which takes two
arguments: the attribute to set, and what to set it to.
Next, let's find a way to move Window 2 over so that we can see what's behind it, without dragging it. Clicking the button twice doesn't help, because all that does is reset the x attribute to a constant amount (150px).
Instead, we need to figure out where the second window is, and then add an increment
to it each time the button is clicked. To do that, use the . operator to get the
x
attribute: this.parent.parent.windowTwo.x
.
So we could say:
<canvas
width
="100%
" height
="200
">
<window
x
="100
" y
="30
" title
="Window 2
" name
="windowTwo
">
<text
>This is the second window.
</text
>
</window
>
<window
x
="20
" y
="20
" width
="150
" title
="Simple Window
">
<button
text
="My button
" onclick
="this.parent.parent.windowTwo.setAttribute('x', this.parent.parent.windowTwo.x + 20)
"/>
</window
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
That works, but the code is getting pretty messy. It would be more elegant to encase all the code in a block and call it whenever the button is clicked…. To do what, we could write a function:
<canvas
width
="100%
" height
="200
">
<script
>
// Moves the second window twenty pixels to the right
//
function moveWindow() {
var increment = 20;
var originalX = canvas.windowTwo.x;
var newX = originalX + increment;
canvas.windowTwo.setAttribute('x', newX);
}
</script
>
<window
x
="100
" y
="30
" title
="Window 2
" name
="windowTwo
">
<text
>This is the second window.
</text
>
</window
>
<window
x
="20
" y
="20
" width
="150
" title
="Simple Window
">
<button
text
="My button
" onclick
="moveWindow();
"/>
</window
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Notice how we use the "canvas.
" syntax for pointing to the second
window. We have to address the view absolutely. The code
is a lot easier to understand, because we can break it up over several lines, comment it
and assign appropriately-named variables.
However, the function is pretty detached from the button. A more elegant way of achieving the same result would be to write a method of the button.
<canvas
width
="100%
" height
="200
">
<window
x
="100
" y
="30
" title
="Window 2
" name
="windowTwo
">
<text
>This is the second window.
</text
>
</window
>
<window
x
="20
" y
="20
" width
="200
" title
="Simple Window
">
<button
text
="My button
" onclick
="this.moveWindow()
">
<!-- Moves the second window twenty pixels
to the right -->
<method
name
="moveWindow
">
var increment = 20;
var originalX = this.parent.parent.windowTwo.x;
var newX = originalX + increment;
this.parent.parent.windowTwo.setAttribute('x', newX);
</method
>
</button
>
</window
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Since methods are not not global, we have to call them relatively. In the case of the
button, we use this.moveWindow()
. In theory we could have a second
button that, when clicked, would call a method of the first button. The only difference
would be the addressing. Before we go any further with methods, let's take a proper look
at addressing:
In LZX, objects can have name
s or id
s or both by
which they can be addressed. A name
needs to be referred to locally, so
there can be more than one view with the same name in a file (they just can't be siblings).
An id
is global, so there can't be two views with the same
id
in a LZX file.
Going back to the idea of having one button call the second button's method:
<canvas
width
="100%
" height
="200
">
<window
x
="100
" y
="60
" title
="Window 2
" name
="windowTwo
">
<text
>This is the second window.
</text
>
</window
>
<window
x
="20
" y
="20
" width
="210
" title
="Simple Window
">
<simplelayout
axis
="x
" spacing
="4
"/>
<button
text
="My button
" name
="button1
" onclick
="this.moveWindow()
">
<!-- Moves the second window twenty pixels
to the right -->
<method
name
="moveWindow
">
var increment = 20;
var originalX = this.parent.parent.windowTwo.x;
var newX = originalX + increment;
this.parent.parent.windowTwo.setAttribute('x', newX);
</method
>
</button
>
<button
text
="Bigger Button
" onclick
="this.parent.button1.moveWindow()
"/>
</window
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Both buttons now cause the window to move. However, it's confusing that one button
points to a method in another button. Since windowTwo is doing the moving, why not make the
method part of the window, and have both buttons point to that? Instead of addressing it
with the whole this.parent…
dot syntax, we can give it an
id
, and access it globally:
<canvas
width
="100%
" height
="200
">
<window
x
="100
" y
="60
" title
="Window 2
" name
="windowTwo
" id
="windowTwoId
">
<!-- Moves the second window twenty pixels
to the right -->
<method
name
="moveWindow
">
var increment = 20;
var originalX = this.x;
var newX = originalX + increment;
this.setAttribute('x', newX);
</method
>
<text
>This is the second window.
</text
>
</window
>
<window
x
="20
" y
="20
" width
="210
" title
="Simple Window
">
<simplelayout
axis
="x
" spacing
="4
"/>
<button
text
="My button
" name
="button1
" onclick
="windowTwoId.moveWindow()
"/>
<button
text
="Bigger Button
" onclick
="this.parent.parent.windowTwo.moveWindow()
"/>
</window
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Just to illustrate id
s and name
s, one button is
addressing the window relatively using its name
, and the other globally,
using its id
. Note that the id
and
name
could have been the same; they were intentionally different in this
example.
In the previous example we have two buttons that do the same thing. Why not make them do different things? Move the box left and right, perhaps? We might write another method to move the box to the left, but it would be more elegant to use the same method for both directions. Just as with functions, we can pass arguments to methods.
Here's one possible solution:
<canvas
width
="100%
" height
="200
">
<window
x
="100
" y
="60
" title
="Window 2
" name
="windowTwo
" id
="windowTwoId
">
<!-- Moves the second window twenty pixels
in specified direction -->
<method
name
="moveWindow
" args
="direction
">
// decide which direction to go
if (direction == "left") {
var increment = -20;
} else if (direction == "right") {
var increment = 20;
}
var originalX = this.x;
var newX = originalX + increment;
this.setAttribute('x', newX);
</method
>
<text
>This is the second window.
</text
>
</window
>
<window
x
="20
" y
="20
" width
="210
" title
="Simple Window
">
<simplelayout
axis
="x
" spacing
="4
"/>
<button
text
="Move Left
" name
="button1
" onclick
="windowTwoId.moveWindow('left')
"/>
<button
text
="Move Right
" onclick
="windowTwoId.moveWindow('right')
"/>
</window
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
We can pass more than one argument to a method, just as with a function:
<canvas
width
="100%
" height
="200
">
<window
x
="100
" y
="60
" title
="Window 2
" name
="windowTwo
" id
="windowTwoId
">
<!-- Moves the second window twenty pixels
in specified direction -->
<method
name
="moveWindow
" args
="direction, distance
">
// decide which direction to go
if (direction == "left") {
var increment = -1 * distance;
} else if (direction == "right") {
var increment = distance;
}
var originalX = this.x;
var newX = originalX + increment;
this.setAttribute('x', newX);
</method
>
<text
>This is the second window.
</text
>
</window
>
<window
x
="20
" y
="20
" width
="300
" title
="Simple Window
">
<simplelayout
axis
="x
" spacing
="4
"/>
<button
text
="Left 2
" name
="button1
" onclick
="windowTwoId.moveWindow('left', 2)
"/>
<button
text
="Left 20
" name
="button2
" onclick
="windowTwoId.moveWindow('left', 20)
"/>
<button
text
="Right 20
" onclick
="windowTwoId.moveWindow('right', 20)
"/>
<button
text
="Right 2
" onclick
="windowTwoId.moveWindow('right', 2)
"/>
</window
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Copyright © 2002-2010 Laszlo Systems, Inc. All Rights Reserved. Unauthorized use, duplication or distribution is strictly prohibited. This is the proprietary information of Laszlo Systems, Inc. Use is subject to license terms.