Symbian.org/FeedUpdateBroker.js
changeset 0 54498df70f5d
child 3 9cbe91927e89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Symbian.org/FeedUpdateBroker.js	Fri Jun 05 16:18:05 2009 +0100
@@ -0,0 +1,220 @@
+///////////////////////////////////////////////////////////////////////////////
+// The FeedUpdateBroker class implements a simple RSS fetcher and parser.
+// Adapted from WRTKit RssReader example
+
+// Constructor.
+function FeedUpdateBroker() {
+    this.httpReq = null;
+    this.feedAddress = null;
+    this.callback = null;
+	this.ignoreContent = false;
+	this.cancelled = false;
+	this.responseParser = this.handleRssResponse;
+	this.startFromItem = 0;
+	this.maxItems = 0;
+}
+
+// Fetches a feed from the specified URL and calls the callback when the feed
+// has been fetched and parsed, or if the process results in an error.
+FeedUpdateBroker.prototype.doFetchFeed = function(){    
+    // create new XML HTTP request
+    this.httpReq = new Ajax();
+    
+    // set callback
+    var self = this;
+    this.httpReq.onreadystatechange = function() { self.readyStateChanged(); };
+
+	var fullURL = this.feedAddress;
+    if (fullURL.indexOf("?") == -1) {
+        fullURL += "?";
+    } else {
+        fullURL += "&";
+    }
+    fullURL += "nocache=" + (new Date().getTime());
+
+    // initiate the request
+    this.httpReq.open("GET", fullURL, true);
+    this.httpReq.send(null);
+}
+
+// has been fetched and parsed, or if the process results in an error.
+FeedUpdateBroker.prototype.fetchFeed = function(feedURL, callback) {
+    // remember callback
+    this.callback = callback;
+    this.feedAddress = feedURL;
+	this.doFetchFeed();
+}
+
+// Callback for ready-state change events in the XML HTTP request.
+FeedUpdateBroker.prototype.readyStateChanged = function() {
+    // complete request?
+    if (this.httpReq.readyState == 4) {
+        // attempt to get response status
+        var responseStatus = null;
+        try {
+            responseStatus = this.httpReq.status;
+        } catch (noStatusException) {}
+        
+		// are we being prompted for login?
+		var text = this.httpReq.responseText;
+		if ( isLoginPrompt (text) ) {
+			var self = this;
+			login(function(){self.doFetchFeed();});
+			return;
+		}
+		
+        // handle the response and call the registered callback object
+		var response = this.httpReq.responseXML;
+		if (response == null) {
+			// if the content type is not set correctly, we get the response as text
+			var xmlparser = new DOMParser();
+		    response = xmlparser.parseFromString(this.httpReq.responseText, "text/xml");
+		}
+        this.callback.feedUpdateCompleted(this.handleResponse(responseStatus, response));
+    }
+}
+
+// Handles a completed response.
+FeedUpdateBroker.prototype.handleResponse = function(responseStatus, xmlDoc){
+	if (this.responseParser == null) {
+		return this.handleRssResponse(responseStatus, xmlDoc);
+	}
+	else {
+		return this.responseParser.call(this, this, responseStatus, xmlDoc);
+	}	
+}
+
+
+FeedUpdateBroker.prototype.handleRssResponse = function(broker, responseStatus, xmlDoc){
+	if ( this.cancelled ) {
+        return { status: "cancelled" };
+	}
+    if (responseStatus == 200 && xmlDoc != null) {
+        // node ref for iterating
+        var node;
+        
+        // get last modified time - default to current time
+        var lastModified = new Date().getTime();
+        var channelElements = xmlDoc.getElementsByTagName("channel");
+        if (channelElements.length > 0) {
+            node = channelElements[0].firstChild;
+            while (node != null) {
+                if (node.nodeType == Node.ELEMENT_NODE) {
+                    if (node.nodeName == "pubDate" ||
+                            node.nodeName == "lastBuildDate" ||
+                            node.nodeName == "dc:date") {
+                        lastModified = getTextOfNode(node);
+                        break;
+                    }
+                }
+                node = node.nextSibling;
+            }
+        }
+        
+        // init feed items array
+        var items = [];
+        
+        // we got the feed XML so now we'll parse it
+        var itemElements = xmlDoc.getElementsByTagName("item");
+		
+        for (var i = this.startFromItem; i < itemElements.length; i++) {
+			if ( this.maxItems > 0 && this.maxItems < i ) {
+				break;
+			}
+            // iterate through child nodes of this item and gather
+            // all the data we need for a feed item
+            var title = null;
+            var date = null;
+            var description = null;
+            var url = null;
+            var author = null;
+            node = itemElements[i].firstChild;
+            while (node != null) {
+                if (node.nodeType == Node.ELEMENT_NODE) {
+                    if (node.nodeName == "title") {
+                        // item title
+                        title = getTextOfNode(node);
+                    } else if (node.nodeName == "pubDate" || node.nodeName == "dc:date") {
+                        // item publishing date
+                        date = getTextOfNode(node);
+                    } else if (node.nodeName == "description" && !this.ignoreContent ) {
+                        // item description
+                        description = getTextOfNode(node);
+                    } else if (node.nodeName == "link") {
+                        // link URL
+                        url = getTextOfNode(node);
+                    } else if (node.nodeName == "dc:creator" ) {
+						author = getTextOfNode(node);
+					}
+                }
+                node = node.nextSibling;
+            }
+            
+            // create the item and add to the items array
+            items.push({ title: title, date: date, description: description, url: url, author: author });
+        }
+        
+        // update was completed successfully
+        return { status: "ok", lastModified: lastModified, items: items };
+    } else {
+        // update failed
+        return { status: "error" };
+    }
+}
+
+// Returns the text of a node.
+function getTextOfNode(node) {
+    var buf = "";
+    
+    // iterate through all child elements and collect all text to the buffer
+    var child = node.firstChild;
+    while (child != null) {
+        if (child.nodeType == Node.TEXT_NODE || child.nodeType == Node.CDATA_SECTION_NODE) {
+            // append text to buffer
+            if (buf != "") {
+                buf += " ";
+            }
+            buf += child.nodeValue;
+        }
+        child = child.nextSibling;
+    }
+    
+    // strip all tags from the buffer
+    var strippedBuf = "";
+    var textStartPos = -1;
+    var tagBalance = 0;
+    
+    // iterate through the text and append all text to the stripped buffer
+    // that is at a tag balance of 0
+    for (pos = 0; pos < buf.length; pos++) {
+        var c = buf.charAt(pos);
+        if (c == '<') {
+            // entering a tag
+            if (tagBalance == 0 && textStartPos != -1) {
+                // everything up to here was valid text
+                strippedBuf += buf.substring(textStartPos, pos);
+                textStartPos = -1;
+            }
+            tagBalance++;
+        } else if (c == '>') {
+            // leaving a tag
+            tagBalance--;
+            textStartPos = -1;
+        } else if (tagBalance == 0 && textStartPos == -1) {
+            // first char of text
+            textStartPos = pos;
+        }
+    }
+    
+    // add remaining text - if any
+    if (tagBalance == 0 && textStartPos != -1) {
+        strippedBuf += buf.substring(textStartPos, pos);
+    }
+    
+    return strippedBuf;
+}
+
+FeedUpdateBroker.prototype.cancel = function() {
+	this.cancelled = true;
+	this.httpReq.abort();
+}