var PostStack = function(options) {
	// allow creating without options - can call init(options) later
	if (options) {
		
		// if options is a node or jquery object, use it to find the related PostStack instance
		if (options instanceof Element || options instanceof jQuery) {
			return $(options).closest('.PostStack').data('PostStack');
		}

		// must be an options block, so initialize
		this.init(options);
	}
};

PostStack.prototype = {
	defaults: {
		// options that will be passed to the stream
		streamOptions: {},
		
		// filter data - shortcut that will be injected into streamOptions
		filter_data: false,
		
		reverse_order: false,
		
		// selector of root element
		root: '#stream_content',
		
		// classname added to root
		className: 'post-stack',
		
		// if set, destroy older posts so that there are never more than [max_posts] posts
		//max_posts: 200,
		
		// if set, this is a selector of an element that would like a 'pause' class added when paused
		pause_selector: '#stream_status',
		
		// if set, this is a selector of an element that would like a 'loading' class added while loading
		loading_selector: false,
		
		// the selector of a jQuery template to use for rendering (used in default renderer)
		template_selector: '#tmpl-post',
		
		// allow a button at the end of the stack which retrieves more posts
		get_more: true,

		get_more_handler: PostStack.prototype.get_more,
		
		sort_tweets: true,
		
		// flag whether to use post actions or not
		postactions: true,
		
		// a function to filter whether a post should be included or not function(post){return true;}
		filter: null,
		
		// should we enable filters on this stack?
		filters: false,
		
		// do you want a reply dropdown with advanced options?
		reply_dropdown: false,
		
		// overridable function that renders an individual post
		render: function(post) {
			return this.render(post);
		},
		
		// overridable function that renders a 'get more' button
		render_get_more: function(){
			return $('<button>More</button>');
		},

		// overridable update callback, called when the stack is updated (after a bunch of renders)
		update: function(){},
		
		no_result_msg: '- No Result -',
		
		no_result: function(){
			var $el = this.element();
			$el.children(':not(.friends_eve)').remove();
			$el.append('<div class="no-result">' + this.options.no_result_msg + '</div>');
		},
		
		// called whenever we start or stop loading stuff
		loading_start: function(){
			if (this.options.loading_selector) {
				$(this.options.loading_selector).toggleClass('loading', true);
			}
		},
		
		loading_stop: function(){
			if (this.options.loading_selector) {
				$(this.options.loading_selector).toggleClass('loading', false);
			}
		},
		
		login: function() {
			// try to auto-detect Popup in outermost frame
			// if there, display the "not logged in" popup
			if (top.Popup) {
				top.Popup.open({type:'not_logged'});
			}
			
			// throw an error, so whatever they were trying to do doesn't happen (eg. posting a tweet)
			throw "Login required";
		},
		
		login_required: function(){
			if (!window.logged_usr || window.logged_usr.id <= 0) {
				this.options.login();
			}
		},
		
		// overridable - can be a function like: function(word){ return 'wordclass1 wordclass2' }
		// then every word will be wrapped in <span class="wordclass1 wordclass2">
		word_class: null
	},
	
	options: {},
	stream: null,
	posts: [],

	max_days: 800, // no more than 800 days in the past

	daysAgo: 0, // used for digging back in time

	filter_data: null, // most recent filter data
	extra_filter_data: {}, // extra stuff that will be merged when searching
	
	boundary: null, // keep track of how early to look (ie. what's the earliest timestamp we know about)
	
	regx: {
		nonChar: /\W/g,
		whitespace: /\s+/,
		url: /^https?:\/\//i
	},
	
	init: function(options){
		var self = this;

		// destroy, if already initialized
		if (this.stream) {
			this.destroy();
		}

		this.options = $.extend(true, {
			streamOptions: {
				// update callback, called whenever new posts are received
				// set here because we need the 'self' closure
				update_callback: function(posts, callbackData){
					self.set_loading(false);
					self.add_posts(posts, callbackData);
				}
			}
		}, this.defaults, options);
		
		if (this.options.root) {
			var $element = this.element();
			
			// if there is already a stack on this element, destroy it
			var old_stack = $element.data('PostStack');
			if (old_stack) old_stack.destroy();
			
			// empty the container
			// add the class name
			// store this stack in the element data
			$element.empty().addClass('PostStack').addClass(this.options.className).data('PostStack', this);
	
			// add live events to the root
			this.add_live_events();
	
			// initialize post actions if requested
			if (this.options.postactions) {
				this.init_postactions(this.options.root);
			}
			
			// initialize filters if requested
			if (this.options.filters) {
				this.init_filters();
			}
		}

		this.set_loading(true);
		
		// keep track of whether we're waiting on 'getting more' - so we only do one at a time
		this.getting_more = false;
		
		this.init_stream(this.options.filter_data);
	},

	// stop updating, clear any data, abort any current requests, destroy html
	destroy: function(){
		if (this.stream) {
			this.stream.destroy();
		}
		if (this.options.root) {
			this.element().empty().data('PostStack', false).unbind('.PostStack').removeClass('PostStack').removeClass(this.options.className);
		}
		this.stream = null;
	},
	
	element: function(){
		return $(this.options.root);
	},
	
	check_element: function(){
		// check if our root element is still there & still a child of the body. if not, self destruct.
		if (this.options && this.element().closest('body').length === 0) {
			this.destroy();
			return false;
		}
		return true;
	},
	
	add_live_events: function(){
		var $element = this.element();
		
		$element.unbind('.PostStack');
		
		$element.bind('focus.PostStack', function(){
			PostStack(this).pause();
		});
		$element.bind('blur.PostStack', function(){
			PostStack(this).play();
		});
		
		$element.delegate('.get-more', 'click.PostStack', function(e){
			PostStack(this).get_more();
		});
	},
	
	filter: function(posts) {
		if (!posts || !(this.options && this.options.filter)) return posts;
		
		var result = [];
		
		for (var i=0;i < posts.length;i++) {
			// run the user's custom filter function, which returns true (add) or false (ignore)
			if (posts[i] && this.options.filter(posts[i])) {
				// ok yes we want this post
				result[result.length] = posts[i];
			}
		}
		
		return result;
	},
	
	add_posts: function(posts, callbackData) {
		// make sure the root element still exists
		if (this.options.root && !this.check_element()) return;

		debug.log(posts.length + ' passed to add_posts');

		// filter posts with optional filter function
		posts = this.filter(posts);

		// merge these posts into this.posts
		var num_merged = this.merge_posts(posts);

		// if there are no posts at all, trigger no_result callback
		if (this.posts.length === 0) {
			return this.options.no_result.apply(this);
			
		// also if we're getting more and there are no results, trigger callback
		// but DO NOT trigger callback if we check for new posts and get no result
		} else if (callbackData && callbackData.getmore && num_merged === 0) {
			return this.options.no_result.apply(this);

		} else if (this.options.root) {
			this.element().find('.no-result').remove();
		}

		// trim posts, in case there are more than max_posts
		//this.trim_posts();
		
		// redraw the posts - adding in the new ones, removing ones that shouldn't be there
		if (this.options.root) {
			this.redraw_posts();
		}
		
		if (this.options.update) this.options.update.apply(this);
	},
	
	redraw_posts: function() {
		var $root = this.element(),
			$pointer = $root.children('.post_container').eq(0),
			pointer_data = $pointer.data('post'),
			post, $post,
			$before = $([]), $after = $([]);
							
		// we'll loop over the posts in this.posts and compare against the elements already on the page
		// this way we can merge in the new posts
		for (var i=0;i < this.posts.length;i++) {
			post = this.posts[i];

			// if the post and the pointer are the same, it's already on the page, so skip both
			if (pointer_data && pointer_data.fmsgid === post.fmsgid) {
				$before.insertBefore($pointer);
				$before = $([]);
				
				$pointer = $pointer.next();
				pointer_data = $pointer.data('post');

				continue;
			}
			
			// if the posts are not the same, create this post and prepend
			$post = this.options.render.apply(this, [ post ]);
			
			// associate the post data with the post element 
			$post.data('post', post);

			// set an attribute so we can find this post by it's msgid
			$post.attr('data-msgid', post.msgid);

			// insert the post before the pointer (or append to the root if pointer is empty)
			if ($pointer.length) {
				$before = $before.add($post);
			} else {
				$after = $after.add($post);
			}
		}
		
		// append any queued up for the bottom
		$after.appendTo($root);
		
		// remove any posts from the pointer on that remain after looping
		// usually $pointer will be an empty jQuery object by now
		if ($pointer.length) {
			$before.insertBefore($pointer);
			$pointer.nextAll().andSelf().remove();
		}

		// add the 'get more' button, if necessary		
		this.add_get_more();
	},
	
	merge_posts: function(posts) {
		var deduped = [],
			fmsgids = {},
			num_merged = 0;
			
		for (var i in this.posts) {
			fmsgids[this.posts[i].fmsgid] = true;
		}
		
		for (i in posts) {
			// if we don't already have this fmsgid
			if (!fmsgids[posts[i].fmsgid]) {
				// add to the posts array
				this.posts[this.posts.length] = posts[i];
				
				// increment counter
				num_merged++;
			}
		}
		
		if (this.options && this.options.sort_tweets) {
			if(this.filter_data && this.filter_data.sort){
				
			}else{
				// sort by date (possibly in reverse order)
				var reverse = this.options.reverse_order;
				this.posts.sort(function(a, b) {
					if (a.date == b.date) return 0;
			
					if (a.date < b.date) { // if a came before b
						return reverse ? -1 : 1; // a should be below b, unless reversed
					} else { // b came before a
						return reverse ? 1 : -1; // b should be below a, unless reversed
					} 
				});
			}
		}
		
		
		return num_merged;
	},

	oldest_timestamp: function(){
		var num_posts = this.posts.length;
		if (!num_posts) return null;
		
		var oldest_post;
		
		if (this.options.reverse_order) {
			oldest_post = this.posts[num_posts - 1];
		} else {
			oldest_post = this.posts[0];
		}
		
		return oldest_post.time;
	},
	
	earliest_timestamp: function(){
		var num_posts = this.posts.length;
		if (!num_posts) return null;
		
		var earliest_post;
		
		if (this.options.reverse_order) {
			earliest_post = this.posts[0];
		} else {
			earliest_post = this.posts[num_posts - 1];
		}
		
		return earliest_post.time;
	},
	/*
	// return the timestamp we should next use as 'before' to look for more tweets
	get_next_boundary: function(){
		var DAY = 24*60*60,
			b = this.get_boundary(),
			b_round = b - (b % DAY);
		
		// we tried to roll back to midnight
		// if it already was rolled back to midnight, go back another 24 hours
		if (b === b_round) {
			b_round -= DAY;
		}
		
		this.boundary = b_round;
		
		return b_round;
	},
	*/
	// return the timestamp we last used as a boundary
	get_boundary: function(){
		var earliest = this.earliest_timestamp() || Math.round((new Date()).getTime()/1000);

		// use the earliest of the boundary we have, and the earliest post we have
		this.boundary = this.boundary ? Math.min(this.boundary, earliest) : earliest;
		
		return this.boundary;
	},
	
	// return the number of hours this stack has looked for
	num_hours: function(){
		var b = this.get_boundary();
		
		if (!b) return 0;
		
		var now = Math.round(new Date().getTime() / 1000);
		
		return Math.round( (now - b) / (60*60) );
	},
	/*
	// ensure there are never more than [options.max_posts] posts
	trim_posts: function(){
		var max_posts = this.options.max_posts;
		
		// no max_posts set, stop
		if (!max_posts) return;
		
		// within allowed number of posts, stop
		if (this.posts.length <= max_posts) return;
		
		// remove the oldest posts
		//var $posts = this.element().children('.post_container');
		
		if (this.options.reverse_order) {
			// remove from start
			this.posts = this.posts.slice(this.posts.length - max_posts);
		} else {
			// remove from the end
			this.posts = this.posts.slice(0, max_posts);
		}		
	},
	*/
	pause_forced: false,
	pause: function(force){
		if (!force) {
			if (this.pause_forced) return;
		} else {
			this.pause_forced = true;
		}
		
		if (this.options.pause_selector) {
			$(this.options.pause_selector).addClass('pause');
		}
		
		this.stream.stop_update();
	},
	
	play: function(force){
		if (!force) {
			if (this.pause_forced) return;
		} else {
			this.pause_forced = false;
		}

		if (this.options.pause_selector) {
			$(this.options.pause_selector).removeClass('pause');
		}

		this.stream.schedule_update();
	},
	
	get_tile_ids: function() {
		return this.stack.tile_ids;
	},
	
	set_loading: function(loading) {
		this.getting_more = false;

		if (loading) {
			this.options.loading_start.apply(this);
		} else {
			this.options.loading_stop.apply(this);
		}
	},
	
	get_more: function(){
		// don't do anything if we're already 'getting more'. otherwise we end up with a lot of loading processes going on
		if (this.getting_more) return;
		
		var localOptions = {},
			before = this.get_boundary();
		
		// if using filter_data, then figure out the before property
		if (this.stream.options.filter_data) {
			
			// no before? then we don't know where to look!
			if (!before) {
				debug.warn('poststack: Getting more when there is no more to get');
				return;
			}
			
			// we'll need to set a before time and look before that
			localOptions.filter_data = { before: before };
		
			// do not add since time when retrieving older posts
			localOptions.add_since_time = false;
		}
		
		// increase limit to at least 20 more than the number of posts we have
		localOptions.limit = this.posts.length + 20;

		this.set_loading(true);
		this.stream.load(null, localOptions, { getmore: true });
		this.getting_more = true;
	},
	
	add_get_more: function(){
		// if get_more is false, don't add this
		if (!this.options.get_more) return;

		var $element = this.element(),
			$get_more = $element.children('.get-more');
		
		/*
		// if we're already at the max_posts, don't add this anymore
		if (this.options.max_posts && this.posts.length >= this.options.max_posts) {
			// remove any existing get more button
			$get_more.remove();
			return;
		}
		*/
		
		if (!$get_more.length) {
			$('<div class="get-more"/>')
				.append( this.options.render_get_more() )
				.appendTo( $element );
		}
	},
	
	remove_get_more: function(){
		this.element().children('.get-more').remove();
	},
	
	link: function(url, maxlen) {
		var link_url = url;
		if(maxlen)
			if(url.length > maxlen)
				url = url.substr(0, maxlen-3)+'...';
		var	el = '<a href="'+link_url+'" target="_blank">'+url+'</a>';
		return el;
	},
	
	ajax_error: function(){
		alert('Error connecting to server.');
	},
	
	init_stream: function(filter_data){		
		// destroy stream, and empty stack, if already initialized
		if (this.stream) {
			this.stream.destroy();
		}

		// remove anything inside the root, except the filters
		if (this.options.root) {
			this.element().children().not('.friends_eve').remove();
		}

		// intialize the posts array
		this.posts = [];
	
		// wire up filter_data 'shortcut'
		if (filter_data) {
			// merge in extra filter data at this point, nowhere else
			this.options.streamOptions.filter_data = $.extend({}, filter_data, this.extra_filter_data);
		}
		this.filter_data = filter_data;

		// start the stream, passing any options in the streamOptions parameter
		this.stream = new PostStream(this.options.streamOptions);
	},
	
	init_postactions: function(root){
		var $root = $(root);
		
		// only do this once
		if ($root.data('init_postactions')) return;
		$root.data('init_postactions', true);
		
		PostActions.add_live_events(root);

		// setup global callbacks for some post actions
		PostActions.add_callback('reply', function(el, data, text){
			var from_extid = logged_usr.active.ext_id;
			AjaxMessage.post_reply('reply', data, from_extid, text, null, function(){
				alert('Error sending reply. Please try again later.')
			});
		});
		
		PostActions.add_callback('dm', function(el, data, text){
			var from_extid = logged_usr.active.ext_id;
			AjaxMessage.post_reply('dm', data, from_extid, text, null, function(){
				alert('Error sending DM. Please try again later.')
			});
		});
		
		PostActions.add_callback('login_required', function(el){
			PostStack(el).login_required();
		});
		
		PostActions.add_callback('pause', function(el){
			PostStack(el).pause(true);
		});
		
		PostActions.add_callback('play', function(el){
			PostStack(el).pause(true);
		});
	},
	
	init_filters: function(){
		// this will stay here until stack is re-inited and init_filters is called again
		var filter_data = this.options.filter_data || this.options.streamOptions.filter_data;

       
		// no filter data? then no filters
		if (!filter_data || filter_data.tags) {
			return;
		}

        var filter_source = filter_data.source; 
        
        // must be filter data, so show filters and wire up
		var $filters = $('#stackfilter').tmpl().prependTo(this.element()),
			$stackfilter = $filters.find('.stackfilter');
		
		// don't display filters if source is not twitter
        if (filter_source === 'twitter') {
			new PostStackFilters({
				root: $stackfilter,
				update: function(){
					var stack = PostStack(this.element());
					var new_filter_data = this.filter(filter_data);
				
					stack.init_stream(new_filter_data);
				}
			});
		} else {
			$stackfilter.hide();
		}
		
		/*
		$filters.find(':checkbox').click(function(){
			var stack = PostStack(this);
			var new_filter_data = stack.add_filters(filter_data);
			
			stack.init_stream(new_filter_data);
		});
		*/
		
		$filters.find('.extraFilter .now').click(function(){
			PostStack(this).history_now();
		});
		$filters.find('.extraFilter .newer').click(function(){
			PostStack(this).history_newer();
		});
		$filters.find('.extraFilter .older').click(function(){
			PostStack(this).history_older();
		});

		$filters.find('.extraFilter input').datepicker({
			dateFormat: 'yy-mm-dd',
			onSelect: function(dateText, inst) {
				var selected = $(this).datepicker('getDate').getTime() / 1000,
				 	now = new Date().getTime() / 1000,
					daysAgo = Math.round((now - selected) / 86400) - 1;
				$(this).val(daysAgo);
				PostStack(this).history_days(daysAgo);
				
			}
		});
	
		$filters.find('.extraFilter input').keypress(function(event) {
			if (event.keyCode == '13') {
				var days = parseInt($(this).val());
				
				PostStack(this).history_days(days);
			}
		});
        
        //RRL: to simulate enter key press on input
		$filters.find('.extraFilter input').bind("update_content", function(){
				var days = parseInt($(this).val());
				PostStack(this).history_days(days);
		});

	/*
		// only allow authority when in 'everywhere' mode
		var hasAuthority = true; //(filter_data.city || filter_data.country) ? false : true;
		$filters.find('.authority_filter').attr('disabled', !hasAuthority).closest('.filter_opt').toggleClass('disabled', !hasAuthority);
		
		// only allow champion when in a community
		var hasChampion = (window.Banner && Banner.community.champion_extid || filter_data.bio);
		$filters.find('.champion_filter').attr('disabled', !hasChampion).closest('.filter_opt').toggleClass('disabled', !hasChampion);
	*/	

		this.updateAgoValue();
	},
	
	history_days: function(days){
		this.daysAgo = days;
				
		if(days > this.max_days){
			alert('If you need data older than '+this.max_days+' days ago, please contact us!');
		}else{
			if(days > 0){
				var ts = parseInt(new Date().getTime() / 1000);
				var midnight = ts - (ts % 86400);
				this.extra_filter_data.shard_source = this.filter_data.source;
				this.extra_filter_data.shard_timestamp = midnight - (86400 * days) ;
			}else{
				this.extra_filter_data.shard_source = null;
				this.extra_filter_data.shard_timestamp = null ;
			}
		}

		this.updateAgoValue();
	},
	
	history_now: function(){
		this.daysAgo = 0;
		delete this.extra_filter_data.shard_timestamp;
		
		this.updateAgoValue();
	},
	
	history_newer: function(){
		this.extra_filter_data.shard_timestamp += 86400;
		this.daysAgo = (this.daysAgo > 0 ? this.daysAgo-- : 0);
		
		this.updateAgoValue();
	},
	
	history_older: function(){
		this.daysAgo++;
		
		if(!this.extra_filter_data.shard_timestamp){
			var ts = parseInt(new Date().getTime() / 1000);
			this.extra_filter_data.shard_timestamp = ts - (ts % 86400);
			this.extra_filter_data.shard_source = this.filter_data.source;
		}
		this.extra_filter_data.shard_timestamp -= 86400;
		
		this.updateAgoValue();
	},
	
	updateAgoValue: function(){
		var $input = this.element().find('.extraFilter input');
		
		if(this.extra_filter_data.shard_timestamp){
			$input.addClass('historyOn');
			var now = parseInt(new Date().getTime() / 1000);
			this.daysAgo = Math.max(0, Math.floor((now - this.extra_filter_data.shard_timestamp) / 86400));
		}else{
			$input.removeClass('historyOn');
		}
		
		$input.val(this.daysAgo);
		
		// if stream already running, re-initialize
		if (this.filter_data && this.stream) {
			this.init_stream(this.filter_data);
		}
	},
	
	stopHistoryLog: function(filter_data){
		this.daysAgo = 0;
		this.extra_filter_data.shard_timestamp = null;
		this.extra_filter_data.shard_source = null;
		this.updateAgoValue();
	},
	
	login_required: function(){
		this.options.login_required.apply(this);
	},
	
	renderText: function(body, classNameCallback){
		var text = '', i, word, className,
			body_words = body.split(this.regx.whitespace);
		
		for (i in body_words){
			word = body_words[i];
			
			// if it looks like a URL, simply link it
			if (this.regx.url.test(word)) {
				text += this.link(word) + ' ';
				continue;
			}

			// see if classNameCallback exists and returns something positive
			if (classNameCallback && (className = classNameCallback(word))) {
				// if so, wrap text in a span
				text += '<span class="' + className + '">' + word + '</span> ';
			} else {
				// otherwise, just use the word
				text += word + ' ';
			}
		}
		
		return text;
		// return body;
	},
	
	render: function(post) {
		var $post = $(this.options.template_selector).tmpl({
			default_avatar: '/templates/iloli/images/comms_icon_friend.png',
			post: post,
			text: this.renderText(post.post, this.options.word_class_callback)
		});

		var uid = post.user.uid || post.uid || post.user.id; 
		var $avatar = $post.find('.avatar');
		
		if(post.avatar)
			$avatar.attr('src', post.avatar);
		
		switch(post.source){
			case 'twitter':
				if (this.options.postactions) {
					var postactions = PostActions.html_avatar_buttons(post, { hasReply: 1, hasRetweet: 1 }, this.options.reply_dropdown);
					$avatar.before(postactions);
				}
				
				if (post.freply) {
					$post.find('.extras').before('<span class="thread_icon stack-button-threads" title="Expand the Thread"></span>');
				}
				
				$post.find('.user').html('<a href="http://twitter.com/'+uid+'" target="_blank">@' + uid + '</a>');

				break;

			case 'blogs': // lots of synonyms for "blogs"
			case 'spinner':
			case 'blogsearch':
			case 'boardreader':
				$('<a target="_blank" class="linkback"></a>').attr('href', post.linkback || post.id).text(post.user.id || post.id).appendTo( $post.find('.user') );

				break;

			// these apparently don't allow embedding
			case 'youtube':
				$('<a target="_blank" class="linkback" data-width="425" data-height="349">www.youtube.com</a>')
					.attr('href', post.linkback.replace('watch?v=', 'embed/'))
					.appendTo( $post.find('.user') );

				break;

			case 'flickr':
				$('<a target="_blank">www.flickr.com</a>').attr('href', post.linkback).appendTo( $post.find('.user') );

				break;
			case 'facebook':
				$post.find('.user').text(post.user.name);

				break;

			default:
		}
		
		if(post.retweets >= 50) {
			//ignore if 0
			$post.find('.retweets').text(post.retweets + ' retweets');
		} else {
			$post.find('.retweets, .rt_analytics').remove();
		}

		if($.inArray(post.source, ['facebook', 'boardreader']) > -1){
		  debug.log("FB post:", post);
			var fburl = 'http://www.facebook.com/plugins/like.php?href='+post.linkback+'&amp;layout=button_count&amp;show_faces=false&amp;width=47&amp;action=like&amp;colorscheme=light&amp;height=21';
			$('<iframe class="fblike" src="'+fburl+'" scrolling="no" frameborder="0" allowTransparency="true"></iframe>').insertAfter($post.find('.user'));
		}else{
			$post.find('.rt_this').remove();
		}
		
		
		var $getFDegree = $post.find('.degree-td');
		if (post.user.numeric_id) {
			$getFDegree.attr('numeric_id', post.user.numeric_id);
		} else {
			$getFDegree.remove();
		}
		/*
		if(post.metadata){
			if(post.metadata.klout)
				$post.find('.kloutScore').text('K:'+parseInt(post.metadata.klout));
		}
		if(post.freply)
			$('<span/>').addClass('getThread').attr('title', 'See Conversation').insertAfter($post.find('.user'));
		*/
		
		
		$post.data('data', post);
		
		// fix up any twitter links to just go to live site in a new tab
		$post.find('a.twitter').attr({
			target: '_blank',
			href: function(i, href){
				return 'http://twitter.com/' + href.replace(/^#/, '');
			}
		});
		
		if(post.date || post.post_date){
			$post.find('.posted-time').attr('title', $.timeago.parseDate(post.date || post.post_date));	//post.time
			$post.find('.ago').attr('data-timestamp', post.date || post.post_date).timeago();
		}

		return $post;
    }/*,
	
	add_filters: function(filter){
		// if filters aren't turned on, don't do anything
		if (!this.options.filters) return filter;
		
		// make a clone of the filter data
		var $filters = this.element().children('.friends_eve'),
			filter_data = $.extend({}, filter),
			gender_male = $filters.find('.male_filter').is(':checked'),
			gender_female = $filters.find('.female_filter').is(':checked'),
			sentiment_p = $filters.find('.positive_filter').is(':checked'),
			sentiment_n = $filters.find('.negative_filter').is(':checked'),
			retweets = $filters.find('.rts_filter').is(':checked'),
			champion = $filters.find('.champion_filter').is(':checked'),
			authority = $filters.find('.authority_filter').is(':checked');
			
		if (gender_male && !gender_female) {
			filter_data.gender = 'm';
		} else if (gender_female && !gender_male){
			filter_data.gender = 'f';
		}
		
		if (sentiment_p && !sentiment_n) {
			filter_data.sentiment = 'p';
		} else if (sentiment_n && !sentiment_p) {
			filter_data.sentiment = 'n';
		}
		
		if (retweets) {
			filter_data.sort = 'retweets';
			filter_data.retweets = true;
		}
		
		// only use this if there is a champion_extid available from the Banner community dropdown
		if (champion && Banner.community.champion_extid){
			var post = filter_data.post;
			filter_data = {};
			filter_data.post = post;
			filter_data.source = 'twitter';
			filter_data.relationship = ['friends'];
			filter_data.extid = [Banner.community.champion_extid];
		}

		if (authority) {
			filter_data.authority = 1;
		}
		
		return filter_data;
	}
	*/	
};

