FeedUpdateBroker.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // Returns an xmlhttprequest object.
  2. function AjaxThing() {
  3. var xmlhttprequest = null;
  4. if(window.XMLHttpRequest) {
  5. try {
  6. xmlhttprequest = new XMLHttpRequest();
  7. }
  8. catch (e) {
  9. }
  10. }
  11. else if (window.ActiveXObject) {
  12. try {
  13. xmlhttprequest = new ActiveXObject("Msxml2.XMLHTTP");
  14. }
  15. catch (e) {
  16. try {
  17. xmlhttprequest = new ActiveXObject("Microsoft.XMLHTTP");
  18. }
  19. catch (e) {
  20. }
  21. }
  22. }
  23. return xmlhttprequest;
  24. }
  25. /*
  26. * The FeedUpdateBroker class implements a simple RSS fetcher and parser.
  27. *
  28. */
  29. // Constructor.
  30. function FeedUpdateBroker() {
  31. this.httpReq = null;
  32. this.callback = null;
  33. }
  34. // Fetches a feed from the specified URL and calls the callback when the feed
  35. // has been fetched and parsed, or if the process results in an error.
  36. FeedUpdateBroker.prototype.fetchFeed = function(feedURL,
  37. callback) {
  38. // remember callback
  39. this.callback = callback;
  40. // create new XML HTTP request
  41. this.httpReq = new AjaxThing();
  42. // set callback
  43. var self = this;
  44. this.httpReq.overrideMimeType('text/xml');
  45. this.httpReq.onreadystatechange = function() { self.readyStateChanged(); };
  46. // append the current time after the URL to bypass caches
  47. var fullURL = feedURL;
  48. if (fullURL.indexOf("?") == -1) {
  49. fullURL += "?";
  50. }
  51. else {
  52. fullURL += "&";
  53. }
  54. fullURL += "nocache=" + (new Date().getTime());
  55. // initiate the request
  56. this.httpReq.open("GET", fullURL, true);
  57. this.httpReq.send(null);
  58. };
  59. // Callback for ready-state change events in the XML HTTP request.
  60. FeedUpdateBroker.prototype.readyStateChanged = function() {
  61. // complete request?
  62. if(this.httpReq.readyState == 4) {
  63. // attempt to get response status
  64. var responseStatus = null;
  65. try {
  66. responseStatus = this.httpReq.status;
  67. }
  68. catch (noStatusException) {
  69. console.debug("FeedUpdateBroker.readyStateChanged: " +noStatusException);
  70. }
  71. // handle the response and call the registered callback
  72. this.callback.callback.call(this.callback.that,
  73. this.handleResponse(this.httpReq,
  74. responseStatus,
  75. this.httpReq.responseXML));
  76. }
  77. };
  78. // Handles a completed response.
  79. FeedUpdateBroker.prototype.handleResponse = function(response,
  80. responseStatus,
  81. xmlDoc) {
  82. console.debug("FeedUpdateBroker.handleResponse: " + response+ " " + xmlDoc);
  83. if (responseStatus == 200 && xmlDoc != null) {
  84. // Parse the
  85. var feedType = this.determineFeedType(xmlDoc);
  86. // node ref for iterating
  87. var node;
  88. // get last modified time - default to the zero
  89. var lastModified = null;
  90. // init feed items array
  91. var items = [];
  92. var descriptionTagName = 'description';
  93. var contentTagRegex = /content/;
  94. var feedTitle = this.determineFeedTitle(feedType, xmlDoc);
  95. var itemElements = xmlDoc.getElementsByTagName("item");
  96. var atomFeed = false;
  97. if(itemElements.length == 0) {
  98. itemElements = xmlDoc.getElementsByTagName("entry");
  99. descriptionTagName = 'summary';
  100. atomFeed = true;
  101. }
  102. /*var forTimes = 0;
  103. var whileTimes = 0;*/
  104. for(var i = 0; i < itemElements.length; i++) {
  105. /*forTimes++;
  106. alert("forTimes: " + forTimes + " whileTimes: " + whileTimes);*/
  107. // iterate through child nodes of this item and gather
  108. // all the data we need for a feed item
  109. var title = undefined;
  110. var date = undefined;
  111. var description = undefined;
  112. var url = undefined;
  113. var content = undefined;
  114. node = itemElements[i].firstChild;
  115. while(node != null) {
  116. /*whileTimes++;*/
  117. if(node.nodeType == Node.ELEMENT_NODE) {
  118. if(node.nodeName == "title") {
  119. // item title
  120. title = this.getTextOfNode(node);
  121. }
  122. else if(node.textContent != "" &&
  123. (node.nodeName == "pubDate" ||
  124. node.nodeName == "dc:date" ||
  125. node.nodeName == "updated")) {
  126. // item publishing date
  127. date = this.getDateOfNode(node);
  128. if(date) {
  129. if(!lastModified) {
  130. lastModified = date;
  131. }
  132. else if(date > lastModified) {
  133. lastModified = date;
  134. }
  135. }
  136. }
  137. else if(node.nodeName == descriptionTagName) {
  138. // item description
  139. description = this.getTextOfNode(node);
  140. }
  141. else if(node.nodeName.search(contentTagRegex) != -1) {
  142. content = this.getTextOfNode(node);
  143. }
  144. else if(node.nodeName == "link") {
  145. // link URL
  146. if(atomFeed) {
  147. url = node.getAttribute('href');
  148. }
  149. else {
  150. url = this.getTextOfNode(node);
  151. }
  152. }
  153. }
  154. node = node.nextSibling;
  155. }
  156. // create the item and add to the items array
  157. items.push({ title: title,
  158. date: date,
  159. description: description,
  160. content: content,
  161. url: url });
  162. }
  163. // update was completed successfully
  164. return { URL: xmlDoc.URL,
  165. status: "ok",
  166. title: feedTitle,
  167. lastModified: lastModified,
  168. items: items };
  169. }
  170. else {
  171. // update failed
  172. return { URL: xmlDoc.URL,
  173. status: "error" ,
  174. statusText: response.statusText};
  175. }
  176. };
  177. FeedUpdateBroker.prototype.determineFeedType = function(xmlDoc) {
  178. var feedType = "";
  179. if(xmlDoc.getElementsByTagName("rss").length > 0) {
  180. feedType = "rss";
  181. }
  182. else if(xmlDoc.getElementsByTagName("feed").length > 0) {
  183. feedType = "atom";
  184. }
  185. return feedType;
  186. };
  187. FeedUpdateBroker.prototype.determineFeedTitle = function(feedType,
  188. xmlDoc) {
  189. var feedTitle = "";
  190. if(feedType === "rss") {
  191. var titleElements = xmlDoc.getElementsByTagName("channel")[0]
  192. .getElementsByTagName("title");
  193. feedTitle = titleElements[0].textContent;
  194. }
  195. if(feedType === "atom") {
  196. var titleElements = xmlDoc.getElementsByTagName("feed")[0]
  197. .getElementsByTagName("title");
  198. feedTitle = titleElements[0].textContent;
  199. }
  200. return feedTitle;
  201. };
  202. // Returns the date as a Date object from a node representing a date
  203. FeedUpdateBroker.prototype.getDateOfNode = function(node) {
  204. var dateString = this.getTextOfNode(node);
  205. var parsedDate = new Date();
  206. // split the string '2009-01-01T22:00:00Z' first into the date and time strings
  207. var timeStamp = Date.parse(dateString);
  208. /** On the S60 device the timeStamp is something like 1.3^-36 so
  209. * timeStampe > 1 covers that as well. */
  210. if(!isNaN(timeStamp) &&
  211. timeStamp > 1) {
  212. parsedDate.setTime(timeStamp);
  213. }
  214. else {
  215. parsedDate = this.parseDateFromString(dateString);
  216. }
  217. return parsedDate;
  218. };
  219. FeedUpdateBroker.prototype.parseDateFromString = function(string) {
  220. var parsedDate = new Date();
  221. var portions = string.split('T');
  222. // split the '2009-01-01' and '22:00:00Z' strings further by - and :, respectively
  223. var yymmdd = portions[0].split('-');
  224. var hhmmss = portions[1].split(':');
  225. hhmmss[2] = hhmmss[2].replace('Z', "");
  226. var year = parseInt(yymmdd[0]);
  227. // For some reason months are presented 0-11
  228. var month = parseInt(yymmdd[1]) -1;
  229. var day = parseInt(yymmdd[2]);
  230. var hour = parseInt(hhmmss[0]);
  231. var minute = parseInt(hhmmss[1]);
  232. var second = parseInt(hhmmss[2]);
  233. parsedDate.setFullYear(year, month, day);
  234. parsedDate.setHours(hour, minute, second, 0);
  235. return parsedDate;
  236. };
  237. // Returns the text of a node.
  238. FeedUpdateBroker.prototype.getTextOfNode = function(node) {
  239. var buf = "";
  240. // iterate through all child elements and collect all text to the buffer
  241. var child = node.firstChild;
  242. while (child != null) {
  243. if (child.nodeType == Node.TEXT_NODE || child.nodeType == Node.CDATA_SECTION_NODE) {
  244. // append text to buffer
  245. if (buf != "") {
  246. buf += " ";
  247. }
  248. buf += child.nodeValue;
  249. }
  250. child = child.nextSibling;
  251. }
  252. return buf;
  253. };