home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 120 / cdrom120.iso / internet / sage / sage.xpi / chrome / sage.jar / content / feedlib.js < prev    next >
Encoding:
JavaScript  |  2005-05-12  |  12.3 KB  |  520 lines

  1.  
  2. /**
  3.  * Feed class
  4.  *
  5.  */
  6.  
  7. function Feed(feedXML) {
  8.     this.feedXML = feedXML;
  9.     this.feedFormat = null;
  10.  
  11.     this.title = null;
  12.     this.link = null;
  13.     this.description = null;
  14.     this.items = new Array();
  15.     this.lastPubDate = null;
  16.  
  17.     if(!feedXML) {
  18.         throw "Empty Feed";
  19.     }
  20.  
  21.     var rootNodeName = feedXML.documentElement.localName.toLowerCase();
  22.     if(rootNodeName == "feed") {
  23.         this.parseAtom();
  24.     } else if(rootNodeName == "rss" || rootNodeName == "rdf") {
  25.         this.parseRSS();
  26.     } else {
  27.         throw "Feed has invalid root element";
  28.     }
  29. }
  30.  
  31. Feed.prototype.parseRSS = function() {
  32.  
  33.     var feedXML = this.feedXML;
  34.  
  35.     const nsIURIFixup = Components.interfaces.nsIURIFixup;
  36.     const URIFixup = Components.classes["@mozilla.org/docshell/urifixup;1"].getService(nsIURIFixup);
  37.  
  38.     var firstElement = feedXML.documentElement;
  39.  
  40.     if(firstElement.localName.toLowerCase() == "rdf") {
  41.         this.feedFormat = "RSS (1.0)";
  42.     } else if(firstElement.localName.toLowerCase() == "rss") {
  43.         if(firstElement.hasAttribute("version")) {
  44.             this.feedFormat = "RSS (" + firstElement.getAttribute("version") + ")";
  45.         } else {
  46.             this.feedFormat = "RSS (?)";
  47.         }
  48.     }
  49.  
  50.     var channelNode;
  51.     for(var i = firstElement.firstChild; i != null; i = i.nextSibling) {
  52.         if(i.nodeType != i.ELEMENT_NODE) continue;
  53.         if(i.localName.toLowerCase() == "channel") {
  54.             channelNode = i;
  55.         }
  56.     }
  57.     if(!channelNode) {
  58.         throw "No channel element where expected";
  59.     }
  60.  
  61.     if(feedXML.getElementsByTagName("channel").length != 0) {
  62.         channelNode = feedXML.getElementsByTagName("channel")[0];
  63.     } else {
  64.         throw "No elements in channel tag";
  65.     }
  66.  
  67.     for(i = channelNode.firstChild; i != null; i = i.nextSibling) {
  68.         if(i.nodeType != i.ELEMENT_NODE) continue;
  69.         switch(i.localName) {
  70.             case "title":
  71.                 this.title = entityDecode(CommonFunc.getInnerText(i));
  72.                 break;
  73.             case "link":
  74.                 this.link = CommonFunc.getInnerText(i);
  75.                 break;
  76.             case "description":
  77.                 this.description = entityDecode(CommonFunc.getInnerText(i));
  78.                 break;
  79.         }
  80.     }
  81.  
  82.     var itemNodes = feedXML.getElementsByTagName("item");
  83.     var item, guid;
  84.     for(i = 0; itemNodes.length > i; i++) {
  85.         item = {title:"", link:"", content:"", pubDate:""};
  86.         guid = null;
  87.  
  88.         for(var j = itemNodes[i].firstChild; j!=null; j=j.nextSibling) {
  89.             if(j.nodeType != j.ELEMENT_NODE) continue;
  90.             switch(j.localName) {
  91.                 case "title":
  92.                     item.title = entityDecode(CommonFunc.getInnerText(j));
  93.                     break;
  94.                 case "link":
  95.                     if(!item.link) {
  96.                         item.link = this.link ? URIFixup.createFixupURI(this.link, nsIURIFixup.FIXUP_FLAG_NONE).resolve(CommonFunc.getInnerText(j)) : CommonFunc.getInnerText(j);
  97.                     }
  98.                     break;
  99.                 case "guid":
  100.                     if(!guid) {
  101.                         guid = CommonFunc.getInnerText(j);
  102.                     }
  103.                     break;
  104.                 case "description":
  105.                     if(!item.content) {
  106.                         item.content = CommonFunc.getInnerText(j);
  107.                     }
  108.                     break;
  109.                 case "encoded":
  110.                     item.content = CommonFunc.getInnerText(j);
  111.                     break;
  112.                 case "pubDate":
  113.                     tmp_str = CommonFunc.getInnerText(j);
  114.                     tmp_date = rfc822ToJSDate(tmp_str);
  115.                     if(tmp_date) {
  116.                         item.pubDate = tmp_date;
  117.                     } else {
  118.                         logMessage("unable to parse date string: " + tmp_str + " feed: " + this.title);
  119.                     }
  120.                     break;
  121.                 case "date":
  122.                     tmp_str = CommonFunc.getInnerText(j);
  123.                     tmp_date = iso8601ToJSDate(tmp_str);
  124.                     if(tmp_date) {
  125.                         item.pubDate = tmp_date;
  126.                     } else {
  127.                         logMessage("unable to parse date string: " + tmp_str + " feed: " + this.title);
  128.                     }
  129.                     break;
  130.             }
  131.         }
  132.  
  133.         if(!item.link && guid) {
  134.             item.link = this.link ? URIFixup.createFixupURI(this.link, nsIURIFixup.FIXUP_FLAG_NONE).resolve(guid) : guid;
  135.         }
  136.  
  137.         var tmpFeedItem = new FeedItem(item.title, item.link, item.content, item.pubDate);
  138.  
  139.         if(tmpFeedItem.hasPubDate()) {
  140.             if(tmpFeedItem.getPubDate() > this.lastPubDate) {
  141.                 this.lastPubDate = tmpFeedItem.getPubDate();
  142.             }
  143.         }
  144.  
  145.         this.items.push(tmpFeedItem);
  146.     }
  147. }
  148.  
  149. Feed.prototype.parseAtom = function() {
  150.  
  151.     var feedXML = this.feedXML;
  152.  
  153.     const nsIURIFixup = Components.interfaces.nsIURIFixup;
  154.     const URIFixup = Components.classes["@mozilla.org/docshell/urifixup;1"].getService(nsIURIFixup);
  155.  
  156.     var firstElement = feedXML.documentElement;
  157.  
  158.     if(firstElement.hasAttribute("version")) {
  159.         this.feedFormat = "Atom (" + firstElement.getAttribute("version") + ")";
  160.     } else {
  161.         this.feedFormat = "Atom (?)";
  162.     }
  163.  
  164.     for(var i = feedXML.documentElement.firstChild; i != null; i = i.nextSibling) {
  165.         if(i.nodeType != i.ELEMENT_NODE) continue;
  166.         switch(i.localName) {
  167.             case "title":
  168.                 this.title = entityDecode(CommonFunc.getInnerText(i));
  169.                 break;
  170.             case "link":
  171.                 if(this.link) {
  172.                     if(i.getAttribute("rel").toLowerCase() == "alternate") {
  173.                         this.link = i.getAttribute("href");
  174.                     }
  175.                 } else {
  176.                     this.link = i.getAttribute("href");
  177.                 }
  178.                 break;
  179.             case "tagline":
  180.                 this.description = entityDecode(CommonFunc.getInnerText(i));
  181.                 break;
  182.         }
  183.     }
  184.  
  185.     var entryNodes = feedXML.getElementsByTagName("entry");
  186.     for(i = 0; entryNodes.length > i; i++) {
  187.         var item = {title:"", link:"", content:"", pubDate:""};
  188.  
  189.         var titleNodes = entryNodes[i].getElementsByTagName("title");
  190.         if(titleNodes.length) {
  191.             item.title = entityDecode(CommonFunc.getInnerText(titleNodes[0]));
  192.         }
  193.  
  194.         var linkNodes = entryNodes[i].getElementsByTagName("link");
  195.         if(linkNodes.length) {
  196.             for (var j = 0; j < linkNodes.length; j++) {
  197.                 if (linkNodes[j].getAttribute("rel").toLowerCase() == "alternate") {
  198.                     item.link = this.link ? URIFixup.createFixupURI(this.link, nsIURIFixup.FIXUP_FLAG_NONE).resolve(linkNodes[j].getAttribute("href")) : linkNodes[j].getAttribute("href");
  199.                     break;
  200.                 }
  201.             }
  202.         }
  203.  
  204.         var issuedNodes = entryNodes[i].getElementsByTagName("issued");
  205.         if(issuedNodes.length) {
  206.             tmp_str = CommonFunc.getInnerText(issuedNodes[0]);
  207.             tmp_date = iso8601ToJSDate(tmp_str);
  208.             if(tmp_date) {
  209.                 item.pubDate = tmp_date;
  210.             } else {
  211.                 logMessage("unable to parse date string: " + tmp_str + " feed: " + this.title);
  212.             }
  213.         }
  214.  
  215.         var aEntryNode = entryNodes[i];
  216.  
  217.         var contentNodes = aEntryNode.getElementsByTagName("content");
  218.         var contentArray = new Array();
  219.         for(j = 0; j < contentNodes.length; j++) {
  220.             var contType = contentNodes[j].getAttribute("type");
  221.             contentArray[contType] = CommonFunc.getInnerText(contentNodes[j]);
  222.         }
  223.  
  224.         var summaryNodes = aEntryNode.getElementsByTagName("summary");
  225.  
  226.         if("application/xhtml+xml" in contentArray) {
  227.             item.content = contentArray["application/xhtml+xml"];
  228.         } else if("text/html" in contentArray) {
  229.             item.content = contentArray["text/html"];
  230.         } else if("text/plain" in contentArray) {
  231.             item.content = contentArray["text/plain"];
  232.         }    else if(summaryNodes.length) {
  233.             item.content = CommonFunc.getInnerText(summaryNodes[0]);
  234.         }
  235.  
  236.         var tmpFeedItem = new FeedItem(item.title, item.link, item.content, item.pubDate);
  237.  
  238.         if(tmpFeedItem.hasPubDate()) {
  239.             if(tmpFeedItem.getPubDate() > this.lastPubDate) {
  240.                 this.lastPubDate = tmpFeedItem.getPubDate();
  241.             }
  242.         }
  243.  
  244.         this.items.push(tmpFeedItem);
  245.     }
  246. }
  247.  
  248. Feed.prototype.getTitle = function() {
  249.     return this.title.replace(/<.*?>/g,'');
  250. }
  251.  
  252. Feed.prototype.hasDescription = function() {
  253.     if(!this.description) {
  254.         return false;
  255.     } else {
  256.         return true;
  257.     }
  258. }
  259.  
  260. Feed.prototype.getDescription = function() {
  261.     if(this.hasDescription()) {
  262.         return this.description;
  263.     } else {
  264.         return "";
  265.     }
  266. }
  267.  
  268. Feed.prototype.getLink = function() {
  269.     return this.link;
  270. }
  271.  
  272. Feed.prototype.hasLastPubDate = function() {
  273.     if(!this.lastPubDate) {
  274.         return false;
  275.     } else {
  276.         return true;
  277.     }
  278. }
  279.  
  280. Feed.prototype.getLastPubDate = function() {
  281.     if(this.hasLastPubDate()) {
  282.         return this.lastPubDate;
  283.     } else {
  284.         return null;
  285.     }
  286. }
  287.  
  288. Feed.prototype.getItemCount = function() {
  289.     return this.items.length;
  290. }
  291.  
  292. Feed.prototype.getItem = function(itemIndex) {
  293.     return this.items[itemIndex];
  294. }
  295.  
  296. Feed.prototype.getItems = function(sort) {
  297.     if(sort == "chrono" && !this.hasLastPubDate()) {  // if the feed doesn't have pub dates, we're going to do a source sort
  298.         sort = "source";
  299.     }
  300.     var items_array;
  301.     switch(sort) {
  302.         case "chrono":
  303.             var items = new Array();
  304.             for(var c = 0; c < this.items.length; c++) {
  305.                 items.push(this.items[c]);
  306.             }
  307.             function chronoSort(a, b) {
  308.                 var a_ts = a.hasPubDate() ? a.getPubDate() : 0;
  309.                 var b_ts = b.hasPubDate() ? b.getPubDate() : 0;
  310.                 return b_ts - a_ts;
  311.             }
  312.             items.sort(chronoSort);
  313.             items_array = items;
  314.             break;
  315.         case "source":
  316.             items_array = this.items;
  317.             break;
  318.         default:
  319.             items_array = this.items;
  320.             break;
  321.     }
  322.     return items_array;
  323. }
  324.  
  325. Feed.prototype.getFormat = function() {
  326.     return this.feedFormat;
  327. }
  328.  
  329. Feed.prototype.getSignature = function() {
  330.     var hashText = "";
  331.     for(var c = 0; c < this.getItemCount(); c++) {
  332.         hashText += this.getItem(c).getTitle();
  333.     }
  334.     sig ="[" + b64_sha1(hashText) + "]";
  335.     return sig;
  336. }
  337.  
  338.  
  339.  
  340. /**
  341.  * FeedItem class
  342.  *
  343.  */
  344.  
  345. function FeedItem(title, link, content, pubDate) {
  346.     this.title = title;
  347.     this.link = link;
  348.     this.content = content;
  349.     this.pubDate = pubDate;
  350. }
  351.  
  352. FeedItem.prototype.hasTitle = function() {
  353.     if(!this.title) {
  354.         return false;
  355.     } else {
  356.         return true;
  357.     }
  358. }
  359.  
  360. FeedItem.prototype.getTitle = function() {
  361.     var title;
  362.     if(this.hasTitle()) {
  363.         title = this.title.replace(/<.*?>/g,'');
  364.     } else {
  365.         if(this.hasContent()) {
  366.             temp = this.getContent();
  367.             temp = temp.replace(/<.*?>/g,'');
  368.             title = temp.substring(0, 30) + "...";
  369.         } else {
  370.             title = "No Title";
  371.         }
  372.     }
  373.     return title;
  374. }
  375.  
  376. FeedItem.prototype.getLink = function() {
  377.     return this.link;
  378. }
  379.  
  380. FeedItem.prototype.hasContent = function() {
  381.     if(!this.content) {
  382.         return false;
  383.     } else {
  384.         return true;
  385.     }
  386. }
  387.  
  388. FeedItem.prototype.getContent = function() {
  389.     if(this.hasContent()) {
  390.         return this.content;
  391.     } else {
  392.         return "No content";
  393.     }
  394. }
  395.  
  396. FeedItem.prototype.hasPubDate = function() {
  397.     if(!this.pubDate) {
  398.         return false;
  399.     } else {
  400.         return true;
  401.     }
  402. }
  403.  
  404. FeedItem.prototype.getPubDate = function() {
  405.     if(this.hasPubDate()) {
  406.         return this.pubDate;
  407.     } else {
  408.         return null;
  409.     }
  410. }
  411.  
  412.  
  413.  
  414. /**
  415.  * Utility functions
  416.  *
  417.  */
  418.  
  419. // Parses an RFC 822 formatted date string and returns a JavaScript Date object, returns null on parse error
  420. // Example inputs:  "Sun, 08 May 05 15:19:37 GMT"  "Mon, 09 May 2005 00:50:19 GMT"
  421. function rfc822ToJSDate(date_str) {
  422.     date_array = date_str.split(" ");
  423.     // check for two digit year
  424.     if(date_array.length == 6 && date_array[3].length == 2) {
  425.         // convert to four digit year with a pivot of 70
  426.         if(date_array[3] < 70) {
  427.             date_array[3] = "20" + date_array[3];
  428.         } else {
  429.             date_array[3] = "19" + date_array[3];
  430.         }
  431.     }
  432.     date_str = date_array.join(" ");
  433.     date = new Date(date_str);
  434.     if(date != "Invalid Date") {
  435.         return date;
  436.     } else {
  437.         return null
  438.     }
  439. }
  440.  
  441. // Parses an ISO 8601 formatted date string and returns a JavaScript Date object, returns null on parse error
  442. // Example inputs:  "2004-06-17T18:00Z" "2004-06-17T18:34:12+02:00"
  443. function iso8601ToJSDate(date_str) {
  444.     var tmp = date_str.split("T");
  445.     var date = tmp[0];
  446.  
  447.     date = date.split("-");
  448.     var year = date[0];
  449.     var month = date[1];
  450.     var day = date[2];
  451.  
  452.     var hours = 0;
  453.     var minutes = 0;
  454.     var seconds = 0;
  455.     var tz_mark = "Z";
  456.     var tz_hours = 0;
  457.     var tz_minutes = 0;
  458.     var time, whole_time, tz;
  459.  
  460.     if(tmp.length == 2) {
  461.         whole_time = tmp[1];
  462.         tz_mark = whole_time.match("[Z+-]{1}");
  463.         if(tz_mark) {
  464.             tmp = whole_time.split(tz_mark);
  465.             time = tmp[0];
  466.             if(tz_mark == "+" || tz_mark == "-") {
  467.                 tz = tmp[1];
  468.                 tmp = tz.split(":");
  469.                 tz_hours = tmp[0];
  470.                 tz_minutes = tmp[1];
  471.             }
  472.         } else {
  473.             tz_mark = "Z";
  474.             time = whole_time;
  475.         }
  476.         tmp = time.split(":");
  477.         hours = tmp[0];
  478.         minutes = tmp[1];
  479.         if(tmp.length == 3) {
  480.             seconds = tmp[2];
  481.         }
  482.     }
  483.  
  484.     var utc = Date.UTC(year, month - 1, day, hours, minutes, seconds);
  485.     var tmp_date;
  486.     if(tz_mark == "Z") {
  487.         tmp_date = new Date(utc);
  488.     } else if(tz_mark == "+") {
  489.         tmp_date = new Date(utc - tz_hours*3600000 - tz_minutes*60000);
  490.     } else if(tz_mark == "-") {
  491.         tmp_date = new Date(utc + tz_hours*3600000 + tz_minutes*60000);
  492.     } else {
  493.         tmp_date = "Invalid Date";
  494.     }
  495.  
  496.     if (tmp_date == "Invalid Date") {
  497.         return null;
  498.     } else {
  499.         return tmp_date;
  500.     }
  501. }
  502.  
  503. function entityDecode(aStr) {
  504.     var    formatConverter = Components.classes["@mozilla.org/widget/htmlformatconverter;1"].createInstance(Components.interfaces.nsIFormatConverter);
  505.     var fromStr = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
  506.     fromStr.data = aStr;
  507.     var toStr = { value: null };
  508.  
  509.     try {
  510.         formatConverter.convert("text/html", fromStr, fromStr.toString().length, "text/unicode", toStr, {});
  511.     } catch(e) {
  512.         return aStr;
  513.     }
  514.     if(toStr.value) {
  515.         toStr = toStr.value.QueryInterface(Components.interfaces.nsISupportsString);
  516.         return toStr.toString();
  517.     }
  518.     return aStr;
  519. }
  520.