/*************************************************************************
*
* Copyright 2008 FlashMobileBlog
* All Rights Reserved.
*
**************************************************************************/
import mobile.core.ArrayDataProvider;
import mobile.events.DataProviderEvent;
import mobile.events.Event;
/**
* This is the RSS data provider class for retrieving RSS articles and parsing them for display with a FLIP
* list component.
*
* The RSSDataProvider class is to be used with components, both UI components and other
* ActionScript classes.
* It responds to data requests and dispatches events to notify of data changes.
* This base class allows for only reading of array data. You can extend this class to provide
* different functionality to interact with other data services.
*
*
* @category Class
* @langversion 2.0
* @playerversion Lite 2.1
*/
class mobile.data.RSSDataProvider extends ArrayDataProvider
{
/**
* @private
*
* The RSS XML Data
*/
private var xData:XML;
/**
* @private
*
* A fake object to allow DataProvider functions to run in the context
* of a request to improve memory and CPU efficiency
*/
private var owner:Object;
/**
* Class Constructor.
*
* @category Constructor
* @productversion FLIP 1.0
* @langversion 2.0
* @playerversion Lite 2.1
*
*/
function RSSDataProvider()
{
super();
}
/**
*
*
* Open the RSSDataProvider list using the parameters provided.
*
* If the RSSDataProvider was previously initialized, then all outstanding
* requests will be cancelled and deleted.
* When the list has completed its first stage of opening and initialization, this event is dispatched.
* The list's Id is available once the open event has been received.
*
* @param filter An array of RSS Feed urls, other data may be required for the service depending on the provider.
* @param sortOrder The sortOrder of the returned list, implemented for example purposes only.
* @param returnFields A bit field of items to maintain for the user interface, for example purposes only.
*
* @see DataRequest
*
* @category Method
* @langversion 2.0
* @playerversion Lite 2.1
*/
public function open(feed:String, sortOrder:Number, returnFields:Number):Void
{
//Set defaults
xData = null;
sortOrder = 0; // Default behaviour
returnFields = -1; // -1 sets all bits to 1
xData = new XML();
xData.ignoreWhite = true;
xData["owner"] = this;
xData.onLoad = onXMLHandler;
xData.load(feed);
}
/**
* Receives calls to fetch the items in the range specified. This function is a redefinition
* of the base class method to encompass asynchronous list creation.. If the list is open then
* processing is immediate.
*
* When the items have been fetched, the DataProviderEvent.GET_ITEMS event
* is dispatched and contains the data. Each call overwrites the previous call for items in cases where
* the list is not open yet.
*
*
* @param startIndex The first index in the range to fetch
* @param endIndex The last index in the range to fetch
*
*
*
* @category Method
* @langversion 2.0
* @playerversion Lite 2.1
*/
public function getItems(startIndex:Number, endIndex:Number):Void
{
if(this.dataArr.length)
{
doGetItems(startIndex, endIndex);
}
else
{
//Record the latest request for items until ready
owner.startIndex = startIndex;
owner.endIndex = endIndex;
}
}
/**
* Fetches the items in the range specified. This function is a redefinition
* of the base class method to work asynchronously.
*
* When the items have been fetched, the DataProviderEvent.GET_ITEMS event
* is dispatched and contains the data.
*
*
* @param startIndex The first index in the range to fetch
* @param endIndex The last index in the range to fetch
*
*
*
* @category Method
* @langversion 2.0
* @playerversion Lite 2.1
*/
private function doGetItems(startIndex:Number, endIndex:Number):Void
{
var dataArrLen:Number = dataArr.length;
startIndex = Math.max(0, Math.min(dataArrLen - 1 , startIndex));
endIndex = Math.max(startIndex, Math.min(dataArrLen - 1, endIndex));
var event:DataProviderEvent = new DataProviderEvent(
DataProviderEvent.GET_ITEMS, false, false,
startIndex, endIndex, dataArrLen,
dataArr.slice(startIndex, endIndex+1)
);
event.status = (dataArr.length != 0) ? DataProviderEvent.SUCCESS : DataProviderEvent.INDEX_OUT_OF_RANGE;
dispatchEvent(event);
}
/**
* Handles the list events sent through onLoad.
*
* This method is run in the context of the XML object, not the DeviceDataProvider class.
*
*
* @category Method
* @langversion 2.0
* @playerversion Lite 2.1
*/
public function onXMLHandler():Void
{
owner.handleXML(this);
}
/**
* @private
*
* Scope has been regained from the asychronous load, now process the XML.
*
* Once parsing of the XML is complete the DataProviderEvent.GET_ITEMS event
* is dispatched.
*
*
* @category Method
* @langversion 2.0
* @playerversion Lite 2.1
*/
private function handleXML(xml:XML):Void
{
if(xml != undefined)
{
parseEntries();
xData = null;
dispatchEvent(new Event("open"));
if(owner.startIndex != undefined && owner.endIndex != undefined)
{
doGetItems(owner.startIndex, owner.endIndex);
}
}
}
/**
* @private
*
* Parse the RSSDataProvider list entries stored in xData.
*
* Each item will be stored in the entries array from the parent ArrayDataProvider object.
*
*
*
* @category Method
* @langversion 2.0
* @playerversion Lite 2.1
*/
private function parseEntries():Void
{
var nodes:Array = xData.firstChild.firstChild.childNodes;
var len:Number = nodes.length;
var node:XMLNode;
var entries:Array = [];
while(--len)
{
node = nodes[len];
var nodeName:String = nodes[len].nodeName;
if(nodeName == "item")
{
entries.push(parseEntry(node));
}
}
addItems(entries);
}
/**
* @private
*
* Retrieves the data from each of the entries passed to it by parseEntries.
*
* Each item will be stored in the entries array from the parent ArrayDataProvider object.
*
*
* @param node A node contains an individual ATOM item
*
* @return An object containing each item detail
*
* @category Method
* @langversion 2.0
* @playerversion Lite 2.1
*/
private function parseEntry(node:XMLNode):Object
{
if(node == undefined)
{
return null;
}
var entryElements:Array = node.childNodes;
var elementLen:Number = entryElements.length;
var entry:Object = new Object();
var entryElement:XMLNode;
while(elementLen--)
{
entryElement = entryElements[elementLen];
switch(entryElement.nodeName)
{
case "title":
{
entry.label = entryElement.firstChild.nodeValue;
break;
}
case "description":
{
entry.desc = entryElement.firstChild.nodeValue;
break;
}
case "media:thumbnail":
{
entry.img = [];
entry.icon = entryElement.attributes.url;
entry.img.ht = entryElement.attributes.height;
entry.img.wt = entryElement.attributes.width;
break;
}
case "link":
{
entry.link = entryElement.firstChild.nodeValue;
break;
}
case "pubDate":
{
entry.pubDate = entryElement.firstChild.nodeValue;
break;
}
default:
{
//pass this to a parse default function
break;
}
}
}
return entry;
}
}