XBLinJS Tutorial

If you don't know what XBLinJS is, see the overview.

In this tutorial, we will build the beginning of an Image selection widget, until we run out of things to talk about in XBLinJS. If completed, it would allow the user to select an image somehow (typing a URL into a text box, perhaps), and see the image live, perhaps before submitting a form.

Browser Choice

While this tutorial will work in both IE and Mozilla, along with any other browser that supports enought Javascript and DOM to run XBLinJS, I strongly recommend Mozilla for this tutorial, because it outputs much better error messages. When this tutorial shows output that varies due to browser differences, the Mozilla output will be shown.

Starting Out

To follow along, start by creating this basic HTML file, and putting it in the same directory as the XBLinJS source Javascript files and CSS files:

<html>
  <head>
    <link rel="stylesheet" type="text/css" href="widgets.css" />
    <script src="jobject.js"></script>
    <script src="flavors.js"></script>
    <script src="widgets.js"></script>
    <script src="misc.js"></script>
    <script src="widgetJavascriptConsole.js"></script>
    <script src="widgetsBasic.js"></script>
    </head>
  <body onload="ReplaceWidgetTags(window)">
    </body>
  </html>
This file is available as tutorial1.html.

This does nothing at the moment, but is the basic shell. In practice, those Javascript files will be all loaded into one file, but with this you can use the XBLinJS source files directly as downloaded.

Breaking it down line by line (other than the HTML lines which I assume you understand):

  1. <script src="jobject.js"></script>: Implements the extended object system that XBLinJS is built on. Right now, it isn't of much concern to you, but if you like the general object system XBLinJS presents, but don't want to use XBLinJS itself, you can take just this file.
  2. <script src="flavors.js"></script>: Defines the ability XBLinJS has to work under multiple situations, like in a normal HTML browser or in SVG in an XML browser. At the moment, this is also just foundation to you.
  3. <script src="widgets.js"></script>: Defines the basic machinery of the Widget class that we will explore here.
  4. <script src="misc.js"></script>: Provides some nice functions for debugging we will be referencing a few times here, but is not itself necessary. It's not really a part of XBLinJS and has some nice functions you may want to grab anyhow.
  5. <script src="widgetJavascriptConsole.js"></script>: Implements the Javascript Console, which we will invoke in a moment.
  6. onload="ReplaceWidgetTags(window)": Runs a function that implements the <widget> tag functionality.
Debugging Environment

Step one is to make it easy to debug. In the body of the HTML file, add a <widget> tag like this:

<widget widgetType="JavascriptConsole"></widget>
This file is available as tutorial2.html.

It is important to include the closing tag here, as we are in HTML mode and it is not sufficient to close the open tag with a slash (<tag />).

Now, pull open that file in your browser. Basic debugging:

And voila; you now have a Javascript console in the context of your development environment which you can play with.

However, that doesn't directly help us with our image selector. Open a <script> tag underneath the <widget> tag, and we'll get started.

Creating a New Widget

Create a new widget class by calling deriveNewWidget.

deriveNewWidget("ImageSelector", Widget);

This creates a new Javascript class called ImageSelector, derived directly from the Widget class. (Changing that parameter will change the base class.)

Creating a new Widget is relatively complicated, so the deriveNewWidget function is provided. This creates a new widget which you can now add to the document. Underneath the JavascriptConsole widget, add a widget tag:

<widget widgetType="ImageSelector"></widget>

(The name ImageSelector is case sensitive.)

This file is available as tutorial3.html.

You can now view the page again. Underneath the Javascript Console, you should see what is basically an error message, starting with "This is the content of the Widget class". This is the default contents of a widget, which you didn't override. Since this is certainly not what you want, the default content is a helpful message. Let's correct that.

Adding Content

Adding content is unfortunately the dodgiest part of XBLinJS, but here's the basics: You define a Javascript Object that tells XBLinJS how to construct the DOM nodes that will make up the the Widget. The full details are available in the documentation, but a quick summary of the relevant attributes we will use:

There are other elements, but we will describe them as we use them.

The attribute of ImageSelector that determines what is used as the content is, not surprisingly, the .content attribute on the prototype. So, after the deriveNewWidget call, add the following Javascript:

ImageSelector.prototype.content = {
  tagName: "p", children: ["This is our new content"],
  name: "paragraph"
};
This is available as tutorial4.html.

Because the .content specification often gets long, I usually place it on multiple lines, even when it initially fits on one.

If you reload the file, you will see that the message has changed to match what you just created.

