Friday, March 24, 2006

options=ignorelayout

I am experimenting with a homemade OpenLaszlo class that is used entirely for audio. Let's call it AudioClass. Naturally, it frequently has setSource('blah.mp3') called on it.

This class has no visible GUI elements, but it is a subview of other classes which do have GUI elements, like buttons and so on.

I had the problem that whenever setSource was called, the GUI elements in the parent view would shift up and down slightly, as if making room for something, but then dropping back to their original positions. It was making me nuts. I tried setting the AudioClass height and width to zero, setting visible to false, and so on. Anything to make this class essentially not visible to the rest of the application, so it would not affect the layout. No luck, buttons kept moving.

Finally I found the answer, sort of by accident: use options=ignorelayout in AudioClass. The ignorelayout option is explained in the Guide, although I feel the explanation is poor, and how it works in my case is not explained.

I found this solution by searching the OpenLaszlo forums for "setSource problems" in the hopes of finding someone else with this exact problem. Instead I found that Shelby had posted some sample code for a media player which did not exhibit my problem; the options=ignorelayout attribute had been set there.

Hooray for forums, and kudos to Shelby for taking the time to post!

Friday, March 17, 2006

error deploying laszlo app

This morning I fired up the eclipse IDE and tried to deploy my Laszlo application as usual: right-click on the file in the Navigator View and choose "Run as... Laszlo Application". After a few moments, I got an uninformative error message in a small window - "Error Deploying Laszlo App" - and the application failed to launch.

After much hunting, I found that there is a .log file in the .metadata subdirectory of my project workspace directory. This .log file had a more informative stack trace for the error: "org.eclipse.core.internal.resources.ResourceException: Resource is out of sync with the file system: /Laszlo_Project/C__Program Files_OpenLaszlo Server 3.1.1_Server_lps-3.1.1_lps_components/base/baseslider.lzx."

Aha! I had edited baseslider.lzx using Notepad to add debug code yesterday to it yesterday. Later I had removed the debug code so the file was actually pristine except for the "last modified" date. Even so, Eclipse apparently cannot handle such an affront; it will smack you if you edit source outside the IDE.

So again, I had to go to the Navigator View, and under Laszlo_Project I found the directory "C__Program Files_OpenLaszlo Server 3.1.1_Server_lps-3.1.1_lps_components" (which I believe is an imported resource). I opened that out, opened the "base" folder inside that, and found baseslider.lzx. I right-clicked on that file and hit the "Refresh" menu item.

This fixed the problem; now the application deploys fine. Yay!

I am less than thrilled with Eclipse as an IDE for OpenLaszlo...

Thursday, March 16, 2006

What is totalframes?

The OpenLaszlo documentation for LzView defines totalframes tersely, as "The total number of frames for this view's resource."

I'm not finding that this is true for streaming audio. When the audio begins playing, totalframes starts at some smaller number (say, 1100), and continues to ramp up until it reaches a constant value (say, 5991) after a few seconds of playing. I think what's happening is that totalframes is actually/approximately the number of frames currently buffered. I'll have to experiment with longer audio files to see how well this supposition holds up.

The docs should clarify the behavior of totalframes for streaming audio, or perhaps there is a bug in LzView.totalframes...

Wednesday, March 15, 2006

resizing an OpenLaszlo Text object

Instances of Text in OpenLaszlo have a default size. Here is the key to getting them to change their size depending on their text content. Set the resize attribute to true, like this:

<text datapath="mydata/text()" resize="true"/>

Monday, March 13, 2006

When is something truly undefined?

I've been browsing the OpenLaszlo blog. They've got a nicely detailed discussion of undefined, which is an ECMAScript primitive, not to be confused with a literal.

OpenLaszlo talks to the browser Javascript engine

Communication between the Flash player and the browser Javascript engine is easier than I thought when using OpenLaszlo. Here's some sample code:

<canvas>
<button>
<method event="onclick">
var js = "var el = document.getElementById(\"hello\"); el.innerHTML = \"Hello World!\"";
LzBrowser.loadJS(js);
</method>
</button>
</canvas>

As usual for SOLO deployment, compile this into a swf and embed it in an html page. In the body of the html, include the tag <div id="hello"/>. Open the html page in your browser, and click the button. Voilà, we see the "Hello World!" text appear in the browser, below the button. This is so cool, I will now proceed to do a happy dance!

OpenLaszlo right-click handler

I had a little trouble with the OpenLaszlo right-click handler, similar to what is described in this post. Specifically, the OpenLaszlo Guide says
To preserve the "about OpenLaszlo" information while disabling source, you can use a method like the following, on the canvas:

<method event="oninit">
this.removeViewSourceMenu();
</method>

