Add Real Application Features
In the first part We already created a simple application that can be use to get started. In the first part also we have learned how the XUL event and the UI interact. It is no different than creating a web page and use JavaScript for UI events.
In this post I am not going to talk all the details, I will only show and describe some highlights for the application.
Added Features
- Add subscription(improved)
- Remove subscription
Highlights
- using jQuery for ajax request
- add feed entry to listbox
- show feed content in the browser
- add listener to web progress
- add status bar
- when to use jQuery selector and when not
This is the latest UI for our Feed Reader application.

Add Subscription Improved!
Show feed name in the tree instead of feed URL. Below snippet show the code that used for adding the feed to the tree.
function addFeedToList(theUrl,data){
var title = $(data).find("rss > channel > title").text();
var treeNode = document.getElementById(subscriptionNodeId);
var item = document.createElement("treeitem");
var row = document.createElement("treerow");
var cell = document.createElement("treecell");
cell.setAttribute("label",title);
cell.setAttribute("value",theUrl);
row.appendChild(cell);
item.appendChild(row);
treeNode.appendChild(item);
setAppStatus("Feed from "+theUrl+" added");
}
theUrl variable contains the feed URL whereas data variable contains RSS XML text. After we parse the data, we can obtain the feed title and assign it to the treecell label attribute and the feed URL to the treecell value attribute, such as in the following code.
var cell = document.createElement("treecell");
cell.setAttribute("label",title);
cell.setAttribute("value",theUrl);
now we can see the feed title instead of feed URL.
Remove Subscription
When removing a subscription, basically user need to select the feed first than choose the “Remove Subscription” menu. Here are the snippet of the function that does it.
function onRemoveSubscription(event){
var tree = document.getElementById(feedTreeId);
if(tree.currentIndex == -1 || tree.currentIndex == 0){alert("Please select a subscription to remove.");return;}
var currentItem = tree.view.getItemAtIndex(tree.currentIndex);
var cellIndex = 0;
var subscriptionName = tree.view.getCellText(tree.currentIndex, tree.columns.getColumnAt(cellIndex));
var ans = confirm("Are you sure to unsubscribe from \""+subscriptionName+"\" ?");
if(ans){
currentItem.parentNode.removeChild(currentItem);
}
}
The main code that remove the subscription is this.
currentItem.parentNode.removeChild(currentItem);
Now we already have some of the basic features Add and Remove subscription to/from our application.
Highlights
After I describe the high level features and how to achieve it, now for some details regarding those features.
Using jQuery for AJAX request, Why not using XUL built-in AJAX system
I am using jQuery when comes to AJAX request because I am familiar with it and also I have tried the built-ind XUL’s AJAX system and it does not work. Maybe I have missed something obvious here. But I need a running prototype fast and I do not have time to search why the built-in XUL’s AJAX does not work.
There has been a question out there similar to what I have encountered.
So we already familiar with the jQuery code to send an AJAX request such as this one
function readNewFeedFrom(theUrl){
if(!theUrl){return;}
jQuery.support.cors = true;
$.ajax({
url: theUrl,
contentType:"text/xml",
success: function(data){
addFeedToList(theUrl,data);
showFeedItem(data);
setAppStatus("Feed from: "+theUrl+" added.");
},
error:function(jqXHR, textStatus, errorThrown){
alert(textStatus);
},
beforeSend:function(jqXHR, settings){
setAppStatus("Adding feed from: "+theUrl);
}
});
}
Basically it is quite simple but very IMPORTANT. But one thing to remember that when using jQuery especially on the AJAX request part you need to activate the cross-domain scripting flag on jQuery, it showed the following line.
jQuery.support.cors = true;
If this flag set to false the AJAX request will not work and the error handler on the AJAX request will be executed.
Add Feed Entry to Listbox
<listbox id="feed-item" onclick="onFeedItemSelected()"></listbox>
JavaScript code
function showFeedItem(data){
var feedItemList = $("#"+feedItemId);
clearFeedItemList(feedItemId);
$(data).find("rss > channel > item").each(function(idx){
var title = $(this).find("title").text();
var link = $(this).find("link").text();
var item = document.createElement("listitem");
item.setAttribute("label",title);
item.setAttribute("value",link);
feedItemList.append(item);
});
}
As you can see I have used the jQuery to read the feed XML and iterate all the item tags.
var feedItemList = $("#"+feedItemId);
var item = document.createElement("listitem");
item.setAttribute("label",title);
item.setAttribute("value",link);
feedItemList.append(item);
In the code above it is very easy to add an item to the listbox, just append the new item to it.
Show feed content in the browser
At this point we already manage to add new subscription and show its item in a listbox, the last thing to do is to display
the feed item contents in a browser. Luckily in XUL there is already a browser component.
The following snippet shows how to display the feed item content in the browser.
function onFeedItemSelected(event){
var feedItem = document.getElementById(feedItemId);
var currentItem = feedItem.currentItem;
var url = currentItem.getAttribute("value");
var title = currentItem.getAttribute("label");
var browser = document.getElementById(feedItemViewId);
browser.setAttribute("src",url);
}
After you set the src attribute of the browser component it will start to render the URL.
We need to know what is being loaded and rendered by the browser we need to “listen” to this process progress.
Add listener to web progress
This part has more code to show because it a bit tricky. First we need to create a listener for the process.
function configureProgressListener(){
var obj = new Object();
obj.wpl = Components.interfaces.nsIWebProgressListener;
obj.QueryInterface = function(aIID) {
if (aIID.equals(obj.wpl) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
aIID.equals(Components.interfaces.nsISupports)) return this;
throw Components.results.NS_NOINTERFACE;
}
obj.onStateChange = function(aProgress, aRequest, aFlag, aStatus) {
if (aFlag & listObj.wpl.STATE_START) {
setAppStatus("Loading entry "+aRequest.name+"...");
} else {
if (aFlag & listObj.wpl.STATE_STOP) {
if ( aFlag & listObj.wpl.STATE_IS_WINDOW ) {
// This fires when ALL load finish
setAppStatus("Entry loaded, well almost.");
}
if ( aFlag & listObj.wpl.STATE_IS_NETWORK ) {
// Fires when ALL load are REALLY over,
// do something "final" here
// (my two cents)
setAppStatus("Entry loaded.");
}
}
}
return 0;
}
return obj;
}
The actual listener class is the nslWebProgressListener which is attached to plain JavaScript object obj.
var obj = new Object();
obj.wpl = Components.interfaces.nsIWebProgressListener;
The original snippet is here
We need to override the onStateChange function in order to “listen” to the process’s progress.
Now need to find a perfect place to show the progress to the user. Here comes the statusbar.
Add status bar
The status bar will shows the resource being loaded and rendered on the browser.
The XUL
<statusbar>
<statusbarpanel label="" id="app-status" flex="1"/>
</statusbar>
The JavaScript
function setAppStatus(status){
$("#"+appStatusId).attr("label",status);
}
When to use jQuery selector and when not
To avoid confusion when to use or not to use the jQuery selector I will try explain.
Do use jQuery when you only need
- To manipulate DOM common operation such as setting an attribute or appending child node
- To shorten the code instead using
document.getElementById("tag_id") instead you can use $("#tag_id")
Example:
$("#app-status").attr("label",status);
Do not user jQuery when you need
- To call XUL specific method on a XUL tag such as:
Example:
This will work
var tree = document.getElementById(feedTreeId);
var currentItem = tree.view.getItemAtIndex(tree.currentIndex);
This will not work
var tree = $("#"+feedTreeId);
var currentItem = tree.view.getItemAtIndex(tree.currentIndex);
Because when you get the DOM object using jQuery style it will resulting a jQuery object and the methods you are trying to call are not available. So be careful. Alright its a wrap, but I know that there are some improvements can be made. I really expect your feedback about the tutorial
, because I am still learning to and I hoping that I can learn together with you.
You can download the code here
Next feature is to persist the feed we already save in the Feed Reader and load it in start up. Cheers