Dynamically Adding Widgets

Before we add any more to our widget, let's take advantage of the Javascript Console for a moment and examine the Widget class we just defined.

We will represent input and output on the Javascript console like this:

> input
output
> input
output

You already have one instance of your new widget on the page, but let's create a new one to play with:

> imageSelectorWidget = new ImageSelector()
[ImageSelector WidgetGlobals.Widgets[5]]

The default toString method of the Widget class, which was implicitly called to generate that output string, tells you two things: The type of the Widget (ImageSelector), and how you can retrieve it if you want to manipulate it, which is really convenient for debugging. Try it:

> WidgetGlobals.Widgets[5]
[ImageSelector WidgetGlobals.Widgets[5]]

Normally, you would not access a Widget via WidgetGlobals, but this is one instance where it's OK (debugging). WidgetGlobals is mostly used for XBLinJS bookkeeping, but if you're curious what is in there:

> display(WidgetGlobals)

(Note display is a function I wrote; it exists in misc.js.)

There isn't much of interest to you at the moment in there, but the display function can be useful.

You might wonder why your new ImageSelector was widget number 5, when you can only count two others on the screen, the Javascript Console and the other ImageSelector. The answer is that Widgets can contain other Widgets, and the Javascript Console contains some other Widgets, which you can peek at:

> display(WidgetGlobals.Widgets)

Widget 0 is an internal widget used for technical purposes; it does not appear in the DOM nodes of the page.

Widget 1 is the Javascript Console.

Widget 2 is a DOMWrap widget, which is wrapping the <input> element in the console so it has the various Widget services available to it. (Without this, the following Widget could not do its job.)

Widget 3 is the HistoryAddin, which is a Widget that implements the history for the Javascript Console, which you may not have noticed it has. After entering a few commands on the console, try pressing "UP".

Widget 4 is the ImageSelector that we added to the page with the <widget> tag.

Now that we have our new ImageSelector widget, we need to add it to the page. Note that imageSelectorInstance is an ImageSelector instance, not a DOM node:

> imageSelectorInstance instanceof Widget
true

That means we can not directly use the .appendChild method of the normal DOM nodes, because DOM nodes won't understand what a Widget is. In Mozilla, for instance:

> document.body.appendChild(imageSelectorInstance)
NS_ERROR_DOM_HIERARCHY_REQUEST_ERR: Node cannot be inserted at
the specified point in the hierarchy

(That's a deceptive error message, as imageSelectorInstance isn't a "node" at all.)

The function DOM() is provided to get the DOM node out of the widget that can be added to the document. The DOM() function will return something that is already a DOM node unchanged, so it may be used safely to make sure you have a DOM node:

> DOM(document.body).appendChild(DOM(imageSelectorInstance))

By now you probably need to do some scrolling to see it, but there are now two instances of your prototypical ImageSelector widget on the bottom of the page. A couple of quick manipulations:

> imageSelectorInstance.hide()

This quickly hides the widget by setting the display of the top-level node to "none". It's the same as:

> DOM(imageSelectorInstance).style.display = "none"

And:

> imageSelectorInstance.show()

will make it re-appear; as you might expect, it is equivalent to

> DOM(imageSelectorInstance).style.display = ""

You may recall that I said the "name" is used by XBLinJS to access that node. One of the things XBLinJS will do is assign that attribute on the created Widget to the corresponding DOM node (or Widget, but more about that in a moment). So:

> imageSelectorInstance.paragraph
[object HTMLParagraphElement]

This is, as it says, the standard DOM node:

> imageSelectorInstance.paragraph.style.backgroundColor = "red"

turns the background red, just as you'd expect.

Now that you've worked with the basic debugging, back to the main event:

Adding An <img> Tag

If we're going to have an image selector, sooner or later we're going to need an image tag. Change your content to read as follows:

ImageSelector.prototype.content = {
  tagName: "div", children: [
    {tagName: "p", children: ["Choose your image: "]},
    {tagName: "img", src: "one.jpg", name: "image"}
  ]
};

Also, add the attribute globalname="selector" to the ImageSelector widget tag; this will give that widget the global name "selector", which you can see in the Javascript console widget:

> selector
[ImageSelector Widgets.WidgetGlobals[4]]
This is available as tutorial5.html.

Notice in the content specification for the <img> widget, we have a src: "one.jpg" entry. Anything XBLinJS doesn't understand in the content entry is passed along to the DOM node (or widget) via .setAttribute. In this case, we set the source of the image to start out pointing at one.jpg.

Setting And Getting Attributes

