<?xml version="1.0" encoding="UTF-8" ?>
<Module>
	<ModulePrefs 
		title="RSS Feed Customizer" 
		title_url="http://www.javascripttoolbox.com/"
		directory_title="RSS Feed Customizer: Improve the look, layout, and functionality of your iGoogle RSS feeds!"
		description="Customize the display of all RSS feeds in your iGoogle page so they take up less room, look better, and have addded functionality: (*) Mark each feed as 'read' so you only see new items, (*) Set a custom title on each feed, rather than just accepting the title delivered, (*) Button to manually refresh each feed in the title bar, (*) Set the time between auto-refreshes of content for each gadget, (*) Decide whether or not to avoid caching of feed content, (*) Customize the number of items and bullet for each feed, (*) Improved feed display with alternating row colors and highlighting of the current item, (*) Right-click an item title to display the summary below. Adding this gadget one time to an existing page will fix ALL your RSS feeds on the page! If you don't like the changes, just remove the gadget. The actual RSS feeds and settings are not changed, so there is no risk of losing anything." 
		author="Matt Kruse" 
		author_email="gadget@mattkruse.com" 
		screenshot="http://www.javascripttoolbox.com/gadget/rss/rss_dev.png" 
		thumbnail="http://www.javascripttoolbox.com/gadget/rss/rss_dev_thumb.png"
		author_affiliation="Matt Kruse" 
		author_location="IL, USA" 
		author_link="http://www.JavascriptToolbox.com/" 
		/> 
		<UserPref name="bullet" display_name="Default Item Bullet:" datatype="enum" default_value="favicon">
			<EnumValue value="arrow" display_value="&#187;"/>
			<EnumValue value="bullet" display_value="&#8226;"/>
			<EnumValue value="favicon" display_value="Site's Favicon"/>
			<EnumValue value="none" display_value=""/>
		</UserPref>
		<UserPref name="linkstyle" display_name="Link CSS:" default_value="display:block;color:black;text-decoration:none;" />

		<UserPref name="itemformat" display_name="Item Format:" default_value="&lt;a href=&quot;%URL%&quot; title=&quot;Right-click for details&quot; oncontextmenu=&quot;_toggle(this.nextSibling);return false;&quot; style=&quot;%STYLE%&quot; onmouseover=&quot;this.oldbg=this.style.backgroundColor;this.style.backgroundColor='yellow';&quot; onmouseout=&quot;this.style.backgroundColor=this.oldbg;&quot; target=&quot;%TARGET%&quot;&gt;%BULLET%%TITLE%&lt;/a&gt;&lt;div style=&quot;display:none;border:1px solid #ccc;margin:5px;&quot;&gt;%SUMMARY%&lt;/div&gt;" />
		<UserPref name="refresh" display_name="Default Refresh Interval (minutes):" default_value="10" />
		<UserPref name="newwindow" display_name="Open Links In A New Window:" datatype="bool" default_value="true" />
		<UserPref name="altbgcolor" display_name="Alt Row Bg Color:" default_value="#f6f6f6" />
		<UserPref name="rewritegoogle" display_name="Rewrite Google Urls:" datatype="bool" default_value="true" />
		<UserPref name="emptymessage" display_name="Message to display when no new items:" default_value=""/>
		<UserPref name="disabled" display_name="Disable:" datatype="bool" default_value="false" />
		<UserPref name="hideempty" display_name="Hide Feeds With No New Items:" datatype="bool" default_value="false" />
		<UserPref name="seen" display_name="Recent URLs (Advanced, don't edit manually!):"/>

		<UserPref name="titles" display_name="Feed Titles (Advanced, don't edit manually!):"/>
		<UserPref name="nums" display_name="Feed Item Count (Advanced, don't edit manually!):"/>
		<UserPref name="refreshes" display_name="Feed Refreshes (Advanced, don't edit manually!):"/>
		<UserPref name="bullets" display_name="Feed Bullets (Advanced, don't edit manually!):"/>
		<UserPref name="templates" display_name="Feed Templates (Advanced, don't edit manually!):"/>
		<UserPref name="cachefixes" display_name="Avoid Caching (Advanced, don't edit manually!):"/>
		<Content type="html-inline"> 
		<![CDATA[ 
<style>
.customRssButton {
	float:right;
	padding:0 1px;
	margin-right:5px;
	font-size:10px;
	height:12px;
	border:1px solid #aaa;
	text-decoration:none;
	z-index:10;
	position:relative;
}
.customRssButton:hover {
	background-color:black;
	color:white !important;
}
.customRSSTable {
	border:1px solid #ccc;
}
.customRSSTable td, .customRSSTable th  {
	border:1px solid #ccc;
}
</style>
<script>
// On load of the page, go in and clean up all RSS feeds
_IG_RegisterOnloadHandler(function(){
	if (!customRssPrefs.getBool('disabled')) {
		var feedIds = [];
		for (var i in window) {
			if (/FEED(\d+)/.test(i)) {
				var id = RegExp.$1;
				feedIds.push(id);
			}
		}
		if (feedIds.length==0) {
			// If there are no RSS feeds this might be IE, so look for them the hard way
			var divs = document.getElementsByTagName('DIV');
			for (var i=0,L=divs.length; i<L; i++) {
				var div = divs[i];
				var id = div.id;
				if (/^m_(\d+)$/.test(id)) {
					var id = RegExp.$1;
					if (window['FEED'+id]) {
						feedIds[feedIds.length] = id;
					}
				}
			}
		}
		for (var i=0,L=feedIds.length; i<L; i++) {
			var id = feedIds[i];
			var f = window['FEED'+id];
			if (f && f.url && f.num_items) {
				var rss = window['CFEED'+id] = new CustomizedRSSFeed(id,f.url);
				rss.num = f.num_items;
				// If the feed has an init method, clear it so it doesn't try to refresh
				if (typeof f.init=="function") {
					f.init = function() { }
				}
				if (typeof f.render=="function") {
					f.render = function() { }
				}
				rss.addControls();
				rss.startRefresh(); 
				rss.load();
			}
		}
		// Customize each feed's settings
		var c = 'Changes made below will be auto-saved immediately when fields are changed. Refresh to see the changes.<br>';
		for (var i=0; i<customRSSFeedObjects.length; i++) {
			var f = customRSSFeedObjects[i];
			var gform = _gel("m_"+f.id+"_form");
			var container = gform.parentNode;
			var row = '<table class="customRSSTable" cellspacing=0 cellpadding=1>';
			row += '<tr><th>Url:</th><td>'+f.url+'</td></tr>';
			row += '<tr><th>Title:</th><td><input name="rsstitle%i%" size="20" value="'+prefsGetObjectValue(customRSSFeedTitles,f.shorturl)+'" onchange="prefsSaveObject(\'titles\',customRSSFeedTitles,\''+f.shorturl+'\',this.value)"></td></tr>';
			row += '<tr><th>Num Items:</th><td><input name="rssnum%i%" size="3" value="'+prefsGetObjectValue(customRSSFeedNums,f.shorturl)+'" onchange="prefsSaveObject(\'nums\',customRSSFeedNums,\''+f.shorturl+'\',this.value)"></td></tr>';
			row += '<tr><th>Refresh Interval (min):</th><td><input name="rssrefresh%i%" size="3" value="'+prefsGetObjectValue(customRSSFeedRefreshes,f.shorturl)+'" onchange="prefsSaveObject(\'refreshes\',customRSSFeedRefreshes,\''+f.shorturl+'\',this.value)"></td></tr>';
			row += '<tr><th>Avoid Caching:</th><td><select name="rssavoidcache%i%" onchange="prefsSaveObject(\'cachefixes\',customRSSFeedCacheFixes,\''+f.shorturl+'\',this.options[this.selectedIndex].value)">';
			var selCache = customRSSFeedCacheFixes[f.shorturl];
			row += '<option value="false">No</option>';
			row += '<option '+(selCache=="true"?"selected ":"")+'value="true">Yes</option>';
			row += '</select></td></tr>';
			row += '<tr><th>Item Bullet:</th><td><select name="rssbullet%i%" onchange="prefsSaveObject(\'bullets\',customRSSFeedBullets,\''+f.shorturl+'\',this.options[this.selectedIndex].value)">';
			var selBul = customRSSFeedBullets[f.shorturl];
			row += '<option value="none">Default</option>';
			row += '<option '+(selBul=="arrow"?"selected ":"")+'value="arrow">&#187;</option>';
			row += '<option '+(selBul=="bullet"?"selected ":"")+'value="bullet">&#8226;</option>';
			row += '<option '+(selBul=="favicon"?"selected ":"")+'value="favicon">Site\'s Favicon</option>';
			row += '</select></td></tr>';
			row += '<tr><td colspan="2" align="center"><input type="button" value="Close" onclick="_cedit(\''+f.id+'\')"></td></tr>';
			row += '</table><br>';
			row = row.replace("%i%",i);
			gform.style.display="none";
			container.innerHTML += c+row;
		}
	}
});

var customRssPrefs = new _IG_Prefs(__MODULE_ID__);

// Get a preference which is delimited by |, and each item contains key!value
// Put it into an object
function prefsGetObject(prefName) {
	var o = {};
	var pref = customRssPrefs.getString(prefName);
	if (pref) {
		pref = pref.split("|");
		for (var i=0; i<pref.length; i++) {
			var keyvalue = pref[i].split("!");
			if (keyvalue && keyvalue.length==2) {
				o[keyvalue[0]] = keyvalue[1];
			}
		}
	}
	return o;
}
// Save a pref object
function prefsSetObject(prefName,o) {
	var pref = "";
	for (key in o) {
		if (pref!="") { pref += "|"; }
		pref += key+"!"+o[key];
	}
	customRssPrefs.set(prefName,pref);
}
function prefsSaveObject(prefName,o,key,val) {
	o[key] = val;
	prefsSetObject(prefName,o);
}
function prefsGetObjectValue(o,key) {
	if (o[key]) {
		return o[key];
	}
	return "";
}

// Get a map of the last seen item for each feed url
var lastSeenUrlByFeedUrl = prefsGetObject("seen");
var customRSSFeedTitles = prefsGetObject("titles");
var customRSSFeedNums = prefsGetObject("nums");
var customRSSFeedRefreshes = prefsGetObject("refreshes");
var customRSSFeedBullets = prefsGetObject("bullets");
var customRSSFeedTemplates = prefsGetObject("templates");
var customRSSFeedCacheFixes = prefsGetObject("cachefixes");

// Keep track of all custom RSS feeds
var customRSSFeedObjects = [];

// The custom RSS Feed object
function CustomizedRSSFeed(mId,url) {
	var me = this;
	var p = customRssPrefs;
	this.url = url;
	this.shorturl = this.url.replace("http","").replace(/\W/g,"");
	this.id = mId;
	this.bullet = p.getString('bullet');
	this.rewritegoogle = p.getBool('rewritegoogle');
	this.TPL_entry = p.getString('itemformat');
	this.hideempty = p.getBool('hideempty');
	this.entries = null;
	this.num=3;
	this.loaded=false;
	this.newLastSeen = null;
	this.refreshInterval = customRSSFeedRefreshes[this.shorturl];
	if (!this.refreshInterval) {
		this.refreshInterval = p.getInt('refresh');
	}
	
	this.msg = function(txt) { _gel("m_"+mId+"_b").innerHTML = txt; };
	this.startRefresh = function() { this.interval = setInterval(function(){me.load()}, 60000 * this.refreshInterval); };

	this.addControls = function() {
		var after = document.getElementById('DD_tg_'+mId);
		if (after && after.parentNode && after.parentNode.insertBefore && after.nextSibling) {
			// Clear button
			var cb = document.createElement("A");
			cb.href="#";
			cb.title="Click to mark all items as read and clear the list";
			cb.onclick = new Function("CFEED"+mId+".clear();return false;");
			cb.innerHTML = "Clear";
			cb.className = "customRssButton";
			// Refresh Button
			var rb = document.createElement("A");
			rb.href="#";
			rb.title="Click to refresh now";
			rb.onclick = new Function("CFEED"+mId+".refresh();return false;");
			rb.innerHTML = "Refresh";
			rb.className = "customRssButton";
			// Options Button
			var ob = document.createElement("A");
			ob.href="#";
			ob.onclick = new Function("_edit("+mId+",null);_IG_DD_hide();return false;");
			ob.innerHTML = "?";
			ob.className = "customRssButton";
			after.parentNode.insertBefore(cb,after.nextSibling);
			after.parentNode.insertBefore(rb,after.nextSibling);
			after.parentNode.insertBefore(ob,after.nextSibling);
		}
	}
	this.setTitle = function(title) {
		_gel("m_"+mId+"_title").innerHTML = customRSSFeedTitles[this.shorturl] || title;
	}
	this.load = function() {
		this.msg("Refreshing...");
		var url = this.url;
		if (customRSSFeedCacheFixes[this.shorturl]=="true") {
			url += (url.indexOf("?")>-1)?"&":"?";
			url += "random="+new Date().getTime();
		}
		var num = customRSSFeedNums[this.shorturl];
		if (!num) { num = this.num; }
		_IG_FetchFeedAsJSON(url,function(){me.render.apply(me,arguments)},num,true);
	};
	this.addEntry = function(index, url, title, summary, date, alternate) {
		var tpl = this.TPL_entry;
		// Re-write google url's to go directory to the news story url
		if (this.rewritegoogle && url.search(/news\.google\.com.*\&url=([^\&]+)/)>=0) { url = unescape(RegExp.$1); }
		
		tpl = tpl.replace(/\%INDEX\%/g, index).replace(/\%ID\%/g, mId).replace(/\%URL\%/g, _hesc(url));
		var style = p.getString("linkstyle");
		if (alternate) { style += ";background-color:"+p.getString("altbgcolor")+";"; }
		var bullet = "";
		if (customRSSFeedBullets[this.shorturl]) {
			bullet = customRSSFeedBullets[this.shorturl];
		}
		else if (this.bullet) {
			bullet = this.bullet;
		}
		if (bullet=="arrow") { bullet="&#187;&nbsp;" }
		else if (bullet=="bullet") { bullet="&#8226;&nbsp;" }
		else if (bullet=="favicon") { 
			if (url.search(/^(https?:\/\/[^\/]+)/)>=0) {
				bullet = '<img src="'+RegExp.$1+'/favicon.ico" border="0" align="absmiddle" style="height:16px;width:16px;" onerror="this.style.visibility=\'hidden\'">&nbsp;';
			}
		}
		tpl = tpl.replace(/\%BULLET\%/g,bullet).replace(/\%STYLE\%/g, style).replace(/\%TITLE\%/g, _hesc(title)).replace(/\%TARGET\%/g, (p.getBool('newwindow')?"_blank":"") );
		tpl = tpl.replace(/\%SUMMARY\%/g, (summary || ""));
		_gel("m_"+mId+"_b").innerHTML += tpl;
		return tpl;
	};
	this.render = function(obj) {
		if (!this.loaded) {
			if (obj && obj.ErrorMsg && obj.ErrorMsg != "") {this.retry(obj.ErrorMsg);return;}
			else if (!obj || !obj.Entry || obj.Entry.length == 0) { this.retry("Information is temporarily unavailable. Will retry...");return;}
		}
		if (obj.Entry) {
			this.setTitle(obj.Title);
			_gel("m_"+mId+"_b").innerHTML = "";
			var count = 0;
			var hide = false;
			var oldLastseen = lastSeenUrlByFeedUrl[this.shorturl];
			this.newLastSeen = obj.Entry[0].Link;
			for (var i = 0; i < obj.Entry.length; i++) {
				if (!hide && oldLastseen && obj.Entry[i].Link==oldLastseen) {
					hide = true;
				}
				if (!hide) {
					this.addEntry(i,obj.Entry[i].Link,obj.Entry[i].Title,obj.Entry[i].Summary,obj.Entry[i].Date,!!(count++%2==1));
				}
			}
			if (count==0) {
				this.msg(p.getString("emptymessage"));
				if (this.hideempty) {
					_gel("m_"+mId).style.display="none";
				}
			}
			else {
				_gel("m_"+mId).style.display="block";
			}
		}
		this.loaded = true;
	};
	this.refresh = function() {
		clearInterval(this.interval);
		this.startRefresh();
		this.load();
	}
	this.clear = function() {
		lastSeenUrlByFeedUrl[this.shorturl] = this.newLastSeen;
		prefsSetObject("seen",lastSeenUrlByFeedUrl);
		this.refresh();
	}
	this.retry = function(msg) {
		if (msg) {this.msg(msg);}
		clearInterval(this.interval);
		setTimeout(function(){me.load()},10*1000);
	};
	
	// Add this RSS feed to list
	customRSSFeedObjects.push(this);
}
</script>
<div id="customRSSFeedContent">The "Edit Settings" content for each RSS feed on the page has been changed to provide more options. Customize each RSS feed by editing its settings. Default settings for all feeds can be found in the settings for this gadget. Minimize this gadget to hide this message in the future.</div>
     ]]> 
   </Content> 
</Module>

