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 = "