1
/* requires jquery.tweet.js */
3
/* TODO: Fix tweet slideDown animation to actually slide down instead of changing height */
5
function escapeHTML(text) {
6
return $('<div/>').text(text).html();
9
function spliceText(text, indices) {
10
// Copyright 2010, Wade Simmons <https://gist.github.com/442463>
11
// Licensed under the MIT license
16
for (i=0; i < text.length; ++i) {
22
result += escapeHTML(text.substring(last_i, i));
31
result += escapeHTML(text.substring(last_i, i));
37
function Tweet(data) {
41
if (data.retweeted_status) innerData = data.retweeted_status;
43
var userID = innerData.from_user || innerData.user.id;
44
var userRealName = innerData.from_user_name || innerData.user.name;
45
var userScreenName = innerData.from_user || innerData.user.screen_name;
47
var linkHashTag = function(hashTag) {
48
return 'https://twitter.com/search?q='+encodeURIComponent('#'+hashTag);
51
var linkUser = function(userName) {
52
return 'https://twitter.com/'+encodeURIComponent(userName);
55
var linkUserID = function(userID) {
56
return 'https://twitter.com/account/redirect_by_id?id='+encodeURIComponent(userID);
59
var linkEntities = function(entities, text) {
62
$.each(entities.media || [], function(i, entry) {
63
var link = '<a class="twitter-url twitter-media" href="'+encodeURI(entry.media_url)+'">'+escapeHTML(entry.display_url || entry.url)+'</a>';
64
entityIndices[entry.indices[0]] = [entry.indices[1], link];
67
$.each(entities.urls || [], function(i, entry) {
68
var link = '<a class="twitter-url" href="'+encodeURI(entry.url)+'">'+escapeHTML(entry.display_url || entry.url)+'</a>';
69
entityIndices[entry.indices[0]] = [entry.indices[1], link];
72
$.each(entities.hashtags || [], function(i, entry) {
73
var link = '<a class="twitter-hashtag" href="'+linkHashTag(entry.text)+'">'+escapeHTML('#'+entry.text)+'</a>';
74
entityIndices[entry.indices[0]] = [entry.indices[1], link];
77
$.each(entities.user_mentions || [], function(i, entry) {
78
var link = '<a class="twitter-mention" href="'+linkUserID(entry.id)+'">'+escapeHTML('@'+entry.screen_name)+'</a>';
79
entityIndices[entry.indices[0]] = [entry.indices[1], link];
82
return spliceText(text, entityIndices);
84
var linkedText = linkEntities(innerData.entities, innerData.text);
86
this.getHtml = function() {
87
var container = $('<div class="tweet">');
89
var authorDetails = $('<a class="tweet-author-details">');
90
authorDetails.attr('href', linkUserID(userID));
92
var authorName = $('<span class="tweet-author-name">');
93
authorName.text(userRealName);
95
var authorID = $('<span class="tweet-author-id">');
96
authorID.text(userScreenName);
98
authorDetails.append(authorName, authorID);
99
container.append(authorDetails);
101
var text = $('<div class="tweet-text">');
102
text.html(linkedText);
103
container.append(text);
109
function TweetsList(container) {
110
var tweetsList = this;
112
container = $(container);
113
var list = $('<ul class="tweets-list">');
114
container.append(list);
116
var cleanup = function() {
117
var bottom = container.height();
118
list.children().each(function(index, listItem) {
119
if ($(listItem).position().top > bottom) {
120
$(listItem).remove();
125
this.showTweet = function(tweet) {
126
var listItem = $('<li>');
127
listItem.html(tweet.getHtml());
129
listItem.css('opacity', '0');
131
list.prepend(listItem);
133
var expandTime = listItem.height() * 8;
137
}, expandTime, 'linear', function() {
141
/*listItem.slideDown(500);*/
145
function TweetQuery(lang) {
146
var tweetQuery = this;
148
// request is tightly encapsulated because we might move that logic to a remote server
150
var QUERY_URL = 'https://api.twitter.com/1/lists/statuses.json';
152
'owner_screen_name' : 'hello_ubuntu',
153
'slug' : 'installer-slideshow',
154
'include_entities' : true,
155
'include_rts' : true,
159
//var QUERY_URL = 'https://search.twitter.com/search.json';
161
'q' : 'from:ubuntu OR from:ubuntudev OR from:planetubuntu OR from:ubuntul10n OR from:ubuntucloud OR from:ubuntuone OR from:ubuntudesigners OR from:ubuntuunity OR from:canonical',
163
'result_type' : 'recent',
165
'include_entities' : true
170
/** Time since last update, in seconds */
171
this.getTimeSinceUpdate = function() {
172
var now = Date.now();
173
return now - lastUpdate;
176
this.loadTweets = function(loadedCallback) {
184
success: function(data, status, xhr) {
185
//var results = data.results || [];
186
var results = data || [];
187
if ('results' in results) results = results.results;
188
$.each(results, function(index, tweetData) {
189
var tweet = new Tweet(tweetData);
190
newTweets.unshift(tweet);
193
complete: function(xhr, status) {
194
loadedCallback(newTweets);
197
lastUpdate = Date.now();
201
function TweetBuffer() {
202
var tweetBuffer = this;
204
var query = new TweetQuery('all');
207
var nextTweetIndex = 0;
209
var loadedCallback = function(newTweets) {
210
if (newTweets.length > 0) {
216
var getNextTweet = function(returnTweet) {
217
if (nextTweetIndex < tweets.length) {
218
returnTweet(tweets[nextTweetIndex]);
221
if (query.getTimeSinceUpdate() > 15 * 60 * 1000) {
222
// load new tweets every 15 minutes
223
query.loadTweets(function(newTweets) {
224
loadedCallback(newTweets);
225
returnTweet(tweets[nextTweetIndex]);
228
returnTweet(tweets[nextTweetIndex]);
234
this.dataIsAvailable = function(response) {
235
getNextTweet(function(tweet) {
236
response ( (tweet !== undefined) );
240
/* Loads (if necessary) the next tweet and sends it asynchronously to
241
* the tweetReceived(tweet) callback. The tweet parameter is undefined
242
* if no tweets are available.
244
this.popTweet = function(tweetReceived) {
245
getNextTweet(function(tweet) {
247
tweetReceived(tweet);
252
function TwitterStream(streamContainer) {
253
var twitterStream = this;
255
var tweetsContainer = $(streamContainer).children('.twitter-stream-tweets');
256
var tweetsList = new TweetsList(tweetsContainer);
258
var tweetBuffer = new TweetBuffer();
260
var showNextInterval = undefined;
262
var showNextTweet = function() {
263
tweetBuffer.popTweet(function(tweet) {
265
twitterStream.enable();
266
tweetsList.showTweet(tweet);
268
// this isn't working, so we'll hide the stream
269
twitterStream.disable();
274
var _enabled = false;
275
this.isEnabled = function() {
278
this.enable = function(immediate) {
279
if (_enabled) return;
281
$(streamContainer).show();
283
$(streamContainer).fadeIn(150);
287
this.disable = function(immediate) {
288
if (! _enabled) return;
290
$(streamContainer).hide();
292
$(streamContainer).fadeOut(150);
298
this.start = function() {
300
showNextInterval = window.setInterval(showNextTweet, 6000);
302
this.stop = function() {
303
if (showNextInterval) window.clearInterval(showNextInterval);
306
var _init = function() {
307
tweetBuffer.dataIsAvailable(function(available) {
309
twitterStream.enable(true);
310
// make sure there is some content visible from the start
313
twitterStream.disable(true);
320
/* Only show the Twitter stuff if the slideshow is supposed to be English */
321
if ('locale' in INSTANCE_OPTIONS) {
322
var locale_data = parse_locale_code(INSTANCE_OPTIONS['locale']);
323
var language = locale_data['language'];
328
var twitterLanguages = ['en', 'C'];
329
if (twitterLanguages.indexOf(language) >= 0) {
330
var doTwitter = true;
332
var doTwitter = false;
335
// Turn off Twitter for security reason
338
Signals.watch('slideshow-loaded', function() {
340
$('.twitter-stream').each(function(index, streamContainer) {
341
var stream = new TwitterStream(streamContainer);
342
$(streamContainer).data('stream-object', stream);
343
// TODO: test connection, show immediately if connection is good
346
$('.twitter-post-status-link').each(function(index, linkContent) {
347
// Twitter-post-status-link is a <div> to avoid being translated. We need to wrap it around an <a> tag
348
var statusText = $(linkContent).children('.twitter-post-status-text').text();
350
link.attr('href', 'https://twitter.com/home?status='+encodeURIComponent(statusText));
351
link.insertBefore(linkContent);
352
$(linkContent).appendTo(link);
355
$('.twitter-stream').hide();
356
/* TODO: show something charming? */
360
Signals.watch('slide-opened', function(slide) {
361
if (! doTwitter) return;
363
var streamContainers = $('.twitter-stream', slide);
364
streamContainers.each(function(index, streamContainer) {
365
var stream = $(streamContainer).data('stream-object');
372
Signals.watch('slide-closing', function(slide) {
373
if (! doTwitter) return;
375
var streamContainers = $('.twitter-stream', slide);
376
streamContainers.each(function(index, streamContainer) {
377
var stream = $(streamContainer).data('stream-object');