Rendering the GUI


The TRee object is the powerhouse in this component because it parses the response data that is received from the treeManager object and creates a tree structure that is later appended to an HTML element or the HTML document body by treeManager. In other words, this object pretty much does it all. The object is a prototype, which means it can be instantiated, and contains a constructor function. Listing 11.5 shows the code snippet for this constructor.

Listing 11.5. Constructing a TRee Object (tree.js)

 function Tree(data) {     this.data = data;     this.childArray = new Array();     this.tree = ''; } 

The tree object's constructor takes a data parameter, which is the response data from treeManager, and sets the data to a local property called data. It also creates a new array called childArray and a property called tree. The childArray array is used to store all the child ids when the structure is created, and the tree property is used to concatenate a string version of the tree in HTML format, which is eventually added to the innerHTML property of the parent div for rendering. This parent div element is called tree and is created in the next method, which we will call display. This method renders all the data into an HTML element. The method is called from treeManager after the tree object is created and populated to display the final tree view structure in the page. Listing 11.6 shows the display method code.

Listing 11.6. Displaying the Tree (tree.js)

[View full width]

 Tree.prototype.display = function() {      var tree = Utilities.createElement("div", {id: 'tree', innerHTML: this.traverse(this .data, 0) });      return tree; } 

This method creates the HTML div element called TRee, which we briefly discussed. This element simply acts as a container that holds the tree elements in its innerHTML property, including all of the nodes and node values. As you can see, we are calling the traverse method in the innerHTML property. This method recursively parses the XML structure and creates the tree view HTML, which is then added to this element.

The TRaverse method takes two parameters: a branch and a depth. The branch represents the node or category that is presently being parsed. For instance, with the current XML structure, we will start with the Mail branch or node as the first branch parameter, and then we will recursively dig deeper into the structure to parse the Inbox, Outbox, and Sent branches. The second parameter, which is the depth, represents the depth level of the tree. For instance, if we are parsing Mail, we are at a depth level of 0, but if we are parsing Inbox, we are at a depth level of 1, and so on. Coincidentally, the actual depth is not what is important here. Instead, it is that the depth be used as a unique id so that we can construct a reference id to each of the categories for later manipulation, such as expanding and collapsing a specific category. Listing 11.7 shows the method declaration and the parameters it receives.

Listing 11.7. Traversing the XML (tree.js)

 Tree.prototype.traverse = function(branch, depth) {} 

Now that we understand the parameters, we can start parsing the data. Since every browser handles data differently, the first thing we need to do when we parse the data is check that the nodeName is a true nodeName and not a text or CDATA reference. We do this by simply checking the first character's code to make sure it is not equal to 35, which would mean it is the # symbol, used to represent text and CDATA references. The following is an example of the code that performs this check (see Listing 11.8).

Listing 11.8. Checking for Folders (tree.js)

 if(branch.nodeName.charCodeAt(0) != 35) 

The next line of code is an example of why we need to keep a reference to the depth level of the tree. The id we construct may seem a bit convoluted, but it is necessary to keep a unique reference to each category (see Listing 11.9).

Listing 11.9. Constructing a Unique Id (tree.js)

 var id = branch.parentNode.nodeName+'_'+branch.nodeName+'_'+depth; 

The reason that such a convoluted id is necessary is so there can be duplicate names within the same tree. Keeping reference to the parent node name, the current node name, and the depth level guarantees us no duplicate ids. The next bit of code (see Listing 11.10) puts our childArray to use. This is where we start pushing the ids to the array. These ids will then be easy to reference later, such as with the toggle method we may choose to call from the treeManager.

Listing 11.10. Storing Ids (TRee.js)

 this.childArray.push(id); 

After we have created the ids and added them to the array, we can start creating the elements that will form our HTML string and represent the tree view in the GUI. This string will be concatenated into the tree property that we created in the constructor function. If a branch does not contain any childNodes, we will simply add an icon that represents an empty folder. If a branch does contain childNodes we will add a hyperlink that will contain our custom folder icon. This hyperlink will fire two methods: the toggle method from the Utilities object, and the toggleImage method from the treeManager object. These methods will toggle the visibility of the contents within a specified folder and toggle the icon image between an expanded and a collapsed folder image. We will pass the toggle method the id that we created because it represents the element that contains the contents of each folder and causes this content to show and hide as we expand and collapse. We will pass the toggleImage method an id that represents the id of the img element we will create on the next line. This id will be used to target the specific image and change the src property. Listing 11.11 shows the code that handles this functionality.

Listing 11.11. Adding Custom Icons and Defining Their Actions (TRee.js)

[View full width]

 if(branch.childNodes.length == 0) {     this.tree += "<img src='/books/1/87/1/html/2/img/folder_empty.gif' border='0' class='folder'>"; } else {     this.tree += "<a href='#' onclick=\"javascript:Utilities.toggle('"+ id +"');  TreeManager.toggleImage('expand_collapse_"+ id +"'); \" class='expand_collapse'  onfocus='javascript:if(this.blur) this.blur();'>";      this.tree += "<img src='/books/1/87/1/html/2/img/folder_o.gif' id='expand_collapse_"+ id +"' border='0'  class='folder'></a>"; } 

