Added menu option to show/hide messages table.
[ptas] / zouba / misc / rss / FeedUpdateBroker.js
1 ///////////////////////////////////////////////////////////////////////////////\r
2 // The FeedUpdateBroker class implements a simple RSS fetcher and parser.\r
3 \r
4 // Constructor.\r
5 function FeedUpdateBroker() {\r
6     this.httpReq = null;\r
7     this.callback = null;\r
8 }\r
9 \r
10 // Fetches a feed from the specified URL and calls the callback when the feed\r
11 // has been fetched and parsed, or if the process results in an error.\r
12 FeedUpdateBroker.prototype.fetchFeed = function(feedURL, callback) {\r
13     // remember callback\r
14     this.callback = callback;\r
15     \r
16     // create new XML HTTP request
17
18     //netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\r
19     this.httpReq = new Ajax();\r
20     \r
21     // set callback\r
22     var self = this;\r
23     this.httpReq.onreadystatechange = function() { self.readyStateChanged(); };\r
24 \r
25     // append the current time after the URL to bypass caches\r
26     var fullURL = feedURL;\r
27     if (fullURL.indexOf("?") == -1) {\r
28         fullURL += "?";\r
29     } else {\r
30         fullURL += "&";\r
31     }\r
32     fullURL += "nocache=" + (new Date().getTime());\r
33     \r
34     // initiate the request\r
35     this.httpReq.open("GET", fullURL, true);\r
36     this.httpReq.send(null);\r
37 }\r
38 \r
39 // Callback for ready-state change events in the XML HTTP request.\r
40 FeedUpdateBroker.prototype.readyStateChanged = function() {\r
41     // complete request?\r
42     if (this.httpReq.readyState == 4) {\r
43         // attempt to get response status\r
44         var responseStatus = null;\r
45         try {\r
46             responseStatus = this.httpReq.status;\r
47         } catch (noStatusException) {}\r
48         \r
49         // handle the response and call the registered callback\r
50         this.callback.call(this, this.handleResponse(responseStatus, this.httpReq.responseXML));\r
51     }\r
52 }\r
53 \r
54 // Handles a completed response.\r
55 FeedUpdateBroker.prototype.handleResponse = function(responseStatus, xmlDoc) {\r
56     if (((responseStatus == 200)||(responseStatus == 0)) && xmlDoc != null) {\r
57         // node ref for iterating\r
58         var node;\r
59         \r
60         // get last modified time - default to current time\r
61         var lastModified = new Date().getTime();\r
62         var channelElements = xmlDoc.getElementsByTagName("channel");\r
63         if (channelElements.length > 0) {\r
64             node = channelElements[0].firstChild;\r
65             while (node != null) {\r
66                 if (node.nodeType == Node.ELEMENT_NODE) {\r
67                     if (node.nodeName == "pubDate" ||\r
68                             node.nodeName == "lastBuildDate" ||\r
69                             node.nodeName == "dc:date") {\r
70                         lastModified = this.getTextOfNode(node);\r
71                         break;\r
72                     }\r
73                 }\r
74                 node = node.nextSibling;\r
75             }\r
76         }\r
77         \r
78         // init feed items array\r
79         var items = [];\r
80         \r
81         // we got the feed XML so now we'll parse it\r
82         var itemElements = xmlDoc.getElementsByTagName("item");\r
83         for (var i = 0; i < itemElements.length; i++) {\r
84             // iterate through child nodes of this item and gather\r
85             // all the data we need for a feed item\r
86             var title = null;\r
87             var date = null;\r
88             var description = null;\r
89             var url = null;\r
90             \r
91             node = itemElements[i].firstChild;\r
92             while (node != null) {\r
93                 if (node.nodeType == Node.ELEMENT_NODE) {\r
94                     if (node.nodeName == "title") {\r
95                         // item title\r
96                         title = this.getTextOfNode(node);\r
97                     } else if (node.nodeName == "pubDate" || node.nodeName == "dc:date") {\r
98                         // item publishing date\r
99                         date = this.getTextOfNode(node);\r
100                     } else if (node.nodeName == "description") {\r
101                         // item description\r
102                         description = this.getTextOfNode(node);\r
103                     } else if (node.nodeName == "link") {\r
104                         // link URL\r
105                         url = this.getTextOfNode(node);\r
106                     }\r
107                 }\r
108                 node = node.nextSibling;\r
109             }\r
110             \r
111             // create the item and add to the items array\r
112             items.push({ title: title, date: date, description: description, url: url });\r
113         }\r
114         \r
115         // update was completed successfully\r
116         return { status: "ok", lastModified: lastModified, items: items };\r
117     } else {\r
118         // update failed\r
119         return { status: "error" };\r
120     }\r
121 }\r
122 \r
123 // Returns the text of a node.\r
124 FeedUpdateBroker.prototype.getTextOfNode = function(node) {\r
125     var buf = "";\r
126     \r
127     // iterate through all child elements and collect all text to the buffer\r
128     var child = node.firstChild;\r
129     while (child != null) {\r
130         if (child.nodeType == Node.TEXT_NODE || child.nodeType == Node.CDATA_SECTION_NODE) {\r
131             // append text to buffer\r
132             if (buf != "") {\r
133                 buf += " ";\r
134             }\r
135             buf += child.nodeValue;\r
136         }\r
137         child = child.nextSibling;\r
138     }\r
139     \r
140     // strip all tags from the buffer\r
141     var strippedBuf = "";\r
142     var textStartPos = -1;\r
143     var tagBalance = 0;\r
144     \r
145     // iterate through the text and append all text to the stripped buffer\r
146     // that is at a tag balance of 0\r
147     for (pos = 0; pos < buf.length; pos++) {\r
148         var c = buf.charAt(pos);\r
149         if (c == '<') {\r
150             // entering a tag\r
151             if (tagBalance == 0 && textStartPos != -1) {\r
152                 // everything up to here was valid text\r
153                 strippedBuf += buf.substring(textStartPos, pos);\r
154                 textStartPos = -1;\r
155             }\r
156             tagBalance++;\r
157         } else if (c == '>') {\r
158             // leaving a tag\r
159             tagBalance--;\r
160             textStartPos = -1;\r
161         } else if (tagBalance == 0 && textStartPos == -1) {\r
162             // first char of text\r
163             textStartPos = pos;\r
164         }\r
165     }\r
166     \r
167     // add remaining text - if any\r
168     if (tagBalance == 0 && textStartPos != -1) {\r
169         strippedBuf += buf.substring(textStartPos, pos);\r
170     }\r
171     \r
172     return strippedBuf;\r
173 }\r