import mx.utils.Delegate; import mx.controls.TextArea; /* This is an ActionScript2 class to load, process and format XML documents in the rss 1 and 2 format. Author: Steve Nelson sn@thegoldenmean.com January 2005 To work around Flash's security restrictions the URL of any feed is passed to a PHP proxy script. */ class ProcessRSS { //declare public properties: public var target_txt:Object; public var container:XML; public var proxyURL:String; //declare private properties: private var _xml:XML; private var items:Array; //the address of the PHP proxy script private var senderObj:LoadVars; private var loaderID:Number; //constructor function ProcessRSS (target:Object, proxy:String, xmlContainer:XML) { target_txt = target; container = xmlContainer; proxyURL = proxy; _xml = new XML(); _xml.ignoreWhite = true; _xml.onLoad = Delegate.create(this, onLoadEvent); } public function onLoadEvent(success:Boolean):Void { if(success) { //terminate any running intervals clearInterval(loaderID); //declare a variable to hold results of parsing method var feedContent:String = getContent(_xml, "item"); /* trace("onLoadEvent: "+_xml); trace("*******************************************************************"); trace("onLoadEvent: "+typeof(_xml)); trace("*******************************************************************"); trace("feedContent: "+feedContent); trace("*******************************************************************");*/ target_txt.xmlRSS = _xml; target_txt.parseXMLRss(); //assign value of feedContent to TextArea component //target_txt.text = "

Click a headline to open that entry in a new window.


"+feedContent; } else { target_txt.text = "

XML failed to load.

" } } /* Because different version of RSS organize their content differently, we need a method that will examine the whole document looking for matches to the node we are interested in, regardless of where in the document it may be located. Fortunately, all versions of RSS define "item" nodes, and within those nodes are "title", "link" and "description" nodes like this (though not necessarily in this order): text text text (Note that within the node there can be many other child nodes. For my project I am only interested in the three mentioned above. Feel free to explore the others if you wish.) We need something that will find all "item" nodes regardless of where in the document they may be located, and from within those nodes find the sub-nodes we want and extract the text data. This primary function examines the entire XML node tree recursivly, seeking a match for a node name ("item" in this case). When it finds a match, it calls a secondary method (getNodeText) on that node three times to extract the text data of the three subnodes we are interested in. It adds formatting tags to the extracted data and returns a complex string which is sent to the TextArea component all at once instead of incrementally. Put another way, the getContent method increases in granularity: the "outside" function sifts the entire document recursively for instances of a specified node name, while the "inside" function does an even finer sift of those nodes for sepcific child nodes. The parsing is thus done in one pass instead of adding elements to an array for subsequent processing. When the functions conclude, the local variables are "garbage collected", leaving no redundant residue to tie up system resources. */ private function getContent (node:XMLNode, name:String):String { //initialize String variable var content:String = ""; //start at the top of the document var c:XMLNode = node.firstChild; while (c) { //text nodes are of type 3 - don't waste cycles looking at them if (c.nodeType != 3) { //check for a match to the node we want if (c.nodeName == name) { //if it matches, get the data from the subnodes by passing //the current node's childNodes (which is an array) to the //"getNodeText()" method var itemTitle:String = getNodeText(c.childNodes, "title"); var itemLink:String = getNodeText(c.childNodes, "link"); var itemDescr:String = getNodeText(c.childNodes, "description"); //add formatting and update the variable that will get //passed to the TextArea component content += ""+itemTitle+"

"+ itemDescr+"

"; } //here's the recursive bit: //call getContent on the current node content += getContent(c, name); } //examine the next node if there is one c = c.nextSibling; } //send the final string back to the method that called it return content; } /* This utility method is used to extract the text content from the items in an array (in the case of this project, that array would be the childNodes of each element found by the getNode method). Using a for-in loop, it searches the node seeking a match for a search term and, when it finds a match, returns the text content as a string. This method is called from inside of the getContent() method. */ private function getNodeText (child:Array, searchTerm:String):String { for (var i in child) { if (child[i].nodeName == searchTerm) { return child[i].firstChild.nodeValue; } } } private function loadFeed(feedURL:String):Void { //terminate any running intervals clearInterval(loaderID); target_txt.text = ""; //reset scroll position to top target_txt.vPosition = 0; senderObj = new LoadVars(); //assign a value to a property of the LoadVars Object senderObj.rss = feedURL; /* The LoadVars.sendAndLoad method conveniently accepts an XML Object as its target. We can send a string (url) to the PHP proxy script and get an XML document back. */ senderObj.sendAndLoad(proxyURL, _xml, "GET"); /* Use setInterval to monitor load progress every 25 milliseconds. Pass the XML document whose download progress you wish to monitor as the 4th argument. Use of "this" in "this.loaderID" is critical for telling the loadingFeedback() method in what scope to find the XML object. Thanks to Colin Moock for the code this is based loosely on. */ this.loaderID = setInterval(this, "loadingFeedback", 25, _xml); } //the progress monitor called by setInterval private function loadingFeedback(xmlObj:XML):Void { var amtLoaded:Number = xmlObj.getBytesLoaded(); if (amtLoaded <= 4){ target_txt.text = "

Requesting Data...

"; } else { target_txt.text = "

Loaded: "+ Math.floor(amtLoaded/1024)+ " kilobytes

"; } } }