My Experimental Ajax FeedReader

Disclaimer: I’m not claiming either my code or design are especially original or high quality. I just have fun teaching myself this stuff and am writing about it for my own future memory. Having said that if you’d like a copy of the code and instructions for use as is and with no support, drop me a line and I may send it back.

The idea for this Ajax feedreader came to me last week at the Google Developer Day. My code, written in PHP, takes as input an OPML file (I used a file generated by Vienna’s export subscriptions function) and puts each folder of feeds into its own tab. Separate Google FeedReader controls are instantiated for each tab but not drawn until the specific tab is clicked using MooTool’s event handling; the tab labels come from the OPML folder names. The first tab is auto-loaded.

You need three key pieces to follow along:

  • MooTools, which I’m already using for the top of page slide in menu
  • SimpleTabs by Harald Kirschner, a MooTools add-on (I may also try and integrate his HistoryManager, but it isn’t in yet)
  • Google AJAX Feeds API

The first two are open source and must be downloaded and installed, to use the last you must sign up for a (free) API key.

Note that my code has some significant limitations:

  • I didn’t work out how to constrict the width of the tab menu to the containing div so the number of tabs was whittled down to fit the space.
  • Feeds must be inside a folder, since the top level elements in the OPML file are used as the tabs–this could be corrected by testing the outline element but not something my sense of neatness needs.
  • Folders within folders are not supported for the same reason as mentioned in the previous bullet.

Consider it a permanent beta.

PHP pseudo-code:


// My own CSS for the tabs and feedcontrol is after this pseudo-code block

// MooTools, SimpleTabs and Google Feeds JavaScript and CSS files
// must be included, e.g.:
$st = "<script type="text/javascript" src="http://www.google.com/jsapi?key=YOUR-GOOGLE-API-KEY"></script>
<script type="text/javascript" src="/includes/js/mootools.v1.1.js"></script>
<script type="text/javascript" src="/includes/js/simpletabs.js"></script>
<link rel="stylesheet" type="text/css" href="/includes/css/simpletabs.css" />n";
// $dynamicJS is the JavaScript result of the PHP code below,
// so it obviously must be run before before this line
$st .= $dynamicJS;

if(!$src = simplexml_load_file("your_sub_list.opml", "SimpleXMLElement", LIBXML_NOCDATA)) die('failed to load xml');

// $jsFeedControl holds the dynamically generated JavaScript
// $t holds the HTML text
$jsFeedControl = "<script type="text/javascript">
window.addEvent('domready', function() {
new SimpleTabs($('tab-block-1'), {entrySelector: 'h4'});n";
$t = "<div id="tab-block-1">n";

// $sections is the array of top-level elements, i.e., the folders
$sections = $src->xpath('body/outline');
$first = true;
foreach($sections as $sct) {
$sa = $sct->attributes();
// the tab's label
$ot = $sa['text'];
// name of the FeedControl for this tab
$fc = str_replace(" ", "", "fc".$ot);
// SimpleTabs converts the list of <h4> elements to the menu,
// with the subsequent div the place where the FeedControl will be drawn
$t .= "<h4><div id="".$fc."_id" class="stmenu">$ot</div></h4>n<div id="".$fc."_view"></div>n";
// JS to create a FeedControl
$js = "var ".$fc." = new google.feeds.FeedControl();n";
// $af is the array of feeds in the current folder
$af = $sct->xpath('outline');
foreach($af as $a) {
$a = $a->attributes();
// JS to add the feed to the FeedControl
$js .= $fc.".addFeed('".htmlspecialchars($a['xmlUrl'])."', '".htmlspecialchars($a['text'], ENT_QUOTES)."');n";
}
// this tab's click event handler JS
$js .= "$('".$fc."_id').addEvent('click', function() {
".$fc.".draw($('".$fc."_view'));
})n";
if($first) {
// auto-load the first tab
$first = false;
$fjs = $fc.".draw($('".$fc."_view'));n";
}
$jsFeedControl .= $js;
}
$t .= "</div>n";
$jsFeedControl .= $fjs."});n</script>n";

// related CSS, some of which is needed to counteract the styles
// set for the top of page slide-in section:
.stmenu {display: inline;}

.tab-wrapper .gfc-resultsHeader, .tab-wrapper .gfc-results, .tab-wrapper .gf-result .gf-snippet, .tab-wrapper .gfc-result .gf-result .gf-author, .tab-wrapper .gfc-result, .tab-wrapper .gf-result, .tab-wrapper .gf-result .gf-title {display: block; width: auto;}
.tab-wrapper .gfc-results {background-color: #ffffff; color: #000;}
.tab-wrapper .gfc-resultsHeader .gfc-title {font: 146% bold; color: #000; background-color: #DBDBF0; margin-bottom: 0; border: none; border-top: 1px dotted #003366;}
.tab-wrapper .gf-result .gf-title a {text-decoration: underline; margin-right: 10px; color: #0000ff;}
.tab-wrapper .gfc-result .gf-result .gf-spacer, .tab-wrapper .gfc-result .gf-result .gf-author {display: none;}