1(function($) { 2 3 $.fn.tweet = function(o){ 4 var s = { 5 username: ["seaofclouds"], // [string] required, unless you want to display our tweets. :) it can be an array, just do ["username1","username2","etc"] 6 list: null, // [string] optional name of list belonging to username 7 avatar_size: null, // [integer] height and width of avatar if displayed (48px max) 8 count: 3, // [integer] how many tweets to display? 9 intro_text: null, // [string] do you want text BEFORE your your tweets? 10 outro_text: null, // [string] do you want text AFTER your tweets? 11 join_text: null, // [string] optional text in between date and tweet, try setting to "auto" 12 auto_join_text_default: "i said,", // [string] auto text for non verb: "i said" bullocks 13 auto_join_text_ed: "i", // [string] auto text for past tense: "i" surfed 14 auto_join_text_ing: "i am", // [string] auto tense for present tense: "i was" surfing 15 auto_join_text_reply: "i replied to", // [string] auto tense for replies: "i replied to" @someone "with" 16 auto_join_text_url: "i was looking at", // [string] auto tense for urls: "i was looking at" http:... 17 loading_text: null, // [string] optional loading text, displayed while tweets load 18 query: null, // [string] optional search query 19 refresh_interval: null , // [integer] optional number of seconds after which to reload tweets 20 twitter_url: "twitter.com", // [string] custom twitter url, if any (apigee, etc.) 21 twitter_api_url: "api.twitter.com", // [string] custom twitter api url, if any (apigee, etc.) 22 twitter_search_url: "search.twitter.com" // [string] custom twitter search url, if any (apigee, etc.) 23 }; 24 25 if(o) $.extend(s, o); 26 27 $.fn.extend({ 28 linkUrl: function() { 29 var returning = []; 30 // See http://daringfireball.net/2010/07/improved_regex_for_matching_urls 31 var regexp = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»ââââ]))/gi; 32 this.each(function() { 33 returning.push(this.replace(regexp, 34 function(match) { 35 var url = (/^[a-z]+:/i).test(match) ? match : "http://"+match; 36 return "<a href=\""+url+"\">"+match+"</a>"; 37 })); 38 }); 39 return $(returning); 40 }, 41 linkUser: function() { 42 var returning = []; 43 var regexp = /[\@]+([A-Za-z0-9-_]+)/gi; 44 this.each(function() { 45 returning.push(this.replace(regexp,"<a href=\"http://"+s.twitter_url+"/$1\">@$1</a>")); }); 46 return $(returning); 47 }, 48 linkHash: function() { 49 var returning = []; 50 var regexp = /(?:^| )[\#]+([A-Za-z0-9-_]+)/gi; 51 this.each(function() { 52 returning.push(this.replace(regexp, ' <a href="http://'+s.twitter_search_url+'/search?q=&tag=$1&lang=all&from='+s.username.join("%2BOR%2B")+'">#$1</a>')); 53 }); 54 return $(returning); 55 }, 56 capAwesome: function() { 57 var returning = []; 58 this.each(function() { 59 returning.push(this.replace(/\b(awesome)\b/gi, '<span class="awesome">$1</span>')); 60 }); 61 return $(returning); 62 }, 63 capEpic: function() { 64 var returning = []; 65 this.each(function() { 66 returning.push(this.replace(/\b(epic)\b/gi, '<span class="epic">$1</span>')); 67 }); 68 return $(returning); 69 }, 70 makeHeart: function() { 71 var returning = []; 72 this.each(function() { 73 returning.push(this.replace(/(<)+[3]/gi, "<tt class='heart'>♥</tt>")); 74 }); 75 return $(returning); 76 } 77 }); 78 79 function parse_date(date_str) { 80 // The non-search twitter APIs return inconsistently-formatted dates, which Date.parse 81 // cannot handle in IE. We therefore perform the following transformation: 82 // "Wed Apr 29 08:53:31 +0000 2009" => "Wed, Apr 29 2009 08:53:31 +0000" 83 return Date.parse(date_str.replace(/^([a-z]{3})( [a-z]{3} \d\d?)(.*)( \d{4})$/i, '$1,$2$4$3')); 84 } 85 86 function relative_time(time_value) { 87 var parsed_date = parse_date(time_value); 88 var relative_to = (arguments.length > 1) ? arguments[1] : new Date(); 89 var delta = parseInt((relative_to.getTime() - parsed_date) / 1000); 90 var r = ''; 91 if (delta < 60) { 92 r = delta + ' seconds ago'; 93 } else if(delta < 120) { 94 r = 'a minute ago'; 95 } else if(delta < (45*60)) { 96 r = (parseInt(delta / 60, 10)).toString() + ' minutes ago'; 97 } else if(delta < (2*60*60)) { 98 r = 'an hour ago'; 99 } else if(delta < (24*60*60)) { 100 r = '' + (parseInt(delta / 3600, 10)).toString() + ' hours ago'; 101 } else if(delta < (48*60*60)) { 102 r = 'a day ago'; 103 } else { 104 r = (parseInt(delta / 86400, 10)).toString() + ' days ago'; 105 } 106 return 'about ' + r; 107 } 108 109 function build_url() { 110 var proto = ('https:' == document.location.protocol ? 'https:' : 'http:'); 111 if (s.list) { 112 return proto+"//"+s.twitter_api_url+"/1/"+s.username[0]+"/lists/"+s.list+"/statuses.json?per_page="+s.count+"&callback=?"; 113 } else if (s.query == null && s.username.length == 1) { 114 return proto+'//'+s.twitter_api_url+'/1/statuses/user_timeline.json?screen_name='+s.username[0]+'&count='+s.count+'&include_rts=1&callback=?'; 115 } else { 116 var query = (s.query || 'from:'+s.username.join(' OR from:')); 117 return proto+'//'+s.twitter_search_url+'/search.json?&q='+encodeURIComponent(query)+'&rpp='+s.count+'&c
117allback=?'; 118 } 119 } 120 121 return this.each(function(i, widget){ 122 var list = $('<ul class="tweet_list">').appendTo(widget); 123 var intro = '<p class="tweet_intro">'+s.intro_text+'</p>'; 124 var outro = '<p class="tweet_outro">'+s.outro_text+'</p>'; 125 var loading = $('<p class="loading">'+s.loading_text+'</p>'); 126 127 if(typeof(s.username) == "string"){ 128 s.username = [s.username]; 129 } 130 131 if (s.loading_text) $(widget).append(loading); 132 $(widget).bind("load", function(){ 133 $.getJSON(build_url(), function(data){ 134 if (s.loading_text) loading.remove(); 135 if (s.intro_text) list.before(intro); 136 list.empty(); 137 var tweets = (data.results || data); 138 $.each(tweets, function(i,item){ 139 // auto join text based on verb tense and content 140 if (s.join_text == "auto") { 141 if (item.text.match(/^(@([A-Za-z0-9-_]+)) .*/i)) { 142 var join_text = s.auto_join_text_reply; 143 } else if (item.text.match(/(^\w+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+) .*/i)) { 144 var join_text = s.auto_join_text_url; 145 } else if (item.text.match(/^((\w+ed)|just) .*/im)) { 146 var join_text = s.auto_join_text_ed; 147 } else if (item.text.match(/^(\w*ing) .*/i)) { 148 var join_text = s.auto_join_text_ing; 149 } else { 150 var join_text = s.auto_join_text_default; 151 } 152 } else { 153 var join_text = s.join_text; 154 }; 155 156 var from_user = item.from_user || item.user.screen_name; 157 var profile_image_url = item.profile_image_url || item.user.profile_image_url; 158 var join_template = '<span class="tweet_join"> '+join_text+' </span>'; 159 var join = ((s.join_text) ? join_template : ' '); 160 var avatar_template = '<a class="tweet_avatar" href="http://'+s.twitter_url+'/'+from_user+'"><img src="'+profile_image_url+'" height="'+s.avatar_size+'" width="'+s.avatar_size+'" alt="'+from_user+'\'s avatar" title="'+from_user+'\'s avatar" border="0"/></a>'; 161 var avatar = (s.avatar_size ? avatar_template : ''); 162 var date = '<span class="tweet_time"><a href="http://'+s.twitter_url+'/'+from_user+'/statuses/'+item.id_str+'" title="view tweet on twitter">'+relative_time(item.created_at)+'</a></span>'; 163 var text = '<span class="tweet_text">' +$([item.text]).linkUrl().linkUser().linkHash().makeHeart().capAwesome().capEpic()[0]+ '</span>'; 164 165 // until we create a template option, arrange the items below to alter a tweet's display. 166 list.append('<li>' + avatar + date + join + text + '</li>'); 167 168 list.children('li:first').addClass('tweet_first'); 169 list.children('li:odd').addClass('tweet_even'); 170 list.children('li:even').addClass('tweet_odd'); 171 }); 172 if (s.outro_text) list.after(outro); 173 $(widget).trigger("loaded").trigger((tweets.length == 0 ? "empty" : "full")); 174 if (s.refresh_interval) { 175 window.setTimeout(function() { $(widget).trigger("load"); }, 1000 * s.refresh_interval); 176 }; 177 }); 178 }).trigger("load"); 179 }); 180 }; 181})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.