Accessing Frontal Tags for Scripting

From Frontal Wiki

Jump to: navigation, search

With JavaScript, the user is given a Document Object Model to traverse and manipulate its elements. Frontal provides a similar mechanism that we'll introduce here.

Contents

Objects and Elements

First we need to differentiate between an element in the XML definition of a document and the object that represents it.

An element in XML is nothing more than a piece of specially formatted text. It's the Frontal SWF's task to read these elements from the XML and then create programmatic objects to handle them. That is, for every element that exists in the XML description, there's a programmatic object created to manage that element's graphic presentation, it's positioning, it's styles, it's attributes, it's interactions, and so on. So when we talk about accessing the Frontal document, we need to differentiate whether we're talking about the object or the element.

As we mentioned, every XML element in the Frontal definition is associated with an object. Every one of these objects is either a DocumentElement object or a variant of this object. (For those familiar with object-oriented programming, DocumentElement is the base class for all of these objects.) What this means is that every one of these objects shares a set of properties and methods, and it's these properties and methods that we use to access the Frontal document. Here are some of the properties and methods of a DocumentElement that are pertinent to this task. (See the Details section for additional useful properties and methods.)

DocumentElement properties useful for accessing the document include:

  • node - returns the XML element this object is associated with as a Flash AS3 XML object.
  • document - returns the DocumentElement object associated with the "document" tag in the XML definition (see the next section for more details).
  • parent - returns the DocumentElement object associated with the XML parent of "node".
  • mgr - returns the DocumentManager object. It's through this object that we can access other documents.
  • elements - returns an array of all the DocumentElement objects that consider this object their parent.
  • containers - returns an array of all of the visual DocumentElement objects that consider this object their parent. This includes those objects associated with 'div', 'text', 'img', 'video', 'form' and 'input' tags, but not 'manager', 'transitioner', 'style', 'include', 'script' and 'template' tags for example.

The methods it provides are:

  • gE (or equivalently getElementByIndex) searches the current document for DocumentElement objects based on their 'id' and 'name' attributes.
  • getElementById is similar but only searches based on the 'id' attribute.
  • destroy removes an object and its associated XML element.

The "gE" method takes the following parameters:

  • index: What to search for. This is a number or a string.
  • stopAtChildren: This specifies if the search by the name attribute should stop after examining the object's children or not. By default it's true.
  • showWarnings: If this is true, then if no matching DocumentElement is found for the index, a message is displayed in the Frontal console to aid debugging. By default it's true.
  • includeNonContainers: If this parameter is false, then the index is tested to see if it's a valid 1-based index of the object's "containers" property. If it's true, then the "elements" property is used. By default it's false.

The "getElementById" method takes these parameters:

  • id: The id to search for. It is a string.
  • showWarnings: Similar to the same parameter for the gE method.

The "destroy" method has no parameters.

There are also methods to get and set attributes and styles. We'll introduce these later, but for now we'll note that the method "gA" returns the value of an attribute. This will be useful in our examples.


Finding Objects and Elements in the Document

With these properties and methods, it's straightforward to find DocumentElement objects in both an absolute, and a relative way. For example, let's work with this document definition:

<div id="div1">
    <manager id="mgr1" />
    <div id="div2" name="a" />
    <div id="div3" name="b" />
    <div id="div4" name="c" />
    <div id="div5">
        <div id="div6" name="a" />
        <div id="div7" name="b" />
        <div id="div8" name="c" />
    </div>
    <div id="a" />
</div>
<script><![CDATA[
    document.write ( gE ( "div1" ).gA ( "id" ) ); // Writes "div1."
]]></script>

The 'script' tag is a DocumentElement object, and so has the properties and methods mentioned above. So let's parse the line of script it contains. First, it's requesting the 'document' property, which returns an object that has a method 'write' on it -- this allows us to append to the document definition. Then, as the argument to this write method, we're calling gE ( "div1" ). gE operates as follows:

  • 1. If the index argument is a valid 1-based index of this object's "containers" property, then return the indexed object.
  • 2. If the object's "elements" property has an object associated with an XML node with a "name" attribute equal to the index, then return that.
  • 3. If the document definition has an XML node with an 'id' attribute equal to the index, then return that.

So in this case, gE ( "div1" ) ends up in step 3, returning the first div in our definition. We then call gA ( "id" ) on that object which returns the value of its 'id' attribute, "div1".

Now let's replace that line of script with this:

document.write ( gE ( "div1" ).gE ( "a" ).gA ( "id" ) ); // Writes "div2."

