RSS Browser


Our application has two basic screens. One shows a selection of URLs, each pointing to an RSS file, and allows the user to choose one (or supply a new URL). The other screen opens the URL and examines the channel that it returns.

URL Choice Screen

First we construct a little MovieClip called a ChannelPick (Figure 18.3). It is used to display, edit, and choose a single URL. It presents the variable url to the user as an editable line of text. Of course, url is specific to the namespace of this MovieClip instance, so a screenful of ChannelPick instances will not interfere with one another.

Figure 18.3. ChannelPick: Button and Editable URL

graphics/18fig03.jpg

ChannelPick also has a simple button ( Go to ) that triggers the select() function. This copies the local value of url to the variable channel one level up and then runs the animation on that level.

The _parent MovieClip has two states. Initially it displays 16 ChannelPick instances and assigns a different URL to each url (Figure 18.4). (We supply a broad assortment of RSS publishers.) The action stops and waits for one of the channels to be picked by the viewer. The second state displays the chosen channel. It displays the channel variable (a URL), a back button, and an instance of the ChannelView MovieClip. It passes the URL to the getChannel method in that instance.

Figure 18.4. Seventeen ChannelPick Choices

graphics/18fig04.jpg

Note that the second state is on the third frame, not the second. If you were to try to call a function within a MovieClip on the same frame in which you first instantiate it, the Flash player would not find the function. So here we wait a frame to allow the instance of ChannelView to realize itself before we try to address its internals (Figure 18.5).

Figure 18.5. Channel Viewing State

graphics/18fig05.jpg

This frame also includes a back button that returns us to the first frame. It is not accidental that this causes the ChannelView instance to go out of scope. We want to destroy the instance and release all its data structures to the garbage collector. This is a cheap and easy way to do so. Each time we view a channel, it will be with a fresh instance of ChannelView.

ChannelView

This object has three simple display-only dynamic text fields. The fields present the title of the name of the channel, the description it supplies of itself and a status display that shows the progress (or problems) of the XML exchange.

We establish our proxy-based send and load as an extension to the definition of the XML object. (The actual function body code has been examined earlier.)

ActionScript
 XML.prototype.sendAndLoadRemote = function ( url, xmlResponse ) { . . . 

The function getChannel grabs the XML from the remote server. It sets up the status dis play and then calls new XML() twice to establish two fresh, new XML objects ( xmlRequest and xmlResponse ). It is often cleaner to use a single object for both purposes, but here clarity is more important than efficiency.

ActionScript
 function getChannel( url ) {   statusline="WAITING for XML";   xmlRequest = new XML("");   xmlResponse = new XML(""); 

After setting up the status display, the getChannel creates two new empty XML objects. It prepares xmlResponse to receive data, then exchanges an empty xmlRequest for an xmlResponse from the server.

ActionScript
 xmlResponse.onload = showMe;   xmlResponse.findElements = findElements;   xmlResponse.target = this;   xmlResponse.sendAndLoadRemote( url, xmlRequest ); } 

As in Chapter 14, we add two new methods and a property to the xmlResponse object, which will contain the downloaded data. The showMe function is registered as the onLoad callback. An introspective method called findElements is installed. The target property hooks the XML object back to its mother MovieClip and allows it to address the functions and variables that directly affect the display. The keyword this , in the penultimate line of the code, refers to this instance of the ChannelView MovieClip, not this instance of the XML object. A bit tricky perhaps, but bear in mind that these lines are MovieClip code ”they are building an XML object from the outside, not executing within the XML object.

Another point to consider is that we could have extended the XML object with submit and findElements, as we did with sendAndLoadRemote, by writing it into the XML prototype. This is often more efficient than duplicating the code in many instances. But not here. Although we have a few XML objects, only one needs an onLoad function or the findElements method. These are specific to the incoming RSS file, and it is more appropriate to install them, as we have, in this single instance than as a general XML property.

ActionScript
 function showMe( ok ) {   statusline= ok? this.toString() : "Server not found"+this.toString();   this.findElements();} } 

This onLoad handler is quite simple. It shows a text version of the incoming XML in the background. (If there is an error in the link between Flash and the proxy, showMe prints its own error message.) Then it launches the findElements() introspector.

ActionScript
 function findElements() {     if( this.nodeName eq "channel" ) this.target.rssChannel(  this)     if( this.nodeName eq "item"    ) this.target.rssItem(     this)     for( var i=0; i<this.childNodes.length; i++)     if( this.childNodes[i].nodeType ==1 ){        this.childNodes[i].target      = this.target;        this.childNodes[i].findElements= this.findElements;        this.childNodes[i].findElements();        } } 

There are only two elements that findElements can recognize, the <channel> and the <item> . The relationship between them is pretty fixed (the file has a single channel, the channel contains 0 to 15 items) and so is their position in the XML file. But it is still easier and more robust to write a generic findElements() function that recursively scans the element nodes of the entire tree.

