/**
 * TwitterFeed
 * A dynamic tweet display engine.
 * Downloads hundreds of tweets, then spoon-feeds them individually to the user.
 * 
 * REQUIRES: jQuery 1.3.2
 * REQUIRES: jQuery JSONP 1.1.0
 * REQUIRES: CanoeUtils 1.0
 * REQUIRES: TwitterSearch 1.0
 * 
 * @author      Robert Arkwright <robert.arkwright@canoe.ca>
 * @since 		December 16th 2009
 * @version		2.0
 *  
 * @param	object	options		An object containing set-up options/configuration parameters. Object parameters include:
 * 								adRefresh		: (function) (optional) A callback function triggered every time advertisements should be refreshed (the callback should handle the ad refresh specifics). 
 * 								autoAbort		: (bool) When true, polling the Twitter API for fresh tweets will cease after several fruitless responses. Default is true.
 * 							 	configJSON		: (string) A URL to the configuration JSON file, which contains additional setup options.
 *								feedTitle		: (string) The title of this TwitterFeed. Will be displayed prominently.
 *								feedImage		: (string) (optional) A URL to an image which will be displayed in the header. 119x90 pixels.
 *								id				: (string) The value of ID attribute, of the HTML element which the TwitterFeed will be created inside.
 *								lang			: (string) (optional) An ISO 639-1 language code. Only tweets matching this language will be displayed. Default is 'en'.
 *								minQueueSize	: (int) (optional) The minimum number of tweets to keep cached in case of a Twitter API failure. Default is 300.
 *								query			: (string) The query search terms to be matched by the Twitter API.
 *								updateSpeed		: (int) (optional) The frequency at which the Twitter API is polled for fresh tweets. In milliseconds. Default is 60000.
 */