Here, after we get "div1", we call gE ( "a" ) on it. This then runs the three steps above in the context of that div with the result being that step 2 returns the div with id "div2". This method of finding a div by name and in context can be very handy as it's often the case that there will be a common structure of elements used multiple times in a definition. That is, there may be many divs that have the same value for their name attribute, but in context, they're unique. Frontal's 'template' tag increases the likelihood of this occurrence. For example, Frontal uses templates to draw scrollbars (of which there may be several) in the document definition. It then uses this aspect of gE to locate each scrollbar's track element, button elements, and slider element.

To illustrate this, if we call gE ( "a" ) in the context of "div5" we get "div6". This is because among the children of "div5", it's "div6" that has the name "a".

document.write ( gE ( "document.write ( gE ( "div5" ).gE( "a" ).gA ( "id" ) ); // Writes "div6."

We could've also gotten to "div2" with this code:

document.write ( gE ( "div1" ).gE ( 1 ).gA ( "id" ) ); // Writes "div2."

This is because the first visual DocumentElement contained by "div1" is "div2". If we had gE include non-visual elements, then an index of 1 would return the 'manager' tag:

document.write ( gE ( "div1" ).gE ( 1, true, true, true ).gA ( "id" ) ); // Writes "mgr1."

Here are a few more examples to consider. Note that when using an array of containers or elements, the arrays are simply Flash AS3 arrays, and so their indexes are zero-based.

document.write ( gE ( "div1" ).containers [ 0 ].gA ( "id" ) ); // Writes "div2."
document.write ( gE ( "div1" ).elements [ 0 ].gA ( "id" ) ); // Writes "mgr1."
document.write ( gE ( "a" ).gA ( "id" ) ); // Writes "a."
document.write ( gE ( "div5" ).gE( "c" ).parent.parent.gA ( "id" ) ); // Writes "div1."

Via the "node" property, we can use some of the E4X functionality to access the document. (We can only use some because parts of the E4X syntax violates that of ECMA-262, which Frontal's scripting language is based on.)

document.write ( gE ( "div1" ).parent.node.name ( ) ); // Writes "http://frontalcode.com/::document."

See the next section for details.

var nodes = gE ( "div5" ).node.div; // Returns all div elements under "div5."
for ( var i = 0; i < nodes.length ( ); i++ ) document.write ( nodes [ i ].attribute ( "id" ) ); // Writes "div6," "div7" and "div8."


The Document Object and Element

We've already explored how XML is the descriptive language behind a Frontal document. Those familiar with XML though may have been concerned by something, and that's the lack of a document or root node in all of our examples. That is, for XML to be called "valid", it must contain exactly one element in which all other elements are contained. For HTML, this is the "html" element. Frontal has the same requirement, but in Frontal, it isn't explicitly written. In Frontal, whenever a new document is created to load and render your description, it's initialized with the following XML:

<document xmlns="http://frontalcode.com/" />

Then, all of the other elements are placed into this element. For example, Frontal uses a default style sheet to provide much of its built-in functionality. If this hasn't been explicitly disabled, as soon as the new document element is created, this style sheet is added as a "script" child element.

To see this basic document, first open the Workspace and submit an empty definition. Click the resulting blank page and then type ctrl-ctrl-ctrl-4-1-1 (where ctrl is the control key or the command key on a Mac and the dashes are just separators) to open up the Frontal console. (This only works if the Frontal console has not been disabled.) Then click "Command Line" and paste in the following script:

com.frontalcode.Debugger.msg ( com.frontalcode.Util.htmlEscape ( mgr.gD ( 1 ).node.toXMLString ( ) ) );

Click "go" and you'll see the XML for this empty document. (The XML might be easier to read if you right-click the text, choose select all and then copy-and-paste it into your favorite text editor.)

A few things to note about what we did. First, it should be clear that the Frontal console allows you to run a piece of code at runtime that interacts with your Frontal document. This can be very handy when trying to debug a problem. And second, we've used a few new methods and properties that we'll mention in passing:

  • com.frontalcode.Debugger.msg is a method to print a string in the Frontal console. It will always be prefixed with "uncategorized:", whereas system messages will always have a category.
  • com.frontalcode.Util.htmlEscape is a method to escape HTML. If we didn't use it here, then Flash would try to render the XML document in the log view and we wouldn't see anything.
  • gD is a method on the "mgr" object and is used to get a document by index or title. We use it here because the Frontal console isn't in the same document that we're interested in. It's in its own document.
  • toXMLString is a method of the Flash XML class.

Returning to the output of our command-line script, the first thing to note is that the result is contained entirely by a "document" element. (The "uncategorized:" label is not part of the document.) So we have our valid XML! The next thing to note is that the rest of our document is the Frontal default style sheet, as well as a few templates for progress indicators and scrollbars. (The astute reader may note that by putting these things in a style sheet, Frontal has given complete control to the user to alter the default style sheet in any way desired.)

We've already seen that the easiest way to get the object associated with the "document" element is to use the "document" property of a DocumentElement object. This "document" object extends that class with a few of its own methods that come in handy when accessing the document. (See the Details section for additional useful properties and methods.)

  • getNodeElement returns a DocumentElement object from an XML node in the document's definition.
  • write appends a valid XML snippet to the current definition.

The "getNodeElement" method takes the following parameters:

  • node: The XML element for which to find a DocumentElement object.

The "write" method takes the following parameters:

  • xmlString: A string description of a valid XML snippet (meaning it has one parent element and all tags are properly closed).
  • parent: The DocumentElement object in which to append the XML. By default this is the document object.
  • insertPoint: An XML node to control the insertion point of the XML. By default this is null, and the content will be appended to the end of the parent's children nodes.
  • insertAfter: If "insertPoint" is not null and this is true, then the XML will be inserted just after the insert point. If it's false, then it will be inserted just before the insert point.


Accessing Styles and Attributes of an Element

We've seen that a DocumentElement object's XML element is always available through the "node" property, so of course all of its attributes are available through the "node" property as well. However, we don't recommend using this technique to read and write attributes -- it circumvents much of the functionality that Frontal provides to, for example, re-render the document when a change is made that effects the visual presentation. Instead, the DocumentElement object provides the following methods:

  • attributeExists returns true if the attribute is set.
  • gA (or equivalently getAttribute) returns the value of an attribute.
  • sA (or equivalently setAttribute) sets the values of an attribute.
  • gS (or equivalently getStyle) gets the value of a style set in the style attribute.
  • sS (or equivalently setStyle) sets the value of a style in the style attribute.
  • getAttributes returns an array of attributes set on this object.
  • getNamespaces returns an array of the namespace URIs for which there exists an attribute.

The "attributeExists" method takes the following parameters:

  • name: The name of the attribute to check.
  • namespace: The namespace to check for the attribute. This may be null in which case the default namespace is checked. Otherwise it may be a Flash Namespace object or a string. If a string, an attempt will be made to find a matching namespace prefix. If none exists, then it will be used as a URI to create a Flash Namespace object. By default this is null, which is likely what you'll want.

The "gA" method takes the following parameters:

  • name: The name of the attribute to retrieve.
  • namespace: See the description of the same attributeExists parameter.

The "sA" method takes the following parameters:

  • name: The name of the attribute to set.
  • value: The value to set it to.
  • namespace: See the description of the same attributeExists parameter.

The "gS" method takes the following parameters. (Note that the 'style' attribute is only supported in the default namespace.)

  • style: The name of the style to retrieve.

The "sS" method takes the following parameters:

  • style: The name of the style to set.
  • value: The value to set it to.

The "getAttributes" method takes the following parameters:

  • namespace: See the description of the same attributeExists parameter.

The "getNamespaces" method takes no parameters.

Here then is an example of these APIs:

<style><![CDATA[
    .red { background-color: red; }
    .blue { background-color: blue; }
    div { width: 25%; height: 25%; }
]]></style>
<div id="div1" class="red" />
<div xmlns:test="http://frontalcode.com/test/" id="div2" class="red" test:class="Hello!" style="background-color: white;" />
<script><![CDATA[
    document.write ( gE ( "div1" ).attributeExists ( "notThere" ) ); // Writes "false."
 
    document.write ( gE ( "div2" ).gA ( "class" ) ); // Writes "red."
 
    // "div2" will render in blue at the next render pass.
    gE ( "div2" ).sA ( "class", "blue" );
 
    document.write ( gE ( "div2" ).gA ( "class" ) ); // Now writes "blue."
 
    // Writes the RGB value for white - the style until the class change above
    // takes effect.
    document.write ( " HERE " + gE ( "div2" ).gS ( "background-color" ) );
 
    gE ( "div2" ).sS ( "border-width", 5 ); // Give "div2" a 5 pixel black border.
 
    var attrs = gE ( "div2" ).getAttributes ( );
    // Writes "id" and "class."
    for ( var i = 0; i < attrs.length; i++ ) document.write ( "div1 attr " + i + ": " + attrs [ i ] );    
 
    attrs = gE ( "div2" ).getAttributes ( "test" );
    // Writes "class."
    for ( var i = 0; i < attrs.length; i++ ) document.write ( "div2 attr " + i + ": " + attrs [ i ] );
 
    var namespaces = gE ( "div2" ).getNamespaces ( );
    // Writes "" and "http://frontalcode.com/test/."
    for ( var i = 0; i < namespaces .length; i++ ) document.write ( "ns" + i + ": " + namespaces [ i ] );   
]]></script>


Personal tools
Get Adobe Flash player