<method name="removeViewSourceMenu">
var cm1 = new ContextMenu();
cm1.hideBuiltInItems();
cm1.addItem(this.__LZdefaultMenuItem);
this.setContextMenu(cm1);
</method>

This simply did not work for me. I even tried compiling the application and hitting the swf from my server - no luck.

I wound up doing what was described in the Changelog pages: building the context menus and handler functions inside <script> tags, and setting the context menu using the oninit method for specific views. However, I could never get this working on the canvas itself; it only worked for descendents of the canvas.

I did hunt through the source and find the way to open a popup window from the context menu. Here's a code snippet, similar to that in the changelog:

<script>
 function rightclick_handler(obj, menuobj) {
   Debug.write("hello");
 }

 function my_itemhandler(obj, item) {
  LzBrowser.loadURL("http://yourlink.com", "_blank");
  Debug.write("menu item selected", obj, item);
 }
  var cm1 = new ContextMenu(rightclick_handler);
  cm1.hideBuiltInItems();
  cm1.addItem(new ContextMenuItem("Goto my site", my_itemhandler));
</script>
...
<view>
...
 <method event="oninit">
  this.setContextMenu(cm1);
 </method event="oninit">
</view>

The hideBuiltInItems function is not useless; it prevents the Flash "Zoom In", "Zoom Out", "Show All", "Quality", etc menu items from appearing on right-click, as I learned when I removed it.

The LzBrowser.loadURL method is documented in the OpenLaszlo reference.

Friday, March 10, 2006

Loose typing is a bitch

A few weeks ago I created a bug in my Javascript by assigning a variable something like this:

function foo(mynum) {
var x = num;
... proceeed to do use x as if it were a number...
}

But x was being interpreted as a string, which made the code fail. Eventually I figured out what was happening, and changed the code to this:

function foo(mynum) {
var x = num*1;
...
}

which fixed the problem. This made me recall why I hate loose typing.

Well, today I did it again! I was setting a Laszlo button's "enabled" attribute as follows:

...
btn.setAttribute("enabled", "false");
...

Mysteriously this was not working. I hunted everywhere for other places in the code where the attribute was being set differently! And eventually I found the problem - to correctly set a boolean you had better not put it in quotes. Like this:

...
btn.setAttribute("enabled", false);
...

Ouch!

Thursday, March 09, 2006

Javascript in OpenLaszlo

Q: Can the Javascript in OpenLaszlo code talk to the browser DOM?

A: No.

I wasn't sure whether the Javascript in OpenLaszlo could access the browser DOM, typical objects like document. So I wrote a little test that did el = document.getElementById("x") within a script tag. I suppose the result is to be expected: the debugger output complains "reference to undefined variable 'document'".

What does the "cache" argument in LzView.setSource do?

According to the OpenLaszlo Reference, LzView has three arguments: source, cache, headers. The cache parameter, "if set, controls caching behavior. Choices are none , clientonly , serveronly , both (the default)."

In debugging, I wanted my resource (a jpg image, for example) not to be cached, and I thought setting cache to "none" would fix this. This seems to be the case when running LPS (Laszlo Presentation Server) on localhost. I set cache to "none" and checked my Firefox cache using the about protocol (specifically, "about:cache?device=disk", and ok, I know I could just look for the files on my harddrive but the about protocol is my new toy). The resources were not in the cache. Then I set cache to "clientonly" and I saw that Firefox was caching the resource.

But when you go SOLO, it appears that this setting is irrelevant. I don't know enough about Flash/SWF/OpenLaszlo to understand this. The documentation does not really spell this out for you, certainly not in the Reference. If you read through the section on Media Resources in the Guide, it's not perfectly clear either.

My tentative conclusion is that when using your OpenLaszlo SWF in SOLO mode you have to rely on your server to control caching. Generally I am not concerned about this problem for deployment, but during the build/debug phase it's a problem. Currently I am clearing the browser cache to force resources to refresh. I've played around with my Apache server settings (the <Files> ExpiresDefault and ExpiresAccess directives) but nothing seems to work.

Wednesday, March 08, 2006

Mozilla about protocol

Handy page with info on the about protocol:

http://kb.mozillazine.org/About_protocol_links

E.g:

about:cache — Displays cache statistics and disk cache directory location.
about:cache?device=memory — Lists memory cache entries.
about:cache?device=disk — Lists disk cache entries.


Good to know when investigating cache problems. The cache itself is located in something like C:\Documents and Settings\user_name\Local Settings\Application Data\Mozilla\Firefox\Profiles\9ach3rkc.default\Cache

Classes extend LzView by default

According to the OpenLaszlo Developer's Guide, "By default, the <class> tag extends the view class...". I thought I had read that somewhere, but it took a while to find the source.

Monday, March 06, 2006

weird bug?

Is this an OpenLaszlo bug or am I missing something? I had created a class with method "play" as follows:

