/* * twitTracker v1.0 * http://phishtwit.com/ * * Copyright (c) 2009 Todd Levy * Contact todd@jamtopia.com for usage rights * * Date: 2009-05-21 12:05am GMT-0500 */ function twitTracker(query,refreshWrapDomId,feedListDomId,maxItemsInFeedList, refreshTime) { mylogger.log('FUNCTION: twitTracker'); this.countdownId = ''; this.countdownObj = null; this.timeoutId = null; this.ajaxRequest = ''; this.running = false; this.cache = new Array(); this.query = query; this.refreshWrapDomId = refreshWrapDomId; this.feedListDomId = feedListDomId; this.maxItemsInFeedList = maxItemsInFeedList; this.refreshTime = refreshTime; this.pauseLabel = "Pause"; this.pauseTitle = "Click to pause automatic updates"; this.resumeLabel = "Resume"; this.resumeTitle = "Click to resume automatic updates"; this.filterReplyLinks = false; // *** INITIALIZE THE TRACKER *** this.init = function() { var that=this; // SET RUNNING TO TRUE this.running = true; // SET TOGGLING TO FALSE this.toggling = false; // SET FETCHING TO FALSE this.fetching = false; // SHOW THE COUNTDOWN this.showCountdown(); // UPDATE PAUSE LINK TEXT $(this.refreshWrapDomId + " a.toggleupdates").html(this.pauseLabel).attr("title",this.pauseTitle); // ATTACH TOGGLE HANDLER $(this.refreshWrapDomId + " a.toggleupdates").click(function () {that.toggleUpdates(); return false;}); } // END INIT } // ********************************* // *** TOGGLE AUTO UPDATES *** twitTracker.prototype.toggleUpdates = function() { mylogger.log('FUNCTION: toggleUpdates'); var that=this; if (that.toggling == true) return; that.toggling == true; if (that.running == true){ that.pause(); // PAUSE IF RUNNING }else{ that.resume(); // RESUME IF PAUSED } // TOGGLE RUNNING FLAG that.running = !that.running; } // END TOGGLE AUTO UPDATE // *** PAUSE AUTO UPDATES *** twitTracker.prototype.pause = function() { mylogger.log('FUNCTION: pause'); var that=this; // HIDE LOADER this.hideLoader(); // HIDE TWEET COUNT this.hideNewTweetCount(0); // HIDE NO TWEETS COUNT this.hideNoTweets(0); // IF COUNTDOWN if (this.countdownObj) { // STOP THE COUNTDOWN clearTimeout(this.countdownObj.countdownId); } // CLEAR THE COUNTDOWN ID this.countdownObj = null; // HIDE THE COUNTDOWN this.hideCountdown(); // HIDE THE TOGGLE LINK $(that.refreshWrapDomId + " a.toggleupdates").animate({opacity: 0},500,function () { // APPLY THE RESUME LABEL $(that.refreshWrapDomId + " a.toggleupdates").html(that.resumeLabel).attr("title",that.resumeTitle); // SHOW THE TOGGLE LINK $(that.refreshWrapDomId + " a.toggleupdates").animate({opacity: 1},500); // DONE TOGGLING that.toggling == false; }); } // END PAUSE // ********************************* // *** RESUME AUTO UPDATES *** twitTracker.prototype.resume = function() { mylogger.log('FUNCTION: resume'); var that=this; // HIDE THE TOGGLE LINK $(this.refreshWrapDomId + " a.toggleupdates").animate({opacity: 0},500,function () { // APPLY THE PAUSE LABEL $(that.refreshWrapDomId + " a.toggleupdates").html(that.pauseLabel).attr("title",that.pauseTitle); // SHOW THE TOGGLE LINK $(that.refreshWrapDomId + " a.toggleupdates").animate({opacity: 1},500,function () { // DONE TOGGLING that.toggling == false; // DO A NEW SEARCH that.prepTweetSearch(); }); }); } // END RESUME // ********************************* // *** PREP A SEARCH *** twitTracker.prototype.prepTweetSearch = function() { mylogger.log('FUNCTION: prepTweetSearch'); var that=this; // EXIT IF PAUSED if (this.running == false) return; // FIND LATEST TWEET ID //alert('ul#'+this.feedListDomId + '--' + this.feedListDomId.length); //FIND BEETER WAY TO IDENTIFY LATEST TWEET //alert($(this.feedListDomId).find('li:first').attr('id')); var latestTweetId = $('ul#'+this.feedListDomId).find('li:first').attr('id').substring(this.feedListDomId.length + 7); //alert(latestTweetId); // SET QUERY URL var url = "http://search.twitter.com/search.json?q=" + this.query + "&since_id=" + latestTweetId + "&rpp=15&callback=?"; // SHOW LOADER this.showLoader(function(data){ that.doTweetSearch(data, url); }); // END SHOW LOADER } //END PREP SEARCH // ********************************* // *** DO A SEARCH *** twitTracker.prototype.doTweetSearch = function(data, url) { mylogger.log('FUNCTION: doTweetSearch'); var that=this; var ajaxRequest = $.ajax({ url: url, type : 'GET', dataType : 'json', timeout: 7000, data : data, success : function(data, textStatus){ mylogger.log('ajax success: testStatus = ' + textStatus); clearTimeout(that.timeoutId); ajaxRequest = null; // PARSE THE AJAX RESULTS that.parseResults(data.results) }, error : function(XMLHttpRequest, textStatus, errorThrown){ mylogger.log('ajax error: testStatus = ' + XMLHttpRequest.status + ' - ' + textStatus + ' - ' + errorThrown); clearTimeout(that.timeoutId); ajaxRequest = null; // SHOW COMMUNICATION BREAKDOWN MESSAGE that.showCommunicationBreakdown(); }, complete : function(XMLHttpRequest, textStatus){ mylogger.log('ajax complete: testStatus = ' + textStatus); } }); that.timeoutId = setTimeout(function(){ // ABORT THE AJAX CALL if (ajaxRequest){ mylogger.log('ABORT THE AJAX CALL'); ajaxRequest.abort(); } // PAUSE THE REFRESHER that.running = false; // SHOW THE ERROR MESSAGE that.showCommunicationBreakdown(); }, 7000); //}); // END SHOW LOADER } //END DO SEARCH // ********************************* // *** PARSE RESULTS*** twitTracker.prototype.parseResults = function(data) { mylogger.log('FUNCTION: parseResults'); var that=this; //mylogger.time('array'); if( data ) // Checks to see if you have any new tweets { var i = -1, result, strTweetOutput='', strTweetOutput2='', arrTweetOutput = []; while( (result = data[++i]) ) { var tweet_id = result.id_str; var tweet_li_id = that.feedListDomId + '-tweet-' + result.id_str; var tweet_published = result.created_at; var tweet_published_relative = relative_time(result.created_at); var tweet_author_name = result.from_user; var tweet_author_uri = 'http://twitter.com/' + tweet_author_name; var tweet_uri = 'http://twitter.com/' + tweet_author_name + '/statuses/' + tweet_id; var tweet_avatar = result['profile_image_url']; //var tweet_avatar = "http://img.tweetimag.es/i/" + tweet_author_name + "_n"; var tweet_title = result.text; var tweet_title_encoded = encodeUrl(tweet_title); tweet_published_gmt = tweet_published.replace("+0000","GMT"); var timezone_offset = -5*1000*60*60; var tweet_published_formatted = new Date().formatDate('d M Y, g:i a',Date.parse(tweet_published_gmt)+timezone_offset); //mylogger.log(tweet_title); /// SANITIZE USTREAM LINKS tweet_title = tweet_title.replace("", ""); tweet_title = tweet_title.replace("", ""); /// TWIT PICS var imageRegEx = /(http:\/\/(www.|)+(twitpic.com|img.ly|yfrog.com|twitgoo.com|plixi.com\/p|lockerz.com\/s|instagr.am\/p)\/){1}[A-Za-z0-9_\.-]{1,}/g; var tweet_twitpic_id = ''; var tweet_twitpic_img = ''; var tweet_twitpic_url = ''; //we have to check if an image actually is found in the search //if not we skip this entry if(tweet_title.search(imageRegEx) !== -1) { // regex to find image links imgFind = tweet_title.match(imageRegEx); //alert(imgFind[0].indexOf("http://twitpic.com/")) if(imgFind[0].indexOf("http://twitpic.com/") > -1) { tweet_twitpic_id = imgFind[0].substr(19); tweet_twitpic_img = 'http://twitpic.com/show/thumb/' + tweet_twitpic_id + '.jpg'; tweet_twitpic_url = 'http://twitpic.com/' + tweet_twitpic_id; } if(imgFind[0].indexOf("http://instagr.am/") > -1) { //tweet_twitpic_id = imgFind[0].substr(14); tweet_twitpic_img = imgFind[0] + '/media/?size=t'; tweet_twitpic_url = imgFind[0]; } if(imgFind[0].indexOf("http://img.ly/") > -1) { tweet_twitpic_id = imgFind[0].substr(14); tweet_twitpic_img = 'http://img.ly/show/thumb/' + tweet_twitpic_id; tweet_twitpic_url = 'http://img.ly/' + tweet_twitpic_id; } if(imgFind[0].indexOf("http://yfrog.com/") > -1) { tweet_twitpic_id = imgFind[0].substr(17); tweet_twitpic_img = 'http://yfrog.com/' + tweet_twitpic_id + '.th.jpg'; tweet_twitpic_url = 'http://yfrog.com/' + tweet_twitpic_id; } if(imgFind[0].indexOf("http://twitgoo.com/") > -1) { tweet_twitpic_id = imgFind[0].substr(19); tweet_twitpic_img = 'http://twitgoo.com/' + tweet_twitpic_id + '/thumb/'; tweet_twitpic_url = 'http://twitgoo.com/' + tweet_twitpic_id; } if(imgFind[0].indexOf("http://plixi.com/p/") > -1) { tweet_twitpic_id = imgFind[0].substr(19); tweet_twitpic_img = 'http://api.plixi.com/api/tpapi.svc/imagefromurl?size=thumbnail&url=http://plixi.com/' + tweet_twitpic_id; tweet_twitpic_url = 'http://plixi.com/' + tweet_twitpic_id; } if(imgFind[0].indexOf("http://lockerz.com/s/") > -1) { tweet_twitpic_id = imgFind[0].substr(19); tweet_twitpic_img = 'http://api.plixi.com/api/tpapi.svc/imagefromurl?size=small&url=http://lockerz.com/' + tweet_twitpic_id; tweet_twitpic_url = 'http://lockerz.com/' + tweet_twitpic_id; } } tweet_title = FormatIt(tweet_title); strTweetOutput += '
'; arrTweetOutput.push(strTweetOutput); strTweetOutput = ''; } // END LOOP if (that.running == false) return; strTweetOutput = arrTweetOutput.join(''); if (that.running == false) return; if( strTweetOutput !== '' ) { // SHOW ALL NEW TWEETS $('ul#'+that.feedListDomId).prepend( strTweetOutput ) // EXTERNAL LINKS $("a",$('ul#'+that.feedListDomId)).filter(function() { if ($(this).attr('rel') == 'samewindow') return false; return this.hostname && this.hostname !== location.hostname; }).attr('target', '_blank'); if (that.filterReplyLinks == true) { twitPosterFilterReplyLinks(); } that.showNewTweets(); // HIDE THE OLD TWEETS $('ul#'+that.feedListDomId +" li:gt(" + that.maxItemsInFeedList + ")").remove(); // Removes everything past the Nth tweet }else{ // NO strTweetOutput FOUND // SHOW NO NEW TWEETS MESSAGE that.showNoTweets(); } // END IF strTweetOutput } // END IF DATA RESULTS //mylogger.timeEnd('array'); return this; }; // END PARSE RESULTS // ********************************* // *** SHOW COUNTDOWN *** twitTracker.prototype.showCountdown = function() { mylogger.log('FUNCTION: showCountdown'); var that=this; if (that.running == false) return; $(this.refreshWrapDomId).append('Next refresh in ...'); this.countdownObj = $(this.refreshWrapDomId + ' span.tweetcountdown span.counter').countdown({seconds: this.refreshTime, loopTime:750}, function(){ // COUNTDOWN COMPLETE CALLBACK that.hideCountdown(function(){ that.prepTweetSearch(); })// HIDE COUNTDOWN CALLBACK }, function(){ // COUNTDOWN LOOP CALLBACK } ); // FADE IN THE COUNTDOWN $(this.refreshWrapDomId + ' span.tweetcountdown').fadeIn(1500); return this; }; // END SHOW COUNTDOWN // ********************************* // *** HIDE COUNTDOWN *** twitTracker.prototype.hideCountdown = function(callback) { mylogger.log('FUNCTION: hideCountdown'); $(this.refreshWrapDomId + ' span.tweetcountdown').fadeOut(750, function () { $(this).remove(); if(callback) { callback(); } }); return this; }; // ********************************* // *** SHOW LOADER *** twitTracker.prototype.showLoader = function(callback) { mylogger.log('FUNCTION: showLoader'); var that=this; if (that.running == false) return; $(this.refreshWrapDomId).append('Fetching new tweets'); $(this.refreshWrapDomId + ' span.loading').fadeIn(1500, function () { if(callback) { callback(); } }); return this; }; // ********************************* // *** HIDE LOADER *** twitTracker.prototype.hideLoader = function(callback) { mylogger.log('FUNCTION: hideLoader'); $(this.refreshWrapDomId + ' span.loading').fadeOut(500, function () { $(this).remove(); if(callback) { callback(); } }); return this; }; // ********************************* // *** SHOW COMMUNICATION BREAKDOWN MESSAGE *** twitTracker.prototype.showCommunicationBreakdown = function() { mylogger.log('FUNCTION: showCommunicationBreakdown'); var that=this; //if (that.running == false) return; this.hideLoader(function() { mylogger.log('hideLoaderCallback'); $(that.refreshWrapDomId).append('Network timeout, trying again'); $(that.refreshWrapDomId + ' span.notweets').fadeIn(500); that.hideCommunicationBreakdown(2); }); return this; } // ********************************* // *** HIDE COMMUNICATION BREAKDOWN MESSAGE *** twitTracker.prototype.hideCommunicationBreakdown = function(waittime) { mylogger.log('FUNCTION: hideCommunicationBreakdown'); var that=this; $(that.refreshWrapDomId + ' span.notweets').wait(waittime).then.fadeOut(500, function () { // REMOVE THE STATUS MESSAGE $(this).remove(); // EXIT IF PAUSED ///if (that.running == false) return; // RESUME THE REFRESHER that.running = true; // RESTART THE COUNTDOWN that.showCountdown(); }); return this; } // ********************************* // *** SHOW NO TWEETS MESSAGE *** twitTracker.prototype.showNoTweets = function() { mylogger.log('FUNCTION: showNoTweets'); var that=this; if (that.running == false) return; this.hideLoader(function() { //alert('showNoTweets'); $(that.refreshWrapDomId).append('No new tweets found'); $(that.refreshWrapDomId + ' span.notweets').fadeIn(500); that.hideNoTweets(2); }); return this; } // ********************************* // *** HIDE NO TWEETS MESSAGE *** twitTracker.prototype.hideNoTweets = function(waittime) { mylogger.log('FUNCTION: hideNoTweets'); var that=this; $(that.refreshWrapDomId + ' span.notweets').wait(waittime).then.fadeOut(500, function () { $(this).remove(); if (that.running == false) return; that.showCountdown(); }); return this; } // ********************************* // *** SHOW NEW TWEETS *** twitTracker.prototype.showNewTweets = function() { mylogger.log('FUNCTION: showNewTweets'); var that=this; // EXIT IF PAUSED if (that.running == false) return; // ISOLATE DOM OBJECT FOR NEW TWEETS var $newTweets = $('ul#'+that.feedListDomId).find('li:hidden'); // DETERMINE HOW MANY NEW TWEETS var tweetCount = $newTweets.length; // HIDE THE LOADER (AND DO LOTS OF CALLBACK ACTIVITIES) that.hideLoader(function() { // FIGURE OUT HOW LONG TO TAKE TO REVEAL THE NEW TWEETS var toggletime; toggletime = Math.log(tweetCount)*750; toggletime = Math.round(toggletime); toggletime = Math.max(toggletime,1000); // SHOW THE COUNT OF NEW TWEETS that.showNewTweetCount(tweetCount,toggletime); // BIND HOVERINTENT TO THE NEW TWEETS $newTweets.hoverIntent({ sensitivity: 2, // number = sensitivity threshold (must be 1 or higher) interval: 25, // number = milliseconds of polling interval over: hideNewFlag, // function = onMouseOver callback (required) timeout: 0, // number = milliseconds delay before onMouseOut function call out:unbindNewFlag // function = onMouseOut callback (required) }); // REVEAL THE NEW TWEETS $newTweets.slideFadeToggle(toggletime,'easeOutQuad',function() { // FADE DOWN THE BACKGROUND COLOR FOR NEW TWEETS $('ul#'+that.feedListDomId + " li.new").highlightFade({start:'#FFEFE5',end:'#FFFFFF',speed:2500}).removeClass("new"); }); }); return this; }; // ********************************* // *** SHOW NEW TWEET COUNT *** twitTracker.prototype.showNewTweetCount = function(tweetCount,toggletime) { mylogger.log('FUNCTION: showNewTweetCount'); var that=this; // EXIT IF PAUSED if (that.running == false) return; waittime = Math.ceil(toggletime/1000) + 1; // FIGURE OUT THE TWEET LABEL PLURAL / SINGULAR BASED ON TWEET COUNT var newTweetLabel; tweetCount == 1 ? newTweetLabel = tweetCount + ' new tweet found.' : newTweetLabel = tweetCount + ' new tweets found'; // APPEND THE NEW TWEETS LABEL TO THE REFRESH TOOLBAR $(that.refreshWrapDomId).append(''); // FADE IN THE TWEET COUNT $(that.refreshWrapDomId + ' span.tweetcount').fadeIn(500, function () { // DELAY NEW TWEET COUNT HIDING BASED ON TOGGLE TIME waittime = Math.ceil(toggletime/1000) + 1; // MAKE CALL TO HIDE NEW TWEET COUNT that.hideNewTweetCount(waittime); }); // CLEAR OUT LOCAL VARS tweetCount = ''; return this; } // ********************************* // *** HIDE NEW TWEET COUNT *** twitTracker.prototype.hideNewTweetCount = function(toggletime) { mylogger.log('FUNCTION: hideNewTweetCount'); var that=this; $(that.refreshWrapDomId + ' span.tweetcount').wait(toggletime).then.fadeOut(750, function () { $(this).remove(); if (that.running == false) return; that.showCountdown(); }); return this; } // ********************************* // *** HIDE NEW FLAG *** function hideNewFlag(){$(this).find(".newflag").doHideNewFlag('')} function unbindNewFlag(){$(this).unbind('mouseover').unbind('mouseout')} jQuery.fn.doHideNewFlag = function(callback) { mylogger.log('FUNCTION: doHideNewFlag'); //$('#hd').wait(2).then.hide('fast').wait(4).then.show('slow'); this.fadeOut(250, function () { $(this).remove(); if(callback) { callback(); } }); return this; }; // ********************************* // *** FORMAT LINKS, HASHTAGS, AND @USER *** function FormatIt(text_results) // Formats the text. { //mylogger.log('FUNCTION: FormatIt'); return text_results.parseURL().parseUsername().parseHashtag(); } //http://www.simonwhatley.co.uk/parsing-twitter-usernames-hashtags-and-urls-with-javascript String.prototype.parseURL = function() { return this.replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&~\?\/.=]+/g, function(url) { urlWithoutHttp = url.replace("http://", ""); return urlWithoutHttp.link(url); }); }; String.prototype.parseUsername = function() { return this.replace(/[@]+[A-Za-z0-9-_]+/g, function(u) { var username = u.replace("@","") return u.link("http://twitter.com/"+username); }); }; String.prototype.parseHashtag = function() { return this.replace(/[#]+[A-Za-z0-9-_]+/g, function(t) { var tag = t.replace("#","%23") return t.link("http://search.twitter.com/search?q="+tag); }); }; /* $.fn.extend({ linkUrl: function() { var returning = []; var regexp = /((ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi; this.each(function() { returning.push(this.replace(regexp,"$1")) }); return $(returning); }, linkUser: function() { var returning = []; var regexp = /(\s|^)[\@]+(\w+)/gi; this.each(function() { returning.push(this.replace(regexp," @$2")) }); return $(returning); }, linkHash: function() { var returning = []; var regexp = / [\#]+(\w+)/gi; this.each(function() { returning.push(this.replace(regexp, ' #$1')) }); return $(returning); } }); */ function encodeUrl( str ) { //mylogger.log('FUNCTION: encodeUrl'); // http://kevin.vanzonneveld.net // + original by: Philip Peterson // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // * example 1: urlencode('Kevin van Zonneveld!'); // * returns 1: 'Kevin+van+Zonneveld%21' var ret = str; ret = ret.toString(); ret = encodeURIComponent(ret); ret = ret.replace(/%20/g, '+'); return ret; } function relative_time(time_value) { //Makes the time thing happen var values = time_value.split(" "); time_value = values[2] + " " + values[1] + ", " + values[3] + " " + values[4]; var parsed_date = Date.parse(time_value); var relative_to = (arguments.length > 1) ? arguments[1] : new Date(); var delta = parseInt((relative_to.getTime() - parsed_date) / 1000); delta = delta + (relative_to.getTimezoneOffset() * 60); var r = ''; if (delta < 60) { r = 'a minute ago'; } else if(delta < 120) { r = 'couple of minutes ago'; } else if(delta < (45*60)) { r = (parseInt(delta / 60)).toString() + ' minutes ago'; } else if(delta < (90*60)) { r = 'an hour ago'; } else if(delta < (24*60*60)) { r = '' + (parseInt(delta / 3600)).toString() + ' hours ago'; } else if(delta < (48*60*60)) { r = '1 day ago'; } else { r = (parseInt(delta / 86400)).toString() + ' days ago'; } return r; } /** * jQuery's Countdown Plugin * * display a countdown effect at given seconds, check out the following website for further information: * http://heartstringz.net/blog/posts/show/jquery-countdown-plugin * * @author Felix Ding * @version 0.1 * @copyright Copyright(c) 2008. Felix Ding * @license http://www.opensource.org/licenses/bsd-license.php The BSD License * @date 2008-03-09 * @lastmodified 2008-03-09 17:48 * @todo error & exceptions handling */ jQuery.fn.countdown = function(options,completeCallback,loopCallback) { mylogger.log('FUNCTION: countdown'); /** * app init */ if(!options) options = '()'; if(jQuery(this).length == 0) return false; this.countdownId = ''; this.loopTime = 1000; if (options.loopTime != '') { this.loopTime = options.loopTime; } var that = this; /** * break out and execute callback (if any) */ if(options.seconds < 0 || options.seconds == 'undefined') { //if(options.callback) eval(options.callback); if(completeCallback){ completeCallback(); } this.countdownId = ''; return null; } /** * recursive countdown */ this.countdownId = window.setTimeout( function() { jQuery(that).html(String(options.seconds)); --options.seconds; //options.seconds = (Math.round( (options.seconds - 0.1) * 10 ) / 10).toFixed(1); if (loopCallback) { //mylogger.log('test'); loopCallback(); } jQuery(that).countdown(options,completeCallback,loopCallback); } , this.loopTime ); /** * return null */ return this; }