This code may be a bit hard to read as a string, but it is simply the HTML that represents the functionality we just covered. As you can see, this code represents the hyperlink with an embedded image so that when a user clicks the image, the JavaScript functions are triggered and perform their specified duties.

The next couple lines of code create the folder name and associate the action attribute we specified in the XML to the action that occurs when a user clicks a folder name. Since we added these actions as attributes, we need to access them through JavaScript's getAttribute method (see Listing 11.12). This method is simple to use because we call it and pass the attribute name as a parameter of the method. Here is the code for this functionality.

Listing 11.12. Parsing Folder Label Actions (tree.js)

 var action = branch.getAttribute("action"); 

Now that we have parsed the action value and set an action variable to the value, we can use it in our hyperlink for the folder name. Following is the code that creates the hyperlink and adds this action to the click event (see Listing 11.13).

Listing 11.13. Adding Folder Labels (TRee.js)

 this.tree += "<a href=\"#\" onclick=\"javascript:"+ action +"\" class='container'>"+ branch.nodeName +"</a>"; 

As you can see, we have also added the nodeName of the branch as the text value in the hyperlink. This value will represent the name of the folder we specified in the XML at the beginning of this chapter. At this point, we have our custom folder icon with expand and collapse functionality, and the folder name that will render next to the folder icons for the first node in our XML. Now comes the harder partrecursively adding the values and the children of the tree. Before we start, you need to understand that we will be either adding node values to the current node (in other words, the contents of the folder), or we will be creating an entirely new category/ folder in the tree. The next piece of code will add an unordered list element with an embedded list item (see Listing 11.14). The embedded list item receives the id we created at the beginning of the method because this is the element that will contain the contents or node value for each folder or node. Within the embedded list, we will iterate through the branch's immediate children and recursively call the traverse method on each. The recursion will allow us to add the values or new folders to the tree. With each iteration, we need to increase the depth level by one to ensure that we are passing a unique depth id to the traverse method. After we finish adding the children, we can simply close the list item and the unordered list parent element.

Listing 11.14. Iterating Through the Children (TRee.js)

 this.tree += "<ul><li id='"+id+"'>"; for(var i=0; i<branch.childNodes.length; i++) {     this.traverse(branch.childNodes[i], depth);     depth++; } this.tree += "</li></ul>"; 

Now that we are recursively firing the traverse method, we need to identify what type of element to add to the tree. This element will either be a div element with the value for the folder, or a new folder element. We decipher this based on the first check we created in the method. This is the check that identifies whether the branch is a node or a text/CDATA value. If it is a node, we simply follow the same algorithm we just created; otherwise, we add the node value with the code in Listing 11.15.

Listing 11.15. Adding Folder Contents/Values (TRee.js)

 var value = branch.nodeValue; if(value != undefined) {     this.tree += "<div class='value'>"+ value +"</div>"; } return; 

This code parses the node value from the branch and creates a value variable. If this variable is not undefined, we add it to the div that holds the content values for each folder and append it to the tree string. After we have run out of node names, we simply return the TRee property and add it to the innerHTML of the tree element that will be appended to either a specific div in the HTML page or, as in our case, the document body.

The last method in this object is the toggle method. In our example, the toggle method (see Listing 11.16) can be used by the treeManager object to expand/collapse the entire tree view, but it also can be used by any other object in the application to accomplish the same goal.

Listing 11.16. Toggling the Tree View State (tree.js)

 Tree.prototype.toggle = function() {     for(var i=0; i<this.childArray.length; i++)     {        Utilities.toggle(this.childArray[i]);        TreeManager.toggleImage("expand_collapse_"+this.childArray[i]);     } } 

Adding Style to the Component

In order to stylize our component, we will create a CSS (Cascading Style Sheets) file called tree.css. This file will contain element ids and classes that we created in the tree object when we created the HTML string that was added to the current document. This file is simple and can be customized to look the way you would like, including link colors, spacing, and so on. Listing 11.17 shows this entire file.

Listing 11.17. Styling the Component (tree.css)

 body {     font-family: Verdana, Arial, Helvetica, sans-serif;     font-size: 11px; } .container {     color: #333333;     font-weight: bold;     line-height: 21px;     text-decoration: none; } ul {     margin: 2px 0px 5px 20px;     list-style-type: none;     padding: 0px; } li {     padding: 2px 0px 0px 0px; } .expand_collapse {     float: left;     font-size: 9px;     color: #333333;     text-decoration: none; } .folder {     float: left;     margin: 0px 0px 0px 0px; } .value a {     color: #999999;     font-weight: normal;     padding-left: 15px; } 

This file is fairly self-explanatory because each class and id is represented in the JavaScript file. If you are looking to create a new look for this tree, I would suggest modifying the colors and decoration for links, and replacing the folder image icons with any image you would like. This tree view component can be used to represent a lot of different data types andwith a little tweakingcan easily be used to represent other XML-based structures, such as RSS and podcasts.



Ajax for Web Application Developers
Ajax for Web Application Developers
ISBN: 0672329123
EAN: 2147483647
Year: 2007
Pages: 129
Authors: Kris Hadlock

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net