1/** 2 * @author http://kirill-novitchenko.com 3 * 4 */ 5 6var TB_version = '4.0.0b4', // Plugin version 7TB_rateLimitData, 8TB_tmp, 9TB_mode = 'widget', 10TB_started = false, 11TB_allSources = new Array(), 12jQnc = jQuery, 13TB_sourceCounts = new Array(), 14TB_sourceNames = new Array(), 15TB_seenTweets = new Array(), 16selectedCell, 17TB_token; 18 19// initialize each widget 20function TB_start() { 21 22 // prevent initializing twice 23 if (TB_started) { 24 return; 25 } 26 else { 27 TB_started = true; 28 } 29 30 // check to make sure config is included 31 if (typeof(TB_config) == 'undefined') { 32 TB_showMessage(null,'noconf',TB_labels.no_config,true); 33 return; 34 } 35 36 // process widget configuration 37 TB_config.widgets = new Object(); 38 jQuery.each(jQuery('form.tb-widget-configuration'),function(i,obj){ 39 40 var widgetConfId = obj.id, 41 widgetId, 42 widgetHTML, 43 needWidgetHTML = false, 44 nextTag = jQuery(obj).next(), 45 isChart = false; 46 47 // if there is widget HTML div following the form we don't need to build HTML 48 if (nextTag.length > 0) { 49 if (typeof(nextTag.attr('id')) != 'undefined' && nextTag.attr('id') != '') { 50 if (nextTag.attr('id').indexOf('-mc') > 0) { 51 widgetId = widgetConfId.substr(0, widgetConfId.length - 2); 52 } 53 else { 54 needWidgetHTML = true; 55 widgetId = widgetConfId; 56 } 57 } 58 else { 59 needWidgetHTML = true; 60 widgetId = widgetConfId; 61 } 62 } 63 // if it's just a form -> assume that's post/page body content 64 else { 65 needWidgetHTML = true; 66 widgetId = widgetConfId; 67 } 68 69 TB_config.widgets[widgetId] = new Object; 70 71 // set all properties 72 jQuery.each(jQuery('#'+widgetConfId).find('input'),function(j,property) { 73 TB_config.widgets[widgetId][property.name] = property.value; 74 }); 75 76 if (typeof(TB_config.widgets[widgetId].sources) != 'undefined') { 77 TB_allSources = TB_allSources.concat(TB_config.widgets[widgetId].sources.split(',')); 78 } 79 80 if(typeof(TB_config.widgets[widgetId].chartType) != 'undefined') { 81 isChart = true; 82 } 83 84 // if no view more url, use default 85 if(typeof(TB_config.widgets[widgetId].viewMoreUrl) == 'undefined') { 86 TB_config.widgets[widgetId].viewMoreUrl = TB_config.default_view_more_url; 87 } 88 89 if (needWidgetHTML) { 90 // add widget HTML 91 widgetHTML = '<div id="' + widgetId + '-mc"><div class="tb_header">' + 92 '<img class="tb_twitterlogo" src="' + TB_pluginPath + '/img/twitter-logo.png" alt="' + TB_labels.twitter_logo + '" />' + 93 '<div class="tb_tools" style="background-image:url(' + TB_pluginPath + '/img/bg_sm.png)">' + 94 '<a class="tb_infolink" href="http://kirill-novitchenko.com" title="' + TB_labels.kino + '" style="background-image:url(' + TB_pluginPath + '/img/info-kino.png)"> </a>' + 95 '<a class="tb_refreshlink" href="javascript:TB_blend(\'' + widgetId + '\');" title="' + TB_labels.refresh + '"><img src="' + TB_pluginPath + '/img/ajax-refresh-icon.gif" alt="' + TB_labels.refresh + '" /></a></div></div>'; 96 97 if (TB_config.general_seo_tweets_googleoff && !isChart) { 98 widgetHTML += '<!--googleoff: index--><div class="tb_tweetlist"></div><!--googleon: index-->'; 99 } 100 else if (isChart){ 101 widgetHTML += '<div id="' + widgetId + '-chart" class="tb_tweetchart"></div>'; 102 } 103 else { 104 widgetHTML += '<div class="tb_tweetlist"></div>'; 105 106 widgetHTML += '<div class="tb_footer">'; 107 if (!TB_config.archive_is_disabled) { 108 109 if (typeof(TB_config.widgets[widgetId].viewMoreText) == 'undefined') { 110 TB_config.widgets[widgetId].viewMoreText = TB_labels.view_more + ' »'; 111 } 112 113 if (TB_config.widgets[widgetId].viewMoreUrl) { 114 widgetHTML += '<a class="tb_archivelink" href="' + TB_config.widgets[widgetId].viewMoreUrl + '">' + TB_config.widgets[widgetId].viewMoreText + '</a>'; 115 } 116 else if (TB_config.default_view_more_url) { 117 widgetHTML += '<a class="tb_archivelink" href="' + TB_config.default_view_more_url + '">' + TB_config.widgets[widgetId].viewMoreText + '</a>'; 118 TB_config.widgets[widgetId].viewMoreUrl = TB_config.default_view_more_url; 119 } 120 } 121 widgetHTML += '</div>'; 122 } 123 124 widgetHTML += '</div>'; 125 126 jQuery('#'+obj.id).after(widgetHTML); 127 } 128 129 // if it's a chart - update width/height 130 if(isChart) { 131 132 // determine width automatically, if needed 133 if (typeof(TB_config.widgets[widgetId].chartWidth) == 'undefined' || TB_config.widgets[widgetId].chartWidth <= 0) { 134 TB_config.widgets[widgetId].chartWidth = jQuery('#' + widgetId + '-chart').parent().width(); 135 } 136 // determine height automatically, if needed
137 if (typeof(TB_config.widgets[widgetId].chartHeight) == 'undefined' || TB_config.widgets[widgetId].chartHeight <= 0) { 138 TB_config.widgets[widgetId].chartHeight = jQuery('#' + widgetId + '-chart').parent().width() + 60; 139 } 140 } 141 }); 142 143 // if there are no widgets on the page - no need to continue 144 if (TB_getObjectSize(TB_config.widgets) < 1) { 145 return; 146 } 147 148 // de-dupe list of all sources 149 TB_allSources = TB_getUniqueElements(TB_allSources); 150 151 jQuery('div.tb_tools').css('background-image','url(' + TB_pluginPath + '/img/bg.png)').width(56); 152 jQuery('a.tb_infolink').css('display','inline').css('margin-right','11px'); 153 154 // if there is no archive page, hide view more links 155 if (!TB_config.default_view_more_url) { 156 jQuery('a.defaultUrl').hide(); 157 } 158 159 // get config options and blend 160 if (typeof(TB_config) != 'undefined') { 161 162 // for each widget on the page 163 for (widgetId in TB_config.widgets) { 164 165 if (typeof(TB_config.widgets[widgetId].sources) == 'undefined' || TB_config.widgets[widgetId].sources == '') { 166 TB_showMessage(widgetId,'nosrc',TB_labels.no_sources, true); 167 } 168 else { 169 170 // create info box shown when Twitter logo is clicked 171 TB_initInfoBox(widgetId); 172 173 // create all the urls for refresh calls 174 TB_makeAjaxURLs(widgetId); 175 176 // if it's not a chart, update values to reflect cache use if there are divs with tweets already 177 if (typeof(TB_config.widgets[widgetId].chartType) == 'undefined') { 178 TB_config.widgets[widgetId]['minTweetId'] = 0; 179 TB_config.widgets[widgetId]['maxTweetId'] = 0; 180 if (jQuery('#'+widgetId + '-mc > div.tb_tweetlist > div.tb_tweet').size() > 0) { 181 if (TB_tmp = jQuery('#'+widgetId + '-mc > div.tb_tweetlist > div:last').attr('id')) { 182 TB_config.widgets[widgetId]['minTweetId'] = TB_tmp; 183 } 184 if (TB_tmp = jQuery('#'+widgetId + '-mc > div.tb_tweetlist > div:first').attr('id')) { 185 TB_config.widgets[widgetId]['maxTweetId'] = TB_tmp; 186 } 187 } 188 TB_config.widgets[widgetId]['tweetsShown'] = jQuery('#'+widgetId + '-mc > div.tb_tweetlist').children('div').size(); 189 190 // wire mouse overs to existing tweets 191 jQuery.each(jQuery('#' + widgetId + '-mc > div.tb_tweetlist').children('div'),function(i,obj){ TB_wireMouseOver(obj.id); }); 192 193 // wire target="_blank" on links 194 jQuery('a.tb_photo, .tb_author a, .tb_msg a, .tweet-tools a, .tb_infolink').click(function(){ 195 this.target = "_blank"; 196 }); 197 } 198 199 // add automatic refresh 200 if (parseInt(TB_config.widgets[widgetId].refreshRate) > 1) { 201 setInterval('TB_blend(\''+widgetId+'\');',parseInt(TB_config.widgets[widgetId].refreshRate) * 1000); 202 } 203 204 205 // if we need to refresh once or 206 // if there are no tweets shown from cache 207 // or if there are less tweets then needed 208 // or if it's a chart 209 // then blend right away 210 if (parseInt(TB_config.widgets[widgetId].refreshRate) == 1 || typeof(TB_config.widgets[widgetId].chartType) != 'undefined' || TB_config.widgets[widgetId].tweetsShown < TB_config.widgets[widgetId].tweetsNum) { 211 TB_blend(widgetId); 212 } 213 } 214 } 215 } 216 else { 217 TB_showMessage(null,'noconf',TB_labels.no_global_config,true); 218 219 // disable refresh 220 jQuery('a.tb_refreshlink').remove(); 221 jQuery('div.tb_tools').css('background-image','url(' + TB_pluginPath + '/img/bg_sm.png)').width(28); 222 } 223} 224 225 226// form refresh queries 227function TB_makeAjaxURLs(widgetId) { 228 var TB_searchTerms = new Array(), 229 TB_screenNameQueries = new Array(), 230 TB_screenNames = new Array(), 231 screenName = '', 232 modifier = '', 233 colonPos, 234 pipePos; 235 236 TB_config.widgets[widgetId]['ajaxURLs'] = new Array(); 237 238 // if it's a chart widget, use all sources for the url 239 if (typeof(TB_config.widgets[widgetId].chartType) != 'undefined') { 240 TB_addAjaxUrl(widgetId,'chart-data',{'chart_type':TB_config.widgets[widgetId].chartType,'chart_period':TB_config.widgets[widgetId].chartPeriod},TB_config.widgets[widgetId].sources,0,null); 241 return; 242 } 243 244 // for other widget types, iterate over sources and make individual urls 245 jQuery.each(TB_config.widgets[widgetId].sources.split(','),function(i,src) { 246 247 // remove spaces 248 src = jQuery.trim(src); 249 250 // if it's a screen name 251 if (src.charAt(0) == '@' && src.indexOf('/') == -1) { 252 253 // if we are serving only favorites 254 if (TB_config.widgets[widgetId].favoritesOnly) { 255 screenName = src.substr(1); 256 257 TB_addAjaxUrl(widgetId,'favorites',{'screen_name':screenName},src,0,null); 258 } 259 // if it includes modifiers, use a one-off URL 260 else if ((pipePos = src.indexOf('|')) > 1) { 261 262 screenName = src.substr(1,pipePos-1); 263 modifier = src.substr(pipePos+1); 264 265 // if modifier is a hashtag 266 if (modifier.charAt(0) == '#') { 267 TB_addAjaxUrl(widgetId,'search',{'from':screenName,'tag':modifier.substr(1)},src,modifier); 268 } 269 // else modifier is just a keyword 270 else { 271 TB_addAjaxUrl(widgetId,'search',{'from':screenName,'ors':modifier},src,modifier); 272 } 273 } 274 else { 275 276 screenName = src.substr(1); 277 278 // if we are not using Search API 279 if (TB_config.advanced_no_search_api) { 280 TB_addAjaxUrl(widgetId,'user_timeline',{'screen_name':screenName},src,null); 281 } 282 // else, group with other screen names 283 else { 284 // check to make sure we are not over the query length limit 285 if (escape(TB_screenNameQueries.join(' OR ')).length + src.length > 140) {
286 TB_addAjaxUrl(widgetId,'search',{'q':TB_screenNameQueries.join(' OR ')},escape('@'+TB_screenNames.join(',@')),null); 287 TB_screenNames = new Array(); 288 TB_screenNameQueries = new Array(); 289 } 290 TB_screenNames.push(screenName); 291 if (TB_config.filter_hide_mentions) { 292 TB_screenNameQueries.push('from:' + screenName); 293 } 294 else { 295 TB_screenNameQueries.push(src + ' OR from:' + screenName); 296 } 297 } 298 } 299 } 300 // if it's a list 301 else if (src.charAt(0) == '@' && src.indexOf('/') > 1) { 302 303 // if we have a modifier 304 if ((pipePos = src.indexOf('|')) > 1) { 305 306 TB_addAjaxUrl(widgetId, 'list_timeline', { 307 'user': src.substr(1, src.indexOf('/') - 1), 308 'list': src.substr(src.indexOf('/') + 1, pipePos - src.indexOf('/') - 1) 309 }, src, src.substr(pipePos+1)); 310 } 311 // if it's just a regular list 312 else { 313 TB_addAjaxUrl(widgetId, 'list_timeline', { 314 'user': src.substr(1, src.indexOf('/') - 1), 315 'list': src.substr(src.indexOf('/') + 1) 316 }, src, null); 317 } 318 } 319 // else it's a hash or keyword 320 else if (src != '') { 321 322 // if it's a multi-word keyword give it a dedicated ajax call 323 if (src.indexOf(' ') > 0) { 324 // if it's not in quotes already then add them 325 if (src.charAt(0) != '"') { 326 src = '"' + src + '"'; 327 } 328 } 329 330 // check to make sure we are not over the query length limit 331 if (TB_searchTerms.join(' OR ').length + src.length > 140) { 332 TB_addAjaxUrl(widgetId,'search',{'q':TB_searchTerms.join(' OR ')},TB_searchTerms.join(','),null); 333 TB_searchTerms = new Array(); 334 } 335 TB_searchTerms.push(src); 336 } 337 }); 338 339 // if there are terms that are not part of a query - add another query 340 if (TB_searchTerms.length > 0) { 341 TB_addAjaxUrl(widgetId,'search',{'q':TB_searchTerms.join(' OR ')},TB_searchTerms.join(','),null); 342 } 343 344 // if there are screenNames - join them into a single query 345 if (TB_screenNames.length > 0) { 346 TB_addAjaxUrl(widgetId,'search',{'q':TB_screenNameQueries.join(' OR ')},encodeURI('@'+TB_screenNames.join(',@')),null); 347 } 348} 349 350function TB_addAjaxUrl(widgetId,actionType,queryData,src,modifier) { 351 352 var url; 353 354 // check language filter 355 if (typeof(TB_config['filter_lang']) != 'undefined' && TB_config.filter_lang.length == 2) { 356 queryData.lang = TB_config.filter_lang; 357 } 358 else { 359 queryData.lang = 'all'; 360 } 361 362 if (actionType == 'search') { 363 queryData.action = actionType; 364 TB_config.widgets[widgetId]['ajaxURLs'].push({ 365 'url':TB_pluginPath + '/ws.php', 366 'data':queryData, 367 'source':src, 368 'modifier':modifier 369 }); 370 } 371 else if (actionType == 'list_timeline') { 372 queryData.action = actionType; 373 374 // remove unneeded params 375 delete queryData.nots; 376 delete queryData.lang; 377 378 TB_config.widgets[widgetId]['ajaxURLs'].push({ 379 'url':TB_pluginPath + '/ws.php', 380 'data':queryData, 381 'source':src, 382 'modifier':modifier 383 }); 384 } 385 else if (actionType == 'user_timeline') { 386 queryData.action = actionType; 387 TB_config.widgets[widgetId]['ajaxURLs'].push({ 388 'url':TB_pluginPath + '/ws.php', 389 'data':queryData, 390 'source':src, 391 'modifier':modifier 392 }); 393 } 394 else if (actionType == 'favorites') { 395 queryData.action = actionType; 396 TB_config.widgets[widgetId]['ajaxURLs'].push({ 397 'data':queryData, 398 'url':TB_pluginPath + '/ws.php', 399 'source':src, 400 'modifier':modifier 401 }); 402 }
403 else if (actionType == 'chart-data') { 404 queryData.action = actionType; 405 TB_config.widgets[widgetId]['ajaxURLs'].push({ 406 'data':queryData, 407 'url':TB_C_pluginPath + '/ws.php', 408 'source':src, 409 'method':'post' 410 }); 411 } 412} 413 414function TB_initInfoBox(widgetId) { 415 // create HTML for sources 416 TB_config.widgets[widgetId].sourcesHTML = ''; 417 TB_config.widgets[widgetId].sourcesCount = 0; 418 jQuery.each(TB_config.widgets[widgetId].sources.split(','),function(i,src) { 419 // if there is a modifier - strip it 420 if ((pipePos = src.indexOf('|')) > 0) { 421 src = src.substr(0, pipePos); 422 } 423 424 TB_config.widgets[widgetId].sourcesHTML += '<a href="'; 425 if (src.charAt(0) == '@') { 426 TB_config.widgets[widgetId].sourcesHTML += 'http://twitter.com/' + src.substr(1); 427 } 428 else { 429 TB_config.widgets[widgetId].sourcesHTML += 'https://twitter.com/search?q=' + escape(src); 430 } 431 TB_config.widgets[widgetId].sourcesHTML += '">' + src + '</a> '; 432 TB_config.widgets[widgetId].sourcesCount++; 433 }); 434 435 // add action to twitter logo 436 jQuery('#' + widgetId + '-mc').children('div.tb_header').children('img.tb_twitterlogo').click(function(){ 437 TB_showMessage(widgetId,'info',TB_labels.version_msg.format(TB_version,TB_config.widgets[widgetId].sourcesHTML),false); 438 }); 439} 440 441function TB_blend(widgetId) { 442 443 // show loading indicator 444 TB_showLoader(widgetId); 445 446 // if it's a chart 447 if (typeof(TB_config.widgets[widgetId].chartType) != 'undefined') { 448 449 // if there is no chart yet 450 if (typeof(TB_config.widgets[widgetId].wrapper) == 'undefined') { 451 TB_C_makeChart(widgetId); 452 } 453 // if we have a chart 454 else { 455 // draw chart 456 TB_config.widgets[widgetId].wrapper.draw(); 457 } 458 459 // hide loader 460 TB_hideLoader(widgetId); 461 462 return; 463 } 464 465 TB_getTweets(widgetId); 466} 467 468function TB_checkComplete(widgetId) { 469 470 if (TB_config.widgets[widgetId].urlsDone == TB_config.widgets[widgetId].ajaxURLs.length) { 471 472 // hide loading message 473 TB_hideLoader(widgetId); 474 475 // if nothing added after we are through all sources let user know 476 if(jQuery('#' + widgetId + '-mc > div.tb_tweetlist').children('div').size() == 0) { 477 // show no tweets message 478 TB_showMessage(widgetId, 'notweets', TB_labels.no_tweets_msg.format(TB_config.widgets[widgetId].sourcesHTML), true); 479 } 480 else { 481 TB_hideMessage(widgetId,'notweets'); 482 } 483 } 484} 485 486function TB_getTweets(widgetId) { 487 488 TB_config.widgets[widgetId]['urlsDone'] = 0; 489 490 // iterate over AJAX URLs 491 jQuery.each(TB_config.widgets[widgetId].ajaxURLs,function(i,urlInfo) { 492 493 // add sources to data 494 495 jQuery.ajax({ 496 data:urlInfo.data, 497 dataType: 'json', 498 url: urlInfo.url, 499 timeout: 6000, 500 success: function (json) { 501//console.log('got response'); 502 // if we had valid JSON but with error 503 if (json.error) { 504 TB_config.widgets[widgetId].urlsDone++; 505 TB_checkComplete(widgetId); 506 } 507 else { 508//console.log('got results'); 509 TB_addTweets(widgetId,json,urlInfo); 510 } 511 }, 512 error: function(jqXHR, textStatus, errorThrown) { 513//console.log('error in ajax: ' + textStatus + ' err: ' + errorThrown); 514 TB_config.widgets[widgetId].urlsDone++; 515 TB_checkComplete(widgetId); 516 } 517 }); 518 }); 519} 520 521function TB_addTweets(widgetId,jsonData,urlInfo) { 522 523 var tweets = jsonData, 524 isNewTweet = false, 525 tb_tweet, 526 tweetExists; 527 528 if (typeof(jsonData.statuses) != 'undefined') { 529 tweets = jsonData.statuses; 530 } 531 532//console.log(jsonData); 533 534 jQuery.each(tweets,function(i,tweetJson) { 535 536 // account for cached tweets returning div ID and json 537 if (typeof(tweetJson.tweet_json) != 'undefined'){ 538 tb_tweet = new TB_tweet(tweetJson.tweet_json); 539 } 540 else { 541 tb_tweet = new TB_tweet(tweetJson); 542 } 543//console.log(i + ": " + tweetJson ); 544 545 tb_tweet.modifier = urlInfo.modifier; 546 547 // make sure it's OK to show 548 if (!tb_tweet.isOKtoShow()) { 549 return true; 550 } 551 552 tb_tweet.selectRelevantSources(urlInfo.source); 553 554 isNewTweet = false;
555 556 // if this tweet already in the set, skip it 557 tweetExists = false; 558 jQuery('div.tb_tweet').each(function() { 559 if (tb_tweet.isSameId(this.id)) { 560 tweetExists = true; 561 } 562 }); 563 if (tweetExists) { 564 return true; 565 } 566 567 // if this is the first tweet, just add it and set it to be both min and max 568 if (TB_config.widgets[widgetId].tweetsShown == 0) { 569 TB_config.widgets[widgetId].tweetsShown++; 570 TB_config.widgets[widgetId].minTweetId = tb_tweet.divId; 571 TB_config.widgets[widgetId].maxTweetId = tb_tweet.divId; 572 573 // add at the end 574 jQuery('#'+widgetId+'-mc > div.tb_tweetlist').append(tb_tweet.getHTML()); 575 576 isNewTweet = true; 577 } 578 // if tweet older than the oldest 579 else if (tb_tweet.isOlderThan(TB_config.widgets[widgetId].minTweetId)) { 580 // if we are at max already, no need to work through the rest of this set as the rest will be older 581 if (TB_config.widgets[widgetId].tweetsShown >= TB_config.widgets[widgetId].tweetsNum) { 582 return false; 583 } 584 else { 585 TB_config.widgets[widgetId].tweetsShown++; 586 587 // add at the end 588 jQuery('#'+widgetId+'-mc > div.tb_tweetlist').append(tb_tweet.getHTML()); 589 590 // make it the oldest 591 TB_config.widgets[widgetId].minTweetId = tb_tweet.divId; 592 593 // if we have only one tweet then make it the newest as well 594 if (TB_config.widgets[widgetId].tweetsNum == 1) { 595 TB_config.widgets[widgetId].maxTweetId = tb_tweet.divId; 596 } 597 598 isNewTweet = true; 599 } 600 } 601 // if tweet is newer than the newest 602 else if (tb_tweet.isNewerThan(TB_config.widgets[widgetId].maxTweetId)) { 603 // if we are at max already, remove bottom tweet 604 TB_enforceLimit(widgetId); 605 606 // add in the beginning 607 jQuery('#'+widgetId+'-mc > div.tb_tweetlist').prepend(tb_tweet.getHTML()); 608 TB_config.widgets[widgetId].tweetsShown++; 609 610 // make it the newest 611 TB_config.widgets[widgetId].maxTweetId = tb_tweet.divId; 612 613 // if we have only one tweet then make it the oldest as well 614 if (TB_config.widgets[widgetId].tweetsNum == 1) { 615 TB_config.widgets[widgetId].minTweetId = tb_tweet.divId; 616 } 617 618 isNewTweet = true; 619 } 620 // if tweet is in the middle 621 else { 622 // if we are at max already, remove bottom tweet 623 TB_enforceLimit(widgetId); 624 625 // traverse currently shown tweets and insert in the appropriate spot 626 var prevTweetId = TB_config.widgets[widgetId].maxTweetId, 627 nextTweetId; 628 jQuery('#'+widgetId+'-mc > div.tb_tweetlist > div.tb_tweet').each(function(i,nextTweet){ 629 nextTweetId = nextTweet.id; 630 if (tb_tweet.isOlderThan(prevTweetId) && tb_tweet.isNewerThan(nextTweetId)) { 631 jQuery('#'+prevTweetId).after(tb_tweet.getHTML()); 632 TB_config.widgets[widgetId].tweetsShown++; 633 return false; 634 } 635 prevTweetId = nextTweetId; 636 }); 637 638 // if got to here and tweet still not there, make it the last 639 if (jQuery('#'+tb_tweet.divId).length <= 0) { 640 jQuery('#'+TB_config.widgets[widgetId].minTweetId).after(tb_tweet.getHTML()); 641 TB_config.widgets[widgetId].minTweetId = tb_tweet.divId; 642 // if we have only one tweet then make it the newest as well 643 if (TB_config.widgets[widgetId].tweetsNum == 1) { 644 TB_config.widgets[widgetId].maxTweetId = tb_tweet.divId; 645 } 646 TB_config.widgets[widgetId].tweetsShown++; 647 } 648 649 isNewTweet = true; 650 } 651 652 // wire mouseover action items 653 TB_wireMouseOver(tb_tweet.divId); 654 655 // if filtering out same tweets - add text to seen tweets 656 if (TB_config.filter_hide_same_text) { 657 TB_seenTweets.push(tb_tweet.jsonCode.text); 658 } 659 }); 660 661 TB_config.widgets[widgetId].urlsDone++; 662 663 // wire target="_blank" on links 664 jQuery('a.tb_photo, .tb_author a, .tb_msg a, .tweet-tools a, .tb_infolink').click(function(){ 665 this.target = "_blank"; 666 }); 667 668 TB_checkComplete(widgetId); 669} 670 671function TB_wireMouseOver(tweetId) { 672 // wire mouseover action items 673 if(TB_config[TB_mode + '_show_reply_link'] || TB_config[TB_mode + '_show_follow_link']) { 674 jQuery('#'+tweetId).hover( 675 function () { 676 jQuery(this).find("div:last").slideDown() 677 }, 678 function () { 679 jQuery(this).find("div:last").slideUp(); 680 } 681 ); 682 } 683} 684 685function TB_enforceLimit(widgetId) { 686 687 if (TB_config.widgets[widgetId].tweetsShown == TB_config.widgets[widgetId].tweetsNum) { 688 var lastTweet = jQuery('#' + TB_config.widgets[widgetId].minTweetId), 689 nextToLastTweet = lastTweet.prev('div.tb_tweet'); 690
691 // remove last tweet 692 lastTweet.remove(); 693 TB_config.widgets[widgetId].tweetsShown--; 694 695 // if no tweets left, reset min and max and finish 696 if (TB_config.widgets[widgetId].tweetsShown == 0) { 697 TB_config.widgets[widgetId].minTweetId = 0; 698 TB_config.widgets[widgetId].maxTweetId = 0; 699 return; 700 } 701 else { 702 // make next to last to be last now 703 if(nextToLastTweet.length > 0) { 704 TB_config.widgets[widgetId].minTweetId = nextToLastTweet.attr('id'); 705 } 706 } 707 } 708} 709 710function TB_showLoader(widgetId) { 711 // if there are not tweets, show loading message 712 if(TB_config.widgets[widgetId].tweetsShown == 0) { 713 TB_showMessage(widgetId,'loading',TB_labels.loading_msg,true); 714 } 715 // show animated icon 716 jQuery('#' + widgetId + '-mc > div.tb_header > div.tb_tools > a.tb_refreshlink > img').attr('src',TB_pluginPath + '/img/ajax-refresh.gif'); 717 jQuery('#' + widgetId + '-mc > div.tb_header > div.tb_tools > a.tb_refreshlink').addClass('loading'); 718} 719 720function TB_hideLoader(widgetId) { 721 // hide loading message 722 TB_hideMessage(widgetId,'loading'); 723 724 // show static icon 725 jQuery('#' + widgetId + '-mc > div.tb_header > div.tb_tools > a.tb_refreshlink > img').attr('src',TB_pluginPath + '/img/ajax-refresh-icon.gif'); 726 jQuery('#' + widgetId + '-mc > div.tb_header > div.tb_tools > a.tb_refreshlink').removeClass('loading'); 727} 728 729function TB_showMessage(widgetId, messageId, msg, keepOnScreen){ 730 731 // if no widgetId is given -> show message in all widgets and ignore keepOnScreen 732 if(!widgetId) { 733 jQuery('div.tb_tweetlist').before('<div id="msg_' + messageId + '" class="tb_msg" style="display:none;">' + msg + '</div>'); 734 return; 735 } 736 737 // if it doesn't exist 738 if (!jQuery('#' + widgetId + '-mc').children('#msg_' + messageId).length) { 739 jQuery('#' + widgetId + '-mc').children('div.tb_tweetlist').before('<div id="msg_' + messageId + '" class="tb_msg" style="display:none;">' + msg + '</div>'); 740 jQuery('#' + widgetId + '-mc').children('#msg_' + messageId).slideDown(); 741 if (!keepOnScreen) { 742 setTimeout('TB_hideMessage("' + widgetId + '","' + messageId + '")', 8000); 743 } 744 } 745 // else if it's hidden 746 else if (jQuery('#' + widgetId + '-mc').children('#msg_' + messageId).is(':hidden')) { 747 jQuery('#' + widgetId + '-mc').children('#msg_' + messageId).slideDown(); 748 } 749} 750 751function TB_hideAllMessages() { 752 jQuery('div.tb_msg').slideUp(1000,function(){jQuery('div.tb_msg').remove()}); 753} 754 755function TB_hideMessage(widgetId,messageId) { 756 jQuery('#' + widgetId + '-mc').children('#msg_' + messageId).slideUp(1000,function(){jQuery('#' + widgetId + '-mc').children('#msg_' + messageId).remove()}); 757} 758 759// TB Class for tweets 760function TB_tweet(tweetJson) { 761 762 // raw JSON for the tweet 763 this.jsonCode = tweetJson; 764 765 // tweetDate property 766 this.tweetDate = TB_str2date(tweetJson.created_at); 767 768 // author screen name property 769 if (typeof(tweetJson.from_user) != 'undefined') { 770 this.screenName = tweetJson.from_user; 771 } 772 else if (typeof(tweetJson.user.screen_name) != 'undefined') { 773 this.screenName = tweetJson.user.screen_name; 774 } 775 776 // id of the tweet 777 this.id = tweetJson.id_str; 778 779 // sources relevant to this tweet 780 this.sources = ''; 781 782 // process sources and select the ones relevant to this tweet 783 this.selectRelevantSources = function(urlSources) { 784 var allSources = new Array(), 785 sourceScreenName = '@'+this.screenName, 786 jsonCode = this.jsonCode; 787 jQuery.each(urlSources.split(','),function(i,src) { 788 if (sourceScreenName == src || jsonCode.text.indexOf(src) > 0){ 789 allSources.push(src); 790 } 791 }); 792 // set property with relevant sources 793 if (allSources.length > 0) { 794 this.sources = allSources.join(','); 795 } 796 // just in case to make sure we don't have empty source 797 else { 798 this.sources = urlSources; 799 } 800 } 801 802 /* setup template 803 * the following placeholders will be used 804 * {0} - tweet div id 805 * {1} - screen name 806 * {2} - profile image url 807 * {3} - tweet message 808 * {4} - tweet id as string 809 * {5} - date 810 * {6} - source as link 811 * {7} - author name 812 */ 813 // if user supplied a custom format - use it 814 var template; 815 if (TB_config.custom_template) { 816 template = TB_config.custom_template; 817 } 818 // if not, use default format 819 else { 820 template = '<div id="{0}" class="tb_tweet">'; 821 template += '<a class="tb_photo" rel="nofollow" href="https://twitter.com/{1}"><img src="{2}" alt="{1}"></a>'; 822 template += '<span class="tb_author">{7}<br /><a rel="nofollow" href="https://twitter.com/{1}">{1}</a>: </span> '; 823 template += '<span class="tb_msg">{3}</span><br />'; 824 // start tweet footer with info 825 if (!TB_config.general_seo_tweets_googleoff && TB_config.general_seo_footer_googleoff) { 826 template += '<!--googleoff: index-->'; 827 } 828 template += ' <span class="tb_tweet-info">'; 829 // show timestamp 830 template += '<a rel="nofollow" href="http://twitter.com/{1}/statuses/{4}">{5}</a>'; 831 // show source if requested 832 if (TB_config['widget_show_source'] && tweetJson.source) { 833 template += ' ' + TB_labels.from + ' {6}'; 834 } 835 // end tweet footer 836 template += '</span>'; 837 if (!TB_config.general_seo_tweets_googleoff && TB_config.general_seo_footer_googleoff) { 838 template += '<!--googleon: index-->'; 839 } 840 // add tweet tools 841 if (TB_config.widget_show_follow_link || TB_config.widget_show_reply_link) { 842 template += '<div class="tweet-tools" style="display:none;">'; 843 if (TB_config.widget_show_reply_link) { 844 template += '<a rel="nofollow" href="http://twitter.com/home?status=@{1}%20&in_reply_to_status_id={4}&in_reply_to={1}">' + TB_labels.reply + '</a>'; 845 } 846 if (TB_config.widget_show_follow_link && TB_config.widget_show_reply_link) { 847 template += ' | '; 848 } 849 if (TB_config.widget_show_follow_link) { 850 template += '<a rel="nofollow" href="http://twitter.com/{1}">' + TB_labels.follow + ' {1}</a>'; 851 } 852 template += '</div>'; 853 } 854 // end tweet 855 template += "</div>\n"; 856 } 857 858 // outputs current template 859 getTemplate = function() { 860 return template; 861 } 862 863 // creates unique div ID for this tweet
864 getDivId = function(tweetDate,screenName,strId) { 865 return 't-' + tweetDate.getTime() + '-' + screenName + '-' + strId; 866 } 867 868 // div id of the tweet 869 this.divId = getDivId(this.tweetDate,this.screenName,this.id); 870 871 // makes HTML for each tweet 872 this.getHTML = function() { 873 /* the following placeholders will be used in the template 874 * {0} - tweet div id 875 * {1} - screen name 876 * {2} - profile image url 877 * {3} - tweet message 878 * {4} - tweet id as string 879 * {5} - date 880 * {6} - source as link 881 */ 882 var textHtml = this.jsonCode.text, 883 nameHtml, 884 imageUrl = '', 885 dateHtml = '', 886 sourceHtml = ''; 887 888 if (typeof(TB_sourceNames[this.screenName.toLowerCase()]) != 'undefined') { 889 nameHtml = TB_sourceNames[this.screenName.toLowerCase()]; 890 } 891 else { 892 nameHtml = this.screenName; 893 } 894 895 // link URLs 896 textHtml = textHtml.replace(/(https?:\/\/\S+)/gi, '<a rel="nofollow" href="$1">$1</a>'); 897 // link screen names 898 textHtml = textHtml.replace(/\@([\w]+)/gi,'<a rel="nofollow" href="http://twitter.com/$1">@$1</a>'); 899 // link hash tags 900 textHtml = textHtml.replace(/\#(\S+)/gi,'<a rel="nofollow" href="http://twitter.com/search?q=%23$1">#$1</a>'); 901 902 if (tweetJson.profile_image_url) { 903 imageUrl = tweetJson.profile_image_url; 904 } 905 else { 906 imageUrl = tweetJson.user.profile_image_url; 907 } 908 // make date 909 if (TB_config.general_timestamp_format) { 910 if (typeof(jQuery.PHPDate) != 'undefined') { 911 dateHtml += jQuery.PHPDate(TB_config.general_timestamp_format,this.tweetDate); 912 } 913 else if (typeof(jQnc.PHPDate) != 'undefined') { 914 dateHtml += jQnc.PHPDate(TB_config.general_timestamp_format,this.tweetDate); 915 } 916 } 917 else { 918 dateHtml += TB_verbalTime(this.tweetDate); 919 } 920 // if source is url encoded -> decode 921 if (tweetJson.source.indexOf('<') >= 0) { 922 sourceHtml += jQuery('<textarea/>').html(tweetJson.source).val(); 923 } 924 // else use as is 925 else { 926 sourceHtml += tweetJson.source; 927 } 928 929 // return formatted string 930 return template.format(this.divId,'@' + nameHtml,imageUrl,textHtml,tweetJson.id_str,dateHtml,sourceHtml,this.jsonCode.user.name); 931 } 932 933 this.isNewerThan = function(TB_tweetId) { 934 var tweetIdParts, 935 ourTimeStamp, 936 otherTweetTimeStamp, 937 otherTweetScreenName; 938 939 // if other tweet's ID is not defined - assume we are newer 940 if (typeof(TB_tweetId) == 'undefined') { 941 return true; 942 } 943 // if it's some weird format - assume we are newer 944 else if (TB_tweetId.indexOf('-') <= 0) { 945 return true; 946 } 947 // else, prepare for real comparisons 948 else { 949 tweetIdParts = TB_tweetId.split('-'); 950 otherTweetTimeStamp = tweetIdParts[1]; 951 otherTweetScreenName = tweetIdParts[2]; 952 ourTimeStamp = this.tweetDate.getTime(); 953 } 954 955 // if our timestamp is later 956 if (ourTimeStamp > otherTweetTimeStamp) { 957 return true; 958 } 959 // if timestamp is older 960 else if (ourTimeStamp < otherTweetTimeStamp) { 961 return false; 962 } 963 // if timestamps are same but users are different 964 else if (this.screenName != otherTweetScreenName){ 965 return true; 966 } 967 // if timestamps are same and users same 968 else { 969 return false; 970 } 971 } 972 973 this.isOlderThan = function(TB_tweetId) { 974 return !this.isNewerThan(TB_tweetId); 975 } 976 977 978 this.isSameId = function(TB_tweetId) { 979 980 var parts1, parts2; 981 982 // if other tweet's ID is not defined - assume we are not the same 983 if (typeof(TB_tweetId) == 'undefined') { 984 return false; 985 } 986 // if it's some weird format - assume we are not the same 987 else if (TB_tweetId.indexOf('-') <= 0) { 988 return false; 989 } 990 // else, do the real comparisons 991 else { 992 993 parts1 = TB_tweetId.split('-'); 994 parts2 = this.divId.split('-'); 995 996 // if one of the IDs is in old format - compare only the first two parts 997 if (parts1.length == 3 || parts2.length == 3) { 998 return (parts1[1] == parts2[1] && parts1[2] == parts2[2]); 999 } 1000 // for new formats - compare all 3 parts 1001 else { 1002 return (parts1[1] == parts2[1] && parts1[2] == parts2[2] && parts1[3] == parts2[3]); 1003 } 1004 } 1005 } 1006 1007 /* returns true if this tweet doesn't contain any words that are supposed to be filtered out 1008 * and if it's not supposed to be hidden due to other criteria 1009 */ 1010 this.isOKtoShow = function() { 1011 1012 var i; 1013 1014 // if we have a modifier on that source and text doesn't contain it 1015 if (this.modifier) { 1016 if (this.modifier.length > 0 && this.jsonCode.text.indexOf(this.modifier) < 0) { 1017 return false;
1018 } 1019 } 1020 1021 // if we don't show tweets with same content 1022 if (TB_config.filter_hide_same_text) { 1023 if (jQuery.inArray(this.jsonCode.text,TB_seenTweets) > 0) { 1024 return false; 1025 } 1026 } 1027 1028 // if this is a reply 1029 if (this.jsonCode.in_reply_to_user_id || this.jsonCode.in_reply_to_status_id || this.jsonCode.in_reply_to_screen_name) { 1030 // if we don't show replies 1031 if (TB_config.filter_hide_replies) { 1032 return false; 1033 } 1034 } 1035 // else, if it's not a reply but we are showing only replies 1036 else if (TB_config.filter_hide_not_replies) { 1037 return false; 1038 } 1039 1040 // if it's a retweet and we are hiding retweets 1041 if (this.jsonCode.retweeted_status && TB_config.filter_hide_retweets) { 1042 return false; 1043 } 1044 1045 1046 // if there are filtered words and the tweet text matches any of them - skip this tweet 1047 if (typeof(TB_config['filter_bad_strings']) != 'undefined' && TB_config.filter_bad_strings.length > 0) { 1048 badStrings = TB_config.filter_bad_strings.base64_decode().split(','); 1049 for (i = 0; i < badStrings.length; i++) { 1050 if (this.jsonCode.text.indexOf(badStrings[i]) >= 0 || this.screenName.indexOf(badStrings[i]) >= 0) { 1051 return false; 1052 } 1053 } 1054 } 1055 1056 // if throttling is ON and we are at max for this user, skip it 1057 if (TB_config.filter_limit_per_source > 0) { 1058 if (typeof(TB_sourceCounts[this.screenName]) != 'undefined' || TB_sourceCounts[this.screenName] == 0) { 1059 1060 if (TB_sourceCounts[this.screenName] >= TB_config.filter_limit_per_source) { 1061 return false; 1062 } 1063 else { 1064 TB_sourceCounts[this.screenName]++; 1065 } 1066 } 1067 else { 1068 TB_sourceCounts[this.screenName] = 1; 1069 } 1070 } 1071 1072 return true; 1073 } 1074 1075} 1076 1077// initialize 1078TB_addLoadEvent(TB_start); jQuery(document).ready(TB_start);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.