We could manipulate the source of the image by accessing selector.image.src all the time, but that reveals more about the implementation of the widget to the outside than we should. It also doesn't give us a chance to hook into the setting process, which we will want to use in a bit. We'd much rather say something like selector.set("imageSource", "newSource.jpg"), as if this were one widget.

XBLinJS Widgets (actually, JObjects) use a generalized .set[Attribute] and .get[Attribute] interface, and provide several ways to hook into that interface as you write Widgets, depending on your needs.

inherits

The simplest way is to use inheritance, allowing the inner widget to "inherit" the setting of the outer widget. Change the content line for the <img> tag to:

{tagName: "img", src: "one.jpg", name: "image",
 inherits: "src"}
This is available as tutorial6.html.

Now, in the Javascript widget:

> selector.get("src")
.../one.jpg

(The exact content of the ... will vary, depending on your situation.)

> selector.set("src", "two.jpg")

And the image changes.

In more complicated cases, you may want to rename the inner attribute; suppose you had two <img> tags inside, and you want to be able to change the src independently. (It will "work" to have both inherit src, but they will then both always be set to the same thing, which isn't what you want.) You can do this:

 inherits: "src=imageSrc"}
This is available as tutorial7.html.

And then:

> selector.set("imageSrc", "two.jpg")

will reset the image.

I sometimes have trouble remembering the order, so the mnemonic to help remember is that the attribute getting set on the inner element ("src" in this case) is on the left, like a conventional variable assignment.

Inheritance works in the simple case, but gives you no opportunity to perform any logic on the setting or getting.

get_* and set_*

"inherits" is a frequently used idea, but a weak one. It's much nicer to have "properties". Ideally, you have a situation where obj.property can fire a method call. Javascript supports that in some environments... but not IE.

To make up for that somewhat, XBLinJS allows you to define methods on your object with a name of the form get_X and set_X. XBLinJS will call those functions when you use obj.get("X") or obj.set("X", "value").

(Actually, XBLinJS supports real Javascript properties in environments that support them, but you can only use them if you commit to those environments, which as of this writing means writing off IE, which most people can't do.)

As it turns out, as nice as it is to set the image directly, we really want to capture the setting of the image, because we don't want to just display the entire thing inline. We want to display a "thumbnail". (It's not a real thumbnail because you still have to load the entire image, but it's as close as we can come online without server support. In some ways, it's not all bad anyhow, as you can verify directly that it is the true image.) We can accomplish this by add a .set_imageSrc method. We'll let the default .get implementation just return the .imageSrc attribute, so we don't have to write that method.

Get methods are called with no parameters, and set methods are of course called with the parameter being set.

First, verify that your new method is called:

ImageSelector.prototype.set_imageSrc = function (value) {
  alert(value);
}

Remove the inherits statement in the content, as you don't need it anymore.

Note we have to use ImageSelector.prototype.set_imageSrc = function (value) and not function ImageSelector.set_imageSrc (value), because Mozilla does not support the latter syntax. If you can commit to IE, feel free to use the latter. (To make the typing shorter, you can do something like is = ImageSelector.prototype, and use is.set_imageSrc = function (value). I choose not to do this because it messes up my documentation system.)

Pull that up, and .set the imageSrc to something. You should get what you set alerted.

> selector.set("imageSrc", "something")

Of course, the image doesn't change anymore, and we should fix that. Replace the alert with something to set the src of the image. Remember, the image is named image:

 this.image.src = value;

We need to be able to get the source of the image back out, too. You have two basic choices... you could store the value in this.imageSrc, and the default behavior of the .get method will retrieve that if you call it.

 this.imageSrc = value;

Or, you could write a .get_imageSrc method to retrieve it directly from the image on demand. I tend to prefer the latter, because it minimizes the possibility that something will get out of sync. It also means you get the full URL, not just what you set the URL too, which is useful behavior we will exploit later.

ImageSelector.prototype.get_imageSrc = function () {
  return this.image.src;
}
This is available as tutorial8.html.
Variables

If you have get_* or set_* functions you use repeatly, you should factor them out into a "Variable", which is a sort of composite variable on a Widget that you can program how it behaves. This makes more sense than copying and pasting the same basic code, over and over. Variables can take parameters in their definition; see the docs for more information, or some of the sample code.

(This isn't a very good name, but I haven't come up with a better one.)

Default Fallback

If you call .set[Attribute] or .get[Attribute], and none of these features are used with that name, Widgets will fall back to setting and getting the attribute on the widget object itself. Thus, to future proof your code, you can, if you choose, use .get or .set to access attributes of the Widget that you may want to use the *Attribute machinery on later.

