Introduction
In this third installment of our series on developing HTML5 applications for AppUp, we’ll look at an example application that can read and display articles from an RSS web feed feed. It will illustrate some HTML5 features such as getting content from a web site and parsing XML.
The RSS Protocol
Our example application will read and display articles from servers that support the RSS protocol.
RSS originally stood for RDF Site Summary but is now often referred to as Really Simple Syndication. It is a standardized format for publishing frequently updated information such as news articles, forums, and blog entries. An RSS document or “feed” uses a standardized XML format. A program, often known as an RSS Reader, can subscribe to one or more RSS feeds and display the information to a user. The main advantage of RSS is that it allows a user to aggregate information from many different sites that they are interested in and view them from a single location.
As RSS files are essentially XML formatted plain text, the RSS file itself is relatively easy to read both by automated processes and by humans. An example file could have contents such as shown in Figure 1. This could be placed on any appropriate communication protocol for file retrieval, such as http or ftp, and reading software would use the information to present a display to the end user.
There are several different versions of RSS. To simplify our example we will only support version 2.0 as most sites are using that version.
<?xml version="1.0" encoding="UTF-8" ?> <rss version="2.0"> <channel> <title>RSS Title</title> <description>This is an example of an RSS feed</description> <link>http://www.someexamplerssdomain.com/main.html</link> <lastBuildDate>Mon, 06 Sep 2010 00:01:00 +0000 </lastBuildDate> <pubDate>Mon, 06 Sep 2009 16:45:00 +0000 </pubDate> <ttl>1800</ttl> <item> <title>Example entry</title> <description>Here is some text containing an interesting description.</description> <link>http://www.wikipedia.org/</link> <guid>unique string per item</guid> <pubDate>Mon, 06 Sep 2009 16:45:00 +0000 </pubDate> </item> </channel> </rss>
Figure 1: RSS Example Data
The Application
Figure 2: RSS Reader Main Screen
If you click on a link on an article (in this case a news headline), it will open a page with the full article. An example is shown in Figure 3 below.
Figure 3: RSS Reader Article View
Figure 4 shows the settings screen. It allows the user to change the URL for the RSS feed. Sites that offer RSS feeds typically publish these URLs. Here we’ve entered a different URL from the default. Clicking Save will go back to the main screen and the content will be updated to reflect the new RSS feed.
Figure 4: RSS Reader Settings View
How It Works
Let’s look at how our application works. All of the source files described in this article can be downloaded from here.
First, to satisfy Encapsulator we need an application icon file named icon.png
.
We will use a couple of style sheets, app.css
and content.css
. These will set some of the application’s look and feel, such as the background image and button appearance, and will be referenced in the html files that use them.
Now let’s look at the starting point, the main HTML file index.html
, shown in Listing 1. This file loads the style sheet, app.css
and the JavaScript file index.js
. It then creates the six clickable images for Home, Back, Forward, Refresh, Settings and Close, and associates them with the JavaScript functions to be executed when they are clicked. Finally, it creates an iframe whose content comes from the file content.html
.
<!DOCTYPE HTML> <html> <head> <title>HTML5 RSS Reader</title> <link href="app.css" rel="stylesheet" type="text/css"/> <script src="index.js" type="text/javascript"></script> </head> <body> <nav> <ul class="navigation"> <li><a href="#" onclick="home()"><img src="images/home.png" title="Home"/></a></li> <li><a href="#" onclick="back()"><img src="images/back.png" title="Back"/></a></li> <li><a href="#" onclick="forward()"><img src="images/forward.png" title="Forward"/></a></li> <li><a href="#" onclick="reload()"><img src="images/refresh.png" title="Refresh"/></a></li> <li><a href="#" onclick="settings()"><img src="images/applications.png" title="Settings"/></a></li> <li><a href="#" onclick="closeApplication()"><img src="images/close.png" title="Close"/></a></li> </ul> </nav> <iframe id="frameId" src="qrc:/content.html" width="100%" height="90%"></iframe> </body> </html>
Listing 2: File content.html
Let’s look at function onFrameLoad()
in rss.js
, shown in Listing 3. If the variable xmlhttp
is null, we call getXmlHttpObject() which creates an instance of an XMLHttpRequest() object. Then we call getRSS() with the path to the URL for the RSS feed site.
<!DOCTYPE HTML> <html> <head> <title>RSS Viewer</title> <link href="content.css" rel="stylesheet" type="text/css"/> <script src="rss.js" type="text/javascript"></script> </head> <body id="bodyId" onload="onFrameLoad()"> </body> </html>
Listing 3: Function onFrameLoad from file rss.js
Examining function getRSS()
in the same file, shown in Listing 4, it sets the function getXmlDoc
to be called when it’s state changes, and then set up an http GET request for the URL.
function onFrameLoad() { if (xmlhttp == null) xmlhttp = getXmlHttpObject() getRSS(top.rss_path) }
Listing 4: Function getRSS from file rss.js
When the XML HTTP request is completed, the function getXmlDoc
is called. Looking at the code for this function (Listing 5), if the status is correct, it calls parseRSS()
with the retrieved XML content. If the status is not correct, we display an error message to the user. The most likely cause for an error here is that we are not running the application from the Encapsulator wrapper program.
function getXmlDoc() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 200) { var xmlDoc = xmlhttp.responseXML parseRSS(xmlDoc) } else { body = document.getElementById('bodyId') if (xmlhttp.statusText != '') body.innerHTML = '<div><strong>Error: </strong>' + xmlhttp.statusText + '</div>' else body.innerHTML = '<div><strong>Error: </strong>Are you trying to run this example outside of Encapsulator?</div>' } } }
Listing 5: Function getXmlDoc from file rss.js
Function parseRSS()
(Listing 6) is the last one to describe in this file and does most of the work. We first check that the returned XML is RSS version 2.0. If not, we report an error message.
We then get a list of the title, description, and publish dates for all the items in the XML document and generate an HTML-formatted list of the information which is set as the body of the HTML document. Finally, if get any errors we catch them and display an error message.
function parseRSS(xmlDoc) { try { if (!xmlDoc) throw 'Error during load of RSS' var rss = xmlDoc.getElementsByTagName('rss') if (rss && rss[0].getAttribute('version') != '2.0') { body = document.getElementById('bodyId') body.innerHTML = '<div><strong>Content is not RSS version 2.0</strong></div>' return } var channel = xmlDoc.getElementsByTagName('channel') if (channel && channel.length > 0) { var channelNode = channel.item(0) var childs = channelNode.childNodes var html = '' for (var i = 0; i < childs.length; i++) { var node = childs.item(i) var top_title var top_link if (node.localName == 'title') { top_title = xmlDoc.getElementsByTagName('title')[0].textContent } else if (node.localName == 'description') { } else if (node.localName == 'link') { top_link = xmlDoc.getElementsByTagName('link')[0].textContent } else if (node.localName == 'language') { } else if (node.localName == 'item') { body = document.getElementById('bodyId') var items = xmlDoc.getElementsByTagName('item') if (top_title && top_title != '') { html += '<div>' html += '<a href="' + top_link.textContent +'">' html += '<h1><strong>' html += top_title html += '</strong></h1>' html += '</a>' html += '</div>' } html += '<ol>' for (i = 0; i < items.length; i++) { node = items.item(i) var title = node.getElementsByTagName('title')[0] var description = node.getElementsByTagName('description')[0] var link = node.getElementsByTagName('link')[0] var pubDate = node.getElementsByTagName('pubDate')[0] var guid = node.getElementsByTagName('guid')[0] html += '<li>' html += '<a href="' + node.getElementsByTagName('link')[0].textContent +'">' html += '<strong id="title">' + node.getElementsByTagName('title')[0].textContent + '</strong>' html += '</a>' html += '<br>' html += '<blockquote>' + node.getElementsByTagName('description')[0].textContent + '</blockquote>' html += '<div>' html += '<small id="right">Publication date: ' + pubDate.textContent + '</small>' html += '</div><br><br></li>' } html += '</ol>' body.innerHTML = html break } } } } catch(err) { body = document.getElementById('bodyId') body.innerHTML = '<div><strong>' + err + '</strong></div>' } }
Listing 6: Function parseRSS from file rss.js
File index.js
(Listing 7) has the JavaScript functions that are called by the buttons at the top of the user interface. Each function is pretty much trivial and calls one line of code. There is also some initialization done here which is called at initial loading.
top.default_path = "http://rss.cnn.com/rss/cnn_topstories.rss" top.rss_path = top.default_path function start() { window.homeLocation = getFrame().contentDocument.location.href } window.onload = start function closeApplication() { intel.adp.encapsulator.closeapplication() } function getFrame() { return document.getElementById('frameId') } function back() { window.history.back() } function forward() { window.history.forward() } function home() { getFrame().contentDocument.location.assign(window.homeLocation) } function reload() { getFrame().contentDocument.location.reload() } function settings() { getFrame().src = 'qrc:/settings.html' }
Listing 7: File index.js
When the user clicks on the Settings button it loads the file settings.html
shown in Listing 8. This page has an input field for the RSS URL and buttons for Cancel, Save, and Clear.
<!DOCTYPE HTML> <html> <head> <title>HTML5 RSS Settings</title> <link href="content.css" rel="stylesheet" type="text/css"/> <script type="text/javascript" src="settings.js"></script> </head> <body id="bodyId" onload="onBodyLoad()"> <div class="search"> <div class= "left-input"> <div class= "right-input"> <div class= "fill-input"> <input id="rssUrlId" type="text" class="box" /> </div></div></div> </div> <a class="button small white" href="#" onclick="cancelSettings()">Cancel</a> <a class="button small white" href="#" onclick="saveSettings()">Save</a> <a class="button small white" href="#" onclick="clearSettings()">Clear</a> </body> </html>
Listing 8: File settings.html
The JavaScript file settings.js
(Listing 9) implements the logic for the settings. It handles saving the settings, cancelling changes, or clearing the text field.
function saveSettings() { top.rss_path = document.getElementById('rssUrlId').value top.document.getElementById('frameId').src = 'qrc:/content.html' } function cancelSettings() { top.document.getElementById('frameId').src = 'qrc:/content.html' } function clearSettings() { var input = document.getElementById('rssUrlId') input.value = '' input.size = 10 } function onBodyLoad() { var input = document.getElementById('rssUrlId') input.onkeypress = input.onkeydown = function() { this.size = ( this.value.length > 10 ) ? (this.value.length > 150 ? 150: this.value.length) : 10; }; input.value = top.rss_path input.size = input.value.length }
Source Intel AppUp(SM) Developer Program