1 /*jslint 2 browser: true, 3 nomen: false, 4 debug: true, 5 forin: true, 6 plusplus: false, 7 regexp: false, 8 sub: true, 9 undef: true, 10 white: false, 11 onevar: false 12 */ 13 var sc, jQuery, Mojo, use_palmhost_proxy; 14 15 /** 16 * @depends ../helpers/string.js 17 * @depends ../helpers/datetime.js 18 * @depends ../helpers/event.js 19 * @depends ../helpers/json.js 20 * @depends ../helpers/sys.js 21 */ 22 23 24 /** 25 * various constant definitions 26 */ 27 /** 28 * @constant 29 */ 30 var SPAZCORE_SECTION_FRIENDS = 'friends'; 31 /** 32 * @constant 33 */ 34 var SPAZCORE_SECTION_HOME = 'home'; 35 /** 36 * @constant 37 */ 38 var SPAZCORE_SECTION_REPLIES = 'replies'; 39 /** 40 * @constant 41 */ 42 var SPAZCORE_SECTION_DMS = 'dms'; 43 /** 44 * @constant 45 */ 46 var SPAZCORE_SECTION_FAVORITES = 'favorites'; 47 /** 48 * @constant 49 */ 50 var SPAZCORE_SECTION_COMBINED = 'combined'; 51 /** 52 * @constant 53 */ 54 var SPAZCORE_SECTION_PUBLIC = 'public'; 55 /** 56 * @constant 57 */ 58 var SPAZCORE_SECTION_SEARCH = 'search'; 59 /** 60 * @constant 61 */ 62 var SPAZCORE_SECTION_USER = 'user-timeline'; 63 /** 64 * @constant 65 */ 66 var SPAZCORE_SECTION_FRIENDLIST = 'friendslist'; 67 /** 68 * @constant 69 */ 70 var SPAZCORE_SECTION_FOLLOWERSLIST = 'followerslist'; 71 /** 72 * @constant 73 */ 74 var SPAZCORE_SECTION_USERLISTS = 'userlists'; 75 76 /** 77 * @constant 78 */ 79 var SPAZCORE_SERVICE_TWITTER = 'twitter'; 80 /** 81 * @constant 82 */ 83 var SPAZCORE_SERVICE_IDENTICA = 'identi.ca'; 84 /** 85 * @constant 86 */ 87 var SPAZCORE_SERVICE_WORDPRESS_TWITTER = 'wordpress-twitter'; 88 /** 89 * @constant 90 */ 91 var SPAZCORE_SERVICE_TUMBLR_TWITTER = 'tumblr-twitter'; 92 /** 93 * @constant 94 */ 95 var SPAZCORE_SERVICE_CUSTOM = 'custom'; 96 /** 97 * @constant 98 */ 99 var SPAZCORE_SERVICEURL_TWITTER = 'https://api.twitter.com/1/'; 100 /** 101 * @constant 102 */ 103 var SPAZCORE_SERVICEURL_IDENTICA = 'https://identi.ca/api/'; 104 /** 105 * @constant 106 */ 107 var SPAZCORE_SERVICEURL_WORDPRESS_TWITTER = 'https://twitter-api.wordpress.com/'; 108 /** 109 * @constant 110 */ 111 var SPAZCORE_SERVICEURL_TUMBLR_TWITTER = 'http://www.tumblr.com/'; 112 113 114 115 /** 116 * A Twitter API library for Javascript 117 * 118 * 119 * jQuery events raised by this library 120 * 121 * <ul> 122 * <li>'spaztwit_ajax_error'</li> 123 * <li>'new_public_timeline_data' (data)</li> 124 * <li>'new_friends_timeline_data' (data)</li> 125 * <li>'error_friends_timeline_data' (data)</li> 126 * <li>'new_replies_timeline_data' (data)</li> 127 * <li>'error_replies_timeline_data' (data)</li> 128 * <li>'new_dms_timeline_data' (data)</li> 129 * <li>'error_dms_timeline_data' (data)</li> 130 * <li>'new_combined_timeline_data' (data)</li> 131 * <li>'error_combined_timeline_data' (data)</li> 132 * <li>'new_favorites_timeline_data' (data)</li> 133 * <li>'error_favorites_timeline_data' (data)</li> 134 * <li>'verify_credentials_succeeded' (data)</li> 135 * <li>'verify_credentials_failed' (data)</li> 136 * <li>'update_succeeded' (data)</li> 137 * <li>'update_failed' (data)</li> 138 * <li>'get_user_succeeded' (data)</li> 139 * <li>'get_user_failed' (data)</li> 140 * <li>'get_one_status_succeeded' (data)</li> 141 * <li>'get_one_status_failed' (data)</li> 142 * <li>'new_search_timeline_data' (data)</li> 143 * <li>'error_search_timeline_data' (data)</li> 144 * <li>'new_trends_data' (data)</li> 145 * <li>'error_trends_data' (data)</li> 146 * <li>'new_saved_searches_data' (data)</li> 147 * <li>'error_saved_searches_data' (data)</li> 148 * <li>'create_saved_search_succeeded' (data)</li> 149 * <li>'create_saved_search_failed' (data)</li> 150 * <li>'destroy_saved_search_succeeded' (data)</li> 151 * <li>'destroy_saved_search_failed' (data)</li> 152 * <li>'create_favorite_succeeded'</li> 153 * <li>'create_favorite_failed'</li> 154 * <li>'destroy_favorite_succeeded'</li> 155 * <li>'destroy_favorite_failed'</li> 156 * <li>'create_friendship_succeeded'</li> 157 * <li>'create_friendship_failed'</li> 158 * <li>'destroy_friendship_succeeded'</li> 159 * <li>'destroy_friendship_failed'</li> 160 * <li>'create_block_succeeded'</li> 161 * <li>'create_block_failed'</li> 162 * <li>'destroy_block_succeeded'</li> 163 * <li>'destroy_block_failed'</li> 164 * <li>'follow_succeeded'</li> 165 * <li>'follow_failed'</li> 166 * <li>'unfollow_succeeded'</li> 167 * <li>'unfollow_failed'</li> 168 * <li>'ratelimit_status_succeeded'</li> 169 * <li>'ratelimit_status_failed'</li> 170 * <li>'destroy_status_succeeded'</li> 171 * <li>'destroy_status_failed'</li> 172 * <li>'destroy_dm_succeeded'</li> 173 * <li>'destroy_dm_failed'</li> 174 * </ul> 175 * 176 * @param {Object} opts various options 177 * @param {Object} [opts.auth] SpazAuth object 178 * @param {String} [opts.event_mode] The event mode to use ('jquery' or 'DOM'). Defaults to 'DOM' 179 * @param {Object} [opts.event_target] the DOM element to target the event on. Defaults to document 180 * @param {Number} [opts.timeout] length of time, in seconds, to timeout 181 * @class SpazTwit 182 * @constructor 183 */ 184 function SpazTwit(opts) { 185 186 this.opts = sch.defaults({ 187 auth: null, 188 username: null, 189 event_mode: 'DOM', 190 event_target: document, 191 timeout: this.DEFAULT_TIMEOUT 192 }, opts); 193 194 195 this.auth = this.opts.auth; 196 197 this.setSource('SpazCore'); 198 199 this.initializeData(); 200 201 this.initializeCombinedTracker(); 202 203 /* 204 Cache for one-shot users and posts. Not sure what we'll do with it yet 205 */ 206 this.cache = { 207 users:{}, 208 posts:{} 209 }; 210 211 this.me = {}; 212 213 214 this.setBaseURL(SPAZCORE_SERVICEURL_TWITTER); 215 216 /** 217 * remap dump calls as appropriate 218 */ 219 if (sc && sc.helpers && sc.helpers.dump) { 220 window.dump = sc.helpers.dump; 221 } else { // do nothing! 222 var dump = function(input) { 223 return; 224 }; 225 } 226 } 227 228 /** 229 * the default timeout value (60 seconds) 230 */ 231 SpazTwit.prototype.DEFAULT_TIMEOUT = 1000*60; 232 233 234 235 /** 236 * retrieves the last status id retrieved for a given section 237 * @param {string} section use one of the defined constants (ex. SPAZCORE_SECTION_HOME) 238 * @return {integer} the last id retrieved for this section 239 */ 240 SpazTwit.prototype.getLastId = function(section) { 241 return this.data[section].lastid; 242 }; 243 244 /** 245 * sets the last status id retrieved for a given section 246 * @param {string} section use one of the defined constants (ex. SPAZCORE_SECTION_HOME) 247 * @param {integer} id the new last id retrieved for this section 248 */ 249 SpazTwit.prototype.setLastId = function(section, id) { 250 this.data[section].lastid = parseInt(id, 10); 251 }; 252 253 254 SpazTwit.prototype.initializeData = function() { 255 /* 256 this is where we store timeline data and settings for persistence 257 */ 258 this.data = {}; 259 this.data[SPAZCORE_SECTION_HOME] = { 260 'lastid': 1, 261 'items': [], 262 'newitems':[], 263 'max':200, 264 'min_age':5*60 265 }; 266 this.data[SPAZCORE_SECTION_FRIENDS] = { 267 'lastid': 1, 268 'items': [], 269 'newitems':[], 270 'max':200, 271 'min_age':5*60 272 }; 273 this.data[SPAZCORE_SECTION_REPLIES] = { 274 'lastid': 1, 275 'items': [], 276 'newitems':[], 277 'max':50, 278 'min_age':5*60 279 }; 280 this.data[SPAZCORE_SECTION_DMS] = { 281 'lastid': 1, 282 'items': [], 283 'newitems':[], 284 'max':50, 285 'min_age':5*60 286 }; 287 this.data[SPAZCORE_SECTION_FAVORITES] = { 288 'lastid': 1, 289 'items': [], 290 'newitems':[], 291 'max':100, 292 'min_age':5*60 293 }; 294 this.data[SPAZCORE_SECTION_COMBINED] = { 295 'items': [], 296 'newitems':[], 297 'updates' :[], 298 'max':400, 299 'min_age':5*60 300 }; 301 this.data[SPAZCORE_SECTION_FRIENDLIST] = { 302 'items': [], 303 'newitems':[], 304 'max':500, 305 'min_age':5*60 306 }; 307 this.data[SPAZCORE_SECTION_FOLLOWERSLIST] = { 308 'items': [], 309 'newitems':[], 310 'max':500, 311 'min_age':5*60 312 }; 313 this.data[SPAZCORE_SECTION_SEARCH] = { 314 'lastid': 0, // search api prefers 0, will freak out on "1" 315 'items': [], 316 'newitems':[], 317 'lastresultdata':{}, 318 'max':200, 319 'min_age':30 320 }; 321 this.data[SPAZCORE_SECTION_USERLISTS] = { 322 'items': [], 323 'newitems':[], 324 'max':500, 325 'min_age':5*60 326 }; 327 // this.data.byid = {}; 328 }; 329 330 331 /** 332 * resets the combined_finished progress tracker 333 */ 334 SpazTwit.prototype.initializeCombinedTracker = function() { 335 this.combined_finished = {}; 336 this.combined_finished[SPAZCORE_SECTION_HOME] = false; 337 this.combined_finished[SPAZCORE_SECTION_REPLIES] = false; 338 this.combined_finished[SPAZCORE_SECTION_DMS] = false; 339 340 this.combined_errors = []; 341 }; 342 343 /** 344 * Checks to see if the combined timeline is finished 345 * @return {boolean} 346 */ 347 SpazTwit.prototype.combinedTimelineFinished = function() { 348 for (var i in this.combined_finished) { 349 if (!this.combined_finished[i]) { 350 return false; 351 } 352 } 353 return true; 354 }; 355 356 /** 357 * Checks to see if the combined timeline is finished 358 * @return {boolean} 359 */ 360 SpazTwit.prototype.combinedTimelineHasErrors = function() { 361 if (this.combined_errors.length > 0) { 362 return true; 363 } else { 364 return false; 365 } 366 }; 367 368 /** 369 * Checks to see if the combined timeline contains sent updates 370 * @return {boolean} 371 */ 372 SpazTwit.prototype.combinedTimelineHasUpdates = function() { 373 return this.data[SPAZCORE_SECTION_COMBINED].updates.length > 0; 374 }; 375 376 /** 377 * Adds ids of array of statuses to updates 378 */ 379 SpazTwit.prototype.combinedTimelineAddUpdates = function(items) { 380 if (items.id) { 381 items = [items]; 382 } 383 var i; 384 for (i in items) { 385 this.data[SPAZCORE_SECTION_COMBINED].updates.push(items[i].id); 386 } 387 }; 388 389 /** 390 * Removes the update items from combined newitems 391 */ 392 SpazTwit.prototype.combinedNewItemsRemoveUpdates = function() { 393 if (!this.combinedTimelineHasUpdates()) { 394 return; 395 } 396 var data = this.data[SPAZCORE_SECTION_COMBINED], 397 iStr = ':' + data.updates.join(':') + ':', 398 news = data.newitems, 399 keep = [], 400 i; 401 402 for (i in news) { 403 if (!RegExp(':' + news[i].id + ':').test(iStr)) { 404 keep.push(news[i]); 405 } 406 } 407 data.newitems = keep; 408 data.updates = []; 409 }; 410 411 412 /** 413 * sets the base URL 414 * @param {string} newurl 415 */ 416 SpazTwit.prototype.setBaseURL= function(newurl) { 417 418 var lastchar = newurl.charAt(newurl.length -1); 419 if (lastchar !== '/') { 420 newurl = newurl + '/'; 421 } 422 423 this.baseurl = newurl; 424 }; 425 426 427 /** 428 * sets the base URL by the service type 429 * @param {string} service see SPAZCORE_SERVICE_* 430 */ 431 SpazTwit.prototype.setBaseURLByService= function(service) { 432 433 var baseurl = ''; 434 435 switch (service) { 436 case SPAZCORE_SERVICE_TWITTER: 437 baseurl = SPAZCORE_SERVICEURL_TWITTER; 438 break; 439 case SPAZCORE_SERVICE_IDENTICA: 440 baseurl = SPAZCORE_SERVICEURL_IDENTICA; 441 break; 442 case SPAZCORE_SERVICE_WORDPRESS_TWITTER: 443 baseurl = SPAZCORE_SERVICEURL_WORDPRESS_TWITTER; 444 break; 445 case SPAZCORE_SERVICE_TUMBLR_TWITTER: 446 baseurl = SPAZCORE_SERVICEURL_TUMBLR_TWITTER; 447 break; 448 default: 449 baseurl = SPAZCORE_SERVICEURL_TWITTER; 450 break; 451 } 452 453 this.baseurl = baseurl; 454 }; 455 456 457 SpazTwit.prototype.setCredentials = function(auth_obj) { 458 this.auth = auth_obj; 459 this.username = this.auth.username; 460 }; 461 462 463 /** 464 * set the source string we will pass on updates 465 * 466 * @param {string} new_source 467 */ 468 SpazTwit.prototype.setSource = function(new_source) { 469 this.source = new_source; 470 }; 471 472 473 474 /* 475 * given a key, it returns the URL (baseurl+API method path) 476 * @param {string} key the key for the URL 477 * @param {array|object} urldata data to included in the URL as GET data 478 */ 479 SpazTwit.prototype.getAPIURL = function(key, urldata) { 480 var urls = {}; 481 482 483 484 // Timeline URLs 485 urls.public_timeline = "statuses/public_timeline.json"; 486 urls.friends_timeline = "statuses/friends_timeline.json"; 487 urls.home_timeline = "statuses/home_timeline.json"; 488 urls.user_timeline = "statuses/user_timeline.json"; 489 urls.replies_timeline = "statuses/replies.json"; 490 urls.show = "statuses/show/{{ID}}.json"; 491 urls.favorites = "favorites.json"; 492 urls.user_favorites = "favorites/{{ID}}.json"; // use this to retrieve favs of a user other than yourself 493 urls.dm_timeline = "direct_messages.json"; 494 urls.dm_sent = "direct_messages/sent.json"; 495 urls.friendslist = "statuses/friends.json"; 496 urls.followerslist = "statuses/followers.json"; 497 urls.show_user = "users/show/{{ID}}.json"; 498 urls.featuredlist = "statuses/featured.json"; 499 500 // Action URLs 501 urls.update = "statuses/update.json"; 502 urls.destroy_status = "statuses/destroy/{{ID}}.json"; 503 urls.dm_new = "direct_messages/new.json"; 504 urls.dm_destroy = "direct_messages/destroy/{{ID}}.json"; 505 urls.friendship_create = "friendships/create/{{ID}}.json"; 506 urls.friendship_destroy = "friendships/destroy/{{ID}}.json"; 507 urls.friendship_show = "friendships/show.json"; 508 urls.friendship_incoming = "friendships/incoming.json"; 509 urls.friendship_outgoing = "friendships/outgoing.json"; 510 urls.graph_friends = "friends/ids.json"; 511 urls.graph_followers = "followers/ids.json"; 512 urls.block_create = "blocks/create/{{ID}}.json"; 513 urls.block_destroy = "blocks/destroy/{{ID}}.json"; 514 urls.follow = "notifications/follow/{{ID}}.json"; 515 urls.unfollow = "notifications/leave/{{ID}}.json"; 516 urls.favorites_create = "favorites/create/{{ID}}.json"; 517 urls.favorites_destroy = "favorites/destroy/{{ID}}.json"; 518 urls.saved_searches_create = "saved_searches/create.json"; 519 urls.saved_searches_destroy = "saved_searches/destroy/{{ID}}.json"; 520 urls.verify_credentials = "account/verify_credentials.json"; 521 urls.ratelimit_status = "account/rate_limit_status.json"; 522 urls.update_profile = "account/update_profile.json"; 523 urls.saved_searches = "saved_searches.json"; 524 urls.report_spam = "report_spam.json"; 525 526 // User lists URLs 527 urls.lists = "{{USER}}/lists.json"; 528 urls.lists_list = "{{USER}}/lists/{{SLUG}}.json"; 529 urls.lists_memberships = "{{USER}}/lists/memberships.json"; 530 urls.lists_timeline = "{{USER}}/lists/{{SLUG}}/statuses.json"; 531 urls.lists_members = "{{USER}}/{{SLUG}}/members.json"; 532 urls.lists_check_member = "{{USER}}/{{SLUG}}/members/{{ID}}.json"; 533 urls.lists_subscribers = "{{USER}}/{{SLUG}}/subscribers.json"; 534 urls.lists_check_subscriber = "{{USER}}/{{SLUG}}/subscribers/{{ID}}.json"; 535 urls.lists_subscriptions = "{{USER}}/lists/subscriptions.json"; 536 537 //trends 538 urls.trends = "trends.json"; 539 urls.trends_current = "trends/current.json"; 540 urls.trends_daily = "trends/daily.json"; 541 urls.trends_weekly = "trends/weekly.json"; 542 543 //retweet 544 urls.retweet = "statuses/retweet/{{ID}}.json"; 545 urls.retweets = "statuses/retweets/{{ID}}.json"; 546 urls.retweeted_by_me = "statuses/retweeted_by_me.json"; 547 urls.retweeted_to_me = "statuses/retweeted_to_me.json"; 548 urls.retweets_of_me = "statuses/retweets_of_me.json"; 549 550 // search 551 if (this.baseurl === SPAZCORE_SERVICEURL_TWITTER) { 552 urls.search = "http://search.twitter.com/search.json"; 553 } else { 554 urls.search = "search.json"; 555 } 556 557 // misc 558 urls.test = "help/test.json"; 559 urls.downtime_schedule = "help/downtime_schedule.json"; 560 561 562 if (urls[key].indexOf('{{ID}}') > -1) { 563 if (typeof(urldata) === 'string') { 564 urls[key] = urls[key].replace('{{ID}}', urldata); 565 } else if (urldata && typeof(urldata) === 'object') { 566 urls[key] = urls[key].replace('{{ID}}', urldata.id); 567 } 568 569 } 570 571 // Token replacement for user lists 572 if (urls[key].indexOf('{{USER}}') > - 1) { 573 if (urldata && typeof(urldata) === 'object') { 574 urls[key] = urls[key].replace('{{USER}}', urldata.user); 575 } 576 } 577 578 if (urls[key].indexOf('{{SLUG}}') > -1) { 579 if (urldata && typeof(urldata) === 'object') { 580 urls[key] = urls[key].replace('{{SLUG}}', urldata.slug); 581 } 582 } 583 584 if (urls[key]) { 585 586 if (urldata && typeof urldata !== "string") { 587 urldata = '?'+jQuery.param(urldata); 588 } else { 589 urldata = ''; 590 } 591 592 if (this.baseurl === SPAZCORE_SERVICEURL_TWITTER && (key === 'search')) { 593 return this._postProcessURL(urls[key] + urldata); 594 } else { 595 return this._postProcessURL(this.baseurl + urls[key] + urldata); 596 } 597 598 } else { 599 return false; 600 } 601 602 603 }; 604 605 606 607 608 /* 609 * Verify authentication credentials. 610 */ 611 SpazTwit.prototype.verifyCredentials = function(onSuccess, onFailure) { 612 var url = this.getAPIURL('verify_credentials'); 613 614 var opts = { 615 'url':url, 616 'process_callback': this._processAuthenticatedUser, 617 'success_event_type':'verify_credentials_succeeded', 618 'failure_event_type':'verify_credentials_failed', 619 'success_callback':onSuccess, 620 'failure_callback':onFailure, 621 'method':'GET' 622 }; 623 624 /* 625 Perform a request and get true or false back 626 */ 627 var xhr = this._callMethod(opts); 628 629 }; 630 631 /** 632 * This takes data retrieved from the verifyCredentials method and stores it 633 * in this.me. it then fires off the event specified in finished_event 634 * 635 * @param {object} data the data returned by a successful call to the verifyCredentials API method 636 * @param {string} finished_event the type of event to fire 637 * @private 638 */ 639 SpazTwit.prototype._processAuthenticatedUser = function(data, opts) { 640 this.me = data; 641 this.initializeData(); 642 643 if (opts.success_callback) { 644 opts.success_callback(this.me); 645 } 646 this.triggerEvent(opts.success_event_type, this.me); 647 648 }; 649 650 651 /** 652 * Initiates retrieval of the public timeline. 653 */ 654 SpazTwit.prototype.getPublicTimeline = function(onSuccess, onFailure) { 655 var url = this.getAPIURL('public_timeline'); 656 657 var xhr = this._getTimeline({ 658 'url':url, 659 'success_callback':onSuccess, 660 'failure_callback':onFailure, 661 'success_event_type': 'new_public_timeline_data' 662 }); 663 }; 664 665 666 /** 667 * Initiates retrieval of the home timeline (all the people you are following) 668 * 669 * @param {integer} since_id default is 1 670 * @param {integer} count default is 200 671 * @param {integer} page default is null (ignored if null) 672 */ 673 SpazTwit.prototype.getHomeTimeline = function(since_id, count, page, processing_opts, onSuccess, onFailure) { 674 675 if (!page) { page = null;} 676 if (!count) { count = 50;} 677 if (!since_id) { 678 if (this.data[SPAZCORE_SECTION_HOME].lastid && this.data[SPAZCORE_SECTION_HOME].lastid > 1) { 679 since_id = this.data[SPAZCORE_SECTION_HOME].lastid; 680 } else { 681 since_id = 1; 682 } 683 } 684 685 if (!processing_opts) { 686 processing_opts = {}; 687 } 688 689 if (processing_opts.combined) { 690 processing_opts.section = SPAZCORE_SECTION_HOME; 691 } 692 693 var data = {}; 694 if (since_id < -1) { 695 data['max_id'] = Math.abs(since_id); 696 } else { 697 data['since_id'] = since_id; 698 } 699 data['count'] = count; 700 if (page) { 701 data['page'] = page; 702 } 703 704 705 var url = this.getAPIURL('home_timeline', data); 706 this._getTimeline({ 707 'url':url, 708 'process_callback' : this._processHomeTimeline, 709 'success_callback':onSuccess, 710 'failure_callback':onFailure, 711 'success_event_type': 'new_home_timeline_data', 712 'failure_event_type': 'error_home_timeline_data', 713 'processing_opts':processing_opts 714 }); 715 }; 716 717 /** 718 * @private 719 */ 720 SpazTwit.prototype._processHomeTimeline = function(ret_items, opts, processing_opts) { 721 sc.helpers.dump('Processing '+ret_items.length+' items returned from home method'); 722 this._processTimeline(SPAZCORE_SECTION_HOME, ret_items, opts, processing_opts); 723 }; 724 725 726 727 /** 728 * Initiates retrieval of the friends timeline (all the people you are following) 729 * 730 * @param {integer} since_id default is 1 731 * @param {integer} count default is 200 732 * @param {integer} page default is null (ignored if null) 733 */ 734 SpazTwit.prototype.getFriendsTimeline = function(since_id, count, page, processing_opts, onSuccess, onFailure) { 735 736 if (!page) { page = null;} 737 if (!count) { count = 50;} 738 if (!since_id) { 739 if (this.data[SPAZCORE_SECTION_FRIENDS].lastid && this.data[SPAZCORE_SECTION_FRIENDS].lastid > 1) { 740 since_id = this.data[SPAZCORE_SECTION_FRIENDS].lastid; 741 } else { 742 since_id = 1; 743 } 744 } 745 746 if (!processing_opts) { 747 processing_opts = {}; 748 } 749 750 if (processing_opts.combined) { 751 processing_opts.section = SPAZCORE_SECTION_FRIENDS; 752 } 753 754 var data = {}; 755 data['since_id'] = since_id; 756 data['count'] = count; 757 if (page) { 758 data['page'] = page; 759 } 760 761 762 var url = this.getAPIURL('friends_timeline', data); 763 this._getTimeline({ 764 'url':url, 765 'process_callback' : this._processFriendsTimeline, 766 'success_callback':onSuccess, 767 'failure_callback':onFailure, 768 'success_event_type': 'new_friends_timeline_data', 769 'failure_event_type': 'error_friends_timeline_data', 770 'processing_opts':processing_opts 771 }); 772 }; 773 774 /** 775 * @private 776 */ 777 SpazTwit.prototype._processFriendsTimeline = function(ret_items, opts, processing_opts) { 778 sc.helpers.dump('Processing '+ret_items.length+' items returned from friends method'); 779 this._processTimeline(SPAZCORE_SECTION_FRIENDS, ret_items, opts, processing_opts); 780 }; 781 782 783 /** 784 * 785 */ 786 SpazTwit.prototype.getReplies = function(since_id, count, page, processing_opts, onSuccess, onFailure) { 787 if (!page) { page = null;} 788 if (!count) { count = null;} 789 if (!since_id) { 790 if (this.data[SPAZCORE_SECTION_REPLIES].lastid && this.data[SPAZCORE_SECTION_REPLIES].lastid > 1) { 791 since_id = this.data[SPAZCORE_SECTION_REPLIES].lastid; 792 } else { 793 since_id = 1; 794 } 795 } 796 797 if (!processing_opts) { 798 processing_opts = {}; 799 } 800 801 if (processing_opts.combined) { 802 processing_opts.section = SPAZCORE_SECTION_REPLIES; 803 } 804 805 806 var data = {}; 807 if (since_id < -1) { 808 data['max_id'] = Math.abs(since_id); 809 } else { 810 data['since_id'] = since_id; 811 } 812 if (page) { 813 data['page'] = page; 814 } 815 if (count) { 816 data['count'] = count; 817 } 818 819 var url = this.getAPIURL('replies_timeline', data); 820 this._getTimeline({ 821 'url':url, 822 'process_callback' : this._processRepliesTimeline, 823 'success_callback':onSuccess, 824 'failure_callback':onFailure, 825 'success_event_type': 'new_replies_timeline_data', 826 'failure_event_type': 'error_replies_timeline_data', 827 'processing_opts':processing_opts 828 }); 829 830 }; 831 832 833 /** 834 * @private 835 */ 836 SpazTwit.prototype._processRepliesTimeline = function(ret_items, opts, processing_opts) { 837 sc.helpers.dump('Processing '+ret_items.length+' items returned from replies method'); 838 this._processTimeline(SPAZCORE_SECTION_REPLIES, ret_items, opts, processing_opts); 839 }; 840 841 /** 842 * 843 */ 844 SpazTwit.prototype.getDirectMessages = function(since_id, count, page, processing_opts, onSuccess, onFailure) { 845 if (!page) { page = null;} 846 if (!count) { count = null;} 847 if (!since_id) { 848 if (this.data[SPAZCORE_SECTION_DMS].lastid && this.data[SPAZCORE_SECTION_DMS].lastid > 1) { 849 since_id = this.data[SPAZCORE_SECTION_DMS].lastid; 850 } else { 851 since_id = 1; 852 } 853 } 854 855 if (!processing_opts) { 856 processing_opts = {}; 857 } 858 859 if (processing_opts.combined) { 860 processing_opts.section = SPAZCORE_SECTION_DMS; 861 } 862 863 var data = {}; 864 if (since_id < -1) { 865 data['max_id'] = Math.abs(since_id); 866 } else { 867 data['since_id'] = since_id; 868 } 869 if (page) { 870 data['page'] = page; 871 } 872 if (count) { 873 data['count'] = count; 874 } 875 876 var url = this.getAPIURL('dm_timeline', data); 877 this._getTimeline({ 878 'url':url, 879 'process_callback' : this._processDMTimeline, 880 'success_callback':onSuccess, 881 'failure_callback':onFailure, 882 'success_event_type': 'new_dms_timeline_data', 883 'failure_event_type': 'error_dms_timeline_data', 884 'processing_opts':processing_opts 885 }); 886 887 }; 888 889 /** 890 * @private 891 */ 892 SpazTwit.prototype._processDMTimeline = function(ret_items, opts, processing_opts) { 893 sc.helpers.dump('Processing '+ret_items.length+' items returned from DM method'); 894 this._processTimeline(SPAZCORE_SECTION_DMS, ret_items, opts, processing_opts); 895 }; 896 897 /** 898 * 899 */ 900 SpazTwit.prototype.getFavorites = function(page, processing_opts, onSuccess, onFailure) { 901 if (!page) { page = null;} 902 if (!processing_opts) { 903 processing_opts = {}; 904 } 905 906 var data = {}; 907 if (page) { 908 data['page'] = page; 909 } 910 911 var url = this.getAPIURL('favorites', data); 912 913 this._getTimeline({ 914 'url':url, 915 'process_callback' : this._processFavoritesTimeline, 916 'success_callback':onSuccess, 917 'failure_callback':onFailure, 918 'success_event_type': 'new_favorites_timeline_data', 919 'failure_event_type': 'error_favorites_timeline_data', 920 'processing_opts':processing_opts 921 }); 922 923 }; 924 /** 925 * @private 926 */ 927 SpazTwit.prototype._processFavoritesTimeline = function(ret_items, opts, processing_opts) { 928 this._processTimeline(SPAZCORE_SECTION_FAVORITES, ret_items, opts, processing_opts); 929 }; 930 931 932 933 SpazTwit.prototype.getSent = function(since_id, count, page, onSuccess, onFailure) {}; // auth user's sent statuses 934 SpazTwit.prototype.getSentDirectMessages = function(since_id, page, onSuccess, onFailure) {}; 935 936 SpazTwit.prototype.getUserTimeline = function(id, count, page, onSuccess, onFailure) { 937 938 var opts = sch.defaults({ 939 'id': id, 940 'since_id': null, 941 'count': count || 10, 942 'page': page || null, 943 'onSuccess': onSuccess, 944 'onFailure': onFailure 945 }, id); 946 947 if (!opts.id || 'object' === typeof opts.id) { 948 return; 949 } 950 951 var data = {}; 952 data['id'] = opts.id; 953 data['count'] = opts.count; 954 if (opts.since_id) { 955 if (opts.since_id < -1) { 956 data['max_id'] = Math.abs(opts.since_id); 957 } else { 958 data['since_id'] = opts.since_id; 959 } 960 } 961 if (opts.page) { 962 data['page'] = opts.page; 963 } 964 965 966 var url = this.getAPIURL('user_timeline', data); 967 968 this._getTimeline({ 969 'url':url, 970 'process_callback' : this._processUserTimeline, 971 'success_callback':opts.onSuccess, 972 'failure_callback':opts.onFailure, 973 'success_event_type': 'new_user_timeline_data', 974 'failure_event_type': 'error_user_timeline_data' 975 }); 976 }; // given user's sent statuses 977 978 979 /** 980 * @private 981 */ 982 SpazTwit.prototype._processUserTimeline = function(ret_items, opts, processing_opts) { 983 this._processTimeline(SPAZCORE_SECTION_USER, ret_items, opts, processing_opts); 984 }; 985 986 987 988 /** 989 * this retrieves three different timelines. the event "new_combined_timeline_data" 990 * does not fire until ALL async ajax calls are made 991 * 992 * 993 */ 994 SpazTwit.prototype.getCombinedTimeline = function(com_opts, onSuccess, onFailure) { 995 var home_count, friends_count, replies_count, dm_count, home_since, friends_since, dm_since, replies_since = null; 996 997 var opts = { 998 'combined':true 999 }; 1000 1001 if (com_opts) { 1002 if (com_opts.friends_count) { 1003 friends_count = com_opts.friends_count; 1004 } 1005 if (com_opts.home_count) { 1006 home_count = com_opts.home_count; 1007 } 1008 if (com_opts.replies_count) { 1009 replies_count = com_opts.replies_count; // this is not used yet 1010 } 1011 if (com_opts.dm_count) { 1012 dm_count = com_opts.dm_count; // this is not used yet 1013 } 1014 if (com_opts.home_since) { 1015 home_since = com_opts.home_since; 1016 } 1017 if (com_opts.friends_since) { 1018 friends_since = com_opts.friend_since; 1019 } 1020 if (com_opts.replies_since) { 1021 replies_since = com_opts.replies_since; 1022 } 1023 if (com_opts.dm_since) { 1024 dm_since = com_opts.dm_since; 1025 } 1026 1027 /* 1028 we might still only pass in friends_* opts, so we translate those to home_* 1029 */ 1030 if (!home_count) { home_count = friends_count; } 1031 if (!home_since) { home_since = friends_since; } 1032 1033 if (com_opts.force) { 1034 opts.force = true; 1035 } 1036 } 1037 1038 this.getHomeTimeline(home_since, home_count, null, opts, onSuccess, onFailure); 1039 this.getReplies(replies_since, replies_count, null, opts, onSuccess, onFailure); 1040 this.getDirectMessages(dm_since, dm_count, null, opts, onSuccess, onFailure); 1041 }; 1042 1043 1044 1045 SpazTwit.prototype.search = function(query, since_id, results_per_page, page, lang, geocode, onSuccess, onFailure) { 1046 if (!page) { page = 1;} 1047 // if (!since_id) { 1048 // if (this.data[SPAZCORE_SECTION_SEARCH].lastid && this.data[SPAZCORE_SECTION_SEARCH].lastid > 1) { 1049 // since_id = this.data[SPAZCORE_SECTION_SEARCH].lastid; 1050 // } else { 1051 // since_id = 1; 1052 // } 1053 // } 1054 if (!results_per_page) { 1055 results_per_page = 50; 1056 } 1057 1058 1059 var data = {}; 1060 data['q'] = query; 1061 data['rpp'] = results_per_page; 1062 // data['since_id'] = since_id; 1063 data['page'] = page; 1064 if (lang) { 1065 data['lang'] = lang; 1066 } 1067 if (geocode) { 1068 data['geocode'] = geocode; 1069 } 1070 1071 var url = this.getAPIURL('search', data); 1072 this._getTimeline({ 1073 'url':url, 1074 'process_callback' : this._processSearchTimeline, 1075 'success_callback':onSuccess, 1076 'failure_callback':onFailure, 1077 'success_event_type': 'new_search_timeline_data', 1078 'failure_event_type': 'error_search_timeline_data' 1079 }); 1080 1081 }; 1082 1083 /** 1084 * @private 1085 */ 1086 SpazTwit.prototype._processSearchTimeline = function(search_result, opts, processing_opts) { 1087 /* 1088 Search is different enough that we need to break it out and 1089 write a custom alternative to _processTimeline 1090 */ 1091 if (!processing_opts) { processing_opts = {}; } 1092 1093 /* 1094 reset .newitems data properties 1095 */ 1096 this.data[SPAZCORE_SECTION_SEARCH].newitems = []; 1097 1098 /* 1099 put these results in the lastresultdata property 1100 */ 1101 this.data[SPAZCORE_SECTION_SEARCH].lastresultdata = search_result; 1102 1103 /* 1104 grab the array of items 1105 */ 1106 var ret_items = search_result.results; 1107 1108 if (ret_items.length > 0){ 1109 /* 1110 we process each item, adding some attributes and generally making it cool 1111 */ 1112 for (var k=0; k<ret_items.length; k++) { 1113 ret_items[k] = this._processSearchItem(ret_items[k], SPAZCORE_SECTION_SEARCH); 1114 } 1115 1116 /* 1117 sort items 1118 */ 1119 ret_items.sort(this._sortItemsAscending); 1120 1121 /* 1122 set lastid 1123 */ 1124 var lastid = ret_items[ret_items.length-1].id; 1125 this.data[SPAZCORE_SECTION_SEARCH].lastid = lastid; 1126 sc.helpers.dump('this.data['+SPAZCORE_SECTION_SEARCH+'].lastid:'+this.data[SPAZCORE_SECTION_SEARCH].lastid); 1127 1128 /* 1129 add new items to data.newitems array 1130 */ 1131 this.data[SPAZCORE_SECTION_SEARCH].newitems = ret_items; 1132 1133 /* 1134 concat new items onto data.items array 1135 */ 1136 this.data[SPAZCORE_SECTION_SEARCH].items = this.data[SPAZCORE_SECTION_SEARCH].items.concat(this.data[SPAZCORE_SECTION_SEARCH].newitems); 1137 1138 this.data[SPAZCORE_SECTION_SEARCH].items = this.removeDuplicates(this.data[SPAZCORE_SECTION_SEARCH].items); 1139 sch.debug('NOT removing extras from search -- we don\'t do that anymore'); 1140 // this.data[SPAZCORE_SECTION_SEARCH].items = this.removeExtraElements(this.data[SPAZCORE_SECTION_SEARCH].items, this.data[SPAZCORE_SECTION_SEARCH].max); 1141 1142 1143 var search_info = { 1144 'since_id' : search_result.since_id, 1145 'max_id' : search_result.max_id, 1146 'refresh_url' : search_result.refresh_url, 1147 'results_per_page' : search_result.results_per_page, 1148 'next_page' : search_result.next_page, 1149 'completed_in' : search_result.completed_in, 1150 'page' : search_result.page, 1151 'query' : search_result.query 1152 }; 1153 1154 if (opts.success_callback) { 1155 opts.success_callback(this.data[SPAZCORE_SECTION_SEARCH].newitems, search_info); 1156 } 1157 this.triggerEvent(opts.success_event_type, [this.data[SPAZCORE_SECTION_SEARCH].newitems, search_info]); 1158 1159 1160 1161 } else { // no new items, but we should fire off success anyway 1162 1163 if (opts.success_callback) { 1164 opts.success_callback(null, []); 1165 } 1166 this.triggerEvent(opts.success_event_type, []); 1167 } 1168 1169 }; 1170 1171 1172 1173 SpazTwit.prototype._processSearchItem = function(item, section_name) { 1174 1175 item.SC_timeline_from = section_name; 1176 if (this.username) { 1177 item.SC_user_received_by = this.username; 1178 } 1179 // sc.helpers.dump(item); 1180 1181 item.SC_is_search = true; 1182 1183 /* 1184 add unix timestamp .SC_created_at_unixtime for easier date comparison 1185 */ 1186 if (!item.SC_created_at_unixtime) { 1187 item.SC_created_at_unixtime = sc.helpers.httpTimeToInt(item.created_at); 1188 } 1189 1190 /* 1191 add raw text .SC_raw_text for unmodified text 1192 */ 1193 if (!item.SC_text_raw) { 1194 item.SC_text_raw = item.text; 1195 } 1196 1197 /* 1198 add "in_reply_to_screen_name" if it does not exist 1199 */ 1200 if (!item.in_reply_to_screen_name && item.in_reply_to_user_id) { 1201 /** 1202 * @todo get this from the Spaz code 1203 */ 1204 } 1205 1206 /* 1207 add .SC_retrieved_unixtime 1208 */ 1209 if (!item.SC_retrieved_unixtime) { 1210 item.SC_retrieved_unixtime = sc.helpers.getTimeAsInt(); 1211 } 1212 1213 /* 1214 normalize so we have as much user data in this object as possible 1215 */ 1216 item.user = { 1217 'profile_image_url':item.profile_image_url, 1218 'screen_name':item.from_user, 1219 'id':item.from_user_id 1220 }; 1221 1222 /* 1223 The source info here is encoded differently 1224 */ 1225 item.source = sc.helpers.fromHTMLSpecialChars(item.source); 1226 1227 1228 return item; 1229 }; 1230 1231 1232 SpazTwit.prototype.getTrends = function(onSuccess, onFailure) { 1233 var url = this.getAPIURL('trends'); 1234 this._getTimeline({ 1235 'url':url, 1236 'process_callback' : this._processTrends, 1237 'success_callback':onSuccess, 1238 'failure_callback':onFailure, 1239 'success_event_type': 'new_trends_data', 1240 'failure_event_type': 'error_trends_data' 1241 }); 1242 }; 1243 1244 1245 /** 1246 * @private 1247 */ 1248 SpazTwit.prototype._processTrends = function(trends_result, opts, processing_opts) { 1249 1250 if (!processing_opts) { processing_opts = {}; } 1251 1252 /* 1253 grab the array of items 1254 */ 1255 var ret_items = trends_result.trends; 1256 1257 if (ret_items.length > 0) { 1258 1259 for (var k=0; k<ret_items.length; k++) { 1260 ret_items[k].searchterm = ret_items[k].name; 1261 if ( /\s+/.test(ret_items[k].searchterm)) { // if there is whitespace, wrap in quotes 1262 ret_items[k].searchterm = '"'+ret_items[k].searchterm+'"'; 1263 } 1264 } 1265 // jQuery().trigger(finished_event, [ret_items]); 1266 1267 if (opts.success_callback) { 1268 opts.success_callback(ret_items); 1269 } 1270 this.triggerEvent(opts.success_event_type, ret_items); 1271 1272 } 1273 }; 1274 1275 1276 /** 1277 * this is a general wrapper for timeline methods 1278 * @param {obj} opts a set of options for this method 1279 * @private 1280 */ 1281 SpazTwit.prototype._getTimeline = function(opts) { 1282 1283 opts = sch.defaults({ 1284 'method':'GET', 1285 'timeout':this.DEFAULT_TIMEOUT, 1286 'url':null, 1287 'data':null, 1288 'process_callback':null, 1289 'processing_opts':null, 1290 'success_event_type':null, 1291 'failure_event_type':null, 1292 'success_callback':null, 1293 'failure_callback':null 1294 }, opts); 1295 1296 /* 1297 for closure references 1298 */ 1299 var stwit = this; 1300 1301 var xhr = jQuery.ajax({ 1302 'timeout' :opts.timeout, 1303 'complete':function(xhr, msg){ 1304 sc.helpers.dump(opts.url + ' complete:'+msg); 1305 if (msg === 'timeout') { 1306 // jQuery().trigger(opts.failure_event_type, [{'url':opts.url, 'xhr':xhr, 'msg':msg}]); 1307 stwit.triggerEvent(opts.failure_event_type, {'url':opts.url, 'xhr':xhr, 'msg':msg}); 1308 } 1309 }, 1310 'error':function(xhr, msg, exc) { 1311 sc.helpers.dump(opts.url + ' error:"'+msg+'"'); 1312 if (msg.toLowerCase().indexOf('timeout') !== -1) { 1313 stwit.triggerEvent(document, opts.failure_event_type, {'url':opts.url, 'xhr':null, 'msg':msg}); 1314 } else if (xhr) { 1315 if (!xhr.readyState < 4) { 1316 sc.helpers.dump("Error:"+xhr.status+" from "+opts['url']); 1317 if (xhr.responseText) { 1318 try { 1319 var data = sc.helpers.deJSON(xhr.responseText); 1320 } catch(e) { 1321 sc.helpers.dump(e.name + ":" + e.message); 1322 data = xhr.responseText; 1323 } 1324 } 1325 } 1326 if (opts.failure_callback) { 1327 opts.failure_callback(xhr, msg, exc); 1328 } 1329 if (opts.failure_event_type) { 1330 sc.helpers.dump("opts.failure_event_type:"+opts.failure_event_type); 1331 // jQuery().trigger(opts.failure_event_type, [{'url':opts.url, 'xhr':xhr, 'msg':msg}]); 1332 stwit.triggerEvent(opts.failure_event_type, {'url':opts.url, 'xhr':xhr, 'msg':msg}); 1333 1334 } 1335 1336 } else { 1337 sc.helpers.dump("Error:Unknown from "+opts['url']); 1338 if (opts.failure_callback) { 1339 opts.failure_callback(null, msg, exc); 1340 } 1341 if (opts.failure_event_type) { 1342 // jQuery().trigger(opts.failure_event_type, [{'url':opts.url, 'xhr':null, 'msg':'Unknown Error'}]); 1343 stwit.triggerEvent(opts.failure_event_type, {'url':opts.url, 'xhr':xhr, 'msg':'Unknown Error'}); 1344 1345 } 1346 } 1347 // jQuery().trigger('spaztwit_ajax_error', [{'url':opts.url, 'xhr':xhr, 'msg':msg}]); 1348 stwit.triggerEvent('spaztwit_ajax_error', {'url':opts.url, 'xhr':xhr, 'msg':msg}); 1349 1350 if (opts.processing_opts && opts.processing_opts.combined) { 1351 sc.helpers.dump('adding to combined processing errors'); 1352 stwit.combined_errors.push( {'url':opts.url, 'xhr':xhr, 'msg':msg, 'section':opts.processing_opts.section} ); 1353 stwit.combined_finished[opts.processing_opts.section] = true; 1354 sc.helpers.dump(stwit.combined_errors); 1355 sc.helpers.dump(stwit.combined_finished); 1356 if (opts.process_callback) { 1357 opts.process_callback.call(stwit, [], opts, opts.processing_opts); 1358 } 1359 } 1360 1361 }, 1362 'success':function(data) { 1363 // sc.helpers.dump("Success! \n\n" + data); 1364 sc.helpers.dump(opts.url + ' success!'+" data:"+data); 1365 1366 try { 1367 data = sc.helpers.deJSON(data); 1368 } catch(e) { 1369 stwit.triggerEvent(document, opts.failure_event_type, {'url':opts.url, 'xhr':xhr, 'msg':'Error decoding data from server'}); 1370 } 1371 1372 if (opts.process_callback) { 1373 /* 1374 using .call here and passing stwit as the first param 1375 ensures that "this" inside the callback refers to our 1376 SpazTwit object, and not the jQuery.Ajax object 1377 */ 1378 opts.process_callback.call(stwit, data, opts, opts.processing_opts); 1379 } else { 1380 if (opts.success_callback) { 1381 sch.error('CALLING SUCCESS CALLBACK'); 1382 opts.success_callback(data); 1383 } 1384 // jQuery().trigger(opts.success_event_type, [data]); 1385 stwit.triggerEvent(opts.success_event_type, data); 1386 } 1387 }, 1388 'beforeSend':function(xhr){ 1389 sc.helpers.dump(opts.url + ' beforesend'); 1390 if (stwit.auth) { 1391 sch.debug('signing request'); 1392 xhr.setRequestHeader('Authorization', stwit.auth.signRequest(opts.method, opts.url, opts.data)); 1393 } else { 1394 sch.debug('NOT signing request -- no auth object provided'); 1395 } 1396 }, 1397 'type': opts.method, 1398 'url': opts.url, 1399 'data': opts.data, 1400 'dataType':'text' 1401 }); 1402 1403 return xhr; 1404 }; 1405 1406 1407 1408 /** 1409 * general processor for timeline data 1410 * @private 1411 */ 1412 SpazTwit.prototype._processTimeline = function(section_name, ret_items, opts, processing_opts) { 1413 1414 sch.debug(opts); 1415 1416 if (!processing_opts) { processing_opts = {}; } 1417 1418 if (section_name !== SPAZCORE_SECTION_USER) { // the user timeline section isn't persistent 1419 /* 1420 reset .newitems data properties 1421 */ 1422 this.data[section_name].newitems = []; 1423 1424 } 1425 1426 1427 if (ret_items.length > 0){ 1428 1429 var proc_items = []; 1430 1431 /* 1432 we process each item, adding some attributes and generally making it cool 1433 */ 1434 for (var k=0; k<ret_items.length; k++) { 1435 if (ret_items[k]) { 1436 proc_items.push(this._processItem(ret_items[k], section_name)); 1437 } 1438 } 1439 ret_items = proc_items; 1440 proc_items = null; 1441 1442 1443 /* 1444 sort items 1445 */ 1446 ret_items.sort(this._sortItemsAscending); 1447 1448 1449 if (section_name === SPAZCORE_SECTION_USER) { // special case -- we don't keep this data, just parse and fire it off 1450 1451 if (opts.success_callback) { 1452 opts.success_callback(ret_items); 1453 } 1454 1455 this.triggerEvent(opts.success_event_type, ret_items); 1456 1457 } else { // this is a "normal" timeline that we want to be persistent 1458 1459 if (opts.is_update_item) { 1460 /* 1461 we do not want this to be the lastid, instead remember it in combined.updates 1462 */ 1463 this.combinedTimelineAddUpdates(ret_items); 1464 } else { 1465 // set lastid 1466 var lastid = ret_items[ret_items.length-1].id; 1467 this.data[section_name].lastid = lastid; 1468 sc.helpers.dump('this.data['+section_name+'].lastid:'+this.data[section_name].lastid); 1469 } 1470 1471 // add new items to data.newitems array 1472 this.data[section_name].newitems = ret_items; 1473 1474 this._addToSectionItems(section_name, this.data[section_name].newitems); 1475 1476 1477 // @todo check length of data.items, and remove oldest extras if necessary 1478 /* 1479 @todo 1480 */ 1481 1482 /* 1483 Fire off the new section data event 1484 */ 1485 if (!processing_opts.combined) { 1486 1487 if (opts.success_callback) { 1488 opts.success_callback(this.data[section_name].newitems); 1489 } 1490 1491 this.triggerEvent(opts.success_event_type, this.data[section_name].items); 1492 } else { 1493 this.combined_finished[section_name] = true; 1494 sc.helpers.dump("this.combined_finished["+section_name+"]:"+this.combined_finished[section_name]); 1495 } 1496 1497 1498 1499 /* 1500 add on to newitems array for combined section 1501 */ 1502 this.data[SPAZCORE_SECTION_COMBINED].newitems = this.data[SPAZCORE_SECTION_COMBINED].newitems.concat(this.data[section_name].newitems); 1503 1504 } 1505 1506 1507 } else { // no new items, but we should fire off success anyway 1508 if (!processing_opts.combined) { 1509 // jQuery().trigger(finished_event, []); 1510 if (opts.success_callback) { 1511 opts.success_callback(); 1512 } 1513 this.triggerEvent(opts.success_event_type); 1514 1515 } else { 1516 this.combined_finished[section_name] = true; 1517 } 1518 } 1519 1520 /* 1521 Fire off the new combined data event 1522 */ 1523 if (this.combinedTimelineFinished()) { 1524 1525 /* 1526 Remove those updates from combined newitems 1527 */ 1528 this.combinedNewItemsRemoveUpdates(); 1529 1530 /* 1531 we do this stuff here to avoid processing repeatedly 1532 */ 1533 1534 this._addToSectionItems(SPAZCORE_SECTION_COMBINED, this.data[SPAZCORE_SECTION_COMBINED].newitems, this._sortItemsByDateAsc); 1535 1536 // sort these items -- the timelines can be out of order when combined 1537 1538 1539 // sc.helpers.dump('Removing duplicates in '+SPAZCORE_SECTION_COMBINED+' newitems'); 1540 // 1541 this.data[SPAZCORE_SECTION_COMBINED].newitems = this._cleanupItemArray(this.data[SPAZCORE_SECTION_COMBINED].newitems, this.data[SPAZCORE_SECTION_COMBINED].max, this._sortItemsByDateAsc); 1542 1543 if (this.combinedTimelineHasErrors()) { 1544 if (opts.failure_callback) { 1545 opts.failure_callback(this.combined_errors); 1546 } 1547 1548 this.triggerEvent('error_combined_timeline_data', this.combined_errors); 1549 } 1550 1551 if (opts.success_callback) { 1552 opts.success_callback(this.data[SPAZCORE_SECTION_COMBINED].newitems); 1553 } 1554 sch.debug('this.data[SPAZCORE_SECTION_COMBINED].newitems has '+this.data[SPAZCORE_SECTION_COMBINED].newitems.length+' items'); 1555 this.triggerEvent('new_combined_timeline_data', this.data[SPAZCORE_SECTION_COMBINED].newitems); 1556 this.data[SPAZCORE_SECTION_COMBINED].newitems = []; // reset combined.newitems 1557 this.initializeCombinedTracker(); 1558 } 1559 }; 1560 1561 1562 /** 1563 * Adds an array of items to the .items property of the appropriate section, then 1564 * removes dupes, extras, and optionally sorts the section items 1565 * @param {string} section_name 1566 * @param {array} arr an array of items 1567 * @param {function} sortfunc - optional 1568 */ 1569 SpazTwit.prototype._addToSectionItems = function(section_name, arr, sortfunc) { 1570 // concat new items onto data.items array 1571 var data = this.data[section_name]; 1572 data.items = this._cleanupItemArray(data.items.concat(arr), null, sortfunc); 1573 }; 1574 1575 /** 1576 * Sorts (optionally), removes dupes, and removes extra items from a given 1577 * array of section items 1578 * 1579 * @param {array} arr 1580 * @param {max} integer 1581 * @param {func} sortfunc - optional 1582 * 1583 * @return {array} 1584 */ 1585 SpazTwit.prototype._cleanupItemArray = function(arr, max, sortfunc) { 1586 if (sortfunc) { 1587 arr = arr.sort(sortfunc); 1588 } 1589 arr = this.removeDuplicates(arr); 1590 sch.debug('NOT removing extras -- we don\'t do that anymore'); 1591 // arr = this.removeExtraElements(arr, max); 1592 return arr; 1593 }; 1594 1595 /** 1596 * This modifies a Twitter post, adding some properties. All new properties are 1597 * prepended with "SC_" 1598 * 1599 * this executes within the jQuery.each scope, so this === the item 1600 */ 1601 SpazTwit.prototype._processItem = function(item, section_name) { 1602 1603 item.SC_timeline_from = section_name; 1604 if (this.username) { 1605 item.SC_user_received_by = this.username; 1606 } 1607 1608 /* 1609 is reply? Then add .SC_is_reply 1610 */ 1611 if ( (item.in_reply_to_screen_name && item.SC_user_received_by) ) { 1612 if (item.in_reply_to_screen_name.toLowerCase() === item.SC_user_received_by.toLowerCase() ) { 1613 item.SC_is_reply = true; 1614 } 1615 } 1616 1617 /* 1618 is an official API retweet? then add .SC_is_retweet 1619 */ 1620 if ( item.retweeted_status ) { 1621 item.SC_is_retweet = true; 1622 } 1623 1624 /* 1625 If it comes from the replies timeline, it's a reply (aka a mention) 1626 */ 1627 if (section_name === SPAZCORE_SECTION_REPLIES) { 1628 item.SC_is_reply = true; 1629 } 1630 1631 /* 1632 Does it contain my name? then it's a reply 1633 */ 1634 if (this.username && sc.helpers.containsScreenName(item.text, this.username) ) { 1635 item.SC_is_reply = true; 1636 } 1637 1638 /* 1639 is dm? 1640 */ 1641 if (item.recipient_id && item.sender_id) { 1642 item.SC_is_dm = true; 1643 } 1644 1645 1646 /* 1647 add unix timestamp .SC_created_at_unixtime for easier date comparison 1648 */ 1649 if (!item.SC_created_at_unixtime) { 1650 item.SC_created_at_unixtime = sc.helpers.httpTimeToInt(item.created_at); 1651 } 1652 1653 /* 1654 add raw text .SC_raw_text for unmodified text 1655 */ 1656 if (!item.SC_text_raw) { 1657 item.SC_text_raw = item.text; 1658 } 1659 1660 /* 1661 add "in_reply_to_screen_name" if it does not exist 1662 */ 1663 if (!item.in_reply_to_screen_name && item.in_reply_to_user_id) { 1664 /** 1665 * @todo get this from the Spaz code 1666 */ 1667 } 1668 1669 /* 1670 add .SC_retrieved_unixtime 1671 */ 1672 if (!item.SC_retrieved_unixtime) { 1673 item.SC_retrieved_unixtime = sc.helpers.getTimeAsInt(); 1674 } 1675 1676 return item; 1677 }; 1678 1679 1680 1681 /** 1682 * This modifies a Twitter post, adding some properties. All new properties are 1683 * prepended with "SC_" 1684 * 1685 * this executes within the jQuery.each scope, so this === the item 1686 */ 1687 SpazTwit.prototype._processUser = function(item, section_name) { 1688 1689 item.SC_timeline_from = section_name; 1690 if (this.username) { 1691 item.SC_user_received_by = this.username; 1692 } 1693 1694 1695 if (section_name === SPAZCORE_SECTION_FOLLOWERSLIST) { 1696 item.SC_is_follower; 1697 } 1698 if (section_name === SPAZCORE_SECTION_FRIENDLIST) { 1699 item.SC_is_followed; 1700 } 1701 1702 /* 1703 add unix timestamp .SC_created_at_unixtime for easier date comparison 1704 */ 1705 if (!item.SC_created_at_unixtime) { 1706 item.SC_created_at_unixtime = sc.helpers.httpTimeToInt(item.created_at); 1707 } 1708 1709 /* 1710 add .SC_retrieved_unixtime 1711 */ 1712 if (!item.SC_retrieved_unixtime) { 1713 item.SC_retrieved_unixtime = sc.helpers.getTimeAsInt(); 1714 } 1715 1716 return item; 1717 }; 1718 1719 1720 /** 1721 * returns the header string for oAuth Echo usage 1722 */ 1723 SpazTwit.prototype.getEchoHeader = function(opts) { 1724 var url; 1725 if (opts && opts.verify_url) { 1726 url = opts.verify_url; 1727 } else { 1728 url = this.getAPIURL('verify_credentials'); 1729 } 1730 1731 var method = 'GET'; 1732 1733 var auth_header = this.auth.signRequest(method, url, null); 1734 1735 return auth_header; 1736 }; 1737 1738 1739 /** 1740 * this is a general wrapper for non-timeline methods on the Twitter API. We 1741 * use this to call methods that will return a single response 1742 * 1743 * @param {obj} opts a set of options for this method 1744 * @param {string} opts.url The url for the request 1745 * @param {string} [opts.method] the HTTP method to use. default is POST 1746 * @param {number} [opts.timeout] the timeout for the request. default is 60 seconds 1747 * @param {object} [opts.data] data to pass with the request 1748 * @param {string} [opts.username] 1749 * @param {string} [opts.password] 1750 * @param {function} [opts.process_callback] a function to call on the retured data for extra processing on success 1751 * @param {string} [opts.success_event_type] the event to trigger on success 1752 * @param {string} [opts.failure_event_type] the event to trigger on failure 1753 * @param {function} [opts.success_callback] a callback to fire on success 1754 * @param {function} [opts.failure_callback] a callback to fire on failure 1755 */ 1756 SpazTwit.prototype._callMethod = function(opts) { 1757 1758 opts = sch.defaults({ 1759 'method':'POST', 1760 'timeout':this.DEFAULT_TIMEOUT, 1761 'url':null, 1762 'data':null, 1763 'process_callback':null, 1764 'success_event_type':null, 1765 'failure_event_type':null, 1766 'success_callback':null, 1767 'failure_callback':null 1768 }, opts); 1769 1770 var method; 1771 1772 /* 1773 for closure references 1774 */ 1775 var stwit = this; 1776 1777 if (opts.method) { 1778 method = opts.method; 1779 } else { 1780 method = 'POST'; 1781 } 1782 1783 var xhr = jQuery.ajax({ 1784 'timeout' :this.opts.timeout, 1785 'complete':function(xhr, msg){ 1786 sc.helpers.dump(opts.url + ' complete:'+msg); 1787 }, 1788 'error':function(xhr, msg, exc) { 1789 sc.helpers.error(opts.url + ' error:'+msg); 1790 if (xhr) { 1791 if (!xhr.readyState < 4) { 1792 sc.helpers.dump("Error:"+xhr.status+" from "+opts['url']); 1793 if (xhr.responseText) { 1794 try { 1795 var data = sc.helpers.deJSON(xhr.responseText); 1796 } catch(e) { 1797 sc.helpers.dump(e.name + ":" + e.message); 1798 data = xhr.responseText; 1799 } 1800 } 1801 } 1802 if (opts.failure_callback) { 1803 opts.failure_callback(xhr, msg, exc); 1804 } 1805 if (opts.failure_event_type) { 1806 // jQuery().trigger(opts.failure_event_type, [{'url':opts.url, 'xhr':xhr, 'msg':msg}]); 1807 stwit.triggerEvent(opts.failure_event_type, {'url':opts.url, 'xhr':xhr, 'msg':msg}); 1808 } 1809 1810 } else { 1811 sc.helpers.dump("Error:Unknown from "+opts['url']); 1812 if (opts.failure_callback) { 1813 opts.failure_callback(null, msg, exc); 1814 } 1815 if (opts.failure_event_type) { 1816 // jQuery().trigger(opts.failure_event_type, [{'url':opts.url, 'xhr':null, 'msg':'Unknown Error'}]); 1817 stwit.triggerEvent(opts.failure_event_type, {'url':opts.url, 'xhr':null, 'msg':'Unknown Error'}); 1818 1819 } 1820 } 1821 // jQuery().trigger('spaztwit_ajax_error', [{'url':opts.url, 'xhr':xhr, 'msg':msg}]); 1822 stwit.triggerEvent('spaztwit_ajax_error', {'url':opts.url, 'xhr':xhr, 'msg':msg}); 1823 }, 1824 'success':function(data) { 1825 sc.helpers.error(opts.url + ' success'); 1826 data = sc.helpers.deJSON(data); 1827 if (opts.process_callback) { 1828 /* 1829 using .call here and passing stwit as the first param 1830 ensures that "this" inside the callback refers to our 1831 SpazTwit object, and not the jQuery.Ajax object 1832 */ 1833 opts.process_callback.call(stwit, data, opts); 1834 } else { 1835 if (opts.success_callback) { 1836 opts.success_callback(data); 1837 } 1838 // jQuery().trigger(opts.success_event_type, [data]); 1839 stwit.triggerEvent(opts.success_event_type, data); 1840 1841 } 1842 }, 1843 'beforeSend':function(xhr){ 1844 sc.helpers.dump(opts.url + ' beforesend'); 1845 if (stwit.auth) { 1846 sch.debug('signing request'); 1847 xhr.setRequestHeader('Authorization', stwit.auth.signRequest(method, opts.url, opts.data)); 1848 } else { 1849 sch.debug('NOT signing request -- no auth object provided'); 1850 } 1851 }, 1852 'type': method, 1853 'url' : opts.url, 1854 'data': opts.data, 1855 'dataType':'text' 1856 }); 1857 return xhr; 1858 }; 1859 1860 1861 1862 SpazTwit.prototype.getUser = function(user_id, onSuccess, onFailure) { 1863 var data = {}; 1864 data['id'] = user_id; 1865 1866 var url = this.getAPIURL('show_user', data); 1867 1868 var opts = { 1869 'url':url, 1870 // 'process_callback': this._processUserData, 1871 'success_event_type':'get_user_succeeded', 1872 'failure_event_type':'get_user_failed', 1873 'success_callback':onSuccess, 1874 'failure_callback':onFailure, 1875 'method':'GET' 1876 }; 1877 1878 /* 1879 Perform a request and get true or false back 1880 */ 1881 var xhr = this._callMethod(opts); 1882 }; 1883 1884 1885 1886 SpazTwit.prototype.getFriendsList = function() { 1887 1888 var url = this.getAPIURL('friendslist'); 1889 1890 var opts = { 1891 'url':url, 1892 'process_callback': this._processFriendsList, 1893 'success_event_type':'get_friendslist_succeeded', 1894 'failure_event_type':'get_friendslist_failed', 1895 'method':'GET' 1896 }; 1897 1898 var xhr = this._getTimeline(opts); 1899 }; 1900 /** 1901 * @private 1902 */ 1903 SpazTwit.prototype._processFriendsList = function(ret_items, opts, processing_opts) { 1904 this._processUserList(SPAZCORE_SECTION_FRIENDLIST, ret_items, opts.success_event_type, processing_opts); 1905 }; 1906 1907 1908 1909 1910 1911 1912 SpazTwit.prototype.getFollowersList = function() { 1913 var url = this.getAPIURL('followerslist'); 1914 1915 var opts = { 1916 'url':url, 1917 'process_callback': this._processFollowersList, 1918 'success_event_type':'get_followerslist_succeeded', 1919 'failure_event_type':'get_followerslist_failed', 1920 'method':'GET' 1921 }; 1922 1923 var xhr = this._getTimeline(opts); 1924 }; 1925 /** 1926 * @private 1927 */ 1928 SpazTwit.prototype._processFollowersList = function(ret_items, opts, processing_opts) { 1929 this._processUserList(SPAZCORE_SECTION_FOLLOWERSLIST, ret_items, opts.success_event_type, processing_opts); 1930 }; 1931 1932 1933 1934 /** 1935 * general processor for timeline data 1936 * @private 1937 */ 1938 SpazTwit.prototype._processUserList = function(section_name, ret_items, opts, processing_opts) { 1939 1940 if (!processing_opts) { processing_opts = {}; } 1941 1942 if (ret_items.length > 0){ 1943 /* 1944 we process each item, adding some attributes and generally making it cool 1945 */ 1946 for (var k=0; k<ret_items.length; k++) { 1947 ret_items[k] = this._processUser(ret_items[k], section_name); 1948 sch.dump(ret_items[k]); 1949 } 1950 1951 /* 1952 sort items 1953 */ 1954 ret_items.sort(this._sortItemsAscending); 1955 1956 1957 // set lastid 1958 var lastid = ret_items[ret_items.length-1].id; 1959 this.data[section_name].lastid = lastid; 1960 sc.helpers.dump('this.data['+section_name+'].lastid:'+this.data[section_name].lastid); 1961 1962 // add new items to data.newitems array 1963 this.data[section_name].newitems = ret_items; 1964 1965 this._addToSectionItems(section_name, this.data[section_name].newitems); 1966 1967 if (opts.success_callback) { 1968 opts.success_callback(this.data[section_name].newitems); 1969 } 1970 this.triggerEvent(opts.success_event_type,this.data[section_name].newitems ); 1971 1972 } else { // no new items, but we should fire off success anyway 1973 if (opts.success_callback) { 1974 opts.success_callback(); 1975 } 1976 this.triggerEvent(opts.success_event_type); 1977 } 1978 1979 }; 1980 1981 1982 SpazTwit.prototype.addFriend = function(user_id, onSuccess, onFailure) { 1983 var data = {}; 1984 data['id'] = user_id; 1985 1986 var url = this.getAPIURL('friendship_create', data); 1987 1988 var opts = { 1989 'url':url, 1990 'success_event_type':'create_friendship_succeeded', 1991 'failure_event_type':'create_friendship_failed', 1992 'success_callback':onSuccess, 1993 'failure_callback':onFailure, 1994 'data':data 1995 }; 1996 1997 /* 1998 Perform a request and get true or false back 1999 */ 2000 var xhr = this._callMethod(opts); 2001 }; 2002 SpazTwit.prototype.removeFriend = function(user_id, onSuccess, onFailure) { 2003 var data = {}; 2004 data['id'] = user_id; 2005 2006 var url = this.getAPIURL('friendship_destroy', data); 2007 2008 var opts = { 2009 'url':url, 2010 'success_event_type':'destroy_friendship_succeeded', 2011 'failure_event_type':'destroy_friendship_failed', 2012 'success_callback':onSuccess, 2013 'failure_callback':onFailure, 2014 'data':data 2015 }; 2016 2017 /* 2018 Perform a request and get true or false back 2019 */ 2020 var xhr = this._callMethod(opts); 2021 2022 }; 2023 2024 /** 2025 * @param {string|number} target_id the target user id, or screen name if prefixed with a "@" 2026 * @param {string|number} [source_id] the surce user id, or screen name if prefixed with a "@" 2027 * @param {function} [onSuccess] success callback 2028 * @param {function} [onFailure] failure callback 2029 */ 2030 SpazTwit.prototype.showFriendship = function(target_id, source_id, onSuccess, onFailure) { 2031 var data = {}; 2032 2033 if (sch.isString(target_id) && target_id.indexOf('@')===0) { 2034 data['target_screen_name'] = target_id.substr(1); 2035 } else { 2036 data['target_id'] = target_id; 2037 } 2038 2039 if (source_id) { 2040 if (sch.isString(source_id) && source_id.indexOf('@')===0) { 2041 data['source_screen_name'] = source_id.substr(1); 2042 } else { 2043 data['source_id'] = source_id; 2044 } 2045 2046 } 2047 2048 2049 var url = this.getAPIURL('friendship_show', data); 2050 2051 var opts = { 2052 'url':url, 2053 'method':'GET', 2054 'success_event_type':'show_friendship_succeeded', 2055 'failure_event_type':'show_friendship_failed', 2056 'success_callback':onSuccess, 2057 'failure_callback':onFailure, 2058 'data':data 2059 }; 2060 2061 /* 2062 Perform a request and get true or false back 2063 */ 2064 var xhr = this._callMethod(opts); 2065 2066 }; 2067 2068 SpazTwit.prototype.getIncomingFriendships = function(cursor, onSuccess, onFailure) { 2069 var data = {}; 2070 if (!cursor) { 2071 cursor = -1; 2072 } 2073 data['cursor'] = cursor; 2074 2075 var url = this.getAPIURL('friendship_incoming', data); 2076 2077 var opts = { 2078 'url':url, 2079 'method':'GET', 2080 'success_event_type':'get_incoming_friendships_succeeded', 2081 'failure_event_type':'get_incoming_friendships_failed', 2082 'success_callback':onSuccess, 2083 'failure_callback':onFailure, 2084 'data':data 2085 }; 2086 2087 /* 2088 Perform a request and get true or false back 2089 */ 2090 var xhr = this._callMethod(opts); 2091 2092 }; 2093 2094 SpazTwit.prototype.getOutgoingFriendships = function(cursor, onSuccess, onFailure) { 2095 var data = {}; 2096 if (!cursor) { 2097 cursor = -1; 2098 } 2099 data['cursor'] = cursor; 2100 2101 var url = this.getAPIURL('friendship_outgoing', data); 2102 2103 var opts = { 2104 'url':url, 2105 'method':'GET', 2106 'success_event_type':'get_outgoing_friendships_succeeded', 2107 'failure_event_type':'get_outgoing_friendships_failed', 2108 'success_callback':onSuccess, 2109 'failure_callback':onFailure, 2110 'data':data 2111 }; 2112 2113 /* 2114 Perform a request and get true or false back 2115 */ 2116 var xhr = this._callMethod(opts); 2117 2118 }; 2119 2120 SpazTwit.prototype.getFriendsGraph = function(user_id, cursor, onSuccess, onFailure) { 2121 var data = {}; 2122 if (!cursor) { 2123 cursor = -1; 2124 } 2125 data['cursor'] = cursor; 2126 data['user_id'] = user_id; 2127 2128 var url = this.getAPIURL('graph_friends', data); 2129 2130 var opts = { 2131 'url':url, 2132 'method':'GET', 2133 'success_event_type':'get_friends_graph_succeeded', 2134 'failure_event_type':'get_friends_graph_failed', 2135 'success_callback':onSuccess, 2136 'failure_callback':onFailure, 2137 'data':data 2138 }; 2139 2140 /* 2141 Perform a request and get true or false back 2142 */ 2143 var xhr = this._callMethod(opts); 2144 2145 }; 2146 2147 SpazTwit.prototype.getFollowersGraph = function(user_id, cursor, onSuccess, onFailure) { 2148 var data = {}; 2149 if (!cursor) { 2150 cursor = -1; 2151 } 2152 data['cursor'] = cursor; 2153 data['user_id'] = user_id; 2154 2155 var url = this.getAPIURL('graph_followers', data); 2156 2157 var opts = { 2158 'url':url, 2159 'method':'GET', 2160 'success_event_type':'get_followers_graph_succeeded', 2161 'failure_event_type':'get_followers_graph_failed', 2162 'success_callback':onSuccess, 2163 'failure_callback':onFailure, 2164 'data':data 2165 }; 2166 2167 /* 2168 Perform a request and get true or false back 2169 */ 2170 var xhr = this._callMethod(opts); 2171 2172 }; 2173 2174 SpazTwit.prototype.block = function(user_id, onSuccess, onFailure) { 2175 var data = {}; 2176 data['id'] = user_id; 2177 2178 var url = this.getAPIURL('block_create', data); 2179 2180 var opts = { 2181 'url':url, 2182 'success_event_type':'create_block_succeeded', 2183 'failure_event_type':'create_block_failed', 2184 'success_callback':onSuccess, 2185 'failure_callback':onFailure, 2186 'data':data 2187 }; 2188 2189 /* 2190 Perform a request and get true or false back 2191 */ 2192 var xhr = this._callMethod(opts); 2193 }; 2194 2195 SpazTwit.prototype.unblock = function(user_id, onSuccess, onFailure) { 2196 var data = {}; 2197 data['id'] = user_id; 2198 2199 var url = this.getAPIURL('block_destroy', data); 2200 2201 var opts = { 2202 'url':url, 2203 'success_event_type':'destroy_block_succeeded', 2204 'failure_event_type':'destroy_block_failed', 2205 'success_callback':onSuccess, 2206 'failure_callback':onFailure, 2207 'data':data 2208 }; 2209 2210 /* 2211 Perform a request and get true or false back 2212 */ 2213 var xhr = this._callMethod(opts); 2214 2215 }; 2216 2217 SpazTwit.prototype.follow = function(user_id, onSuccess, onFailure) { // to add notification 2218 var data = {}; 2219 data['id'] = user_id; 2220 2221 var url = this.getAPIURL('follow', data); 2222 2223 var opts = { 2224 'url':url, 2225 'username':this.username, 2226 'password':this.password, 2227 'success_event_type':'follow_succeeded', 2228 'failure_event_type':'follow_failed', 2229 'success_callback':onSuccess, 2230 'failure_callback':onFailure, 2231 'data':data 2232 }; 2233 2234 /* 2235 Perform a request and get true or false back 2236 */ 2237 var xhr = this._callMethod(opts); 2238 2239 }; 2240 2241 SpazTwit.prototype.unfollow = function(user_id, onSuccess, onFailure) { // to remove notification 2242 var data = {}; 2243 data['id'] = user_id; 2244 2245 var url = this.getAPIURL('unfollow', data); 2246 2247 var opts = { 2248 'url':url, 2249 'username':this.username, 2250 'password':this.password, 2251 'success_event_type':'unfollow_succeeded', 2252 'failure_event_type':'unfollow_failed', 2253 'success_callback':onSuccess, 2254 'failure_callback':onFailure, 2255 'data':data 2256 }; 2257 2258 /* 2259 Perform a request and get true or false back 2260 */ 2261 var xhr = this._callMethod(opts); 2262 2263 }; 2264 2265 2266 SpazTwit.prototype.update = function(status, source, in_reply_to_status_id, onSuccess, onFailure) { 2267 2268 var url = this.getAPIURL('update'); 2269 2270 var data = {}; 2271 if (in_reply_to_status_id) { 2272 data.in_reply_to_status_id = in_reply_to_status_id; 2273 } 2274 if (source) { 2275 data.source = source; 2276 } else { 2277 data.source = this.source; 2278 } 2279 data.status = status; 2280 2281 var opts = { 2282 'url':url, 2283 'data':data, 2284 'process_callback': this._processUpdateReturn, 2285 'success_callback':onSuccess, 2286 'failure_callback':onFailure, 2287 'success_event_type':'update_succeeded', 2288 'failure_event_type':'update_failed' 2289 }; 2290 2291 /* 2292 Perform a request and get true or false back 2293 */ 2294 var xhr = this._callMethod(opts); 2295 2296 2297 }; 2298 2299 SpazTwit.prototype._processUpdateReturn = function(data, opts) { 2300 2301 /* 2302 Add this to the HOME section and fire off the event when done 2303 */ 2304 opts.is_update_item = true; 2305 this._processTimeline(SPAZCORE_SECTION_HOME, [data], opts); 2306 }; 2307 2308 /** 2309 * destroy/delete a status 2310 * @param {Number|String} id the id of the status 2311 */ 2312 SpazTwit.prototype.destroy = function(id, onSuccess, onFailure) { 2313 var data = {}; 2314 data['id'] = id; 2315 2316 var url = this.getAPIURL('destroy_status', data); 2317 2318 var opts = { 2319 'url':url, 2320 'data':data, 2321 'success_event_type':'destroy_status_succeeded', 2322 'success_callback':onSuccess, 2323 'failure_callback':onFailure, 2324 'failure_event_type':'destroy_status_failed' 2325 }; 2326 2327 /* 2328 Perform a request and get true or false back 2329 */ 2330 var xhr = this._callMethod(opts); 2331 }; 2332 2333 /** 2334 * destroy/delete a direct message 2335 * @param {Number|String} id the id of the status 2336 */ 2337 SpazTwit.prototype.destroyDirectMessage = function(id, onSuccess, onFailure) { 2338 var data = {}; 2339 data['id'] = id; 2340 2341 var url = this.getAPIURL('dm_destroy', data); 2342 2343 var opts = { 2344 'url':url, 2345 'data':data, 2346 'success_event_type':'destroy_dm_succeeded', 2347 'success_callback':onSuccess, 2348 'failure_callback':onFailure, 2349 'failure_event_type':'destroy_dm_failed' 2350 }; 2351 2352 /* 2353 Perform a request and get true or false back 2354 */ 2355 var xhr = this._callMethod(opts); 2356 }; 2357 2358 2359 SpazTwit.prototype.getOne = function(id, onSuccess, onFailure) { 2360 var data = {}; 2361 data['id'] = id; 2362 2363 var url = this.getAPIURL('show', data); 2364 2365 var opts = { 2366 'url':url, 2367 'process_callback': this._processOneItem, 2368 'success_event_type':'get_one_status_succeeded', 2369 'success_callback':onSuccess, 2370 'failure_callback':onFailure, 2371 'failure_event_type':'get_one_status_failed', 2372 'method':'GET' 2373 }; 2374 2375 /* 2376 Perform a request and get true or false back 2377 */ 2378 var xhr = this._callMethod(opts); 2379 }; 2380 2381 2382 SpazTwit.prototype._processOneItem = function(data, opts) { 2383 2384 /* 2385 this item needs to be added to the friends timeline 2386 so we can avoid dupes 2387 */ 2388 data = this._processItem(data); 2389 if (opts.success_callback) { 2390 opts.success_callback(data); 2391 } 2392 this.triggerEvent(opts.success_event_type, data); 2393 2394 }; 2395 2396 // Retweet API 2397 2398 /* 2399 * Retweets a tweet. 2400 * id: the numeric id of a tweet 2401 */ 2402 2403 SpazTwit.prototype.retweet = function(id, onSuccess, onFailure) { 2404 var data = {}; 2405 data['id'] = id; 2406 2407 var url = this.getAPIURL('retweet', data); 2408 2409 var opts = { 2410 'url' : url, 2411 'username' : this.username, 2412 'password' : this.password, 2413 'success_event_type' : 'retweet_succeeded', 2414 'failure_event_type' : 'retweet_failed', 2415 'success_callback' : onSuccess, 2416 'failure_callback' : onFailure, 2417 'data' : data 2418 }; 2419 2420 var xhr = this._callMethod(opts); 2421 }; 2422 2423 /* 2424 * Gets up to 100 of the latest retweets of a tweet. 2425 * id: the tweet to get retweets of 2426 * count: the number of retweets to get 2427 */ 2428 2429 SpazTwit.prototype.getRetweets = function(id, count) { 2430 var url = this.getAPIURL('retweets', { 2431 'id' : id, 2432 'count' : count 2433 }); 2434 2435 var opts = { 2436 'url' : url, 2437 'username' : this.username, 2438 'password' : this.password, 2439 'success_event_type' : 'get_retweets_succeeded', 2440 'failure_event_Type' : 'get_retweets_failed', 2441 'method' : 'GET' 2442 }; 2443 2444 var xhr = this._getTimeline(opts); 2445 }; 2446 2447 /* 2448 * Returns up to 200 of the most recent retweets by the user 2449 * since: the numeric id of the tweet serving as a floor 2450 * max: the numeric id of the tweet serving as a ceiling 2451 * count: the number of tweets to return. Cannot be over 200. 2452 * page: the page of results to return. 2453 */ 2454 2455 SpazTwit.prototype.retweetedByMe = function(since, max, count, page){ 2456 var params = {}; 2457 if(since != null){ 2458 params['since_id'] = since; 2459 } 2460 if(max != null){ 2461 params['max_id'] = max; 2462 } 2463 if(count == null){ 2464 count = 20; 2465 } 2466 params['count'] = count; 2467 if(page == null){ 2468 page = 1; 2469 } 2470 params['page'] = page; 2471 var url = this.getAPIURL('retweeted_by_me', params); 2472 2473 var opts = { 2474 'url' : url, 2475 'username' : this.username, 2476 'password' : this.password, 2477 'success_event_type' : 'retweeted_by_me_succeeded', 2478 'failure_event_type' : 'retweeted_by_me_failed', 2479 'method' : 'GET' 2480 }; 2481 2482 var xhr = this._getTimeline(opts); 2483 }; 2484 2485 /* 2486 * Returns up to 200 of the most recent retweets by the user's friends 2487 * since: the numeric id of the tweet serving as a floor 2488 * max: the numeric id of the tweet serving as a ceiling 2489 * count: the number of tweets to return. Cannot be over 200. 2490 * page: the page of results to return. 2491 */ 2492 2493 SpazTwit.prototype.retweetedToMe = function(since, max, count, page){ 2494 var params = {}; 2495 if(since != null){ 2496 params['since_id'] = since; 2497 } 2498 if(max != null){ 2499 params['max_id'] = max; 2500 } 2501 if(count == null){ 2502 count = 20; 2503 } 2504 params['count'] = count; 2505 if(page == null){ 2506 page = 1; 2507 } 2508 params['page'] = page; 2509 var url = this.getAPIURL('retweeted_to_me', params); 2510 2511 var opts = { 2512 'url' : url, 2513 'username' : this.username, 2514 'password' : this.password, 2515 'success_event_type' : 'retweeted_to_me_succeeded', 2516 'failure_event_type' : 'retweeted_to_me_failed', 2517 'method' : 'GET' 2518 }; 2519 2520 var xhr = this._getTimeline(opts); 2521 }; 2522 2523 /* 2524 * Returns up to 200 of the most recent retweets of the user's tweets 2525 * since: the numeric id of the tweet serving as a floor 2526 * max: the numeric id of the tweet serving as a ceiling 2527 * count: the number of tweets to return. Cannot be over 200. 2528 * page: the page of results to return. 2529 */ 2530 2531 SpazTwit.prototype.retweetsOfMe = function(since, max, count, page){ 2532 var params = {}; 2533 if(since != null){ 2534 params['since_id'] = since; 2535 } 2536 if(max != null){ 2537 params['max_id'] = max; 2538 } 2539 if(count == null){ 2540 count = 20; 2541 } 2542 params['count'] = count; 2543 if(page == null){ 2544 page = 1; 2545 } 2546 params['page'] = page; 2547 var url = this.getAPIURL('retweets_of_me', params); 2548 2549 var opts = { 2550 'url' : url, 2551 'username' : this.username, 2552 'password' : this.password, 2553 'success_event_type' : 'retweets_of_me_succeeded', 2554 'failure_event_type' : 'retweets_of_me_failed', 2555 'method' : 'GET' 2556 }; 2557 2558 var xhr = this._getTimeline(opts); 2559 }; 2560 2561 SpazTwit.prototype.favorite = function(id, onSuccess, onFailure) { 2562 var data = {}; 2563 data['id'] = id; 2564 2565 var url = this.getAPIURL('favorites_create', data); 2566 2567 var opts = { 2568 'url':url, 2569 'success_event_type':'create_favorite_succeeded', 2570 'failure_event_type':'create_favorite_failed', 2571 'success_callback':onSuccess, 2572 'failure_callback':onFailure, 2573 'data':data 2574 }; 2575 2576 /* 2577 Perform a request and get true or false back 2578 */ 2579 var xhr = this._callMethod(opts); 2580 }; 2581 2582 SpazTwit.prototype.unfavorite = function(id, onSuccess, onFailure) { 2583 var data = {}; 2584 data['id'] = id; 2585 2586 var url = this.getAPIURL('favorites_destroy', data); 2587 2588 var opts = { 2589 'url':url, 2590 'success_event_type':'destroy_favorite_succeeded', 2591 'failure_event_type':'destroy_favorite_failed', 2592 'success_callback':onSuccess, 2593 'failure_callback':onFailure, 2594 'data':data 2595 }; 2596 2597 /* 2598 Perform a request and get true or false back 2599 */ 2600 var xhr = this._callMethod(opts); 2601 }; 2602 2603 2604 2605 2606 SpazTwit.prototype.updateLocation = function(location_str, onSuccess, onFailure) { 2607 var data = {}; 2608 data.location = location_str; 2609 2610 this.setBaseURL(SPAZCORE_SERVICEURL_TWITTER); 2611 2612 var url = this.getAPIURL('update_profile'); 2613 2614 var opts = { 2615 'url':url, 2616 'success_event_type':'update_location_succeeded', 2617 'failure_event_type':'update_location_failed', 2618 'success_callback':onSuccess, 2619 'failure_callback':onFailure, 2620 'data':data 2621 }; 2622 2623 /* 2624 Perform a request and get true or false back 2625 */ 2626 var xhr = this._callMethod(opts); 2627 }; 2628 2629 SpazTwit.prototype.updateProfile = function(name, email, url, location, description) { 2630 2631 }; 2632 2633 2634 2635 /** 2636 * get the current rate limit status 2637 * @param {Function} onSuccess callback for success 2638 * @param {Function} onFailure callback for failure 2639 */ 2640 SpazTwit.prototype.getRateLimitStatus = function(onSuccess, onFailure) { 2641 2642 var url = this.getAPIURL('ratelimit_status'); 2643 2644 var opts = { 2645 'method':'GET', 2646 'url':url, 2647 'success_event_type':'ratelimit_status_succeeded', 2648 'failure_event_type':'ratelimit_status_failed', 2649 'success_callback':onSuccess, 2650 'failure_callback':onFailure 2651 }; 2652 2653 /* 2654 Perform a request and get true or false back 2655 */ 2656 var xhr = this._callMethod(opts); 2657 2658 }; 2659 2660 SpazTwit.prototype.test = function() {}; 2661 2662 2663 /** 2664 * @private 2665 */ 2666 SpazTwit.prototype._postProcessURL = function(url) { 2667 2668 if (typeof Mojo !== "undefined") { // we're in webOS 2669 if (use_palmhost_proxy) { // we are not on an emu or device, so proxy calls 2670 var re = /https?:\/\/.[^\/:]*(?::[0-9]+)?/; 2671 var match = url.match(re); 2672 if (match && match[0] !== Mojo.hostingPrefix) { 2673 url = "/proxy?url=" + encodeURIComponent(url); 2674 } 2675 return url; 2676 } else { 2677 return url; 2678 } 2679 2680 } else { 2681 return url; 2682 } 2683 }; 2684 2685 2686 /** 2687 * sorting function for an array of tweets. Asc by ID. 2688 * 2689 * Example: itemsarray.sort(this._sortItemsAscending) 2690 * @param {object} a a twitter message object 2691 * @param {object} b a twitter message object 2692 * @return {integer} 2693 * @private 2694 */ 2695 SpazTwit.prototype._sortItemsAscending = function(a,b) { 2696 return (a.id - b.id); 2697 }; 2698 2699 /** 2700 * sorting function for an array of tweets. Desc by ID. 2701 * 2702 * Example: itemsarray.sort(this._sortItemsDescending) 2703 * @param {object} a a twitter message object 2704 * @param {object} b a twitter message object 2705 * @return {integer} 2706 * @private 2707 */ 2708 SpazTwit.prototype._sortItemsDescending = function(a,b) { 2709 return (b.id - a.id); 2710 }; 2711 2712 2713 2714 /** 2715 * sorting function for an array of tweets. Asc by date. 2716 * 2717 * requires SpazCore helpers/datetime.js for httpTimeToInt() 2718 * 2719 * Example: itemsarray.sort(this._sortItemsByDateAsc) 2720 * @param {object} a a twitter message object 2721 * @param {object} b a twitter message object 2722 * @return {integer} 2723 * @private 2724 */ 2725 SpazTwit.prototype._sortItemsByDateAsc = function(a,b) { 2726 var time_a = sc.helpers.httpTimeToInt(a.created_at); 2727 var time_b = sc.helpers.httpTimeToInt(b.created_at); 2728 return (time_a - time_b); 2729 }; 2730 2731 /** 2732 * sorting function for an array of tweets. Desc by date. 2733 * 2734 * requires SpazCore helpers/datetime.js for httpTimeToInt() 2735 * 2736 * Example: itemsarray.sort(this._sortItemsByDateDesc) 2737 * @param {object} a a twitter message object 2738 * @param {object} b a twitter message object 2739 * @return {integer} 2740 * @private 2741 */ 2742 SpazTwit.prototype._sortItemsByDateDesc = function(a,b) { 2743 var time_a = sc.helpers.httpTimeToInt(a.created_at); 2744 var time_b = sc.helpers.httpTimeToInt(b.created_at); 2745 return (time_b - time_a); 2746 }; 2747 2748 2749 /** 2750 * this takes an array of messages and returns one with any duplicates removed 2751 * 2752 * This is based on the jQuery.unique() method 2753 * 2754 * @param {array} array an array of Twitter message objects 2755 * @return {array} 2756 */ 2757 SpazTwit.prototype.removeDuplicates = function(arr) { 2758 2759 var ret = [], done = {}, length = arr.length; 2760 2761 try { 2762 for ( var i = 0; i < length; i++ ) { 2763 var id = arr[i].id; 2764 2765 if ( !done[ id ] ) { 2766 done[ id ] = true; 2767 ret.push( arr[ i ] ); 2768 } else { 2769 sc.helpers.dump("removing dupe " + arr[i].id + ', "'+arr[i].text+'"'); 2770 } 2771 } 2772 2773 } catch( e ) { 2774 sc.helpers.dump(e.name + ":" + e.message); 2775 ret = arr; 2776 } 2777 return ret; 2778 2779 }; 2780 2781 2782 /** 2783 * removes extra elements from a timeline array. 2784 * @param {array} items the timeline array 2785 * @param {integer} max the max # of items we should have 2786 * @param {boolean} remove_from_top whether or not to remove extra items from the top. default is FALSE 2787 */ 2788 SpazTwit.prototype.removeExtraElements = function(items, max, remove_from_top) { 2789 2790 if (!remove_from_top) { 2791 remove_from_top = false; 2792 } 2793 2794 var diff = items.length - max; 2795 if (diff > 0) { 2796 2797 if (!remove_from_top) { 2798 sc.helpers.dump("array length is " + items.length + " > " + max + "; removing last " + diff + " entries"); 2799 items.splice(diff * -1, diff); 2800 } else { 2801 sc.helpers.dump("array length is " + items.length + " > " + max + "; removing first " + diff + " entries"); 2802 items.splice(0, diff); 2803 } 2804 } 2805 2806 return items; 2807 }; 2808 2809 2810 /** 2811 * gets the saved searches the authenticating user has 2812 */ 2813 SpazTwit.prototype.getSavedSearches = function(onSuccess, onFailure) { 2814 var url = this.getAPIURL('saved_searches'); 2815 2816 var opts = { 2817 'url':url, 2818 'success_event_type':'new_saved_searches_data', 2819 'failure_event_type':'error_saved_searches_data', 2820 'success_callback':onSuccess, 2821 'failure_callback':onFailure, 2822 'method':'GET' 2823 }; 2824 2825 /* 2826 Perform a request and get true or false back 2827 */ 2828 var xhr = this._callMethod(opts); 2829 }; 2830 2831 /** 2832 * Saves the search query to the Twitter servers 2833 * 2834 * @param {String} search_query 2835 */ 2836 SpazTwit.prototype.addSavedSearch = function(search_query, onSuccess, onFailure) { 2837 var url = this.getAPIURL('saved_searches_create'); 2838 2839 var opts = { 2840 'url':url, 2841 'success_event_type':'create_saved_search_succeeded', 2842 'failure_event_type':'create_saved_search_failed', 2843 'success_callback':onSuccess, 2844 'failure_callback':onFailure, 2845 'data':{'query':search_query}, 2846 'method':'POST' 2847 }; 2848 2849 /* 2850 Perform a request and get true or false back 2851 */ 2852 var xhr = this._callMethod(opts); 2853 2854 }; 2855 2856 /** 2857 * Delete the saved search corresponding to the given ID 2858 * 2859 * @param {String} search_id Note that this is converted to a string via search_id.toString() 2860 */ 2861 SpazTwit.prototype.removeSavedSearch = function(search_id, onSuccess, onFailure) { 2862 var url = this.getAPIURL('saved_searches_destroy', search_id.toString()); 2863 2864 var opts = { 2865 'url':url, 2866 'success_event_type':'destroy_saved_search_succeeded', 2867 'failure_event_type':'destroy_saved_search_failed', 2868 'success_callback':onSuccess, 2869 'failure_callback':onFailure, 2870 'data':{'id':search_id}, 2871 'method':'POST' 2872 }; 2873 2874 sch.debug('opts for removeSavedSearch'); 2875 sch.debug(opts); 2876 2877 /* 2878 Perform a request and get true or false back 2879 */ 2880 var xhr = this._callMethod(opts); 2881 2882 }; 2883 2884 2885 2886 2887 /** 2888 * retrieves the list of lists 2889 */ 2890 SpazTwit.prototype.getLists = function(user, onSuccess, onFailure) { 2891 if (!user && !this.username) { 2892 return; 2893 } else if (!user) { 2894 user = this.username; 2895 } 2896 2897 var url = this.getAPIURL('lists', { 2898 'user':user 2899 }); 2900 2901 var opts = { 2902 'url':url, 2903 'success_event_type':'get_lists_succeeded', 2904 'failure_event_type':'get_lists_failed', 2905 'success_callback':onSuccess, 2906 'failure_callback':onFailure, 2907 'method':'GET' 2908 }; 2909 2910 var xhr = this._callMethod(opts); 2911 }; 2912 2913 2914 2915 2916 /** 2917 * general processor for user lists data 2918 * @private 2919 */ 2920 SpazTwit.prototype._processUserLists = function(section_name, ret_items, opts, processing_opts) { 2921 2922 if (!processing_opts) { processing_opts = {}; } 2923 2924 if (ret_items.length > 0){ 2925 /* 2926 we process each item, adding some attributes and generally making it cool 2927 */ 2928 for (var k=0; k<ret_items.length; k++) { 2929 ret_items[k] = this._processList(ret_items[k], section_name); 2930 sch.dump(ret_items[k]); 2931 } 2932 2933 /* 2934 sort items 2935 */ 2936 ret_items.sort(this._sortItemsAscending); 2937 2938 // set lastid 2939 var lastid = ret_items[ret_items.length-1].id; 2940 this.data[section_name].lastid = lastid; 2941 sc.helpers.dump('this.data['+section_name+'].lastid:'+this.data[section_name].lastid); 2942 2943 // add new items to data.newitems array 2944 this.data[section_name].newitems = ret_items; 2945 2946 this._addToSectionItems(section_name, this.data[section_name].newitems); 2947 2948 if (opts.success_callback) { 2949 opts.success_callback(this.data[section_name].newitems); 2950 } 2951 this.triggerEvent(opts.success_event_type, this.data[section_name].newitems ); 2952 2953 } else { // no new items, but we should fire off success anyway 2954 2955 if (opts.success_callback) { 2956 opts.success_callback(); 2957 } 2958 this.triggerEvent(opts.success_event_type); 2959 } 2960 }; 2961 2962 /** 2963 * This modifies a Twitter user list, adding some properties. All new properties are 2964 * prepended with "SC_" 2965 * 2966 * this executes within the jQuery.each scope, so this === the item 2967 */ 2968 SpazTwit.prototype._processList = function(item, section_name) { 2969 /* 2970 add .SC_retrieved_unixtime 2971 */ 2972 if (!item.SC_retrieved_unixtime) { 2973 item.SC_retrieved_unixtime = sc.helpers.getTimeAsInt(); 2974 } 2975 2976 return item; 2977 }; 2978 2979 2980 /** 2981 * retrieves a given list timeline 2982 * @param {string} list 2983 */ 2984 SpazTwit.prototype.getListInfo = function(list, user, onSuccess, onFailure) { 2985 if (!user && !this.username) { 2986 sch.error('must pass a username or have one set to get list'); 2987 return; 2988 } 2989 2990 user = user || this.username; 2991 2992 var url = this.getAPIURL('lists_list', { 2993 'user':user, 2994 'slug':list 2995 }); 2996 2997 var opts = { 2998 'url':url, 2999 'success_event_type':'get_list_succeeded', 3000 'failure_event_type':'get_list_failed', 3001 'success_callback':onSuccess, 3002 'failure_callback':onFailure, 3003 'method':'GET' 3004 }; 3005 3006 var xhr = this._callMethod(opts); 3007 }; 3008 3009 3010 /** 3011 * retrieves a given list timeline 3012 * @param {string} list 3013 * @param {string} user the user who owns this list 3014 * @param {function} [onSuccess] function to call on success 3015 * @param {function} [onFailure] function to call on failure 3016 */ 3017 SpazTwit.prototype.getListTimeline = function(list, user, onSuccess, onFailure) { 3018 if (!user && !this.username) { 3019 sch.error('must pass a username or have one set to get list'); 3020 return; 3021 } 3022 3023 user = user || this.username; 3024 3025 var url = this.getAPIURL('lists_timeline', { 3026 'user':user, 3027 'slug':list 3028 }); 3029 3030 var opts = { 3031 'url':url, 3032 'success_event_type':'get_list_timeline_succeeded', 3033 'failure_event_type':'get_list_timeline_failed', 3034 'success_callback':onSuccess, 3035 'failure_callback':onFailure, 3036 'method':'GET', 3037 'process_callback':this._processListTimeline, 3038 'processing_opts': { 3039 'user':user, 3040 'slug':list 3041 } 3042 }; 3043 3044 var xhr = this._getTimeline(opts); 3045 }; 3046 3047 3048 SpazTwit.prototype._processListTimeline = function(data, opts, processing_opts) { 3049 if (!processing_opts) { processing_opts = {}; } 3050 3051 var user = processing_opts.user || null; 3052 var slug = processing_opts.slug || null; 3053 3054 var rdata = { 3055 'statuses':data, 3056 'user':user, 3057 'slug':slug 3058 }; 3059 3060 /* 3061 grab the array of items 3062 */ 3063 // jQuery().trigger(finished_event, [ret_items]); 3064 3065 if (opts.success_callback) { 3066 opts.success_callback(rdata); 3067 } 3068 this.triggerEvent(opts.success_event_type, rdata); 3069 }; 3070 3071 /** 3072 * retrieves a given list's members 3073 * @param {string} list 3074 */ 3075 SpazTwit.prototype.getListMembers = function(list, user) { 3076 if (!user && !this.username) { 3077 sch.error('must pass a username or have one set to get list'); 3078 return; 3079 } 3080 3081 user = user || this.username; 3082 3083 var url = this.getAPIURL('lists_members', { 3084 'user':user, 3085 'slug':list 3086 }); 3087 3088 var opts = { 3089 'url':url, 3090 'success_event_type':'get_list_members_succeeded', 3091 'failure_event_type':'get_list_members_failed', 3092 'method':'GET', 3093 'process_callback':this._processListTimeline, 3094 'processing_opts': { 3095 'user':user, 3096 'slug':list 3097 } 3098 }; 3099 3100 var xhr = this._getTimeline(opts); 3101 }; 3102 3103 /** 3104 * create a new list for the authenticated user 3105 * @param {string} list The list name 3106 * @param {string} visibility "public" or "private" 3107 * @param {string} [description] The list description 3108 */ 3109 SpazTwit.prototype.addList = function(list, visibility, description) { 3110 var data = {}; 3111 data['name'] = list; 3112 data['mode'] = visibility; 3113 data['description'] = description; 3114 3115 var url = this.getAPIURL('lists', { 3116 'user': this.username 3117 }); 3118 3119 var opts = { 3120 'url':url, 3121 'success_event_type':'create_list_succeeded', 3122 'failure_event_type':'create_list_failed', 3123 'success_callback':null, 3124 'failure_callback':null, 3125 'data':data 3126 }; 3127 3128 var xhr = this._callMethod(opts); 3129 }; 3130 3131 SpazTwit.prototype.updateList = function(list, name, visibility, description){ 3132 var data = {}; 3133 data['name'] = name; 3134 data['mode'] = visibility; 3135 data['description'] = description; 3136 3137 var url = this.getAPIURL('lists_list', { 3138 'user': this.username, 3139 'slug': list 3140 }); 3141 3142 var opts = { 3143 'url':url, 3144 'username':this.username, 3145 'password':this.password, 3146 'success_event_type':'update_list_succeeded', 3147 'failure_event_type':'update_list_failed', 3148 'data':data 3149 }; 3150 3151 var xhr = this._callMethod(opts); 3152 }; 3153 3154 /** 3155 * delete a list 3156 * @param {string} list The list name 3157 */ 3158 SpazTwit.prototype.removeList = function(list, user) { 3159 3160 if (!user && !this.username) { 3161 sch.error('must pass a username or have one set to remove list'); 3162 return; 3163 } 3164 3165 user = user || this.username; 3166 3167 var url = this.getAPIURL('lists_list', { 3168 'user': user, 3169 'slug':list 3170 }); 3171 3172 var opts = { 3173 'url':url, 3174 'success_event_type':'remove_list_succeeded', 3175 'failure_event_type':'remove_list_failed', 3176 'method':'DELETE' 3177 }; 3178 3179 var xhr = this._callMethod(opts); 3180 }; 3181 3182 /** 3183 * add a user to a list 3184 */ 3185 SpazTwit.prototype.addUserToList = function(user, list, list_user) { 3186 var data = {}; 3187 data['list_id'] = list; 3188 data['id'] = list_user; 3189 3190 3191 if (!user && !this.username) { 3192 sch.error('must pass a username or have one set to add a user to a list'); 3193 return; 3194 } 3195 3196 user = user || this.username; 3197 3198 var url = this.getAPIURL('lists_members', { 3199 'user': user, 3200 'slug': list 3201 }); 3202 3203 var opts = { 3204 'url':url, 3205 'success_event_type':'add_list_user_succeeded', 3206 'failure_event_type':'add_list_user_failed', 3207 'data':data 3208 }; 3209 3210 var xhr = this._callMethod(opts); 3211 }; 3212 3213 /** 3214 * delete a user from a list 3215 */ 3216 SpazTwit.prototype.removeUserFromList = function(user, list, list_user) { 3217 var data = {}; 3218 data['list_id'] = list; 3219 data['id'] = list_user; 3220 3221 3222 if (!user && !this.username) { 3223 sch.error('must pass a username or have one set to remove a user from a list'); 3224 return; 3225 } 3226 3227 user = user || this.username; 3228 3229 var url = this.getAPIURL('lists_members', { 3230 'user': user, 3231 'slug': list 3232 }); 3233 3234 var opts = { 3235 'url':url, 3236 'success_event_type':'create_list_succeeded', 3237 'failure_event_type':'create_list_failed', 3238 'success_event_type':'remove_list_user_succeeded', 3239 'failure_event_type':'remove_list_user_failed', 3240 'data':data, 3241 'method':'DELETE' 3242 }; 3243 3244 var xhr = this._callMethod(opts); 3245 }; 3246 3247 3248 SpazTwit.prototype.listsSubscribedTo = function(user) { 3249 if(!user && !this.username) { 3250 sch.error('must pass a username or have one set to retrieve subscribed lists'); 3251 return false; 3252 } 3253 3254 user = user || this.username; 3255 3256 var url = this.getAPIURL('lists_subscriptions', { 3257 'user': user 3258 }); 3259 3260 var opts = { 3261 'url':url, 3262 'username': this.username, 3263 'password': this.password, 3264 'success_event_type':'get_subscriptions_succeeded', 3265 'failure_event_type':'get_subscriptions_failed' 3266 }; 3267 3268 var xhr = this._callMethod(opts); 3269 }; 3270 3271 SpazTwit.prototype.listMemberships = function(user) { 3272 if(!user && !this.username) { 3273 sch.error('must pass a username or have one set to retrieve list memberships'); 3274 return false; 3275 } 3276 3277 user = user || this.username; 3278 3279 var url = this.getAPIURL('lists_memberships', { 3280 'user': user 3281 }); 3282 3283 var opts = { 3284 'url':url, 3285 'username': this.username, 3286 'password': this.password, 3287 'success_event_type':'get_list_memberships_succeeded', 3288 'failure_event_type':'get_list_memberships_failed' 3289 }; 3290 3291 var xhr = this._callMethod(opts); 3292 }; 3293 3294 SpazTwit.prototype.getListSubscribers = function(list, user){ 3295 if(!user && !this.username) { 3296 sch.error('must pass a username or have one set to retrieve list subscribers'); 3297 return false; 3298 } 3299 3300 user = user || this.username; 3301 3302 var url = this.getAPIURL('lists_subscribers', { 3303 'user': user, 3304 'slug': list 3305 }); 3306 3307 var opts = { 3308 'url':url, 3309 'username': this.username, 3310 'password': this.password, 3311 'success_event_type':'get_list_subscribers_succeeded', 3312 'failure_event_type':'get_list_subscribers_failed', 3313 'method':'GET' 3314 }; 3315 3316 var xhr = this._callMethod(opts); 3317 }; 3318 3319 SpazTwit.prototype.isSubscribed = function(list, list_user, user){ 3320 if(!user && !this.username) { 3321 sch.error('must pass a username or have one set to retrieve list subscribers'); 3322 return false; 3323 } 3324 3325 user = user || this.username; 3326 3327 var url = this.getAPIURL('lists_check_subscriber', { 3328 'user': user, 3329 'slug': list, 3330 'id': list_user 3331 }); 3332 3333 var opts = { 3334 'url':url, 3335 'username': this.username, 3336 'password': this.password, 3337 'success_event_type':'check_list_subscribers_succeeded', 3338 'failure_event_type':'check_list_subscribers_failed', 3339 'method':'GET' 3340 }; 3341 3342 var xhr = this._callMethod(opts); 3343 }; 3344 3345 SpazTwit.prototype.subscribe = function(list, user){ 3346 if(!user && !this.username) { 3347 sch.error('must pass a username or have one set to subscribe to a list'); 3348 return false; 3349 } 3350 3351 user = user || this.username; 3352 3353 var url = this.getAPIURL('lists_subscribers', { 3354 'user': user, 3355 'slug': list 3356 }); 3357 3358 var opts = { 3359 'url':url, 3360 'username': this.username, 3361 'password': this.password, 3362 'success_event_type':'list_subscribe_succeeded', 3363 'failure_event_type':'list_subscribe_failed', 3364 'method':'POST' 3365 }; 3366 3367 var xhr = this._callMethod(opts); 3368 }; 3369 3370 SpazTwit.prototype.unsubscribe = function(list, user){ 3371 if(!user && !this.username) { 3372 sch.error('must pass a username or have one set to unsubscribe'); 3373 return false; 3374 } 3375 3376 user = user || this.username; 3377 3378 var url = this.getAPIURL('lists_subscribers', { 3379 'user': user, 3380 'slug': list, 3381 'id': list_user 3382 }); 3383 3384 var opts = { 3385 'url':url, 3386 'username': this.username, 3387 'password': this.password, 3388 'success_event_type':'list_unsubscribe_succeeded', 3389 'failure_event_type':'list_unsubscribe_failed', 3390 'method':'DELETE' 3391 }; 3392 3393 var xhr = this._callMethod(opts); 3394 }; 3395 3396 SpazTwit.prototype.isMember = function(list, list_user, user){ 3397 if(!user && !this.username) { 3398 sch.error('must pass a username or have one set to retrieve list memberships'); 3399 return false; 3400 } 3401 3402 user = user || this.username; 3403 3404 var url = this.getAPIURL('lists_check_member', { 3405 'user': user, 3406 'slug': list, 3407 'id': list_user 3408 }); 3409 3410 var opts = { 3411 'url':url, 3412 'username': this.username, 3413 'password': this.password, 3414 'success_event_type':'check_list_members_succeeded', 3415 'failure_event_type':'check_list_members_failed', 3416 'method':'GET' 3417 }; 3418 3419 var xhr = this._callMethod(opts); 3420 }; 3421 3422 /* 3423 * Marks a user as a spammer and blocks them 3424 * @param {integer} user_id a user_id (not a screen name!) 3425 * @param {function} onSuccess callback 3426 * @param {function} onFailure callback 3427 */ 3428 SpazTwit.prototype.reportSpam = function(user_id, onSuccess, onFailure) { 3429 var url = this.getAPIURL('report_spam'); 3430 3431 var data = {}; 3432 data['user_id'] = user_id; 3433 3434 var opts = { 3435 'url':url, 3436 'username': this.username, 3437 'password': this.password, 3438 'success_callback': onSuccess, 3439 'failure_callback': onFailure, 3440 'success_event_type':'report_spam_succeeded', 3441 'failure_event_type':'report_spam_failed', 3442 'method':'POST', 3443 'data':data 3444 }; 3445 3446 var xhr = this._callMethod(opts); 3447 }; 3448 3449 3450 3451 SpazTwit.prototype.openUserStream = function(onData, onFailure) { 3452 var that = this; 3453 3454 /* 3455 close existing stream 3456 */ 3457 this.closeUserStream(); 3458 3459 /* 3460 open new stream 3461 */ 3462 this.userstream = new SpazTwitterStream({ 3463 'auth' : this.auth, 3464 'onData' : function(data) { 3465 var item; 3466 data = sch.trim(data); 3467 if (data) { 3468 sch.error('new data:'+data); 3469 item = sch.deJSON(data); 3470 3471 if (item.source && item.user && item.text) { // is "normal" status 3472 item = that._processItem(item, SPAZCORE_SECTION_HOME); 3473 if (onData) { 3474 onData(item); 3475 } 3476 } 3477 3478 if (item.direct_message) { // is DM 3479 item = that._processItem(item.direct_message, SPAZCORE_SECTION_HOME); 3480 if (onData) { 3481 onData(item); 3482 } 3483 } 3484 3485 } 3486 } 3487 }); 3488 this.userstream.connect(); 3489 return this.userstream; 3490 }; 3491 3492 3493 SpazTwit.prototype.closeUserStream = function() { 3494 if (this.userstream) { 3495 sch.error('userstream exist… disconnecting'); 3496 this.userstream.disconnect(); 3497 this.userstream = null; 3498 } 3499 }; 3500 3501 3502 SpazTwit.prototype.userStreamExists = function() { 3503 if (this.userstream) { 3504 return true; 3505 } 3506 return false; 3507 }; 3508 3509 3510 3511 /** 3512 * 3513 */ 3514 SpazTwit.prototype.triggerEvent = function(type, data) { 3515 var target = this.opts.event_target || document; 3516 data = data || null; 3517 3518 sc.helpers.dump('TriggerEvent: target:'+target.toString()+ ' type:'+type+ ' data:'+data); 3519 3520 if (this.opts.event_mode === 'jquery') { 3521 data = [data]; 3522 jQuery(target).trigger(type, data); 3523 } else { 3524 sc.helpers.trigger(type, target, data); 3525 } 3526 3527 }; 3528 3529 /** 3530 * shortcut for SpazTwit if the SpazCore libraries are being used 3531 * 3532 */ 3533 if (sc) { 3534 var scTwit = SpazTwit; 3535 } 3536 3537