<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="900" height="650" creationComplete="init();" xmlns:ui="com.adobe.arise.ui.*" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
import com.adobe.xml.syndication.generic.IItem;
import mx.events.CloseEvent;
import mx.controls.CheckBox;
import mx.formatters.DateFormatter;
import mx.events.ListEvent;
import com.adobe.xml.syndication.generic.IItem;
import flash.data.SQLConnection;
import flash.data.SQLStatement;
import flash.data.SQLResult;
import flash.events.SQLEvent;
import flash.filesystem.*;
import com.adobe.xml.syndication.generic.*;
import mx.collections.ArrayCollection;
import com.adobe.arise.aggregator.*;
import com.adobe.arise.database.*;
import mx.controls.Alert;
import com.adobe.arise.ui.Info;
private var agg:Aggregator;
private var conn:SQLConnection;
private var sql:XML;
private var dateFormatter:DateFormatter;
private var timer:Timer;
[Bindable] private var topics:ArrayCollection;
[Bindable] private var authors:ArrayCollection;
[Bindable] private var feeds:ArrayCollection;
[Bindable] private var posts:ArrayCollection;
[Bindable] private var views:ArrayCollection;
[Bindable]
[Embed(source="assets/face-smile.png")]
private var smileIcon:Class;
[Bindable]
[Embed(source="assets/dialog-error.png")]
private var errorIcon:Class;
[Bindable]
[Embed(source="assets/emblem-important.png")]
private var warningIcon:Class;
[Bindable]
[Embed(source="assets/unread.png")]
public var unreadIcon:Class;
[Bindable]
[Embed(source="assets/read.png")]
public var readIcon:Class;
private function init():void
{
topics = new ArrayCollection();
authors = new ArrayCollection();
feeds = new ArrayCollection();
posts = new ArrayCollection();
views = new ArrayCollection();
dateFormatter = new DateFormatter();
dateFormatter.formatString = "EEE MMM D at L:NN A";
agg = new Aggregator();
var sqlFile:File = File.applicationResourceDirectory.resolvePath("sql.xml");
var sqlFileStream:FileStream = new FileStream();
sqlFileStream.open(sqlFile, FileMode.READ);
sql = new XML(sqlFileStream.readUTFBytes(sqlFileStream.bytesAvailable));
sqlFileStream.close();
views.addItemAt({"viewName":"All Unread", "count":0}, 0);
views.addItemAt({"viewName":"Checked", "count":0}, 1);
initializeDatabase();
flash.net.registerClassAlias("flash.geom.Rectangle", flash.geom.Rectangle);
flash.net.registerClassAlias("flash.geom.Point", flash.geom.Point);
}
private function start():void
{
refreshUI(true);
}
private function savePreferences(e:Event):void
{
var prefs:Object = new Object();
prefs.bounds = this.bounds;
var prefsFile:File = File.applicationStorageDirectory.resolvePath("prefs.obj");
var fs:FileStream = new FileStream();
fs.open(prefsFile, FileMode.WRITE);
fs.writeObject(prefs);
fs.close();
}
private function restorePreferences():void
{
var prefsFile:File = File.applicationStorageDirectory.resolvePath("prefs.obj");
if (prefsFile.exists)
{
var fs:FileStream = new FileStream();
fs.open(prefsFile, FileMode.READ);
var prefs:Object = fs.readObject();
fs.close();
this.bounds = Rectangle(prefs.bounds);
}
else {
}
this.visible = true;
}
/**
* Aggregates every hour.
*/
private function resetTimer():void
{
if (this.timer == null)
{
timer = new Timer(3600000, 0); timer.addEventListener(TimerEvent.TIMER, onRefreshAll);
}
timer.stop();
timer.start();
}
private function addFeed():void
{
if (newFeedUrl.text.length == 0) return;
newFeedDrawer.close();
aggregateFeeds([newFeedUrl.text]);
}
private function onRefreshAll(e:Event = null):void
{
var feedUrls:Array = new Array();
for each (var feed:Object in feeds)
{
feedUrls.push(feed.url);
}
aggregateFeeds(feedUrls);
resetTimer();
}
private function importFeeds():void
{
var opmlFile:File = File.desktopDirectory;
opmlFile.addEventListener(Event.SELECT,
function(e:Event):void
{
var fs:FileStream = new FileStream();
fs.open(opmlFile, FileMode.READ);
var opmlStr:String = fs.readUTFBytes(fs.bytesAvailable);
fs.close();
var opml:XML;
try
{
opml = new XML(opmlStr);
}
catch (e:Error)
{
trace("Invalid OPML");
return;
}
var xmlUrls:ArrayCollection = new ArrayCollection();
for each (var xmlUrl:String in opml..@xmlUrl)
{
if (!xmlUrls.contains(xmlUrl))
{
xmlUrls.addItem(xmlUrl);
}
}
aggregateFeeds(xmlUrls.source);
});
opmlFile.browseForOpen("Select an OPML file to import.");
}
private function aggregateFeeds(feedUrls:Array):void
{
if (feedUrls.length == 0)
{
refreshUI();
return;
}
var feedUrl:String = feedUrls.pop();
showStatus("Aggregating " + feedUrl);
var aggResponder:AggregatorResponder = new AggregatorResponder();
aggResponder.addEventListener(AggregatorEvent.FEED_EVENT,
function(e:AggregatorEvent):void
{
var feed:IFeed = e.data as IFeed;
if (!validateFeed(feedUrl, feed))
{
aggregateFeeds(feedUrls);
return;
}
var responder:DatabaseResponder = getDatabaseResponder();
var aggregateFeed:AggregateFeed = new AggregateFeed(responder, sql, conn, feedUrl, feed);
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function (e:DatabaseEvent):void
{
aggregateFeeds(feedUrls);
});
aggregateFeed.execute();
});
aggResponder.addEventListener(AggregatorEvent.ERROR_EVENT,
function(e:AggregatorEvent):void
{
trace("aggregation failed for: " + feedUrl);
trace("error is: " + e.data);
aggregateFeeds(feedUrls);
});
agg.getFeed(aggResponder, feedUrl);
}
private function validateFeed(feedUrl:String, feed:IFeed):Boolean
{
if (feed.metadata.title == null)
{
trace("The feed ["+feedUrl+"] is invalid. Missing title.");
return false;
}
return true;
}
private function refreshUI(initialRefresh:Boolean = false):void
{
feeds.removeAll();
authors.removeAll();
topics.removeAll();
hideStatus();
var feedResponder:DatabaseResponder = getDatabaseResponder();
feedResponder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(feedEvent:DatabaseEvent):void
{
feeds.source = feedEvent.data as Array;
countUnreadFeeds();
if (initialRefresh)
{
onRefreshAll();
}
});
var feedDBO:Feeds = new Feeds(feedResponder, sql, conn);
feedDBO.getAll();
var authorResponder:DatabaseResponder = getDatabaseResponder();
authorResponder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(authorEvent:DatabaseEvent):void
{
authors.source = authorEvent.data as Array;
countUnreadAuthors();
});
var authorDBO:Authors = new Authors(authorResponder, sql, conn);
authorDBO.getUnique();
var topicResponder:DatabaseResponder = getDatabaseResponder();
topicResponder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(topicEvent:DatabaseEvent):void
{
topics.source = topicEvent.data as Array;
countUnreadTopics();
});
var topicDBO:Topics = new Topics(topicResponder, sql, conn);
topicDBO.getUnique();
countUnreadAll();
countChecked();
}
private function showStatus(msg:String):void
{
status = msg;
}
private function hideStatus():void
{
status = "";
}
private function refreshCounts():void
{
this.countUnreadAll();
this.countUnreadAuthors();
this.countUnreadFeeds();
this.countUnreadTopics();
this.countChecked();
}
private function countUnreadTopics():void
{
var topicResponder:DatabaseResponder = getDatabaseResponder();
topicResponder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
var results:Object = e.data;
for each(var o:Object in topics)
{
if (results[o.topic] != null)
{
o.unread = results[o.topic];
}
else
{
o.unread = 0;
}
topics.itemUpdated(o, "unread");
}
});
var topicDBO:Topics = new Topics(topicResponder, sql, conn);
topicDBO.getUnread();
}
private function countUnreadAuthors():void
{
var authorResponder:DatabaseResponder = getDatabaseResponder();
authorResponder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
var results:Object = e.data;
for each(var o:Object in authors)
{
if (results[o.author] != null)
{
o.unread = results[o.author];
}
else
{
o.unread = 0;
}
authors.itemUpdated(o, "unread");
}
});
var authorDBO:Authors = new Authors(authorResponder, sql, conn);
authorDBO.getUnread();
}
private function countUnreadFeeds():void
{
var feedResponder:DatabaseResponder = getDatabaseResponder();
feedResponder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
var results:Object = e.data;
for each(var o:Object in feeds)
{
if (results[o.name] != null)
{
o.unread = results[o.name];
}
else
{
o.unread = 0;
}
feeds.itemUpdated(o, "unread");
}
});
var feedDBO:Feeds = new Feeds(feedResponder, sql, conn);
feedDBO.getUnread();
}
private function countChecked():void
{
var postResponder:DatabaseResponder = getDatabaseResponder();
postResponder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
for each(var o:Object in views)
{
if (o.viewName == "Checked")
{
o.count = e.data as int;
views.itemUpdated(o, "count");
break;
}
}
});
var postDBO:Posts = new Posts(postResponder, sql, conn);
postDBO.countChecked();
}
private function countUnreadAll():void
{
var postResponder:DatabaseResponder = getDatabaseResponder();
postResponder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
for each(var o:Object in views)
{
if (o.viewName == "All Unread")
{
o.count = e.data as int;
views.itemUpdated(o, "count");
break;
}
}
});
var postDBO:Posts = new Posts(postResponder, sql, conn);
postDBO.countUnread();
}
private function toggleSelection(e:Event):void
{
if (e.target != viewGrid) viewGrid.selectedItem = null;
if (e.target != topicGrid) topicGrid.selectedItem = null;
if (e.target != authorGrid) authorGrid.selectedItem = null;
if (e.target != feedGrid) feedGrid.selectedItem = null;
}
private function getPostsByTopic(e:ListEvent):void
{
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
posts.source = e.data as Array;
});
var postDBO:Posts = new Posts(responder, sql, conn);
postDBO.getByTopic(topicGrid.selectedItem.topic);
}
private function getPostsByAuthor(e:ListEvent):void
{
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
posts.source = e.data as Array;
});
var postDBO:Posts = new Posts(responder, sql, conn);
postDBO.getByAuthor(authorGrid.selectedItem.author);
}
private function getPostsByFeed(e:ListEvent):void
{
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
posts.source = e.data as Array;
});
var postDBO:Posts = new Posts(responder, sql, conn);
postDBO.getByFeed(feedGrid.selectedItem.id);
}
private function getPostsByView(e:ListEvent):void
{
var view:String = viewGrid.selectedItem.viewName as String;
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
posts.source = e.data as Array;
});
var postDBO:Posts = new Posts(responder, sql, conn);
if (view == "All Unread")
{
postDBO.getAllUnread();
}
else if (view == "Checked")
{
postDBO.getAllChecked();
}
}
private function getPostsBySearch():void
{
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
posts.source = e.data as Array;
});
var postDBO:Posts = new Posts(responder, sql, conn);
postDBO.getBySearch(searchTerm.text);
}
private function onPostSelected():void
{
var selectedPost:Object = postGrid.selectedItem;
if (selectedPost == null) return;
renderContent();
if (selectedPost.read == 0)
{
changeReadFlag(true, selectedPost);
}
setUnreadButtonLabel();
}
public function onPostChecked(cb:CheckBox, postId:Number):void
{
if (postGrid.selectedItem == null) return;
var selectedPost:Object = postGrid.selectedItem;
selectedPost.checked = cb.selected;
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
countChecked();
});
var postDBO:Posts = new Posts(responder, sql, conn);
postDBO.changeChecked(cb.selected, postId);
}
private function onDeleteFeed():void
{
if (feedGrid.selectedItem == null) return;
Alert.show("Are you sure you want to delete the selected feed and all it's posts?",
"Confirm",
Alert.OK|Alert.CANCEL,
null,
function(e:CloseEvent):void {
if (e.detail == Alert.OK)
{
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(ee:DatabaseEvent):void
{
refreshUI();
});
var deleteFeed:DeleteFeed = new DeleteFeed(responder, sql, conn, feedGrid.selectedItem.id);
deleteFeed.execute();
}
},
warningIcon);
}
private function toggleRead():void
{
var selectedPost:Object = postGrid.selectedItem;
if (selectedPost == null) return;
if (selectedPost.read == 0)
{
changeReadFlag(true, selectedPost);
}
else
{
changeReadFlag(false, selectedPost);
}
setUnreadButtonLabel();
}
private function setUnreadButtonLabel():void
{
if (postGrid.selectedItem == null) return;
unreadButton.label = (postGrid.selectedItem.read == 1) ? "Mark as Unread" : "Mark as Read";
}
private function markAllRead():void
{
if (posts.length == 0) return;
var unreadPosts:Array = new Array();
for each (var post:Object in posts)
{
if (post.read == 0)
{
unreadPosts.push(post);
}
}
if (unreadPosts.length > 0)
{
_markAllRead(unreadPosts);
}
}
private function _markAllRead(unreadPosts:Array):void
{
if (unreadPosts.length == 0)
{
refreshCounts();
setUnreadButtonLabel();
return;
}
var post:Object = unreadPosts.shift();
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
post.read = 1;
posts.itemUpdated(post, "read");
_markAllRead(unreadPosts);
});
var postDBO:Posts = new Posts(responder, sql, conn);
postDBO.changeReadFlag(true, post.id);
}
private function changeReadFlag(read:Boolean, post:Object):void
{
post.read = (read) ? 1 : 0;
posts.itemUpdated(post, "read");
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
refreshCounts();
});
var postDBO:Posts = new Posts(responder, sql, conn);
postDBO.changeReadFlag(read, post.id);
}
private function renderContent():void
{
if (postGrid.selectedItem == null) return;
if (contentTabs.selectedChild == summaryTab)
{
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
if (e.data == null) return;
var contentArray:Array = e.data as Array;
if (contentArray.length == 0) return;
summaryHTML.htmlText = (contentArray[0].content == null || contentArray[0].content == "null") ? wrapContent("(empty)") : wrapContent(contentArray[0].content) ;
});
var postDBO:Posts = new Posts(responder, sql, conn);
postDBO.getContentById(postGrid.selectedItem.id);
}
else if(contentTabs.selectedChild == siteTab)
{
if (siteHtml.location != postGrid.selectedItem.url)
{
this.showStatus("Loading " + postGrid.selectedItem.url);
siteHtml.location = postGrid.selectedItem.url;
}
}
}
private function loadUrl():void
{
if (locationBar.text.length == 0) return;
if (locationBar.text.search(/^http(s?):\/\/.+$/) == -1)
{
locationBar.text = ("http://" + locationBar.text);
}
this.showStatus("Loading " + locationBar.text);
siteHtml.location = locationBar.text;
}
private function onHtmlLoadComplete(e:Event):void
{
showStatus('Done');
backButton.enabled = (siteHtml.htmlControl.historyLength > 0 && siteHtml.htmlControl.historyPosition > 0) ? true : false ;
forwardButton.enabled = (siteHtml.htmlControl.historyLength > 0 && siteHtml.htmlControl.historyPosition != (siteHtml.htmlControl.historyLength - 1)) ? true : false ;
}
private function wrapContent(content:String):String
{
var str:String = "<html>";
str += "<div style='font-family: Arial'>";
str += content;
str += "</div></html>";
return str;
}
private function getDatabaseResponder():DatabaseResponder
{
var responder:DatabaseResponder = new DatabaseResponder();
responder.addEventListener(DatabaseEvent.ERROR_EVENT,
function(e:DatabaseEvent):void
{
Alert.show("An unexpected database error occurred. The error ID is: " + e.data,
"Database Error",
Alert.OK, null, null, errorIcon);
});
return responder;
}
private function initializeDatabase():void
{
conn = new SQLConnection();
conn.addEventListener(SQLEvent.OPEN,
function (e:SQLEvent):void
{
var responder:DatabaseResponder = getDatabaseResponder();
responder.addEventListener(DatabaseEvent.RESULT_EVENT,
function(e:DatabaseEvent):void
{
start();
});
var initDB:InitializeDatabase = new InitializeDatabase(responder, sql, conn);
initDB.execute();
});
var dbFile:File = File.applicationStorageDirectory.resolvePath("arise.db");
conn.open(dbFile, true, true);
}
public function formatDate(d:Date):String
{
return dateFormatter.format(d);
}
private function topicCompare(a:Object, b:Object):int
{
return caseInsensitiveCompare(a.topic, b.topic);
}
private function authorCompare(a:Object, b:Object):int
{
return caseInsensitiveCompare(a.author, b.author);
}
private function feedCompare(a:Object, b:Object):int
{
return caseInsensitiveCompare(a.name, b.name);
}
private function caseInsensitiveCompare(a:String, b:String):int
{
if (a.toLowerCase().localeCompare(b.toLowerCase()) > 0)
{
return 1;
}
else if(a.toLowerCase().localeCompare(b.toLowerCase()) < 0)
{
return -1;
}
else
{
return 0;
}
}
private function onInfo():void
{
new Info().open();
}
]]>
</mx:Script>
<mx:VBox width="100%" height="100%">
<mx:ApplicationControlBar width="100%">
<mx:Button label="Add Feed" click="newFeedUrl.text = ''; newFeedDrawer.open();"/>
<mx:Button label="Import Feeds" click="importFeeds();"/>
<mx:Button label="Refresh All" click="onRefreshAll();"/>
<mx:Button label="Mark as Unread" id="unreadButton" enabled="{postGrid.selectedItem != null}" click="toggleRead();"/>
<mx:Button label="Mark all as Read" enabled="{posts.length > 0}" click="markAllRead();"/>
<mx:Button label="Info" click="onInfo();"/>
<mx:Spacer width="100%" />
<mx:Label text="Search:" />
<mx:TextInput id="searchTerm" width="200" enabled="true" change="getPostsBySearch();"/>
</mx:ApplicationControlBar>
<mx:HDividedBox width="100%" height="100%">
<mx:VBox width="200" height="100%" verticalGap="0">
<mx:DataGrid id="viewGrid" dataProvider="{views}" width="100%" height="68" change="toggleSelection(event);" itemClick="getPostsByView(event);">
<mx:columns>
<mx:DataGridColumn headerText="View" dataField="viewName" textAlign="left"/>
<mx:DataGridColumn headerText="New" dataField="count" textAlign="right" width="48"/>
</mx:columns>
</mx:DataGrid>
<mx:Accordion width="100%" height="100%" creationPolicy="all">
<mx:Canvas label="Feeds" width="100%" height="100%">
<mx:VBox width="100%" height="100%" verticalGap="0">
<mx:DataGrid id="feedGrid" dataProvider="{feeds}" width="100%" height="100%" change="toggleSelection(event);" itemClick="getPostsByFeed(event);">
<mx:columns>
<mx:DataGridColumn headerText="Name" dataField="name" textAlign="left" sortCompareFunction="feedCompare"/>
<mx:DataGridColumn headerText="New" dataField="unread" textAlign="right" width="48"/>
</mx:columns>
</mx:DataGrid>
<mx:HBox width="100%" horizontalGap="0" paddingBottom="2" paddingTop="2" paddingLeft="2" paddingRight="2">
<mx:Button label="New" width="50%" click="newFeedUrl.text = ''; newFeedDrawer.open();"/>
<mx:Button label="Delete" width="50%" enabled="{feedGrid.selectedItem != null}" click="onDeleteFeed();"/>
</mx:HBox>
</mx:VBox>
</mx:Canvas>
<mx:Canvas label="Topics" width="100%" height="100%">
<mx:DataGrid id="topicGrid" dataProvider="{topics}" width="100%" height="100%" change="toggleSelection(event);" itemClick="getPostsByTopic(event);">
<mx:columns>
<mx:DataGridColumn headerText="Name" dataField="topic" textAlign="left" sortCompareFunction="topicCompare"/>
<mx:DataGridColumn headerText="New" dataField="unread" textAlign="right" width="48"/>
</mx:columns>
</mx:DataGrid>
</mx:Canvas>
<mx:Canvas label="Authors" width="100%" height="100%">
<mx:DataGrid id="authorGrid" dataProvider="{authors}" width="100%" height="100%" change="toggleSelection(event);" itemClick="getPostsByAuthor(event);">
<mx:columns>
<mx:DataGridColumn headerText="Name" dataField="author" textAlign="left" sortCompareFunction="authorCompare"/>
<mx:DataGridColumn headerText="New" dataField="unread" textAlign="right" width="48"/>
</mx:columns>
</mx:DataGrid>
</mx:Canvas>
</mx:Accordion>
</mx:VBox>
<mx:Canvas width="100%" height="100%">
<mx:VDividedBox width="100%" height="100%">
<mx:DataGrid id="postGrid" width="100%" height="30%" dataProvider="{posts}" draggableColumns="false" allowMultipleSelection="false" change="onPostSelected();" doubleClickEnabled="true" itemDoubleClick="if(postGrid.selectedItem != null) {navigateToURL(new URLRequest(postGrid.selectedItem.url))}">
<mx:columns>
<mx:DataGridColumn textAlign="center" width="30" dataField="checked">
<mx:headerRenderer>
<mx:Component>
<mx:Box verticalAlign="middle" horizontalAlign="center">
<mx:CheckBox enabled="false" labelPlacement="bottom"/>
</mx:Box>
</mx:Component>
</mx:headerRenderer>
<mx:itemRenderer>
<mx:Component>
<mx:CheckBox selected="{(data.checked) ? true : false}" click="outerDocument.onPostChecked(this, data.id)"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn width="22" dataField="read" textAlign="center">
<mx:headerRenderer>
<mx:Component>
<mx:Image source="{outerDocument.unreadIcon}"/>
</mx:Component>
</mx:headerRenderer>
<mx:itemRenderer>
<mx:Component>
<mx:Image source="{(data.read == 0) ? outerDocument.unreadIcon : outerDocument.readIcon}"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn headerText="Post Title" textAlign="left" dataField="title">
<mx:itemRenderer>
<mx:Component>
<mx:Label text="{data.title}" fontWeight="{(data.read == 0) ? 'bold' : 'normal'}"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn headerText="Feed" textAlign="left" width="170" dataField="feed_name">
<mx:itemRenderer>
<mx:Component>
<mx:Label text="{data.feed_name}" fontWeight="{(data.read == 0) ? 'bold' : 'normal'}"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn headerText="Date" textAlign="left" width="140" dataField="post_date">
<mx:itemRenderer>
<mx:Component>
<mx:Label text="{outerDocument.formatDate(data.post_date)}" fontWeight="{(data.read == 0) ? 'bold' : 'normal'}"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>
<mx:TabNavigator id="contentTabs" width="100%" height="70%" horizontalAlign="center" creationPolicy="all" change="renderContent();">
<mx:Canvas id="summaryTab" label="Post Summary" width="100%" height="100%">
<mx:HTML width="100%" height="100%" id="summaryHTML"/>
</mx:Canvas>
<mx:Canvas id="siteTab" label="Site" width="100%" height="100%">
<mx:VBox width="100%" height="100%">
<mx:HBox width="100%">
<mx:Button label="Back" click="siteHtml.htmlControl.historyBack()" enabled="false" id="backButton"/>
<mx:Button label="Forward" click="siteHtml.htmlControl.historyForward()" enabled="false" id="forwardButton"/>
<mx:TextInput width="90%" id="locationBar" text="{siteHtml.location}"/>
<mx:Button width="10%" label="Load" click="loadUrl();" enabled="{locationBar.text.length > 0}"/>
</mx:HBox>
<mx:HTML width="100%" height="100%" id="siteHtml" complete="onHtmlLoadComplete(event)"/>
</mx:VBox>
</mx:Canvas>
</mx:TabNavigator>
</mx:VDividedBox>
</mx:Canvas>
</mx:HDividedBox>
</mx:VBox>
<ui:TopDrawer id="newFeedDrawer" width="400" height="100">
<mx:VBox width="100%" height="100%" horizontalAlign="center" verticalAlign="middle" verticalGap="20">
<mx:Grid>
<mx:GridRow>
<mx:GridItem>
<mx:Label fontWeight="bold" text="Feed URL:"/>
</mx:GridItem>
<mx:GridItem>
<mx:TextInput id="newFeedUrl" width="300"/>
</mx:GridItem>
</mx:GridRow>
</mx:Grid>
<mx:HBox width="100%" horizontalAlign="center">
<mx:Button label="Add" click="addFeed();" enabled="{newFeedUrl.text.search(/^http(s?):\/\/.+$/) != -1}"/>
<mx:Button label="Cancel" click="newFeedDrawer.close();"/>
</mx:HBox>
</mx:VBox>
</ui:TopDrawer>
</mx:WindowedApplication>