<method name="play" args="obj, frame">
  Debug.write("in play method");
</method>

I called it from elsewhere and the Debug statement never printed. I changed the name to "playIt" and called the method with that name and it ran fine.

According to the manual, even if the superclass has a "play" method, I should just be overriding it here. I do not understand why this method would not be called!

Sunday, March 05, 2006

Fun with delegates

I just figured out an interesting little bug in my code. I had a view bound to a datapath. There were multiple items in the datapath, so there were multiple views created. My code looked like this (I haven't tested it, I've trimmed down my own code to get to the bare bones of the problem):

<MyItem datapath="resource-url/text()">
   <button onclick="startTimer()">
     <method name="startTimer()">
     parent.play(); // play resource
     if ("undefined" == typeof canvas.forwardDelegate) {
       canvas.forwardDelegate = new LzDelegate( parent, "forward" );
       LzTimer.addTimer( canvas.forwardDelegate, 1 );
     } else {
       LzTimer.resetTimer( canvas.forwardDelegate, 1 );
     }
     </method>
   </button>
   <method name="forward">
     Debug.write("forward: this.playing = " + this.playing);
   </method>
</MyItem>

Why I attached the delegate to the canvas is beyond me at this point; I think originally I was having problems accessing the delegate from other methods and I decided that stashing it in the canvas was a good idea. But the problem is the implied loop going on in the code (at the level where datapath is declared) - where forwardDelegate was getting attached over and over again to canvas.

When testing the code, I'd hit the button on each instance of MyItem, and I'd find one case where Debug.write said the resource was playing, but in the other cases they weren't! I didn't understand it because I knew empirically that the resource was actually playing. It was as if the "forward" method had some problem with it... and it did.

What was happening, I think, was that there was only one delegate pointing to a single instance method of "forward." The other delegates were getting overwritten (I assume) during the loop. So the resource was being played when I hit the button, all right; but when forwardDelegate called the "forward" method, it was not the "forward" method of the instance currently playing. Rather it was the "forward" method of a view whose resource was currently not playing.

This is the new code, which works as intended:

<MyItem datapath="resource-url/text()">
   <button onclick="startTimer()">
     <method name="startTimer()">
     parent.play(); // play resource
     if ("undefined" == typeof parent.forwardDelegate) {
       parent.forwardDelegate = new LzDelegate( parent, "forward" );
       LzTimer.addTimer( parent.forwardDelegate, 1 );
     } else {
       LzTimer.resetTimer( parent.forwardDelegate, 1 );
     }
     </method>
   </button>
   <method name="forward">
     Debug.write("forward: this.playing = " + this.playing);
   </method>
</MyItem>

setSource, but don't getSource

More weirdness with get/set methods in OpenLaszlo.

One uses the method setSource on LzView, but there is no getSource method. Rather, you just grab the resource directly as in the following code snippet:

this.setSource("http:./" + this.data);
...
Debug.inspect("parent source: " + parent.resource);

I can't wait to figure out the logic for all this. For example, LzView has getVolume and setVolume methods, no volume attribute. Why would that be? I need to spend some time digging around in the source.

OpenLaszlo is a declarative language

From Chapter 25 of The Guide, Section 4 "Creating objects from script":
"LZX is designed to be a declarative language, so instantiating views from script is not the recommended method for creating Laszlo applications."

Good to know. Don't you just love The Guide with all those little nuggets of wisdom strewn about in it?

scope in OpenLaszlo

I have problems with scope in OpenLaszlo. I do not understand. A Google search on "OpenLaszlo scope" is no help. So I'm reduced to running little tests.

This code does nothing:

<canvas debug="true">
<method name="createIt">
var it = "Buffy";
</method>
<method name="useIt">
Debug.write(it);
</method>
</canvas>


It does nothing because the methods are never called. Good, I understand this.

<canvas debug="true" oninit="createIt(); useIt()">
<method name="createIt">
var it = "Buffy";
</method>
<method name="useIt">
Debug.write(it);
</method>
</canvas>


This code produces a warning in the debugger window:
"WARNING: test_scope.lzx:7: reference to undefined variable 'it'
«undefined»"

Good, that is what I was expecting. The variable "it" was declared in createIt and is out of scope in useIt.


<canvas debug="true" oninit="createIt(); useIt()">
<method name="createIt">
it = "Buffy";
</method>
<method name="useIt">
Debug.write(it);
</method>
</canvas>


The debugger window reads "Buffy". In other words, "it" is not out of scope. This makes me weep. It is probably another one of those weird javascript things, and I do not understand. Is "it" global because I didn't use "var"? *sigh*

Friday, March 03, 2006

One blogger's experience with OpenLaszlo

