root / drupal7 / sites / all / themes / simplecorp / js / plugins / jquery.jtweetsanywhere-1.3.1.js @ 27370441
1 |
/**
|
---|---|
2 |
* jTweetsAnywhere V1.3.1
|
3 |
* http://thomasbillenstein.com/jTweetsAnywhere/
|
4 |
*
|
5 |
* Copyright 2011, Thomas Billenstein
|
6 |
* Licensed under the MIT license.
|
7 |
* http://thomasbillenstein.com/jTweetsAnywhere/license.txt
|
8 |
*/
|
9 |
|
10 |
|
11 |
/**
|
12 |
* The code below is used as supplied by Twitter (https://dev.twitter.com/docs/intents)
|
13 |
*
|
14 |
* Twitter says:
|
15 |
|
16 |
* "Some sites may prefer to embed the unobtrusive Web Intents pop-up Javascript inline
|
17 |
* or without a dependency to platform.twitter.com. The snippet below will offer the
|
18 |
* equivalent functionality without the external dependency."
|
19 |
*/
|
20 |
(function()
|
21 |
{ |
22 |
if (window.__twitterIntentHandler)
|
23 |
return;
|
24 |
|
25 |
var intentRegex = /twitter\.com(\:\d{2,4})?\/intent\/(\w+)/, |
26 |
windowOptions = 'scrollbars=yes,resizable=yes,toolbar=no,location=yes',
|
27 |
width = 550,
|
28 |
height = 420,
|
29 |
winHeight = screen.height, |
30 |
winWidth = screen.width; |
31 |
|
32 |
|
33 |
function handleIntent(e) |
34 |
{ |
35 |
e = e || window.event; |
36 |
|
37 |
var target = e.target || e.srcElement,
|
38 |
m, left, top; |
39 |
|
40 |
while (target && target.nodeName.toLowerCase() !== 'a') |
41 |
{ |
42 |
target = target.parentNode; |
43 |
} |
44 |
|
45 |
if (target && target.nodeName.toLowerCase() === 'a' && target.href) |
46 |
{ |
47 |
m = target.href.match(intentRegex); |
48 |
if (m)
|
49 |
{ |
50 |
left = Math.round((winWidth / 2) - (width / 2)); |
51 |
top = 0;
|
52 |
|
53 |
if (winHeight > height)
|
54 |
{ |
55 |
top = Math.round((winHeight / 2) - (height / 2)); |
56 |
} |
57 |
|
58 |
window.open(target.href, 'intent', windowOptions + ',width=' + width + ',height=' + height + ',left=' + left + ',top=' + top); |
59 |
e.returnValue = false;
|
60 |
e.preventDefault && e.preventDefault(); |
61 |
} |
62 |
} |
63 |
} |
64 |
|
65 |
if (document.addEventListener)
|
66 |
{ |
67 |
document.addEventListener('click', handleIntent, false); |
68 |
} |
69 |
else if (document.attachEvent) |
70 |
{ |
71 |
document.attachEvent('onclick', handleIntent);
|
72 |
} |
73 |
|
74 |
window.__twitterIntentHandler = true;
|
75 |
}()); |
76 |
|
77 |
|
78 |
/**
|
79 |
* JTA_I18N is based on SimpleI18N V0.1.0
|
80 |
*
|
81 |
* SimpleI18N.js is a tiny library for simple i18n support in Javascript.
|
82 |
* Currently only translation is supported.
|
83 |
*/
|
84 |
(function()
|
85 |
{ |
86 |
if (window.__JTA_I18N)
|
87 |
{ |
88 |
return;
|
89 |
} |
90 |
|
91 |
JTA_I18N = function() |
92 |
{ |
93 |
var _resources = {};
|
94 |
|
95 |
function ResourceBundle(locale, resources) |
96 |
{ |
97 |
this.getLocale = function() |
98 |
{ |
99 |
return locale;
|
100 |
}; |
101 |
|
102 |
this.get = function(key, params) |
103 |
{ |
104 |
return xlate(key, 1, params); |
105 |
}; |
106 |
|
107 |
this._ = this.get; |
108 |
|
109 |
this.nget = function(singular, plural, count, params) |
110 |
{ |
111 |
return count === 1 ? xlate(singular, 1, params) : xlate(plural, count, params); |
112 |
}; |
113 |
|
114 |
this.__ = this.nget; |
115 |
|
116 |
function xlate(key, count, params) |
117 |
{ |
118 |
var resource = getValue(key);
|
119 |
|
120 |
if (count !== 1 && typeof resource === "object") |
121 |
{ |
122 |
resource = evalMulti(key, resource, count); |
123 |
} |
124 |
|
125 |
if (resource && params)
|
126 |
{ |
127 |
for (p in params) |
128 |
{ |
129 |
resource = resource.replace(p, getValue(params[p])); |
130 |
} |
131 |
} |
132 |
|
133 |
return resource;
|
134 |
}; |
135 |
|
136 |
function getValue(resource) |
137 |
{ |
138 |
return resources ? (resources[resource] || resource) : resource;
|
139 |
}; |
140 |
|
141 |
function evalMulti(key, resource, count) |
142 |
{ |
143 |
for (pat in resource) |
144 |
{ |
145 |
var re = /(\d+)\s*-\s*(\d+)/, |
146 |
match = re.exec(pat); |
147 |
|
148 |
if (match)
|
149 |
{ |
150 |
var from = match[1]; |
151 |
var to = match[2]; |
152 |
if (count >= from && count <= to)
|
153 |
{ |
154 |
return resource[pat];
|
155 |
} |
156 |
} |
157 |
|
158 |
re = /([<>]=?)\s*(\d+)/;
|
159 |
match = re.exec(pat); |
160 |
|
161 |
if (match)
|
162 |
{ |
163 |
var op = match[1]; |
164 |
var num = match[2]; |
165 |
if (op === '>' && count > num) |
166 |
{ |
167 |
return resource[pat];
|
168 |
} |
169 |
else if (op === '>=' && count >= num) |
170 |
{ |
171 |
return resource[pat];
|
172 |
} |
173 |
else if (op === '<' && count < num) |
174 |
{ |
175 |
return resource[pat];
|
176 |
} |
177 |
else if (op === '<=' && count <= num) |
178 |
{ |
179 |
return resource[pat];
|
180 |
} |
181 |
} |
182 |
|
183 |
re = /\s*,\s*/;
|
184 |
match = pat.split(re); |
185 |
|
186 |
if (match)
|
187 |
{ |
188 |
for (var i = 0; i < match.length; i++) |
189 |
{ |
190 |
if (count === ~~match[i])
|
191 |
{ |
192 |
return resource[pat];
|
193 |
} |
194 |
} |
195 |
} |
196 |
} |
197 |
|
198 |
return key;
|
199 |
} |
200 |
}; |
201 |
|
202 |
return {
|
203 |
|
204 |
addResourceBundle: function(project, locale, resources) |
205 |
{ |
206 |
if (!_resources[project])
|
207 |
{ |
208 |
_resources[project] = {}; |
209 |
} |
210 |
|
211 |
_resources[project][locale] = resources; |
212 |
}, |
213 |
|
214 |
getResourceBundle: function(project, locale) |
215 |
{ |
216 |
return new ResourceBundle(locale, _resources[project] ? _resources[project][locale] : null); |
217 |
} |
218 |
}; |
219 |
}(); |
220 |
|
221 |
window.__JTA_I18N = true;
|
222 |
}()); |
223 |
|
224 |
JTA_I18N.addResourceBundle('jTweetsAnywhere', 'en', |
225 |
{ |
226 |
'$$monthNames': [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] |
227 |
}); |
228 |
|
229 |
(function($) |
230 |
{ |
231 |
$.fn.jTweetsAnywhere = function(config) |
232 |
{ |
233 |
// setup the default options
|
234 |
var options = $.extend( |
235 |
{ |
236 |
/**
|
237 |
* The user's name who's tweet feed or list feed is displayed. This
|
238 |
* param is also used when a Twitter "Follow Button" is displayed. Usually
|
239 |
* this param is a string, but can also be an array of strings. If an array
|
240 |
* is supplied (and the params 'list' and 'searchParams' are null), a
|
241 |
* combined feed of all users is displayed.
|
242 |
*
|
243 |
* Sample: 'tbillenstein' or ['twitterapi', '...', '...']
|
244 |
*/
|
245 |
username: 'morethanthemes', |
246 |
|
247 |
/**
|
248 |
* The name of a user's list where the tweet feed is generated from. The special
|
249 |
* list name 'favorites' can be used to display a user's favorited tweets.
|
250 |
*/
|
251 |
list: null, |
252 |
|
253 |
/**
|
254 |
* A single search param string or an array of search params, to be used in
|
255 |
* a Twitter search call. All Twitter Search Params are supported
|
256 |
* See here for the details:
|
257 |
* http://apiwiki.twitter.com/Twitter-Search-API-Method%3A-search
|
258 |
*
|
259 |
* Sample: 'q=twitter' or ['q=twitter', 'geocode=48.856667,2.350833,30km']
|
260 |
*/
|
261 |
searchParams: null, |
262 |
|
263 |
/**
|
264 |
* The number of tweets shown in the tweet feed. If this param is 0, no feed
|
265 |
* is displayed. For user or list feeds the maximum count is 20, for search
|
266 |
* results the maximum count is 100.
|
267 |
*
|
268 |
* Unlike in previous releases, since 1.2.0 jTweetsAnywhere is based on a
|
269 |
* tweets caching algorithm that will always deliver the requested count of
|
270 |
* tweets accepting that this request can only be fullfilled by calling Twitter
|
271 |
* more than once.
|
272 |
*
|
273 |
* IMPORTANT: Please always keep in mind, that the use of the Twitter API is
|
274 |
* rate limited. Non-authenticated users are rated IP-based and you have only
|
275 |
* 150 calls per hour available. Every retrieval of tweets counts and so does
|
276 |
* for example hovering over a profile image to show the hovercard.
|
277 |
* jTweetsAnywhere will always check the remaining count of free API calls before
|
278 |
* actually calling Twitter to avoid black listing your visitor's IP.
|
279 |
*/
|
280 |
count: 0, |
281 |
|
282 |
/**
|
283 |
* A flag (true/false) that specifies whether to display a profile image in
|
284 |
* tweets. If the param is set to null (the default value), a profile image
|
285 |
* is displayed only if the feed represents a user's list or the result of a
|
286 |
* Twitter search.
|
287 |
*
|
288 |
* THIS OPTION IS DEPRECATED. You should use showTweetFeed.showProfileImages
|
289 |
* instead.
|
290 |
*/
|
291 |
tweetProfileImagePresent: null, |
292 |
|
293 |
/**
|
294 |
* Each tweet that is loaded from Twitter will pass the tweetFilter. if
|
295 |
* the filter returns true, the tweet will be added to the tweets cache
|
296 |
* otherwise it is ignored. The defaultTweetFilter alsways retruns true
|
297 |
* but you can supply your own tweet filter to customize the tweet feed.
|
298 |
*/
|
299 |
tweetFilter: defaultTweetFilter,
|
300 |
|
301 |
/**
|
302 |
* A flag (true/false) that specifies whether to display a Tweet Feed
|
303 |
* or an object literal representing the configuration options for the
|
304 |
* Tweet Feed. This flag works in conjunction with the count parameter:
|
305 |
* - if count equals 0, no feed is displayed, ignoring showTweetFeed
|
306 |
* - if count not equals 0 and showTweetFeed equals false, no feed
|
307 |
* is displayed
|
308 |
* {
|
309 |
* autoConformToTwitterStyleguide: false,
|
310 |
* // Boolean - as the name implies, sets all options to confirm to Twitter's
|
311 |
* // styleguide regulations. Implies:
|
312 |
* // showTweetFeed: {
|
313 |
* // showUserFullNames: null, // null means: if usernames are shown, show
|
314 |
* // // fullnames too
|
315 |
* // showTwitterBird: true,
|
316 |
* // showActionReply: true,
|
317 |
* // showActionRetweet: true,
|
318 |
* // showActionFavorite: true
|
319 |
* // }
|
320 |
*
|
321 |
* showTwitterBird: true, // Boolean - show Twitter bird icon beneath the timestamp of a tweet, linking to
|
322 |
* // the tweeter's MiniProfile Web Intent
|
323 |
*
|
324 |
* showTimestamp: true, // A flag (true/false) that specifies whether to display a tweet's timestamp
|
325 |
* // or an object literal representing the configuration options for the
|
326 |
* // timestamp.
|
327 |
* // {
|
328 |
* // refreshInterval: 0, // Time in seconds to be waited until the
|
329 |
* // // timestamps of the displayed tweets get refreshed
|
330 |
* // // 0 means no refreshing.
|
331 |
* // }
|
332 |
*
|
333 |
* showSource: false, // Boolean - Show info about the source of the tweet.
|
334 |
*
|
335 |
* showGeoLocation: true, // Boolean - Show geolocation info and link to Google maps.
|
336 |
*
|
337 |
* showInReplyTo: true, // Boolean - Show link to the "replied to" tweet (if available).
|
338 |
*
|
339 |
* showActionReply: false, // Boolean - Show tweet's 'Reply' action (supplies a link to popup the tweet's
|
340 |
* // Reply Web Intent)
|
341 |
*
|
342 |
* showActionRetweet: false, // Boolean - Show tweet's 'Retweet' action (supplies a link to popup the tweet's
|
343 |
* // Retweet Web Intent)
|
344 |
*
|
345 |
* showActionFavorite: false, // Boolean - Show tweet's 'Favorite' action (supplies a link to popup the tweet's
|
346 |
* // Favorite Web Intent)
|
347 |
*
|
348 |
* showProfileImages: null, // A flag (true/false) that specifies whether to display a profile image in
|
349 |
* // tweets. If the param is set to null (the default value), a profile image
|
350 |
* // is displayed only if the feed represents a user's list or the result of a
|
351 |
* // Twitter search.
|
352 |
*
|
353 |
* showUserScreenNames: null, // A flag (true/false/null) that specifies whether to display a username in
|
354 |
* // tweets. If the param is set to null (the default value), a username
|
355 |
* // is displayed only if the feed represents a user's list or the result of a
|
356 |
* // Twitter search.
|
357 |
*
|
358 |
* showUserFullNames: false, // A flag (true/false/null) that specifies whether to display a user's full name
|
359 |
* // in tweets. If the param is set to null, a user's full name
|
360 |
* // is displayed only if the feed represents a user's list or the result of a
|
361 |
* // Twitter search.
|
362 |
*
|
363 |
* expandHovercards: false, // Boolean - Show Hovercards in expanded mode.
|
364 |
*
|
365 |
* includeRetweets: true, // Boolean - Include native retweets in a user's tweet feed
|
366 |
*
|
367 |
* paging: // An object literal representing the configuration options for the
|
368 |
* { // paging support, that specifies how more/earlier tweets can be loaded
|
369 |
* mode: "none" // by using the supplied UI controls (more/next buttons, scrollbar).
|
370 |
* }, // Accepted values for mode are: "none" | "more" | "prev-next" | "endless-scroll"
|
371 |
* // if mode equals "endless-scroll" you have to set the height of the tweet feed
|
372 |
* // element (.jta-tweet-list) in your CSS to get a scrollbar! You should also set
|
373 |
* // the "overflow" attribute to "auto".
|
374 |
*
|
375 |
* autorefresh: // An object literal representing the configuration options for the
|
376 |
* { // autorefresh behaviour.
|
377 |
*
|
378 |
* // IMPORTANT: Please always keep in mind, that using the Twitter API is rate
|
379 |
* // limited. Non-authenticated users are rated IP-based and you have only 150
|
380 |
* // calls per hour available. Every retrieval of tweets counts and so does for
|
381 |
* // example hovering over a profile image to show the hovercard. jTweetsAnywhere will
|
382 |
* // always check the remaining count of free API calls before actually calling
|
383 |
* // Twitter to avoid black listing your visitor's IP.
|
384 |
*
|
385 |
* // However - choose your settings wisely to keep your visitors happy. An update
|
386 |
* // interval of 30 seconds on a feed that is updated averaged once per hour
|
387 |
* // does not make sense and is a total waste of remaining API calls!
|
388 |
*
|
389 |
* mode: "none", // Accepted values for mode are: "none" | "auto-insert" | "trigger-insert"
|
390 |
* // "none" (the default value) - disables the autorefresh feature
|
391 |
* // "auto-insert" - automatically insert the new tweets on top of the tweet feed
|
392 |
* // "trigger-insert" - if new tweets arrived, show or update a button that displays
|
393 |
* // the number of new tweets. These new tweets are inserted on top of the tweet
|
394 |
* // feed, if the user clicks on the button.
|
395 |
*
|
396 |
* interval: 60, // Time in seconds to be waited until the next request for new tweets. Minimum
|
397 |
* // value is 30.
|
398 |
*
|
399 |
* duration: 3600 // Time in seconds for how long the autorefresh will be active. After
|
400 |
* // this period of time, autorefreshing will stop. A value of -1 means
|
401 |
* // autorefresh for ever.
|
402 |
* }
|
403 |
* }
|
404 |
*/
|
405 |
showTweetFeed: true, |
406 |
|
407 |
/**
|
408 |
* A flag (true/false) that specifies whether to display a Twitter "Follow
|
409 |
* Button".
|
410 |
*/
|
411 |
showFollowButton: false, |
412 |
|
413 |
/**
|
414 |
* A flag (true/false) that specifies whether to display a Twitter "Connect
|
415 |
* Button" or an object literal representing the configuration options for
|
416 |
* the "Tweet Box".
|
417 |
* {
|
418 |
* size: 'medium' // String - The size of the Connect Button. Valid values are: small, medium, large, xlarge
|
419 |
* }
|
420 |
*/
|
421 |
showConnectButton: false, |
422 |
|
423 |
/**
|
424 |
* A flag (true/false) that specifies whether to display Login Infos.
|
425 |
*/
|
426 |
showLoginInfo: false, |
427 |
|
428 |
/**
|
429 |
* A flag (true/false) that specifies whether to display a Twitter "Tweet
|
430 |
* Box" or an object literal representing the configuration options for
|
431 |
* the "Tweet Box".
|
432 |
* {
|
433 |
* counter: true, // Boolean - Display a counter in the Tweet Box for counting characters
|
434 |
* width: 515, // Number - The width of the Tweet Box in pixels
|
435 |
* height: 65, // Number - The height of the Tweet Box in pixels
|
436 |
* label: "What's happening?", // String - The text above the Tweet Box, a call to action
|
437 |
* defaultContent: <none>, // String - Pre-populated text in the Tweet Box. Useful for an @mention, a #hashtag, a link, etc.
|
438 |
* onTweet: <none> // Function - Specify a listener for when a tweet is sent from the Tweet Box. The listener receives two arguments: a plaintext tweet and an HTML tweet
|
439 |
* }
|
440 |
*/
|
441 |
showTweetBox: false, |
442 |
|
443 |
/**
|
444 |
* Identifies the locale for I18N support. The default locale is 'en'. To use this option you have to inlude the
|
445 |
* adequate locale script, jtweetsanywhere-{language}-{version}.js, e.g. jtweetsanywhere-de-1.3.0.js
|
446 |
*/
|
447 |
locale: 'en', |
448 |
|
449 |
/**
|
450 |
* A dataProvider is a function that delivers the "raw" Twitter data in
|
451 |
* JSON format. ATM internal use only!
|
452 |
*/
|
453 |
tweetDataProvider:
|
454 |
defaultTweetDataProvider, |
455 |
//mockedTweetDataProvider,
|
456 |
rateLimitDataProvider:
|
457 |
defaultRateLimitDataProvider, |
458 |
//mockedRateLimitDataProvider,
|
459 |
|
460 |
/**
|
461 |
* A decorator is a function that is responsible for constructing a certain
|
462 |
* element of the widget. Most of the decorators return a HTML string.
|
463 |
* Exceptions are the mainDecorator, which defines the basic sequence of
|
464 |
* the widget's components, plus the linkDecorator, the usernameDecorator
|
465 |
* and the hashtagDecorator which return the string that is supplied as a
|
466 |
* function param, enriched with the HTML tags.
|
467 |
*
|
468 |
* For details, see the implementations of the default decorators. Each
|
469 |
* default decorator can be overwritten by your own implementation.
|
470 |
*/
|
471 |
mainDecorator: defaultMainDecorator,
|
472 |
|
473 |
tweetFeedDecorator: defaultTweetFeedDecorator,
|
474 |
|
475 |
tweetDecorator: defaultTweetDecorator,
|
476 |
tweetProfileImageDecorator: defaultTweetProfileImageDecorator,
|
477 |
tweetBodyDecorator: defaultTweetBodyDecorator,
|
478 |
tweetUsernameDecorator: defaultTweetUsernameDecorator,
|
479 |
tweetTextDecorator: defaultTweetTextDecorator,
|
480 |
|
481 |
tweetAttributesDecorator: defaultTweetAttributesDecorator,
|
482 |
tweetTwitterBirdDecorator: defaultTweetTwitterBirdDecorator,
|
483 |
tweetTimestampDecorator: defaultTweetTimestampDecorator,
|
484 |
tweetSourceDecorator: defaultTweetSourceDecorator,
|
485 |
tweetGeoLocationDecorator: defaultTweetGeoLocationDecorator,
|
486 |
tweetInReplyToDecorator: defaultTweetInReplyToDecorator,
|
487 |
tweetRetweeterDecorator: defaultTweetRetweeterDecorator,
|
488 |
|
489 |
tweetActionsDecorator: defaultTweetActionsDecorator,
|
490 |
tweetActionReplyDecorator: defaultTweetActionReplyDecorator,
|
491 |
tweetActionRetweetDecorator: defaultTweetActionRetweetDecorator,
|
492 |
tweetActionFavoriteDecorator: defaultTweetActionFavoriteDecorator,
|
493 |
|
494 |
tweetFeedControlsDecorator: defaultTweetFeedControlsDecorator,
|
495 |
tweetFeedControlsMoreBtnDecorator: defaultTweetFeedControlsMoreBtnDecorator,
|
496 |
tweetFeedControlsPrevBtnDecorator: defaultTweetFeedControlsPrevBtnDecorator,
|
497 |
tweetFeedControlsNextBtnDecorator: defaultTweetFeedControlsNextBtnDecorator,
|
498 |
|
499 |
tweetFeedAutorefreshTriggerDecorator: defaultTweetFeedAutorefreshTriggerDecorator,
|
500 |
tweetFeedAutorefreshTriggerContentDecorator: defaultTweetFeedAutorefreshTriggerContentDecorator,
|
501 |
|
502 |
connectButtonDecorator: defaultConnectButtonDecorator,
|
503 |
|
504 |
loginInfoDecorator: defaultLoginInfoDecorator,
|
505 |
loginInfoContentDecorator: defaultLoginInfoContentDecorator,
|
506 |
|
507 |
followButtonDecorator: defaultFollowButtonDecorator,
|
508 |
|
509 |
tweetBoxDecorator: defaultTweetBoxDecorator,
|
510 |
|
511 |
linkDecorator: defaultLinkDecorator,
|
512 |
usernameDecorator: defaultUsernameDecorator,
|
513 |
hashtagDecorator: defaultHashtagDecorator,
|
514 |
|
515 |
loadingDecorator: defaultLoadingDecorator,
|
516 |
errorDecorator: defaultErrorDecorator,
|
517 |
noDataDecorator: defaultNoDataDecorator,
|
518 |
|
519 |
/**
|
520 |
* Formatters are currently used for date format processing only.
|
521 |
*
|
522 |
* The tweetTimestampFormatter formats the tweet's timestamp to be shown
|
523 |
* in the tweet attributes section
|
524 |
*
|
525 |
* For details, see the implementation of the defaultTweetTimestampFormatter.
|
526 |
*/
|
527 |
tweetTimestampFormatter : defaultTweetTimestampFormatter,
|
528 |
|
529 |
/**
|
530 |
* The tweetTimestampTooltipFormatter formats the tweet's timestamp to be shown
|
531 |
* in the tooltip when hovering over the timestamp link.
|
532 |
*/
|
533 |
tweetTimestampTooltipFormatter : defaultTweetTimestampTooltipFormatter,
|
534 |
|
535 |
/**
|
536 |
* A visualizer is a function that is responsible for adding one or more
|
537 |
* elements to the DOM and thereby making them visible to the user.
|
538 |
* A visualizer might also be responsible to do the opposite effect:
|
539 |
* To remove one or more elements from the DOM.
|
540 |
*
|
541 |
* The tweetVisualizer gets called each time a tweet element should be
|
542 |
* appended or prepended to the tweet feed element.
|
543 |
*
|
544 |
* For details, see the implementation of the defaultTweetVisualizer.
|
545 |
*
|
546 |
* Each default visualizer can be overwritten by your own implementation.
|
547 |
*/
|
548 |
tweetVisualizer: defaultTweetVisualizer,
|
549 |
|
550 |
/**
|
551 |
* The loadingIndicatorVisualizer gets called each time data is retrieved
|
552 |
* from Twitter to visualize the loading indicator. This visualizer is also
|
553 |
* used to hide the loading indicator.
|
554 |
*
|
555 |
* For details, see the implementation of the defaultLoadingIndicatorVisualizer.
|
556 |
*/
|
557 |
loadingIndicatorVisualizer: defaultLoadingIndicatorVisualizer,
|
558 |
|
559 |
/**
|
560 |
* The autorefreshTriggerVisualizer will be called if the autorefresh
|
561 |
* trigger should be visualized or hidden.
|
562 |
*
|
563 |
* For details, see the implementation of the autorefreshTriggerVisualizer.
|
564 |
*/
|
565 |
autorefreshTriggerVisualizer: defaultAutorefreshTriggerVisualizer,
|
566 |
|
567 |
/**
|
568 |
* An event handler is a function that gets called whenever the event you
|
569 |
* are interested in, occurs.
|
570 |
*
|
571 |
* The onDataRequest event handler will be called immediatly before calling
|
572 |
* Twitter to retrieve new data and gives you the opportunity to deny
|
573 |
* the call by returning false from the function.
|
574 |
*
|
575 |
* This feature might be used in conjunction with the paging feature,
|
576 |
* especially when using the "endless-scroll" paging mode, to avoid the
|
577 |
* exhaustion of remaining Twitter API calls, before the rate limit is
|
578 |
* reached. The stats parameter contains statistical infos and counters
|
579 |
* that you can examine to base your decision whether to return true or
|
580 |
* false.
|
581 |
*/
|
582 |
onDataRequestHandler: defaultOnDataRequestHandler,
|
583 |
|
584 |
/**
|
585 |
* The onRateLimitData event handler is called each time
|
586 |
* jTweetsAnywhere retrieved the rate limit data from Twitter. The actual
|
587 |
* rate limit data is contained in the stats object.
|
588 |
*/
|
589 |
onRateLimitDataHandler: defaultOnRateLimitDataHandler,
|
590 |
|
591 |
/**
|
592 |
* The OnOptionsInitializingHandler event handler is called before initializing
|
593 |
* the user options
|
594 |
*/
|
595 |
onOptionsInitializingHandler: defaultOnOptionsInitializingHandler,
|
596 |
|
597 |
_tweetFeedConfig:
|
598 |
{ |
599 |
autoConformToTwitterStyleguide: false, |
600 |
showTwitterBird: true, |
601 |
showTimestamp:
|
602 |
{ |
603 |
refreshInterval: 0 |
604 |
}, |
605 |
showSource: false, |
606 |
showGeoLocation: true, |
607 |
showInReplyTo: true, |
608 |
showActionReply: false, |
609 |
showActionRetweet: false, |
610 |
showActionFavorite: false, |
611 |
showProfileImages: null, |
612 |
showUserScreenNames: null, |
613 |
showUserFullNames: false, |
614 |
expandHovercards: false, |
615 |
includeRetweets: true, |
616 |
paging:
|
617 |
{ |
618 |
mode: "none", |
619 |
_limit: 0, |
620 |
_offset: 0 |
621 |
}, |
622 |
autorefresh:
|
623 |
{ |
624 |
mode: "none", |
625 |
interval: 60, |
626 |
duration: 3600, |
627 |
max: -1, |
628 |
_startTime: null, |
629 |
_triggerElement: null |
630 |
}, |
631 |
_pageParam: 0, |
632 |
_maxId: null, |
633 |
_recLevel: 0, |
634 |
_noData: false, |
635 |
_clearBeforePopulate: false |
636 |
}, |
637 |
_tweetBoxConfig:
|
638 |
{ |
639 |
counter: true, |
640 |
width: 515, |
641 |
height: 65, |
642 |
label: null, |
643 |
defaultContent: '', |
644 |
onTweet: function(textTweet, htmlTweet) {} |
645 |
}, |
646 |
_connectButtonConfig:
|
647 |
{ |
648 |
size: "medium" |
649 |
}, |
650 |
_baseSelector: null, |
651 |
_baseElement: null, |
652 |
_tweetFeedElement: null, |
653 |
_tweetFeedControlsElement: null, |
654 |
_followButtonElement: null, |
655 |
_loginInfoElement: null, |
656 |
_connectButtonElement: null, |
657 |
_tweetBoxElement: null, |
658 |
_loadingIndicatorElement: null, |
659 |
_noDataElement: null, |
660 |
_tweetsCache: [],
|
661 |
_autorefreshTweetsCache: [],
|
662 |
_stats:
|
663 |
{ |
664 |
dataRequestCount: 0, |
665 |
rateLimitPreventionCount: 0, |
666 |
rateLimit:
|
667 |
{ |
668 |
remaining_hits: 150, |
669 |
hourly_limit: 150 |
670 |
} |
671 |
}, |
672 |
_resourceBundle: null |
673 |
}, config); |
674 |
|
675 |
// save the plugin's base selector
|
676 |
options._baseSelector = this.selector;
|
677 |
|
678 |
options.onOptionsInitializingHandler(options); |
679 |
setupOptions(options); |
680 |
|
681 |
// no main decorator? nothing to do!
|
682 |
if (!options.mainDecorator)
|
683 |
{ |
684 |
return;
|
685 |
} |
686 |
|
687 |
$.ajaxSetup({ cache: true }); |
688 |
|
689 |
return this.each(function() |
690 |
{ |
691 |
// the DOM element, where to display the widget
|
692 |
options._baseElement = $(this); |
693 |
|
694 |
// create the widget's necessary sub DOM elements
|
695 |
options._tweetFeedElement = options.tweetFeedDecorator ? $(options.tweetFeedDecorator(options)) : null; |
696 |
options._tweetFeedControlsElement = options.tweetFeedControlsDecorator ? $(options.tweetFeedControlsDecorator(options)) : null; |
697 |
options._followButtonElement = options.followButtonDecorator ? $(options.followButtonDecorator(options)) : null; |
698 |
options._tweetBoxElement = options.tweetBoxDecorator ? $(options.tweetBoxDecorator(options)) : null; |
699 |
options._connectButtonElement = options.connectButtonDecorator ? $(options.connectButtonDecorator(options)): null; |
700 |
options._loginInfoElement = options.loginInfoDecorator ? $(options.loginInfoDecorator(options)) : null; |
701 |
|
702 |
// add the widget to the DOM
|
703 |
options.mainDecorator(options); |
704 |
|
705 |
populateTweetFeed(options); |
706 |
populateAnywhereControls(options); |
707 |
|
708 |
bindEventHandlers(options); |
709 |
|
710 |
setupAutorefresh(options); |
711 |
}); |
712 |
}; |
713 |
defaultMainDecorator = function(options) |
714 |
{ |
715 |
// defines the default sequence of the widget's elements
|
716 |
if (options._tweetFeedElement)
|
717 |
{ |
718 |
options._baseElement.append(options._tweetFeedElement); |
719 |
} |
720 |
|
721 |
if (options._tweetFeedControlsElement)
|
722 |
{ |
723 |
options._baseElement.append(options._tweetFeedControlsElement); |
724 |
} |
725 |
|
726 |
if (options._connectButtonElement)
|
727 |
{ |
728 |
options._baseElement.append(options._connectButtonElement); |
729 |
} |
730 |
|
731 |
if (options._loginInfoElement)
|
732 |
{ |
733 |
options._baseElement.append(options._loginInfoElement); |
734 |
} |
735 |
|
736 |
if (options._followButtonElement)
|
737 |
{ |
738 |
options._baseElement.append(options._followButtonElement); |
739 |
} |
740 |
|
741 |
if (options._tweetBoxElement)
|
742 |
{ |
743 |
options._baseElement.append(options._tweetBoxElement); |
744 |
} |
745 |
}; |
746 |
defaultTweetFeedControlsDecorator = function(options) |
747 |
{ |
748 |
// the default tweet feed's paging controls
|
749 |
var html = ''; |
750 |
|
751 |
if (options._tweetFeedConfig.paging.mode == 'prev-next') |
752 |
{ |
753 |
if (options.tweetFeedControlsPrevBtnDecorator)
|
754 |
{ |
755 |
html += options.tweetFeedControlsPrevBtnDecorator(options); |
756 |
} |
757 |
|
758 |
if (options.tweetFeedControlsNextBtnDecorator)
|
759 |
{ |
760 |
html += options.tweetFeedControlsNextBtnDecorator(options); |
761 |
} |
762 |
} |
763 |
else if (options._tweetFeedConfig.paging.mode == 'endless-scroll') |
764 |
{ |
765 |
// nothing to do here
|
766 |
} |
767 |
else
|
768 |
{ |
769 |
if (options.tweetFeedControlsMoreBtnDecorator)
|
770 |
{ |
771 |
html += options.tweetFeedControlsMoreBtnDecorator(options); |
772 |
} |
773 |
} |
774 |
|
775 |
return '<div class="jta-tweet-list-controls">' + html + '</div>'; |
776 |
}; |
777 |
defaultTweetFeedControlsMoreBtnDecorator = function(options) |
778 |
{ |
779 |
return '<span class="jta-tweet-list-controls-button jta-tweet-list-controls-button-more">' + options._resourceBundle._('More') + '</span>'; |
780 |
}; |
781 |
defaultTweetFeedControlsPrevBtnDecorator = function(options) |
782 |
{ |
783 |
return '<span class="jta-tweet-list-controls-button jta-tweet-list-controls-button-prev">' + options._resourceBundle._('Prev') + '</span>'; |
784 |
}; |
785 |
defaultTweetFeedControlsNextBtnDecorator = function(options) |
786 |
{ |
787 |
return '<span class="jta-tweet-list-controls-button jta-tweet-list-controls-button-next">' + options._resourceBundle._('Next') + '</span>'; |
788 |
}; |
789 |
defaultTweetFeedAutorefreshTriggerDecorator = function(count, options) |
790 |
{ |
791 |
var html = ''; |
792 |
|
793 |
if (options.tweetFeedAutorefreshTriggerContentDecorator)
|
794 |
{ |
795 |
html = options.tweetFeedAutorefreshTriggerContentDecorator(count, options); |
796 |
} |
797 |
|
798 |
return '<li class="jta-tweet-list-autorefresh-trigger">' + html + '</li>'; |
799 |
}; |
800 |
defaultTweetFeedAutorefreshTriggerContentDecorator = function(count, options) |
801 |
{ |
802 |
var content = options._resourceBundle.__('%count% new tweet', '%count% new tweets', count, { '%count%' : count }); |
803 |
|
804 |
return '<span class="jta-tweet-list-autorefresh-trigger-content">' + content + '</span>'; |
805 |
}; |
806 |
defaultTweetFeedDecorator = function(options) |
807 |
{ |
808 |
// the default placeholder for the tweet feed is an unordered list
|
809 |
return '<ul class="jta-tweet-list"></ul>'; |
810 |
}; |
811 |
defaultTweetDecorator = function(tweet, options) |
812 |
{ |
813 |
// the default tweet is made of the optional user's profile image and the
|
814 |
// tweet body inside a list item element
|
815 |
var html = ''; |
816 |
|
817 |
if (options._tweetFeedConfig.showProfileImages)
|
818 |
{ |
819 |
html += options.tweetProfileImageDecorator(tweet, options); |
820 |
} |
821 |
|
822 |
if (options.tweetBodyDecorator)
|
823 |
{ |
824 |
html += options.tweetBodyDecorator(tweet, options); |
825 |
} |
826 |
|
827 |
html += '<div class="jta-clear"> </div>';
|
828 |
|
829 |
return '<li class="jta-tweet-list-item">' + html + '</li>'; |
830 |
}; |
831 |
defaultTweetProfileImageDecorator = function(tweet, options) |
832 |
{ |
833 |
// if tweet is a native retweet, use the retweet's profile
|
834 |
var t = tweet.retweeted_status || tweet;
|
835 |
|
836 |
// the default profile image decorator simply adds a link to the user's Twitter profile
|
837 |
var screenName = getScreenName(tweet);
|
838 |
var imageUrl = t.user ? t.user.profile_image_url : false || t.profile_image_url; |
839 |
|
840 |
var html =
|
841 |
'<a class="jta-tweet-profile-image-link" href="http://twitter.com/' + screenName + '" target="_blank">' + |
842 |
'<img src="' + imageUrl + '" alt="' + screenName + '"' + |
843 |
(isAnywherePresent() ? '' : (' title="' + screenName + '"')) + |
844 |
'/>' +
|
845 |
'</a>';
|
846 |
|
847 |
return '<div class="jta-tweet-profile-image">' + html + '</div>'; |
848 |
}; |
849 |
defaultTweetBodyDecorator = function(tweet, options) |
850 |
{ |
851 |
// the default tweet body contains the tweet text and the tweet's creation date
|
852 |
var html = ''; |
853 |
|
854 |
if (options.tweetTextDecorator)
|
855 |
{ |
856 |
html += options.tweetTextDecorator(tweet, options); |
857 |
} |
858 |
|
859 |
if (options.tweetAttributesDecorator)
|
860 |
{ |
861 |
html += options.tweetAttributesDecorator(tweet, options); |
862 |
} |
863 |
|
864 |
if (options.tweetActionsDecorator)
|
865 |
{ |
866 |
html += options.tweetActionsDecorator(tweet, options); |
867 |
} |
868 |
|
869 |
return '<div class="jta-tweet-body ' + |
870 |
(options._tweetFeedConfig.showProfileImages ? 'jta-tweet-body-list-profile-image-present' : '') + '">' + |
871 |
html + |
872 |
'</div>';
|
873 |
}; |
874 |
defaultTweetTextDecorator = function(tweet, options) |
875 |
{ |
876 |
var tweetText = tweet.text;
|
877 |
|
878 |
// if usernames should be visible and tweet is a native retweet, use
|
879 |
// the original tweet text
|
880 |
if (tweet.retweeted_status &&
|
881 |
( |
882 |
options._tweetFeedConfig.showUserScreenNames || |
883 |
options._tweetFeedConfig.showUserScreenNames == null ||
|
884 |
options._tweetFeedConfig.showUserFullNames || |
885 |
options._tweetFeedConfig.showUserFullNames == null
|
886 |
) |
887 |
) |
888 |
{ |
889 |
tweetText = tweet.retweeted_status.text; |
890 |
} |
891 |
|
892 |
// the default tweet text decorator optionally marks links, @usernames,
|
893 |
// and #hashtags
|
894 |
if (options.linkDecorator)
|
895 |
{ |
896 |
tweetText = options.linkDecorator(tweetText, options); |
897 |
} |
898 |
|
899 |
if (options.usernameDecorator)
|
900 |
{ |
901 |
tweetText = options.usernameDecorator(tweetText, options); |
902 |
} |
903 |
|
904 |
if (options.hashtagDecorator)
|
905 |
{ |
906 |
tweetText = options.hashtagDecorator(tweetText, options); |
907 |
} |
908 |
|
909 |
if (options._tweetFeedConfig.showUserScreenNames ||
|
910 |
options._tweetFeedConfig.showUserFullNames || |
911 |
tweet.retweeted_status && |
912 |
( |
913 |
options._tweetFeedConfig.showUserScreenNames == null ||
|
914 |
options._tweetFeedConfig.showUserFullNames == null
|
915 |
) |
916 |
) |
917 |
{ |
918 |
tweetText = options.tweetUsernameDecorator(tweet, options) + ' ' + tweetText;
|
919 |
} |
920 |
|
921 |
return '<span class="jta-tweet-text">' + tweetText + '</span>'; |
922 |
}; |
923 |
defaultTweetUsernameDecorator = function(tweet, options) |
924 |
{ |
925 |
// if tweet is a native retweet, use the retweet's profile
|
926 |
var screenName = getScreenName(tweet);
|
927 |
var fullName = getFullName(tweet);
|
928 |
|
929 |
var htmlScreenName = null; |
930 |
if (screenName && (options._tweetFeedConfig.showUserScreenNames || (options._tweetFeedConfig.showUserScreenNames == null && tweet.retweeted_status))) |
931 |
{ |
932 |
htmlScreenName = |
933 |
'<span class="jta-tweet-user-screen-name">' +
|
934 |
'<a class="jta-tweet-user-screen-name-link" href="http://twitter.com/' + screenName + '" target="_blank">' + |
935 |
screenName + |
936 |
'</a>' +
|
937 |
'</span>';
|
938 |
} |
939 |
|
940 |
var htmlFullName = null; |
941 |
if (fullName && (options._tweetFeedConfig.showUserFullNames || (options._tweetFeedConfig.showUserFullNames == null && tweet.retweeted_status))) |
942 |
{ |
943 |
htmlFullName = |
944 |
'<span class="jta-tweet-user-full-name">' +
|
945 |
(htmlScreenName ? ' ' : '') + |
946 |
'<a class="jta-tweet-user-full-name-link" href="http://twitter.com/' + screenName + '" name="' + screenName + '" target="_blank">' + |
947 |
fullName + |
948 |
'</a>' +
|
949 |
'</span>';
|
950 |
} |
951 |
|
952 |
var html = ''; |
953 |
|
954 |
if (htmlScreenName)
|
955 |
{ |
956 |
html += htmlScreenName; |
957 |
} |
958 |
|
959 |
if (htmlFullName)
|
960 |
{ |
961 |
if (htmlScreenName)
|
962 |
{ |
963 |
html += ' ';
|
964 |
} |
965 |
|
966 |
html += htmlFullName; |
967 |
} |
968 |
|
969 |
if (htmlScreenName || htmlFullName)
|
970 |
{ |
971 |
html = |
972 |
'<span class="jta-tweet-user-name">' +
|
973 |
(tweet.retweeted_status ? 'RT ' : '') + |
974 |
html + |
975 |
'</span>';
|
976 |
} |
977 |
|
978 |
return html;
|
979 |
}; |
980 |
defaultTweetAttributesDecorator = function(tweet, options) |
981 |
{ |
982 |
var html = ''; |
983 |
|
984 |
if (options.tweetTwitterBirdDecorator ||
|
985 |
options.tweetTimestampDecorator || |
986 |
options.tweetSourceDecorator || |
987 |
options.tweetGeoLocationDecorator || |
988 |
options.tweetInReplyToDecorator || |
989 |
(tweet.retweeted_status && options.tweetRetweeterDecorator) |
990 |
) |
991 |
{ |
992 |
html += '<span class="jta-tweet-attributes">';
|
993 |
|
994 |
if (options.tweetTwitterBirdDecorator)
|
995 |
{ |
996 |
html += options.tweetTwitterBirdDecorator(tweet, options); |
997 |
} |
998 |
|
999 |
if (options.tweetTimestampDecorator)
|
1000 |
{ |
1001 |
html += options.tweetTimestampDecorator(tweet, options); |
1002 |
} |
1003 |
|
1004 |
if (options.tweetSourceDecorator)
|
1005 |
{ |
1006 |
html += options.tweetSourceDecorator(tweet, options); |
1007 |
} |
1008 |
|
1009 |
if (options.tweetGeoLocationDecorator)
|
1010 |
{ |
1011 |
html += options.tweetGeoLocationDecorator(tweet, options); |
1012 |
} |
1013 |
|
1014 |
if (options.tweetInReplyToDecorator)
|
1015 |
{ |
1016 |
html += options.tweetInReplyToDecorator(tweet, options); |
1017 |
} |
1018 |
|
1019 |
if (tweet.retweeted_status && options.tweetRetweeterDecorator)
|
1020 |
{ |
1021 |
html += options.tweetRetweeterDecorator(tweet, options); |
1022 |
} |
1023 |
|
1024 |
html += '</span>';
|
1025 |
} |
1026 |
|
1027 |
return html;
|
1028 |
}; |
1029 |
defaultTweetTimestampDecorator = function(tweet, options) |
1030 |
{ |
1031 |
// the default tweet timestamp decorator does a little bit of Twitter like formatting.
|
1032 |
|
1033 |
// if tweet is a native retweet, use the retweet's timestamp
|
1034 |
var tw = tweet.retweeted_status || tweet;
|
1035 |
|
1036 |
// reformat timestamp from Twitter, so IE is happy
|
1037 |
var createdAt = formatDate(tw.created_at);
|
1038 |
|
1039 |
// format the timestamp by the tweetTimestampFormatter
|
1040 |
var tweetTimestamp = options.tweetTimestampFormatter(createdAt, options);
|
1041 |
var tweetTimestampTooltip = options.tweetTimestampTooltipFormatter(createdAt);
|
1042 |
|
1043 |
var html =
|
1044 |
'<span class="jta-tweet-timestamp">' +
|
1045 |
'<a class="jta-tweet-timestamp-link" data-timestamp="' + createdAt +
|
1046 |
'" href="http://twitter.com/' + getScreenName(tweet) + '/status/' + tw.id + '" target="_blank" title="' + |
1047 |
tweetTimestampTooltip + '">' +
|
1048 |
tweetTimestamp + |
1049 |
'</a>' +
|
1050 |
'</span>';
|
1051 |
|
1052 |
return html;
|
1053 |
}; |
1054 |
defaultTweetTwitterBirdDecorator = function(tweet, options) |
1055 |
{ |
1056 |
var screenName = getScreenName(tweet);
|
1057 |
var intentUrl = 'https://twitter.com/intent/user?screen_name=' + screenName; |
1058 |
var linkTitle = screenName + ' ' + options._resourceBundle._('on Twitter'); |
1059 |
|
1060 |
var html =
|
1061 |
'<span class="jta-tweet-twitter-bird">' +
|
1062 |
'<a href="' + intentUrl + '" target="_blank" title="' + linkTitle + '">' + |
1063 |
'<span class="jta-tweet-twitter-bird-icon"> </span>' +
|
1064 |
'</a>' +
|
1065 |
'</span>';
|
1066 |
|
1067 |
return html;
|
1068 |
}; |
1069 |
defaultTweetTimestampTooltipFormatter = function(timeStamp) |
1070 |
{ |
1071 |
var d = new Date(timeStamp); |
1072 |
|
1073 |
return d.toLocaleString();
|
1074 |
}; |
1075 |
defaultTweetTimestampFormatter = function(timeStamp, options) |
1076 |
{ |
1077 |
var now = new Date(); |
1078 |
|
1079 |
var diff = parseInt((now.getTime() - Date.parse(timeStamp)) / 1000); |
1080 |
|
1081 |
var tweetTimestamp = ''; |
1082 |
if (diff < 60) |
1083 |
{ |
1084 |
tweetTimestamp += options._resourceBundle.__('%secs% second ago', '%secs% seconds ago', diff, { '%secs%': diff }); |
1085 |
} |
1086 |
else if (diff < 3600) |
1087 |
{ |
1088 |
var t = parseInt((diff + 30) / 60); |
1089 |
tweetTimestamp += options._resourceBundle.__('%mins% minute ago', '%mins% minutes ago', t, { '%mins%': t }); |
1090 |
} |
1091 |
else if (diff < 86400) |
1092 |
{ |
1093 |
var t = parseInt((diff + 1800) / 3600); |
1094 |
tweetTimestamp += options._resourceBundle.__('%hours% hour ago', '%hours% hours ago', t, { '%hours%': t }); |
1095 |
} |
1096 |
else
|
1097 |
{ |
1098 |
var d = new Date(timeStamp); |
1099 |
|
1100 |
var monthName = options._resourceBundle._('$$monthNames'); |
1101 |
tweetTimestamp += monthName[d.getMonth()] + ' ' + d.getDate();
|
1102 |
|
1103 |
if (d.getFullYear() < now.getFullYear())
|
1104 |
{ |
1105 |
tweetTimestamp += ', ' + d.getFullYear();
|
1106 |
} |
1107 |
|
1108 |
var t = parseInt((diff + 43200) / 86400); |
1109 |
tweetTimestamp += ' (' + options._resourceBundle.__('%days% day ago', '%days% days ago', t, { '%days%': t }) + ')'; |
1110 |
} |
1111 |
|
1112 |
return tweetTimestamp;
|
1113 |
}; |
1114 |
defaultTweetSourceDecorator = function(tweet, options) |
1115 |
{ |
1116 |
// if tweet is a native retweet, use the retweet's source
|
1117 |
var tw = tweet.retweeted_status || tweet;
|
1118 |
|
1119 |
var source = tw.source.replace(/\<\;/gi,'<').replace(/\>\;/gi,'>').replace(/\"\;/gi,'"'); |
1120 |
var html =
|
1121 |
'<span class="jta-tweet-source">' +
|
1122 |
' ' + options._resourceBundle._('via') + ' ' + |
1123 |
'<span class="jta-tweet-source-link">' +
|
1124 |
source + |
1125 |
'</span>' +
|
1126 |
'</span>';
|
1127 |
|
1128 |
return html;
|
1129 |
}; |
1130 |
defaultTweetGeoLocationDecorator = function(tweet, options) |
1131 |
{ |
1132 |
var html = ''; |
1133 |
|
1134 |
// if tweet is a native retweet, use the retweet's source
|
1135 |
var tw = tweet.retweeted_status || tweet;
|
1136 |
|
1137 |
var q = null; |
1138 |
if (tw.geo && tw.geo.coordinates)
|
1139 |
{ |
1140 |
q = tw.geo.coordinates.join(); |
1141 |
} |
1142 |
else if (tw.place && tw.place.full_name) |
1143 |
{ |
1144 |
q = tw.place.full_name; |
1145 |
} |
1146 |
|
1147 |
if (q)
|
1148 |
{ |
1149 |
var location = options._resourceBundle._('here'); |
1150 |
if (tw.place && tw.place.full_name)
|
1151 |
{ |
1152 |
location = tw.place.full_name; |
1153 |
} |
1154 |
|
1155 |
var link = 'http://maps.google.com/maps?q=' + q; |
1156 |
|
1157 |
html = |
1158 |
'<span class="jta-tweet-location">' +
|
1159 |
' ' + options._resourceBundle._('from') + ' ' + |
1160 |
'<a class="jta-tweet-location-link" href="' + link + '" target="_blank">' + |
1161 |
location + |
1162 |
'</a>' +
|
1163 |
'</span>';
|
1164 |
} |
1165 |
|
1166 |
return html;
|
1167 |
}; |
1168 |
defaultTweetInReplyToDecorator = function(tweet, options) |
1169 |
{ |
1170 |
// if tweet is a native retweet, use the retweet's source
|
1171 |
var tw = tweet.retweeted_status || tweet;
|
1172 |
|
1173 |
var html = ''; |
1174 |
|
1175 |
if (tw.in_reply_to_status_id && tw.in_reply_to_screen_name)
|
1176 |
{ |
1177 |
var linkHref = 'http://twitter.com/' + tw.in_reply_to_screen_name + '/status/' + tw.in_reply_to_status_id; |
1178 |
var linkText = options._resourceBundle._('in reply to') + ' ' + tw.in_reply_to_screen_name; |
1179 |
|
1180 |
html = |
1181 |
'<span class="jta-tweet-inreplyto">' +
|
1182 |
' ' +
|
1183 |
'<a class="jta-tweet-inreplyto-link" href="' + linkHref + '" target="_blank">' + |
1184 |
linkText + |
1185 |
'</a>' +
|
1186 |
'</span>';
|
1187 |
} |
1188 |
|
1189 |
return html;
|
1190 |
}; |
1191 |
defaultTweetRetweeterDecorator = function(tweet, options) |
1192 |
{ |
1193 |
var html = ''; |
1194 |
|
1195 |
if (tweet.retweeted_status)
|
1196 |
{ |
1197 |
var screenName = getUserScreenName(tweet);
|
1198 |
|
1199 |
var rtc = (tweet.retweeted_status.retweet_count || 0) - 1; |
1200 |
|
1201 |
var link =
|
1202 |
'<a class="jta-tweet-retweeter-link" href="http://twitter.com/' + screenName + '" target="_blank">' + |
1203 |
screenName + |
1204 |
'</a>';
|
1205 |
|
1206 |
var rtcount = options._resourceBundle.__(' and %rtc% other', ' and %rtc% others', rtc, { '%rtc%': rtc }); |
1207 |
|
1208 |
html = |
1209 |
'<br/>' +
|
1210 |
'<span class="jta-tweet-retweeter">' +
|
1211 |
options._resourceBundle._('Retweeted by') + ' ' + link + |
1212 |
(rtc > 0 ? rtcount : '') + |
1213 |
'</span>';
|
1214 |
} |
1215 |
|
1216 |
return html;
|
1217 |
}; |
1218 |
defaultTweetActionsDecorator = function(tweet, options) |
1219 |
{ |
1220 |
var html = ''; |
1221 |
|
1222 |
if (options.tweetActionReplyDecorator ||
|
1223 |
options.tweetActionRetweetDecorator || |
1224 |
options.tweetActionFavoriteDecorator |
1225 |
) |
1226 |
{ |
1227 |
html += '<span class="jta-tweet-actions">';
|
1228 |
|
1229 |
if (options.tweetActionReplyDecorator)
|
1230 |
{ |
1231 |
html += options.tweetActionReplyDecorator(tweet, options); |
1232 |
} |
1233 |
|
1234 |
if (options.tweetActionRetweetDecorator)
|
1235 |
{ |
1236 |
html += options.tweetActionRetweetDecorator(tweet, options); |
1237 |
} |
1238 |
|
1239 |
if (options.tweetActionFavoriteDecorator)
|
1240 |
{ |
1241 |
html += options.tweetActionFavoriteDecorator(tweet, options); |
1242 |
} |
1243 |
|
1244 |
html += '</span>';
|
1245 |
} |
1246 |
|
1247 |
return html;
|
1248 |
}; |
1249 |
defaultTweetActionReplyDecorator = function(tweet, options) |
1250 |
{ |
1251 |
var intentUrl = 'https://twitter.com/intent/tweet?in_reply_to=' + tweet.id; |
1252 |
var actionLabel = options._resourceBundle._('Reply'); |
1253 |
|
1254 |
var html =
|
1255 |
'<span class="jta-tweet-action-reply">' +
|
1256 |
'<a href="' + intentUrl + '">' + actionLabel + '</a>' + |
1257 |
'</span>';
|
1258 |
|
1259 |
return html;
|
1260 |
}; |
1261 |
defaultTweetActionRetweetDecorator = function(tweet, options) |
1262 |
{ |
1263 |
var intentUrl = 'https://twitter.com/intent/retweet?tweet_id=' + tweet.id; |
1264 |
var actionLabel = options._resourceBundle._('Retweet'); |
1265 |
|
1266 |
var html =
|
1267 |
'<span class="jta-tweet-action-retweet">' +
|
1268 |
'<a href="' + intentUrl + '">' + actionLabel + '</a>' + |
1269 |
'</span>';
|
1270 |
|
1271 |
return html;
|
1272 |
}; |
1273 |
defaultTweetActionFavoriteDecorator = function(tweet, options) |
1274 |
{ |
1275 |
var intentUrl = 'https://twitter.com/intent/favorite?tweet_id=' + tweet.id; |
1276 |
var actionLabel = options._resourceBundle._('Favorite'); |
1277 |
|
1278 |
var html =
|
1279 |
'<span class="jta-tweet-action-favorite">' +
|
1280 |
'<a href="' + intentUrl + '">' + actionLabel + '</a>' + |
1281 |
'</span>';
|
1282 |
|
1283 |
return html;
|
1284 |
}; |
1285 |
defaultConnectButtonDecorator = function(options) |
1286 |
{ |
1287 |
// the default placeholder for the @Anywhere ConnectButton
|
1288 |
return '<div class="jta-connect-button"></div>'; |
1289 |
}; |
1290 |
defaultLoginInfoDecorator = function(options) |
1291 |
{ |
1292 |
// the default placeholder for the LoginInfo
|
1293 |
return '<div class="jta-login-info"></div>'; |
1294 |
}; |
1295 |
defaultLoginInfoContentDecorator = function(options, T) |
1296 |
{ |
1297 |
// the default markup of the LoginInfo content: the user's profile image, the
|
1298 |
// user's screen_name and a "button" to sign out
|
1299 |
var html = ''; |
1300 |
|
1301 |
if (T.isConnected())
|
1302 |
{ |
1303 |
var screenName = T.currentUser.data('screen_name'); |
1304 |
var imageUrl = T.currentUser.data('profile_image_url'); |
1305 |
|
1306 |
html = |
1307 |
'<div class="jta-login-info-profile-image">' +
|
1308 |
'<a href="http://twitter.com/' + screenName + '" target="_blank">' + |
1309 |
'<img src="' + imageUrl + '" alt="' + screenName + '" title="' + screenName + '"/>' + |
1310 |
'</a>' +
|
1311 |
'</div>' +
|
1312 |
'<div class="jta-login-info-block">' +
|
1313 |
'<div class="jta-login-info-screen-name">' +
|
1314 |
'<a href="http://twitter.com/' + screenName + '" target="_blank">' + screenName + '</a>' + |
1315 |
'</div>' +
|
1316 |
'<div class="jta-login-info-sign-out">' +
|
1317 |
options._resourceBundle._('Sign out') +
|
1318 |
'</div>' +
|
1319 |
'</div>' +
|
1320 |
'<div class="jta-clear"> </div>'
|
1321 |
; |
1322 |
} |
1323 |
|
1324 |
return html;
|
1325 |
}; |
1326 |
defaultFollowButtonDecorator = function(options) |
1327 |
{ |
1328 |
// the default placeholder for the @Anywhere FollowButton
|
1329 |
return '<div class="jta-follow-button"></div>'; |
1330 |
}; |
1331 |
defaultTweetBoxDecorator = function(options) |
1332 |
{ |
1333 |
// the default placeholder for the @Anywhere TweetBox
|
1334 |
return '<div class="jta-tweet-box"></div>'; |
1335 |
}; |
1336 |
defaultLinkDecorator = function(text, options) |
1337 |
{ |
1338 |
// the regex to markup links
|
1339 |
return text.replace(/((ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi,'<a href="$1" class="jta-tweet-a jta-tweet-link" target="_blank" rel="nofollow">$1<\/a>'); |
1340 |
}; |
1341 |
defaultUsernameDecorator = function(text, options) |
1342 |
{ |
1343 |
// the regex to markup @usernames. if @Anywhere is present the task is left to
|
1344 |
// them
|
1345 |
return isAnywherePresent() ? text : text.replace(/\B@(\w+)/gi,'@<a href="http://twitter.com/$1" class="jta-tweet-a twitter-anywhere-user" target="_blank" rel="nofollow">$1<\/a>'); |
1346 |
}; |
1347 |
defaultHashtagDecorator = function(text, options) |
1348 |
{ |
1349 |
// the regex to markup #hashtags
|
1350 |
return text.replace(/#([a-zA-Z0-9_]+)/gi,'<a href="http://search.twitter.com/search?q=%23$1" class="jta-tweet-a jta-tweet-hashtag" title="#$1" target="_blank" rel="nofollow">#$1<\/a>'); |
1351 |
}; |
1352 |
defaultLoadingDecorator = function(options) |
1353 |
{ |
1354 |
// the default loading decorator simply says: loading ...
|
1355 |
return '<li class="jta-loading">' + options._resourceBundle._('loading') + ' ...</li>'; |
1356 |
}; |
1357 |
defaultErrorDecorator = function(errorText, options) |
1358 |
{ |
1359 |
// the default error decorator shows the error message
|
1360 |
return '<li class="jta-error">' + options._resourceBundle._('ERROR') + ': ' + errorText + '</li>'; |
1361 |
}; |
1362 |
defaultNoDataDecorator = function(options) |
1363 |
{ |
1364 |
// the default no-data decorator simply says: No more data
|
1365 |
return '<li class="jta-nodata">' + options._resourceBundle._('No more data') + '</li>'; |
1366 |
}; |
1367 |
|
1368 |
defaultTweetFilter = function(tweet, options) |
1369 |
{ |
1370 |
return true; |
1371 |
}; |
1372 |
|
1373 |
defaultTweetVisualizer = function(tweetFeedElement, tweetElement, inserter, options) |
1374 |
{ |
1375 |
// insert (append/prepend) the tweetElement to the tweetFeedElement
|
1376 |
tweetFeedElement[inserter](tweetElement); |
1377 |
}; |
1378 |
defaultLoadingIndicatorVisualizer = function(tweetFeedElement, loadingIndicatorElement, options, callback) |
1379 |
{ |
1380 |
defaultVisualizer(tweetFeedElement, loadingIndicatorElement, 'append', 'fadeIn', 600, 'fadeOut', 200, callback); |
1381 |
}; |
1382 |
defaultAutorefreshTriggerVisualizer = function(tweetFeedElement, triggerElement, options, callback) |
1383 |
{ |
1384 |
defaultVisualizer(tweetFeedElement, triggerElement, 'prepend', 'slideDown', 600, 'fadeOut', 200, callback); |
1385 |
}; |
1386 |
defaultVisualizer = function(container, element, inserter, effectIn, durationIn, effectOut, durationOut, callback) |
1387 |
{ |
1388 |
// if param container is null element has to be removed from
|
1389 |
// the DOM, else element has to be inserted in container
|
1390 |
|
1391 |
// if param callback is not null, the callback function must be called
|
1392 |
// in any case, if the visualizer is done
|
1393 |
|
1394 |
var cb = function() |
1395 |
{ |
1396 |
if (callback)
|
1397 |
{ |
1398 |
callback(); |
1399 |
} |
1400 |
}; |
1401 |
|
1402 |
if (container)
|
1403 |
{ |
1404 |
element.hide(); |
1405 |
container[inserter](element); |
1406 |
element[effectIn](durationIn, cb); |
1407 |
} |
1408 |
else
|
1409 |
{ |
1410 |
element[effectOut](durationOut, function()
|
1411 |
{ |
1412 |
element.remove(); |
1413 |
cb(); |
1414 |
}); |
1415 |
} |
1416 |
}; |
1417 |
defaultOnDataRequestHandler = function(stats, options) |
1418 |
{ |
1419 |
return true; |
1420 |
}; |
1421 |
defaultOnRateLimitDataHandler = function(stats, options) |
1422 |
{ |
1423 |
}; |
1424 |
defaultOnOptionsInitializingHandler = function(options) |
1425 |
{ |
1426 |
}; |
1427 |
updateLoginInfoElement = function(options, T) |
1428 |
{ |
1429 |
// update the content of the LoginInfo element
|
1430 |
if (options._loginInfoElement && options.loginInfoContentDecorator)
|
1431 |
{ |
1432 |
options._loginInfoElement.children().remove(); |
1433 |
options._loginInfoElement.append(options.loginInfoContentDecorator(options, T)); |
1434 |
$(options._baseSelector + ' .jta-login-info-sign-out').bind('click', function() |
1435 |
{ |
1436 |
twttr.anywhere.signOut(); |
1437 |
}); |
1438 |
} |
1439 |
}; |
1440 |
getFeedUrl = function(options, flPaging) |
1441 |
{ |
1442 |
// create the Twitter API URL based on the configuration options
|
1443 |
var url = ('https:' == document.location.protocol ? 'https:' : 'http:'); |
1444 |
|
1445 |
if (options.searchParams)
|
1446 |
{ |
1447 |
url += '//search.twitter.com/search.json?' +
|
1448 |
((options.searchParams instanceof Array) ? options.searchParams.join('&') : options.searchParams) + |
1449 |
'&rpp=100';
|
1450 |
} |
1451 |
else if (options.list) |
1452 |
{ |
1453 |
if ('favorites' == options.list) |
1454 |
{ |
1455 |
url += '//api.twitter.com/1/favorites/' + options.username + '.json?count=20'; |
1456 |
} |
1457 |
else
|
1458 |
{ |
1459 |
url += '//api.twitter.com/1/' + options.username + '/lists/' + options.list + '/statuses.json?per_page=20'; |
1460 |
} |
1461 |
} |
1462 |
else
|
1463 |
{ |
1464 |
url += '//api.twitter.com/1/statuses/user_timeline.json?screen_name=' + options.username + '&count=20'; |
1465 |
if (options._tweetFeedConfig.includeRetweets)
|
1466 |
url += '&include_rts=true';
|
1467 |
} |
1468 |
|
1469 |
if (flPaging)
|
1470 |
{ |
1471 |
url += |
1472 |
(options._tweetFeedConfig._maxId ? '&max_id=' + options._tweetFeedConfig._maxId : '') + |
1473 |
'&page=' + options._tweetFeedConfig._pageParam;
|
1474 |
} |
1475 |
|
1476 |
url += '&callback=?';
|
1477 |
|
1478 |
return url;
|
1479 |
}; |
1480 |
isAnywherePresent = function() |
1481 |
{ |
1482 |
// check, if @Anywhere is present
|
1483 |
return (typeof(twttr) != 'undefined' && typeof(twttr.anywhere) != 'undefined'); |
1484 |
}; |
1485 |
clearTweetFeed = function(options) |
1486 |
{ |
1487 |
if (options._tweetFeedElement)
|
1488 |
{ |
1489 |
options._tweetFeedElement.empty(); |
1490 |
} |
1491 |
}; |
1492 |
setupOptions = function(options) |
1493 |
{ |
1494 |
options._resourceBundle = JTA_I18N.getResourceBundle('jTweetsAnywhere', options.locale);
|
1495 |
|
1496 |
options._tweetBoxConfig.label = options._resourceBundle._("What's happening?");
|
1497 |
|
1498 |
// if username is an array, create the search query and flatten username
|
1499 |
if (typeof(options.username) != 'string') |
1500 |
{ |
1501 |
if (!options.searchParams)
|
1502 |
{ |
1503 |
options.searchParams = ['q=from:' + options.username.join(" OR from:")]; |
1504 |
} |
1505 |
|
1506 |
options.username = options.username[0];
|
1507 |
} |
1508 |
|
1509 |
// if showTweetFeed is not set to a boolean value, we expect the configuration of
|
1510 |
// the tweet feed
|
1511 |
if (typeof(options.showTweetFeed) == 'object') |
1512 |
{ |
1513 |
$.extend(true, options._tweetFeedConfig, options.showTweetFeed); |
1514 |
} |
1515 |
|
1516 |
// if showTweetBox is not set to a boolean value, we expect the configuration of
|
1517 |
// the TweetBox
|
1518 |
if (typeof(options.showTweetBox) == 'object') |
1519 |
{ |
1520 |
$.extend(true, options._tweetBoxConfig, options.showTweetBox); |
1521 |
options.showTweetBox = true;
|
1522 |
} |
1523 |
|
1524 |
// if showConnectButton is not set to a boolean value, we expect the
|
1525 |
// configuration of the Connect Button
|
1526 |
if (typeof(options.showConnectButton) == 'object') |
1527 |
{ |
1528 |
options._connectButtonConfig = options.showConnectButton; |
1529 |
options.showConnectButton = true;
|
1530 |
} |
1531 |
|
1532 |
// to be compatible, check the deprecated option 'tweetProfileImagePresent'
|
1533 |
if (options._tweetFeedConfig.showProfileImages == null) |
1534 |
{ |
1535 |
options._tweetFeedConfig.showProfileImages = options.tweetProfileImagePresent; |
1536 |
} |
1537 |
|
1538 |
// if _tweetFeedConfig.showProfileImages is not set to a boolean value,
|
1539 |
// we decide to show a profile image if the feed represents a user's
|
1540 |
// list or the results of a Twitter search
|
1541 |
if (options._tweetFeedConfig.showProfileImages == null) |
1542 |
{ |
1543 |
options._tweetFeedConfig.showProfileImages = (options.list || options.searchParams) && options.tweetProfileImageDecorator; |
1544 |
} |
1545 |
|
1546 |
// handle the autoConformToTwitterStyleguide
|
1547 |
if (options._tweetFeedConfig.autoConformToTwitterStyleguide)
|
1548 |
{ |
1549 |
options._tweetFeedConfig.showUserFullNames = null;
|
1550 |
options._tweetFeedConfig.showTwitterBird = true;
|
1551 |
options._tweetFeedConfig.showActionReply = true;
|
1552 |
options._tweetFeedConfig.showActionRetweet = true;
|
1553 |
options._tweetFeedConfig.showActionFavorite = true;
|
1554 |
} |
1555 |
|
1556 |
// if _tweetFeedConfig.showUserScreenNames is not set to a boolean value,
|
1557 |
// we decide to show a username if the feed represents a user's
|
1558 |
// list or the results of a Twitter search or a tweet is a native retweet
|
1559 |
if (options._tweetFeedConfig.showUserScreenNames == null) |
1560 |
{ |
1561 |
if (options.list || options.searchParams)
|
1562 |
{ |
1563 |
options._tweetFeedConfig.showUserScreenNames = true;
|
1564 |
} |
1565 |
|
1566 |
if (!options.tweetUsernameDecorator)
|
1567 |
{ |
1568 |
options._tweetFeedConfig.showUserScreenNames = false;
|
1569 |
} |
1570 |
} |
1571 |
|
1572 |
// if _tweetFeedConfig.showUserFullNames is not set to a boolean value,
|
1573 |
// we decide to show a user's full name if the feed represents a user's
|
1574 |
// list or the results of a Twitter search or a tweet is a native retweet
|
1575 |
if (options._tweetFeedConfig.showUserFullNames == null) |
1576 |
{ |
1577 |
if (options.list || options.searchParams)
|
1578 |
{ |
1579 |
options._tweetFeedConfig.showUserFullNames = true;
|
1580 |
} |
1581 |
|
1582 |
if (!options.tweetUsernameDecorator)
|
1583 |
{ |
1584 |
options._tweetFeedConfig.showUserFullNames = false;
|
1585 |
} |
1586 |
} |
1587 |
|
1588 |
|
1589 |
options.count = validateRange(options.count, 0, options.searchParams ? 100 : 20); |
1590 |
|
1591 |
options._tweetFeedConfig.autorefresh.interval = Math.max(30, options._tweetFeedConfig.autorefresh.interval);
|
1592 |
if (options._tweetFeedConfig.autorefresh.max <= 0) |
1593 |
{ |
1594 |
options._tweetFeedConfig.autorefresh.max = -1;
|
1595 |
} |
1596 |
options._tweetFeedConfig.paging._offset = 0;
|
1597 |
options._tweetFeedConfig.paging._limit = options.count; |
1598 |
|
1599 |
// internally, the decision of what parts of a widget are to be
|
1600 |
// displayed is based on the existence of the decorators
|
1601 |
if (options.count == 0 || !options.showTweetFeed) |
1602 |
{ |
1603 |
options.tweetFeedDecorator = null;
|
1604 |
options.tweetFeedControlsDecorator = null;
|
1605 |
} |
1606 |
|
1607 |
if (options._tweetFeedConfig.paging.mode == 'none') |
1608 |
{ |
1609 |
options.tweetFeedControlsDecorator = null;
|
1610 |
} |
1611 |
|
1612 |
if (!options.showFollowButton)
|
1613 |
{ |
1614 |
options.followButtonDecorator = null;
|
1615 |
} |
1616 |
|
1617 |
if (!options.showTweetBox)
|
1618 |
{ |
1619 |
options.tweetBoxDecorator = null;
|
1620 |
} |
1621 |
|
1622 |
if (!options.showConnectButton)
|
1623 |
{ |
1624 |
options.connectButtonDecorator = null;
|
1625 |
} |
1626 |
|
1627 |
if (!options.showLoginInfo)
|
1628 |
{ |
1629 |
options.loginInfoDecorator = null;
|
1630 |
} |
1631 |
|
1632 |
if (!options._tweetFeedConfig.showTwitterBird)
|
1633 |
{ |
1634 |
options.tweetTwitterBirdDecorator = null;
|
1635 |
} |
1636 |
|
1637 |
if (!options._tweetFeedConfig.showTimestamp)
|
1638 |
{ |
1639 |
options.tweetTimestampDecorator = null;
|
1640 |
} |
1641 |
|
1642 |
if (!options._tweetFeedConfig.showSource)
|
1643 |
{ |
1644 |
options.tweetSourceDecorator = null;
|
1645 |
} |
1646 |
|
1647 |
if (!options._tweetFeedConfig.showGeoLocation)
|
1648 |
{ |
1649 |
options.tweetGeoLocationDecorator = null;
|
1650 |
} |
1651 |
|
1652 |
if (!options._tweetFeedConfig.showInReplyTo)
|
1653 |
{ |
1654 |
options.tweetInReplyToDecorator = null;
|
1655 |
} |
1656 |
|
1657 |
if (!options._tweetFeedConfig.showActionReply)
|
1658 |
{ |
1659 |
options.tweetActionReplyDecorator = null;
|
1660 |
} |
1661 |
|
1662 |
if (!options._tweetFeedConfig.showActionRetweet)
|
1663 |
{ |
1664 |
options.tweetActionRetweetDecorator = null;
|
1665 |
} |
1666 |
|
1667 |
if (!options._tweetFeedConfig.showActionFavorite)
|
1668 |
{ |
1669 |
options.tweetActionFavoriteDecorator = null;
|
1670 |
} |
1671 |
}; |
1672 |
setupAutorefresh = function(options) |
1673 |
{ |
1674 |
options._tweetFeedConfig.autorefresh._startTime = new Date().getTime();
|
1675 |
|
1676 |
startAutorefresh(options); |
1677 |
startTimestampRefresh(options); |
1678 |
}; |
1679 |
populateTweetFeed = function(options) |
1680 |
{ |
1681 |
// if a tweet feed is to be displayed, get the tweets and show them
|
1682 |
if (options.tweetDecorator && options._tweetFeedElement)
|
1683 |
{ |
1684 |
getPagedTweets(options, function(tweets, options)
|
1685 |
{ |
1686 |
if (options._tweetFeedConfig._clearBeforePopulate)
|
1687 |
{ |
1688 |
clearTweetFeed(options); |
1689 |
} |
1690 |
|
1691 |
hideLoadingIndicator(options, function()
|
1692 |
{ |
1693 |
// process the tweets
|
1694 |
$.each(tweets, function(idx, tweet) |
1695 |
{ |
1696 |
// decorate the tweet and give it to the tweet visualizer
|
1697 |
options.tweetVisualizer( |
1698 |
options._tweetFeedElement, |
1699 |
$(options.tweetDecorator(tweet, options)),
|
1700 |
'append',
|
1701 |
options |
1702 |
); |
1703 |
}); |
1704 |
|
1705 |
if (options._tweetFeedConfig._noData && options.noDataDecorator && !options._tweetFeedConfig._noDataElement)
|
1706 |
{ |
1707 |
options._tweetFeedConfig._noDataElement = $(options.noDataDecorator(options));
|
1708 |
options._tweetFeedElement.append(options._tweetFeedConfig._noDataElement); |
1709 |
} |
1710 |
|
1711 |
if (options._tweetFeedConfig._clearBeforePopulate)
|
1712 |
{ |
1713 |
options._tweetFeedElement.scrollTop(0);
|
1714 |
} |
1715 |
|
1716 |
addHovercards(options); |
1717 |
}); |
1718 |
}); |
1719 |
} |
1720 |
}; |
1721 |
populateTweetFeed2 = function(options) |
1722 |
{ |
1723 |
if (options._tweetFeedElement && options._autorefreshTweetsCache.length > 0) |
1724 |
{ |
1725 |
if (options._tweetFeedConfig.autorefresh.mode == 'trigger-insert') |
1726 |
{ |
1727 |
if (options._tweetFeedConfig.autorefresh._triggerElement)
|
1728 |
{ |
1729 |
if (options.tweetFeedAutorefreshTriggerContentDecorator)
|
1730 |
{ |
1731 |
options._tweetFeedConfig.autorefresh._triggerElement.html( |
1732 |
options.tweetFeedAutorefreshTriggerContentDecorator(options._autorefreshTweetsCache.length, options) |
1733 |
); |
1734 |
} |
1735 |
} |
1736 |
else
|
1737 |
{ |
1738 |
if (options.tweetFeedAutorefreshTriggerDecorator)
|
1739 |
{ |
1740 |
options._tweetFeedConfig.autorefresh._triggerElement = |
1741 |
$(options.tweetFeedAutorefreshTriggerDecorator(options._autorefreshTweetsCache.length, options));
|
1742 |
options._tweetFeedConfig.autorefresh._triggerElement.bind('click', function() |
1743 |
{ |
1744 |
options.autorefreshTriggerVisualizer( |
1745 |
null,
|
1746 |
options._tweetFeedConfig.autorefresh._triggerElement, |
1747 |
options, |
1748 |
function()
|
1749 |
{ |
1750 |
insertTriggerTweets(options); |
1751 |
} |
1752 |
); |
1753 |
options._tweetFeedConfig.autorefresh._triggerElement = null;
|
1754 |
}); |
1755 |
|
1756 |
options.autorefreshTriggerVisualizer(options._tweetFeedElement, options._tweetFeedConfig.autorefresh._triggerElement, options); |
1757 |
} |
1758 |
} |
1759 |
} |
1760 |
else
|
1761 |
{ |
1762 |
insertTriggerTweets(options); |
1763 |
} |
1764 |
} |
1765 |
}; |
1766 |
insertTriggerTweets = function(options) |
1767 |
{ |
1768 |
// populate the tweet feed with tweets from the autorefresh cache
|
1769 |
if (options.tweetDecorator && options._autorefreshTweetsCache.length > 0) |
1770 |
{ |
1771 |
// process the autorefresh cache
|
1772 |
while (options._autorefreshTweetsCache.length > 0) |
1773 |
{ |
1774 |
// get the last tweet and remove it from the autorefresh cache
|
1775 |
var tweet = options._autorefreshTweetsCache.pop();
|
1776 |
|
1777 |
// put that tweet on top of the tweets cache
|
1778 |
options._tweetsCache.unshift(tweet); |
1779 |
|
1780 |
// adjust paging offset
|
1781 |
options._tweetFeedConfig.paging._offset++; |
1782 |
|
1783 |
// decorate the tweet and give it to the tweet visualizer
|
1784 |
options.tweetVisualizer( |
1785 |
options._tweetFeedElement, |
1786 |
$(options.tweetDecorator(tweet, options)),
|
1787 |
'prepend',
|
1788 |
options |
1789 |
); |
1790 |
} |
1791 |
|
1792 |
addHovercards(options); |
1793 |
} |
1794 |
}; |
1795 |
addHovercards = function(options) |
1796 |
{ |
1797 |
if (isAnywherePresent())
|
1798 |
{ |
1799 |
// if @Anywhere is present, append Hovercards to @username and
|
1800 |
// profile images
|
1801 |
twttr.anywhere(function(T)
|
1802 |
{ |
1803 |
T(options._baseSelector + ' .jta-tweet-list').hovercards({expanded: options._tweetFeedConfig.expandHovercards}); |
1804 |
T(options._baseSelector + ' .jta-tweet-profile-image img').hovercards(
|
1805 |
{ |
1806 |
expanded: options._tweetFeedConfig.expandHovercards,
|
1807 |
username: function(node) { return node.alt; } |
1808 |
}); |
1809 |
T(options._baseSelector + ' .jta-tweet-retweeter-link').hovercards(
|
1810 |
{ |
1811 |
expanded: options._tweetFeedConfig.expandHovercards,
|
1812 |
username: function(node) { return node.text; } |
1813 |
}); |
1814 |
T(options._baseSelector + ' .jta-tweet-user-screen-name-link').hovercards(
|
1815 |
{ |
1816 |
expanded: options._tweetFeedConfig.expandHovercards,
|
1817 |
username: function(node) { return node.text; } |
1818 |
}); |
1819 |
T(options._baseSelector + ' .jta-tweet-user-full-name-link').hovercards(
|
1820 |
{ |
1821 |
expanded: options._tweetFeedConfig.expandHovercards,
|
1822 |
username: function(node) { return node.name; } |
1823 |
}); |
1824 |
}); |
1825 |
} |
1826 |
}; |
1827 |
populateAnywhereControls = function(options) |
1828 |
{ |
1829 |
if (isAnywherePresent())
|
1830 |
{ |
1831 |
twttr.anywhere(function(T)
|
1832 |
{ |
1833 |
// optionally add an @Anywhere TweetBox
|
1834 |
if (options.tweetBoxDecorator)
|
1835 |
{ |
1836 |
T(options._baseSelector + ' .jta-tweet-box').tweetBox(options._tweetBoxConfig);
|
1837 |
} |
1838 |
|
1839 |
// optionally add an @Anywhere FollowButton
|
1840 |
if (options.followButtonDecorator)
|
1841 |
{ |
1842 |
T(options._baseSelector + ' .jta-follow-button').followButton(options.username);
|
1843 |
} |
1844 |
|
1845 |
// optionally add an @Anywhere ConnectButton
|
1846 |
if (options.connectButtonDecorator)
|
1847 |
{ |
1848 |
var o = $.extend( |
1849 |
{ |
1850 |
authComplete: function(user) |
1851 |
{ |
1852 |
// display/update login infos on connect/signin event
|
1853 |
updateLoginInfoElement(options, T); |
1854 |
}, |
1855 |
signOut: function() |
1856 |
{ |
1857 |
// display/update login infos on signout event
|
1858 |
updateLoginInfoElement(options, T); |
1859 |
} |
1860 |
}, options._connectButtonConfig); |
1861 |
|
1862 |
T(options._baseSelector + ' .jta-connect-button').connectButton(o);
|
1863 |
|
1864 |
// display/update login infos
|
1865 |
updateLoginInfoElement(options, T); |
1866 |
} |
1867 |
}); |
1868 |
} |
1869 |
}; |
1870 |
bindEventHandlers = function(options) |
1871 |
{ |
1872 |
if (options.tweetFeedControlsDecorator)
|
1873 |
{ |
1874 |
if (options._tweetFeedConfig.paging.mode == 'prev-next') |
1875 |
{ |
1876 |
$(options._baseSelector + ' .jta-tweet-list-controls-button-prev').bind('click', function() |
1877 |
{ |
1878 |
if (!isLoading(options) && options._tweetFeedConfig.paging._offset > 0) |
1879 |
{ |
1880 |
prevPage(options, true);
|
1881 |
} |
1882 |
}); |
1883 |
$(options._baseSelector + ' .jta-tweet-list-controls-button-next').bind('click', function() |
1884 |
{ |
1885 |
if (!isLoading(options))
|
1886 |
{ |
1887 |
nextPage(options, true);
|
1888 |
} |
1889 |
}); |
1890 |
} |
1891 |
else if (options._tweetFeedConfig.paging.mode == 'endless-scroll') |
1892 |
{ |
1893 |
options._tweetFeedElement.bind("scroll", function() |
1894 |
{ |
1895 |
if (!isLoading(options) && ($(this)[0].scrollHeight - $(this).scrollTop() == $(this).outerHeight())) |
1896 |
{ |
1897 |
nextPage(options, false);
|
1898 |
} |
1899 |
}); |
1900 |
} |
1901 |
else
|
1902 |
{ |
1903 |
$(options._baseSelector + ' .jta-tweet-list-controls-button-more').bind('click', function() |
1904 |
{ |
1905 |
if (!isLoading(options))
|
1906 |
{ |
1907 |
nextPage(options, false);
|
1908 |
} |
1909 |
}); |
1910 |
} |
1911 |
} |
1912 |
}; |
1913 |
nextPage = function(options, flClear) |
1914 |
{ |
1915 |
doPage(options, flClear, Math.min(options._tweetFeedConfig.paging._offset + options._tweetFeedConfig.paging._limit, options._tweetsCache.length)); |
1916 |
}; |
1917 |
prevPage = function(options, flClear) |
1918 |
{ |
1919 |
doPage(options, flClear, Math.max(0, options._tweetFeedConfig.paging._offset - options._tweetFeedConfig.paging._limit));
|
1920 |
}; |
1921 |
doPage = function(options, flClear, newOffset) |
1922 |
{ |
1923 |
options._tweetFeedConfig.paging._offset = newOffset; |
1924 |
options._tweetFeedConfig._clearBeforePopulate = flClear; |
1925 |
|
1926 |
populateTweetFeed(options); |
1927 |
}; |
1928 |
startAutorefresh = function(options) |
1929 |
{ |
1930 |
if (options._tweetFeedConfig.autorefresh.mode != 'none' && |
1931 |
options._tweetFeedConfig.paging.mode != 'prev-next' &&
|
1932 |
options._tweetFeedConfig.autorefresh.duration != 0 &&
|
1933 |
( |
1934 |
options._tweetFeedConfig.autorefresh.duration < 0 ||
|
1935 |
(new Date().getTime() - options._tweetFeedConfig.autorefresh._startTime) <= options._tweetFeedConfig.autorefresh.duration * 1000 |
1936 |
) |
1937 |
) |
1938 |
{ |
1939 |
window.setTimeout(function() { processAutorefresh(options); }, options._tweetFeedConfig.autorefresh.interval * 1000); |
1940 |
} |
1941 |
}; |
1942 |
stopAutorefresh = function(options) |
1943 |
{ |
1944 |
options._tweetFeedConfig.autorefresh.duration = 0;
|
1945 |
}; |
1946 |
processAutorefresh = function(options) |
1947 |
{ |
1948 |
if (options._tweetFeedConfig.autorefresh.duration != 0) |
1949 |
{ |
1950 |
// load the data ...
|
1951 |
getRateLimitedData(options, true, getFeedUrl(options, false), function(data, options) |
1952 |
{ |
1953 |
// reverse the sequence of the autorefresh tweets ...
|
1954 |
var tweets = (data.results || data).slice(0); |
1955 |
tweets.reverse(); |
1956 |
|
1957 |
// ...then process them
|
1958 |
$.each(tweets, function(idx, tweet) |
1959 |
{ |
1960 |
// Snowflake support: just update ids that are currently used
|
1961 |
if (tweet.id_str)
|
1962 |
{ |
1963 |
tweet.id = tweet.id_str; |
1964 |
} |
1965 |
|
1966 |
if (tweet.in_reply_to_status_id_str)
|
1967 |
{ |
1968 |
tweet.in_reply_to_status_id = tweet.in_reply_to_status_id_str; |
1969 |
} |
1970 |
|
1971 |
// if this tweet is already in one of the tweet caches, ignore it
|
1972 |
if (!isTweetInAutorefreshCache(tweet, options) && !isTweetInCache(tweet, options))
|
1973 |
{ |
1974 |
// optionally filter tweet ...
|
1975 |
if (options.tweetFilter(tweet, options))
|
1976 |
{ |
1977 |
// ... then put it to the top of the autorefresh cache
|
1978 |
options._autorefreshTweetsCache.unshift(tweet); |
1979 |
|
1980 |
// if a maximum autorefresh cache size is configured, remove elder tweets
|
1981 |
if (options._tweetFeedConfig.autorefresh.max > 0) |
1982 |
{ |
1983 |
while (options._autorefreshTweetsCache.length > options._tweetFeedConfig.autorefresh.max)
|
1984 |
{ |
1985 |
options._autorefreshTweetsCache.pop(); |
1986 |
} |
1987 |
} |
1988 |
} |
1989 |
} |
1990 |
}); |
1991 |
|
1992 |
populateTweetFeed2(options); |
1993 |
}); |
1994 |
|
1995 |
// restart autorefresh
|
1996 |
startAutorefresh(options); |
1997 |
} |
1998 |
}; |
1999 |
startTimestampRefresh = function(options) |
2000 |
{ |
2001 |
if (
|
2002 |
options.tweetTimestampDecorator && |
2003 |
typeof(options._tweetFeedConfig.showTimestamp) == 'object' && |
2004 |
options._tweetFeedConfig.showTimestamp.refreshInterval > 0
|
2005 |
) |
2006 |
{ |
2007 |
window.setTimeout(function() { processTimestampRefresh(options); }, options._tweetFeedConfig.showTimestamp.refreshInterval * 1000); |
2008 |
} |
2009 |
}; |
2010 |
processTimestampRefresh = function(options) |
2011 |
{ |
2012 |
$.each(options._tweetFeedElement.find('.jta-tweet-timestamp-link'), function(idx, element) |
2013 |
{ |
2014 |
var dataTimestamp = $(element).attr('data-timestamp'); |
2015 |
|
2016 |
$(element).html(options.tweetTimestampFormatter(dataTimestamp, options));
|
2017 |
}); |
2018 |
|
2019 |
startTimestampRefresh(options); |
2020 |
}; |
2021 |
isTweetInCache = function(tweet, options) |
2022 |
{ |
2023 |
var l = options._tweetsCache.length;
|
2024 |
|
2025 |
for (var i = 0; i < l; i++) |
2026 |
{ |
2027 |
if (tweet.id == options._tweetsCache[i].id)
|
2028 |
{ |
2029 |
return true; |
2030 |
} |
2031 |
} |
2032 |
|
2033 |
return false; |
2034 |
}; |
2035 |
isTweetInAutorefreshCache = function(tweet, options) |
2036 |
{ |
2037 |
var l = options._autorefreshTweetsCache.length;
|
2038 |
|
2039 |
for (var i = 0; i < l; i++) |
2040 |
{ |
2041 |
if (tweet.id == options._autorefreshTweetsCache[i].id)
|
2042 |
{ |
2043 |
return true; |
2044 |
} |
2045 |
} |
2046 |
|
2047 |
return false; |
2048 |
}; |
2049 |
showLoadingIndicator = function(options) |
2050 |
{ |
2051 |
if (options._tweetFeedElement && options.loadingDecorator && !options._loadingIndicatorElement)
|
2052 |
{ |
2053 |
options._loadingIndicatorElement = $(options.loadingDecorator(options));
|
2054 |
options.loadingIndicatorVisualizer(options._tweetFeedElement, options._loadingIndicatorElement, options, null);
|
2055 |
options._tweetFeedElement.scrollTop(1000000);
|
2056 |
} |
2057 |
}; |
2058 |
hideLoadingIndicator = function(options, callback) |
2059 |
{ |
2060 |
if (options._loadingIndicatorElement)
|
2061 |
{ |
2062 |
options.loadingIndicatorVisualizer(null, options._loadingIndicatorElement, options, callback);
|
2063 |
options._loadingIndicatorElement = null;
|
2064 |
} |
2065 |
else
|
2066 |
{ |
2067 |
if (callback)
|
2068 |
{ |
2069 |
callback(); |
2070 |
} |
2071 |
} |
2072 |
}; |
2073 |
isLoading = function(options) |
2074 |
{ |
2075 |
return options._loadingIndicatorElement != null; |
2076 |
}; |
2077 |
formatDate = function(dateStr) |
2078 |
{ |
2079 |
return dateStr.replace(/^([a-z]{3})( [a-z]{3} \d\d?)(.*)( \d{4})$/i, '$1,$2$4$3'); |
2080 |
}; |
2081 |
getUserScreenName = function(tweet) |
2082 |
{ |
2083 |
var screenName = tweet.user ? tweet.user.screen_name : false || tweet.from_user; |
2084 |
|
2085 |
return screenName;
|
2086 |
}; |
2087 |
getScreenName = function(tweet) |
2088 |
{ |
2089 |
var t = tweet.retweeted_status || tweet;
|
2090 |
var screenName = t.user ? t.user.screen_name : false || t.from_user; |
2091 |
|
2092 |
return screenName;
|
2093 |
}; |
2094 |
getFullName = function(tweet) |
2095 |
{ |
2096 |
var t = tweet.retweeted_status || tweet;
|
2097 |
var fullName = t.user ? t.user.name : undefined; |
2098 |
|
2099 |
return fullName;
|
2100 |
}; |
2101 |
validateRange = function(num, lo, hi) |
2102 |
{ |
2103 |
if (num < lo)
|
2104 |
num = lo; |
2105 |
|
2106 |
if (num > hi)
|
2107 |
num = hi; |
2108 |
|
2109 |
return num;
|
2110 |
}; |
2111 |
showError = function(options, errorText) |
2112 |
{ |
2113 |
if (options.errorDecorator && options._tweetFeedElement)
|
2114 |
{ |
2115 |
options._tweetFeedElement.append(options.errorDecorator(errorText, options)); |
2116 |
} |
2117 |
}; |
2118 |
getPagedTweets = function(options, callback) |
2119 |
{ |
2120 |
options._tweetFeedConfig._recLevel = 0;
|
2121 |
|
2122 |
getRecPagedTweets(options, options._tweetFeedConfig.paging._offset, options._tweetFeedConfig.paging._limit, callback); |
2123 |
}; |
2124 |
getRecPagedTweets = function(options, offset, limit, callback) |
2125 |
{ |
2126 |
++options._tweetFeedConfig._recLevel; |
2127 |
|
2128 |
if (offset + limit <= options._tweetsCache.length ||
|
2129 |
options._tweetFeedConfig._recLevel > 3 ||
|
2130 |
options._tweetFeedConfig._noData |
2131 |
) |
2132 |
{ |
2133 |
// if the requested data is already cached or the max. no. of
|
2134 |
// consecutive API calls is reached, use the records
|
2135 |
|
2136 |
if (offset + limit > options._tweetsCache.length)
|
2137 |
{ |
2138 |
limit = Math.max(0, options._tweetsCache.length - offset);
|
2139 |
} |
2140 |
|
2141 |
var tweets = [];
|
2142 |
|
2143 |
for (var i = 0; i < limit; i++) |
2144 |
{ |
2145 |
tweets[i] = options._tweetsCache[offset + i]; |
2146 |
} |
2147 |
|
2148 |
callback(tweets, options); |
2149 |
} |
2150 |
else
|
2151 |
{ |
2152 |
// ... if not, load the data, fill the cache and try again
|
2153 |
++options._tweetFeedConfig._pageParam; |
2154 |
|
2155 |
getRateLimitedData(options, false, getFeedUrl(options, true), function(data, options) |
2156 |
{ |
2157 |
var tweets = data.results || data;
|
2158 |
|
2159 |
if (tweets.length == 0) |
2160 |
{ |
2161 |
options._tweetFeedConfig._noData = true;
|
2162 |
} |
2163 |
else
|
2164 |
{ |
2165 |
$.each(tweets, function(idx, tweet) |
2166 |
{ |
2167 |
// Snowflake support: just update ids that are currently used
|
2168 |
if (tweet.id_str)
|
2169 |
{ |
2170 |
tweet.id = tweet.id_str; |
2171 |
} |
2172 |
|
2173 |
if (tweet.in_reply_to_status_id_str)
|
2174 |
{ |
2175 |
tweet.in_reply_to_status_id = tweet.in_reply_to_status_id_str; |
2176 |
} |
2177 |
|
2178 |
// save the first tweet id for subsequent paging requests
|
2179 |
if (!options._tweetFeedConfig._maxId)
|
2180 |
{ |
2181 |
options._tweetFeedConfig._maxId = tweet.id; |
2182 |
} |
2183 |
|
2184 |
// optionally filter tweet ...
|
2185 |
if (options.tweetFilter(tweet, options))
|
2186 |
{ |
2187 |
// then put it into the cache
|
2188 |
options._tweetsCache.push(tweet); |
2189 |
} |
2190 |
}); |
2191 |
} |
2192 |
|
2193 |
getRecPagedTweets(options, offset, limit, callback); |
2194 |
}); |
2195 |
} |
2196 |
}; |
2197 |
getRateLimitedData = function(options, flAutorefresh, url, callback) |
2198 |
{ |
2199 |
getRateLimit(options, function(rateLimit)
|
2200 |
{ |
2201 |
if (rateLimit && rateLimit.remaining_hits <= 0) |
2202 |
{ |
2203 |
options._stats.rateLimitPreventionCount++; |
2204 |
hideLoadingIndicator(options, null);
|
2205 |
return;
|
2206 |
} |
2207 |
|
2208 |
getData(options, flAutorefresh, url, callback); |
2209 |
}); |
2210 |
}; |
2211 |
getData = function(options, flAutorefresh, url, callback) |
2212 |
{ |
2213 |
options._stats.dataRequestCount++; |
2214 |
|
2215 |
if (!options.onDataRequestHandler(options._stats, options))
|
2216 |
{ |
2217 |
hideLoadingIndicator(options, null);
|
2218 |
return;
|
2219 |
} |
2220 |
|
2221 |
if (!flAutorefresh)
|
2222 |
{ |
2223 |
showLoadingIndicator(options); |
2224 |
} |
2225 |
|
2226 |
options.tweetDataProvider(url, function(data)
|
2227 |
{ |
2228 |
if (data.error)
|
2229 |
{ |
2230 |
// in case of an error, display the error message
|
2231 |
showError(options, data.error); |
2232 |
} |
2233 |
else
|
2234 |
{ |
2235 |
callback(data, options); |
2236 |
} |
2237 |
}); |
2238 |
}; |
2239 |
getRateLimit = function(options, callback) |
2240 |
{ |
2241 |
options.rateLimitDataProvider(function(rateLimit)
|
2242 |
{ |
2243 |
options._stats.rateLimit = rateLimit; |
2244 |
|
2245 |
options.onRateLimitDataHandler(options._stats, options); |
2246 |
|
2247 |
callback(rateLimit); |
2248 |
}); |
2249 |
}; |
2250 |
defaultTweetDataProvider = function(url, callback) |
2251 |
{ |
2252 |
$.getJSON(url, callback);
|
2253 |
}; |
2254 |
defaultRateLimitDataProvider = function(callback) |
2255 |
{ |
2256 |
$.getJSON('http://api.twitter.com/1/account/rate_limit_status.json?callback=?', callback); |
2257 |
}; |
2258 |
})(jQuery); |