Projet

Général

Profil

Paste
Télécharger (66,6 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / themes / simplecorp / js / plugins / jquery.jtweetsanywhere-1.3.1.js @ b9383c72

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">&nbsp;</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">&nbsp;</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(/\&lt\;/gi,'<').replace(/\&gt\;/gi,'>').replace(/\&quot\;/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">&nbsp;</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);