Skip to content

Instantly share code, notes, and snippets.

@RichardDavies
Created October 11, 2011 17:46
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RichardDavies/1278827 to your computer and use it in GitHub Desktop.
Save RichardDavies/1278827 to your computer and use it in GitHub Desktop.
Alternate JavaScript implementation of Feed2JS using the Google Feed API
/**
* Feed2JS using the Google Feed API
*
* Inspired by Feed2JS (http://feed2js.org)
*
* Author: Richard Davies (http://www.richarddavies.us)
*
* Example Usage:
* <!-- Requires the Google Loader and API key from http://code.google.com/apis/loader/signup.html -->
* <script src="https://www.google.com/jsapi?key=YOUR_KEY_HERE"></script>
* <script src="feed2js.js" data-src="http://www.example.com/feed.xml" data-num="3" data-html="n" data-pc="y"></script>
*
* Attributes * = default value
* data-src URL of feed
* data-chan Display channel?
* y* Display title and description
* title Display title only
* n Don't Display title or description
* data-num Max number of feed items to display
* 0* Display all items (max 100 items)
* data-desc Display item descriptions?
* y* Display description
* n No description
* data-html Use HTML in item description?
* y* Use HTML from feed, ignoring data-desc character limit
* n Use plain text description snippet (< 120 characters)
* data-date Display time and date for each item
* y Display date/time
* n* No date/time
* pc Podcast (media) enclosures (for RSS 2.0 feeds with enclosures)
* y Display link to media files
* n* Don't display media files
*
* Google Feed API Documentation
* http://code.google.com/apis/ajaxfeeds/documentation/reference.html
*/
// Get a reference to the currently executing <script> element
// Since scripts are executed sequentially the currently executing script is always the last script in the DOM so far.
// (Unless executing script adds any additional script elements, as google.load() does... so put it first)
var scripts = document.getElementsByTagName('script');
var thisScript = scripts[scripts.length - 1];
// If this is the first execution on the page initialize some global variables and load Google Feeds API
if (typeof feed2js_idx === 'undefined') {
var feed2js_idx = 0; // Global var to track current index # (for multiple feeds on a page)
var feed2js_DOMready = false; // Global flag to signal when DOM has finished loading
google.load('feeds', '1');
google.setOnLoadCallback(function() {
feed2js_DOMready = true;
}); // i.e. <body onload="...">
}
(function() {
var idx = feed2js_idx++;
var id = 'rss-box-' + (idx+1);
var attributes = new Object();
attributes.feedURL = thisScript.getAttribute('data-src');
attributes.chan = thisScript.getAttribute('data-chan') || 'y';
attributes.num = thisScript.getAttribute('data-num') || 0;
attributes.desc = thisScript.getAttribute('data-desc') || 'y';
attributes.html = thisScript.getAttribute('data-html') || 'y';
attributes.date = thisScript.getAttribute('data-date') || 'n';
attributes.pc = thisScript.getAttribute('data-pc') || 'n';
if (attributes.num <= 0)
attributes.num = 100;
// Create container div element for feed and insert into DOM after current <script> element
var container = document.createElement('div');
container.id = id;
container.className = 'rss-box';
thisScript.parentNode.insertBefore(container, thisScript.nextSibling);
// There is a new WHATWG proposal to add document.currentScript, but it only works in Firefox as of Oct. 2011
// document.currentScript.parentNode.insertBefore(container, document.currentScript.nextSibling);
// Wait unti feed2js_DOMready is true before proceeding
var intervalID = setInterval(function() {
if (feed2js_DOMready) {
clearInterval(intervalID);
// Create a Google Feed API instance that will grab RSS feed
var feed = new google.feeds.Feed(attributes.feedURL);
// Set number of feed items to load
feed.setNumEntries(attributes.num);
// Request the feed and call the callback function with the result
feed.load(function(result) {
// Grab the DOM container we will put the results into
var container = document.getElementById(id);
if (!result.error) {
// Build feed title and description
if (attributes.chan == 'y' || attributes.chan == 'title') {
// Feed title
var title = document.createElement('div');
title.className = 'rss-title';
title.innerHTML = '<a href="{url}">{title}</a>'.supplant({
title: result.feed.title,
url: result.feed.link
});
// Include feed description?
if (attributes.chan == 'y')
title.innerHTML += '<br />{desc}'.supplant({
desc: result.feed.description
});
// Output title and description
container.appendChild(title);
}
// Build list of feed items
var ul = document.createElement('ul');
ul.className = 'rss-items';
for (var i = 0; i < result.feed.entries.length; i++) {
var entry = result.feed.entries[i];
var li = document.createElement('li');
li.className = 'rss-item';
li.innerHTML = '<a href="{url}">{title}</a>'.supplant({
title: entry.title,
url: entry.link
});
// Include item timestamp?
if (attributes.date == 'y') {
var entryDate = new Date(entry.publishedDate);
li.innerHTML += '<br /><span class="rss-date">{date}</span>'.supplant({
date: entryDate.toLocaleString()
});
}
// Include item description?
if (attributes.desc == 'y')
li.innerHTML += '<br />{desc}'.supplant({
desc: attributes.html == 'y' ? entry.content : entry.contentSnippet
});
// Include media enclosures?
if (attributes.pc == 'y' && entry.mediaGroups) {
var media = document.createElement('div');
media.className = 'rss-item-media';
var p = document.createElement('p');
p.innerHTML = 'Media files';
media.appendChild(p);
var ul2 = document.createElement('ul');
media.appendChild(ul2);
for (var prop1 in entry.mediaGroups)
for (var prop2 in entry.mediaGroups[prop1].contents) {
var mediaEnc = entry.mediaGroups[prop1].contents[prop2];
var li2 = document.createElement('li');
li2.innerHTML += '<a href="{url}" title="{type}">{title}</a> ({size})'.supplant({
url: mediaEnc.url,
title: (function() {
// Extract filename from URL
var matches = mediaEnc.url.match(/([^\/]*)$/);
return RegExp.$1;
})(),
type: mediaEnc.type,
size: (function() {
// Convert file size in bytes to KB or MB
var fileSizeKB = mediaEnc.fileSize/1024;
if (fileSizeKB < 1000)
return Math.round(fileSizeKB) + ' KB';
else {
var fileSizeMB = fileSizeKB/1024;
return Math.round(fileSizeMB*10)/10 + ' MB';
}
})()
});
ul2.appendChild(li2);
}
li.appendChild(media);
}
ul.appendChild(li);
}
// Output list of feed items
container.appendChild(ul);
} else { // Error loading feed
container.className += ' rss-error';
var errorHead = document.createElement('p');
errorHead.innerHTML = '<strong>ERROR: The following error occurred while loading the feed <a href="{feedURL}">{feedURL}</a>:</strong>'.supplant({
feedURL: attributes.feedURL
});
var errorMsg = document.createElement('p');
errorMsg.innerHTML = '{code} {desc}'.supplant({
code: result.error.code,
desc: result.error.message
});
var errorHelp = document.createElement('p');
errorHelp.innerHTML = 'Please verify that the <a href="{feedURL}">feed URL</a> works in your browser and that the feed passes a <a href="{validatorURL}">validator test</a>.'.supplant({
feedURL: attributes.feedURL,
validatorURL: 'http://feedvalidator.org/check.cgi?url=' + attributes.feedURL
});
// Output error message
container.appendChild(errorHead);
container.appendChild(errorMsg);
container.appendChild(errorHelp);
}
});
}
}, 100);
})();
// Supplant function allows for variable substitution inside a string
// http://javascript.crockford.com/remedial.html
String.prototype.supplant = function (o) {
return this.replace(/{([^{}]*)}/g,
function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
}
);
};
@RichardDavies
Copy link
Author

Feed2JS is a RSS to HTML parser. This is a (mostly complete) alternate implementation I created that uses the Google Feed API instead of the hosted service at http://feed2js.org. It has the following advantages over the hosted service:

  • Google's Feed API is probably more reliable and robust than feed2js.org
  • The Google Feed API is more tolerant of invalid feeds
  • Since the RSS to HTML conversion is done client side via JavaScript, you can easily customize the output to meet your own needs

@cogdog
Copy link

cogdog commented Oct 12, 2011

This is cool, Richard. Where can I see it in action? and how would one use it?

@RichardDavies
Copy link
Author

There is an example of how to use it in the comment header in the code. You can see it in use at http://www.publicalerts.org/index.cfm?c=51183

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment