bde55c000b
* index pages (php -> html) * favicon * robots.txt * static resources (js/css/img) Change-Id: I25c214f6153be3a3dccf75138b1a921006534588
1807 lines
57 KiB
JavaScript
Executable File
1807 lines
57 KiB
JavaScript
Executable File
/*
|
|
* jTweetsAnywhere V1.2.1
|
|
* http://thomasbillenstein.com/jTweetsAnywhere/
|
|
*
|
|
* Copyright 2010, Thomas Billenstein
|
|
* Licensed under the MIT license.
|
|
* http://thomasbillenstein.com/jTweetsAnywhere/license.txt
|
|
*/
|
|
(function($)
|
|
{
|
|
$.fn.jTweetsAnywhere = function(options)
|
|
{
|
|
// setup the default options
|
|
var options = $.extend(
|
|
{
|
|
/**
|
|
* The user's name who's tweet feed or list feed is displayed. This
|
|
* param is also used when a Twitter "Follow Button" is displayed. Usually
|
|
* this param is a string, but can also be an array of strings. If an array
|
|
* is supplied (and the params 'list' and 'searchParams' are null), a
|
|
* combined feed of all users is displayed.
|
|
*
|
|
* Sample: 'tbillenstein' or ['twitterapi', '...', '...']
|
|
*/
|
|
username: 'tbillenstein',
|
|
|
|
/**
|
|
* The name of a user's list where the tweet feed is generated from
|
|
*/
|
|
list: null,
|
|
|
|
/**
|
|
* A single search param string or an array of search params, to be used in
|
|
* a Twitter search call. All Twitter Search Params are supported
|
|
* See here for the details:
|
|
* http://apiwiki.twitter.com/Twitter-Search-API-Method%3A-search
|
|
*
|
|
* Sample: 'q=twitter' or ['q=twitter', 'geocode=48.856667,2.350833,30km']
|
|
*/
|
|
searchParams: null,
|
|
|
|
/**
|
|
* The number of tweets shown in the tweet feed. If this param is 0, no feed
|
|
* is displayed. For user or list feeds the maximum count is 20, for search
|
|
* results the maximum count is 100.
|
|
*
|
|
* Unlike in previous releases, since 1.2.0 jTweetsAnywhere is based on a
|
|
* tweets caching algorithm that will always deliver the requested count of
|
|
* tweets accepting that this request can only be fullfilled by calling Twitter
|
|
* more than once.
|
|
*
|
|
* IMPORTANT: Please always keep in mind, that the use of the Twitter API is
|
|
* rate limited. Non-authenticated users are rated IP-based and you have only
|
|
* 150 calls per hour available. Every retrieval of tweets counts and so does
|
|
* for example hovering over a profile image to show the hovercard.
|
|
* jTweetsAnywhere will always check the remaining count of free API calls before
|
|
* actually calling Twitter to avoid black listing your visitor's IP.
|
|
*/
|
|
count: 0,
|
|
|
|
/**
|
|
* A flag (true/false) that specifies whether to display a profile image in
|
|
* tweets. If the param is set to null (the default value), a profile image
|
|
* is displayed only if the feed represents a user's list or the result of a
|
|
* Twitter search.
|
|
*
|
|
* THIS OPTION IS DEPRECATED. You should use showTweetFeed.showProfileImages
|
|
* instead.
|
|
*/
|
|
tweetProfileImagePresent: null,
|
|
|
|
/**
|
|
* Each tweet that is loaded from Twitter will pass the tweetFilter. if
|
|
* the filter returns true, the tweet will be added to the tweets cache
|
|
* otherwise it is ignored. The defaultTweetFilter alsways retruns true
|
|
* but you can supply your own tweet filter to customize the tweet feed.
|
|
*/
|
|
tweetFilter: defaultTweetFilter,
|
|
|
|
/**
|
|
* A flag (true/false) that specifies whether to display a Tweet Feed
|
|
* or an object literal representing the configuration options for the
|
|
* Tweet Feed. This flag works in conjunction with the count parameter:
|
|
* - if count equals 0, no feed is displayed, ignoring showTweetFeed
|
|
* - if count not equals 0 and showTweetFeed equals false, no feed
|
|
* is displayed
|
|
* {
|
|
* expandHovercards: false, // Boolean - Show Hovercards in expanded mode.
|
|
*
|
|
* showTimestamp: true, // A flag (true/false) that specifies whether to display a tweet's timestamp
|
|
* // or an object literal representing the configuration options for the
|
|
* // timestamp.
|
|
* // {
|
|
* // refreshInterval: 0, // Time in seconds to be waited until the
|
|
* // // timestamps of the displayed tweets get refreshed
|
|
* // // 0 means no refreshing.
|
|
* // }
|
|
*
|
|
* showSource: false, // Boolean - Show info about the source of the tweet.
|
|
*
|
|
* showGeoLocation: true, // Boolean - Show geolocation info and link to Google maps.
|
|
*
|
|
* showInReplyTo: true, // Boolean - Show link to the "replied to" tweet (if available).
|
|
*
|
|
* showProfileImages: null, // A flag (true/false) that specifies whether to display a profile image in
|
|
* // tweets. If the param is set to null (the default value), a profile image
|
|
* // is displayed only if the feed represents a user's list or the result of a
|
|
* // Twitter search.
|
|
*
|
|
* showUserScreenNames: null, // A flag (true/false/null) that specifies whether to display a username in
|
|
* // tweets. If the param is set to null (the default value), a username
|
|
* // is displayed only if the feed represents a user's list or the result of a
|
|
* // Twitter search.
|
|
*
|
|
* showUserFullNames: false, // A flag (true/false/null) that specifies whether to display a user's full name
|
|
* // in tweets. If the param is set to null, a user's full name
|
|
* // is displayed only if the feed represents a user's list or the result of a
|
|
* // Twitter search.
|
|
*
|
|
* includeRetweets: true, // Boolean - Include native retweets in a user's tweet feed
|
|
*
|
|
* paging: // An object literal representing the configuration options for the
|
|
* { // paging support, that specifies how more/earlier tweets can be loaded
|
|
* mode: "none" // by using the supplied UI controls (more/next buttons, scrollbar).
|
|
* }, // Accepted values for mode are: "none" | "more" | "prev-next" | "endless-scroll"
|
|
* // if mode equals "endless-scroll" you have to set the height of the tweet feed
|
|
* // element (.jta-tweet-list) in your CSS to get a scrollbar! You should also set
|
|
* // the "overflow" attribute to "auto".
|
|
*
|
|
* autorefresh: // An object literal representing the configuration options for the
|
|
* { // autorefresh behaviour.
|
|
*
|
|
* // IMPORTANT: Please always keep in mind, that using the Twitter API is rate
|
|
* // limited. Non-authenticated users are rated IP-based and you have only 150
|
|
* // calls per hour available. Every retrieval of tweets counts and so does for
|
|
* // example hovering over a profile image to show the hovercard. jTweetsAnywhere will
|
|
* // always check the remaining count of free API calls before actually calling
|
|
* // Twitter to avoid black listing your visitor's IP.
|
|
*
|
|
* // However - choose your settings wisely to keep your visitors happy. An update
|
|
* // interval of 30 seconds on a feed that is updated averaged once per hour
|
|
* // does not make sense and is a total waste of remaining API calls!
|
|
*
|
|
* mode: "none", // Accepted values for mode are: "none" | "auto-insert" | "trigger-insert"
|
|
* // "none" (the default value) - disables the autorefresh feature
|
|
* // "auto-insert" - automatically insert the new tweets on top of the tweet feed
|
|
* // "trigger-insert" - if new tweets arrived, show or update a button that displays
|
|
* // the number of new tweets. These new tweets are inserted on top of the tweet
|
|
* // feed, if the user clicks on the button.
|
|
*
|
|
* interval: 60, // Time in seconds to be waited until the next request for new tweets. Minimum
|
|
* // value is 30.
|
|
*
|
|
* duration: 3600 // Time in seconds for how long the autorefresh will be active. After
|
|
* // this period of time, autorefreshing will stop. A value of -1 means
|
|
* // autorefresh for ever.
|
|
* }
|
|
* }
|
|
*/
|
|
showTweetFeed: true,
|
|
|
|
/**
|
|
* A flag (true/false) that specifies whether to display a Twitter "Follow
|
|
* Button".
|
|
*/
|
|
showFollowButton: false,
|
|
|
|
/**
|
|
* A flag (true/false) that specifies whether to display a Twitter "Connect
|
|
* Button" or an object literal representing the configuration options for
|
|
* the "Tweet Box".
|
|
* {
|
|
* size: 'medium' // String - The size of the Connect Button. Valid values are: small, medium, large, xlarge
|
|
* }
|
|
*/
|
|
showConnectButton: false,
|
|
|
|
/**
|
|
* A flag (true/false) that specifies whether to display Login Infos.
|
|
*/
|
|
showLoginInfo: false,
|
|
|
|
/**
|
|
* A flag (true/false) that specifies whether to display a Twitter "Tweet
|
|
* Box" or an object literal representing the configuration options for
|
|
* the "Tweet Box".
|
|
* {
|
|
* counter: true, // Boolean - Display a counter in the Tweet Box for counting characters
|
|
* width: 515, // Number - The width of the Tweet Box in pixels
|
|
* height: 65, // Number - The height of the Tweet Box in pixels
|
|
* label: "What's happening", // String - The text above the Tweet Box, a call to action
|
|
* defaultContent: <none>, // String - Pre-populated text in the Tweet Box. Useful for an @mention, a #hashtag, a link, etc.
|
|
* 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
|
|
* }
|
|
*/
|
|
showTweetBox: false,
|
|
|
|
/**
|
|
* A decorator is a function that is responsible for constructing a certain
|
|
* element of the widget. Most of the decorators return a HTML string.
|
|
* Exceptions are the mainDecorator, which defines the basic sequence of
|
|
* the widget's components, plus the linkDecorator, the usernameDecorator
|
|
* and the hashtagDecorator which return the string that is supplied as a
|
|
* function param, enriched with the HTML tags.
|
|
*
|
|
* For details, see the implementations of the default decorators. Each
|
|
* default decorator can be overwritten by your own implementation.
|
|
*/
|
|
mainDecorator: defaultMainDecorator,
|
|
|
|
tweetFeedDecorator: defaultTweetFeedDecorator,
|
|
|
|
tweetDecorator: defaultTweetDecorator,
|
|
tweetProfileImageDecorator: defaultTweetProfileImageDecorator,
|
|
tweetBodyDecorator: defaultTweetBodyDecorator,
|
|
tweetUsernameDecorator: defaultTweetUsernameDecorator,
|
|
tweetTextDecorator: defaultTweetTextDecorator,
|
|
tweetAttributesDecorator: defaultTweetAttributesDecorator,
|
|
tweetTimestampDecorator: defaultTweetTimestampDecorator,
|
|
tweetSourceDecorator: defaultTweetSourceDecorator,
|
|
tweetGeoLocationDecorator: defaultTweetGeoLocationDecorator,
|
|
tweetInReplyToDecorator: defaultTweetInReplyToDecorator,
|
|
tweetRetweeterDecorator: defaultTweetRetweeterDecorator,
|
|
|
|
tweetFeedControlsDecorator: defaultTweetFeedControlsDecorator,
|
|
tweetFeedControlsMoreBtnDecorator: defaultTweetFeedControlsMoreBtnDecorator,
|
|
tweetFeedControlsPrevBtnDecorator: defaultTweetFeedControlsPrevBtnDecorator,
|
|
tweetFeedControlsNextBtnDecorator: defaultTweetFeedControlsNextBtnDecorator,
|
|
|
|
tweetFeedAutorefreshTriggerDecorator: defaultTweetFeedAutorefreshTriggerDecorator,
|
|
tweetFeedAutorefreshTriggerContentDecorator: defaultTweetFeedAutorefreshTriggerContentDecorator,
|
|
|
|
connectButtonDecorator: defaultConnectButtonDecorator,
|
|
|
|
loginInfoDecorator: defaultLoginInfoDecorator,
|
|
loginInfoContentDecorator: defaultLoginInfoContentDecorator,
|
|
|
|
followButtonDecorator: defaultFollowButtonDecorator,
|
|
|
|
tweetBoxDecorator: defaultTweetBoxDecorator,
|
|
|
|
linkDecorator: defaultLinkDecorator,
|
|
usernameDecorator: defaultUsernameDecorator,
|
|
hashtagDecorator: defaultHashtagDecorator,
|
|
|
|
loadingDecorator: defaultLoadingDecorator,
|
|
errorDecorator: defaultErrorDecorator,
|
|
noDataDecorator: defaultNoDataDecorator,
|
|
|
|
/**
|
|
* Formatters are currently used for date format processing only.
|
|
*
|
|
* The tweetTimestampFormatter formats the tweet's timestamp to be shown
|
|
* in the tweet attributes section
|
|
*
|
|
* For details, see the implementation of the defaultTweetTimestampFormatter.
|
|
*/
|
|
tweetTimestampFormatter : defaultTweetTimestampFormatter,
|
|
|
|
/**
|
|
* The tweetTimestampTooltipFormatter formats the tweet's timestamp to be shown
|
|
* in the tooltip when hovering over the timestamp link.
|
|
*/
|
|
tweetTimestampTooltipFormatter : defaultTweetTimestampTooltipFormatter,
|
|
|
|
/**
|
|
* A visualizer is a function that is responsible for adding one or more
|
|
* elements to the DOM and thereby making them visible to the user.
|
|
* A visualizer might also be responsible to do the opposite effect:
|
|
* To remove one or more elements from the DOM.
|
|
*
|
|
* The tweetVisualizer gets called each time a tweet element should be
|
|
* appended or prepended to the tweet feed element.
|
|
*
|
|
* For details, see the implementation of the defaultTweetVisualizer.
|
|
*
|
|
* Each default visualizer can be overwritten by your own implementation.
|
|
*/
|
|
tweetVisualizer: defaultTweetVisualizer,
|
|
|
|
/**
|
|
* The loadingIndicatorVisualizer gets called each time data is retrieved
|
|
* from Twitter to visualize the loading indicator. This visualizer is also
|
|
* used to hide the loading indicator.
|
|
*
|
|
* For details, see the implementation of the defaultLoadingIndicatorVisualizer.
|
|
*/
|
|
loadingIndicatorVisualizer: defaultLoadingIndicatorVisualizer,
|
|
|
|
/**
|
|
* The autorefreshTriggerVisualizer will be called if the autorefresh
|
|
* trigger should be visualized or hidden.
|
|
*
|
|
* For details, see the implementation of the autorefreshTriggerVisualizer.
|
|
*/
|
|
autorefreshTriggerVisualizer: defaultAutorefreshTriggerVisualizer,
|
|
|
|
/**
|
|
* An event handler is a function that gets called whenever the event you
|
|
* are interested in, occurs.
|
|
*
|
|
* The onDataRequest event handler will be called immediatly before calling
|
|
* Twitter to retrieve new data and gives you the opportunity to deny
|
|
* the call by returning false from the function.
|
|
*
|
|
* This feature might be used in conjunction with the paging feature,
|
|
* especially when using the "endless-scroll" paging mode, to avoid the
|
|
* exhaustion of remaining Twitter API calls, before the rate limit is
|
|
* reached. The stats parameter contains statistical infos and counters
|
|
* that you can examine to base your decision whether to return true or
|
|
* false.
|
|
*/
|
|
onDataRequestHandler: defaultOnDataRequestHandler,
|
|
|
|
/**
|
|
* The onRateLimitData event handler is called each time
|
|
* jTweetsAnywhere retrieved the rate limit data from Twitter. The actual
|
|
* rate limit data is contained in the stats object.
|
|
*/
|
|
onRateLimitDataHandler: defaultOnRateLimitDataHandler,
|
|
|
|
_tweetFeedConfig:
|
|
{
|
|
expandHovercards: false,
|
|
showTimestamp:
|
|
{
|
|
refreshInterval: 0
|
|
},
|
|
showSource: false,
|
|
showGeoLocation: true,
|
|
showInReplyTo: true,
|
|
showProfileImages: null,
|
|
showUserScreenNames: null,
|
|
showUserFullNames: false,
|
|
includeRetweets: true,
|
|
paging:
|
|
{
|
|
mode: "none",
|
|
_limit: 0,
|
|
_offset: 0
|
|
},
|
|
autorefresh:
|
|
{
|
|
mode: "none",
|
|
interval: 60,
|
|
duration: 3600,
|
|
_startTime: null,
|
|
_triggerElement: null
|
|
},
|
|
_pageParam: 0,
|
|
_maxId: null,
|
|
_recLevel: 0,
|
|
_noData: false,
|
|
_clearBeforePopulate: false
|
|
},
|
|
_tweetBoxConfig:
|
|
{
|
|
counter: true,
|
|
width: 515,
|
|
height: 65,
|
|
label: "What's happening?",
|
|
defaultContent: '',
|
|
onTweet: function(textTweet, htmlTweet) {}
|
|
},
|
|
_connectButtonConfig:
|
|
{
|
|
size: "medium"
|
|
},
|
|
_baseSelector: null,
|
|
_baseElement: null,
|
|
_tweetFeedElement: null,
|
|
_tweetFeedControlsElement: null,
|
|
_followButtonElement: null,
|
|
_loginInfoElement: null,
|
|
_connectButtonElement: null,
|
|
_tweetBoxElement: null,
|
|
_loadingIndicatorElement: null,
|
|
_noDataElement: null,
|
|
_tweetsCache: [],
|
|
_autorefreshTweetsCache: [],
|
|
_stats:
|
|
{
|
|
dataRequestCount: 0,
|
|
rateLimitPreventionCount: 0,
|
|
rateLimit:
|
|
{
|
|
remaining_hits: 150,
|
|
hourly_limit: 150
|
|
}
|
|
}
|
|
}, options);
|
|
|
|
// no main decorator? nothing to do!
|
|
if (!options.mainDecorator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
options._baseSelector = this.selector;
|
|
|
|
// if username is an array, create the search query and flatten username
|
|
if (typeof(options.username) != 'string')
|
|
{
|
|
if (!options.searchParams)
|
|
{
|
|
options.searchParams = ['q=from:' + options.username.join(" OR from:")];
|
|
}
|
|
|
|
options.username = options.username[0];
|
|
}
|
|
|
|
// if showTweetFeed is not set to a boolean value, we expect the configuration of
|
|
// the tweet feed
|
|
if (typeof(options.showTweetFeed) == 'object')
|
|
{
|
|
$.extend(true, options._tweetFeedConfig, options.showTweetFeed);
|
|
}
|
|
|
|
// if showTweetBox is not set to a boolean value, we expect the configuration of
|
|
// the TweetBox
|
|
if (typeof(options.showTweetBox) == 'object')
|
|
{
|
|
options._tweetBoxConfig = options.showTweetBox;
|
|
options.showTweetBox = true;
|
|
}
|
|
|
|
// if showConnectButton is not set to a boolean value, we expect the
|
|
// configuration of the Connect Button
|
|
if (typeof(options.showConnectButton) == 'object')
|
|
{
|
|
options._connectButtonConfig = options.showConnectButton;
|
|
options.showConnectButton = true;
|
|
}
|
|
|
|
// to be compatible, check the deprecated option 'tweetProfileImagePresent'
|
|
if (options._tweetFeedConfig.showProfileImages == null)
|
|
{
|
|
options._tweetFeedConfig.showProfileImages = options.tweetProfileImagePresent;
|
|
}
|
|
|
|
// if _tweetFeedConfig.showProfileImages is not set to a boolean value,
|
|
// we decide to show a profile image if the feed represents a user's
|
|
// list or the results of a Twitter search
|
|
if (options._tweetFeedConfig.showProfileImages == null)
|
|
{
|
|
options._tweetFeedConfig.showProfileImages = (options.list || options.searchParams) && options.tweetProfileImageDecorator;
|
|
}
|
|
|
|
// if _tweetFeedConfig.showUserScreenNames is not set to a boolean value,
|
|
// we decide to show a username if the feed represents a user's
|
|
// list or the results of a Twitter search or a tweet is a native retweet
|
|
if (options._tweetFeedConfig.showUserScreenNames == null)
|
|
{
|
|
if (options.list || options.searchParams)
|
|
{
|
|
options._tweetFeedConfig.showUserScreenNames = true;
|
|
}
|
|
|
|
if (!options.tweetUsernameDecorator)
|
|
{
|
|
options._tweetFeedConfig.showUserScreenNames = false;
|
|
}
|
|
}
|
|
|
|
// if _tweetFeedConfig.showUserFullNames is not set to a boolean value,
|
|
// we decide to show a user's full name if the feed represents a user's
|
|
// list or the results of a Twitter search or a tweet is a native retweet
|
|
if (options._tweetFeedConfig.showUserFullNames == null)
|
|
{
|
|
if (options.list || options.searchParams)
|
|
{
|
|
options._tweetFeedConfig.showUserFullNames = true;
|
|
}
|
|
|
|
if (!options.tweetUsernameDecorator)
|
|
{
|
|
options._tweetFeedConfig.showUserFullNames = false;
|
|
}
|
|
}
|
|
|
|
options.count = validateRange(options.count, 0, options.searchParams ? 100 : 20);
|
|
|
|
options._tweetFeedConfig.autorefresh.interval = Math.max(30, options._tweetFeedConfig.autorefresh.interval);
|
|
|
|
options._tweetFeedConfig.paging._offset = 0;
|
|
options._tweetFeedConfig.paging._limit = options.count;
|
|
|
|
// internally, the decision of what parts of a widget are to be
|
|
// displayed is based on the existence of the decorators
|
|
if (options.count == 0 || !options.showTweetFeed)
|
|
{
|
|
options.tweetFeedDecorator = null;
|
|
options.tweetFeedControlsDecorator = null;
|
|
}
|
|
|
|
if (options._tweetFeedConfig.paging.mode == 'none')
|
|
{
|
|
options.tweetFeedControlsDecorator = null;
|
|
}
|
|
|
|
if (!options.showFollowButton)
|
|
{
|
|
options.followButtonDecorator = null;
|
|
}
|
|
|
|
if (!options.showTweetBox)
|
|
{
|
|
options.tweetBoxDecorator = null;
|
|
}
|
|
|
|
if (!options.showConnectButton)
|
|
{
|
|
options.connectButtonDecorator = null;
|
|
}
|
|
|
|
if (!options.showLoginInfo)
|
|
{
|
|
options.loginInfoDecorator = null;
|
|
}
|
|
|
|
if (!options._tweetFeedConfig.showTimestamp)
|
|
{
|
|
options.tweetTimestampDecorator = null;
|
|
}
|
|
|
|
if (!options._tweetFeedConfig.showSource)
|
|
{
|
|
options.tweetSourceDecorator = null;
|
|
}
|
|
|
|
if (!options._tweetFeedConfig.showGeoLocation)
|
|
{
|
|
options.tweetGeoLocationDecorator = null;
|
|
}
|
|
|
|
if (!options._tweetFeedConfig.showInReplyTo)
|
|
{
|
|
options.tweetInReplyToDecorator = null;
|
|
}
|
|
|
|
// setup ajax
|
|
$.ajaxSetup({ cache: true });
|
|
|
|
return this.each(function()
|
|
{
|
|
// the DOM element, where to display the widget
|
|
options._baseElement = $(this);
|
|
|
|
// create the widget's necessary sub DOM elements
|
|
options._tweetFeedElement = options.tweetFeedDecorator ? $(options.tweetFeedDecorator(options)) : null;
|
|
options._tweetFeedControlsElement = options.tweetFeedControlsDecorator ? $(options.tweetFeedControlsDecorator(options)) : null;
|
|
options._followButtonElement = options.followButtonDecorator ? $(options.followButtonDecorator(options)) : null;
|
|
options._tweetBoxElement = options.tweetBoxDecorator ? $(options.tweetBoxDecorator(options)) : null;
|
|
options._connectButtonElement = options.connectButtonDecorator ? $(options.connectButtonDecorator(options)): null;
|
|
options._loginInfoElement = options.loginInfoDecorator ? $(options.loginInfoDecorator(options)) : null;
|
|
|
|
// add the widget to the DOM
|
|
options.mainDecorator(options);
|
|
|
|
populateTweetFeed(options);
|
|
populateAnywhereControls(options);
|
|
|
|
// bind event handlers to support paging
|
|
bindEventHandlers(options);
|
|
|
|
// install autorefresh support
|
|
options._tweetFeedConfig.autorefresh._startTime = new Date().getTime();
|
|
startAutorefresh(options);
|
|
startTimestampRefresh(options);
|
|
});
|
|
};
|
|
defaultMainDecorator = function(options)
|
|
{
|
|
// defines the default sequence of the widget's elements
|
|
if (options._tweetFeedElement)
|
|
{
|
|
options._baseElement.append(options._tweetFeedElement);
|
|
}
|
|
|
|
if (options._tweetFeedControlsElement)
|
|
{
|
|
options._baseElement.append(options._tweetFeedControlsElement);
|
|
}
|
|
|
|
if (options._connectButtonElement)
|
|
{
|
|
options._baseElement.append(options._connectButtonElement);
|
|
}
|
|
|
|
if (options._loginInfoElement)
|
|
{
|
|
options._baseElement.append(options._loginInfoElement);
|
|
}
|
|
|
|
if (options._followButtonElement)
|
|
{
|
|
options._baseElement.append(options._followButtonElement);
|
|
}
|
|
|
|
if (options._tweetBoxElement)
|
|
{
|
|
options._baseElement.append(options._tweetBoxElement);
|
|
}
|
|
};
|
|
defaultTweetFeedControlsDecorator = function(options)
|
|
{
|
|
// the default tweet feed's paging controls
|
|
var html = '';
|
|
|
|
if (options._tweetFeedConfig.paging.mode == 'prev-next')
|
|
{
|
|
if (options.tweetFeedControlsPrevBtnDecorator)
|
|
{
|
|
html += options.tweetFeedControlsPrevBtnDecorator(options);
|
|
}
|
|
|
|
if (options.tweetFeedControlsNextBtnDecorator)
|
|
{
|
|
html += options.tweetFeedControlsNextBtnDecorator(options);
|
|
}
|
|
}
|
|
else if (options._tweetFeedConfig.paging.mode == 'endless-scroll')
|
|
{
|
|
// nothing to do here atm
|
|
}
|
|
else
|
|
{
|
|
if (options.tweetFeedControlsMoreBtnDecorator)
|
|
{
|
|
html += options.tweetFeedControlsMoreBtnDecorator(options);
|
|
}
|
|
}
|
|
|
|
return '<div class="jta-tweet-list-controls">' + html + '</div>';
|
|
};
|
|
defaultTweetFeedControlsMoreBtnDecorator = function(options)
|
|
{
|
|
return '<span class="jta-tweet-list-controls-button jta-tweet-list-controls-button-more">' + 'More' + '</span>';
|
|
};
|
|
defaultTweetFeedControlsPrevBtnDecorator = function(options)
|
|
{
|
|
return '<span class="jta-tweet-list-controls-button jta-tweet-list-controls-button-prev">' + 'Prev' + '</span>';
|
|
};
|
|
defaultTweetFeedControlsNextBtnDecorator = function(options)
|
|
{
|
|
return '<span class="jta-tweet-list-controls-button jta-tweet-list-controls-button-next">' + 'Next' + '</span>';
|
|
};
|
|
defaultTweetFeedAutorefreshTriggerDecorator = function(count, options)
|
|
{
|
|
var html = '';
|
|
|
|
if (options.tweetFeedAutorefreshTriggerContentDecorator)
|
|
{
|
|
html = options.tweetFeedAutorefreshTriggerContentDecorator(count, options);
|
|
}
|
|
|
|
return '<li class="jta-tweet-list-autorefresh-trigger">' + html + '</li>';
|
|
};
|
|
defaultTweetFeedAutorefreshTriggerContentDecorator = function(count, options)
|
|
{
|
|
var content = '' + count + ' new ' + (count > 1 ? ' tweets' : ' tweet');
|
|
|
|
return '<span class="jta-tweet-list-autorefresh-trigger-content">' + content + '</span>';
|
|
};
|
|
defaultTweetFeedDecorator = function(options)
|
|
{
|
|
// the default placeholder for the tweet feed is an unordered list
|
|
return '<ul class="jta-tweet-list"></ul>';
|
|
};
|
|
defaultTweetDecorator = function(tweet, options)
|
|
{
|
|
// the default tweet is made of the optional user's profile image and the
|
|
// tweet body inside a list item element
|
|
var html = '';
|
|
|
|
if (options._tweetFeedConfig.showProfileImages)
|
|
{
|
|
html += options.tweetProfileImageDecorator(tweet, options);
|
|
}
|
|
|
|
if (options.tweetBodyDecorator)
|
|
{
|
|
html += options.tweetBodyDecorator(tweet, options);
|
|
}
|
|
|
|
html += '<div class="jta-clear"> </div>';
|
|
|
|
return '<li class="jta-tweet-list-item">' + html + '</li>';
|
|
};
|
|
defaultTweetProfileImageDecorator = function(tweet, options)
|
|
{
|
|
// if tweet is a native retweet, use the retweet's profile
|
|
var t = tweet.retweeted_status || tweet;
|
|
|
|
// the default profile image decorator simply adds a link to the user's Twitter profile
|
|
var screenName = t.user ? t.user.screen_name : false || t.from_user;
|
|
var imageUrl = t.user ? t.user.profile_image_url : false || t.profile_image_url;
|
|
|
|
var html =
|
|
'<a class="jta-tweet-profile-image-link" href="http://twitter.com/' + screenName + '" target="_blank">' +
|
|
'<img src="' + imageUrl + '" alt="' + screenName + '"' +
|
|
(isAnywherePresent() ? '' : (' title="' + screenName + '"')) +
|
|
'/>' +
|
|
'</a>';
|
|
|
|
return '<div class="jta-tweet-profile-image">' + html + '</div>';
|
|
};
|
|
defaultTweetBodyDecorator = function(tweet, options)
|
|
{
|
|
// the default tweet body contains the tweet text and the tweet's creation date
|
|
var html = '';
|
|
|
|
if (options.tweetTextDecorator)
|
|
{
|
|
html += options.tweetTextDecorator(tweet, options);
|
|
}
|
|
|
|
if (options.tweetAttributesDecorator)
|
|
{
|
|
html += options.tweetAttributesDecorator(tweet, options);
|
|
}
|
|
|
|
return '<div class="jta-tweet-body ' + (options._tweetFeedConfig.showProfileImages ? 'jta-tweet-body-list-profile-image-present' : '') + '">' + html + '</div>';
|
|
};
|
|
defaultTweetTextDecorator = function(tweet, options)
|
|
{
|
|
var tweetText = tweet.text;
|
|
|
|
// if usernames should be visible and tweet is a native retweet, use
|
|
// the original tweet text
|
|
if (tweet.retweeted_status &&
|
|
(
|
|
options._tweetFeedConfig.showUserScreenNames ||
|
|
options._tweetFeedConfig.showUserScreenNames == null ||
|
|
options._tweetFeedConfig.showUserFullNames ||
|
|
options._tweetFeedConfig.showUserFullNames == null
|
|
)
|
|
)
|
|
{
|
|
tweetText = tweet.retweeted_status.text;
|
|
}
|
|
|
|
// the default tweet text decorator optionally marks links, @usernames,
|
|
// and #hashtags
|
|
if (options.linkDecorator)
|
|
{
|
|
tweetText = options.linkDecorator(tweetText, options);
|
|
}
|
|
|
|
if (options.usernameDecorator)
|
|
{
|
|
tweetText = options.usernameDecorator(tweetText, options);
|
|
}
|
|
|
|
if (options.hashtagDecorator)
|
|
{
|
|
tweetText = options.hashtagDecorator(tweetText, options);
|
|
}
|
|
|
|
if (options._tweetFeedConfig.showUserScreenNames ||
|
|
options._tweetFeedConfig.showUserFullNames ||
|
|
tweet.retweeted_status &&
|
|
(
|
|
options._tweetFeedConfig.showUserScreenNames == null ||
|
|
options._tweetFeedConfig.showUserFullNames == null
|
|
)
|
|
)
|
|
{
|
|
tweetText = options.tweetUsernameDecorator(tweet, options) + ' ' + tweetText;
|
|
}
|
|
|
|
return '<span class="jta-tweet-text">' + tweetText + '</span>';
|
|
};
|
|
defaultTweetUsernameDecorator = function(tweet, options)
|
|
{
|
|
// if tweet is a native retweet, use the retweet's profile
|
|
var t = tweet.retweeted_status || tweet;
|
|
var screenName = t.user ? t.user.screen_name : false || t.from_user;
|
|
var fullName = t.user ? t.user.name : null;
|
|
|
|
var htmlScreenName;
|
|
if (screenName && (options._tweetFeedConfig.showUserScreenNames || (options._tweetFeedConfig.showUserScreenNames == null && tweet.retweeted_status)))
|
|
{
|
|
htmlScreenName =
|
|
'<span class="jta-tweet-user-screen-name">' +
|
|
'<a class="jta-tweet-user-screen-name-link" href="http://twitter.com/' + screenName + '" target="_blank">' +
|
|
screenName +
|
|
'</a>' +
|
|
'</span>';
|
|
}
|
|
|
|
var htmlFullName;
|
|
if (fullName && (options._tweetFeedConfig.showUserFullNames || (options._tweetFeedConfig.showUserFullNames == null && tweet.retweeted_status)))
|
|
{
|
|
htmlFullName =
|
|
'<span class="jta-tweet-user-full-name">' +
|
|
(htmlScreenName ? ' (' : '') +
|
|
'<a class="jta-tweet-user-full-name-link" href="http://twitter.com/' + screenName + '" name="' + screenName + '" target="_blank">' +
|
|
fullName +
|
|
'</a>' +
|
|
(htmlScreenName ? ')' : '') +
|
|
'</span>';
|
|
}
|
|
|
|
var html = "";
|
|
|
|
if (htmlScreenName)
|
|
{
|
|
html += htmlScreenName;
|
|
}
|
|
|
|
if (htmlFullName)
|
|
{
|
|
if (htmlScreenName)
|
|
{
|
|
html += ' ';
|
|
}
|
|
|
|
html += htmlFullName;
|
|
}
|
|
|
|
if (htmlScreenName || htmlFullName)
|
|
{
|
|
html =
|
|
'<span class="jta-tweet-user-name">' +
|
|
(tweet.retweeted_status ? 'RT ' : '') +
|
|
html +
|
|
'</span>';
|
|
}
|
|
|
|
return html;
|
|
};
|
|
defaultTweetAttributesDecorator = function(tweet, options)
|
|
{
|
|
var html = '';
|
|
|
|
if (options.tweetTimestampDecorator ||
|
|
options.tweetSourceDecorator ||
|
|
options.tweetGeoLocationDecorator ||
|
|
options.tweetInReplyToDecorator ||
|
|
(tweet.retweeted_status && options.tweetRetweeterDecorator)
|
|
)
|
|
{
|
|
html += '<span class="jta-tweet-attributes">';
|
|
|
|
if (options.tweetTimestampDecorator)
|
|
{
|
|
html += options.tweetTimestampDecorator(tweet, options);
|
|
}
|
|
|
|
if (options.tweetSourceDecorator)
|
|
{
|
|
html += options.tweetSourceDecorator(tweet, options);
|
|
}
|
|
|
|
if (options.tweetGeoLocationDecorator)
|
|
{
|
|
html += options.tweetGeoLocationDecorator(tweet, options);
|
|
}
|
|
|
|
if (options.tweetInReplyToDecorator)
|
|
{
|
|
html += options.tweetInReplyToDecorator(tweet, options);
|
|
}
|
|
|
|
if (tweet.retweeted_status && options.tweetRetweeterDecorator)
|
|
{
|
|
html += options.tweetRetweeterDecorator(tweet, options);
|
|
}
|
|
|
|
html += '</span>';
|
|
}
|
|
|
|
return html;
|
|
};
|
|
defaultTweetTimestampDecorator = function(tweet, options)
|
|
{
|
|
// the default tweet timestamp decorator does a little bit of Twitter like formatting.
|
|
|
|
// if tweet is a native retweet, use the retweet's timestamp
|
|
var tw = tweet.retweeted_status || tweet;
|
|
|
|
// reformat timestamp from Twitter, so IE is happy
|
|
var createdAt = formatDate(tw.created_at);
|
|
|
|
// format the timestamp by the tweetTimestampFormatter
|
|
var tweetTimestamp = options.tweetTimestampFormatter(createdAt);
|
|
var tweetTimestampTooltip = options.tweetTimestampTooltipFormatter(createdAt);
|
|
|
|
var screenName = tw.user ? tw.user.screen_name : false || tw.from_user;
|
|
var html =
|
|
'<span class="jta-tweet-timestamp">' +
|
|
'<a class="jta-tweet-timestamp-link" data-timestamp="' + createdAt +
|
|
'" href="http://twitter.com/' + screenName + '/status/' + tw.id + '" target="_blank" title="' +
|
|
tweetTimestampTooltip + '">' +
|
|
tweetTimestamp +
|
|
'</a>' +
|
|
'</span>';
|
|
|
|
return html;
|
|
};
|
|
defaultTweetTimestampTooltipFormatter = function(timeStamp)
|
|
{
|
|
var d = new Date(timeStamp);
|
|
|
|
return d.toLocaleString();
|
|
};
|
|
defaultTweetTimestampFormatter = function(timeStamp)
|
|
{
|
|
var now = new Date();
|
|
|
|
var diff = parseInt((now.getTime() - Date.parse(timeStamp)) / 1000);
|
|
|
|
var tweetTimestamp = '';
|
|
if (diff < 60)
|
|
{
|
|
tweetTimestamp += diff + ' second' + (diff == 1 ? '' : 's') + ' ago';
|
|
}
|
|
else if (diff < 3600)
|
|
{
|
|
var t = parseInt((diff + 30) / 60);
|
|
tweetTimestamp += t + ' minute' + (t == 1 ? '' : 's') + ' ago';
|
|
}
|
|
else if (diff < 86400)
|
|
{
|
|
var t = parseInt((diff + 1800) / 3600);
|
|
tweetTimestamp += t + ' hour' + (t == 1 ? '' : 's') + ' ago';
|
|
}
|
|
else
|
|
{
|
|
var d = new Date(timeStamp);
|
|
var period = 'AM';
|
|
|
|
var hours = d.getHours();
|
|
if (hours > 12)
|
|
{
|
|
hours -= 12;
|
|
period = 'PM';
|
|
}
|
|
|
|
var mins = d.getMinutes();
|
|
var minutes = (mins < 10 ? '0' : '') + mins;
|
|
|
|
var monthName = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
|
|
|
|
tweetTimestamp += monthName[d.getMonth()] + ' ' + d.getDate();
|
|
|
|
if (d.getFullYear() < now.getFullYear())
|
|
{
|
|
tweetTimestamp += ', ' + d.getFullYear();
|
|
}
|
|
|
|
var t = parseInt((diff + 43200) / 86400);
|
|
tweetTimestamp += ' (' + t + ' day' + (t == 1 ? '' : 's') + ' ago)';
|
|
}
|
|
|
|
return tweetTimestamp;
|
|
};
|
|
exTimestampFormatter = function(timeStamp)
|
|
{
|
|
var diff = parseInt((new Date().getTime() - Date.parse(timeStamp)) / 1000);
|
|
|
|
var tweetTimestamp = '';
|
|
if (diff < 60)
|
|
{
|
|
tweetTimestamp += 'less than a minute ago';
|
|
}
|
|
else if (diff < 3600)
|
|
{
|
|
var t = parseInt((diff + 30) / 60);
|
|
tweetTimestamp += t + ' minute' + (t == 1 ? '' : 's') + ' ago';
|
|
}
|
|
else if (diff < 86400)
|
|
{
|
|
var t = parseInt((diff + 1800) / 3600);
|
|
tweetTimestamp += 'about ' + t + ' hour' + (t == 1 ? '' : 's') + ' ago';
|
|
}
|
|
else
|
|
{
|
|
var t = parseInt((diff + 43200) / 86400);
|
|
tweetTimestamp += 'about ' + t + ' day' + (t == 1 ? '' : 's') + ' ago';
|
|
|
|
var d = new Date(timeStamp);
|
|
var period = 'AM';
|
|
|
|
var hours = d.getHours();
|
|
if (hours > 12)
|
|
{
|
|
hours -= 12;
|
|
period = 'PM';
|
|
}
|
|
|
|
var mins = d.getMinutes();
|
|
var minutes = (mins < 10 ? '0' : '') + mins;
|
|
|
|
tweetTimestamp += ' (' + hours + ':' + minutes + ' ' + period + ' ' + (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear() + ')';
|
|
}
|
|
|
|
return tweetTimestamp;
|
|
};
|
|
defaultTweetSourceDecorator = function(tweet, options)
|
|
{
|
|
// if tweet is a native retweet, use the retweet's source
|
|
var tw = tweet.retweeted_status || tweet;
|
|
|
|
var source = tw.source.replace(/\<\;/gi,'<').replace(/\>\;/gi,'>').replace(/\"\;/gi,'"');
|
|
var html =
|
|
'<span class="jta-tweet-source">' +
|
|
' via ' +
|
|
'<span class="jta-tweet-source-link">' +
|
|
source +
|
|
'</span>' +
|
|
'</span>';
|
|
|
|
return html;
|
|
};
|
|
defaultTweetGeoLocationDecorator = function(tweet, options)
|
|
{
|
|
var html = '';
|
|
|
|
// if tweet is a native retweet, use the retweet's source
|
|
var tw = tweet.retweeted_status || tweet;
|
|
|
|
var q;
|
|
if (tw.geo && tw.geo.coordinates)
|
|
{
|
|
q = tw.geo.coordinates.join();
|
|
}
|
|
else if (tw.place && tw.place.full_name)
|
|
{
|
|
q = tw.place.full_name;
|
|
}
|
|
|
|
if (q)
|
|
{
|
|
var location = 'here';
|
|
if (tw.place && tw.place.full_name)
|
|
{
|
|
location = tw.place.full_name;
|
|
}
|
|
|
|
var link = 'http://maps.google.com/maps?q=' + q;
|
|
|
|
html =
|
|
'<span class="jta-tweet-location">' +
|
|
' from ' +
|
|
'<a class="jta-tweet-location-link" href="' + link + '" target="_blank">' +
|
|
location +
|
|
'</a>' +
|
|
'</span>';
|
|
}
|
|
|
|
return html;
|
|
};
|
|
defaultTweetInReplyToDecorator = function(tweet, options)
|
|
{
|
|
// if tweet is a native retweet, use the retweet's source
|
|
var tw = tweet.retweeted_status || tweet;
|
|
|
|
var html = '';
|
|
if (tw.in_reply_to_status_id && tw.in_reply_to_screen_name)
|
|
{
|
|
html =
|
|
'<span class="jta-tweet-inreplyto">' +
|
|
' ' +
|
|
'<a class="jta-tweet-inreplyto-link" href="http://twitter.com/' + tw.in_reply_to_screen_name + '/status/' + tw.in_reply_to_status_id + '" target="_blank">' +
|
|
'in reply to ' + tw.in_reply_to_screen_name +
|
|
'</a>' +
|
|
'</span>';
|
|
}
|
|
|
|
return html;
|
|
};
|
|
defaultTweetRetweeterDecorator = function(tweet, options)
|
|
{
|
|
var html = '';
|
|
|
|
if (tweet.retweeted_status)
|
|
{
|
|
var screenName = tweet.user ? tweet.user.screen_name : false || tweet.from_user;
|
|
var rtc = (tweet.retweeted_status.retweet_count || 0) - 1;
|
|
|
|
var link =
|
|
'<a class="jta-tweet-retweeter-link" href="http://twitter.com/' + screenName + '" target="_blank">' +
|
|
screenName +
|
|
'</a>';
|
|
var rtcount = ' and ' + rtc + (rtc > 1 ? ' others' : ' other');
|
|
html =
|
|
'<br/>' +
|
|
'<span class="jta-tweet-retweeter">' +
|
|
'Retweeted by ' + link +
|
|
(rtc > 0 ? rtcount : '') +
|
|
'</span>';
|
|
}
|
|
|
|
return html;
|
|
};
|
|
defaultConnectButtonDecorator = function(options)
|
|
{
|
|
// the default placeholder for the @Anywhere ConnectButton
|
|
return '<div class="jta-connect-button"></div>';
|
|
};
|
|
defaultLoginInfoDecorator = function(options)
|
|
{
|
|
// the default placeholder for the LoginInfo
|
|
return '<div class="jta-login-info"></div>';
|
|
};
|
|
defaultLoginInfoContentDecorator = function(options, T)
|
|
{
|
|
// the default markup of the LoginInfo content: the user's profile image, the
|
|
// user's screen_name and a "button" to sign out
|
|
var html = '';
|
|
|
|
if (T.isConnected())
|
|
{
|
|
var screenName = T.currentUser.data('screen_name');
|
|
var imageUrl = T.currentUser.data('profile_image_url');
|
|
|
|
html =
|
|
'<div class="jta-login-info-profile-image">' +
|
|
'<a href="http://twitter.com/' + screenName + '" target="_blank">' +
|
|
'<img src="' + imageUrl + '" alt="' + screenName + '" title="' + screenName + '"/>' +
|
|
'</a>' +
|
|
'</div>' +
|
|
'<div class="jta-login-info-block">' +
|
|
'<div class="jta-login-info-screen-name">' +
|
|
'<a href="http://twitter.com/' + screenName + '" target="_blank">' + screenName + '</a>' +
|
|
'</div>' +
|
|
'<div class="jta-login-info-sign-out">' +
|
|
'Sign out' +
|
|
'</div>' +
|
|
'</div>' +
|
|
'<div class="jta-clear"> </div>'
|
|
;
|
|
}
|
|
|
|
return html;
|
|
};
|
|
defaultFollowButtonDecorator = function(options)
|
|
{
|
|
// the default placeholder for the @Anywhere FollowButton
|
|
return '<div class="jta-follow-button"></div>';
|
|
};
|
|
defaultTweetBoxDecorator = function(options)
|
|
{
|
|
// the default placeholder for the @Anywhere TweetBox
|
|
return '<div class="jta-tweet-box"></div>';
|
|
};
|
|
defaultLinkDecorator = function(text, options)
|
|
{
|
|
// the regex to markup links
|
|
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>');
|
|
};
|
|
defaultUsernameDecorator = function(text, options)
|
|
{
|
|
// the regex to markup @usernames. if @Anywhere is present the task is left to
|
|
// them
|
|
return isAnywherePresent() ? text : text.replace(/@([a-zA-Z0-9_]+)/gi,'@<a href="http://twitter.com/$1" class="jta-tweet-a twitter-anywhere-user" target="_blank" rel="nofollow">$1<\/a>');
|
|
};
|
|
defaultHashtagDecorator = function(text, options)
|
|
{
|
|
// the regex to markup #hashtags
|
|
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>');
|
|
};
|
|
defaultLoadingDecorator = function(options)
|
|
{
|
|
// the default loading decorator simply says: loading ...
|
|
return '<li class="jta-loading">loading ...</li>';
|
|
};
|
|
defaultErrorDecorator = function(errorText, options)
|
|
{
|
|
// the default error decorator shows the error message
|
|
return '<li class="jta-error">ERROR: ' + errorText + '</li>';
|
|
};
|
|
defaultNoDataDecorator = function(options)
|
|
{
|
|
// the default no-data decorator simply says: No more data
|
|
return '<li class="jta-nodata">No more data</li>';
|
|
};
|
|
|
|
defaultTweetFilter = function(tweet, options)
|
|
{
|
|
return true;
|
|
};
|
|
|
|
defaultTweetVisualizer = function(tweetFeedElement, tweetElement, inserter, options)
|
|
{
|
|
// insert (append/prepend) the tweetElement to the tweetFeedElement
|
|
tweetFeedElement[inserter](tweetElement);
|
|
};
|
|
defaultLoadingIndicatorVisualizer = function(tweetFeedElement, loadingIndicatorElement, options, callback)
|
|
{
|
|
defaultVisualizer(tweetFeedElement, loadingIndicatorElement, 'append', 'fadeIn', 600, 'fadeOut', 200, callback);
|
|
};
|
|
defaultAutorefreshTriggerVisualizer = function(tweetFeedElement, triggerElement, options, callback)
|
|
{
|
|
defaultVisualizer(tweetFeedElement, triggerElement, 'prepend', 'slideDown', 600, 'fadeOut', 200, callback);
|
|
};
|
|
defaultVisualizer = function(container, element, inserter, effectIn, durationIn, effectOut, durationOut, callback)
|
|
{
|
|
// if param container is null element has to be removed from
|
|
// the DOM, else element has to be inserted in container
|
|
|
|
// if param callback is not null, the callback function must be called
|
|
// in any case, if the visualizer is done
|
|
|
|
var cb = function()
|
|
{
|
|
if (callback)
|
|
{
|
|
callback();
|
|
}
|
|
};
|
|
|
|
if (container)
|
|
{
|
|
element.hide();
|
|
container[inserter](element);
|
|
element[effectIn](durationIn, cb);
|
|
}
|
|
else
|
|
{
|
|
element[effectOut](durationOut, function()
|
|
{
|
|
element.remove();
|
|
cb();
|
|
});
|
|
}
|
|
};
|
|
|
|
defaultOnDataRequestHandler = function(stats, options)
|
|
{
|
|
return true;
|
|
};
|
|
defaultOnRateLimitDataHandler = function(stats, options)
|
|
{
|
|
};
|
|
|
|
updateLoginInfoElement = function(options, T)
|
|
{
|
|
// update the content of the LoginInfo element
|
|
if (options._loginInfoElement && options.loginInfoContentDecorator)
|
|
{
|
|
options._loginInfoElement.children().remove();
|
|
options._loginInfoElement.append(options.loginInfoContentDecorator(options, T));
|
|
$(options._baseSelector + ' .jta-login-info-sign-out').bind('click', function()
|
|
{
|
|
twttr.anywhere.signOut();
|
|
});
|
|
}
|
|
};
|
|
getFeedUrl = function(options, flPaging)
|
|
{
|
|
// create the Twitter API URL based on the configuration options
|
|
var url = ('https:' == document.location.protocol ? 'https:' : 'http:');
|
|
|
|
if (options.searchParams)
|
|
{
|
|
url += '//search.twitter.com/search.json?' +
|
|
((options.searchParams instanceof Array) ? options.searchParams.join('&') : options.searchParams) +
|
|
'&rpp=100';
|
|
}
|
|
else if (options.list)
|
|
{
|
|
url += '//api.twitter.com/1/' + options.username + '/lists/' + options.list + '/statuses.json?per_page=20';
|
|
}
|
|
else
|
|
{
|
|
url += '//api.twitter.com/1/statuses/user_timeline.json?screen_name=' + options.username + '&count=20';
|
|
if (options._tweetFeedConfig.includeRetweets)
|
|
url += '&include_rts=true';
|
|
}
|
|
|
|
if (flPaging)
|
|
{
|
|
url +=
|
|
(options._tweetFeedConfig._maxId ? '&max_id=' + options._tweetFeedConfig._maxId : '') +
|
|
'&page=' + options._tweetFeedConfig._pageParam;
|
|
}
|
|
|
|
url += '&callback=?';
|
|
|
|
return url;
|
|
};
|
|
isAnywherePresent = function()
|
|
{
|
|
// check, if @Anywhere is present
|
|
return typeof(twttr) != 'undefined';
|
|
};
|
|
clearTweetFeed = function(options)
|
|
{
|
|
if (options._tweetFeedElement)
|
|
{
|
|
options._tweetFeedElement.empty();
|
|
}
|
|
};
|
|
populateTweetFeed = function(options)
|
|
{
|
|
// if a tweet feed is to be displayed, get the tweets and show them
|
|
if (options.tweetDecorator && options._tweetFeedElement)
|
|
{
|
|
getPagedTweets(options, function(tweets, options)
|
|
{
|
|
if (options._tweetFeedConfig._clearBeforePopulate)
|
|
{
|
|
clearTweetFeed(options);
|
|
}
|
|
|
|
hideLoadingIndicator(options, function()
|
|
{
|
|
// process the tweets
|
|
$.each(tweets, function(idx, tweet)
|
|
{
|
|
// decorate the tweet and give it to the tweet visualizer
|
|
options.tweetVisualizer(
|
|
options._tweetFeedElement,
|
|
$(options.tweetDecorator(tweet, options)),
|
|
'append',
|
|
options
|
|
);
|
|
});
|
|
|
|
if (options._tweetFeedConfig._noData && options.noDataDecorator && !options._tweetFeedConfig._noDataElement)
|
|
{
|
|
options._tweetFeedConfig._noDataElement = $(options.noDataDecorator(options));
|
|
options._tweetFeedElement.append(options._tweetFeedConfig._noDataElement);
|
|
}
|
|
|
|
if (options._tweetFeedConfig._clearBeforePopulate)
|
|
{
|
|
options._tweetFeedElement.scrollTop(0);
|
|
}
|
|
|
|
addHovercards(options);
|
|
});
|
|
});
|
|
}
|
|
};
|
|
populateTweetFeed2 = function(options)
|
|
{
|
|
if (options._tweetFeedElement && options._autorefreshTweetsCache.length > 0)
|
|
{
|
|
if (options._tweetFeedConfig.autorefresh.mode == 'trigger-insert')
|
|
{
|
|
if (options._tweetFeedConfig.autorefresh._triggerElement)
|
|
{
|
|
if (options.tweetFeedAutorefreshTriggerContentDecorator)
|
|
{
|
|
options._tweetFeedConfig.autorefresh._triggerElement.html(
|
|
options.tweetFeedAutorefreshTriggerContentDecorator(options._autorefreshTweetsCache.length, options)
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (options.tweetFeedAutorefreshTriggerDecorator)
|
|
{
|
|
options._tweetFeedConfig.autorefresh._triggerElement =
|
|
$(options.tweetFeedAutorefreshTriggerDecorator(options._autorefreshTweetsCache.length, options));
|
|
options._tweetFeedConfig.autorefresh._triggerElement.bind('click', function()
|
|
{
|
|
options.autorefreshTriggerVisualizer(
|
|
null,
|
|
options._tweetFeedConfig.autorefresh._triggerElement,
|
|
options,
|
|
function()
|
|
{
|
|
insertTriggerTweets(options);
|
|
}
|
|
);
|
|
options._tweetFeedConfig.autorefresh._triggerElement = null;
|
|
});
|
|
|
|
options.autorefreshTriggerVisualizer(options._tweetFeedElement, options._tweetFeedConfig.autorefresh._triggerElement, options);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
insertTriggerTweets(options);
|
|
}
|
|
}
|
|
};
|
|
insertTriggerTweets = function(options)
|
|
{
|
|
// populate the tweet feed with tweets from the autorefresh cache
|
|
if (options.tweetDecorator && options._autorefreshTweetsCache.length > 0)
|
|
{
|
|
// process the autorefresh cache
|
|
while (options._autorefreshTweetsCache.length > 0)
|
|
{
|
|
// get the last tweet and remove it from the autorefresh cache
|
|
var tweet = options._autorefreshTweetsCache.pop();
|
|
|
|
// put that tweet on top of the tweets cache
|
|
options._tweetsCache.unshift(tweet);
|
|
|
|
// adjust paging offset
|
|
options._tweetFeedConfig.paging._offset++;
|
|
|
|
// decorate the tweet and give it to the tweet visualizer
|
|
options.tweetVisualizer(
|
|
options._tweetFeedElement,
|
|
$(options.tweetDecorator(tweet, options)),
|
|
'prepend',
|
|
options
|
|
);
|
|
}
|
|
|
|
addHovercards(options);
|
|
}
|
|
};
|
|
addHovercards = function(options)
|
|
{
|
|
if (isAnywherePresent())
|
|
{
|
|
// if @Anywhere is present, append Hovercards to @username and
|
|
// profile images
|
|
twttr.anywhere(function(T)
|
|
{
|
|
T(options._baseSelector + ' .jta-tweet-list').hovercards({expanded: options._tweetFeedConfig.expandHovercards});
|
|
T(options._baseSelector + ' .jta-tweet-profile-image img').hovercards(
|
|
{
|
|
expanded: options._tweetFeedConfig.expandHovercards,
|
|
username: function(node) { return node.alt; }
|
|
});
|
|
T(options._baseSelector + ' .jta-tweet-retweeter-link').hovercards(
|
|
{
|
|
expanded: options._tweetFeedConfig.expandHovercards,
|
|
username: function(node) { return node.text; }
|
|
});
|
|
T(options._baseSelector + ' .jta-tweet-user-screen-name-link').hovercards(
|
|
{
|
|
expanded: options._tweetFeedConfig.expandHovercards,
|
|
username: function(node) { return node.text; }
|
|
});
|
|
T(options._baseSelector + ' .jta-tweet-user-full-name-link').hovercards(
|
|
{
|
|
expanded: options._tweetFeedConfig.expandHovercards,
|
|
username: function(node) { return node.name; }
|
|
});
|
|
});
|
|
}
|
|
};
|
|
populateAnywhereControls = function(options)
|
|
{
|
|
if (isAnywherePresent())
|
|
{
|
|
twttr.anywhere(function(T)
|
|
{
|
|
// optionally add an @Anywhere TweetBox
|
|
if (options.tweetBoxDecorator)
|
|
{
|
|
T(options._baseSelector + ' .jta-tweet-box').tweetBox(options._tweetBoxConfig);
|
|
}
|
|
|
|
// optionally add an @Anywhere FollowButton
|
|
if (options.followButtonDecorator)
|
|
{
|
|
T(options._baseSelector + ' .jta-follow-button').followButton(options.username);
|
|
}
|
|
|
|
// optionally add an @Anywhere ConnectButton
|
|
if (options.connectButtonDecorator)
|
|
{
|
|
var o = $.extend(
|
|
{
|
|
authComplete: function(user)
|
|
{
|
|
// display/update login infos on connect/signin event
|
|
updateLoginInfoElement(options, T);
|
|
},
|
|
signOut: function()
|
|
{
|
|
// display/update login infos on signout event
|
|
updateLoginInfoElement(options, T);
|
|
}
|
|
}, options._connectButtonConfig);
|
|
|
|
T(options._baseSelector + ' .jta-connect-button').connectButton(o);
|
|
|
|
// display/update login infos
|
|
updateLoginInfoElement(options, T);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
bindEventHandlers = function(options)
|
|
{
|
|
if (options.tweetFeedControlsDecorator)
|
|
{
|
|
if (options._tweetFeedConfig.paging.mode == 'prev-next')
|
|
{
|
|
$(options._baseSelector + ' .jta-tweet-list-controls-button-prev').bind('click', function()
|
|
{
|
|
if (!isLoading(options) && options._tweetFeedConfig.paging._offset > 0)
|
|
{
|
|
prevPage(options, true);
|
|
}
|
|
});
|
|
$(options._baseSelector + ' .jta-tweet-list-controls-button-next').bind('click', function()
|
|
{
|
|
if (!isLoading(options))
|
|
{
|
|
nextPage(options, true);
|
|
}
|
|
});
|
|
}
|
|
else if (options._tweetFeedConfig.paging.mode == 'endless-scroll')
|
|
{
|
|
options._tweetFeedElement.bind("scroll", function()
|
|
{
|
|
if (!isLoading(options) && ($(this)[0].scrollHeight - $(this).scrollTop() == $(this).outerHeight()))
|
|
{
|
|
nextPage(options, false);
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
$(options._baseSelector + ' .jta-tweet-list-controls-button-more').bind('click', function()
|
|
{
|
|
if (!isLoading(options))
|
|
{
|
|
nextPage(options, false);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
nextPage = function(options, flClear)
|
|
{
|
|
doPage(options, flClear, Math.min(options._tweetFeedConfig.paging._offset + options._tweetFeedConfig.paging._limit, options._tweetsCache.length));
|
|
};
|
|
prevPage = function(options, flClear)
|
|
{
|
|
doPage(options, flClear, Math.max(0, options._tweetFeedConfig.paging._offset - options._tweetFeedConfig.paging._limit));
|
|
};
|
|
doPage = function(options, flClear, newOffset)
|
|
{
|
|
options._tweetFeedConfig.paging._offset = newOffset;
|
|
options._tweetFeedConfig._clearBeforePopulate = flClear;
|
|
|
|
populateTweetFeed(options);
|
|
};
|
|
startAutorefresh = function(options)
|
|
{
|
|
if (options._tweetFeedConfig.autorefresh.mode != 'none' &&
|
|
options._tweetFeedConfig.paging.mode != 'prev-next' &&
|
|
options._tweetFeedConfig.autorefresh.duration != 0 &&
|
|
(
|
|
options._tweetFeedConfig.autorefresh.duration < 0 ||
|
|
(new Date().getTime() - options._tweetFeedConfig.autorefresh._startTime) <= options._tweetFeedConfig.autorefresh.duration * 1000
|
|
)
|
|
)
|
|
{
|
|
window.setTimeout(function() { processAutorefresh(options); }, options._tweetFeedConfig.autorefresh.interval * 1000);
|
|
}
|
|
};
|
|
stopAutorefresh = function(options)
|
|
{
|
|
options._tweetFeedConfig.autorefresh.duration = 0;
|
|
};
|
|
processAutorefresh = function(options)
|
|
{
|
|
if (options._tweetFeedConfig.autorefresh.duration != 0)
|
|
{
|
|
// load the data ...
|
|
getRateLimitedData(options, true, getFeedUrl(options, false), function(data, options)
|
|
{
|
|
// reverse the sequence of the autorefresh tweets ...
|
|
var tweets = (data.results || data).slice(0);
|
|
tweets.reverse();
|
|
|
|
// ...then process them
|
|
$.each(tweets, function(idx, tweet)
|
|
{
|
|
// if this tweet is already in the standard tweets cache, ignore
|
|
if (!isTweetInCache(tweet, options))
|
|
{
|
|
// optionally filter tweet ...
|
|
if (options.tweetFilter(tweet, options))
|
|
{
|
|
// ... then put it to the top of the autorefresh cache
|
|
options._autorefreshTweetsCache.unshift(tweet);
|
|
}
|
|
}
|
|
});
|
|
|
|
populateTweetFeed2(options);
|
|
});
|
|
|
|
// restart autorefresh
|
|
startAutorefresh(options);
|
|
}
|
|
};
|
|
startTimestampRefresh = function(options)
|
|
{
|
|
if (
|
|
options.tweetTimestampDecorator &&
|
|
typeof(options._tweetFeedConfig.showTimestamp) == 'object' &&
|
|
options._tweetFeedConfig.showTimestamp.refreshInterval > 0
|
|
)
|
|
{
|
|
window.setTimeout(function() { processTimestampRefresh(options); }, options._tweetFeedConfig.showTimestamp.refreshInterval * 1000);
|
|
}
|
|
};
|
|
processTimestampRefresh = function(options)
|
|
{
|
|
$.each(options._tweetFeedElement.find('.jta-tweet-timestamp-link'), function(idx, element)
|
|
{
|
|
var dataTimestamp = $(element).attr('data-timestamp');
|
|
|
|
$(element).html(options.tweetTimestampFormatter(dataTimestamp));
|
|
});
|
|
|
|
startTimestampRefresh(options);
|
|
};
|
|
isTweetInCache = function(tweet, options)
|
|
{
|
|
var l = options._tweetsCache.length;
|
|
|
|
for (var i = 0; i < l; i++)
|
|
{
|
|
if (tweet.id == options._tweetsCache[i].id)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
showLoadingIndicator = function(options)
|
|
{
|
|
if (options._tweetFeedElement && options.loadingDecorator && !options._loadingIndicatorElement)
|
|
{
|
|
options._loadingIndicatorElement = $(options.loadingDecorator(options));
|
|
options.loadingIndicatorVisualizer(options._tweetFeedElement, options._loadingIndicatorElement, options, null);
|
|
options._tweetFeedElement.scrollTop(1000000);
|
|
}
|
|
};
|
|
hideLoadingIndicator = function(options, callback)
|
|
{
|
|
if (options._loadingIndicatorElement)
|
|
{
|
|
options.loadingIndicatorVisualizer(null, options._loadingIndicatorElement, options, callback);
|
|
options._loadingIndicatorElement = null;
|
|
}
|
|
else
|
|
{
|
|
if (callback)
|
|
{
|
|
callback();
|
|
}
|
|
}
|
|
};
|
|
isLoading = function(options)
|
|
{
|
|
return options._loadingIndicatorElement != null;
|
|
};
|
|
formatDate = function(dateStr)
|
|
{
|
|
return dateStr.replace(/^([a-z]{3})( [a-z]{3} \d\d?)(.*)( \d{4})$/i, '$1,$2$4$3');
|
|
};
|
|
validateRange = function(num, lo, hi)
|
|
{
|
|
if (num < lo)
|
|
num = lo;
|
|
|
|
if (num > hi)
|
|
num = hi;
|
|
|
|
return num;
|
|
};
|
|
showError = function(options, errorText)
|
|
{
|
|
if (options.errorDecorator && options._tweetFeedElement)
|
|
{
|
|
options._tweetFeedElement.append(options.errorDecorator(errorText, options));
|
|
}
|
|
};
|
|
getPagedTweets = function(options, callback)
|
|
{
|
|
options._tweetFeedConfig._recLevel = 0;
|
|
|
|
getRecPagedTweets(options, options._tweetFeedConfig.paging._offset, options._tweetFeedConfig.paging._limit, callback);
|
|
};
|
|
getRecPagedTweets = function(options, offset, limit, callback)
|
|
{
|
|
++options._tweetFeedConfig._recLevel;
|
|
|
|
if (offset + limit <= options._tweetsCache.length ||
|
|
options._tweetFeedConfig._recLevel > 3 ||
|
|
options._tweetFeedConfig._noData
|
|
)
|
|
{
|
|
// if the requested data is already cached or the max. no. of
|
|
// consecutive API calls is reached, use the records
|
|
|
|
if (offset + limit > options._tweetsCache.length)
|
|
{
|
|
limit = Math.max(0, options._tweetsCache.length - offset);
|
|
}
|
|
|
|
var tweets = [];
|
|
|
|
for (var i = 0; i < limit; i++)
|
|
{
|
|
tweets[i] = options._tweetsCache[offset + i];
|
|
}
|
|
|
|
callback(tweets, options);
|
|
}
|
|
else
|
|
{
|
|
// ... if not, load the data, fill the cache and try again
|
|
++options._tweetFeedConfig._pageParam;
|
|
|
|
getRateLimitedData(options, false, getFeedUrl(options, true), function(data, options)
|
|
{
|
|
var tweets = data.results || data;
|
|
|
|
if (tweets.length == 0)
|
|
{
|
|
options._tweetFeedConfig._noData = true;
|
|
}
|
|
else
|
|
{
|
|
$.each(tweets, function(idx, tweet)
|
|
{
|
|
// Snowflake support: just update ids that are currently used
|
|
if (tweet.id_str) { tweet.id = tweet.id_str; }
|
|
if (tweet.in_reply_to_status_id_str) { tweet.in_reply_to_status_id = tweet.in_reply_to_status_id_str; }
|
|
|
|
// save the first tweet id for subsequent paging requests
|
|
if (!options._tweetFeedConfig._maxId)
|
|
{
|
|
options._tweetFeedConfig._maxId = tweet.id;
|
|
}
|
|
|
|
// optionally filter tweet ...
|
|
if (options.tweetFilter(tweet, options))
|
|
{
|
|
// then put it into the cache
|
|
options._tweetsCache.push(tweet);
|
|
}
|
|
});
|
|
}
|
|
|
|
getRecPagedTweets(options, offset, limit, callback);
|
|
});
|
|
}
|
|
};
|
|
getRateLimitedData = function(options, flAutorefresh, url, callback)
|
|
{
|
|
getRateLimit(options, function(rateLimit)
|
|
{
|
|
if (rateLimit && rateLimit.remaining_hits <= 0)
|
|
{
|
|
options._stats.rateLimitPreventionCount++;
|
|
hideLoadingIndicator(options, null);
|
|
return;
|
|
}
|
|
|
|
getData(options, flAutorefresh, url, callback);
|
|
});
|
|
};
|
|
getData = function(options, flAutorefresh, url, callback)
|
|
{
|
|
options._stats.dataRequestCount++;
|
|
|
|
if (!options.onDataRequestHandler(options._stats, options))
|
|
{
|
|
hideLoadingIndicator(options, null);
|
|
return;
|
|
}
|
|
|
|
if (!flAutorefresh)
|
|
{
|
|
showLoadingIndicator(options);
|
|
}
|
|
|
|
$.getJSON(url, function(data)
|
|
{
|
|
if (data.error)
|
|
{
|
|
// in case of an error, display the error message
|
|
showError(options, data.error);
|
|
}
|
|
else
|
|
{
|
|
callback(data, options);
|
|
}
|
|
});
|
|
};
|
|
getRateLimit = function(options, callback)
|
|
{
|
|
$.getJSON("http://api.twitter.com/1/account/rate_limit_status.json?callback=?", function(rateLimit)
|
|
{
|
|
options._stats.rateLimit = rateLimit;
|
|
|
|
options.onRateLimitDataHandler(options._stats, options);
|
|
|
|
callback(rateLimit);
|
|
});
|
|
};
|
|
})(jQuery); |