function TwitterFeed(options){this.AD_REFRESH_RATE=30000;this.DISPLAY_SPEED=3000;this.NEW_TWEET_ANIM_SPEED=1000;this.adRefreshInterval=null;this.badWordRegEx=null;this.configJSON=null;this.destroyed=false;this.displayInterval=null;this.elements={};this.loadingComplete=false;this.options=options;this.paused=false;this.selectors={"allTweets":"div#"+options.id+" div.tweets div.tweet","newestTweet":"div#"+options.id+" div.tweets div.tweet:first","statusMessages":"div#"+options.id+" div.tweets div.statusMessages"};this.tagEndCharRegEx=new RegExp("[^0-9A-Za-z/]+$","i");this.tweetsDisplayed=0;this.tweetsReceived=false;this.twitterSearch=null;this.buildHTML();this.displayMessage("loading");if(typeof this.options.adRefresh==="function"){this.adRefreshInterval=setInterval(this.options.adRefresh,this.AD_REFRESH_RATE)}var ajaxRequest=new Object();if(this.options.configJSON!=""){ajaxRequest.url=this.options.configJSON;ajaxRequest.dataType="json";ajaxRequest.success=CanoeUtils.scope(this,this.configAjaxSuccess);ajaxRequest.error=CanoeUtils.scope(this,this.configAjaxError);jQuery.ajax(ajaxRequest)}this.twitterSearch=new TwitterSearch({autoAbort:this.options.autoAbort,error:CanoeUtils.scope(this,this.twitterAPIError),lang:this.options.lang,minQueueSize:this.options.minQueueSize,query:this.options.query,success:CanoeUtils.scope(this,this.twitterAPISuccess),updateSpeed:this.options.updateSpeed})}TwitterFeed.prototype.badWordTest=function(word){var match=word.search(this.badWordRegEx);return match};TwitterFeed.prototype.bindUIHandlers=function(){this.elements.clearButton.bind("click",CanoeUtils.scope(this,this.clearClickHandler));this.elements.playButton.bind("click",CanoeUtils.scope(this,this.playClickHandler));this.elements.pauseButton.bind("click",CanoeUtils.scope(this,this.pauseClickHandler))};TwitterFeed.prototype.buildHTML=function(){var imageHTML="";var titleHTML="";this.elements.twitterFeed=jQuery("div#"+this.options.id);this.elements.twitterFeed.empty();titleHTML=this.options.feedTitle.substring(0,8);if(titleHTML.toLowerCase()==="twitter:"){titleHTML="<strong>"+titleHTML+"</strong>"+this.options.feedTitle.substring(8)}else{titleHTML=this.options.feedTitle}if(this.options.feedImage!=""&&typeof this.options.feedImage!=="undefined"){imageHTML='<div class="headlinePhoto"><img src="'+this.options.feedImage+'" alt="" /></div>'}var newHTML="";newHTML+='<div class="twitterHead"><h2>'+titleHTML+"</h2>"+imageHTML+"</div>";newHTML+='<div class="twitterControls">';newHTML+='<a class="tfPlaySelected" href="#">Play</a><a class="tfPause" href="#">Pause</a><a class="tfClear" href="#">Clear</a>';newHTML+="</div>";newHTML+='<div class="tweets"></div>';this.elements.twitterFeed.append(newHTML);this.elements.head=jQuery(this.elements.twitterFeed.selector+" div.twitterHead");this.elements.clearButton=jQuery(this.elements.twitterFeed.selector+" div.twitterControls a.tfClear");this.elements.pauseButton=jQuery(this.elements.twitterFeed.selector+" div.twitterControls a.tfPause");this.elements.playButton=jQuery(this.elements.twitterFeed.selector+" div.twitterControls a.tfPlaySelected");this.elements.tweets=jQuery(this.elements.twitterFeed.selector+" div.tweets")};TwitterFeed.prototype.clearClickHandler=function(){jQuery(this.selectors.allTweets).fadeOut();this.tweetsDisplayed=0;this.elements.clearButton.blur();return false};TwitterFeed.prototype.configAjaxSuccess=function(data,textStatus){this.configJSON=data;this.init()};TwitterFeed.prototype.configAjaxError=function(xhr,textStatus,errorThrown){this.displayMessage("configError")};TwitterFeed.prototype.destroy=function(){clearInterval(this.adRefreshInterval);clearInterval(this.displayInterval);this.twitterSearch.destroy();this.destroyHTML();this.destroyed=true};TwitterFeed.prototype.destroyHTML=function(){this.elements.twitterFeed.empty()};TwitterFeed.prototype.displayMessage=function(type){var messageHTML="";messageHTML+='<div class="statusMessage">';if(type==="loading"){messageHTML+='<p class="first"><strong>Retrieving Twitter Feed</strong></p>';messageHTML+='<span class="loading"></span>'}else{if(type==="configError"){messageHTML+='<p class="first"><strong>An error has occurred!</strong></p>';messageHTML+="<p>We're sorry, important data could not be loaded. Please refresh this page to try again.</p>"}else{if(type==="twitterError"){messageHTML+='<p class="first"><strong>Can\'t connect to Twitter!</strong></p>';messageHTML+="<p>We're sorry, data cannot be loaded from Twitter. Please try this page again in 5 minutes.</p>"}}}messageHTML+="</div>";this.elements.tweets.empty();this.elements.tweets.append(messageHTML).children().hide().fadeIn("slow")};TwitterFeed.prototype.displayTweet=function(){var newestTweet=null;var tweetText="";while(tweet=this.twitterSearch.getTweet()){tweetText=tweet["text"];tweetTime=this.tweetTime(tweet["created_at"]);if(this.badWordTest(tweet["from_user"]+" "+tweetText)==-1){if(this.tweetsDisplayed===0){this.elements.tweets.empty();this.loadingComplete=true}tweetText=this.linkTwitterTags(tweetText);this.elements.tweets.prepend('<div class="tweet"><p><img src="'+tweet["profile_image_url"]+'" alt="" />'+'<a href="http://www.twitter.com/'+tweet["from_user"]+'"><strong>'+tweet["from_user"]+"</strong></a>"+tweetText+'<span class="time">'+tweetTime+"</span>"+"</p></div>");newestTweet=jQuery(this.selectors.newestTweet);newestTweet.css("height",newestTweet.height()).hide().slideDown(this.NEW_TWEET_ANIM_SPEED);this.tweetsDisplayed++;break}}};TwitterFeed.prototype.init=function(){var regEx="";for(var i in this.configJSON["badwords"]){regEx+="|"+this.configJSON["badwords"][i]}regEx=regEx.substring(1);this.badWordRegEx=new RegExp(regEx,"i");this.bindUIHandlers();this.play()};TwitterFeed.prototype.linkTwitterTags=function(text){var returnString="";var words=text.split(" ");for(var i in words){if(words[i].length>1){if(words[i].indexOf("@")>-1){returnString+=" "+this.tagToLink("@",words[i])}else{if(words[i].indexOf("#")>-1){returnString+=" "+this.tagToLink("#",words[i])}else{if(words[i].indexOf("http://")>-1){returnString+=" "+this.tagToLink("http://",words[i])}else{if(words[i].indexOf("www.")>-1){returnString+=" "+this.tagToLink("www.",words[i])}else{returnString+=" "+words[i]}}}}}}return returnString.substring(1)};TwitterFeed.prototype.pause=function(){clearInterval(this.displayInterval);this.paused=true};TwitterFeed.prototype.play=function(){this.displayInterval=setInterval(CanoeUtils.scope(this,this.displayTweet),this.DISPLAY_SPEED);this.paused=false;this.displayTweet()};TwitterFeed.prototype.pauseClickHandler=function(){if(!this.paused&&this.loadingComplete){this.pause();this.elements.playButton.removeClass("tfPlaySelected").addClass("tfPlay");this.elements.pauseButton.removeClass("tfPause").addClass("tfPauseSelected")}this.elements.pauseButton.blur();return false};TwitterFeed.prototype.playClickHandler=function(){if(this.paused){this.play();this.elements.playButton.removeClass("tfPlay").addClass("tfPlaySelected");this.elements.pauseButton.removeClass("tfPauseSelected").addClass("tfPause")}this.elements.playButton.blur();return false};TwitterFeed.prototype.tagToLink=function(tagPrefix,tag){var endChar="";var href="";var prefix="";var prefixIndex=tag.indexOf(tagPrefix);var tagBody="";if(prefixIndex!=0){prefix=tag.substr(0,prefixIndex);tag=tag.substr(prefixIndex)}tagBody=tag.substring(tagPrefix.length);if(tagBody.search(this.tagEndCharRegEx)>-1){endChar=tagBody.charAt(tagBody.length-1);tagBody=tagBody.substr(0,tagBody.length-1)}if(tagPrefix=="@"){href="http://www.twitter.com/"+encodeURIComponent(tagBody)}else{if(tagPrefix=="#"){href="http://search.twitter.com/search?q="+encodeURIComponent(tagPrefix+tagBody)}else{if(tagPrefix=="www."){href="http://"+tagPrefix+tagBody}else{if(tagPrefix=="http://"){href=tagPrefix+tagBody}}}}return prefix+'<a href="'+href+'">'+tagPrefix+tagBody+"</a>"+endChar};TwitterFeed.prototype.tweetTime=function(utcTime){var months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];var tempDate=new Date(utcTime);var hours=tempDate.getHours();var ampm="PM";if(hours>12){hours=hours-12;ampm="PM"}var minutes=tempDate.getMinutes();if(minutes<10){minutes="0"+minutes}var day=tempDate.getDate();var month=months[tempDate.getMonth()];var year=tempDate.getFullYear();return hours+":"+minutes+" "+ampm+" "+month+" "+day+" "+year};TwitterFeed.prototype.twitterAPIError=function(){if(this.tweetsReceived===false&&this.destroyed===false){this.displayMessage("twitterError")}};TwitterFeed.prototype.twitterAPISuccess=function(data,textStatus){this.tweetsReceived=true};