Michael Sica of Ataraxis Software blogs about his OpenLaszlo experience. Even though his list of "bad" things is lengthier than his list of "good" things, the "good" thing which really nails it is the fact that OpenLaszlo runs in the Flash player. You just can't beat that.

I totally get his complaint about the parent trap (parent.parent.parent). It is very nasty to hardcode stuff like that; when you move a component one level down or up you have to redo everything. But I don't think this is an OpenLaszlo problem per se; I recall having a similar experience with a C# application that I maintained a couple of years ago.

On a side note, you see code in OpenLaszlo components with documentation like "this component expects to be in a bla component". The class then refers to its parent with the assumption that the parent is an instance of bla. That kind of thing makes me unhappy. I'm not a component developer, though; maybe there's no way around it.

What is "this?"

Here's an OpenLaszlo question that bothered me when I first started, although I have a better feel for how things work now.

In the OpenLaszlo "dashboard" demo, there is a class called music in file musiclib.lzx. The music class has a view called audioviewer; that view has a method playMedia.

Q: When the audioviewer method says this.play(), to what does "this" refer? Is it an instance of music, audioviewer, the canvas in which music is instantiated, or what?

A: I added debug code Debug.write("this is " + this); right after this.play() and ran it; output is: this is $t8 id: audioviewer. So, "this" means audioviewer.

I did not understand what the view tag was doing, initially, or any of the other tags for that matter.

I was thinking in terms of Java. There, you write methods and properties for a class. When you instantiate a class, you do not add things inside it. The only way you can affect the instance that you are creating is by passing arguments to it. (Possibly that is just what you are doing when adding tags inside the canvas tag - I'm still not clear about what really happens there.)

So in Java terms, when I put a view tag inside the canvas tag, it doesn't make sense that I've just declared an instance of a view object (instance of LzView) inside the canvas object. With Laszlo I get the feeling that this is what is happening. Currently, whenever I see a tag like <view> or <canvas>, I associate it with an instance of a class (LzView or LzCanvas). I need to do more reading about this!

Thursday, March 02, 2006

OpenLaszlo: "value is an expression type"

"Note that value is an expression type." Aha.... this clears up some confusion I had when using setValue.

In Chapter 26 sec 5.2 of the Guide, we read:
By default attributes are typed as "expression," which means JavaScript expression. In many cases this is not what you want. ... To make sure attributes do what you want them to do, type your attributes when you declare them.
Good stuff. My only problem is that you have to get to Chapter 26 before attribute types are fully explained. That's just wrong!

[Edit For the record, attributes have the following types:
boolean | color | expression | number | size | string | text | html]

Wednesday, March 01, 2006

Laszlo critique at Burtonia Blogs

Jeff Burton has posted a series on his experience with Laszlo:

  • Preface
  • Part 1
  • Part 2
  • Part 3
  • Part 4   Quoting Burton: There are some restrictions on [dataset]that are kind of onerous. For instance, it can't be part of a class. That may be true, but so far I haven't found any documentation to that effect. Also I have built classes that contain the dataset declaration and they compile ok. But at runtime when the code attempts to access data in the given path, I get the an error message like "couldn't find dataset for blaDataset:/myXML[1]/myData".

  • Part 5   Quoting Burton: Speaking of getting and setting... Laszlo has a confusing variety of ways to changing object attributes.... Well said! This is the source of my "setAttribute, don't setText" confusion.

    I also agree with his comments about the Laszlo component extensibility. It's kind of not-so-good. The stuff is opensource and you have to be willing to muck around in it.

  • Part 6
  • Conclusion

assigning an event multiple methods; setAttribute not setText

This is a little piece of Laszlo code that demos a couple of things I keep forgetting: 1) how to assign multiple methods to an event and 2) how to set button text programmatically (button does not have a setText method, and this.text = "bla" does not work).


<canvas height="100%" width="100%" debug="true">
<view>
   <button name="mybutton" onclick="writethat(); shrink();">
     <method name="writethat">
       this.setAttribute("text", "that");
     </method>
     <method name="shrink">
       this.setAttribute("width", this.width-1);
     </method>
   </button>
</view>
</canvas>

The following code will work to assign multiple methods as well:

<button name="mybutton" onclick="writethat(); shrink()">

Or you can do it without the space, even:

<button name="mybutton" onclick="writethat();shrink()">

It's adding quotes which gets problematic. This is the right way:

<button name="mybutton" onclick="writethat();shrink();Debug.write(&quot;hello&quot;)">

Escaping quotes with a backslash like this does not work because we are in XML-land:

<button name="mybutton" onclick="writethat();shrink();Debug.write(\"hello\")">

If you do the latter, you get this completely unhelpful compiler error: "Whitespace required before attributes. cannot be resolved or is not a field" and also 'Element type "button" must be followed by either attribute specifications, ">" or "/>"'.