Event Handling

What use would a widget library be without event handling?

At the moment, we are in a sort of transition period between two event models for web pages. The original event model is that each element can have one event handler, and the handler is called as appropriate. The 4.x series of browsers introduced some idea of "event bubbling", which was rather poorly defined.

XHTML inherits its behavior from the DOM Events specification. In that model, you add "listeners" to nodes for given events, and there can be any number of listeners for the same event. There are no order guarantees for the order of the listeners called.

Despite the fact the latter should only be in XHTML documents, for some value of "should", it is often available even in pure HTML documents, and the interaction can be interesting if you use the both together.

Both models have their weaknesses; standard HTML can't have multiple things looking at the same event. This may not seem like that big a deal, but it leads to a natural implementation of some things. For instance, the Javascript console you use on the page has a History Widget that independently implements the History behavior. It gets first crack at the keypress events, and only acts if it sees a keypress it "wants", like the Up Arrow. If it does, it eats the event, otherwise, it passes it on. This is often quite useful.

DOM events, for reasons that I don't quite follow, don't guarantee the order the handlers listeners will be called, which also makes this behavior impossible.

XBLinJS implements its own event routing system on top of either of these systems, as long as you don't interfere by adding more listeners or resetting the handler XBLinJS sets. It allows both multiple handlers for one event, and guarantees they will be called in reverse order of registration (most useful).

This is an appropriate time to discuss it in terms of our lil' project, because when the users changes the image, we really would like to capture some information about it, so we can size it correctly. However, changing the image might require a trip out to the network, and the browser doesn't let your code wait for that. Fortunately, it does fire a load event on the image when the image loads in. (We're going to ignore the case where the image fails to load for the purpose of this tutorial.) We need to capture that event and handle our sizing then.

The easiest way to capture an event in XBLinJS is to create a specially-named method, named after the event you want to capture. In the simplest case, when you want to handle that event on the top-level DOM node, or you want to allow it to bubble up to that point, you can just name it after the event, prefixed with "on" (like in an HTML tag). In this case, we want to capture it on a specific sub-element of the Widget, which we can do by adding an underscore, then the name of the thing we want to add the handler to.

We'll start once again by simply proving that the function we create is being called:

ImageSelector.prototype.onload_image = function (event) {
 display(this.image);
}
This is available as tutorial9.html.

Now, when you load the page, you get a dialog box pretty quickly showing the details of the image element. If you reset the image source...

> selector.set("imageSrc", "two.jpg")

you'll get another dialog box, and note it's filled with the info for the new image, not the old one.

You can of course capture any event that the browser will let you capture.

Advanced Handling

Advanced event handling is beyond the scope of this tutorial, but if you study the behavior of the History Widget, you can learn a lot about how one Widget can tie into the events of another cleanly. You can implement "plug-in" Widgets like the History Widget, or, if you need it, use that Widget yourself. You can also see how to register events manually.

Thumbnailing Behavior

The logic needed to get the images to show up as thumbnails is boring and not relevant to this tutorial. Therefore, to continue this tutorial, and to make sure you are in sync with the tutorial, pull tutorial10.html out of the tutorial directory to continue.

This already contains the basic logic to shrink the image to thumbnail sizes, if necessary.

Passing Attributes To The Widget Via HTML

When creating a widget in code, you pass the desired attributes in as a Javascript object as the first parameter of the new invocation:

var imageSelector = new ImageSelector({imageSrc: "one.jpg"})

In order when using HTML to construct the widgets, you can pass attributes on the HTML tag itself. These attributes are passed through the standard get_*/set_* system,

Note in tutorial10.html, the thumbnail logic looks up this.maxsize to see what the max size ought to be. You can change this in the HTML tag that invokes the ImageSelector widget:

<widget widgetType="ImageSelector" globalname="selector"
        maxsize='100'></widget>

No explicit code is needed for that; a .set("maxsize", "100") call is made during Widget creation, and since nothing was defined especially for the maxsize parameter, it fell through to the object itself.

More Things To Do

Obviously, this is not yet a complete widget, but the remainder now is applying what you already have learned. To complete the widget, you would need to:

You could also:

But I figure you'd rather use the library to solve your own problems than watch me putter through this one.

Jerf.org : Programs & Resources : XBLinJS Tutorial

 

Current Section Links

 

Sub-Sections

 

Search Jerf.org
Google

Search WWW
Search jerf.org