﻿// parseUri 1.2.2
// (c) Steven Levithan <stevenlevithan.com>
// MIT License
function parseUri (str) {
	var	o   = parseUri.options,
		m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
		uri = {},
		i   = 14;

	while (i--) uri[o.key[i]] = m[i] || "";

	uri[o.q.name] = {};
	uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
		if ($1) uri[o.q.name][$1] = $2;
	});

	return uri;
};
parseUri.options = {
	strictMode: false,
	key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
	q:   {
		name:   "queryKey",
		parser: /(?:^|&)([^&=]*)=?([^&]*)/g
	},
	parser: {
		strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
		loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
	}
};
/* end parseURI */

/* FeedParser
 *   (c) 1992-2010 Cisco Systems, Inc. All rights reserved.
 *   Terms and Conditions: http://cisco.com/en/US/swassets/sw293/sitewide_important_notices.html'
 */
var Days = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ];
var Months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];

var CfgIDSeed = 0;
var ParentIDSeed = 0;

function PipeParser(cfg) {
	var thisVar = this;
	
	var pipeData;
	var domParent;
	var domFeedback;
	var domDebug;
	this.timeoutID = null;

	this.config = {
		'parentid': '',
		'parentid_prefix': 'id-feed-',
		'cfghookid': '',
		'cfghookid_prefix': 'id-cfg-feed-',
		'pipeurl': 'http://pipes.yahoo.com/pipes/pipe.run?_id=d6d35b2eb0829de9bad0d0f40e26050d',
		'feedurl': '',
		'cl_id': '',
		'd_len': 250,
		'max_items': 5,
		'max_items_s12v4': 10,
		'class_once': '.feed-once',
		'class_list': '.feed-list',
		'class_item': '.feed-item',
		'class_title': '.title',
		'class_cite': '.cite',
		'class_date': '.date',
		'class_desc': '.desc',
		'class_feedback': '.feedback',
		'img_loading': 'http://cdc-site-dev.cisco.com/cbonham/smw/loading.gif',
		'debug': false
	};

	this.debug = function (str) {
		if ( this.config.debug ) {
			try {
				domDebug.append(str);
				domDebug.append('<br />');
			} catch (err) { 	
				// do nothing 
			}
		}
		return;
	};
	
	this.isIE6 = function () {
		var out = false;
		this.debug( 'browser: ' + navigator.appName );
		this.debug( 'version: ' + navigator.appVersion);
		if ( (navigator.appName == 'Microsoft Internet Explorer') && (navigator.appVersion.indexOf('MSIE 6.0')) ) {
			out = true;
		}
		this.debug( 'isIE6?: ' + out );
		return out;
	};
	
	this.isS12v4 = function () {
		return ( this.config.cl_id == 's12v4-pilot' ); 
	};
	
	this.initIE6 = function () {
		this.debug( 'init ie6' );
		domParent.find(this.config.class_feedback).html('<div class="ie-upgrade">You are using an old browser, you should upgrade.</div>');
	};

	this.initConfig = function () {
//		thisVar = this;
		
		/* set up the parent div 
		/* can we sniff it? */
		cfghook = jQuery('#' + this.config.cfghookid); 
		domParent = cfghook.parent();
		if ( domParent.attr('id') ) {
			this.config.parentid = domParent.attr('id');
		} else {
			/* Is there any reason I need to put one there? */
		}

		domFeedback = domParent.find( this.config.class_feedback ); 
		
		/* set up debugging */
		if ( cfg && cfg.debug ) {
			this.config.debug = cfg.debug;	
		}		
		/* allows global debug on */
		if ( this.config.debug ) {
			domDebug = jQuery('<div class="debug"></div>');

			var debuga = jQuery('<a class="debug-click">Debug Info: Click to Hide</a>');
			debuga.click( function() { domDebug.css('display', 'none'); } );
			debuga.css('cursor', 'pointer');
			debuga.appendTo(domDebug);		
			
			domParent.append(domDebug);
			domDebug.show();
		}	
		
		/* get the feed url */
		this.config.feedurl = domParent.children('.feed-links').children('.a-sub').attr('href');	
		this.debug('url: ' + this.config.feedurl);
		
		/* get the component id */
		this.config.cl_id = this.parseCLID( domParent.attr('class') )[0];
		
		/* set the max number for the list. twitter gets more */	
		if ( this.isS12v4() ) {
			this.config.max_items = this.config.max_items_s12v4;
		}	
		// allow an override
		if ( cfg && cfg.max_items ) {
			this.config.max_items = cfg.max_items;
		}
		
		if ( cfghook ) {
			this.debug('cfg hook found, id: "' + cfghook.attr('id') + '"' );
		}
		if ( domParent ) {
			this.debug('parent found, id: "' + domParent.attr('id') + '"');			
		}
		this.debug( 'cl id: ' + this.config.cl_id );	
		if ( this.isS12v4() ) { 
			this.debug ( 's12v4 (twitter) detected' ); 
		}
		this.debug( 'max_items: ' + this.config.max_items ); 
		this.debug( 'feedurl: ' + this.config.feedurl );
	};
	
	this.parseCLID = function(str) {
		var rgxStr = String(str);
		out = rgxStr.match(/\b[A-Za-z0-9]+v[0-9]+(\-pilot)?\b/);
		return out;
	};
	
	this.initLoading = function () {
		this.debug( 'init loading' );
		domFeedback.html('<div class="loading"><img src="' + this.config.img_loading + '" /><br />Loading</div>');
	};
	
	this.initCorners = function () {
		this.debug( 'adding corners' );
		cdc.util.addCorners( domParent );
	}; 
	
	this.initPipe = function () {
		var escfeed = escape(this.config.feedurl);	

		var urlPipe = this.config.pipeurl  + '&_render=json&dlen=' + this.config.d_len + '&feed=' + escfeed + '&_callback=?';	

		this.debug('pipe request url: ' + urlPipe);

		try {
			this.debug('send json:');
			// do something if pipes doesn't respond.
			this.timeoutID = setTimeout( function(){ thisVar.timeoutCallback(); }, 20000);
			// make the pipe request.
			jQuery.getJSON( urlPipe, function (data) { thisVar.pipeCallback(data); } );
		} catch (err) {
			this.debug('try failed');	
		} 
		
		this.debug('pipe request sent: timeoutID: ' + this.timeoutID); 
	}; 
	
	this.init = function () {
		this.initConfig();
		this.initCorners();
		this.initLoading();
		this.initPipe();
	};
	
	this.timeoutCallback = function() {
		this.debug('timeoutCallback:');
		this.renderUnavailable('timeout');
	};
	
	this.pipeCallback = function (data) {
		this.debug('pipeCallback:');
		// clear the timeout
		clearTimeout(this.timeoutID);
		
		// render the feed
		this.pipeData = data;
		this.renderFeed(this.pipeData);
	}; 
	
	this.getDate = function (utime) {
		// takes a utime in seconds, returns a formatted date string.
		var newDate = new Date();

		// setTime takes ms, utime contains seconds since epoch.
		newDate.setTime(utime * 1000);

		var day = Days[newDate.getDay()];
		var month = Months[newDate.getMonth()];
		var ampm = 'am';
		var hours = newDate.getHours();
		if (hours >= 12) {
			/* if it is pm, set it. 
			 * subtract 12, so not military time. */
			ampm = 'pm';
			hours = hours - 12;
		}
		if (hours === 0) {
			hours = '12';	
		} 
		var minutes = newDate.getMinutes();
		if (minutes === 0) {
			minutes = '00';
		}
			
		out = day + ', ' + month + ' ' + newDate.getDate() + ', ' + newDate.getFullYear() 
			+ ' ' + hours + ':' + minutes + ' ' + ampm;
		
		//this.debug('local date: ' + newDate.toLocaleString() );
		
		return out; 
	};
	
	this.getUnusedCfgHookID = function () {
		var newid = '';
		var found = false;
		
		/* create an id, check if it already exists in the dom.
		 * keep doing that until you find one that is not being used. 
		 * 
		 * the global vars IDSeed, and this.config.cfghookid_prefix
		 * should prevent collisions resulting from IDs created by 
		 * this script, or other libraries with similar strategy. */
		while ( !found ) {
			newid = this.config.cfghookid_prefix + CfgIDSeed;			
			if ( jQuery('#'+newid).length == 0 ) {
				found = true;		
			} else {
				found = false;
			}		
			CfgIDSeed++
		}	
		return newid;
	}
	this.getUnusedParentID = function () {
		var newid = '';
		var found = false;
		
		/* create an id, check if it already exists in the dom.
		 * keep doing that until you find one that is not being used. 
		 * 
		 * the global vars IDSeed, and this.config.cfghookid_prefix
		 * should prevent collisions resulting from IDs created by 
		 * this script, or other libraries with similar strategy. */
		while ( !found ) {
			newid = this.config.parentid_prefix + ParentIDSeed;			
			if ( jQuery('#'+newid).length == 0 ) {
				found = true;		
			} else {
				found = false;
			}		
			ParentIDSeed++
		}	
		return newid;
	}

	this.paintTwitterTitle = function( str ) {
		var out = String(str);	
		out = out.substring( out.indexOf(':')+1 );	
		return out;	
	};

	this.renderItem = function (div, rssitem, youtube) {
		this.debug("renderItem:");

		// info for icons
		if ( rssitem.site ) {
			var newthumb = rssitem.site;
			this.debug ( '- site: ' + newthumb );
			div.addClass(newthumb);
		}
		
		if ( this.config.cl_id == 's12v4-pilot' ) {
			rssitem.title = this.paintTwitterTitle( rssitem.title );
		}
		
		// get the title tag, or create one if it doesn't exist.
		// this is the only tag we do this for, others are based on dom info.
		var newtitle = div.find(this.config.class_title);
		if ( newtitle.length === 0 ) {
			this.debug('- Title tag needed');
			newtitle = jQuery('<a href="' + rssitem.link + '" class="' + this.config.class_title + '">' + rssitem.title + '</a>');
			newtitle.prependTo(div);
		} else {
			// we found the dom ele, now fill it up.
			if ( newtitle[0].nodeName == 'A' ) {
				// the tag in the dom template is an anchor already
				newtitle.html( rssitem.title );
				newtitle.attr( 'href', rssitem.link );
			} else {
				// it's not an anchor, so just put the title inside.  (twitter)
				newtitle.html( rssitem.title );	
			}		
		}
	
		// add citation
		var newcite = div.find(this.config.class_cite);
		if ( newcite.length !== 0 ) {
			// TODO: make sure this is robust.  Not all feeds come through with source.
			// Google reader does for sure.
			if ( rssitem.source ) {
				this.debug ( '- cite needed:' + rssitem.source.title.content );	
				newcite.html ( rssitem.source.title.content );
			}
		}

		// add date
		var newdate = div.find(this.config.class_date);
		if ( newdate.length !== 0 ) {
			this.debug ( '- date needed:' );
			if ( rssitem['y:published'].utime ) {
				sdate = this.getDate( rssitem['y:published'].utime );
				newdate.html(sdate);
			}
		}
		
		// add description
		var newdesc = div.find(this.config.class_desc);
		if (newdesc.length !== 0) {
			this.debug ( '- description needed:' );
			newdesc.html(rssitem.description);
		}
		
		return;		
	};
	
	this.renderUnavailable = function(err) {
		// remove the loading/noscript feedback.
		this.debug('renderUnavailable');
		domFeedback.html('');
		newp = jQuery('<p class="emptyfeed">This feed is not currently available.  Please try again later. </p><!-- ' + err + ' -->');
/*		arefresh = jQuery('<a href="javascript:void(0)">Refresh</a>');
		arefresh.click( function(){ thisVar.initLoading(); thisVar.initPipe(); } );
		newp.append(arefresh); */
		domFeedback.append(newp);	
	}
	
	this.renderFeed = function (feed) {
		var youtube = false;
		var nItem = 0;
		var newItem;
		
		this.debug('Processing Feed... parent: "' + this.config.parentid + '"');
		
		if ( feed.value.items.length > 0 ) {
		
			// remove the loading gif and it's parent.
			domFeedback.remove();
			
			// find the container for rendering a single item
			// usually this is used to deliver a layout with description
			var divOnce = domParent.find(this.config.class_once);
			if ( divOnce !== 0 ) {
				this.debug('Found ' + divOnce.length + ' once divs.');
				for (var i=0; i < divOnce.length; i++) {
					newItem = jQuery(divOnce[i]).find(this.config.class_item);
					if ( !newItem ) {
						this.debug('Item missing, create item');
						newItem = jQuery('<div class="feed-item"></div>');
						newItem.appendTo(divOnce[i]);
					}
					this.renderItem(newItem, feed.value.items[nItem], youtube);		
					nItem++;	
				}	
			}		

			// find the container to put the list in.
			var divList = domParent.find(this.config.class_list);
			if ( divList.length !== 0 ) {
				
				// create the template for the elements
				// to be used in each rss item
				var tplItem = divList.find(this.config.class_item);
				tplItem.remove();
				
				var newul = jQuery('<ul>');
				divList.append(newul);
				
				// max_items is the max number for the list, not total displayed.
				// accounts for displayOnce stuff.
				for (i=nItem; (i < this.config.max_items + nItem) && (i < feed.value.items.length); i++) {
					// the new list item
					var newli = jQuery('<li></li>');	
	
					// get the dom for the new item
					newItem = tplItem.clone();
					if (i==nItem) {
						newItem.addClass('first');
					}
					this.renderItem(newItem, feed.value.items[i], youtube);		
	
					newli.append(newItem);
					newul.append(newli);
				}		
			}						
		} else {
			this.renderUnavailable('zero entries');
		}
		
		
	};
	
	this.config.cfghookid = this.getUnusedCfgHookID();
	document.write('<div id="' + this.config.cfghookid + '" class="cfghook"></div>');
	jQuery.ready( thisVar.init() );
	
}



