Like XUL widgets, XBL uses JavaScript to provide functionality to bindings by accessing XPCOM methods via XPConnect. Like binding content, behavior is optional in a binding. Each can exist without the other. At times, you might want only implementations, such as a base binding that contains certain properties that are inherited by other bindings.
The <implementation> element is the container for all other elements that make up a binding's behavioral portion. Example 7-1 shows an empty implementation shell that highlights the element's contained hierarchy.
The code in Example 7-1 shows the <implementation> element having a constructor, destructor, method, property, and field as possible children. Each component can exist in quantities of zero or more, with the exception of the constructor and destructor, of which there can be only zero or one. The rest of this section describes each binding implementation component in more detail.
Bindings can exist solely as content generators, acting passively when they are drawn. But you can also create bindings that provide new capabilities and more interactive functions or that execute routines in your application.
In the spirit of self-containment, functions can be added to a binding that carry out functionality related to that binding. These functions are the behavior of a binding. The ideal way to add behavior is to add methods to your binding with the <method> element. Each parameter for a method defined in the <method> element is contained within its own <parameter> tag.
<method name="dumpString"> <parameter name="aString1"/> <parameter name="aString2"/> <body> <![CDATA[ if (!aString1 && aString2) return; return dump(aString1+" "+aString2+"\n"); ]]> </body> </method>
To use the method to print text to the command shell, call the name that is specified in the name attribute. All methods created in a binding are added to the binding element object and called as members of that object, as shown here:
<mybinding id="myNewWidget">
<image src="http://www.mozdev.org/sharedimages/header.gif" />
</mybinding>
<button label="test method"
oncommand="document.getElementById('myNewWidget')
.dumpString('hello', 'there!');"/>
Using the <![CDATA XML entity is also important. The purpose of <![CDATA is to escape JavaScript that may otherwise cause conflicts with the XML parser. Having characters like quotes and slashes in XML is problematic when they are not escaped. Using <!CDATA with large portions of JavaScript in a binding can improve performance and minimize bugs.
Methods were designed for language neutrality with the type attribute and getter and setter elements. Currently, bindings support only JavaScript, which is the default when no type is specified. However, this may change as other scripting engines are plugged into Gecko.
Two special methods exist in XBL that allow you to manipulate what happens when a binding is added or removed from a document. These methods use the native <constructor> and <destructor> tags. A constructor is valuable, for example, when you need to initialize a binding object or prefill the UI based on stored values. Destructors can clean up when a binding is discarded.
When bindings are attached, their content is inserted into the document at the point you specify. After insertion -- or, more specifically, after the attachment occurs -- the code in the <constructor> executes. Similarly, you can use the <destructor> element to execute functions when a binding is destroyed.
<implementation> <constructor> <![CDATA[ dump("\n********\nCreate\n********\n");]]> </constructor> <destructor> <![CDATA[ dump("\n********\nDestroy\n********\n");]]> </destructor> </implementation>
This example prints some text to output, but you can include code that carries out variable initialization or anything else you want to occur at these stages of the binding's life. Bound elements constructed before the document load event execute this script before the document load event fires. In the case of extended bindings, base handlers are fired first, followed by derived handlers.
Properties on a binding are included by using the <property> element. Fundamentally, properties are used to hold and manipulate values that are used elsewhere in the binding, such as when a text widget has its displayed value changed periodically by the application. Currently, there are two classifications for properties, one of which gets a raw value directly on the element.
<property name="someAttribute"> false; </property>
This example always sets the attribute named someAttribute to false. Though still supported, this use of the <property> element was replaced by a new element called <field>. Although it is not in the XBL 1.0 specification, the <field> element is implemented and used in the Mozilla code base. It has the same syntax but a different name. We recommend using the <field> element.
<field name="someAttribute"> false; </field>
The second property usage defines functions that are carried out when getting and setting a property's value. Take this anonymous content with a label child:
<xul:box align="left" flex="1"> <xul:label xbl:inherits="value=title"/> <xul:spacer flex="1"/> </xul:box>
This content simply shows some text on the screen. The bound element that uses this content looks like this:
<mybinding id="my-binding" title="For Those Who Love to Use XBL" />
The XUL label used in the binding inherits the title attribute value that is set on the bound element. Example 7-2 shows how to set this title and its retrieval. Both access the bound element, and any changes filter down to the label.
The script keyword val is used internally to represent the latest property value. The request to change the property or retrieve its value can come from another property in the same binding (this.<propertyName>), from a binding method, or from a method in the bound document that accesses the binding object directly. This JavaScript sets the value of a property named title on the binding object:
var titleElement = document.getElementById("my-binding"); titleElement.title = "The Adventures of an XBL hacker";
You can use the onget and onset attribute as an alternative to <getter> and <setter> elements. Properties are initialized after the content is generated but before the binding attached event is set off. This ensures that all properties are available once that event occurs.
Although it is most commonly used just for getting and setting values on the property, nothing stops you from putting more code in the <properties> element that carries out other actions on the binding. One scenario shown in Example 7-3 is if you have a property that holds a search value, you can send that text to the Google [1] API, and fill another widget in the UI with the results every time the value is updated. [2]
The value of the search string is set to the value that has been given to the property: var q = val. This value is then added to the parameter list (SOAPParameter) for the SOAP call, along with other parameters that are obtained from other properties in the binding (e.g., this.maxResults).
[1] | This example is modified code taken from http://www.segment7.net/mozilla/GoogleAPI/GoogleAPI.html, and is covered by a three-clause BSD license. More on SOAP and the SOAP API in Mozilla can be found at http://lxr.mozilla.org/mozilla/source/extensions/xmlextras/docs/Soap_Scripts_in_Mozilla.html. |
[2] | The Google API requires a Google Key, and more information can be found at http://www.google.com/apis/. |