// =================================================================================================
// 
// 	LinkedList v1.2
//  by Prateek Rungta
//  http://prateekrungta.com
//  
//  For more information visit
//  http://prateekrungta.com/linkedlist
//  
//  Copyright 2007-2008 Prateek Rungta
//
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//	
//	This program 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 General Public License for more details.
//	
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	
// =================================================================================================

/*
global variables
*/

var gv_month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];


/*
LinkedList
	class which accepts a list of RSS / Atom URLs and the id of the element where
	they will be displayed
*/

function LinkedList (containerId, numEntries, fadeIn, useCookies) {
	this.containerId = containerId;
	this.feeds = new Array();
	this.loaded = false;
	this.numLoaded = 0;
	this.numEntries = numEntries;
	this.fadeIn = (fadeIn != false) ? true : false;
	this.useCookies = (useCookies == null || useCookies == false) ? false : true;
	this.limit = 0;
	this.dots = 0;
	var o = this;
	
	//methods
	this.limitTo	= function(noOfLinks) { // public
		if (noOfLinks > 0) this.limit = noOfLinks;
	}
	
	this.add		= function(feedURL, name, isFeed) { // public method
		if (this.loaded) { return false; } // ensure feeds are not added after the initial ones have been displayed
		this.feeds[this.feeds.length] = new LiveLink(feedURL, name, this.numEntries, isFeed);
	}
	
	this.display	= function() { // public 
		if (this.loaded) { return false; } // or will create duplicate entries in the container element
		this.loaded = true;
		for (var i = this.feeds.length - 1; i >= 0; i--) {
			if (this.feeds[i].isFeed && !window.google) {
				this.showError('Google\'s AJAX Feed API not loaded.');
				return false;
			}
			if (!window.google.feeds || !window.google.feeds.Feed) {
				this.showError('Google\'s AJAX Feed API not loaded. You probably forgot to add google.load(\'feeds\', \'1\');');
				return false;
			}
			this.feeds[i].load(this);
		}
		if (this.fadeIn) this.startLoading(); // cosmetic
	}
	
	this.loadedFeed	= function(loadedLink) { // private 
		this.numLoaded++;
		
		// sorting the loaded link in the right place
		var j = this.feeds.length - 1;
		while (j > 0 && loadedLink != this.feeds[j]) {
			j--;
		}
		while (j > 0 && loadedLink.published > this.feeds[j-1].published) {
			this.feeds[j] = this.feeds[j-1];
			j--;
		}
		this.feeds[j] = loadedLink;
		
		if (this.numLoaded >= this.feeds.length) { this.show(); }
	}
	
	this.show		= function() { // private 
		var container, linksList, noOfLinks;
		container = document.getElementById(this.containerId);
		if (!container) {
			// container = document.getElementsByTagName('body');
			// container = container[0];
			this.showError('No element with id = ' + this.containerId + ' found.');
			return true;
		}
		
		linksList = document.createElement('ul');
		linksList.className = 'll-linksList';
		
		noOfLinks = (this.feeds.length < this.limit) ? this.feeds.length : ((this.limit != 0)?this.limit:this.feeds.length);
		for (var i=0; i < noOfLinks; i++) {
			var feed, feedLi;
			feed = this.feeds[i];
			
			if (feed.error) { continue; }

			feedLi = document.createElement('li');			
			feedLi.appendChild(feed.show());
			linksList.appendChild(feedLi);
		}
		
		if (this.fadeIn) setOpacity (linksList, 0);	// to make the links "fade in"
		
		this.stopLoading(); // cosmetics
		container.appendChild(linksList);
		
		if (!this.fadeIn) return true;
		// else fade the element
		var currentFrame = 0;
		var totalFrames = 35;
		linksList.currentlyAppearing	=	window.setInterval(
			function ()	{
				setOpacity (linksList, (currentFrame * 10) / totalFrames);
				currentFrame++;
				if(currentFrame > totalFrames)	{
					window.clearInterval(linksList.currentlyAppearing);
				}
			},
			15);
	}
	
	this.showError	= function(errorMessage) { // private 
		var container, p, error;
		container = document.getElementById(this.containerId);
		if (!container) {
			container = document.getElementsByTagName('body');
			container = container[0];
		}
		
		p = document.createElement('p');
		error = document.createElement('div');
		
		p.appendChild(document.createTextNode(errorMessage));
		error.appendChild(p);
		
		p.style.color = '#EEE';
		p.style.fontSize = '0.95em';
		p.style.paddingLeft = '3px';
		p.style.paddingRight = '3px';
		p.style.textAlign = 'center';
		error.style.clear = 'both';
		error.style.marginTop = '5px';
		error.style.marginBottom = '5px';
		error.style.border = '1px solid #666';
		error.style.background = '#930';

		container.appendChild(error);
	}
	
	this.startLoading = function() {
		var container = document.getElementById(o.containerId);
		if (!container) return false;
		if (container._loading) container.removeChild(container._loading);
		o.dots = o.dots % 3;
		o.dots++;
		var message = "Loading";
		for (var i=0; i<o.dots; i++) message+='.';
		container._loading = document.createElement('p');
		container._loading.appendChild(document.createTextNode(message));
		container.appendChild(container._loading);
		o.loading = window.setTimeout(o.startLoading, 30);
	}
	this.stopLoading = function() {
		var container = document.getElementById(this.containerId);
		if (!container) return false;
		if (container._loading) container.removeChild(container._loading);
		window.clearTimeout(this.loading);
	}
	
	function LiveLink (feedURL, name, numEntries, isFeed) { // private class
		if (name == false || name == true) {
			isFeed = name;
			name = null;
		}

		this.feedURL = feedURL;
		this.name = name;
		this.numEntries = (numEntries >= 0 && numEntries != null) ? numEntries : 1; // defaults to 1 entry
		this.isFeed = (isFeed != false) ? true : false;
		this.url = (this.isFeed) ? null : this.feedURL;
		this.entries = null;
		this.published = 0;
		this.error = false;

		//methods
		this.load		= function(linkedListObject) {
			var parentObject = this;
			
			// if the link is not a feed, try discovering the page's feed automatically
			if (!this.isFeed) {
				
				// first check if there is a cookie with the feed url saved already
				var cache = readCookie('linkedlist_'+parentObject.url);

				if (cache != null) { // cookie found
					if (cache == 'null') {
						// the page doesn't have a feed associated with it, so display as a normal link
						linkedListObject.loadedFeed(parentObject);
						return true;
					}

					// else 

					// the cookie has the feed url stored so use that
					parentObject.feedURL = cache;
					parentObject.isFeed = true;
					parentObject.loadFeed(linkedListObject);
					return true;
				}

				// no cookie found so ask google to try retrieving the feed url
				google.feeds.lookupFeed(this.url, function (lookupResult) {

					if (lookupResult.error) { 
						// no feed associated with the page, so dispaly as a normal link
						linkedListObject.loadedFeed(parentObject);
					
						// and set a cookie so we don't go looking for a feed url within the next 7 days
						createCookie('linkedlist_'+parentObject.url, 'null', 7);
						return true;
					} 

					// else, Google found a feed for the given link, so load that feed
					parentObject.feedURL = lookupResult.url;
					parentObject.isFeed = true;
					parentObject.loadFeed(linkedListObject);
					
					// and set a cookie so we don't have to look for the url again
					createCookie('linkedlist_'+parentObject.url, parentObject.feedURL, 365);
				} 
				);
				return true;
			}
			
			this.loadFeed(linkedListObject);
		}
		
		this.loadFeed 	= function(linkedListObject) {
			var parentObject = this;
			
			if (!this.isFeed) { return false; }
			
			this.googleFeed = new google.feeds.Feed(this.feedURL);
			this.googleFeed.setNumEntries( (this.numEntries > 0) ? this.numEntries : 1 );
			this.googleFeed.setResultFormat(google.feeds.Feed.MIXED_FORMAT); // to get lastBuildDate incase publishedDate is missing
			this.googleFeed.load( function(result) {
				parentObject.loadedFeed(result, linkedListObject);
			} );
		}

		this.loadedFeed	= function(result, linkedListObject) {
			if (!result.error) {
				this.url = result.feed.link;
				this.entries = result.feed.entries;
				this.name = (this.name == null) ? result.feed.title : this.name;

				if (this.entries.length > 0) {
					var lastUpdatedOn = '';
					
					if (this.entries[0].publishedDate != '') {
						lastUpdatedOn = this.entries[0].publishedDate;
					} else {
						var lastBuildDate = result.xmlDocument.getElementsByTagName('lastBuildDate');
						if (lastBuildDate && lastBuildDate.length > 0) {
							lastUpdatedOn = lastBuildDate[0].childNodes[0].nodeValue;
							this.entries[0].publishedDate = lastUpdatedOn; // assigning the build date to the latest? entry 
						}
					}

					this.published = new Date(lastUpdatedOn);
				}
			} else {
				this.error = true;
				if (this.url != null) {
					// erase the cookie so we DO look for the url again
					eraseCookie('linkedlist_' + this.url);
					this.isFeed = false;
					if (this.name != null) this.error = false; // display as a normal link
				}

			}

			linkedListObject.loadedFeed(this);
		}
		
		this.show		= function() {
			if (this.error) { return false; }
			
			var link, title;
			link = document.createElement('div');
			link.style.display = "inline";
			title = document.createElement('a');
			this.name = (this.name == null) ? this.url : this.name;
			// title.appendChild(document.createTextNode(this.name)); because HTML entities are strings in JS
			title.innerHTML = this.name;
			title.href = this.url;
			link.appendChild(title);
			
			if (this.entries != null) {	
				if (this.numEntries != 0) {
					var entriesList = document.createElement('ul');
					entriesList.className = 'll-entriesList';
					for (var j = 0; j < this.entries.length; j++) {
						var entry, entryLi;
						entry = this.entries[j];
						entryLi = document.createElement('li');
						entryLi.appendChild(document.createTextNode(entry.title));
						if (entry.publishedDate != '') entryLi.appendChild(getDateSpan(entry.publishedDate));
					
						entriesList.appendChild(entryLi);
					}
					link.appendChild(entriesList);
				} else {
					link.appendChild(getDateSpan(this.published));
				}
			}
			
			return link;
		}

		function getDateSpan(dateString) {
			var date, now, diff, string, dateSpan;
			date = new Date(dateString);
			now = new Date();
			diff = now - date;
			string = ' - ';
			
			if ((diff = Math.floor(diff/1000)) < 0)
				string += gv_month[date.getMonth()] + ' ' + date.getDate(); // future!!
			else if (diff < 60)
				string += diff + ' second' + ((diff == 1)?'':'s');
			else if ((diff = Math.floor(diff/60)) < 60)
				string += diff + ' minute' + ((diff == 1)?'':'s');
			else if ((diff = Math.floor(diff/60)) < 24)
				string += diff + ' hour' + ((diff == 1)?'':'s');
			else {
				string += gv_month[date.getMonth()] + ' ' + date.getDate();
				if (date.getFullYear() != now.getFullYear()) string += " '" + String(date.getFullYear()).substr(2);
			}
			
			dateSpan = document.createElement('span');
			dateSpan.className = 'll-entryDate';
			dateSpan.appendChild(document.createTextNode(string));
			return dateSpan;
		}
	}

	// helper functions
	
	function setOpacity (element, value) {
		element.style.opacity = value/10;
		element.style.filter = 'alpha(opacity=' + value*10 + ')';
	}
	
	/*
		Cookies script
		Taken from www.quirksmode.org
		http://www.quirksmode.org/js/cookies.html
	*/
	function createCookie (name, value, days) {
		if (!this.useCookies) return;
		if (days) {
			var date, expires;
			date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			expires = "; expires="+date.toGMTString();
		}
		else { var expires = ""; }
		document.cookie = name+"="+value+expires+"; path=/";
	}

	function readCookie (name) {
		if (!this.useCookies) return null;
		
		var nameEQ, name, ca;
		nameEQ = name + "=";
		ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	}
	
	function eraseCookie(name) {
		createCookie(name,"",-1);
	}
}

