OpenLaszlo Application Developer's Guide Developer Guide Preface
Preface We invented this technology to make it possible to create web applications that are delightful to use. Every computer user knows the power of the Internet, and every web surfer has been awed at one time or another by the vast wealth of the Web. But at the same time everyone knows, even if they only know it subconsciously, that web-based applications running in a browser offer a user experience that is inherently inferior to that of locally-running programs. There is something almost retrograde about interacting with websites; they're anomalous, like rotary telephones in a touch-tone world. The reason for this is clear. It's not because web designers don't know anything about usability or visual design or software engineering. It's because they have not had the tools they require to build the web experience they conceive. HTML is a great thing; DHTML is a great thing. Adobe's Flash is a great thing. But none of them allow developers to create astounding user experiences, because all of them are based on fundamentally limited models. The variants of HTML are based on the metaphor of a "page," which forces users to process information in arbitrary discrete chunks separated by jarring transitions and "page refreshes." Flash offers the possibility of a much more continuous user experience, but its "movie" metaphor forces developers to leave behind many essential tools and techniques of modern software development. In contrast, the OpenLaszlo solution was built from the ground up for application development ??? not "page" development, not "movie" development ??? and is centered around standard development approaches. LZX applications are written in XML files with embedded JavaScript, which provide an ideal foundation for serious developers. OpenLaszlo supports standards-based object-oriented development and data binding, and enables rich interactivity without requiring complex, timeline-based visual authoring. The result is applications that are delightful to use. Now, delightfulness is in the eye of the beholder, of course, and that's why we suggest that you right now click on our e-commerce application. This program communicates with Amazon.com's data servers and allows you to browse their products, create a wish list, fill a shopping cart, and so forth. So in many ways it's just like any other similar e-commerce site. But notice that there are no screen refreshes, and that you can move items by dragging them with the mouse, that it's hard to get "lost." Now consider that this program was written in a few hours using a simple text editor. We suggest that you play around with any of the dozens of other sample applications here or on the Laszlo Systems website to get a feel for what a Laszlo application is like. One sometimes hears the phrase "Rich Internet Application" to describe programs like these, and their defining characteristic is that they make people smile. A growing body of empirical studies source shows that when people have a more pleasant experience on a website, the longer they stay there, and on commercial sites, the more commerce they transact.
Audience and rationale for this book This book is written for software developers who have familiarity with the concepts of object-oriented programming as implemented in languages such as Java, C++, or JavaScript, and with the fundamentals of XML data formats. We also hope that this guide will be accessible to web developers who know HTML and CSS but haven't yet worked with object oriented languages, and to developers and designers familiar with Flash or FlashMX. If this describes you, you may have a little homework to do outside the scope of this book, but in general you should have no trouble becoming a fluent LZX developer. A good place for you to start learning would be with the tutorials.
On Runtimes and Deployment Modes and the "Core" API OpenLaszlo applications can be compiled to run in any of several runtimes, and in either of two deployment modes. Some capabilities are available in only a subset of these use cases. OpenLaszlo's "core" APIs run on all target runtimes, and they get the most attention in this Guide. The "Core API" is kind of loose concept, since there's no formal definition, but what it means is that unless you see indication to the contrary, you can assume that the topic under discussion applies to all runtimes. Similarly the default deployment mode is assumed to be SOLO. Nevertheless, as a developer you have to decide whether to: optimize for swf (employ swf-only APIs, (and maybe even touch Flash APIs))optimize for dhtml (employ dhtml-only APIs)code to lowest common denominator ("core" APIs only)conditionalize (include both. An example would be including comma separated list of embedded and system fonts; when compiled to swf you get embedded font, to dhtml you get system font). The focus of the OpenLaszlo Application Developer's Guide is (3), the lowest common denominator; runtime-specific APIs and engineering approaches are presented as adjuncts. Graphic devices ("glyphs") in the margin call attention to non core/non SOLO information. FIXME: illustrate the glyphs here when they're ready Depending on which approach you're taking, you'll make different engineering decisions and different approaches to reading the this Guide. For example, if you're determined to deploy SOLO, you can just skip all the chapters marked with the "Server Required" device, etc. Finally, this Guide does not address any browser-specific considerations. If a thing does not run on all supported browsers for a given runtime, it's by definition not supported and thus should not be cluttering up our lovely Guide. Nonstandard, browser-specific things are described in the release notes or the OpenLaszo Wiki.
Learn by Doing This Guide contains hundreds of "live" examples, that is, Laszlo Applications that are embedded in this page. You'll have more fun and get more out of this document if you "play with the code." To do so, simply click on the "Edit" link at the bottom left corner of each example. This will cause a new window to pop up that is a self-contained environment for modifying and executing Laszlo applications. Edit the sample code then click "update" and see the results displayed in the right-hand side of the window. To return the sample code to its initial state, click "Reset." If you would like to save a copy of the application for later use, click on the "Save As" button.
Sequential and Random Access Designing, Developing and Deploying Laszlo Applications is intended for sequential and random access. In general, each chapter draws on the concepts covered in the chapters that precede it, but not (too much) on those in the chapters that follow it. If you start with Chapter One and read to the end, you won't get lost. At the same time, each chapter is designed to be encyclopedic: the chapter on Animation covers the entire subject of animation, and so forth. Short, self-contained examples appear throughout. Links are provided to longer examples.
Typographic Conventions Code is displayed thus: var a = f(2). Tags are displayed thus: <canvas> and their attributes are displayed like this: height. Function names are displayed like this: setAttribute(). Longer sections of code and other verbatim information is displayed in a separate paragraph: An example file that spans several lines Example programs are shown thus: <canvas height="20"> <text>Hello World!</text> </canvas> Some example programs are run within the page. The running application is shown first, followed by the application source code, followed by an Edit button that allows you to try out the effect of making changes to the application source code. width: 100%height: 20<canvas height="20" width="100%"> <text>Hello World!</text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** TODO: Material marked thus is only shown in the beta distributions of the documentation, and must be eliminated before the product is final. FIXME: Material in fixme is only shown internally, and should not be present in beta or final distributions.
Related documentation In addition to this OpenLaszlo Application Developer's Guide, please see also The LZX Reference Manual. This online, hyperlinked, comprehensive reference for LZX contains entries for each LZX tag and API. Its format will be familiar to users of JavaDoc(tm) and similar systems. The online tutorials and sample applications at http://www.laszlosystems.com/demos provide a hands-on instructional overview of the LZX language.
Suggested reading paths TODO: This section, ideally, may be replaced with an interactive (lzx) map. The best way to use the LZX Developer's Guide depends on your experience. Experienced Object-Oriented programmers: We suggest that you start by reading Chapters 1, 2 and 4 of this Guide. If you have never worked with JavaScript, you can learn the basics quickly at one of the JavaScript tutorials at W3Schools, WVDL, or PageResource.com. Then visit the OpenLaszlo website and play around with the tutorials and sample programs; as you read the sample code be sure to look up appropriate entries in the Reference Manual. At that point you'll know what further reading you need to do in this guide. Web/Flash developers with no Object-Oriented experience: We suggest that you start with the tutorials in order to get a general feel for LZX programming. Don't worry about mastering the subjects they cover; your goal is to begin to get comfortable with the language. If you need to, learn the basics of XML. Then read the rest of . TODO: Work your way through a JavaScript tutorial link. Caution: it makes sense to read the first four chapters of this book before doing the JavaScript tutorial, because LZX employs JavaScript in a way that is quite distinct from the way it is used in web browsers. TODO: Then read Chapters 4 through 12 of this manual, making sure that you edit the sample programs. After you've done that you'll be an experienced Object Oriented programmer and able to decide for yourself how best to proceed from there.
Use the Forums The best way to learn any programming language is to draw upon the experience and goodwill of its development community (and to write code! as the saying goes, nobody learned to ride a bicycle by reading about it). The LZX developer community has a home at http://www.laszlosystems.com/developers/community/forums/. There you will find an active discussion of all aspects of the language, and a place to ask questions of your own.
Overview OpenLaszlo Architecture OpenLaszlo is a platform for rich Internet applications that are easy to develop and deploy. The OpenLaszlo system architecture combines the power and usability of client/server design with the administrative advantages and cost effectiveness of web applications. OpenLaszlo applications are written in the XML language LZX, which compiles to any of several runtime targets, including, as of OpenLaszlo 4.0, sw7, sw8, and native browser JavaScript, also sometimes called DHTML or Ajax. Applications compiled to SWF8 or SWF9 run in the Flash 9 player.
Deployment Modes OpenLaszlo applications can be made available on the web, or deployed in either of two ways: Proxied. The OpenLaszlo Server runs on your machine and Compiles source programs as necessary and sends the resultant file to be executed on the client; andProxies interactions between the client and other servers on the Internet, performing data manipulation as necessary SOLO You use the OpenLaszlo compiler to "precompile" programs and make the resulting file (in SWF or JavaScript) available on your server. When executed on the client, the application contacts other servers directly, without mediation by the OpenLaszlo Server. This "serverless", or Standalone OpenLaszlo Output deployment is called SOLO.
Applications may be deployed either SOLO or proxied by the OpenLaszlo Server
Later chapters explain in detail the differences between proxied and SOLO OpenLaszlo applications. In general: Proxied applications can do a few things that SOLO applications cannot do, but they are more trouble to deploy and sometimes perform more slowly.SOLO applications are easier to deploy and sometimes better-performing. In many cases you don't need to decide which deployment mode to use until you're ready to deploy, and the default choice is usually SOLO. In reading the discussions below, keep in mind that when you deploy your applications statically, the run-time capabilities of OpenLaszlo Server (such as media transcoding) will not be available.
Client and Server: Synopsis The OpenLaszlo Server is a Java application that executes in a J2EE servlet container. The OpenLaszlo Server may communicate with back end servers and data sources using a variety of protocols. OpenLaszlo applications written in LZX are compiled by the OpenLaszlo Server and served either as bytecode to a plug-in that runs in the client's web browser (such as Flash or J2ME) or as JavaScript (DHTML) that is directly executed by the browser itself. This constitutes the front endfront end. OpenLaszlo applications run consistently and reliably on a variety of operating systems and device types, including Windows, Pocket PC, Mac OS, Linux, and Solaris, and a variety of mobile and set-top box platforms. When compiling for execution by the Flashplayer, the OpenLaszlo Server outputs bytecode in the SWF ("shockwave format") file format recognized by the Macromedia Flash player (version 7 and higher). When compiled for execution natively in the browser, the OpenLaszlo Server outputs JavaScript. This script is human readable but optimized for size and execution speed, with comments removed, variable names shortened, etc. In the future, OpenLaszlo may support other run-time clients as they become widely available.
OpenLaszlo Compiles to Different Runtimes
In the OpenLaszlo context, clientclient means an LZX application executing in user's web browser, and serverserver means OpenLaszlo Server (which may, in turn, communicate with other servers). The LZX client and OpenLaszlo Server server communicate over HTTP; the OpenLaszlo Server sends bytecode and the LZX application sends XML. All OpenLaszlo platform features, including streaming media and notification, are delivered over HTTP or HTTPS. OpenLaszlo-based applications thus maintain compatibility with standard corporate firewalls. This is a critical capability for public Internet applications.
Laszlo Server Architecture The OpenLaszlo Server executes within a standard J2EE application server or Java servlet container running JRE 1.4 or higher. These application server environments scale well, as does the OpenLaszlo Server itself. OpenLaszlo applications can run on any OS supported by these server products. OpenLaszlo supports Windows, Solaris, Linux and Mac OS X server environments. The OpenLaszlo Server consists of four main subsystems: the Interface Compilerthe Media Transcoderthe Data Manager the Cache
OpenLaszlo Server Architecture
The Interface Compiler The Interface Compiler consists of an LZX Tag Compiler, and a Script Compiler. The Interface Compiler additionally invokes the Media Compiler and the Data Manager to compile media and data sources that are baked in to the application. The LZX tag and script compilers convert LZX application description tags and JavaScript into executable (swf) bytecode for transmission to the OpenLaszlo client environment. This code is placed into the cache, from which it is sent to the client. Depending on how the application is invoked, it is transmitted either as a SWF file, or as an HTML file with an embedded SWF object. The Media Transcoder converts a full range of media assets into a single format for rendering by OpenLaszlo's target client rendering engine. This enables an OpenLaszlo application to present supported media types in a unified manner on a single canvas, without the distraction of multiple helper applications or supplemental playback software. The Media Transcoder automatically transcodes the following media types: JPEG, GIF, PNG, MP3, TrueType, and SWF (art/animation only).
The Data Manager The Data Manager is comprised of a data compiler that converts all data into a compressed binary format readable by OpenLaszlo applications and a series of data connectors that enable OpenLaszlo applications to retrieve data via XML/HTTP. OpenLaszlo applications can thus interface across the network with databases, XML Web Services, and Web-server based files or executables.
The Cache The cache contains the most recently compiled version of any application. The first time an OpenLaszlo application is requested, it is compiled and the resultant SWF file is sent to the client. A copy is also cached on the server, so that subsequent requests do not require waiting for the compilation.
OpenLaszlo Client Architecture OpenLaszlo's client architecture consists of the OpenLaszlo runtime library called the Laszlo Foundation Class, or LFC, a core library compiled into every OpenLaszlo application that provides run-time services (such as, for example, a timer and an idling function) and a presentation renderer to provide 2D graphics rendering and sound playback. None of these classes rely on Flash services or use the Flash object model. When compiled to .swf format, the Flash Player is used solely as a rendering engine.
Laszlo Client Architecture
Empty Application<canvas/> When this application is running, even though it isn't "doing anything," it is maintaining a connection with the server, and all the capabilities necessary for running an LZX application have actually been downloaded. There are four primary components within the LFC: the Event System, the Data Loader/Binder, the Layout & Animation system and a set of Application Services.
The Event System The event system recognizes and handles application events such as user mouse clicks or server data pushes. This component permits standard event-based programming on the client. Relative to conventional Web implementations, OpenLaszlo applications typically reduce the processing load placed on the host server, by enabling tasks such as client-side sorting, processing, validation, and dynamic display across all application states.
The Data Loader/Binder The data loader serves as a data traffic director, accepting data streams across the network from the OpenLaszlo Server and binding data to appropriate visual display elements such as text fields, forms, and menu items.
The Layout and Animation System The layout and animation system provides OpenLaszlo applications with a constraint-based screen layout of interface elements and algorithmic animation of interface state changes. This component enables you to build a dynamic application interface with minimal programming. It allows you to position a variable number of interface elements using either relative positioning or absolute pixel positioning. With the animation algorithms, screen interface updates are rendered in a visually continuous manner, clearly communicating the application's state changes to the user.
Openlaszlo Application Software Stack
OpenLaszlo Services System The OpenLaszlo runtime includes support for timers, sound and modal dialogs.
Data Flow of an OpenLaszlo Server-proxied OpenLaszlo Application This example uses an OpenLaszlo application stored on the server as weather.lzx. The following diagram illustrates how this application would be executed by the OpenLaszlo Server.
OpenLaszlo Application Data Flow diagram for the Weather Application (proxied)
Starting with a user entering a URL that requests the Weather application, this diagram illustrates the data flow sequence from client to server, integrating data from an XML Web service, and sending the result back to the client. In OpenLaszlo applications, presentation-related logic is separated from business logic and executed locally on the client. The OpenLaszlo server sends the client compressed binary data rather than text, reducing the amount of data transported in comparison to HTML-based and other Web applications. Caching on both the server and client eliminates unnecessary code execution and data transmission.
Security Model The OpenLaszlo application platform supports the SSL security model. Data transmissions across the Internet can be encrypted using SSL encryption over HTTPS. When compiled for native execution by the browser, the security model is that of whichever browser is used to execute the application. The security model employed by OpenLaszlo applications differs slightly depending on the runtime target. OpenLaszlo applications compiled to swf format execute on the client computer within the secure "sandbox" environment of the Flash Player, and cannot write to the local file system or access the client's native environment. Web services and databases used by an OpenLaszlo application are also secured using a per-user authentication model. This mechanism prevents use of the OpenLaszlo Server as a proxy or gateway into insecure services or data.
Platform Support for Multiple Devices The OpenLaszlo architecture is designed to support multiple device types. Its dynamic layout mechanisms enable simple modifications to such properties as an application's overall size to be intelligently applied by the platform. This simplifies adapting an application to work on screens and devices of different size. All screen visualizations in OpenLaszlo applications use time-based rather than frame-based animation, and thus transparently accommodate the processor speed differences of various device types. An interface transition specified to take 500 milliseconds will take 500 milliseconds regardless of the number of frames shown ??? faster processors result in more frames and smoother animation, but the same duration of transition.
Accessibility OpenLaszlo provides partial support for the Microsoft Active Accessibility specification, which states, "By using Active Accessibility and following accessible design practices, developers can make applications running on Windows more accessible to many people with vision, hearing, or motion disabilities." For a discussion of this feature, see . This support requires installation of third party software on the client machine, and is only available in the OpenLaszlo targets that run the Flash Player under Internet Explorer.
Language Preliminaries 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 dozens of programmable attributes, including size, position, background color, opacity, clickability, stretchability, 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:
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. FIXME: At some point we will have to mention CSS here. LZX strictly adheres to XML and JavaScript syntax In earlier releases, LZX did not enforce case sensitivity in JavaScript. Since OpenLaszlo release 3.0 LZX has been a completely case-sensitive language. See below for a more in-depth discussion. 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.
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. 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 capitalized, e.g. "String", "Number". The XML type names are used in <attribute name="foo" type="string"/>; they're lowercase for compatibility with the XML Schema Description datatypes.
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 quotes, by " 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.
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"). 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, that is, the namespace doesn't point to anything in particular on the Laszlo Systems website (if you click on it you'll get a 404 error). For more on namespaces, see the W3C specification.
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. In order to write reasonable LZX code you will need to be comfortable with these aspects of JavaScript: basic control structures (for, while, etc.); the object model; "loose" data typing; scoping, that is, the range of namespaces within a program. 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, in particular with regard to data typing, the object model, and the scope or namespace of variables. Like other so-called scripting languages such as Perl and Python, JavaScript is a loosely-typed language ??? you can declare a variable without specifying its type. This makes for rapid prototyping and arguably more readable code, but it also makes it possible for data-type errors to go undetected. 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 library is present in LZX, the the REGXEP library for regular expressions is not. Moreover, LZX has a more complete object-oriented programming model than does simple JavaScript. That is, LZX has classes and inheritance. If you have experience with neither Java nor JavaScript you may want to first work through a JavaScript tutorial like the ones available at w3schools.com before delving too deeply into LZX.
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.
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.
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 =`. `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). Functions close over their scope when they are defined, so by wrapping `with (_root)` around each global function definition, free references in functions will be looked up in the global context (as opposed to the SWF implementation, where global functions are given a null scope, so when they are called from the idle context, their free references appear unbound).
"this" in states and animators Inside Animators, the "this" keyword refers to the animator, and "parent" refers to the view or node it is nested in. In states, the "this" keyword refers to the view or node that state is nested inside of. Methods nested inside of a state apply to the view. There is an exception to the above rule: when listening to the onapply or onremove events, the methods apply to the state, and the "this" keyword points to the state.
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<view>[unknown tag] tag. If your program contains a view<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.
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<view> tag corresponds to an lz.viewlz.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 .
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 49 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<class>[unknown tag] 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. discusses in depth when and how objects are defined in your code and built by the compiler at runtime.
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"/> demeanordemeanor 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 heightheight and widthwidth. The attribute<attribute>[unknown tag] 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 heightheight 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. heightheight is also an attribute, in the XML sense, of the first attribute<attribute> tag. Its value can be referenced by script as myview.height. You also use the attribute<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 fifty attributes: the 49 that it has inherited from view, plus the new attribute named whatnotwhatnot. 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.
Events and Methods
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 approximately two dozen defined events, as listed on the entry for view<view>[unknown tag] in the LZX Reference Manual. Many of these events that deal with user input, such as onblur eventonblur, onclick eventonclick, onkeydown eventonkeydown, will be familiar to JavaScript programmers. Other events, such as onheight eventonheight and onopacity eventonopacity, pertain to views' visible attributes. Finally, the oninit eventoninit and onconstruct eventonconstruct 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()setAttribute() method is to set the named property and send the event called "on" + property. For instance, when a view changes its xx (horizontal) position, it sends the event onx eventonx with the new value for its xx 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, discusses the relationship between events and delegates.
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.
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 eventonclick event causes the execution of the handler()handler() which in turn invokes the method named bark()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<method>[unknown tag] 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 .
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 constraintwhen is an optional compiler directive: immediately, once, or always. $always{expression} can be abbreviated to ${expression} { and } are tokens delimiting the expression to be evaluatedexpression is a JavaScript expression As we have seen above, whenever the value of an attribute changes, its on eventon 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 eventon 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. covers constraints in depth.
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 . 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. covers this topic in depth.
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 namename attribute is local and the idid 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<script>[unknown tag] tag can be accessed from anywhere in the program.
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. discusses this subject in depth.
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<dataset>[unknown tag] 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<dataset> are called lz.datasets. Methods on lz.datasets allow you to, for example, get and set query strings, parameters and so forth.
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()selectNext() and by running an XPath request using setXPath()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()addNode(), setNodeName()setNodeName(), setXpath()setXpath(), and selectParent()selectParent(), you can build and manipulate XML structures.
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 ; you may also want to examine some of the examples on http://www.laszlosystems.com/demos 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.
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.
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.viewlz.view and the tag name view<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. At some point, however, the old forms will probably be deprecated, and it would be a good practice to adopt the new form in your code.
How to Combine Tags and Script Let's start by making a distinction between what is syntactically allowable and what is meaningful.
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 escapingescaping delimiter characters with an entity referenceentity 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 datacharacter data. This is the sum total of rules for making sure that XML does not trip on JavaScript.
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" .
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.
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<canvas>[unknown tag] . Every LZX program begins with <canvas> and ends with </canvas>; there is no alternative structure using script. Similarly there are no script equivalents for splash<splash>[unknown tag] , method<method>, attribute<attribute>, resource<resource>[unknown tag] , font<font>[unknown tag] , and several other tags. Moreover, within certain tag definitions there are certain attributes that can only be set in the tag.
Use JavaScript when that is the only option. There are several JavaScript APIs that perform functions that cannot be done using tags. For example, LzDelegate, lz.event, LzParam 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.
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.
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. FIXME: Appendix _____ contains a schematic overview of what functions can be done by tag alone, script alone, or by both.
Compilation and Execution LZX programs are compiled by the OpenLaszlo Compiler, downloaded as byte code, 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. The splash<splash> tag allows you use the canvas to display information while the program is being initialized.
On Runtimes and Deployment Modes and the "Core" API The following section also appears in the Preface; we repeat it here on the assumption that the Preface is an often-skipped part of any manual. OpenLaszlo applications can be compiled to run in any of several runtimes, and in either of two deployment modes. Some capabilities are available in only a subset of these use cases. OpenLaszlo's "core" APIs run on all target runtimes, and they get the most attention in this Guide. The "Core API" is kind of loose concept, since there's no formal definition, but what it means is that unless you see indication to the contrary, you can assume that the topic under discussion applies to all runtimes. Similarly the default deployment mode is assumed to be SOLO. Nevertheless, as a developer you have to decide whether to: optimize for swf (employ swf-only APIs, (and maybe even touch Flash APIs))optimize for dhtml (employ dhtml-only APIs)code to lowest common denominator ("core" APIs only)conditionalize (include both. An example would be including comma separated list of embedded and system fonts; when compiled to SWF you get embedded font, to DHTML you get system font). The focus of the OpenLaszlo Application Developer's Guide is (c), the lowest common denominator; runtime-specific APIs and engineering approaches are presented as adjuncts. Graphic devices ("glyphs") in the margin call attention to non core/non SOLO information. FIXME: illustrate the glyphs here when they're ready Depending on which approach you're taking, you'll make different engineering decisions and different approaches to reading the this Guide. For example, if you're determined to deploy SOLO, you can just skip all the chapters marked with the "Server Required" device, etc. Finally, this Guide does not address any browser-specific considerations. If a thing does not run on all supported browsers for a given runtime, it's by definition not supported and thus should not be cluttering up our lovely Guide. Nonstandard, browser-specific things are described in the release notes or the OpenLaszlo Wiki.
OpenLaszlo for Designers
Overview This chapter discusses the OpenLaszlo platform from the point of view of the designer. However your team is set up, somebody is responsible for the aesthetic experience of the application you're building. This chapter is for them. We'll discuss OpenLaszlo's ability to support designers and engineers in building innovative Internet applications.
Learning to Think Laszlorificly With OpenLaszlo technology, web application designers are freed from the familiar limitations of static, linear, page-based task flow. Your visions of dynamic, data-driven, concise and intelligent UI can become a reality. But you may need to change your thinking a little bit, because things are possible with OpenLaszlo that aren't possible with most other technologies. And while it is not necessary for you to learn to write code in order to be an effective designer on an OpenLaszlo project, a familiarity with the basic structure and features of the LZX language will help you understand how to approach your task. So we recommend that you at least work through a few of the tutorials. Some thoughts on how OpenLaszlo is different as a web design technology: You have complete control of the end user experience. Most web design technologies cause the application to look different depending on the OS and browser used to deliver it. This is often a design challenge that can lead to unwanted compromises. With OpenLaszlo, especially when you compile to SWF, you have control of every pixel and gesture. Fast prototyping: OpenLaszlo offers a complete set of UI components that includes UI controls supported by HTML (radio buttons, checkboxes, etc.) along with standard desktop UI (windows, menus, tree control, tabs) and more. These components in the context of a concise declarative XML for application layout and event-based interaction login allow for high productivity. Object oriented programming: You can organically move from prototyping to serious application development though Laszlo's powerful class declarations that support full inheritance. In-page transactions: OpenLaszlo applications do not require a "page refresh" when you get data from the server. This allows for data to be delivered to the user in a way that is integrated seamlessly into the UI. It also allows for "ambient" data ???data updates asynchronously and is provided in context. When approaching a project, think about desktop applications that you were able to use effortlessly and intuitively without ever picking up a manual. Recall a beautiful UI from some Flash site and imagine it being successfully data-driven, scalable and easily modified. Spend some time walking around the problem. Look at it sideways. Slice it into cross sections, rearrange the pieces and then use those perceptions to drive your design.
Continuous interaction instead of page refresh Consider the task or tasks to be accomplished by the end-user, and the flow to completion. One of the key benefits of using OpenLaszlo to build your project is its strength in consolidating information through interaction and presentation. A process that might have taken 20 html pages to accomplish for the very patient user, might now be designed to occur within the framework of a single "page" with small bits of information elegantly surfaced as needed. This ability to present information in a useful, contextual manner, regardless of complexity, is the foundation of a successful application.
Continuous interaction
Design Process Design processes are unique to the experience and culture of a team. Overlapping skill sets between designers who write code and engineers with design sensibility mean that there are often no strict divisions of labor. Yet all teams need to spend time dissecting the problem, brainstorming, and arriving at a unanimous understanding of, and agreement on the solution. These are some time-prooven steps in the process: Wire-frames Wire-frames. Wire-frame examples of layouts will help begin the visualization of the pieces as a whole. These are basic definitions of placement for the UI; often including navigation, branding and content. Storyboards Storyboards. Once a structure has been defined, storyboards of both general and detailed task flow help to refine and communicate the vision of interactivity. Storyboards can begin as simple flowcharts and evolve into pictures of each state of each element. The complexity of the problem and solution will likely dictate how obsessive the storyboarding needs to be. Animations Animations. Simple, linear animations of functionality and process can help both engineers and designers evaluate and experiment with interactive elements. Engineering prototypes Engineering prototypes. Once all (or some subset) of these are mapped and agreed upon, the engineering team can begin to block out the framework using place-holder artwork or primitive shapes in LZX.
Design process
How Does It Look? While engineering is busy developing the application's infrastructure, designers can resume focus on the "look" of the application. Some designers prefer to have a fully flushed-out design before any coding starts, but the reality of deadlines and the likelihood of iterative modification means that visual design and production will likely happen in parallel to coding efforts. Elemental design decisions need to be made. Are your users going to be more comfortable with traditional UI elements, or is there room to experiment with new methods of representation, interaction or display? Comps for the look and feel should be designed in the application you are most comfortable with. Bitmap and vector tools are equally viable, and resources of either format can be leveraged when producing art resources.
Using Resources Resources are the visual assets used by LZX code to construct the look of an application. LZX supports numerous image formats, including bitmap (GIF, JPEG, PNG) and vector (SWF) files. Animations and video (in the form of multi-frame SWF files) as well as audio (MP3 files) can also be integrated into applications. Resources are often constructed of multiple pieces assembled to create various UI and their states (up, down, rollover, etc). To enable these states LZX provides multi-frame resources. It is also possible to use a single resource and affect its look via code (eg: tinted, resized, repositioned, etc.). In the LZX language, color, tint, brightness and opacity of resources can be modified programmatically.
Using resources
Bitmap vs. Vector-Based Resource Files The decision to work with bitmap or vector files is dictated by the design and the runtime to which you're compiling. If many complex, fixed-size objects, such as gradients, shadows and/or alpha elements are used, bitmap resources are more appropriate. Vector files, though generally physically smaller (file size), require more processing power to draw. The other major consideration is whether the design requires the resizing of resources. If a resource must scale, you should use a vector file, as it will do so without degrading. Scaling bitmap files to a size larger than the original will cause interpolation and image degradation. In general SWF files are the most flexible in applications compiled to run in the Flash player. Although the obvious tool for creating SWF files is Macromedia Flash, many vector-based applications, such as Adobe Illustrator allow export of files to this format. If the option is given, SWF files should be saved as version 5.0. The exporter for Illustrator 10 does not provide an option, but uses 5.0 by default. With the exception of the "stop" command, Flash ActionScript should not be used in SWF files included in LZX.
Fonts Your options with regard to fonts differ greatly depending on whether the application will be compiled to SWF or DHTML. In applications compiled to SWF, you have the option to include virtually whatever font you like within the application. Applications that are to be compiled to DHTML, on the other hand, are limited to those fonts that are commonly found in all runtime environments (that is, all supported browsers and operating systems). OpenLaszlo supports all True Type Format (TTF) fonts. In applications compiled to SWF, embedded fonts ensure that the appearance of type within your application will be consistent across all browsers and operating systems. The default font in OpenLaszlo applications is the Ultra Pixel Font "Verity". The Flash Plugin (which is responsible for rendering LZX files) displays small outline fonts poorly so designers frequently use bitmap (aka pixel) fonts at small sizes (usually less than 12pt). Bitmap fonts circumvent the rendering problem by eliminating anti-aliasing. Though quite legible, traditional bitmap fonts lose the nuances of traditional typography. Ultra Pixel Fonts employ fixed anti-aliasing, which maximizes legibility while retaining smooth characters and distinct form. Verity was designed to mimic the Bitstream outline font "Vera" (also included in the OpenLaszlo Server).
Fonts
At larger sizes, Vera can be seamlessly integrated with Verity. Bitmap fonts such as Verity only render correctly when specified at 8pt. The Verity TTF file (9pt or 11pt) must be used to change size. Verity contains an unaccented subset of characters; for the full Windows Western Unicode font set, use VerityPlus, which includes accented European characters. The Bitstream font Vera was released as an open source font for the Gnome Project. The Ultra Pixel font Verity was built by Christopher Lowery using technology produced by Truth in Design. For more on fonts, see OpenLaszlo Application Developer's Guide.
Designing Custom Components For details on designing user-interface components in OpenLaszlo, see .
Overview of OpenLaszlo Application Development This chapter summarizes information about OpenLaszlo application structure and mechanics that an experienced programmer will need in order to start playing with code. This discussion is necessarily abbreviated and incomplete; its purpose is merely to point you in the right direction. As you begin to write LZX applications, you should also work through the tutorials.
The program development cycle in a nutshell The program development cycle differs somewhat depending on whether you deploy your application proxied or SOLO. Developing proxied applications is the simpler case, so we'll start with that here. The process of developing an OpenLaszlo application can be summarized: start up the OpenLaszlo Server using a text editor, write program code; save the file with a .lzx extension place file in an appropriate directory compile the application debug and modify the program repeat steps 2-5 until the program is perfect deploy the application Each of these steps is described in turn below, and explored in greater depth throughout this Guide. But first we'll say a word about the Developer's Console, which is the default interface for performing common development activities.
The Developer's Console The Developer's Console is a small OpenLaszlo application for selecting things like the target runtime, the deployment mode (proxied or SOLO) and whether the debugger is included. When you first compile an OpenLaszlo application (as explained below), by default it is returned with the Developer's Console appearing at the bottom of the application. Depending on whether your compile to SWF (the default) or DHTML, a slightly different version of the Developer's Console is returned. The illustrations below call out features of both versions.
Turning off the Developer's Console To return the "naked" OpenLaszlo application without the Developer's Console, simply compile the application using the "URL" method of placing the URL to the application in your browser's address bar with ?lzt=html appended to the address. This will return the application in an HTML page. See and for elaboration of this topic.
Start up the OpenLaszlo Server The way to start the OpenLaszlo Server (OLS) depends on the operating system and how it was installed. On Windows, typically you start OLS from the Start menu; on Mac OS X the default installation places the OpenLaszlo launch icon on your desktop. If you don't have the OpenLaszlo Server installed on your machine, you can download it from http://www.openlaszlo.org/download.
Create a program file Because LZX files are XML documents, you can use any text or XML editor to create and edit source. Filenames must end with the .lzx extension. As you write, you'll want to have the LZX Reference Manual handy. See below for a discussion of how to use this document efficiently.
Place file in the appropriate directory In order to be compiled by the OpenLaszlo Server, files must be placed in subdirectories of the following path: [Windows] c:\Program Files\Laszlo Presentation Server 4.9.0\jakarta-tomcat-5.0.24\webapps\lps-4.9.0\ [MacOS] Macintosh HD:/Applications/Laszlo Presentation Server 4.9.0/jakarta-tomcat-5.0.24/webapps/lps-4.9.0: Typically you will create a directory with a name such as my-apps in which to place programs under development. You can nest subdirectories, such as my-apps/practice/samples so long as they are under the correct path to the OpenLaszlo Server (lps).
Compile the application See for a complete discussion of all compilation options. There are two distinct techniques for compiling the application: You can load it into a web browser, which will cause the OpenLaszlo Server to compile it automatically when the program is first used and any time it changes; orYou can invoke the stand-alone compiler separately. The simplest and most common way to compile applications, especially when you are first getting familiar with OpenLaszlo, is to let the OpenLaszlo Server handle it. Both techniques are described in turn below.
Compiling by loading the application into a web browser In order to run your program, simply load it into your browser. The exact URL depends on the configuration of the server, but will typically look something like: http://127.0.0.1:8080/lps-4.9.0/path to your directory The OpenLaszlo Server checks the source files for valid syntax, compiles them, caches the executables and makes the application immediately visible in the browser.
Using the standalone compiler lzc The standalone compiler is called lzc. Its location depends on how you have installed OpenLaszlo. Developers who build OpenLaszlo from the source will find lzc in $LPS_HOME/WEB-INF/lps/server/bin/. Developers who install OpenLaszlo with a binary installer will find it in the directory OpenLaszlo Server 4.9.0/bin. Developers with more than one version of the OpenLaszlo server in play should define an alias: alias lzc='$LPS_HOME/WEB-INF/lps/server/bin/lzc'. By defining an alias based on $LPS_HOME, one can have multiple shells -- each with a different $LPS_HOME -- and the alias will map to the correct compiler for each shell
Spurious Internet Explorer Content Block Warning If you open a local SWF file in Internet Explorer, or an HTML page that accesses a local SWF file in Internet Explorer, you will get a "blocked content" warning message. You can safely disregard this message and continue developing. Here is why: this is not a problem with OpenLaszlo nor is it a problem with IE per se. Rather it is a security feature built into IE to keep viruses from accessing ActiveX controls, and it only appears when you're developing applications, not when you're deploying them. Internet Explorer uses an ActiveX control to display Flash files. It assumes that a local file trying to access an ActiveX control may very well be a virus, so it puts up the warning. When you actually deploy your OpenLaszlo application, however, it will be served to your visitors through a Web Server (IIS, Apache, etc) off of an actual domain, and therefore will not show the error message. To verify this, run a web server locally on your system and serve the page off of your local web server. you will see that the error message will not be displayed. Please see also the discussion of SWFObject in for a more complete discussion of embedding Flash files in HTML pages.
Debug and modify the program If the Sever detects errors that prevent compilation, error messages are displayed in the browser. If it detects non-critical errors or questionable constructs that do not prevent compilation, warning messages are displayed in the browser below the application (you may have to scroll down to see them): Runtime errors are displayed in the debugger, if the debugger is running. See for a brief discussion of the debugger. See for a full discussion.
Iterate After you've made changes to the source, simply click the Refresh button on the browser. The OpenLaszlo Server automatically rechecks the source for syntax, then recompiles, re-caches and makes the application visible in the browser.
Optimize Optimize your program using the techniques in .
Deploy See for discussion of how to make your application available for general use.
From "Hello, World" to real programs The canonical "Hello, World" program can be written in LZX: Hello world width: 100%height: 40<canvas height="40" width="100%"> <text>Hello, World!</text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** This program illustrates three essential features of all Laszlo applications: LZX files are well-formed XML documentscanvas<canvas>[unknown tag] is the root node; programs open with the <canvas> tag and close with </canvas>Within the canvas, views are displayed. As we shall see, a text<text>[unknown tag] element is simply a kind of view. The next section discusses the ingredients of a typical OpenLaszlo application. See also the example programs to get a feel for the general structure of LZX applications.
Essential Structures Typical Laszlo applications contain the following parts, which are discussed briefly in turn below canvas ()views ()data ()libraries and includes ()comments ()
The Canvas The root node of every Laszlo application is the canvas; there is one and only one canvas per LZX application. The canvas is the logical container of all other nodes in the program; visually it is a rectangle that is displayed in the content area of a web browser. You can explicitly set the height and width of the canvas, in pixels, by assigning values to attributes in the opening tag. <canvas height="20" width="30"> </canvas> If you do not set the height and width, the canvas ??? like other views ??? sizes itself to the size of the views it contains. Unlike other views, the canvas, by default, has a nonzero width and height: it sizes itself to the HTML page that contains it. Therefore the null LZX application <canvas/> defines an invisible object that is the size of the page. In addition to its height and width, the canvas has several other visible attributes. The background color, defined by the bgcolorbgcolor attribute, is most useful for learning about the visual structures of LZX applications.
The <indexterm><primary>script</primary></indexterm><sgmltag class="element"><script></sgmltag><remark role="fixme">[unknown tag]</remark> <!--unknown tag: script--> tag Within LZX applications, you can embed arbitrary JavaScript functions by nesting them in script<script> constructs. This is helpful for defining (global) functions that will be used by different classes. The script<script> tag must be a child of canvas<canvas>. That is to say, <canvas> <script> var Constant = 1; </script> </canvas> is an allowed structure while <canvas> <view> <script> var Constant = 1; </script> </view> </canvas> Wrong! The script<script> tag is not allowed inside a view!
<indexterm><primary>name</primary></indexterm><sgmltag class="attribute">name</sgmltag> vs. <indexterm><primary>id</primary></indexterm><sgmltag class="attribute">id</sgmltag> In LZX the idid attribute of an object is a global identifier that is visible throughout the entire program space, while the namename of an object is an attribute like any other, which can only be referenced by its path (except in the case of named children of the canvas, as noted below). Consider <canvas> <view id="v1" name="outer_view"> <view id="v2" name="inner_view" bgcolor="blue"/> </view> </canvas> The value of the outer view's background color can be referenced as v1.bgcolor or outer_view.bgcolor. The background color of the inner view can be referenced as v2.bgcolor from anywhere within the application. To reference it by name from outside of inner_view you would specify outer_view.inner_view.bgcolor.
Named children of the canvas Objects that are named children of the canvas can be simply addressed. For example, consider <canvas> <view name="artichoke"> <!-- more program code --> </canvas> The view artichoke can be referenced from anywhere within the application simply as artichoke. That is, it is not necessary to reference it as canvas.artichoke.
Views The view is the basic visible element in an OpenLaszlo application. Anything that is displayed on the canvas is a view (or an object that is an instance of a class that extends view).
Visible and invisible views A view is only visible if it has color, or text, or an image assigned to it, and if the height and width of the view are greater than zero. For example, the following code would display only one view even though three views are defined. The second and third views exist but they are invisible. The second has no color assigned to it and the third has zero width. They still, however, affect the arrangement of the other two views. Nested views width: 100%height: 100<canvas height="100" width="100%"> shows a red square: <view width="50" height="50" bgcolor="red"/> nothing is displayed, but view still exists: <view width="50" height="50"/> nothing is displayed, but view still exists: <view width="0" height="50" bgcolor="blue"/> <simplelayout axis="x" spacing="5"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ******************************************************
Views as containers of other views Views can also contain other views, allowing you to create complex visual elements. Each 'parent' view can have any number of children. By default, each child view is positioned relative to the top-left corner of its parent as shown in the example. Although it is always possible to position any view by specifying its horizontal (xx) and vertical (yy) origin, stated in pixels, relative to its parent, it is often convenient to have the system lay things out for you. Layout types built into the system include simplelayout<simplelayout>[unknown tag] , stableborderlayout<stableborderlayout>[unknown tag] , constantlayout<constantlayout>[unknown tag] , resizelayout<resizelayout>[unknown tag] and wrappinglayout<wrappinglayout>[unknown tag] .
View sizing and clipping Consider the following application: Parent and children dimensions width: 100%height: 200<canvas height="200" width="100%"> <view bgcolor="red" x="50" y="50" width="100" height="100"> <view bgcolor="yellow" x="50" y="50" width="60" height="105"/> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Running the example above also shows that the width and height of a view can be different than the dimensions of the bounding box of its child views. No clipping occurred on the "yellow" view even though it lies outside the boundary of its parent. If no width and height are actually defined for a view, then it will adopt the width and height of the bounding box not its subviews. If clipping is desired, however, then the attribute clip="true" can be added to the parent, which would look like the following. Clipping width: 100%height: 200<canvas height="200" width="100%"> <view bgcolor="red" x="50" y="50" width="100" height="100" clip="true"> <view bgcolor="yellow" x="50" y="50" width="60" height="105"/> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ******************************************************
Images and other resources In addition to showing text and color, views are used to display, or play, media files of various formats, such as .gif, .jpeg, .png, .swf, and .mp3, for example. These resources may be compiled into the application or brought in at run time; they can be on the OpenLaszlo server or on a remote back end, and can be referenced by relative paths or absolute ids.
Data Binding LZX derives much of its power from its unique implementation of data binding, in which the contents of a view are determined by the contents of a dataset. A dataset is simply a named hierarchy of XML data that has a single root node. All data in LZX applications is contained in datasets. The concept of data binding implies more than the use of views to display XML data; rather the data itself can determine the size, color, contents, placement, etc. of views, and even cause views to be created or destroyed. Consider the following program: Simple databinding width: 100%height: 100<canvas height="100" width="100%"> <dataset name="ds"> <record x="10" y="20" name="first" color="332136432"/> <record x="5" y="5" name="second" color="56521236432"/> <record x="20" y="2" name="third" color="1565336432"/> </dataset> <simplelayout axis="y"/> <view datapath="ds:/record"> <text datapath="@name" bgcolor="$path{'@color'}" x="$path{'@x'}"/> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** In the above example, the one line <view datapath="ds:/record"> Causes three views to be created, and the line <text datapath="@name" bgcolor="$path{@color}" x="$path{@x}"/> causes each view's textual content, background color and xx position to be determined by the contents of the dataset.
Includes and libraries The source code for an LZX application can be contained in a single file; such files can grow quite large and thus hard to manipulate and maintain. By dividing your application into a number of smaller files, however, you can increase maintainability and understandability of your application. You can even break deep view hierarchies into multiple files to improve modularity, clarity, and source code organization.
The <indexterm><primary>include</primary></indexterm><sgmltag class="element"><include></sgmltag><remark role="fixme">[unknown tag]</remark> <!--unknown tag: include--> tag This tag allows you to specify the name of a file to be include at any point in your application. The file to be included can be a library, a view, or text. When the target is a library file (an XML file whose root element is library<library>[unknown tag] ), the contents of the library file are included in the application. Any views, scripts, fonts, resources, audios, datasources, datasets, class definitions, etc. in the library file are included in the application. A library file can include other libraries, but a library is included in an application only once, no matter how many include<include> statements reference it. For example, <canvas> <include href="library.lzx"/> <mywindow/> </canvas> <library> <class name="mywindow" extends"window" title="My Title"> <button> Click me! </button> </class> </library> The semantics for including views and text are analogous but slightly different. Unlike including a library file, a non text or view file is inserted once each time it's included.
Comments
XML comments These take the form <!-- comment --> and may appear between (but not within) tags in XML text. XML does not have a separate syntax for line ending comments, and does not allow nested comments. Often when debugging you find yourself commenting out sections of code. Because it's illegal to nest XML comments within XML comments, this technique does not work for commented sections of declarative LZX. A good way around this problem is to use XML processing instructions which are of the form <?ignore ?> So, to comment out the blue and green views below, <canvas height="100"> <simplelayout/> <!-- This is a red view --> <view bgcolor="red" width="100" height="20"/> <?ignore <!-- This is a blue view --> <view bgcolor="blue" width="100" height="20"/> <!-- This is a green view --> <view bgcolor="green" width="100" height="20"/> ?> <!-- This is a yellow view --> <view bgcolor="yellow" width="100" height="20"/> </canvas> Lines between <?ignore and ?> are ignoredEnd of the ignore section
Script comments In script, block comments are of the form /* comment */ Line ending comments start with // and continue to the end of the line: // line comment <script> /* script comments look like this */ some.method() // this is an example of comment syntax <!-- ERROR! do not enclose XML comments in script! --> </script> // ERROR! Do not include script comments in XML!
Debugging The OpenLaszlo system includes an interactive debugger that can be compiled into any application. The debugger displays run time errors, and can be used interactively to inspect and set any tag attributes or JavaScript fields. You can also use the JavaScript Debug.debug()Debug.debug() method to cause messages to be displayed in the debugger pane. For example, Using the debugger to write messages width: 100%height: 200<canvas width="100%" height="200" debug="true"> <script> Debug.debug("Well now how about that!") </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 ****************************************************** To invoke the debugger, set the attribute debug="true" in the canvas<canvas> tag. You can modify the appearance and position of the debug<debug>[unknown tag] tag. Note, however, that this tag does not invoke the debugger. To use the debugger interactively to inspect a value, you type an expression in to the evaluation pane. For example, Nested views bgcolor: 'red'width: 100%height: 200<canvas width="100%" height="200" bgcolor="red" debug="true"> <debug y="60"/> <view name="sam" bgcolor="blue" height="50" width="50"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** In the evaluation pane, type, sam.setAttribute('x', 50) and press return. The view named sam (the blue square) now appears fifty pixels to the right. See for a full discussion of the debugger.
The OpenLaszlo code viewer The OpenLaszlo Server contains a code viewer that you can use to inspect any XML file in the lps directory, including, of course, .lzx sources. When used to read .lzx files, the viewer displays syntactically-colored sources, as well as a list of cross-references such as classes, libraries and art assets. To invoke the veiwer, in a browser window enter the URL to /lps-4.9.0/lps/utils/viewer/viewer.jsp and supply the name of the file you want to view as a file= request type, for example: http://127.0.0.1:8080/lps-4.9.0/lps/utils/viewer/viewer.jsp?file=/my-apps/copy-of-hello.lzx FIXME: Say something about running various LPS's at the same time during development? Also, about cache clearing?
Tutorial Introduction OpenLaszlo Basics TODO: explain how tutorials relate to Laszlo in ten minutes TODO: Rename this tutorial TODO: Mention components and how to build apps by accreting components.
Building a simple OpenLaszlo application The source code to an OpenLaszlo application is a set of XML files, and media asset files (such as image, sound, and font files). Each application has a canvas filecanvas file, which is an XML file that contains the canvas<canvas>[unknown tag] tag. By convention, LZX files end with the extension .lzx. Every LZX file is an XML file, so if your editor has an XML mode, set it to that for working with LZX files. The enclosing tag of every OpenLaszlo application is the canvas<canvas> tag. The canvas is a view (like every other displayable object on the screen) but it has some special properties. For instance, resources can't be attached directly to the canvas. Here is a simple lzx file: Empty Canvas height: 100, width: 500, bgcolor: 'green'width: 100%height: 100<canvas width="100%" height="100" bgcolor="green"> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007-2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** For this simple example we have set the background color to green, just to show that it's there. If you don't set a background color for the canvas, it will be rendered white. (Throughout the rest of this tutorial no background color will be specified for the canvas.) Now let's put a window on a white canvas. Simple_window height: 100, width: 500width: 100%height: 100<canvas width="100%" height="100"> <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 ****************************************************** Note the XML empty tag format we're using: window<window>[unknown tag] . Using the attributes of that window, let's customize it a bit. Just as in HTML, LZX tags can have attributes: Specifying window size width: 100%height: 300<canvas width="100%" height="300"> <window x="20" y="20" width="200" height="250"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** In these simple examples, all measurements are in pixels. (A later explains how units of measurement may vary in more complicated situations.) Notice how the window is now absolutely positioned relative to the top-left corner of the canvas. Window Title width: 100%height: 350<canvas width="100%" height="350"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Now we've got a window, which both has a title, and can be resized. Notice how we're breaking the code up across lines to keep it neat. Let's stick something in that window. Window Text width: 100%height: 350<canvas width="100%" height="350"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <text>Here is some text.</text> </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've given the window<window> element a child (text), which contains the text we want written out. Notice how the text gets put in the top-left hand corner. Suppose we want to add another line of text? Why not just add another text tag, below the existing one: Overlapping text fields width: 100%height: 350<canvas width="100%" height="350"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <text>Here is some text.</text> <text>I could ramble for hours.</text> </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 ****************************************************** Eugh! The two text fields are sitting on top of each other. That's because the default place to put content is in the top-left hand corner. To correct this, we could position both text elements absolutely, as we did with the window: Manually positioning text width: 100%height: 250<canvas width="100%" height="250"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <text x="10" y="10">Here is some text.</text> <text x="10" y="50">I could ramble for hours.</text> </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 ****************************************************** Notice how the text is now positioned relative to its parent element, the window<window>. This worked great, and is extremely useful for positioning elements all over the place. But it's not very elegant when you think about the way elements flow relative to each other. OpenLaszlo provides a solution to this: Simplelayout width: 100%height: 350<canvas width="100%" height="350"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <text>Here is some text.</text> <text>I could ramble for hours.</text> </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 ****************************************************** Now the first text field is positioned relative to the second. The simplelayout<simplelayout>[unknown tag] tag tells OpenLaszlo that everything in that view (in this case the window<window>) will be positioned relative to its siblings. Here, the axis property makes everything align vertically, and the spacing specifies how far apart the elements should be.
Introduction to Components OpenLaszlo components are high-level objects that implement common user-interface functions. They range from relatively simple objects such as button<button>[unknown tag] to complex objects such as form<form>[unknown tag] and grid<grid>[unknown tag] . Components that are written by Laszlo Systems, Inc., and supplied with the OpenLaszlo Server are called LZ components.
Overview This tutorial introduction is intended to show you the variety of OpenLaszlo components and demonstrate how to use their basic features. Later chapters explain how to use advanced features of components and how to create your own components. The simplest way to build an OpenLaszlo application is to use LZ components "out of the box" with their default behaviors. This chapter includes a few such examples. It also includes a few examples that use concepts that have not yet been introduced. Don't worry if you don't understand exactly how they work. The idea here is to get acquainted with the general "feel" of an OpenLaszlo application that is built mostly from components. The subsequent tutorials present the concepts you'll need in order to create your own applications.
A Sampler Before reading this chapter about how to use components to build an OpenLaszlo application, you should take a few moments to get familiar with the range of components that ship with OpenLaszlo. The example below shows a miscellaneous collection of components. It's an abbreviated version of the sampler in the OpenLaszlo Explorer, which contains a visual tour of all the components. The code that implements these components may look a little intimidating if you have not worked with this kind of language before, but don't be put off. In fact you may not even want to look at the code yet. Your goal here is to start thinking about the kinds of things you can do in an OpenLaszlo application, and components provide your quickest route to productivity. The LZX Reference documents all the tags, attributes and methods associated with each component. Components miscellany height: 500, width: 800width: 100%height: 500<canvas width="100%" height="500"> <silverstyle name="silvercolors"/> <greenstyle name="greencolors"/> <bluestyle name="bluecolors"/> <view id="s1" x="20" y="20"> <view layout="spacing:20"> <text>Choose a style to change colors...</text> <view name="stylechooser" layout="axis:x; spacing:4"> <text>Style:</text> <combobox width="120" editable="false"> <handler name="onselect"> var colorchoice = this.getText(); canvas[colorchoice+'colors'].setAttribute("isdefault", true); </handler> <textlistitem text="silver"/> <textlistitem text="green"/> <textlistitem text="blue" selected="true"/> </combobox> </view> <tabslider width="250" height="200"> <tabelement text="holiday cheer" selected="true"> <radiogroup> <radiobutton text="peace on earth"/> <radiobutton text="joy to the world"/> <radiobutton text="happy new year"/> </radiogroup> </tabelement> <tabelement text="housewares"> <simplelayout axis="y" spacing="10"/> <checkbox text="stainless steel"/> <checkbox text="glassware"/> </tabelement> <tabelement text="isotope"> <text multiline="true" width="${immediateparent.width}"> Atoms that have the same number of protons but a different number of neutrons. They are atoms of the same element that have different masses. The isotope number is the number of protons plus the number of neutrons. </text> </tabelement> </tabslider> <tabs> <tabpane>Insecticides <simplelayout spacing="10" inset="10"/> <radiogroup> <radiobutton>Yes, I want to know more</radiobutton> <radiobutton>No, I prefer to remain blissfully unaware</radiobutton> <radiobutton>Please tell my neighbor, who may tell me</radiobutton> </radiogroup> </tabpane> <tabpane text="Subliminal"> <button height="22">Submit</button> </tabpane> </tabs> </view> <window text="test window" width="250" height="250" x="410" y="180" resizable="true" id="fw" title="A Simple Window"> <menubar name="mbar" placement="menubar"> <menu name="file" id="mfile" width="80">File <menuitem text="item 1"/> <menuseparator/> <menuitem id="MWS" text="item 3"> <menu name="subedit">subedit <menuitem text="subitem 1"/> <menuitem text="subitem 2"/> </menu> </menuitem> </menu> <menu name="Options">Document <menuitem text="option 1"/> <menuitem text="option 2"/> </menu> </menubar> <scrollbar/> </window> <view layout="spacing:14"> <button height="22">Submit</button> <button height="22" enabled="false">disabled</button> <combobox width="100"> <textlistitem text="pistachio" selected="true"/> <textlistitem text="chocolate chip"/> </combobox> <radiogroup> <radiobutton text="tension"/> <radiobutton text="distance"/> </radiogroup> </view> <view layout="spacing:20"> <view layout="spacing:10"> <checkbox text="I want to take a weekend flight"/> <checkbox text="Also search airports within 70 miles"/> </view> <view layout="spacing:5"> <edittext width="200" text="text entry here"/> <edittext width="200" text="disabled" enabled="false"/> </view> </view> <simplelayout axis="x" spacing="20"/> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ******************************************************
Form components and "general" components You will notice that the OpenLaszlo Explorer groups components into "form components" and "general components." This is merely a heuristic grouping of those components that usually appear within a form<form>[unknown tag] tag and those that don't. There is no essential difference; for example, the button<button> tag can be contained in a view<view>[unknown tag] or window<window>[unknown tag] ??? it does not have to be contained in a form<form>.
When to use the form tag Depending on your background in building web applications, you may be inclined to use the form<form> in place of the HTML <form> tag. This is often a mistake; OpenLaszlo applications are based on a different interaction model than HTML-based applications and overuse of the <form> tag results in LZX programs that fail to exploit OpenLaszlo's range of options. The form<form> tag is, generally speaking, not a widely used tag in LZX applications. It is intended to be used in situations in which the client-server interaction is simple, and there is a 1:1 mapping between the returned server data and the form elements. When the server interaction is more complicated, more advanced databinding techniques should be used, as explained in later chapters.
Building with Components In this section we'll explore the various ways to use components in OpenLaszlo applications: using tagsusing script APIsusing databinding As mentioned above, the concepts of scripting and databinding are presented in later chapters. Don't worry if you're not completely comfortable with these topics.
Using Tags The simplest way to use the components is in an application that has tags only???no script. The following trivial example shows what this looks like. Components example-tags only height: 100width: 100%height: 100<canvas height="100" width="100%"> <simplelayout axis="x" spacing="10" inset="10"/> <list shownitems="4"> <textlistitem>judy</textlistitem> <textlistitem>ann</textlistitem> <textlistitem>betsy</textlistitem> <textlistitem>sarah</textlistitem> <textlistitem>carol</textlistitem> <textlistitem>danah</textlistitem> </list> <radiogroup> <radiobutton text="apple"/> <radiobutton text="cherry"/> <radiobutton text="key lime"/> </radiogroup> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** As a practical matter, you will usually need to use some script, whether in a method, or, less frequently, to create a component within a script<script>[unknown tag] element.
Creating components from script API In addition to declaring components in tags, you can create and manipulate components from script. The following example shows how you can create buttons using tags and script. Creating components from script height: 150width: 100%height: 150<canvas height="150" width="100%"> <simplelayout/> Here is a button created with a tag <button name="framitz" width="50"> hello </button> <script> //And here is a button created with using script var b = new lz.button(); b.setAttribute("width", 50); b.setAttribute("height", 50); </script> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** In the example below, the onclick method procedurally adds an item to the list <list >[unknown tag] component. Component APIs height: 150width: 100%height: 150<canvas height="150" width="100%"> <simplelayout spacing="10"/> <list id="mylist" height="82"> <textlistitem text="something"/> </list> <view layout="axis:x;spacing:4"> <edittext id="item" text="new item"/> <button text="Add" isdefault="true"> <handler name="onclick"> mylist.addItem(item.getText()); </handler> </button> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ******************************************************
Data-Driven Components A third way to create components is through databinding. In the example below, a new component textlistitem is created for each matching node in the "mydata" dataset. Data-Driven Components height: 200, width:800width: 100%height: 200<canvas height="200" width="100%"> <dataset name="mydata" src="resources/contacts.xml" request="true"/> <simplelayout axis="x" spacing="10" inset="10"/> <list id="a"> <textlistitem datapath="mydata:/contacts/person" text="$path{'@firstname'}"/> </list> <list id="b" shownitems="4"> <textlistitem datapath="mydata:/contacts/person" text="$path{'@firstname'}"/> </list> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ******************************************************
Introduction to Views
Introduction Views are the basic visual element in OpenLaszlo applications. They allow you to specify how elements will interact with one another. In the OpenLaszlo Basics tutorial we've seen how to specify a top-to-bottom organization of text elements. In this tutorial you'll learn how to do more complex and powerful interactions among visual elements. Here's the code from the previous tutorial: Simple window width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button>My Button</button> </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've added a button (My Button) just to remind us of how the simplelayout<simplelayout>[unknown tag] affected the sibling elements. That button would look better if it were centered. There is an attribute that we could use to do this, but just to see better how LZX works, let's center the button programatically. This will give you a good look at how constraints and modifiers work, which will help you build more complex views.
Constraints and Modifiers A constraint is an object which takes responsibility for keeping a view property set to a certain value. Constraints are described in full in , but here we'll just get a flavor of them because they are a key feature of LZX syntax. In order to show constraints, we'll also introduce the key concepts of "parent" and "immediateparent", which describe where views fit in different kinds of hierarchies. Depending on the type of constraint, that can be 'the x position of the mouse' or 'the width of the parent view' or whatever. In this case, we will constrain a property to the width of the parent view. Constraining button position relative to window width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button x="${immediateparent.width / 2}"> My Button </button> </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 ****************************************************** What we've said here is "constrain the x position of the button to half the width of its parent". Actually we said "immediateparent". What's that about?
Parent and Immediateparent As explained in greater depth in , "parent" is a term that refers to the lexical parent in the code, and "immediateparent" refers to the runtime parent of a view as the code is executed. Often they are the same thing, but it's important to understand when they're not. In particular, you generally need to use "immediateparent" when you create views by instantiating classes. You can always use immediateparent, instead of parent (you just have to type those extra 9 characters). In this case the immediateparent is the window's content area (the white bit). In short we had to use immediateparent here because window<window>[unknown tag] is a class, and when you instantiate it and put contents inside of it, you must use the immediateparent reference to get the desired behavior.
Operations in constraints You can perform operations on the value an attribute that you constrain to. The modifier in our example is the "divide by two" operator. Notice that this code does not center the button. Rather, it constrains the position of the button to the middle of the window. The problem is that the position of a view is determined by its top-left corner. In other words, the top-left corner of the button is centered in the window, not the button itself. In order to center the button, we need to move it over by half of its own width. One way to do that would be to just give the button an explicit width, and offset the constraint by half of that. Centering the button width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button width="160" x="${immediateparent.width / 2 - 80}"> My Button </button> </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 ****************************************************** That works but it's not a general solution. We want the button to always be in the middle of the window even if it changes width. Here's how we do that: A button that stays centered width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button x="${(immediateparent.width / 2) - (this.width / 2)}"> My Button </button> </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 ****************************************************** This will center the button regardless of its size. Parenthesis have been added to clarify the execution order of the code. To prove that the button will remain centered, we'll add an onclick script to the button that will resize it. For now, you don't need to understand how this works. Simply observe that when you click the button, its width grows. We'll revisit this topic in the "Scripting" tutorial. Growing button width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button x="${(immediateparent.width / 2) - (this.width / 2)}" onclick="this.setAttribute('width', this.width + 10);"> My Button </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 ****************************************************** Notice that as the button grows, it still stays centered. TODO: The following discussion of syntax is really unfortunate. In the above example we are using two methods of referencing properties of a view. For example, we use this.width and this.width. They are different, and there are specific situations when you can and can't use the this.width syntax. In short, this.width can only be used in constraints.
Layouts and container views What if now we wanted to have a row of buttons going across the top of our window? We can try adding a few buttons at the top: Vertically aligned buttons width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <button>1</button> <button>2</button> <button>3</button> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button x="${(immediateparent.width / 2) - (this.width / 2)}" onclick="this.setAttribute('width', this.width + 10);"> My Button </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 ****************************************************** We can't change the simplelayout<simplelayout> tag, because that would make a mess of the other elements in the window. Instead we can put all three buttons in their own view, and that view will then become a single element in the window. Then we can apply whatever kind of layout/positioning to the button view we want: Grouping buttons in a <view> width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <view bgcolor="#ff0000"> <simplelayout axis="x" spacing="5"/> <button>1</button> <button>2</button> <button>3</button> </view> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button x="${(immediateparent.width / 2) - (this.width / 2)}" onclick="this.setAttribute('width', this.width + 10);"> My Button </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 ****************************************************** TODO: mention property that we're illustrating here--views grow to accommodate That's better. By using the attribute bgcolor (background color) we can see how big the view is; it's only as big as it needs to be. We haven't specified either a height or width, so it is just large enough to accommodate the buttons. What if we wanted the row to be like the toolbars at the top of your browser???a uniform color, that stretches from end to end? We would constrain the width of the red view to the width of its parent. Constraining a width to a parent's width width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <view bgcolor="#ff0000" width="${parent.width}"> <simplelayout axis="x" spacing="5"/> <button>1</button> <button>2</button> <button>3</button> </view> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button x="${(immediateparent.width / 2) - (this.width / 2)}" onclick="this.setAttribute('width', this.width + 10);"> My Button </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 ****************************************************** Now the view stretches across, but if you look at the toolbars you'll notice that they are a little bit taller than the buttons they contain. To make our buttons look the same, we could set an absolute height, but let's make it dependent on one of the buttons: Constraining view to child's height width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <view bgcolor="#ff0000" width="${parent.width}" height="${this.refButton.height + 8}"> <simplelayout axis="x" spacing="5"/> <button name="refButton">1</button> <button>2</button> <button>3</button> </view> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button x="${(immediateparent.width / 2) - (this.width / 2)}" onclick="this.setAttribute('width', this.width + 10);"> My Button </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 ****************************************************** In order to be able to reference a particular button, we had to give it a namename attribute. Now the buttons are top-aligned ??? they should be vertically aligned to the center of the red bar. Remember how earlier we said that there was an attribute to align stuff to the center? We're going to use it now: Using the 'valign' attribute width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <view bgcolor="#ff0000" width="${parent.width}" height="${this.refButton.height + 8}"> <simplelayout axis="x" spacing="5"/> <button name="refButton" valign="middle"> 1 </button> <button>2</button> <button>3</button> </view> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button x="${(immediateparent.width / 2) - (this.width / 2)}" onclick="this.setAttribute('width', this.width + 10);"> My Button </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 ****************************************************** The valignvalign attribute aligns the view to the middle of its parent's height. It can take a value of top, middle or bottom. There is an alignalign attribute that aligns views to the x-axis. The only problem with using that here is that we'd have to give each button an alignalign attribute. The solution is to wrap all of our buttons in a view. Then we could apply a valignvalign attribute to all of them at once. Applying 'valign' to container view width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <view bgcolor="#ff0000" width="${parent.width}" height="${this.buttons.refButton.height + 8}"> <simplelayout axis="x" spacing="5"/> <view name="buttons" valign="middle"> <button name="refButton">1</button> <button>2</button> <button>3</button> </view> </view> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button x="${(immediateparent.width / 2) - (this.width / 2)}" onclick="this.setAttribute('width', this.width + 10);"> My Button </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 ****************************************************** OK, we seem to have lost buttons one and two, but let's come back to that. You'll notice that we gave the new view a name: "buttons". We also had to change the address to the button's height from this.refButton.height to this.buttons.refButton.height. This was necessary because refButton is now inside buttons, which is inside the red view. Now to get buttons one and two back! In the "buttons" view, there is nothing to tell the OpenLaszlo Runtime that it should space them out. Why not? Because we forgot to transfer the simplelayout<simplelayout> tag to the buttons view. Using <simplelayout> to align buttons width: 100%height: 300<canvas height="300" width="100%"> <window x="20" y="20" width="200" height="250" title="Simple Window" resizable="true"> <simplelayout axis="y" spacing="10"/> <view bgcolor="#bdbdbd" width="${parent.width}" height="${this.buttons.refButton.height + 8}"> <view name="buttons" valign="middle"> <simplelayout axis="x" spacing="5"/> <button name="refButton">1</button> <button>2</button> <button>3</button> </view> </view> <text>Here is some text.</text> <text>I could ramble for hours.</text> <button x="${(immediateparent.width / 2) - (this.width / 2)}" onclick="this.setAttribute('width', this.width + 10);"> My Button </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 ****************************************************** We changed the color of the view to make it look better.
Introduction to Media and Art Assets This tutorial provides an informal overview of how to use media resources such as images, video and audio in OpenLaszlo applications. These topics are covered in greater depth in . There are two ways in which you can import an image into OpenLaszlo applications: as a media resource attached to a view;as an image inserted within HTML text In this tutorial we'll be talking about using resources attached to views, which is the more general mechanism. This technique allows you to import not only images, but audio, video "progressive" images and similar complex media.
Runtime Considerations Depending on which runtime (SWF or DHTML) you're compiling to and whether your application is deployed proxied or SOLO, there may be certain types of assets that may be available. For example, in applications deployed to SWF, you can use assets in SWF format, these are not available in applications compiled to DHTML. Additionally, various formats that are not natively supported by the Flash Player are available only when you run in proxied mode, in which the OpenLaszlo Server transcodes formats. When you insert an image into HTML text, you can only include images in the formats natively supported by the targeted runtime. For a discussion of how to use the imgimg tag within HTML text, see . Being able to bring in art assets is not just for pictures - you can create your own custom view system too.
Resources In OpenLaszlo applications, viewview s are the fundamental visible entities, and resources are externally generated media to be incorporated in applications. Resources are made available by being attached to views. Thus in OpenLaszlo applications you don't "insert an image" into a view; instead you insert a view whose resource is an image. Let's look at the simplest way of including an art asset (resource). In this case, a GIF image: Using the 'resource' attribute width: 100%height: 80<canvas width="100%" height="80"> <view resource="resources/smiley.gif"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** This tiny OpenLaszlo application loads an image called smiley.gif (which is located in resources, a subdirectory of the one that contains the application). To position an image, position the view that contains that image as a resource: Positioning images width: 100%height: 100<canvas width="100%" height="100"> <view resource="resources/smiley.gif"/> <view x="50" y="50" resource="resources/smiley.gif"/> </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'll come to changing the size of an image later.
Ways to include resources In the example above, we included an image from the current local directory that was loaded when the application was compiled on the server. This may or may not be right for the particular application, so LZX has four ways to load images:
Loading resources at compile time The resource gets bundled with the rest of the application when it is compiled on the server, so there will be a larger initial download, but the images will display instantly. All of these four methods will eventually show the same result on screen: Local pathname <canvas width="500" height="100"> <view resource="../resources/smiley.gif"/> </canvas> Using the resource global identifier <canvas width="500" height="100"> <resource name="smileyFaceImg" src="../resources/smiley.gif"/> <view resource="smileyFaceImg"/> </canvas>
Loading resources at run-time The resource does not get loaded until the view is initialized, so the rest of the application will load and there may be a visible delay. The initial download will be smaller, because the images are not bundled with it. The server that does the compiling requests the image (if it resides on a different server), and it routes it to the app that is already running in the client. Absolute referencing <canvas> <view resource="http://www.laszlosystems.com/images/smiley.gif"> </canvas> Local referencing <canvas> <view resource="http:../resources/smiley.gif"/> </canvas> The best way to include a resources that are part of your application is usually with the global identifier (using the resourceresource tag). That way all resources can be included in one place, and if you need to change a resource's location or the resource itself, you only need to change it once. (Reasons to use other ways of including resources are described in later chapters.)
File types Stretching resources width: 100%height: 200<canvas width="100%" height="200"> <resource name="sourFace" src="resources/sourface.png"/> <resource name="smileyFace" src="resources/smiley.gif"/> <view resource="smileyFace" x="10" y="10"/> <view resource="sourFace" x="50" y="10"/> <view x="10" y="50" width="363" height="242" stretches="both" resource="resources/sunset.jpg"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** OpenLaszlo supports GIFs, JPEGs and PNGs. They can all be resized by setting the stretchesstretches attribute of the view that contains the resource to both, width or height.
Multi-frame Resources Resources can be multi frame, meaning that a single resource is actually made up of a number of different resources, each of which can only be shown one at a time. The format for doing this is: Multiframe resources width: 100%height: 100<canvas width="100%" height="100"> <resource name="face"> <frame src="resources/sourface.png"/> <frame src="resources/smiley.gif"/> </resource> <view x="150" y="50" resource="face" onclick="this.setAttribute('frame', 2);"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The resources nested within the resource tag are numbered starting with 1. As you can see, OpenLaszlo supports GIFs, JPEGs and PNGs. They can all be resized by setting the stretchesstretches attribute of the view that contains the resource to either both, width or height. You can also use multi-frame resources for animation: see in the Animation chapter.
Working with SWFs In applications compiled to SWF, you can treat SWF-formatted assets as resources, whether they are animated or not: Working with swf files width: 100%height: 100<canvas width="100%" height="100"> <view resource="resources/still_swf.swf"/> <view x="150" y="20" resource="resources/anim_swf.swf"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Any animation will loop ad infinitum. You will probably want to control the animation of a SWF file from the script in your application. To prevent it from playing, we can tell it to stop when the view is initialized: <view x="150" y="20" resource="anim_swf.swf" oninit="this.stop();"/> Stopping swf animations width: 100%height: 150<canvas width="100%" height="150"> <view name="spinningClock" resource="resources/clock.swf" onclick="this.stop();" clickable="true"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Clicking on the image will cause the animation to stop. The onclickonclick event handler will be covered in the "Scripting" tutorial. Instead of just using stopstop(), we could have passed the stopstop() method an argument instructing it at which frame to stop. In addition, we could have used the playplay() method (also with optional frame argument) to play from a given frame. We can't really progress beyond this point without covering scripting, but here is a preview of some of the other attributes and methods of viewviews that pertain to resources: frameframe The number of the current frame. totalframestotalframes The total number of frames in the SWF. seekseek Jumps forward or backward n seconds through the SWF. These and other APIs are discussed in greater detail in .
Introduction to Text and Fonts
Introduction This tutorial shows how to work with text and fonts in OpenLaszlo applications. Later chapters on and explain more advanced topics. Note that the behavior of LZX applications differs between applications compiled for the Flash Player (SWF format) and those compiled for DHTML. Certain APIs are available in only one or the other runtime. We'll call attention to these subjects in the sections that follow.
The <text> tag Entering text is easy with the text<text>[unknown tag] tag: The simple <text> tag width: 100%height: 100<canvas height="100" width="100%"> <text> Hello, World! </text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Text that is between the text<text> tags is normalized, meaning that preceding and trailing whitespace is ignored, as are multiple whitespace elements between words. i.e. if you put several spaces between words, the output will still be a single space. If you put a line break in the text, it will be rendered as a single whitespace element (i.e. a space), and text fields will stretch to fit their contents. The default OpenLaszlo font is used (LzTahoe), at its default size (10pt). The text<text> tag is a view, so it can be treated as one. It can be given all the default view attributes, such as width, x, y and so forth. <text> is a <view> width: 100%height: 100<canvas height="100" width="100%"> <text x="15" y="20" width="150"> Hello, World! We would like to welcome you to the launch of the OpenLaszlo Server. </text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Notice how the text gets cut off because we set the width attribute?
Multiline text Having multiline areas of text is just as easy. All we do is set the multilinemultiline attribute to true: Multiline text width: 100%height: 100<canvas height="100" width="100%"> <text x="15" y="20" width="150" multiline="true"> Hello, World! We would like to welcome you to the launch of the OpenLaszlo Server. </text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The text still follows the same normalization rules, so in this case the text wraps at 150px (because we explicitly set the width of the text<text> tag to that), and stretches vertically as far as it needs to go. The line breaks are still being ignored, but we can fix that by entering them manually: Inserting line breaks width: 100%height: 100<canvas height="100" width="100%"> <text x="15" y="20" width="150" multiline="true"> Hello, World!<br/><br/> We would like to welcome you to OpenLaszlo 4. </text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The br<br>[unknown tag] tag is used to denote a single line break. This may be familiar to you from HTML. Just be careful to note that since we are working in XML, empty elements have to be closed with a forward slash: <br/>. (This is the same as in XHTML.)
Including HTML markup in OpenLaszlo applications Openlaszlo provides support for incorporating HTML. Here's a list of tags that are supported: TODO: Find out if P tag has been made obsolete. TagMeaning a<a>[unknown tag] Hypertext Link b<b>[unknown tag] Bold Text font<font>[unknown tag] Defines and implements fonts i<i>[unknown tag] Italic text p<p>[unknown tag] Paragraph u<u>[unknown tag] Underline img<img>[unknown tag] image
Including links Specifying a font width: 100%height: 150<canvas height="150" width="100%"> <font name="default" style="bold"/> <text x="15" y="20" width="150" multiline="true"> <b>Hello, <font color="#ff0000">World!</font></b><br/><br/> <font size="16">We</font> would like to welcome <u>you</u> to the launch of the <a href="http://www.openlaszlo.org/" target="_blank">OpenLaszlo</a> Server. </text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The word "OpenLaszlo" becomes a hyperlink because of the a<a> tag. It does not get underlined as it might in a browser. We can do that ourselves by nesting a u<u> tag inside it. However, it is important to note that tags must be nested correctly: <a href="..."><u>Link</u></a> is correct, but <a href="..."><u>Link</a></u> is wrong because the a<a> tag was closed before the u<u> tag.
The <html> tag The html<html>[unknown tag] tag allows you to include complete html pages within an OpenLaszlo application. In the example below, the html<html> tag is used inside an OpenLaszlo window, which automatically provides scrollbars. Don't worry if you don't understand how the code in this example works; the concepts will be explained in later chapters. The key thing to note is that you can embed entire HTML pages. The <html> tag width: 100%height: 400<canvas width="100%" height="400"> <include href="extensions/html.lzx"/> <class name="browser" extends="window" resizable="true" bgcolor="silver"> <simplelayout axis="y" spacing="2"/> <hbox width="100%"> <edittext name="txt" text="http://www.openlaszlo.org/" width="300"/> <button>Load <handler name="onclick"> if (classroot.main) { classroot.main.setAttribute('src', parent.txt.getText()); classroot.main.setAttribute('visible', true); } </handler> </button> </hbox> <html name="main" history="false" width="100%" height="${classroot.height - this.y - 44}"> <handler name="oninit"> this.bringToFront(); </handler> </html> </class> <browser width="100%" height="100%"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ******************************************************
Fonts OpenLaszlo provides capabilities for specifying the which fonts will appear in your application. All applications can use fonts that are provided by the local system. In addition, applications compiled to SWF can also embed fonts.
Fonts in SWF applications In applications compiled to SWF, OpenLaszlo supports both embedded fonts, which are downloaded with your application, and client fonts, which reside natively on the machines on which your application is used. By default, client fonts are used. Client fonts have certain limitations; for example they cannot be rotated or changed in opacity (due to a limitation of the Flash Player). See .
Including Fonts When compiling to SWF, you can embed fonts using the srcsrc of the font<font> tag. Using the src attribute to specify font width: 100%height: 150<canvas height="150" width="100%"> <font name="default" style="bold" src="lztahoe8b.ttf"/> <text x="15" y="20" width="150" multiline="true"> <b>Hello, <font color="#ff0000">World!</font></b><br/><br/> <font size="16">We</font> would like to welcome <u>you</u> to the launch of the <a href="http://www.openlaszlo.org/" target="_blank">OpenLaszlo</a> Server. </text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** OpenLaszlo supports TrueType fonts, and the simplest way to use one is as follows: Naming a font family width: 100%height: 100<canvas height="100" width="100%"> <font name="serifFont" src="timmonsr.ttf"/> <text width="150" height="20"> <font face="serifFont" size="14">Hello, World!</font> </text> </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 called this font serifFont, but we could call it whatever we wanted, and that is what will be used to declare its use elsewhere in the application. The font TTF files can be located in the current directory or another one and referenced relatively (src="../fonts/..."). Otherwise they are included in the LPS webapp directory of the installation under WEB-INF/lps/fonts. timonnsr is a font that comes packaged with the OpenLaszlo Server in this location. A TrueType file is required for each style of font (e.g. italic, bold, regular), which is how fonts are actually packaged. So the following code will not work: <canvas> <!-- WARNING: Bad example! --> <font name="serifFont" src="timmonsr.ttf"/> <text width="150" height="20"> <font face="serifFont" size="14">Hello, <b>World!</b></font> </text> </canvas> To correct this, we need to add one line of code to include the bold font: Including bold font width: 100%height: 100<canvas height="100" width="100%"> <font name="serifFont"> <face src="timmonsr.ttf"/> <face src="timmonsb.ttf" style="bold"/> </font> <text width="150" height="20"> <font face="serifFont" size="14">Hello, <b>World!</b></font> </text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The plain font style does not need to be explicitly set; style="plain" is implied. This method of markup is not always the best solution, because it depends on writing cumbersome font<font> tags. Instead we can tell the text<text> tag what fonts and sizes to use: Assigning fonts to <text> tag width: 100%height: 100<canvas height="100" width="100%"> <font name="serifFont"> <face src="timmonsr.ttf"/> <face src="timmonsb.ttf" style="bold"/> </font> <text width="150" height="150" font="serifFont" fontsize="15" multiline="true"> Hello, <b>World!</b><br/> Sometimes it is easier to avoid writing extra <u>font</u> tags. </text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** In fact, we can do this with any view (including window<window>[unknown tag] tags, and so forth) tag, and its children will inherit the fonts that were set: Views inherit font properties width: 100%height: 100<canvas height="100" width="100%"> <font name="serifFont"> <face src="timmonsr.ttf"/> <face src="timmonsb.ttf" style="bold"/> </font> <view name="grandParent" font="serifFont" fontsize="15"> <view name="firstView" bgcolor="#FFCCCC" height="20"> <text>Hello</text> </view> <view x="80" name="secondView" bgcolor="#CCCCFF" height="20"> <text>World</text> </view> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The font here is specified in the grandParent view, and it is inherited by both text fields. Of course font specifications can be overridden further down the hierarchy, both through the use of font<font> tags, and by defining font specifications of child views: Overwriting font specifications width: 100%height: 100<canvas height="100" width="100%"> <font name="serifFont"> <face src="timmonsr.ttf"/> <face src="timmonsb.ttf" style="bold"/> </font> <font name="sansSerifFont" src="helmetr.ttf"/> <view name="grandParent" font="serifFont" fontsize="15"> <view name="firstView" bgcolor="#FFCCCC" height="20"> <text> <font color="#CC6600" size="12">Hello</font> </text> </view> <view x="80" name="secondView" bgcolor="#CCCCFF" height="20"> <text font="sansSerifFont">World</text> </view> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Both the text fields here have overridden some of the font specs. The ones that aren't overridden (such as the face in the left example and the size in the right example) are inherited.
Changing Text The text<text> element has two methods for reading and writing contents: getText()getText()setAttribute()setAttribute() The getText()getText() method returns the contents of the text box, and the setAttribute()setAttribute() method takes an argument of the string to put in the text field: Getting and setting text width: 100%height: 200<canvas height="200" width="100%" debug="true"> <debug x="200"/> <button x="15" y="15" onclick="Debug.debug('%s', canvas.theField.getText());"> Get Text </button> <button x="100" y="15" onclick="canvas.theField.setAttribute('text', 'Hello, Laszlo!');"> Set Text </button> <button x="180" y="15" onclick="addText();"> Add Text </button> <script> function addText() { var origText = canvas.theField.getText(); var newText = origText + " And on."; canvas.theField.setAttribute('text', newText); } </script> <text x="45" y="60" width="150" height="75" multiline="true" name="theField">Some sample text.</text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Using these two methods it is possible to concatenate text and work with it as a string. An important thing to remember when working with text is that everything between the text<text> tags is returned by getText()getText(). Text returned width: 100%height: 200<canvas height="200" width="100%" debug="true"> <debug x="200"/> <font name="sansSerifFont" src="helmetr.ttf"/> <script> function writeOutBit() { var myText = canvas.theField.getText(); Debug.debug("%s", myText); } </script> <button x="15" y="15" onclick="writeOutBit();"> Write part of line </button> <text x="45" y="60" width="150" height="75" multiline="true" name="theField"> <font face="sansSerifFont" size="12">Some lovely sample text. </font> </text> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** This can be both useful and a nuisance. If we were to start parsing the string that is returned by the getText()getText() method, then we would have to compensate for the markup tags. To avoid it, set the font in the text<text> tag.
Input Text The simplest way to provide users with a text field is with the inputtext<inputtext>[unknown tag] tag: Using <inputtext> width: 100%height: 200<canvas height="200" width="100%" debug="true"> <inputtext name="myText" x="10" y="15">Enter text here</inputtext> <button x="45" y="45" onclick="Debug.debug('%s', canvas.myText.getText());"> Write out text </button> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The inputtext<inputtext> field gives no indication that the text can be selected and edited, other than an insert cursor when the user rolls over it. The inputtext<inputtext> element can accept all the text<text> attributes, as well as fonts: <inputtext> accepts <text> attributes width: 100%height: 100<canvas height="100" width="100%"> <font name="Helmet" src="helmetr.ttf"/> <inputtext name="myText" x="10" y="15" font="Helmet" fontsize="12" width="200" height="50" multiline="true"> Enter very large quantities of text here. </inputtext> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** getText() and setArgument() methods will work on inputtext<inputtext> fields as well. To overcome the problem of the text field not being recognizable, OpenLaszlo provides the edittext<edittext>[unknown tag] tag: Using <edittext> width: 100%height: 160<canvas height="160" width="100%"> <edittext name="myText" x="10" y="15">Enter text here</edittext> <edittext name="myBigText" x="10" y="45" width="250" height="100" multiline="true"> Enter a lot more text here </edittext> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The edittext<edittext> tag can be multiline, and can be adjusted in size.
Introduction to Scripting
Introduction LZX applications can include procedural code, called as well as declarative XML tags. Within an LZX program, script can appear between <script> tagsbetween <method> tagsbetween <handler> tagsbetween quotation marks as an assigned value for an attribute
The <script> tag The quickest way to get a script running is to put it in script<script>[unknown tag] tags. Code in script tags is executed immediately. script<script> tags are only embeddable in the canvas<canvas>[unknown tag] . Using the <literal><script></literal> tag width: 100%height: 50<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: Incorporating JavaScript width: 100%height: 100<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 ******************************************************
JavaScript gotchas 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: JavaScript Subtleties width: 100%height: 100<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. 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.
XML characters in script Let's try a simple "for" loop: XML characters cause errors <canvas height="80" width="500" debug="true"> <-- the following code will not compile because of the angle bracket --> <script> for (var i = 0; i < 11; i++) { Debug.debug("%d", i); } </script> </canvas> Oops! Because LZX is an XML-based language, there are rules as to what is legal between tags (in particular, script<script> tags). The "<" character in the for loop is what is causing the problem. We are not allowed "<" or ">" characters between LZX tags. Fortunately there are ways around this. The next two examples are not interactive, but they are correct LZX programs Encoding XML characters <canvas height="120"> <script> for (var i = 0; i &lt; 11; i++) { Debug.debug("%d", i); } </script> </canvas> Using CDATA <canvas height="120"> <script> <![CDATA[ for (var i = 0; i < 11; i++) { Debug.debug("%d", i); } ]]> </script> </canvas> Either of the above two methods will work. Whichever you use is up to you, and of course the particular application. The CDATA method is perhaps a little more practical for larger blocks of code.
Functions You can write functions in script<script> tags. JavaScript functions width: 100%height: 100<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 Methods are in some ways similar to functions. They contain blocks of code between method<method>[unknown tag] tags, and are associated with particular classes. In the tutorial, we saw a few methods of the LzViewlz.view class that pertain to resources: play()play()stop()stop()getOffset()getOffset()getMaxOffset()getMaxOffset() Let's explore methods with a simple example of a window. The window<window>[unknown tag] element is actually a view, as we saw before. There are a few methods that apply to it. The windowwindow class extends LzViewlz.view class. This means that windows inherit all the attributes and methods of views. A simple method width: 100%height: 200<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 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<window> has a setAttribute()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.
Using Script to manipulate attributes Remember that windowwindow extends the LzViewlz.view class. That means that each window has all the attributes of a view<view>[unknown tag] . Here's an example of how to use script to manipulate some of those assets. Manipulating attributes width: 100%height: 200<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 namename (windowTwo). We are also using the setAttribute()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: Getting attributes width: 100%height: 200<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: Moving window by function call width: 100%height: 200<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. From function to method width: 100%height: 200<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:
Addressing In LZX, objects can have namenames and/or idids by which they can be addressed. A namename 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 idid is global, so there can't be two views with the same idid in a LZX file. Going back to the idea of having one button call the second button's method: One button call another's methods width: 100%height: 200<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 idid, and access it globally: Using the <id> attribute width: 100%height: 200<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 idids and namenames, one button is addressing the window relatively using its namename, and the other globally, using its idid. Note that the idid and namename could have been the same, they were intentionally different in this example.
Methods and arguments In the forgoing 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: Methods and arguments width: 100%height: 200<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: Passing multiple arguments to a method width: 100%height: 200<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 ******************************************************
Using attributes to reduce complexity Continuing from the same example as above, let's look at ways to reduce the number of arguments being passed. Here we put the functionality into the button itself: More on attributes width: 100%height: 200<canvas width="100%" height="200"> <window x="100" y="60" title="Window 2" name="windowTwo" id="windowTwoId"> Moves the second window specified number of pixels in specified direction <method name="moveWindow" args="buttonObj"> direction = buttonObj.direction; distance = parseInt(buttonObj.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 onclick="windowTwoId.moveWindow(this)">Left 2 <attribute name="direction" value="'left'"/> <attribute name="distance" value="'2'"/> </button> <button onclick="windowTwoId.moveWindow(this)">Left 20 <attribute name="direction" value="'left'"/> <attribute name="distance" value="'20'"/> </button> <button onclick="windowTwoId.moveWindow(this)">Right 20 <attribute name="direction" value="'right'"/> <attribute name="distance" value="'20'"/> </button> <button onclick="windowTwoId.moveWindow(this)">Right 2 <attribute name="direction" value="right" type="string"/> <attribute name="distance" value="'2'"/> </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 ****************************************************** The buttons have attributes which we have named "distance" and "direction". The values of those attributes need to be in double quoted strings, because the value needs to be a JavaScript expression, not a string or a number. That's the reason for the value="''". Alternatively we could give the attribute<attribute>[unknown tag] tag an attribute type="string", as shown in the "right 2" button. In this case the word "right" does not need to be single-quoted. This example is actually more lengthy than the previous one, but it demonstrates the power of object oriented programming in LZX. If the number of attributes were to grow, and if various buttons had different attributes, the code would still remain very clear.
Introduction to Databinding
Introduction We shall be examining how to interact with data in LZX.
The Basics As far as importing data into an LZX app is concerned, we always work in XML. The XML declaration is not required, but a single root XML node is. Data is represented by the dataset<dataset>[unknown tag] element. Datasets width: 100%height: 80<canvas height="80" width="100%"> <dataset name="myData"> <myXML> <person show="simpsons"> <firstName>Homer</firstName> <lastName>Simpson</lastName> </person> <person show="simpsons"> <firstName>Marge</firstName> <lastName>Simpson</lastName> </person> <person show="simpsons"> <firstName>Montgomery</firstName> <lastName>Burns</lastName> </person> </myXML> </dataset> <text datapath="myData:/myXML[1]/person[1]/firstName[1]/text()"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** In the above example, the single root element is myXML. The datapathdatapath attribute of the text<text>[unknown tag] tag binds it to the data. Datapaths use XPath attributes to navigate through the XML data. So the name of the dataset to use goes before the colon myData:, followed by the nodes, separated by forward slashes (/). The square brackets provide a (one-based) space to enter which sibling node we want. In plain English, the above example says: "get me the text from the first firstName node, of the first person node of the first (and of course, only) myXML node." The text() method call returns that node's text. To get Marge's name, we could rewrite the text<text> tag as follows: <text datapath="myData:/myXML/person[2]/firstName/text()" /> To get the "show" attribute of Montgomery, we could write: <text datapath="myData:/myXML/person[3]/@show" /> The /text() path segment is unnecessary with the datapathdatapath attribute. So far we've used the text<text> tag in conjunction with a single datapath. If we wanted to present tabular information, this would mean each text element would need its own datapath, and would be cumbersome and difficult to write. Instead let's make a quick table, by giving a view<view>[unknown tag] a datapath: Assigning a datapath to a view width: 100%height: 80<canvas height="80" width="100%"> <dataset name="myData"> <myXML> <person show="simpsons"> <firstName>Homer</firstName> <lastName>Simpson</lastName> </person> <person show="simpsons"> <firstName>Marge</firstName> <lastName>Simpson</lastName> </person> <person show="simpsons"> <firstName>Montgomery</firstName> <lastName>Burns</lastName> </person> </myXML> </dataset> <view name="rowOfData" datapath="myData:/myXML[1]/person[1]"> <simplelayout axis="x"/> <text datapath="firstName/text()"/> <text datapath="lastName/text()"/> <text datapath="@show"/> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The datapath of the entire rowOfData view has now become Homer's person node. The child elements of rowOfData inherit this, so their datapaths can be referenced relatively.
Multiple rows of data In the above example we used a single rowOfData node. Next, we shall use a range of nodes: Range of nodes width: 100%height: 80<canvas height="80" width="100%"> <dataset name="myData"> <myXML> <person show="simpsons"> <firstName>Homer</firstName> <lastName>Simpson</lastName> </person> <person show="simpsons"> <firstName>Marge</firstName> <lastName>Simpson</lastName> </person> <person show="simpsons"> <firstName>Montgomery</firstName> <lastName>Burns</lastName> </person> </myXML> </dataset> <view name="myTable"> <simplelayout axis="y"/> <view name="rowOfData" datapath="myData:/myXML[1]/person"> <simplelayout axis="x"/> <text datapath="firstName/text()"/> <text datapath="lastName/text()"/> <text datapath="@show"/> </view> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Whichever tag contains the datapathdatapath attribute will get repeated as often as is necessary. Remember that datapaths bind themselves to a view, so if the data changes, so will the view. We shall come to that a little later.
Ways to include data So far we've been using embedded data (XML that is written into the document). This is fine for very small amounts of static data, but there are other methods better suited to larger (or dynamic) data. How is it included? When is it loaded?SyntaxEmbeddedCompile-time <dataset name="myData"> <myXML> <!-- ... other XML tags ... --> </myXML> </dataset> IncludedCompile-time <dataset name="myData" src="myXMLDoc.xml"/> HTTP dataRuntime <dataset name="myData" request="true" type="http" src="myXMLDoc.xml" />
Embedded Data Embedded data is XML between the dataset<dataset> tags. When the OpenLaszlo Server compiles the application, the data is bound into it. The data can still be changed after the application runs. Included data is static.
Included Data Included data is essentially the same as embedded data, except that the XML itself is kept in a separate file. The size of the initial download will be the same as with embedded data. It is locally referenced via the filesystem, so it can be placed in other directories. Included data is static.
HTTP Data Remote data goes over HTTP, which means it can (but doesn't have to) be dynamic. If it is static, then the only difference between it and included or embedded data is that it is downloaded after the application loads. The type="http" attribute indicates that this is an HTTP request. The requests can be either GET or POST. There are several points at which the client requests the data: The client will request the data as soon as the app loads if the dataset's requestrequest attribute is true. If the requestrequest attribute is set to "true", the client will also request the data every time the querystring or base URL of the dataset changes (using the setQueryString()setQueryString() or setSrc()setSrc() respectively) methods of the lz.dataset object.When the dataset's doRequest()doRequest() method gets called. In the table above, we referenced a file locally (myXMLDoc.xml), but we could have done it absolutely, or we could have hit a server-side (PHP, ASP, JSP or some CGI) that returned an XML document. We could add the query string to the dataset<dataset> tag: <dataset name="myData" src="http://www.myServer.com/cgi-bin/myXMLDoc.cgi?return=addresses"/> The type="http" attribute gets implied when the srcsrc attribute contains "http://".
Datapointers Datapaths are extremely handy, but if you need more control over the data, they can become cumbersome. Datapaths are actually extensions of datapointers, but are easier to learn, which is why we introduced them first. A datapointer is a pointer into the dataset, which can be moved around. It can only be in one place of the dataset at a time, but you can have multiple datapointers, each pointing to a different part of a dataset. Datapointers are not bound to views like datapaths are, but they do have a place in the view hierarchy (that is, they know about parents and children). You will use a datapointer when you need to operate on the data in some way. For example, using the same format of data as in the previous examples, say you wanted to find all the people who were in the South Park show: Manipulating datapointers width: 100%height: 50<canvas height="50" width="100%"> <text id="sometext"/> <dataset name="myData" src="resources/myShowData.xml"/> <datapointer xpath="myData:/myXML" ondata="processData()"> <method name="processData"> this.selectChild(); do { if (this.xpathQuery( '@show' ) == 'south park') { sometext.addText(this.xpathQuery('firstName/text()') + "\n"); } } while (this.selectNext()); </method> </datapointer> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** We are including the data from a local file. You can download that XML file here, and here's what it looks like: <myXML> <person show="simpsons"> <firstName>Homer</firstName> <lastName>Simpson</lastName> </person> <person show="simpsons"> <firstName>Marge</firstName> <lastName>Simpson</lastName> </person> <person show="simpsons"> <firstName>Montgomery</firstName> <lastName>Burns</lastName> </person> <person show="south park"> <firstName>Eric</firstName> <lastName>Cartman</lastName> </person> <person show="south park"> <firstName>Stan</firstName> <lastName>Marsh</lastName> </person> </myXML> The first time the selectChild() method is called, it selects the first <person> node. The selectNext() method call returns true as long as an XML node was successfully selected (that is, until there aren't any more). We exploit this by using it in a do ??? while loop, so that the same iteration occurs for every <person> node. We could also have given the <datapointer> onerror and ontimeout event handlers to capture any problems.
Differential debugging SWF and DHTML When you query or manipulate an invalid datapointer, in the past, you got back `undefined`. In DHTML, this often led to disaster down the road, so the compiler added a warning when you did this, but still returned `undefined` for backwards compatibility. In SWF, which is very lenient about dealing with undefined values, manipulating an invalid datapointer appears to have no ill effects; but the compiler still issues the warning, because you may run into trouble if you compile your code for DHTML. When you define an LZX class whose name is the same as an underlying runtime global, in the past, the runtime clobbered the runtime global, to allow you to say `new <tagname>` to dynamically create instances of your LZX tag. This leads to disaster in any runtime, so the runtime no longer clobbers the global and it emits a warning telling you you will need to use `lz.<tagname>` to reference the class dynamically.
Introduction to Classes and Object Oriented Programming
Introduction TODO: References to other chapters that deal with classes. LZX is an object-oriented, prototype-based language that allows you to create custom, reusable classes to streamline and minimize code. This tutorial shows you how to define and instantiate classes, as well as where to use them. The class<class>[unknown tag] tag is used to define classes. Classes are instantiated when the tag is used. Simple Class Example width: 100%height: 80<canvas width="100%" height="80"> <class name="MyClass" width="80" height="25" bgcolor="#CFD9AB"> <text align="center" valign="middle">Hello, World!</text> </class> <MyClass name="myFirstInstance"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The class<class> tag defined the class, and the MyClass<MyClass> tag instantiated it. Everything that was in the definition of the class is inherited by each instance of it that is created. That is, the instance of MyClass inherits a width of 80, a height of 25, a background color, as well as some text. The instance is named "myFirstInstance", in the same way we might name a view<view>[unknown tag] , or a window<window>[unknown tag] . In fact, when we write window<window>, we are actually instantiating the window class, which is a pre-defined class. In this example, there is only one element within the class (the text<text>[unknown tag] element). But classes can contain many objects; for example, views can contain many subviews.
The id attribute It's important to note that you should not assign an idid attribute in a class definition. Each id should be unique; ids are global and if you were to include an id assignment in the class definition, then creating several instances of a class would several views with the same id, which would cause unpredictable behavior.
Inheritance As mentioned above, instances of MyClass<MyClass> are going to inherit from it. Just because we didn't specify xx and yy coordinates in the class definition doesn't mean we can't give them to the instance: The class<class> tag defined the class, and the MyClass<MyClass> tag instantiated it. Everything that was in the definition of the class is inherited by the instance of it that we have created. i.e. The instance of MyClass<MyClass> inherits a width of 80, a height of 25, a background color, as well as some text. The instance is named "myFirstInstance", in the same way we might name a view, or a window<window>. The instances behave just like views. In fact, that's because we are extending the view class. By default, the class<class> tag extends the view class, so: <class name="MyClass"> ??? is the same as??? <class name="MyClass" extends="view"> It follows that you can extend any class you want: Extending the 'button' class width: 100%height: 80<canvas width="100%" height="80"> <class name="SpecialButton" extends="button" onclick="changeLabel()"> <method name="changeLabel"> this.setAttribute('text', 'Clicked! '); </method> </class> <SpecialButton>Not clicked</SpecialButton> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Since the button<button>[unknown tag] can take a text childnode that becomes its label, the SpecialButton<SpecialButton> class that extends it can also take the text childnode. The method changeLabel()changeLabel() is also inherited by the instance. Just as with attributes, methods can be overridden in instances: Overwriting methods width: 100%height: 80<canvas width="100%" height="80"> <class name="SpecialButton" extends="button" onclick="changeLabel()"> <method name="changeLabel"> this.setAttribute('text', 'Clicked!'); </method> </class> <simplelayout axis="y" spacing="10"/> <SpecialButton>Not clicked</SpecialButton> <SpecialButton> Click Me Now <method name="changeLabel"> this.setAttribute('text', 'Smashing!'); </method> </SpecialButton> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Rewriting methods can be very handy when using both your own and pre-defined classes components, but it's not practical for when you have a class for which every instance may need a particular argument.
Attributes We've used attributes before (e.g. width="160"), but these have been attributes that are present in the class we are extending. As mentioned above, it is useful to be able to pass an instance of a class an argument when it is created: Passing an argument to a class instance width: 100%height: 80<canvas width="100%" height="80"> <class name="SpecialButton" extends="button" onclick="changeLabel()"> <attribute name="changeToLabel" value="Clicked!" type="string"/> <method name="changeLabel"> var newLabel = this.changeToLabel; this.setAttribute('text', newLabel); </method> </class> <simplelayout axis="y" spacing="10"/> <SpecialButton>Not clicked</SpecialButton> <SpecialButton changeToLabel="Thank You!">Please click me!</SpecialButton> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** If we give the attribute a value, then that will be its default value, and will get assigned if we don't explicitly set it.
More Inheritance You can extend a class more than once. For example, an application might contain more than one kind of button: A standard button that is blue, then switches to green when we roll over it. Clicking this button writes out the name of a pet.A special button, that is red, but also switches to green when we roll over it. Clicking this button writes out a day of the week. Extending a class more than once width: 100%height: 180<canvas width="100%" height="180"> <resource name="standardButton"> <frame src="resources/button_blue.gif"/> <frame src="resources/button_green.gif"/> </resource> <resource name="specialButton"> <frame src="resources/button_red.gif"/> <frame src="resources/button_green.gif"/> </resource> <class name="MyButton" resource="standardButton" onclick="doAction()" onmouseover="doOver()" onmouseout="doOut()"> <method name="doAction"> pet.addText("dog "); </method> <method name="doOver"> this.setAttribute('frame', 2); </method> <method name="doOut"> this.setAttribute('frame', 1); </method> </class> <class name="MySpecialButton" extends="MyButton" resource="specialButton"> <method name="doAction"> weekday.addText("Monday "); </method> </class> <view name="buttons" x="5" y="5"> <simplelayout axis="y" spacing="10"/> <MyButton/> <text id="pet"/> <MySpecialButton/> <text id="weekday"/> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Here we only changed the doAction()doAction() method and the resource. The doOver()doOver() and doOut()doOut() methods remained the same, so there was no need to redefine them in the MySpecialButton definition.
Placing views inside of classes Since we frequently write classes that extend view, at some stage we're going to need to place a view inside an instance of a class that we have created. For example, we may want to write a window class and put a text field inside of its title bar. Or we may want to put some content inside the middle of our window. There are two ways to do this: giving the subviews placementplacement attributes, or giving the class the defaultplacementdefaultplacement attribute.
Placing views using the placement attribute If you had one view and you specifically wanted to place it in a particular subview of a class, you would use the placementplacement attribute of that view. An example is the title bar of a window: It's unlikely that you would need to place several things in the title bar of a window, so it's OK to have to add the placementplacement attribute to the subview of the class that you want to place in the title bar.
Placing views by defining class with a defaultplacement attribute TODO: Link to other chapters where this is described If you want every subview of a class to be placed in the same location, then you give the class definition a defaultplacement attribute. An example is the contents of a window class. If you built your own window class (see the window tutorial), you might well have an area for all window content. You might well place numerous subviews in that window, and you don't want to explicitly position each one. TODO: conclusion and link to next subjects.
Introduction to Drawing OpenLaszlo provides capability to do two-dimensional graphics. You use procedural code in a <drawview> element to draw lines and fill in shapes. In this tutorial we'll look at some of the key parts of the API and show some interesting effects that you can get with a few lines of code.
Background of the programming model The Web Hypertext Application Technology Working Group ("WHATWG") is "a loose unofficial collaboration of Web browser manufacturers and interested parties who wish to develop new technologies designed to allow authors to write and deploy Applications over the World Wide Web." This group recently published a specification for drawing (which can be found here). OpenLaszlo implements a subset of the whatwg drawing APIs. For a discussion of where OpenLaszlo differs from the full whatwg specification, please see the LZX Reference Manual.
The drawview The <drawview> class extends the <view> class. Thus, it inherits all the <view> properties; a <drawview> tag creates a rectangular view. At first glance a view seems like just another view: view and drawview width: 100%height: 75<canvas width="100%" height="75" proxied="false"> <simplelayout spacing="5" axis="x"/> <view height="50" width="50" bgcolor="red"/> <drawview height="50" width="50" bgcolor="blue"/> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Previously, <drawview> auto-sized when OpenLaszlo only supported Flash. To make this work in DHTML and Flash consistently, size must now be explicitly set, because DHTML doesn't support autosize. size can be changed at runtime, but it may clear the canvas, in which case a new oncontext event is sent to signal a redraw is needed. size need not be a fixed value; it can also be set to percentage values or constraint values. There are no additional tag attributes in the drawview class beyond those of view. However, drawview has four attributes in addition to those it inherits: fillStyleglobalAlphalineWidthstrokeStyle These attributes can be accessed and manipulated by procedural (script) code inside <method> tags inside a <drawview> element. The drawview class has ten methods associated with it. We'll take a look at them below, after explaining some more concepts.
Lines, shapes, fills To draw a line shape (or "path"), you position a logical pen at a starting point and then move it sequentially to x and y coordinates connecting points either by straight or quadratic lines. This line remains invisible until you "stroke" it. When you stroke a path you apply to it the defined line width and style, thereby making it visible. For example, this code produces nothing visible: invisble drawing <canvas height="200" proxied="false"> <drawview height="50" width="50"> <handler name="oninit"> this.moveTo(100,100) this.lineTo(200,100) this.quadraticCurveTo(120, 200, 300, 100) </handler> </drawview> </canvas> But by adding the line this.stroke() we produce the following: Stroking the line width: 100%height: 200<canvas width="100%" height="200" proxied="false"> <drawview height="200" width="200"> <handler name="oncontext"> this.moveTo(100,100); this.lineTo(200,100); this.quadraticCurveTo(120, 200, 300, 100); this.stroke(); </handler> </drawview> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The closePathclosePath method draws a line back to the beginning of the current path: Stroking the line width: 100%height: 200<canvas width="100%" height="200" proxied="false"> <drawview height="200" width="200"> <handler name="oncontext"> this.moveTo(100,100); this.lineTo(200,100); this.quadraticCurveTo(120, 200, 300, 100) this.closePath() this.stroke() </handler> </drawview> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Now that we have a closed shape, we can fill it. First we define a fillStyle, and then we fill the shape. fillstyle width: 100%height: 200<canvas width="100%" height="200" proxied="false"> <drawview height="200" width="200"> <handler name="oncontext"> this.moveTo(100,100); this.lineTo(200,100); this.quadraticCurveTo(120, 200, 300, 100) this.stroke() //fillSytles are hex numbers representing color. this.fillStyle = 0xff00ff this.fill() </handler> </drawview> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Note that if you don't close a path but call the fill() method, the path will be closed implicitly. Please consult the reference page for particulars.
Opacity and Gradients The globalAlpha attribute is used to set the opacity of lines and fills. It takes a value between zero (transparent) and one (opaque). Using globalAlpha width: 100%height: 200<canvas width="100%" height="200" proxied="false"> <drawview height="200" width="200"> <handler name="oncontext"> this.moveTo(100,100); this.lineTo(200,100); this.quadraticCurveTo(120, 200, 300, 100) this.stroke() this.fillStyle = 0xff00ff this.globalAlpha= .3 this.fill() </handler> </drawview> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** Remember that the code here is procedural, so alpha values apply to fillStyles and lineStyles that are in effect at the time that a stroke() or fill() method is called. In the above example, the globalAlpha value is 1 (the default) when the line is stroked and .3 when the color is filled in. A fillStyle can be a color gradient, that is, a pattern that blends colors over its area. To use a gradient, first create it using the appropriate (linear or radial) constructor function, then set the fillStyle to be the gradient. A gradient is an object of the type LzCanvasGradientlz.CanvasGradient; you define the parameters of the gradient by using methods such as addColorStopaddColorStop on it. addColorStopaddColorStop takes two arguments: the number of the stop, and the color. Adding a gradient width: 100%height: 200<canvas width="100%" height="200" proxied="false"> <drawview height="200" width="200"> <handler name="oncontext"> this.moveTo(100,100); this.lineTo(200,100); this.quadraticCurveTo(120, 200, 300, 100) this.stroke() //the gradient starts at the x and y where the curved line starts var g = this.createLinearGradient(200,100, 150, 300) //opacity is 0 -- the fill is invisible this.globalAlpha = 0; //starting color is black g.addColorStop(0, 0x000000); //now the opacity is set to opaque this.globalAlpha = 1; //the gradient goes from black to purple g.addColorStop(1, 0xff00ff); this.fillStyle = g; this.fill(); </handler> </drawview> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** To get a sense of what gradients look like, try varying the parameters of the ending x and y. Also, remember that drawviews have background colors. You can use them in conjunction with gradients and alpha values: Backgrounds and gradients width: 100%height: 200<canvas width="100%" height="200" proxied="false"> <drawview height="150" width="300" bgcolor="blue"> <handler name="oncontext"> this.moveTo(100,100); this.lineTo(200,100); this.quadraticCurveTo(120, 200, 300, 100) this.stroke() var g = this.createLinearGradient(200,100, 150, 300) this.globalAlpha = 0; g.addColorStop(0, 0xffffff); this.globalAlpha = 1; g.addColorStop(.3, 0xff00ff); this.fillStyle = g; this.fill(); </handler> </drawview> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The gradient can be linear or radial (for details, see the Reference).
Starting Over We've said that drawing a line is like moving a pen. So, how do you pick up the pen in order to move it to another spot on the canvas? Use the beginPathbeginPath method. beginPath width: 100%height: 200<canvas width="100%" height="200" proxied="false"> <drawview height="200" width="200"> <handler name="oncontext"> this.moveTo(100,100); this.lineTo(200,100); this.quadraticCurveTo(120, 200, 300, 100) this.closePath() this.stroke() this.beginPath() this.moveTo(200,000); this.lineTo(100,200); this.quadraticCurveTo(120, 200, 300, 100) this.closePath() this.stroke() </handler> </drawview> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** The clear method wipes the slate clean, so to speak. Clearing the view width: 100%height: 200<canvas width="100%" height="200" proxied="false"> <drawview height="200" width="200"> <handler name="oncontext"> this.moveTo(40,40) this.lineTo(80,40) this.lineTo(80, 80) this.lineTo(40,90) this.closePath() this.stroke() this.fillStyle = 0xff00ff; this.fill() </handler> <button onclick="parent.clear()" text="clear"/> </drawview> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ******************************************************
Integrating drawviews into OpenLaszlo applications Although the drawview<drawview>[unknown tag] API is procedural, it's simple to blend the procedural drawing API with the declarative LZX style. The following little program illustrates this. The r attribute is modulated in by an animator<animator>[unknown tag] element; when r changes, the redrawredraw method is evoked. Drawview with animator width: 100%height: 300<canvas width="100%" height="300" debug="false"> <class name="circle" extends="drawview" width="200" height="200"> <attribute name="circlecolor" type="color" value="white"/> <attribute name="r" value="100"/> <handler name="oncontext"> this.redraw(); </handler> <handler name="onr"> this.redraw(); </handler> <method name="redraw"><![CDATA[ if (! this.context) return; this.clear(); this.beginPath(); this.moveTo(x+r, y); var a = Math.tan(22.5 * Math.PI/180); for (var angle = 45; angle<=360; angle += 45) { // endpoint: var endx = r*Math.cos(angle*Math.PI/180); var endy = r*Math.sin(angle*Math.PI/180); // control: // (angle-90 is used to give the correct sign) var cx =endx + r*a*Math.cos((angle-90)*Math.PI/180); var cy =endy + r*a*Math.sin((angle-90)*Math.PI/180); this.quadraticCurveTo(cx+x, cy+y, endx+x, endy+y); } var g = this.createLinearGradient(0, 0, x+r, y+r) this.globalAlpha = 1; g.addColorStop(0, 0xffffff); this.globalAlpha = 0; g.addColorStop(1, this.circlecolor); this.fillStyle = g; this.fill() this.globalAlpha = 1; this.linewidth= 5; this.stroke(); ]]></method> </class> <circle r="50" x="50" y="50" circlecolor="blue"/> <circle x="50" y="50" circlecolor="teal"> <animatorgroup process="sequential" repeat="Infinity"> <animator attribute="r" from="20" to="0" duration="3000"/> <animator attribute="r" from="0" to="20" duration="3000"/> </animatorgroup> </circle> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2008, 2010 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** OpenLaszlo developers have already used the drawview API to create a color chooser and a paint program. See what you can do. Have fun!
Building a Calculator
Introduction In this section we draw on the concepts presented thus far to create a simple calculator. When you compile this application for SWF, you can uncomment the lines that embed fonts. This will import fonts that give a somewhat more polished look to the calculator. As throughout this Guide, the examples are "live" and you can edit them. However, many of the examples are a bit long and not ideally suited to the example editor. So, if you wish to do some experimenting with this example you may wish to copy the source files and resources to whichever directory you use for development. Keep in mind that this is an exercise in basic LZX, not in scripting, so there won't be too much discussion on how the script works.
Visual Layout Our first step will be to build the calculator visually. That is, to start with we will not worry about the functionality, we'll just make it look right. Let's start by bringing in all the art assets. There are a body, a display and a number of buttons. Using resourced for body image width: 100%height: 500<canvas height="500" width="100%"> Uncomment to include fonts for SWF fonts <font src="helmetbi.ttf" name="obliqueText" /> <font src="helmetr.ttf" name="displayText" /> art assets <resource name="calc_body" src="resources/body.png"/> <resource name="calc_display" src="resources/display.png"/> <resource name="button_grn" src="resources/button_green.gif"/> <resource name="button_red" src="resources/button_red.gif"/> <resource name="button_blu" src="resources/button_blue.gif"/> <view name="calculator" resource="calc_body" x="20" y="20"> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** In this code we have brought in the fonts and art assets we plan to use, and created the body of the calculator. Remember, if you're compiling for DHTML you cannot use imported fonts. We don't need to set the size of the view that is the calculator body, because it will stretch to the size of its resource automatically. Next we add the display: Adding the display width: 100%height: 500<canvas width="100%" height="500"> Uncomment to include fonts for SWF fonts <font src="helmetbi.ttf" name="obliqueText" /> <font src="helmetr.ttf" name="displayText" /> art assets <resource name="calc_body" src="resources/body.png"/> <resource name="calc_display" src="resources/display.png"/> <resource name="button_grn" src="resources/button_green.gif"/> <resource name="button_red" src="resources/button_red.gif"/> <resource name="button_blu" src="resources/button_blue.gif"/> <view name="calculator" resource="calc_body" x="20" y="20"> display <view id="display" resource="calc_display" x="20" y="31"> the text in the display <text name="screen" height="30" width="165" font="displayText" fontsize="25" y="5" x="5"> 0 </text> </view> </view> </canvas> * X_LZ_COPYRIGHT_BEGIN *************************************************** * Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. * * Use is subject to license terms. * * X_LZ_COPYRIGHT_END ****************************************************** There are two parts to this. The first is the light gray art asset that is the display itself, and the second is the text in the display. We're naming everything with name attributes now, so that later we will be able to refer to these elements from scripts (for example, to change the text in the display). Note that the text tag is within the display view tag. In fact it's convenient because it allows us to position the text tag relative to the display, which is how we think of it. The buttons are all the same size, and they're arranged in a grid. The buttons are actually arranged in rows (or you might describe them as columns, but for the purposes of this tutorial, we're going to treat them as rows). Each button will be a view with a resource attached to it. We could have used images for the labels, but it is far easier to use text. Here's the code for a single button: code for a single button <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> 7 </text> </view> The resource attribute tells the button what image to use. Remember the button will automatically size to fit the view. Next the clickable attribute means that this button will be clickable - it will have an onclick event, which will come to later and the mouse will change to a hand when it hovers over it. The text tag is the button's label. The onclick event handler is calling a method that we haven't written yet. The code for a row of buttons is as follows: code for a row of buttons <!-- row 2 --> <view name="row2"> <simplelayout axis="x" spacing="7" /> <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> 7 </text> </view> <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> 8 </text> </view> <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> 9 </text> </view> <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> * </text> </view> </view> Notice that we're using the simplelayout tag to control the spacing of the buttons in the x-axis. For four rows of buttons, we will just repeat the above code, and we shall space all the rows with a simplelayout in the y-axis. Here's the code so far: Spacing rows with <simplelayout> width: 100%height: 500<canvas height="500" width="100%"> Uncomment to include fonts for SWF fonts <font src="helmetbi.ttf" name="obliqueText" /> <font src="helmetr.ttf" name="displayText" /> art assets <resource name="calc_body" src="resources/body.png"/> <resource name="calc_display" src="resources/display.png"/> <resource name="button_grn" src="resources/button_green.gif"/> <resource name="button_red" src="resources/button_red.gif"/> <resource name="button_blu" src="resources/button_blue.gif"/> <view name="calculator" resource="calc_body" x="20" y="20"> display <view id="display" resource="calc_display" x="20" y="31"> the text in the display <text name="screen" height="30" width="165" font="displayText" fontsize="25" y="5" x="5"> 0 </text> </view> 5 rows of buttons <view name="buttons" x="19" y="88"> <simplelayout axis="y" spacing="7"/> row 2 <view name="row2"> <simplelayout axis="x" spacing="7"/> <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> 7 </text> </view> <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> 8 </text> </view> <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> 9 </text> </view> <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> * </text> </view> </view> row 3 <view name="row3"> <simplelayout axis="x" spacing="7"/> <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> 4 </text> </view> <view resource="button_blu" clickable="true" onclick="display.inputDigit( this );"> <text name="buttonText" font="obliqueText" fontsize="30" fgcolor="#ffffff" x="9" valign="middle"> 5 </text> </view> <view resource="button_blu" clickable="