Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Rewrite processing-twitter code and example.

  • Loading branch information...
commit 128693806b0a5d04ea1fae3973e8cd53488e9039 1 parent 6ae9ee8
David Humphrey authored
View
111 examples/twitter/index.html
@@ -2,99 +2,84 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Processing.js LiveTwitter Demo</title>
- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript" ></script>
<script src="../../processing.js" type="text/javascript"></script>
- <script src="livetwitter.js" type="text/javascript"></script>
+ <script src="processing-twitter.js" type="text/javascript"></script>
</head>
<body>
<script id="sketch" type="application/processing">
// Dynamic list of Tweet objects
- HashMap tweets;
+ HashMap uniqueTweets;
- // A class representing a Tweet
+ // A class representing a Tweet. Tweet objects are created in JS
+ // and automatically placed in the tweets ArrayList (created in JS)
class Tweet {
- String id;
- String profileName;
- PImage profileImage;
- String text;
- Date time;
- int randomPosX;
- int randomPosY;
-
- Tweet(String aId,
- String aProfileName,
- String aProfileImageURL,
- String aText,
- Date aTime) {
- id = aId;
- profileName = aProfileName;
- profileImage = requestImage(aProfileImageURL);
- text = aText;
- time = aTime;
-
- // Draw profile images to the same place each frame
- randomPosX = random(0, width);
- randomPosY = random(0, height);
- }
+ public String id;
+ public String profileName;
+ public String profileImageUrl;
+ public PImage profileImage; // needs to be loaded manually
+ public String text;
+ public Date time;
+ public int randomPosX; // needs to be set -- randomPosX = random(0, width);
+ public int randomPosY; // needs to be set -- randomPosY = random(0, height);
}
void setup() {
size(1024, 1024);
- tweets = new HashMap();
+
+ uniqueTweets = new HashMap();
+ frameRate(1);
+
+ // Start a live feed loading, results will be available in a global
+ // ArrayList called tweets.
+ loadTweets('bacon');
+
+ // You can also pass geolocation data to limit tweets to a geographic area
+ // loadTweets('class', '43.7496,-79.4886,1km'); // York University
}
void draw() {
- background(255);
+ background(121);
- if (tweets.size() == 0) {
- return;
+ Tweet tweet;
+
+ // Store unique tweets that have come in
+ for (int i = 0; i < tweets.size(); i++) {
+ tweet = (tweet)tweets.get(i);
+ addTweet(tweet);
}
+ tweets.clear();
- Tweet tweet;
- Iterator it = tweets.entrySet().iterator();
+ // Loop through unique tweets we've stored
+ Iterator it = uniqueTweets.entrySet().iterator();
object elem;
-
+ int w, r;
while (it.hasNext()) {
elem = it.next();
tweet = (Tweet)elem.getValue();
+
+ // Draw the profile image
image(tweet.profileImage, tweet.randomPosX, tweet.randomPosY);
+
+ // Print the tweet to the Processing debug console
+ println(tweet.text);
}
}
- // Called from external JS, passes new Tweet object
+ // Store unique tweets (keyed on tweet.id), loading image data
void addTweet(Tweet tweet) {
- // Only store each tweet once (twitter sends them over and over)
- if (!tweets.containsKey(tweet.id)) {
- tweets.put(tweet.id, tweet);
- }
- }
+ if (!uniqueTweets.containsKey(tweet.id)) {
+ // Load a PImage of the profile pic
+ tweet.profileImage = requestImage(tweet.profileImageUrl);
- // Called from external JS, removes count tweets.
- void removeTweets(count) {
- while(count-- >= 0) {
- tweets.remove(tweets.size() - 1);
- }
- }
+ // Draw profile images to the same place each frame
+ tweet.randomPosX = random(0, width);
+ tweet.randomPosY = random(0, height);
- void clearTweets() {
- background(255);
- tweets.clear();
+ // Store the tweet in our list of unique tweets
+ uniqueTweets.put(tweet.id, tweet);
+ }
}
</script>
-
- <div>
- <button onclick="twitter.start();">Start</button>
- <button onclick="twitter.stop();">Stop</button>
- <button onclick="twitter.clear();">Clear</button>
- </div>
-
<canvas id="tweets"></canvas>
-
- <script type="text/javascript">
- var p = new Processing("tweets", document.getElementById("sketch").text);
-// $('#twitterSearch').liveTwitter('chicago', {limit: 10, rate: 5000, rpp: 10, geocode: '41.7897563,-87.5997711,1km'});
- liveTwitter('toronto', {limit: 50, rate: 5000, rpp: 50}, p);
-
- </script>
</body>
</html>
View
36 examples/twitter/index2.html
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
- <title>Processing.js LiveTwitter Demo</title>
- <script src="../../processing.js" type="text/javascript"></script>
- <script src="processing-twitter2.js" type="text/javascript"></script>
-</head>
-<body>
- <script id="sketch" type="application/processing">
- int lineHeight = 3;
-
- void setup() {
- size(1024, 1024);
- strokeWeight(lineHeight);
- loadTweets('bacon');
- }
-
- void draw() {
- background(121);
- for (int i = 0; i < tweets.size(); i++) {
- Tweet tweet = (tweet)tweets.get(i);
- int w = map(tweet.text.length, 0, 140, 0, width);
- line(0, i * lineHeight, w, i * lineHeight);
- }
- }
- </script>
- <canvas id="tweets"></canvas>
-
- <script type="text/javascript">
-// var p = new Processing("tweets", document.getElementById("sketch").text);
-// $('#twitterSearch').liveTwitter('chicago', {limit: 10, rate: 5000, rpp: 10, geocode: '41.7897563,-87.5997711,1km'});
-// liveTwitter('toronto', {limit: 50, rate: 5000, rpp: 50}, p);
-
- </script>
-</body>
-</html>
View
362 examples/twitter/livetwitter.js
@@ -1,362 +0,0 @@
-/*
- * Based on jQuery LiveTwitter 1.7.2 https://github.com/elektronaut/jquery.livetwitter
- * - Live updating Twitter plugin for jQuery
- *
- * Copyright (c) 2009-2011 Inge Jørgensen (elektronaut.no)
- * Licensed under the MIT license (MIT-LICENSE.txt)
- *
- * Modified by David Humphrey (@humphd) for use with Processing.js
- */
-
-/*
- * Usage example:
- * liveTwitter('bacon', {limit: 10, rate: 15000}, processingInstance);
- */
-
-
-(function ($) {
-
- // Extend jQuery with a reverse function if it isn't already defined
- if (!$.fn.reverse) {
- $.fn.reverse = function () {
- return this.pushStack(this.get().reverse(), arguments);
- };
- }
-
- this.liveTwitter = function(query, options, processing, callback) {
-
- // Put the tweets on the global so Processing can get them too.
- window.tweets = [];
-
- $(this).each(function () {
- var settings = {};
-
- // Does this.twitter already exist? Let's just change the settings.
- if (this.twitter) {
- settings = $.extend(this.twitter.settings, options);
- this.twitter.settings = settings;
- if (query) {
- this.twitter.query = query;
- }
- if (this.twitter.interval) {
- this.twitter.refresh();
- }
- if (callback) {
- this.twitter.callback = callback;
- }
- // ..if not, let's initialize.
- } else {
-
- // These are the default settings.
- settings = $.extend({
- mode: 'search', // Mode, valid options are: 'search', 'user_timeline', 'list', 'home_timeline'
- rate: 15000, // Refresh rate in ms
- limit: 10, // Limit number of results
- imageSize: 24, // Size of image in pixels
- refresh: true,
- timeLinks: true,
- retweets: false,
- service: false,
- localization: {
- seconds: 'seconds ago',
- minute: 'a minute ago',
- minutes: 'minutes ago',
- hour: 'an hour ago',
- hours: 'hours ago',
- day: 'a day ago',
- days: 'days ago'
- },
- }, options);
-
- // showAuthor should default to true unless mode is 'user_timeline'.
- if (typeof settings.showAuthor === "undefined") {
- settings.showAuthor = (settings.mode === 'user_timeline') ? false : true;
- }
-
- // Set up a dummy function for the Twitter API callback.
- if (!window.twitter_callback) {
- window.twitter_callback = function () {
- return true;
- };
- }
-
- this.twitter = {
- settings: settings,
- query: query,
- interval: false,
- container: this,
- lastTimeStamp: 0,
- callback: callback,
-
- // Convert the time stamp to a more human readable format
- relativeTime: function (timeString) {
- var parsedDate = Date.parse(timeString);
- var delta = (Date.parse(Date()) - parsedDate) / 1000;
- var r = '';
- if (delta < 60) {
- r = delta + " " + settings.localization.seconds;
- } else if (delta < 120) {
- r = settings.localization.minute;
- } else if (delta < (45 * 60)) {
- r = (parseInt(delta / 60, 10)).toString() + " " + settings.localization.minutes;
- } else if (delta < (90 * 60)) {
- r = settings.localization.hour;
- } else if (delta < (24 * 60 * 60)) {
- r = '' + (parseInt(delta / 3600, 10)).toString() + " " + settings.localization.hours;
- } else if (delta < (48 * 60 * 60)) {
- r = settings.localization.day;
- } else {
- r = (parseInt(delta / 86400, 10)).toString() + " " + settings.localization.days;
- }
- return r;
- },
-
- // Update the relative timestamps
- updateTimestamps: function () {
- var twitter = this;
- $(twitter.container).find('span.time').each(function () {
- var time_element = twitter.settings.timeLinks ? $(this).find('a') : $(this);
- time_element.html(twitter.relativeTime(this.timeStamp));
- });
- },
-
- apiURL: function () {
- var params = {};
-
- var protocol = (window.location.protocol === 'https:') ? 'https:' : 'http:';
- var baseURL = 'api.twitter.com/1/';
- var endpoint = '';
-
- // Override for Twitter-compatible APIs like Status.net
- if (this.settings.service) {
- baseURL = this.settings.service + '/api/';
- }
-
- // Search mode
- if (this.settings.mode === 'search') {
- baseURL = (this.settings.service) ? this.settings.service + '/api/' : 'search.twitter.com/';
- endpoint = 'search';
- params = {
- q: (this.query && this.query !== '') ? this.query : null,
- geocode: this.settings.geocode,
- lang: this.settings.lang,
- rpp: (this.settings.rpp) ? this.settings.rpp : this.settings.limit
- };
-
- // User/home timeline mode
- } else if (this.settings.mode === 'user_timeline' || this.settings.mode === 'home_timeline') {
- endpoint = 'statuses/' + this.settings.mode + '/' + encodeURIComponent(this.query);
- params = {
- count: this.settings.limit,
- include_rts: (this.settings.mode === 'user_timeline' && this.settings.retweets) ? '1' : null
- };
-
- // Favorites mode
- } else if (this.settings.mode === 'favorites') {
- endpoint = 'favorites';
- params = {
- id: encodeURIComponent(this.query)
- };
-
- // List mode
- } else if (this.settings.mode === 'list') {
- endpoint = encodeURIComponent(this.query.user) +
- '/lists/' +
- encodeURIComponent(this.query.list) +
- '/statuses';
- params = {
- per_page: this.settings.limit
- };
- }
-
- // Construct the query string
- var queryString = [];
- for (var param in params) {
- if (params.hasOwnProperty(param) && typeof params[param] !== 'undefined' && params[param] !== null) {
- queryString[queryString.length] = param + '=' + encodeURIComponent(params[param]);
- }
- }
- queryString = queryString.join("&");
-
- // Return the full URL
- return protocol + '//' + baseURL + endpoint + '.json?' + queryString + '&callback=?';
- },
-
- // The different APIs will format the results slightly different,
- // so this method normalizes the tweet object.
- parseTweet: function (json) {
- var tweet = {
- id: (json.id_str) ? json.id_str : json.id,
- text: json.text,
- created_at: json.created_at
- };
-
- // Search/regular API differences
- if (this.settings.mode === 'search') {
- tweet = $.extend(tweet, {
- screen_name: json.from_user,
- profile_image_url: json.profile_image_url
- });
- } else {
- tweet = $.extend(tweet, {
- screen_name: json.user.screen_name,
- profile_image_url: json.user.profile_image_url,
- created_at: json.created_at.replace(/^(\w+)\s(\w+)\s(\d+)(.*)(\s\d+)$/, "$1, $3 $2$5$4") // Fix for IE
- });
- }
-
- // Twitter/Status.net
- if (this.settings.service) {
- tweet = $.extend(tweet, {
- url: 'http://' + this.settings.service + '/notice/' + tweet.id,
- profile_url: 'http://' + this.settings.service + '/' + json.from_user
- });
- if (window.location.protocol === 'https:') {
- tweet.profile_image_url = tweet.profile_image_url.replace('http:', 'https:');
- }
-
- } else {
- tweet = $.extend(tweet, {
- url: 'http://twitter.com/#!/' + tweet.screen_name + '/status/' + tweet.id,
- profile_url: 'http://twitter.com/#!/' + tweet.screen_name
- });
- // Someday, Twitter will add HTTPS support to twimg.com, but until then
- // we have to rewrite the profile image urls to the old Amazon S3 urls.
- if (window.location.protocol === 'https:') {
- var matches = tweet.profile_image_url.match(/http[s]?:\/\/a[0-9]\.twimg\.com\/(\w+)\/(\w+)\/(.*?)\.(\w+)/i);
- if (matches) {
- tweet.profile_image_url = "https://s3.amazonaws.com/twitter_production/" + matches[1] + "/" + matches[2] + "/" + matches[3] + "." + matches[4];
- } else {
- // Failsafe, if profile image url does not match the pattern above
- // then, at least, change the protocol to HTTPS.
- // The image may not load, but at least the page stays secure.
- tweet.profile_image_url = tweet.profile_image_url.replace('http:', 'https:');
- }
- }
- }
-
- return tweet;
- },
-
- // Parses the tweet body, linking URLs, #hashtags and @usernames.
- parseText: function (text) {
- // URLs
- text = text.replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+/g, function (m) {
- return '<a href="' + m + '" rel="external">' + m + '</a>';
- });
-
- // Twitter
- if (!this.settings.service) {
- // @usernames
- text = text.replace(/@[A-Za-z0-9_]+/g, function (u) {
- return '<a href="http://twitter.com/#!/' + u.replace(/^@/, '') + '" rel="external">' + u + '</a>';
- });
- // #hashtags
- text = text.replace(/#[A-Za-z0-9_\-]+/g, function (u) {
- return '<a href="http://twitter.com/#!/search?q=' + u.replace(/^#/, '%23') + '" rel="external">' + u + '</a>';
- });
-
- // Other APIs
- } else {
- text = text.replace(/@[A-Za-z0-9_]+/g, function (u) {
- return '<a href="http://' + settings.service + '/' + u.replace(/^@/, '') + '" rel="external">' + u + '</a>';
- });
- text = text.replace(/#[A-Za-z0-9_\-]+/g, function (u) {
- return '<a href="http://' + settings.service + '/search/notice?q?' + u.replace(/^#/, '%23') + '" rel="external">' + u + '</a>';
- });
- }
-
- return text;
- },
-
- // Create an object useable by Processing
- toP5: function (tweet) {
- return new processing.Tweet(
- tweet.id,
- tweet.screen_name,
- tweet.profile_image_url, // will get async loaded
- tweet.text,
- this.relativeTime(tweet.created_at)
- );
- },
-
- // Handle reloading
- refresh: function (initialize) {
- var twitter = this;
- if (twitter.settings.refresh || initialize) {
-
- $.getJSON(twitter.apiURL(), function (json) {
- var newTweets = 0;
-
- // The search and regular APIs differ somewhat
- var results = (twitter.settings.mode === 'search') ? json.results : json;
-
- $(results).reverse().each(function () {
- var tweet = this; // twitter.parseTweet(this);
-
- // Check if tweets should be filtered
- if (!twitter.settings.filter || twitter.settings.filter(this)) {
- // Check if this is actually a new tweet
- if (Date.parse(tweet.created_at) > twitter.lastTimeStamp) {
- processing.addTweet(twitter.toP5(tweet));
-
- // Remember the last timestamp for the next refresh.
- twitter.lastTimeStamp = Date.parse(tweet.created_at);
-
- newTweets++;
- }
- }
- });
-
- // Did we get any new tweets?
- if (newTweets > 0) {
- // Remove old entries exceeding the limit
- var extra = newTweets - twitter.settings.limit;
- if (extra > 0) {
- processing.removeTweets(newTweets - twitter.settings.limit);
- newTweets -= extra;
- }
- }
- });
- }
- },
-
- // Start refreshing
- start: function () {
- var twitter = this;
- if (!this.interval) {
- this.interval = setInterval(function () {
- twitter.refresh();
- }, twitter.settings.rate);
- this.refresh(true);
- }
- },
-
- // Stop refreshing
- stop: function () {
- if (this.interval) {
- clearInterval(this.interval);
- this.interval = false;
- }
- },
-
- // Clear all tweets
- clear: function () {
- processing.clearTweets();
- this.lastTimeStamp = null;
- }
- };
-
- var twitter = this.twitter;
-
- // Update the timestamps in realtime
- this.timeInterval = setInterval(function () {
- twitter.updateTimestamps();
- }, 5000);
-
- this.twitter.start();
- }
- });
- return this;
- };
-})(jQuery);
View
17 examples/twitter/processing-twitter2.js → examples/twitter/processing-twitter.js
@@ -8,15 +8,8 @@
* Modified by David Humphrey (@humphd) for use with Processing.js
*/
-/*
- * Usage example:
- * liveTwitter('bacon', {limit: 10, rate: 15000}, processingInstance);
- */
-
-
(function (document, Processing) {
-
function getScript(success) {
var script = document.createElement('script');
script.src = "http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js";
@@ -29,7 +22,6 @@
done=true;
success(jQuery);
script.onload = script.onreadystatechange = null;
- head.removeChild(script);
}
};
head.appendChild(script);
@@ -42,9 +34,10 @@
Pp.tweets = new Pp.ArrayList();
- Pp.loadTweets = function(query) {
+ Pp.loadTweets = function(query, geocode) {
+ var options = {geocode: geocode};
getScript(function(jQuery) {
- loadTweets$(jQuery, query);
+ loadTweets$(jQuery, query, options);
});
};
@@ -78,8 +71,8 @@
// These are the default settings.
settings = $.extend({
mode: 'search', // Mode, valid options are: 'search', 'user_timeline', 'list', 'home_timeline'
- rate: 15000, // Refresh rate in ms
- limit: 10, // Limit number of results
+ rate: 3000, // Refresh rate in ms
+ limit: 100, // Limit number of results
imageSize: 24, // Size of image in pixels
refresh: true,
timeLinks: true,
Please sign in to comment.
Something went wrong with that request. Please try again.