When it finds either a channel or an item, it calls the appropriate function back in the MovieClip instance.

ActionScript
 function rssChannel( element  ) {   for( var i=0; i< element.childNodes.length; i++)       if(      element.childNodes[i].nodeType== 1)          if(      element.childNodes[i].nodeName== "title")               this.title = element.childNodes[i].childNodes[0].nodeValue;          else if( element.childNodes[i].nodeName== "description")               this.text  = element.childNodes[i].childNodes[0].nodeValue; } 

The channel decoding function has an easy job. It looks through all the child nodes of the channel, and it ignores everything except <title> and <description> nodes. We are careful, in this case, not to use a recursive function, because titles and descriptions are also found in the items within the channel. We want only the channel's own title and description. When the title or the description is found in an element node, we (somewhat boldly) assume that the node immediately below this one will be the matching CDATA node (the text). We assign that text to variables that are displayed on this screen.

An <item> element requires a bit more effort. For each one we find we spawn a new instance of the ItemView MovieClip. Each of these instances requires a (locally) unique name, a globally unique depth, and an x and y position:

 function rssItem( element  ){     this.attachMovie( "ItemView", this.i, 1500- ++this.i);     clip= eval( this.target + this.i );     clip._x= this.xchild;     clip._y= this.ychild;     if( (this.ychild+=20) >160) { this.ychild=0;this.xchild+=150;}     clip.item= element; } 

A simple counter this.i provides both the name and the depth (notice that we want them to stack visually behind one another, so we use negative i. Note also that in an expression like ++this.i or 1500+this.i the + is an arithmetic operator and this.i is a number. However in this._target + this.i the same symbol is the concatenation operator and this.i is a string .

Depth, x, y, and name are counted off by variables in this , which makes them local to the ChannelVie w. Thus the variables persist across calls to rssItem and the ItemView objects stack nicely and are named neatly. The variables are reset when ChannelView is destroyed and recreated (when a new channel is being viewed ).

The clip variable is just a temporary variable within this function that we use to address the properties and variables of the new ItemView. The final line sets one of these variables, item, to be a copy of the XML description of the item. This pushes the decoding of XML data even deeper into the application. In fact, the ItemView contains the most interesting implementation of RSS functionality.

The ItemView has only three parts : a title, a description, and a link. We build these into a single object that shows only the title. When the mouse rolls over it, the description is presented. And when the mouse is clicked, the link is opened (in a new window).

This sounds like a fancy button, and the easiest way to implement it is by attaching a button (which provides nice predefined states) to a MovieClip that provides the unique namespace we require (Figure 18.6). (Since multiple items are viewed at once, all the buttons would share namespace and be indistinguishable.) The button is not complicated. It consists of text and flat backgrounds for the text. Different variables are displayed in the three states. The variables for the text are title and description. These (and the mouse action) are defined in the ItemView MovieScript that wraps around the button (Figure 18.7).

Figure 18.6. The ItemView Displays as

graphics/18fig06.jpg

Figure 18.7. The Item Acts as a Hyperlink

graphics/18fig07.jpg

On frame 1 nothing happens. If we were to try to access the data in this MovieClip on the first frame of its existence, we would be disappointed. The data is placed there by the parent object, but this instance is created (and its initial frame is executed) before the data assignment happens. They happen on the same frame, but not simultaneously .

On frame 2, we have all the muscle:

ActionScript
 function setVariable( node ) {    name= node.nodeName;    value= "";    for( var i=0; i< node.childNodes.length; i++)       if(      node.childNodes[i].nodeType== 1)         setVariable( node.childNodes[i] );       else if( node.childNodes[i].nodeType== 3)         value += node.childNodes[i].nodeValue;     eval( name ) = value; } 

This recursive function is used to set the title, description, and link variables. In fact, it will create any variables it finds. Whenever it finds a named element, it creates a variable of the same name and assigns to it the value of all the concatenated text nodes beneath it. If the RSS file is valid, this function will always fill in the title, description, and link that will make the button displays function.

 setVariable( item ); stop(); 

Now our little browser is ready to run (Figure 18.8). It needs to be in the same directory as our PHP proxy. That proxy needs to be named "xml-proxy.php" or whatever is called by the function XML.prototype.sendAndLoadRemote();

Figure 18.8. A Channel Displayed in the RSS Browser

graphics/18fig08.jpg

To work the browser, a user simply clicks on a channel that seems interesting. The items in that channel can be examined, and underlying pages can be browsed. Try the preprogrammed channels, or type in the URL of any RSS channel.



Flash and XML[c] A Developer[ap]s Guide
Flash and XML[c] A Developer[ap]s Guide
ISBN: 201729202
EAN: N/A
Year: 2005
Pages: 160

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