/* ***** BEGIN LICENSE BLOCK ***** Copyright © 2015 Center for History and New Media George Mason University, Fairfax, Virginia, USA http://zotero.org This file is part of Zotero. Zotero is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Zotero is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Zotero. If not, see . ***** END LICENSE BLOCK ***** */ /** * Sample feeds: * * http://cyber.law.harvard.edu/rss/examples/rss2sample.xml * http://feeds.feedburner.com/acs/acbcct * http://www.cell.com/molecular-cell/current.rss * http://ieeexplore.ieee.org/search/searchresult.jsp?searchField%3DSearch_All%26queryText%3Dwater&searchOrigin=saved_searches&rssFeed=true&rssFeedName=water * http://www.sciencemag.org/rss/current.xml * http://rss.sciencedirect.com/publication/science/20925212 * http://www.ncbi.nlm.nih.gov/entrez/eutils/erss.cgi?rss_guid=1fmfIeN4X5Q8HemTZD5Rj6iu6-FQVCn7xc7_IPIIQtS1XiD9bf * http://export.arxiv.org/rss/astro-ph */ /** * class Zotero.FeedReader * Asynchronously reads an ATOM/RSS feed * * @param {String} url URL of the feed * * @property {Zotero.Promise} feedProperties An object * representing feed properties * @property {Zotero.Promise*} itemIterator Returns an iterator * for feed items. The iterator returns FeedItem promises that have to be * resolved before requesting the next promise. When all items are exhausted. * the promise resolves to null. * @method {void} terminate Stops retrieving/parsing the feed. Data parsed up * to this point is still available. */ Zotero.FeedReader = new function() { let ios = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); /***************************** * Item processing functions * *****************************/ /** * Determine item type based on item data */ function guessItemType(item) { // Default to journalArticle item.itemType = 'journalArticle'; if (item.ISSN) { return; // journalArticle } if (item.ISBN) { item.itemType = 'bookSection'; return; } if (item.publicationType) { let type = item.publicationType.toLowerCase(); if (type.indexOf('conference') != -1) { item.itemType = 'conferencePaper'; return; } if (type.indexOf('journal') != -1) { item.itemType = 'journalArticle'; return; } if (type.indexOf('book') != -1) { item.itemType = 'bookSection'; return; } } }; /* * Fetch creators from given field of a feed entry */ function processCreators(feedEntry, field, role) { let names = [], nameStr; try { let personArr = feedEntry[field]; // Seems like this part can throw if there is no author data in the feed for (let i=0; i 1 // If only one comma and first part has more than one space, // it's probably not lastName, firstName || (commas == 1 && name.split(/\s*,/)[0].indexOf(' ') != -1) ) ) { // Probably multiple authors listed in a single field nameStr = name; break; // For clarity. personArr.length == 1 anyway } else { names.push(name); } } } catch(e) { if (e.result != Components.results.NS_ERROR_FAILURE) throw e if (field != 'authors') return []; // ieeexplore places these in "authors"... sigh nameStr = getFeedField(feedEntry, null, 'authors'); if (nameStr) nameStr = Zotero.Utilities.trimInternal(nameStr); if (!nameStr) return []; } if (nameStr) { names = nameStr.split(/\s(?:and|&)\s|\s*[,;]\s*/); } let creators = []; for (let i=0; i { let items = feed.items; if (items && items.length) { for (let i=0; i { // Make sure the last promise gets resolved to null let lastItem = this._feedItems[this._feedItems.length - 1]; lastItem.resolve(null); }); // Set up asynchronous feed processor let feedProcessor = Components.classes["@mozilla.org/feed-processor;1"] .createInstance(Components.interfaces.nsIFeedProcessor); let feedUrl = ios.newURI(url, null, null); feedProcessor.parseAsync(null, feedUrl); feedProcessor.listener = { /* * MDN suggests that we could use nsIFeedProgressListener to handle the feed * as it gets loaded, but this is actually not implemented (as of 32.0.3), * so we have to load the whole feed and handle it in handleResult. */ handleResult: (result) => { if (!result.doc) { this.terminate("No Feed"); return; } let newFeed = result.doc.QueryInterface(Components.interfaces.nsIFeed); this._feed.resolve(newFeed); } }; Zotero.debug("FeedReader: Fetching feed from " + feedUrl.spec); this._channel = ios.newChannelFromURI(feedUrl); this._channel.asyncOpen(feedProcessor, null); // Sends an HTTP request } Zotero.defineProperty(FeedReader.prototype, 'feedProperties', { get: function() this._feedProperties }); /* * Feed item iterator * Each iteration returns a _promise_ for an item. The promise _MUST_ be * resolved before requesting the next item. * The last item will always be resolved to `null`, unless the feed processing * is terminated ahead of time, in which case it will be rejected with the reason * for termination. */ Zotero.defineProperty(FeedReader.prototype, 'itemIterator', { get: function() { let items = this._feedItems; return new function() { let i = 0; this.next = function() { let item = items[i++]; return { value: item ? item.promise : null, done: i >= items.length }; }; } } }); /* * Terminate feed processing at any given time * @param {String} status Reason for terminating processing */ FeedReader.prototype.terminate = function(status) { Zotero.debug("FeedReader: Terminating feed reader (" + status + ")"); // Reject feed promise if not resolved yet if (this._feed.promise.isPending()) { this._feed.reject(status); } // Reject feed item promise if not resolved yet let lastItem = this._feedItems[this._feedItems.length - 1]; if (lastItem.promise.isPending()) { lastItem.reject(status); } // Close feed connection if (channel.isPending) { channel.cancel(Components.results.NS_BINDING_ABORTED); } }; return FeedReader; };