<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss version="2.0"><channel><title>teebes.com</title><link>http://teebes.com/blog/</link><description>teebes.com's blog</description><language>en-us</language><lastBuildDate>Wed, 10 Mar 2010 23:58:11 -0000</lastBuildDate><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/teebescom" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="teebescom" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><title>Introducing Git-Goggles</title><link>http://teebes.com/blog/22/introducing-git-goggles</link><description>&lt;p&gt;&lt;strong&gt;&lt;a href="http://github.com/nowells/git-goggles" target="_blank"&gt;Git-Goggles&lt;/a&gt;&lt;/strong&gt;: &lt;em&gt;A series of Git utilities to streamline working with remote branches and reviewing code.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The development team I am in at PBS has adopted a new methodology over the last few months that includes using &lt;a href="http://git-scm.com/" target="_blank"&gt;Git&lt;/a&gt;, frequent (2 weeks) release cycles and rigorous code reviews. We spend a reasonable amount of time reviewing every iteration since every line of code gets critiqued by at least 2 members of the team before it gets pushed to production.&lt;/p&gt;
&lt;p&gt;We also have a lot of branches. Since git makes branching so inexpensive and easy to manage, we use them to group tasks by functional area. We generally have in between 10 to 15 branches at once that regularly get merged into our master repo, which is the production code.&lt;/p&gt;
&lt;p&gt;What is tricky in all this is keeping track of what code still needs to be reviewed versus what is good to go. We wanted a way to get both a nice visual, tabulated view of the state of our branches along with their status from a code review standpoint. It was to scratch that itch that our team lead, &lt;a href="http://twitter.com/nowells" target="_blank"&gt;Nowell Strite&lt;/a&gt;, created git-goggles. The project was open sourced last week under the MIT license.&lt;/p&gt;
&lt;p&gt;Here's a demonstration of git-goggles using a basic scenario that utilizes this type of workflow. The goal here is to push a new piece of code to a production repository, while making sure that it has been reviewed and that the branches are merged in.&lt;/p&gt;
&lt;p&gt;First I create a new repo, a test file, and push it to github.&lt;/p&gt;
&lt;!--more--&gt;
&lt;pre class="highlight"&gt;$ mkdir demo
$ cd demo
$ git init
$ echo "file1" &amp;gt; file1
$ git add file1
$ git commit -m "initial"
$ git remote add origin git@github.com:teebes/demo.git
$ git push origin master
$ git push origin master:refs/heads/staging
$ git branch --track staging origin/staging
$ git co staging
$ git goggles
&lt;/pre&gt;
&lt;p&gt;&lt;img class="shell_img" src="/static/mediacenter/git-workflow-1.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I then introduce a code change and commit. The table shows that 1 commit has not been reviewed, and that I need to push the change to origin.&lt;/p&gt;
&lt;pre&gt;$ echo "file2" &amp;gt; file2
$ git add file2
$ git commit -m "added second file"
$ git goggles
&lt;/pre&gt;
&lt;p&gt;&lt;img class="shell_img" src="/static/mediacenter/git-workflow-2.png" alt="" /&gt;&lt;/p&gt;
&lt;pre&gt;$ git push
$ git goggles
&lt;/pre&gt;
&lt;p&gt;After pushing, the 'Push' column now has a green checkmark instead of the red X.&lt;/p&gt;
&lt;p&gt;&lt;img class="shell_img" src="/static/mediacenter/git-workflow-3.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I start the code review, which brings up a diff between the development branch (staging) and the production branch (master). Once the review is complete, running git-goggles again shows that there is now no unreviewed code. Also, the status of the staging branch has changed to 'MERGE', which indicates that those changes still need to be merged back into the production branch.&lt;/p&gt;
&lt;pre class="highlight"&gt;$ git goggles codereview
diff --git a/file2 b/file2
new file mode 100644
index 0000000..6c493ff
--- /dev/null
+++ b/file2
@@ -0,0 +1 @@
+file2

[ &amp;hellip; code changes happen &amp;hellip;]

$ git goggles codereview complete
Counting objects: 1, done.
Writing objects: 100% (1/1), 194 bytes, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.com:teebes/demo.git
 * [new tag]         codereview--staging -&amp;gt; codereview--staging
Created tag codereview--staging
Already on 'staging'
Switched back to staging branch.

$ git goggles
&lt;/pre&gt;
&lt;p&gt;&lt;img class="shell_img" src="/static/mediacenter/git-workflow-4.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Now staging is ready for production and therefore gets merged back into master.&lt;/p&gt;
&lt;pre class="highlight"&gt;$ git co master
$ git merge staging
$ git push
&lt;/pre&gt;
&lt;p&gt;The status column now reads 'DONE', showing that no further action needs to be taken.&lt;/p&gt;
&lt;p&gt;&lt;img class="shell_img" src="/static/mediacenter/git-workflow-5.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Any subsequent pushed changes to the staging branch will change its status to 'REVIEW', and the codereview command will bring up the diff since the last review.&lt;/p&gt;
</description><guid>http://teebes.com/blog/22/introducing-git-goggles</guid></item><item><title>New year&amp;#39;s Python meme</title><link>http://teebes.com/blog/21/new-years-python-meme</link><description>&lt;p&gt;&lt;img style="border: 1px solid black; float: right; margin: 0 0 5px 5px;" src="http://teebes.com/static/mediacenter/python.jpg" alt="python" width="150" align="right" /&gt;Following in &lt;a href="http://tarekziade.wordpress.com/2009/12/28/new-years-python-meme/" target="_blank"&gt;Tyrek Ziade's footsteps&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. What&amp;rsquo;s the coolest Python application, framework or library you have discovered in 2009 ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I hate to be so unoriginal... Django. I had been mostly using Zope 2/3 and &lt;a href="http://grok.zope.org/" target="_blank"&gt;Grok&lt;/a&gt; for web development (the latter is actually pretty cool).&amp;nbsp; But starting to work for PBS I got my first taste of Django and I was able to really completely dive in. Incredibly happy with it, I really appreciate all of the core functionality as well as a lot of contrib (not so much comments though, seriously what happened there?) and I feel like I can just get up and running much more quickly than before with minimal aggravation.&lt;/p&gt;
&lt;p&gt;Just because Django is such a boring answer, I'll mention a few other really cool Python libs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://bitbucket.org/jespern/django-piston/" target="_blank"&gt;Piston&lt;/a&gt; (been using that one a whole lot, creates RESTful APIs)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://pygments.org/" target="_blank"&gt;Pygments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://sphinx.pocoo.org/" target="_blank"&gt;Sphinx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://feedparser.org/" target="_blank"&gt;feedparser.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/ask/celery" target="_blank"&gt;Celery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!--more--&gt;
&lt;p&gt;&lt;strong&gt;2. What new programming technique did you learn in 2009 ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;High concurrency programming. I just made that name up but basically what I mean are programs where a lot of concurent requests come in and it becomes important to utilize asynchronous, evented and non-blocking I/O wherever possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. What&amp;rsquo;s the name of the open source project you contributed the most in 2009 ? What did you do ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Well... basically none. I'm such a user! I submitted a couple patches to Django but one got the shot down and the other is in limbo, and other than that the only open source projects I've contributed to are my own, on which I am the only comitter anyway. I'm hoping to do better here in 2010 but it's difficult sometimes to contribute back because a lot of your suggestions have ramifications down the road you couldn't quite grok when you suggested them and then you get (rightfull) shot down. As frameworks evolve the problems left to fix become less and less trivial and it's tough to know where to start sometimes.&lt;/p&gt;
&lt;p&gt;But so the actual correct answer to the question would be &lt;a href="http://github.com/teebes/stark" target="_blank"&gt;Stark&lt;/a&gt;, my web-based visual open &lt;a href="http://en.wikipedia.org/wiki/MUD" target="_blank"&gt;MUD&lt;/a&gt; project. It's basically a web-based mud that has (non-flash) visual components to it and is open to the world via a sorta-RESTful API. The front-end is in &lt;a href="http://processingjs.org/" target="_blank"&gt;processing.js&lt;/a&gt; + &lt;a href="http://jquery.com/" target="_blank"&gt;jQuery&lt;/a&gt; (yes, I owe a lot to John Resig...) and the back-end is Django + Piston. This started as a weekend project to learn processing.js and has turned into a pretty big animal at this point...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. What was the Python blog or website you read the most in 2009 ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I have a Twitter list with some of the web development people I follow: &lt;a href="http://twitter.com/teebesz/dev" target="_blank"&gt;http://twitter.com/teebesz/dev&lt;/a&gt;. I definitely keep up with that...&lt;/p&gt;
&lt;p&gt;I also have a google reader feed where I follow a wide range of tech people from Simon Willison to Paul Graham.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. What are the three top things you want to learn in 2010 ?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to build things people actually want :)&lt;/li&gt;
&lt;li&gt;The world outside Django: I think it's easy once you enter the Django world to become pretty consumed by its greater universe, and I need to remember to keep looking at other cool pythonic things as well as other progamming languages (Erlang &amp;amp; Ruby might be cool to learn...)&lt;/li&gt;
&lt;li&gt;Become good at automated testing... tough but essential.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy new year's everyone!&lt;/p&gt;
</description><guid>http://teebes.com/blog/21/new-years-python-meme</guid></item><item><title>An easy way to forward your django feed to feedburner</title><link>http://teebes.com/blog/20/an-easy-way-to-forward-your-django-feed-to-feedburner</link><description>&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/feedburner.png" alt="feedburner traffic" width="125" align="right" /&gt;If you are using Django's &lt;a href="http://docs.djangoproject.com/en/dev/ref/contrib/syndication/#ref-contrib-syndication"&gt; syndication framework&lt;/a&gt; and want an easy way to track subscribers, &lt;a href="http://feedburner.google.com/"&gt;Feedburner&lt;/a&gt; is probably your best bet. Of course if you already have a substantial amount of subscribers you'll want to forward the old feed to the new feed so as not to lose them.&lt;/p&gt;
&lt;p&gt;I did this last week for this site and it worked very well, so if anyone else is trying to accomplish the same thing here is how I went about it:&lt;/p&gt;
&lt;p&gt;First, I added the feedburner URL in settings.py (replace the URL with your own):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;FEEDBURNER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;http://feeds.feedburner.com/teebescom/&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, in urls.py I changed the view being called for the feed from the standard syndication view to a custom wrapper around that view.&lt;/p&gt;
&lt;p&gt;So before it was:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^(?P(rss|atom))/$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;django.contrib.syndication.views.feed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;feed_dict&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And I changed it to:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^(?P(rss|atom))/$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;blog.views.rss.feedburner&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;feed_dict&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Last, I added a rss.py file in the blog.views package with the following contents:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.syndication.views&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;feedburner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;feed_dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;FEEDBURNER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;FEEDBURNER&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;FEEDBURNER&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;META&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;HTTP_USER_AGENT&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;FeedBurner&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;feed_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FEEDBURNER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And that's it! that should be all you need.&lt;/p&gt;
&lt;p&gt;Post a comment if you have any questions.&lt;/p&gt;
</description><guid>http://teebes.com/blog/20/an-easy-way-to-forward-your-django-feed-to-feedburner</guid></item><item><title>Playing with Google&amp;#39;s Closure JS library</title><link>http://teebes.com/blog/19/playing-with-googles-closure-js-library</link><description>&lt;img align='right' src='http://teebes.com/static/mediacenter/google-closure.png' alt='google closure tools image'/&gt;

&lt;p&gt;On Thursday, &lt;a href="http://googlecode.blogspot.com/2009/11/introducing-closure-tools.html"&gt;Google open-sourced&lt;/a&gt; several of its JavaScript building blocks: a compiler, a library and template extension. They've been released together as the &lt;a href="http://code.google.com/closure/"&gt;Closure&lt;/a&gt; toolkit.&lt;/p&gt;

&lt;p&gt;I was particularly interested in its JavaScript library, specifically the graphics engine that implements the W3C's Scalable Vector Graphics as well as the event listeners. So I threw together a very small interactive app to play with the parts I'm curious about:&lt;/p&gt;
&lt;!--more--&gt;
    Use your up / down / right / left arrow keys to move the ball:
    
    &lt;div id="shapes"&gt;&lt;/div&gt;
  
    &lt;script type="text/javascript"&gt;
    var graphics = goog.graphics.createGraphics(200, 150);

    // define the colors for the squares and the dot
    var square_fill = new goog.graphics.SolidFill('yellow');
    var square_stroke = new goog.graphics.Stroke(2, 'green');
    var dot_fill = new goog.graphics.SolidFill('blue');
    var dot_stroke = new goog.graphics.Stroke(1, 'black');

    // the dot's initial position
    var dot = {x: 1, y: 1};

    // properties    
    var size = 40;
    var margin = 5;
    var width = size - margin;
    var num_rows = 3;
    var num_cols = 4;

    // draw the squares
    for (var x = 0; x &lt; num_cols; x++) {
        for (var y = 0; y &lt; num_rows; y++) {
            graphics.drawRect(margin + x * size, margin + y * size, width, width, square_stroke, square_fill);
        }
    }

    // draw the dot
    dot['graphic'] = graphics.drawEllipse(margin + dot['x'] * size + width / 2, margin + dot['y'] * size + width / 2, width / 4, width / 4, dot_stroke, dot_fill);
    
    // call if the dot's position changes
    redraw_dot = function() {
        dot['graphic'].setCenter(margin + dot['x'] * size + width / 2, margin + dot['y'] * size + width / 2);
    }
    
    // key event handler
    var key_handler = new goog.events.KeyHandler(document);
    var key_event = function (e) {
        if (e.keyCode == goog.events.KeyCodes.UP &amp;&amp; dot['y'] &gt; 0) {
            dot['y'] -= 1;
        } else if (e.keyCode == goog.events.KeyCodes.RIGHT &amp;&amp; dot['x'] &lt;= num_cols - 2) {
            dot['x'] += 1;
        } else if (e.keyCode == goog.events.KeyCodes.DOWN &amp;&amp; dot['y'] &lt;= num_rows - 2) {
            dot['y'] += 1;
        } else if (e.keyCode == goog.events.KeyCodes.LEFT &amp;&amp; dot['x'] &gt; 0) {
            dot['x'] -= 1;
        }
        redraw_dot();
    }
    
    // put everything together
    goog.events.listen(key_handler, 'key', key_event);
    graphics.render(document.getElementById('shapes'));
  &lt;/script&gt;

&lt;p&gt;If you're reading this via rss, or if you want to see it in its own window, &lt;a href="http://teebes.com/closure/"&gt;click here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This does something similiar to what I do on &lt;a href="http://stark.teebes.com/"&gt;Stark&lt;/a&gt;, my visual mud project [&lt;a href="http://github.com/teebes/stark/"&gt;github page&lt;/a&gt;] that uses the &lt;a href="http://processingjs.org/"&gt;Processing&lt;/a&gt; JS library and the HTML 5 &amp;lt;canvas&amp;gt; element, but instead of creating a draw function that is executed x amounts per seconds, you just call a draw function and assign it to a variable for future interactions. Here is the source code for this example (you can also just look at the current page's source):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE HTML&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Playing with Closure graphics &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; events&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;text/css&amp;#39;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
    
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/static/js/closure-library-read-only/closure/goog/base.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;goog.graphics&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;goog.events&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;goog.events.KeyCodes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;goog.events.KeyHandler&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    Use your up / down / right / left arrows to move the ball
    
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;shapes&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;graphics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createGraphics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c"&gt;// define the colors for the squares and the dot&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;square_fill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SolidFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;yellow&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;square_stroke&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;green&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dot_fill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SolidFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;blue&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dot_stroke&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c"&gt;// the dot&amp;#39;s initial position&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c"&gt;// properties    &lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;num_rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;num_cols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c"&gt;// draw the squares&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;num_cols&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;num_rows&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drawRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;square_stroke&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;square_fill&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// draw the dot&lt;/span&gt;
    &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;graphic&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drawEllipse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;dot_stroke&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;dot_fill&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    
    &lt;span class="c"&gt;// call if the dot&amp;#39;s position changes&lt;/span&gt;
    &lt;span class="nx"&gt;redraw_dot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;graphic&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;setCenter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                            &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    
    &lt;span class="c"&gt;// key event handler&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;key_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;key_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UP&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RIGHT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;num_cols&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DOWN&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;num_rows&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LEFT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;redraw_dot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    
    &lt;span class="c"&gt;// put everything together&lt;/span&gt;
    &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key_handler&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key_event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;shapes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;margin-left: 20px;&amp;#39;&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://teebes.com/blog/19/playing-with-googles-closure-js-library&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;what&amp;#39;s this?&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
</description><guid>http://teebes.com/blog/19/playing-with-googles-closure-js-library</guid></item><item><title>A power companion for your smartphone</title><link>http://teebes.com/blog/18/a-power-companion-for-your-smartphone</link><description>&lt;p&gt;&lt;!--img src="http://teebes.com/static/mediacenter/iphone-duracell.png" alt="iphone with duracell instant charger" width="300" align="right" /--&gt;Often find yourself on the run with your phone or your iPod running low and no time to stop and recharge?&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I just picked up (at Harris Teeter, of all places) this pretty awesome gizmo I had read about a few weeks ago on one of the gadget blogs and that pretty much solves exactly that problem: the &lt;a href="http://www.amazon.com/Duracell-Instant-Charger-Lithium-universal/dp/B002FU6KF2/ref=sr_1_1?ie=UTF8&amp;amp;s=hpc&amp;amp;qid=1255291060&amp;amp;sr=8-1" target="_blank"&gt;Duracell Instant Usb Charger&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Costs $35 (that's what I paid at the store and what Amazon sells it at, but I remember reading it can be gotten for as low as $20... Either way, it's cheap).&lt;/li&gt;
&lt;li&gt;Gave my iPhone 60% power back in just over an hour and a half.&lt;/li&gt;
&lt;li&gt;Uses USB both to charge other devices and itself be recharged.&lt;/li&gt;
&lt;li&gt;Starts giving power as soon as you plug it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall a very thoughtfully designed little device that is cheap, slick, and useful to boot.&lt;/p&gt;
&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/iphone-duracell.png" alt="" width="320px" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/iphone-duracell-before.png" alt="" width="320px" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/iphone-duracell-after.png" alt="" width="320px" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/iphone-duracell2.png" alt="" width="320px" /&gt;&lt;/p&gt;
</description><guid>http://teebes.com/blog/18/a-power-companion-for-your-smartphone</guid></item><item><title>Simple Python Twitter rss feed parser</title><link>http://teebes.com/blog/17/simple-python-twitter-rss-feed-parser</link><description>&lt;p&gt;If you want to display your tweets somewhere on your own web page, the easiest way is to use the RSS feed in your Twitter profile page (for example &lt;a href="http://twitter.com/teebesz"&gt;http://twitter.com/teebesz&lt;/a&gt;). Of course if you want to parse the @, # and links, you need just a little bit of code. &lt;/p&gt;

&lt;p&gt;Here is the Python script I use for this site's Twitter display. You'll need the &lt;a href="http://feedparser.org/"&gt;feedparser&lt;/a&gt; library installed (how have you been living without it anyway!)&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;feedparser&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
    
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_twitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Takes a twitter rss feed and returns a list of dictionaries, one per&lt;/span&gt;
&lt;span class="sd"&gt;    tweet. Each dictionary contains two attributes:&lt;/span&gt;
&lt;span class="sd"&gt;        - An html ready string with the @, # and links parsed to the correct&lt;/span&gt;
&lt;span class="sd"&gt;        html code&lt;/span&gt;
&lt;span class="sd"&gt;        - A datetime object of the posted date&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;twitter_entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;feedparser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;entries&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;

        &lt;span class="c"&gt;# convert the given time format to datetime&lt;/span&gt;
        &lt;span class="n"&gt;posted_datetime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="c"&gt;# format the date a bit&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;posted_datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;posted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posted_datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%b &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;posted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posted_datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%b &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt; %y&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="c"&gt;# strip the &amp;quot;&amp;lt;username&amp;gt;: &amp;quot; that preceeds all twitter feed entries&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^\w+:\s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        
&lt;span class="c"&gt;# parse links&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;r&amp;quot;[^&lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;](http://(\w|\.|/|\?|=|%|&amp;amp;)+)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;a href=&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/a&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="c"&gt;# parse @tweeter&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;r&amp;#39;@(\w+)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;a href=&amp;#39;http://twitter.com/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/a&amp;gt;&amp;quot;&lt;/span&gt;\
                 &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="c"&gt;# parse #hashtag&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;r&amp;#39;#(\w+)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;a href=&amp;#39;http://twitter.com/search?q=&lt;/span&gt;&lt;span class="si"&gt;%%&lt;/span&gt;&lt;span class="s"&gt;23&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/a&amp;gt;&amp;quot;&lt;/span&gt;\
                 &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="n"&gt;twitter_entries&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;posted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;posted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;twitter_entries&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

</description><guid>http://teebes.com/blog/17/simple-python-twitter-rss-feed-parser</guid></item><item><title>Remember to do nothing</title><link>http://teebes.com/blog/16/remember-to-do-nothing</link><description>&lt;p&gt;&lt;img src="http://teebes.com/static/mediacenter/workflow.jpg" alt="complicated workflow" width="200" align="right" /&gt;In the technology world we're always after the next big thing, the differentiating feature that our competitor doesn't yet have and that will give us the edge. The emphasis is always on adding stuff, and hardly ever on staying lean and focused. This is mostly good for innovation, but not necessarily good for the end users.&lt;/p&gt;
&lt;p&gt;It's always striking to me to see how many of the really successful companies out there can be differentiated by what they didn't do. The concept of narrowing down your vision, concentrating it on a smaller subset of what's possible has sometimes surprising repercussions. Here are some famous examples:&lt;/p&gt;
&lt;!--more--&gt; 
&lt;ul&gt;
&lt;li style="margin-bottom: 10px"&gt;&lt;a href="http://twitter.com" target="_blank"&gt;Twitter&lt;/a&gt;: not only do they limit tweets to 140 characters, they've also kept their site surprisingly light and simple, letting 3rd parties do the work of extending their core functionality for them. Can you imagine how many people have likely suggested to Twitter adding multimedia to tweets or fancy ways to arrange threads?&lt;/li&gt;
&lt;li style="margin-bottom: 10px"&gt;&lt;a href="http://www.apple.com/iphone/" target="_blank"&gt;The iPhone&lt;/a&gt;: it only has 1 central button. It also has 3 other buttons and a switch but at its core it really just has one button. I have no doubt the Apple designers filtered thousands of suggestions to add either a few more buttons to the front-end or even a full keyboard. Of course they had to do a great deal of innovation to overcome this limitation but that's exactly my point: sometimes you need to limit your options in order to truly get creative.&lt;/li&gt;
&lt;li style="margin-bottom: 10px"&gt;&lt;a href="http://www.google.com" target="_blank"&gt;&lt;span style="font-weight: normal;"&gt;Google&lt;/span&gt;&lt;/a&gt; and &lt;a href="http://www.craigslist.org/" target="_blank"&gt;Craigslist&lt;/a&gt;: they mostly use plain markup, very close to basic html. Over the years each site has probably had hundreds of consulting firm pitch them projects to pimp-up their pages with all sorts of eye candy. And yet they've made the conscious decision to stay very simple.&lt;/li&gt;
&lt;li style="margin-bottom: 10px"&gt;&lt;a title="woot.com" href="http://woot.com" target="_blank"&gt;Woot.com&lt;/a&gt;: they sell only one item per day. Of course they get around this limitation a bit with wootoffs, but the vast majority of the time there is still just one item per day.&lt;/li&gt;
&lt;li style="margin-bottom: 10px"&gt;&lt;a title="stack overflow" href="http://stackoverflow.com" target="_blank"&gt;Stack Overflow&lt;/a&gt;: the popular site only answers programming questions. It would be easy to say, once the initial concept has proven successful (which it absolutely has), to say "ok let's expand this to all technology questions". But instead Stack Overflow created a second site, also focused on a specific topic, Super User. Sure they use the same engine, look the same, and probably have a large overlap in their communities but nevertheless, each site is focused on one area and does not expand for the sake of expansion alone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's easy to see why this is a rare thing, why consciously limiting your focus is difficult. Managers, &amp;nbsp;Board Members, Shareholders, they want to see growth, deltas, news, they need to see visible changes. If you've got the project team that goes to a CEO and says that they have decided to add nothing new, they'll probably be a lot less likely to say that again next quarter because they'll no longer have their job.&lt;/p&gt;
&lt;p&gt;And yet, it's always easier to not do something in the first place than it is later to try and scale back in a re-focusing effort. So remember: sometimes, the correct decision is to do nothing, and sometimes you'll need to work your butt off in order to look like you did nothing.&lt;/p&gt;
</description><guid>http://teebes.com/blog/16/remember-to-do-nothing</guid></item><item><title>One year and 60 pounds later, five weightloss tips</title><link>http://teebes.com/blog/15/one-year-and-60-pounds-later-five-weightloss-tips</link><description>&lt;p&gt;A year ago today, I began following a nutrition and workout program with the goal in mind of losing weight. The very initial plan was to just follow Bill Phillip's 90 days &lt;a href="http://en.wikipedia.org/wiki/Body_for_life" target="_blank"&gt;Body for Life&lt;/a&gt; challenge, but I also had a more long term goal of dropping about 70 lbs although I didn't quite know how long that would take. It turns out I've lost 60 lbs so far. Not nearly as impressive as &lt;a href="http://consumerist.com/5314225/how-i-lost-1004-pounds-in-6-months" target="_blank"&gt;some others&lt;/a&gt;, but still a decent amount.&lt;/p&gt;
&lt;p&gt;What I'm going to offer here are tips and advice that I've drawn from my experience. I don't pretend to have all of the answers but I can promise an honest, open account of my experience. First though, the before-after shots:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://teebes.com/static/mediacenter/weightloss-before.png"&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/weightloss-before.png" alt="" width="250px" /&gt;&lt;/a&gt; &lt;a href="http://teebes.com/static/mediacenter/weightloss-after2.png"&gt;&lt;img style="border: 1px solid #000000; margin-left: 50px;" src="http://teebes.com/static/mediacenter/weightloss-after2.png" alt="" width="250px" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;&lt;span style="font-size: 12px; font-weight: bold; "&gt;Nutrition&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Sunday-Friday, I eat six meals a day, relatively evenly spaced throughout the day:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Meal one: omelette made with 1 egg beater serving and 1 real egg, fat-free yougurt&lt;/li&gt;
&lt;li&gt;Meal two: protein shake/bar (I use regular Myoplex and Cliff Bars)&lt;/li&gt;
&lt;li&gt;Meal three: turkey-cheese sandwhich, raw carrots&lt;/li&gt;
&lt;li&gt;Meal four: protein shake/bar&lt;/li&gt;
&lt;li&gt;Meal five: this is the meal I eat when I get back from work, it varies every day but is always chicken, lean meat or fish, a serving of carbs (potato or whole weat pasta / rice) and a serving of veggies&lt;/li&gt;
&lt;li&gt;Meal six: It varies somewhat but usually either chicken and rice soup or a burrito with 1 full egg and 2 egg whites with a slice of cheese.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This adds up to just over 2,000 calories a day. If I get hungry throughout the day, I eat almonds or a slice of turkey. On Saturdays I eat &lt;em&gt;whatever I want&lt;/em&gt;. No restriction whatsoever. I typically don't feel that good on Sunday mornings...&lt;/p&gt;
&lt;h3&gt;Exercise&lt;/h3&gt;
&lt;p&gt;I exercise in the mornings Sunday-Friday (Saturdays are off). I do the cardio on an empty stomach and don't eat for an hour after I'm done (this allows the body to keep burning fat even after you're done). For lifting, though, I do eat beforehand to have more strength and push harder.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sun-Tue-Thu: 20 min of stationary bike at variable intensity.&lt;/li&gt;
&lt;li&gt;Fridays: 45 min of Biceps &amp;amp; Back&lt;/li&gt;
&lt;li&gt;Mondays: 45 min of Chest &amp;amp; Triceps&lt;/li&gt;
&lt;li&gt;Wednesdays: 45 min Quads, Hamstrings, Calves, Abs and Shoulders&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although you can lose weight with just cardio or even just by changing your nutrition alone, you'd be missing out on a huge aid. Lifting doesn't just develop your muscles: it raises your whole metabolism, training your body to burn more calories, not just when you're lifting but all the time.&lt;/p&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;p&gt;I started at 289 pounds. I'm now at 229. That's 60 pounds even. I've kept a pretty good record of my progress, which can be found on &lt;a href="http://teebes.com/weightloss/teebes/" target="_blank"&gt;this chart&lt;/a&gt;. Here is the small version of it:&lt;/p&gt;
&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/weightloss-chart.png" alt="" width="400px" /&gt;&lt;/p&gt;
&lt;p&gt;The spikes in the line are almost always caused by my day off. Since I can eat whatever I want on that day I'll sometimes gain 4 or 5 pounds from that day alone. Of course most of that is just water retention, and I lose it all and then some during the week.&lt;/p&gt;
&lt;p&gt;Obviously, this is getting harder as time goes on. I lost 21 pounds in the first quarter, 20 in the second, 11 in the third and 8 in the fourth. I seem to be approaching some sort of natural weight plateau, barely losing a quarter pound a week. Some of this may be due to the fact that my muscles have been developing quite a bit in the last couple of months and there is no doubt that it's adding weight to the equation.&lt;/p&gt;
&lt;p&gt;Although I had initially fantasized about losing 100 pounds (it was easy to get carried away when I was losing 2 pounds a week...), I think that even losing the last 10 pounds is going to be difficult so at this point my goal is to reach 220 and stay there relatively stable for a while.&lt;/p&gt;
&lt;h3&gt;Tip #1: Plan, plan, plan&lt;/h3&gt;
&lt;p&gt;I cannot stress enough how true the old adage is: &lt;em&gt;if you are failing to plan you are planning to fail&lt;/em&gt;. This is especially true if you're going to go on the 6 meals a day plan, because on most days you'll be away from home for more than 3-4 hours at a time which means that you need to bring a meal with you. If you don't bring a meal with you and go hungry, you'll be in a real danger zone to go binge somewhere. It can be as simple as grabbing a protein bar on your way out, but you have to get into the habbit to always know exactly when and what your next meal will be. This is also true if you only have 3 meals a day: it's still important for you to know when those meals will be, and what you will eat at least half a day in advance. I've definitely observed a rule in my own behavior that the hungrier I am, the more likely I am to eat crap. If I am hungry and haven't planned what to eat, healthy food just doesn't seem as appealing to me.&lt;/p&gt;
&lt;p&gt;This isn't always as easy as it sounds. This plan was definitely easier to follow when I was working on my startup full time from home. There I had control over everything. Now that I work at PBS, it's not as easy. There have been several get togethers every week of my employment so far where there are cup cakes, chips, ice cream, hot dogs, burgers &amp;amp; pizzas everywhere. They like to go out to lunch and grab beers after work. They bake sutff.&lt;/p&gt;
&lt;p&gt;This is where another kind of planning can be useful. You can plan out in your mind how the event will go, say something like this to yourself: "I'm going to go to this get together, I won't eat any chips or any cake, if someone offers me something I'll smile and decline, then I'll go home and I'll feel great about myself for having said no." What this does is give your mind a clear map of how you're going to get through that situation with a positive outcome and will be able to fight against the urge to eat. Visualize the way you'll decline the food too. Imagine yourself saying 'no thanks' and passing the bowl of chips to the guy on your left. It really does help...&lt;/p&gt;
&lt;p&gt;The silver lining here is that when you're faced with a dangerous situation and come out on top, you are re-wiring your brain, teaching it that you have control and that the stimulus of food doesn't necessarily need to generate the response of eating. In the long run, this is the real goal and can only be accomplished by planning how you're going to do it.&lt;/p&gt;
&lt;h3&gt;Tip #2: Get a very basic understanding of nutrition &amp;amp; learn to read labels:&lt;/h3&gt;
&lt;p&gt;You don't need a degree in nutrition, but a few simple rules of thumb go a long way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Protein = good&lt;/li&gt;
&lt;li&gt;Carbs = good in moderation, but:                            
&lt;ul&gt;
&lt;li&gt;The more dietary fiber the better&lt;/li&gt;
&lt;li&gt;Sugar is not great, it will be converted into fat later, so try to keep that low&lt;/li&gt;
&lt;li&gt;Whole wheat really is better. I use 7-grain bread. Stay away from processed carbs, they soak in fat like a sponge&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sodium = the lower the better, but for some people (like me), salty tastes better so don't use too much but don't cut every pleasure out of your life either: you can use a little salt.&lt;/li&gt;
&lt;li&gt;Fat = usually bad, but:                            
&lt;ul&gt;
&lt;li&gt;Saturated fat is the enemy. Any serving with more than 3g should raise your eyebrows.&lt;/li&gt;
&lt;li&gt;Unsaturated fat is ok, in moderation. For example &lt;a href="http://caloriecount.about.com/calories-almonds-i12061" target="_blank"&gt;almonds&lt;/a&gt; are fine to eat because their fat is mostly unsaturated.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Tip #3: learn to cook basic stuff&lt;/h3&gt;
&lt;p&gt;I recently read &lt;a href="http://www.amazon.com/End-Overeating-Insatiable-American-Appetite/dp/1605297852/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1253405298&amp;amp;sr=8-1" target="_blank"&gt;the end of overeating: taking control of the insatiable american appetite&lt;/a&gt; by David Kessler, a former FDA commissioner, after I heard him on Bill Maher.&amp;nbsp; If I came away with one thing, it's that restaurants really are the devil when it comes to dieting. Even when you order a seemingly healthy meal it's often loaded with hidden fat, sugar and salt to make your brain chemically crave more (caesar salads are a notorious example). The best way to not be tricked is to learn to cook food: instead of going to a restaurant for lunch, bring a sandwich with healthy ingredients that you made in the morning, and instead of ordering dinner, cook a decent meal. It doesn't have to be time consuming, and it will also have the side bonus of saving you money.&lt;/p&gt;
&lt;h3&gt;Tip #4: Work out in the mornings&lt;/h3&gt;
&lt;p&gt;A bit afer I graduated from college I'd work out three times a week after work, and even though that worked well enough I've done it in the mornings for the last year and I like it a lot better, and here's why:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You burn extra calories throughout the day if you worked out in the morning&lt;/li&gt;
&lt;li&gt;The gyms are &lt;em&gt;far&lt;/em&gt; less crowded&lt;/li&gt;
&lt;li&gt;You'll skip less because you won't have to go work out feeling shitty from working all day&lt;/li&gt;
&lt;li&gt;It helps you focus throughout the day&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The #1 downside is that it makes you wake up earlier, which makes you go to bed earlier, and that to me is a real sacrifice to make because I'm naturally quite the night owl.&lt;/p&gt;
&lt;h3&gt;Tip #5: Don't skip the day off&lt;/h3&gt;
&lt;p&gt;Like I said above, I sometimes gain 3, 4 or even 5 pounds on saturdays when I can eat whatever I want. You may be tempted to think that if you just didn't take that Saturday off you'd lose more weight more quickly. You'd be right of course, but taking the day off is important. The trick of dieting is to find a way to do it that is sustainable for the rest of your life. Otherwise you're just doing a temporary dip and you'll always regain the weight, like I have once.&lt;/p&gt;
&lt;p&gt;The weekly day off lets you tell yourself when you see food that looks really good "I'll have that Saturday" as opposed to saying "I'll never be able to have that." For me, that's been a huge difference. It keeps me sane.&lt;/p&gt;
&lt;h3&gt;What's difficult&lt;/h3&gt;
&lt;p&gt;There are, unfortunately, a few challenges to doing this kind of diet. The biggest problem, I'd say, is that social events become problematic: many of them involve either eating restaurant food or drinking, neither of which are great. On the one hand, it sucks seeing a week's worth of work foiled because you had a plate of cheese nachos after downing shots and beers late night at a bar... on the other, you gotta keep seeing your friends. So it's a careful balance of willpower and compromise, but it can definitley be challenging.&lt;/p&gt;
&lt;p&gt;Sometimes, you're hungry. I wish I could say that it never happen, but it does. Drinking water, light soda or coffee with skim milk helps, and if that's not enough I snack on almonds or on a slice or two of turkey.&lt;/p&gt;
&lt;p&gt;Temptation is everywhere. It is to the food industry's best interest that you eat fattening food. They've gotten very good at marketing, food manipulation and altering the very chemical circuitry in your brain to make you crave more fattening food. This is where planning helps, for example so that you make sure you don't go hungry at times where you might be tempted were you on an empty stomach.&lt;/p&gt;
&lt;h3&gt;Do this sooner rather than later (I've done this before)&lt;/h3&gt;
&lt;p&gt;So this is actually the second (and hopefully last) time I do this kind of weight drop. Five years ago, I lost over 40 pounds over about 9 months but I then took a consulting gig and stopped working out completely because I was traveling every week... and regained it all over about 3 years, with an extra layer as a free bonus.&lt;/p&gt;
&lt;p&gt;Why is this relevant? because it was much easier five years ago. I only worked out 3 times a week, I didn't diet nearly as strictly as I do now and I was drinking much more... and I lost at a comparable rate. So if you want to lose weight, don't wait, because it only gets harder.&lt;/p&gt;
&lt;p&gt;Thankfully, this time around I have a really solid understanding of nutrition that has opened my eyes to a lot of things, and in a lot of ways I don't think I'd be able to go back now. My views on food have fundamentally changed, and hopefully will keep on changing for the better as I keep working on this.&lt;/p&gt;
&lt;h3&gt;Thank yous&lt;/h3&gt;
&lt;p&gt;Just wanted to give a shout out to a few people...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My fiancee Galen Bascom, not only for supporting my nazi meal schedule and working out with me in the mornings but for telling me I was at the perfect weight when I was almost 300 pounds.&lt;/li&gt;
&lt;li&gt;My brother Tristan Morel l'Horset who gave me many great tips and who ultimately showed me just how insignificant my workout routine was when he started training for triathlons.&lt;/li&gt;
&lt;li&gt;Scott Monsefan, who introduced me a lot of the fundamental principles of nutrition and working out. This basic understanding of nutrition went a long way when I'd try to cook healthy but tasty meals.&lt;/li&gt;
&lt;li&gt;Bill Philips, since after all he did come up with the basic rules of the diet I followed. Props to him for designing a system that you can keep up in the long run. To me that's the biggest win in his program.&lt;/li&gt;
&lt;li&gt;Anyone else who's ever encouraged me&lt;/li&gt;
&lt;/ul&gt;
</description><guid>http://teebes.com/blog/15/one-year-and-60-pounds-later-five-weightloss-tips</guid></item><item><title>Django - Facebook Connect integration with ajax (no middleware)</title><link>http://teebes.com/blog/13/django-facebook-connect-integration-with-ajax-no-middleware</link><description>&lt;p&gt;&lt;img src="http://teebes.com/static/images/base/connect/facebook.png" border="1" alt="Facebook Connect logo" align="right" /&gt;&lt;/p&gt;

&lt;div style='padding: 3px; border: 1px solid #e02b2b; margin-left: 50px; float: left; text-align: center;'&gt;
&lt;b&gt;There is now a github repo for this code at&lt;br /&gt;
&lt;a href="http://github.com/teebes/django-facebookconnect/"&gt;http://github.com/teebes/django-facebookconnect/&lt;/a&gt;&lt;/b&gt;
&lt;/div&gt;
&lt;div style='clear: both'&gt;&lt;/div&gt;

&lt;h3&gt;Why do this&lt;/h3&gt;
&lt;p&gt;I hardly need to prove the point that web authentication is fast becoming a nightmare for users to manage. Obviously Facebook Connect and Open ID are interesting solutions but implementing them is not always straight forward... and yet until every site starts implementing those solutions, the problem will remain. I looked around for a really simple Django solution and found a few but they involved either custom middleware or even a custom auth backend.&lt;/p&gt;
&lt;p&gt;So I took at stab at this using only Ajax, jQuery and standard django stuff, with the aim of making the integration as minimalist and non-invasive as possible.&lt;/p&gt;
&lt;p&gt;This site's facebook connect implementation started out with exactly the code below. It's a bit more sophisticated now because I had to implement various linking capabilities. If there is any interest in seeing the source code as it currently stands leave a comment and I'll look into it.&lt;/p&gt;

&lt;h3&gt;Requirements&lt;/h3&gt;
&lt;p&gt;None! all of this should come standard with Python and Django.&lt;/p&gt;
&lt;h3&gt;How it works&lt;/h3&gt;
&lt;p&gt;If a connect login occurs and that facebook ID has never logged in to this site, a standard django.contrib.auth user is created, as well as a custom FacebookUser object that keeps track of the Facebook - Django user ID mappings. If the user has previously logged on, the corresponding Django user is used. The user is then logged in just like any regular Django user.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Start by &lt;a href="http://wiki.developers.facebook.com/index.php/Trying_Out_Facebook_Connect"&gt;creating a facebook application&lt;/a&gt; that your site will use for connect.&lt;/p&gt;
&lt;p&gt;The important thing here is to get the 'Connect URL' right, which is under the 'Connect' tab. It will be http://&amp;lt;your-project's-address&amp;gt;/facebookconnect&lt;/p&gt;
&lt;p&gt;In your django project that will be using connect, setup the following values in your settings.py (or wherever you keep your settings) (replace the xxxxxxxx with the correct values for your setup):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# where the user will go after they log in via facebook&lt;/span&gt;
&lt;span class="n"&gt;LOGIN_REDIRECT_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/blog/&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;# the application api key given by facebook&lt;/span&gt;
&lt;span class="n"&gt;FACEBOOK_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;xxxxxxxx&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;# the applications ecret key given by facebook&lt;/span&gt;
&lt;span class="n"&gt;FACEBOOK_APPLICATION_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;xxxxxxxx&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;# add the facebookconnect app to the list of apps.&lt;/span&gt;
&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;facebookconnect&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Include the facebook connect url file to your urls:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="o"&gt;...&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^facebookconnect/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;facebookconnect.urls&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;The facebookconnect Django app&lt;/h3&gt;
&lt;p&gt;Here is the anatomy of the app. Make sure the app is in your python path or site-packages directory.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;facebookconnect&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
	&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
	&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
	&lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
	&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
        &lt;span class="n"&gt;templates&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
                &lt;span class="n"&gt;xd_receiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;htm&lt;/span&gt;
	&lt;span class="n"&gt;templatetags&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
		&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
		&lt;span class="n"&gt;facebookconnect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;models.py&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FacebookUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;facebook_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;contrib_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OneToOneField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;contrib_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;views.py&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.shortcuts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render_to_response&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.template&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;facebookconnect.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FacebookUser&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login_facebook_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;unknown failure&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;expires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;expires&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ss&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;session_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;session_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sig&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;pre_hash_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;expires=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;session_key=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;ss=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;user=&lt;/span&gt;&lt;span class="si"&gt;%s%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;expires&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;session_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FACEBOOK_APPLICATION_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;post_hash_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;md5&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;post_hash_string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pre_hash_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;post_hash_string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;fb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FacebookUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;facebook_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;logged in existing user&amp;quot;&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;FacebookUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DoesNotExist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;contrib_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;u&amp;quot;fbuser_&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

                &lt;span class="n"&gt;fb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FacebookUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;facebook_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
                &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contrib_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contrib_user&lt;/span&gt;

                &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sha1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
                &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

                &lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contrib_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;
                &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;created new user&amp;quot;&lt;/span&gt;

            &lt;span class="n"&gt;authenticated_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                         &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                         &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contrib_password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authenticated_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;wrong hash sig&amp;#39;&lt;/span&gt;

            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;FBConnect: user &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; with exit status &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Exception thrown in the FBConnect ajax call: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;xd_receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_to_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;facebookconnect/xd_receiver.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;urls.py&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf.urls.defaults&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.views.generic.simple&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;direct_to_template&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;facebookconnect.views&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^xd_receiver\.htm$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;direct_to_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;template&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;xd_receiver.htm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;xd_receiver&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^login_facebook_connect/$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;login_facebook_connect&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;facebook_connect_ajax&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;templates/xd_receiver.htm&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Strict//EN&amp;quot;&lt;/span&gt;
&lt;span class="cp"&gt;   &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.js&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;templatetags/facebookconnect.py&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.urlresolvers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;

&lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Library&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FacebookScriptNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;            &amp;lt;script src=&amp;quot;http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;            &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;            &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt; FB.init(&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;);&lt;/span&gt;
&lt;span class="s"&gt;                function facebook_onlogin() {&lt;/span&gt;
&lt;span class="s"&gt;                    var uid = FB.Facebook.apiClient.get_session().uid;&lt;/span&gt;
&lt;span class="s"&gt;                    var session_key = FB.Facebook.apiClient.get_session().session_key;&lt;/span&gt;
&lt;span class="s"&gt;                    var expires = FB.Facebook.apiClient.get_session().expires;&lt;/span&gt;
&lt;span class="s"&gt;                    var secret = FB.Facebook.apiClient.get_session().secret;&lt;/span&gt;
&lt;span class="s"&gt;                    var sig = FB.Facebook.apiClient.get_session().sig;&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;                    fb_connect_ajax(expires, session_key, secret, uid, sig);&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;                }&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;                function fb_connect_ajax(expires, session_key, ss, user, sig) {&lt;/span&gt;
&lt;span class="s"&gt;        &lt;/span&gt;
&lt;span class="s"&gt;                    var post_string = &amp;#39;expires=&amp;#39; + expires;&lt;/span&gt;
&lt;span class="s"&gt;                    post_string = post_string + &amp;#39;&amp;amp;session_key=&amp;#39; + session_key;&lt;/span&gt;
&lt;span class="s"&gt;                    post_string = post_string + &amp;#39;&amp;amp;ss=&amp;#39; + ss;&lt;/span&gt;
&lt;span class="s"&gt;                    post_string = post_string + &amp;#39;&amp;amp;user=&amp;#39; + user;&lt;/span&gt;
&lt;span class="s"&gt;                    post_string = post_string + &amp;#39;&amp;amp;sig=&amp;#39; + sig;&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;                    $.ajax({&lt;/span&gt;
&lt;span class="s"&gt;                        type: &amp;quot;POST&amp;quot;,&lt;/span&gt;
&lt;span class="s"&gt;                        url: &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&lt;/span&gt;
&lt;span class="s"&gt;                        data: post_string,&lt;/span&gt;
&lt;span class="s"&gt;                        success: function(msg) {&lt;/span&gt;
&lt;span class="s"&gt;                            window.location = &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;; //.reload()&lt;/span&gt;
&lt;span class="s"&gt;                        }&lt;/span&gt;
&lt;span class="s"&gt;                    });&lt;/span&gt;
&lt;span class="s"&gt;                } &lt;/span&gt;
&lt;span class="s"&gt;            &amp;lt;/script&amp;gt;       &lt;/span&gt;
&lt;span class="s"&gt;            &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FACEBOOK_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;xd_receiver&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;facebook_connect_ajax&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LOGIN_REDIRECT_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;facebook_connect_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FacebookScriptNode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;facebook_connect_script&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FacebookLoginNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;fb:login-button onlogin=&amp;#39;facebook_onlogin();&amp;#39;&amp;gt;&amp;lt;/fb:login-button&amp;gt;&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;facebook_connect_login_button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FacebookLoginNode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;facebook_connect_login_button&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Putting it all together&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&lt;/span&gt;
      &lt;span class="na"&gt;xmlns:fb=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.facebook.com/2008/fbml&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Login via facebook!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

{% load facebookconnect %}
{% facebook_connect_login_button %}

{% facebook_connect_script %}

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

</description><guid>http://teebes.com/blog/13/django-facebook-connect-integration-with-ajax-no-middleware</guid></item><item><title>Video of my assembled Lego set of Frank Lloyd Wright&amp;#39;s Falling Water</title><link>http://teebes.com/blog/14/video-of-my-assembled-lego-set-of-frank-lloyd-wrights-falling-water</link><description>&lt;p&gt;A &lt;a href="http://twitter.com/ravenell" target="_blank"&gt;friend of mine&lt;/a&gt; sent me &lt;a href="http://www.wired.com/gadgetlab/2009/05/frank-lloyd-wright-lego-sets/" target="_blank"&gt;a link&lt;/a&gt; a while back to a 800+ pieces Lego set of &lt;a href="http://en.wikipedia.org/wiki/Frank_lloyd_wright" target="_blank"&gt;Frank Lloyd Wright&lt;/a&gt;'s &lt;a href="http://en.wikipedia.org/wiki/Falling_Water" target="_blank"&gt;Falling Water&lt;/a&gt;, which I studied for an architecture class in College and have loved ever since. So I decided to get the set last week and put it together for a couple hours on Friday and another couple hours today. Here is the finished result (some bricks in this video are a little loose, I've tightened the whole thing since). This was really a lot of fun, I hadn't built lego since I was maybe 14 and it brought back all sorts of great memories. I'm definitely considering getting more sets.&lt;/p&gt;
&lt;p&gt;Video was taken with my iPhone 3GS. The grass and trees to the back corners of the set look black but that's just the iPhone sucking at recording green.&lt;/p&gt;
&lt;p&gt;
&lt;object width="425" height="344"&gt;
&lt;param name="movie" value="http://www.youtube.com/v/x0PK13IFFUo&amp;amp;hl=en&amp;amp;fs=1&amp;amp;" /&gt;
&lt;param name="allowFullScreen" value="true" /&gt;
&lt;param name="allowscriptaccess" value="always" /&gt;&lt;embed type="application/x-shockwave-flash" width="425" height="344" src="http://www.youtube.com/v/x0PK13IFFUo&amp;amp;hl=en&amp;amp;fs=1&amp;amp;" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;
&lt;/object&gt;
&lt;/p&gt;
</description><guid>http://teebes.com/blog/14/video-of-my-assembled-lego-set-of-frank-lloyd-wrights-falling-water</guid></item><item><title>My upgrade to Snow Leopard: a non-event</title><link>http://teebes.com/blog/12/my-upgrade-to-snow-leopard-a-non-event</link><description>&lt;p&gt;&lt;img src="http://teebes.com/images/2009-08/snow-leopard.png" alt="Apple Snow Leopard OSX 10.6" width="200" align="right" /&gt;So I upgraded to Snow Leopard today. I pre-ordered it right as it became available on Monday and it was waiting for me when I got home from work today. I popped the CD in, clicked the upgrade button, typed my password, watched an episode of Damages and by the time it was done my computer was all set (so less than an hour). Talk about painless...&lt;/p&gt;
&lt;p&gt;As far as the OS itself goes, it's basically as if nothing had happened. Everything is where it was, all of my applications still work and Time Machine is backing up the 6 or so new gigs as I'm writing this (in reality Snow Leopard &lt;a href="http://gizmodo.com/5283157/everything-you-need-to-know-about-snow-leopard" target="_blank"&gt;gives you space back&lt;/a&gt;, but it's still new data as far as Time Machine is concerned).&lt;/p&gt;
&lt;p&gt;I haven't really noticed any speed difference yet but then again I wasn't expecting to: most applications haven't yet been built to take advantage of Snow Leopard's new capabilities and I haven't run many heavy programs simultaneously. I definitely like the tweaks to expose though, and the new QuickTime is pretty slick.&lt;/p&gt;
&lt;p&gt;Of course none of this was unexpected: Snow Leopard's improvements are supposed to be all under the hood and Apple has been pretty upfront about that from the get go. All in all the upgrade was a non-event (I say this in a good way), and well worth the $29.&lt;/p&gt;
&lt;p&gt;If you want more info on Snow Leopard, here are the links to the reviews from &lt;a href="http://www.engadget.com/2009/08/26/snow-leopard-review/" target="_blank"&gt;Engadget&lt;/a&gt;, &lt;a href="http://gizmodo.com/5346418/snow-leopard-review-lightened-and-enlightened" target="_blank"&gt;Gizmodo&lt;/a&gt; and &lt;a href="http://www.macworld.com/article/142423/2009/08/snow_leopard_review.html" target="_blank"&gt;MacWorld&lt;/a&gt;.&lt;/p&gt;
</description><guid>http://teebes.com/blog/12/my-upgrade-to-snow-leopard-a-non-event</guid></item><item><title>Not so evil after all: the public eye softens on Microsoft</title><link>http://teebes.com/blog/11/not-so-evil-after-all-the-public-eye-softens-on-microsoft</link><description>&lt;p&gt;&lt;img src="http://teebes.com/images/2009-08/microsoft-retail-logo.png" alt="Microsoft new retail logo" width="200" height="200" align="right" /&gt;As I've &lt;a href="http://teebes.com/2009/08/05/is-apple-more-or-less-vulnerable-than-ever/" target="_blank"&gt;pointed out before&lt;/a&gt;, I've been a PC guy most of my life and have only been using Macs for the last two years. The biggest takeaway for me has been that Microsoft and Apple are really pretty even: they both pose as poster children for anti-competitive practices, generally favor proprietary code over open source, and make very questionable ethics calls.&lt;/p&gt;
&lt;p&gt;Microsoft has been living with the reputation of a big bad corporation for over a decade now. But in the last year or so, there has been a subtle but significant shift in the public opinion, for the better. I can actually pin-point exactly when: it's since Bill Gates has stepped down. Although &lt;a href="http://slashdot.org/" target="_blank"&gt;Slashdot&lt;/a&gt; still illustrates all Microsoft news with the now famous &lt;a href="http://c.fsdn.com/sd/topics/topicms.gif" target="_blank"&gt;Borg avatar of Bill&lt;/a&gt;, the tech media seems to have softened its perception of him. People speak of him with some nostalgia now, and he's often portrayed as some sort of wise elder spokesman for technology. A lot of that may be due to Ballmer being very unpopular and Gates therefore shining by contrast, but I think that there is something deeper going on here.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;As Google is starting to &lt;a href="http://dashes.com/anil/2009/07/googles-microsoft-moment.html" target="_blank"&gt;increasingly resemble&lt;/a&gt; a traditional, bloated corporation with billboard ads and multiple competing product lines, and as Apple is being &lt;a href="http://dashes.com/anil/2009/07/apple-secrecy-does-not-scale.html" target="_blank"&gt;increasingly criticized&lt;/a&gt; for its lack of openness, Microsoft all the sudden doesn't seem quite so bad. Remember all of the heat that it took for bundling IE with Windows? Well Apple is merrily doing the same with Safari on OSX (including the iPhone) and Google will do the same with Chrome OS (although they'll spin it in reverse as "we're bundling the Browser with the OS"). Microsoft's anti-trust issues? We now have Intel with microchips and Google with search.&lt;/p&gt;
&lt;p&gt;Windows 7 final builds have started going out to manufacturers and the press is giving it very, very good reviews so far. A lot of that is probably because the OS is actually very good. It's beautiful, lean, responsive, and plays nice with more modest hardware like netbooks. But I think that the tech media is also compensating on some level for the excessive criticism of Vista, which most people nowadays would agree was a little over the top. It's almost as if everyone feels bad for giving Microsoft such a hard time and this is their way of making a truce.&lt;/p&gt;
&lt;p&gt;Microsoft is moving in all of the right directions with their other products, too: Office free in the cloud, an increasingly open XBox Live platform (love the Netflix integration &amp;amp; community games), retail stores, a very sleek looking Zune HD... sure they're not innovating much compared to the other big players, but they don't really need to at this point. They just need to keep up with the trends and not piss off their user base too much.&lt;/p&gt;
&lt;p&gt;I often ask myself nowadays: what would it take to make me switch back to Windows as my primary OS? I think the answer is simple: it would need to become a Unix-compliant system like OSX is. This is because as a web developer it really makes my life easier to have access to a native shell. Now I'm aware that this will almost certainly never happen, but it doesn't really need to for me to benefit from the presence of a better Windows: the biggest benefit to me is that it keeps Google, Apple and the other contenders that I actually use in check. As long as Microsoft doesn't screw up too badly (and it looks like it learned its lesson with Vista), it will remain a force to be reckoned with and promote competition.&lt;/p&gt;
&lt;p&gt;Wait... Microsoft's existence promotes competition? now there's something I wouldn't have been able to say ten years ago.&lt;/p&gt;
</description><guid>http://teebes.com/blog/11/not-so-evil-after-all-the-public-eye-softens-on-microsoft</guid></item><item><title>Never temporarily disable your backup</title><link>http://teebes.com/blog/10/never-temporarily-disable-your-backup</link><description>&lt;p&gt;&lt;img src="http://teebes.com/images/2009-08/time-machine.png" border="1" alt="Apple Time Machine" width="250" align="right" /&gt;I often find myself in the situation where there are too many simultaneous data transfers going on over my Wi-Fi at home: I'll be watching a TV show on my Apple TV (which streams from the Drobo, see &lt;a href="http://teebes.com/2009/07/28/me-and-my-apple-tv-life-after-cable/" target="_blank"&gt;this post&lt;/a&gt; for details), transferring media to the Drobo, doing a large svn or git checkout, all of this while Time Machine is wirelessly backing up the Mac.&lt;/p&gt;
&lt;p&gt;At this point I'll often decide to stop the ongoing backup, knowing it will start again an hour later when there is less going on. So far so good. What's less good is that when Time Machine is doing a very large backup, say 20 gigs, I know I will need to stop it every hour for a while and I'll sometimes disable it with the intent of re-enabling it once all of the data transfers are done.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;This has proven to be a horrible idea: I am missing all of the first half of May and the 2nd half of July. Reason? I completely forgot both times to turn Time Machine back on. Now thanks to Murphy's Law, you can be sure that the files you'll need to recover will always happen to fall within the time period where you forgot to re-enable your backup.&lt;/p&gt;
&lt;p&gt;I'm tempted to say that disabling your backup is OK as long as you also create a reminder to turn it back on, but chances are you'll either forget to create it or dismiss it as something you feel confident you'll remember on your own (you won't).&lt;/p&gt;
&lt;p&gt;Yes, backing up can be inconvenient sometimes, but nothing is more important than keeping your life's data safe. Don't disable your backup.&lt;/p&gt;
</description><guid>http://teebes.com/blog/10/never-temporarily-disable-your-backup</guid></item><item><title>Is Apple more or less vulnerable than ever?</title><link>http://teebes.com/blog/9/is-apple-more-or-less-vulnerable-than-ever</link><description>&lt;p align="left"&gt;&lt;img style="margin-right: 5px" src="http://teebes.com/images/2009-08/apple-logo.png" border="0" alt="The Apple logo with a black background" width="150" height="150" align="left" /&gt;I hate to be blogging about Apple again, but it has been quite ubiquitous of late and given that I own an iPhone, an Apple TV, a Mac Mini and a MacBook Pro, I cannot help caring.&lt;/p&gt;
&lt;p align="left"&gt;From a PR standpoint, the last few months have been mixed at best for Apple (more on that later), and yet from a financial standpoint it has been pretty much blowing right through this recession, &lt;a href="http://www.apple.com/pr/library/2009/07/21results.html" target="_blank"&gt;announcing just last month&lt;/a&gt; the best non-holiday quarter in the company's history. What's going on here?&lt;/p&gt;
&lt;p align="left"&gt;I grew up very much a PC guy. My first computer, in 1994, was a 75MHz  Pentium I with 8 megs of ram and a 500 MB hard drive running Dos and Windows 3.11. I was 14, it was by far the coolest thing I'd ever owned, and the internet was just getting started. Sometimes when I'd go visit a school buddy of mine, I'd take a peek at his dad's Apple PowerPC which, he liked to remind me, was much more expensive and cooler than my PC. It also wasn't compatible with mine. It never would be. Apple actually cultivated that difference, it thrived on it: remember 'think different?'&lt;/p&gt;
&lt;p align="left"&gt;From that point until about a year and a half ago, I discarded Macs as over-priced, over-engineered rounded cubes without a delete key or a right-click button, designed by and for intellectual snobs.&lt;/p&gt;
&lt;p align="left"&gt;&lt;!--more--&gt;Fast forward 15 years and I find two facts remarkable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I now use Macs almost exclusively, at home and at work&lt;/li&gt;
&lt;li&gt;Apple switched to Intel&lt;/li&gt;
&lt;/ul&gt;
&lt;p align="left"&gt;The two are of course very closely related. Apple switching to Intel enabled software developers to easily make software that worked in Windows, Linux, and OSX as desktop &lt;em&gt;and&lt;/em&gt; cloud applications. This made Apple more a part of the greater ecosystem. Now the lines in between Mac and PC users weren't so clearly defined.&lt;/p&gt;
&lt;p align="left"&gt;I think it's fair to say that the switch to Intel was mostly a financial decision, and one to everyone's best interest since it takes a lot of capital to put out high quality hardware &lt;em&gt;and&lt;/em&gt; software. But the bottom line is Apple is still exclusive at heart. It still wants everyone to do things their way. That's exactly why the iPhone still only has one App Store, one browser, and why it considers that people who want to play with the guts of the hardware they paid for &lt;a href="http://reviews.cnet.com/8301-19512_7-10298646-233.html" target="_blank"&gt;are threats to national security&lt;/a&gt;.&lt;/p&gt;
&lt;p align="left"&gt;So Apple is now more inclusive than it used to be thanks to the Intel switch. It even &lt;a href="http://www.macworld.com/article/138000/2009/01/drm_faq.html" target="_blank"&gt;gave up on the DRM battle&lt;/a&gt;, a move which I personally believe it disagreed with from a philosophic standpoint but just had to do for PR reasons. Moves like this sometimes give the impression that Apple wants to play nice with the other kids, but that is simply not the case. The last few months have happened to highlight of a lot of its core values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sling and Skype apps &lt;a href="http://news.cnet.com/8301-13579_3-10239277-37.html" target="_blank"&gt;not allowed to&lt;/a&gt; &lt;a href="http://gizmodo.com/5198594/fcc-pressed-to-investigate-apple-att-regarding-skype-over-3g" target="_blank"&gt;work over 3G&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apple specifically &lt;a href="http://www.precentral.net/apple-blocks-palm-pre-itunes-syncing" target="_blank"&gt;blocking iTunes from syncing with the Pre&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Numerous accounts of the &lt;a href="http://mobile.slashdot.org/story/09/07/20/2318201/How-Apples-App-Review-Is-Sabotaging-the-iPhone?from=rss" target="_blank"&gt;poor and arbitrary approval process&lt;/a&gt; of the app store&lt;/li&gt;
&lt;li&gt;Safari and Apple's own store still the only browser/app stores allowed on the device&lt;/li&gt;
&lt;li&gt;And the big one, last week &lt;a href="http://www.techcrunch.com/2009/07/27/apple-is-growing-rotten-to-the-core-and-its-likely-atts-fault/" target="_blank"&gt;Apple rejecting the Google Voice app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p align="left"&gt;To those saying that #1 and #5 are AT&amp;amp;T's doing, even if it is (I think it's more complicated than that) you can't have a partnership when it's profitable that you then denounce when it's not convenient from a PR standpoint. It's too easy.&lt;/p&gt;
&lt;p align="left"&gt;Now you have high-profile bloggers like &lt;a href="http://gigaom.com/2009/02/11/my-big-iphone-break-up/" target="_blank"&gt;Om Malik&lt;/a&gt; and &lt;a href="http://www.techcrunch.com/2009/07/31/i-quit-the-iphone/" target="_blank"&gt;Michael Arrington&lt;/a&gt; that are quitting their iPhones and the whole tech press is upside down over the Google Voice controversy. But like I said, Apple has always had this 'do it my way' attitude. Sure they like innovation, but only so far as it benefits their advancement.&lt;/p&gt;
&lt;p align="left"&gt;So then one may ask, is all of this bad press hurting the company's bottom-line? It may be a little early to tell, but I would be willing to bet they'll do fine next quarter, and will hit it out of the park again during the holiday quarter. I think there are a few reasons for this.&lt;/p&gt;
&lt;p align="left"&gt;First, Apple gets about half of its business from the international market. The issues they are having as a consequence of the partnership with AT&amp;amp;T (Google Voice, poor call quality, MMS) are simply not factors abroad.&lt;/p&gt;
&lt;p align="left"&gt;Second, what makes the phone most useful to a lot of us are all of the 3rd party apps: &lt;a href="http://www.rememberthemilk.com/" target="_blank"&gt;Remember the Milk&lt;/a&gt;, &lt;a href="http://evernote.com/" target="_blank"&gt;Evernote&lt;/a&gt;, &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=289943355&amp;amp;mt=8" target="_blank"&gt;Air Sharing&lt;/a&gt;, games... Once you're hooked to the system you start depending on it, and that brings an additional, hidden factor against switching that most users will be well aware of once their contracts expire and they need to make a decision on which new phone to get.&lt;/p&gt;
&lt;p align="left"&gt;Last, and most importantly, Apple's biggest asset is its ability to create &lt;em&gt;envy&lt;/em&gt; in consumers. It is a delicate balance of software design, hardware design and marketing that so far Apple has been the only one to strike. But they remain an advocate of a 'complete Apple solution'. Your phone, hardware, software, everything should have the Apple brand. If you're trying to mix and match, you will always run into the rougher edges of the company's philosophy.&lt;/p&gt;
&lt;p align="left"&gt;At the end of the day, it's a matter of how much of your freedom as a consumer are you willing to give up. This makes the technological world delicate to walk into as you are always locking yourself into contract, or buying devices that are really the foot into the door of a much larger structure that will keep you hooked for many years.&lt;/p&gt;
</description><guid>http://teebes.com/blog/9/is-apple-more-or-less-vulnerable-than-ever</guid></item><item><title>Click by click: how to browse files offline on the iPhone</title><link>http://teebes.com/blog/8/click-by-click-how-to-browse-files-offline-on-the-iphone</link><description>&lt;p&gt;I wanted to be able to browse code on the bus going to and from work. So I asked &lt;a href="http://stackoverflow.com" target="_blank"&gt;Stack Overflow&lt;/a&gt; for a suggestion and got &lt;a href="http://stackoverflow.com/questions/1197643/iphone-code-browser" target="_blank"&gt;two answers&lt;/a&gt;: Air Sharing and Mobile Me. I decided to go with Air Sharing because I like paying the fixed price of $4.99 instead of being sucked in by the free trial.&lt;/p&gt;
&lt;p&gt;The way it works is you connect to your iPhone from your Mac via Wi-Fi, copy your files and folders to your phone and browse them anywhere thereafter, even offline.&lt;/p&gt;
&lt;p&gt;I've had a very good experience with it so far: it can read most well-known file formats including pictures, PDFs, Word, Excel and even source code. I can load the django source tree and read the code whenever I'm thinking about implementing a solution. It even colors the code.&lt;/p&gt;
&lt;p&gt;In my opinion, well worth the money. Here is the click by click:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Buy and download &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=289943355&amp;amp;mt=8" target="_blank"&gt;Air Sharing&lt;/a&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=289943355&amp;amp;mt=8" target="_blank"&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-1.png" border="0" alt="" align="middle" /&gt;&lt;!--more--&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure the phone and your laptop are on the same wifi network. I recommend doing this at home on your secure network.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Start the app and click the &lt;em&gt;Done&lt;/em&gt; button&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-2.png" alt="" width="320" height="480" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click the wheel with spokes on it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-3.png" alt="" width="320" height="480" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write down the IP address (for example http://192.168.1.113:8080) of your phone as is displayed just below the Port field&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-4.png" alt="" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to your Mac and connect to your phone by clicking and holding the Finder icon and selecting &lt;em&gt;Connect to Server...&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-5.png" alt="" width="267" height="225" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt; In the &lt;strong&gt;Server Address&lt;/strong&gt; box, enter your phone's IP address (for example http://192.168.1.113:8080) and click &lt;em&gt;Connect&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-6.png" alt="" width="548" height="321" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Drag and drop the folders you want to see, wait for the transfer to complete (it's quite slow)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-7.png" alt="" width="569" height="421" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;That's it! go to the Air Sharing app and browse your files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-8.png" alt="" width="320" height="480" align="middle" /&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-9.png" alt="" width="320" height="480" align="middle" /&gt;Some parting words: one can never be too careful with security, so if you're going to be putting sensitive files on here you'll want to do 2 things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enable &lt;em&gt;Require Password&lt;/em&gt; under  &lt;strong&gt;Sharing Security&lt;/strong&gt; and enter a password. You can put files in your Public folder and enable Public Access if you want them accessed by guests.&lt;/li&gt;
&lt;li&gt;Click &lt;em&gt;Passcode&lt;/em&gt; under &lt;strong&gt;Application Lock&lt;/strong&gt; to set a code needed to access the device&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Also, if you have a PC you can use your browser to connect to your phone: just type the phone's IP address (that you found in the Air Sharing app's settings screen) in the browser's URL bar.If you like this but want to do more than just read (for example move files or sync with Mobile Me), you may want to look into the &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=312686749&amp;amp;mt=8" target="_blank"&gt;pro version&lt;/a&gt;, which does all that.&lt;/p&gt;
</description><guid>http://teebes.com/blog/8/click-by-click-how-to-browse-files-offline-on-the-iphone</guid></item><item><title>Me and my Apple TV: life after cable</title><link>http://teebes.com/blog/7/me-and-my-apple-tv-life-after-cable</link><description>&lt;p&gt;I've been meaning to document my home media setup for a while now because, well, I think it's pretty sweet and I very much enjoy the setup literally every day. Before I go into the details of how everything works, here are the key benefits of this setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I can watch 3TB worth of content (202 movies and 99 TV seasons at the moment) anytime I want at home. I don't have to get up and put anything in or out, it's just all always available. The data is protected against any single drive failure.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;The computers are set up to backup over Wi-Fi without me having to even think about it&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;The computers can play music on the Bose speakers by taking over the &lt;a href="http://www.apple.com/appletv/" target="_blank"&gt;Apple TV&lt;/a&gt;'s speaker output (this is an iTunes feature)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;In addition to playing games, I can use the &lt;a href="http://www.xbox.com/en-US/default.htm" target="_blank"&gt;XBox 360&lt;/a&gt; for DVDs and to stream instantly from &lt;a href="http://www.netflix.com/MemberHome" target="_blank"&gt;Netflix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I can remote control the Apple TV with my iPhone! (via the excellent &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=284417350&amp;amp;mt=8" target="_blank"&gt;Remote&lt;/a&gt; app)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://teebes.com/images/2009-07/multimedia-setup.png" alt="Apple TV with extra storage multimedia setup" width="492" height="390" align="middle" /&gt;&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;A couple of years ago I noticed that some of my old DVDs were no longer playing properly simply due to the disks aging, and a number of others had scratches and dents that made them glitchy. I looked around for good digital solutions, and the Apple TV caught my eye. The main problem for me was its hard-drive limitation, which at the time was around 140gigs. Given that I had a DVD library of well over 300 titles (I didn't convert them all), there was no way that was going to cut it.&lt;/p&gt;
&lt;p&gt;I was also getting to the point where I could no longer take commercial interruptions to the shows I love to watch, and I was determined to try to do something about it.&lt;/p&gt;
&lt;p&gt;I started looking around for good storage solutions, and after a lot of time spent researching, I settled on the &lt;a href="http://drobo.com/" target="_blank"&gt;Drobo&lt;/a&gt;. It's incredibly easy to set up and use, it protects you against any one of the hard-drives failing, and it's easy to expand the capacity later on. Of course the challenge was making the Apple TV use an external source for its data while still allowing me to easily manage the whole thing.&lt;img src="http://teebes.com/images/2009-07/multimedia-rack.png" border="0" alt="The multimedia setup below my TV" hspace="0" width="400" height="266" align="right" /&gt;&lt;/p&gt;
&lt;p&gt;Thankfully, the Apple TV lets you define an external iTunes instance as its main library as long as it is connected to the same network. I also knew that I wanted to have a robust solution to back up the important data on the various computers that my fiancee and I own. That's where the &lt;a href="http://www.apple.com/macmini/" target="_blank"&gt;Mac Mini&lt;/a&gt; comes in, which serves mainly as an iTunes library but also as a sharing point for the &lt;a href="http://www.maxtor.com/en/index.html" target="_blank"&gt;Maxtor&lt;/a&gt; drive (backup) and Drobo (media) to the network.&lt;/p&gt;
&lt;p&gt;Here is what each node does:&lt;/p&gt;
&lt;h3&gt;Drobo:&lt;/h3&gt;
&lt;p&gt;The Drobo has four 1TB hard-drives. Internally they are setup as a RAID 5, which means that each drive is backed up by the other three, and therefore any one of them can fail without losing any data. If one does (it's happened once already), just replace it and the data is internally re-laid out. This means that you get 3TB usable out of the 4TB capacity that you have. The really nice thing here is that the Drobo does all of this without you having to worry about any of it. Just plug it in and it works.&lt;/p&gt;
&lt;p&gt;In the interest of full disclosure, I did have some issues at first, but this was due to one of the Western Digital hard-drives, and Drobo's customer support was incredibly helpful. They actually identified via a diagnostics file I ran for them which drive was bad and generated the return forms for me. Pretty sweet.&lt;/p&gt;
&lt;h3&gt;Maxtor:&lt;/h3&gt;
&lt;p&gt;This is a 500GB external drive that I use to backup my MacBook Pro via Time Machine. My hard drive has fried once on me already and I've got to say that both this drive and Time Machine worked flawlessly. It could also be used by my fiancee for her backup needs but she prefers a system where she has a shared folder on the Drobo that she drags and drops stuff to (to each their own!).&lt;/p&gt;
&lt;p&gt;It has no RAID protection internally, but that's not a problem for me given how the odds of both it and the MacBook Pro going down are relatively small. If you want absolute peace of mind, you have to look at cloud storage. &lt;a href="http://blog.etilities.com/2008/10/remote-data-backup-the-final-component/" target="_blank"&gt;Here is an awesome article on the subject&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;h3&gt;Mac Mini:&lt;/h3&gt;
&lt;p&gt;&lt;img src="http://teebes.com/images/2009-07/mac-screensharing.png" alt="Controlling the mini's iTunes instance that the Apple TV is conected to" width="300" height="240" align="right" /&gt;It's connected via USB to the Drobo and Maxtor drive, and to the router via a regular ethernet cord. It runs iTunes (set to launch automatically on boot) and screen sharing so that it can be controlled via the MacBook Pros. It also runs uTorrent to automatically manage torrents I'm interested in as they become available. Since I know the Mini is always on, this is a good place for that to happen. I haven't set it up so I can access it from anywhere yet, but I'm definitely thinking about it. This is probably a little more expensive than it needs to be for what it does, and if you're the crafty kind you can definitely &lt;a href="http://lifehacker.com/5122782/build-a-hackintosh-for-under-240" target="_blank"&gt;build yourself a hackintosh&lt;/a&gt; for far less.&lt;/p&gt;
&lt;h3&gt;Apple TV:&lt;/h3&gt;
&lt;p&gt;The Apple TV's main purpose is to play movies and shows on the actual Samsung TV. A quick note here that Apple is really shooting itself in the foot with the naming of that thing because everyone I talk to about this always says "oh that's cool. And how big is it?" The initial impression is always that the Apple TV comes with a screen just like an iMac would.&lt;/p&gt;
&lt;p&gt;Additionally, I can rent or buy stuff directly through the Apple TV, and it gets automatically synced to the Drobo. I have it connected to the router via a physical cord, but you can also do it via Wi-Fi. It also serves the purpose of a hub between the Bose speakers and the Computers. iTunes has this really sweet option to select any speaker connected to another iTunes instance. And since the Apple TV redirects all audio output to the TV and the TV redirects all audio output to the Bose system, any one of the laptops/desktops can take over the speakers as long it's running iTunes. All you have to do is add it as a shared library. Here are some pointers if you're here trying to get this to work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt; &lt;img src="http://teebes.com/images/2009-07/apple-tv-sharing.png" alt="The Apple TV sharing itself with other iTunes library" align="right" /&gt;To manage your Apple TV's connections, go to Settings &amp;gt; Computers.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;The 'Connect to iTunes' link adds your 'main library'. It should be the iTunes instance that's on the computer that will always be on. Obviously it should be a desktop that is always powered and always connected to the router.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;The 'Add Shared iTunes Library' can add any other computer. The only difference in between the main library and a shared library is that former is accessed via 'My Movies', 'My TV Shows', etc. while the latter are accessed via 'Shared Movies', 'Shared TV Shows'.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt; You can play Movies, TV Shows, Podcasts, YouTube videos, and set your own pictures as the TV's scrolling screensaver when it's idle.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;TV:&lt;/h3&gt;
&lt;p&gt;It's a &lt;a href="http://www.samsung.com/us/consumer/detail/detail.do?group=televisions&amp;amp;type=televisions&amp;amp;subtype=lcdtv&amp;amp;model_cd=LN40B530P7NXZA" target="_blank"&gt;Samsung 40" LCD 1080p HDTV&lt;/a&gt; connected to the XBox, Apple TV and speakers. Not much to say here except that it's a really great picture quality, incidentally.&lt;/p&gt;
&lt;h3&gt;XBox 360:&lt;/h3&gt;
&lt;p&gt;Of course this is mainly to get my &lt;a href="http://shoryuken.com/" target="_blank"&gt;Street Fighter IV&lt;/a&gt; fix, but it also serves nicely to play DVDs whenever someone lends us a good series and to stream instantly from Netflix online.&lt;/p&gt;
&lt;h3&gt;Speakers:&lt;/h3&gt;
&lt;p&gt;&lt;img src="http://teebes.com/images/2009-07/apple-tv-speakers.png" alt="Using iTunes to connect to remote speaker" width="289" height="203" align="right" /&gt;I use the &lt;a href="http://www.bose.com/controller?url=/shop_online/home_theater/home_theater_speakers/cinemate/index.jsp" target="_blank"&gt;Bose Cinemate&lt;/a&gt; system, it's a very nice sound quality although at some point I'll definitely want a sound-round setup. The nice thing here is that the laptops and desktops can play music on them over Wi-Fi via iTunes.&lt;/p&gt;
&lt;p&gt;Well, that about concludes it. I'm very happy with this setup, but in the interest of fairness here are its shortcomings so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I always have to wait a day or two before I can see the latest episode of a show I'm really into (for example Lost) just because they take a little bit to get onto iTunes so far. This wouldn't be that big a deal except for the fact that all of the Facebook people usually blast spoilers minutes after the end of the episode. Thankfully I don't mind spoilers too much but still.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;No access to Hulu (yet). There would be ways to set this up via Boxee on the desktop PC I have lying around but since Hulu has &lt;a href="http://lifehacker.com/5156151/hulu-blocks-boxee-tvcom-kicks-early-adopters-in-the-face" target="_blank"&gt;specifically started blocking them&lt;/a&gt; (grrrr) it's been harder to get it working.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;That's it!&lt;/li&gt;
&lt;/ul&gt;
</description><guid>http://teebes.com/blog/7/me-and-my-apple-tv-life-after-cable</guid></item><item><title>The state of the browser-os market</title><link>http://teebes.com/blog/6/the-state-of-the-browser-os-market</link><description>&lt;p&gt;Today I was going to write a long-winded post about &lt;a href="http://googleblog.blogspot.com/2009/07/introducing-google-chrome-os.html" target="_blank"&gt;Chrome OS&lt;/a&gt;, &lt;a href="http://www.engadget.com/2009/07/09/silverlight-3-out-of-beta-joins-forces-with-your-gpu-for-hd-str/" target="_blank"&gt;Silverlight&lt;/a&gt;, Firefox's support of &lt;a href="http://arstechnica.com/open-source/news/2009/07/firefox-stability-to-get-a-boost-with-multiprocess-browsing.ars" target="_blank"&gt;multiprocess browsing&lt;/a&gt;, and overall the interesting turning point we are at in terms of browsers slowly becoming operating systems in their own rights. But as I started writing I kept on having this image pop in my head, which I think illustrates my point nicely. So I hopped on Photoshop and here it is.&lt;img src="http://www.teebes.com/images/state_of_the_market.png" alt="" align="middle" /&gt;&lt;/p&gt;
</description><guid>http://teebes.com/blog/6/the-state-of-the-browser-os-market</guid></item><item><title>Three ways to manage your passwords (across multiple computers)</title><link>http://teebes.com/blog/5/three-ways-to-manage-your-passwords-across-multiple-computers</link><description>&lt;p&gt;&lt;img src="http://www.teebes.com/images/lock_keyboard.jpg" alt="" width="125" height="186" align="right" /&gt;Having to manage the hundreds of user names and passwords you've had to create over the last ten years can suck. If you also need to keep them synchronized across multiple computers in OSX, Windows, and Linux, it can get ridiculous. Luckily I've been down this road before, and here are my three recommendations. I personally use XP, Leopard, and Ubuntu, and these setups work for me.&lt;/p&gt;
&lt;h5&gt;Firefox and Weave / Password Exporter&lt;/h5&gt;
&lt;p&gt;&lt;a href="http://www.mozilla.com/en-US/firefox/upgrade.html" target="_blank"&gt;Firefox&lt;/a&gt; comes built-in with a great password manager to handle all of the web sites that you need to store credentials from, and it's very intuitive and non-intrusive. Remember to create a Master Password in the Security settings so that no one can see your saved passwords. One of Firefox's biggest assets is its vast community of extension developers, and this comes in very handy to keep passwords synchronized:&lt;/p&gt;
&lt;!--more--&gt; 
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://labs.mozilla.com/projects/weave/" target="_blank"&gt;Weave&lt;/a&gt; synchronizes your Firefox bookmarks, history, passwords, preferences, even tabs across browser instances. Your data is saved on the Mozilla servers but if you'd rather use your own &lt;a href="http://marios.tziortzis.com/page/blog/article/setting-up-mozilla-weave-on-your-server/" target="_blank"&gt;you can do that too&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/2848" target="_blank"&gt;Password Exporter&lt;/a&gt; can export and import your Firefox passwords in XML and CSV formats. Obviously you'll need to export/import quite often, but if you don't trust your data in the cloud then this could be a good alternative for you.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;KeePass and DropBox&lt;/h5&gt;
&lt;p&gt;KeePass is an open-source, well-known, secure password manager. It has all the bells and whistles, including a handy password generator for when you want nice random strong passwords. Here are all of the download links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://downloads.sourceforge.net/keepass/KeePass-1.16-Setup.exe" target="_blank"&gt;Windows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://downloads.sourceforge.net/keepassx/KeePassX-0.4.0.dmg" target="_blank"&gt;OSX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://launchpad.net/~keepassx/+archive/ppa" target="_blank"&gt;Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In terms of having the passwords available across multiple computers, you have a couple options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.getdropbox.com/" target="_blank"&gt;Dropbox&lt;/a&gt; is essentially a cloud backup service which safely stores up to 2GB for free. It's a small program that runs on your computer and creates a folder whose contents are shared across all of your other computers (you install it on every computer). What you can then do is have all of your KeePass instances use one same password file in the shared folder, always synchronized as long as they are online.&lt;/li&gt;
&lt;li&gt;Again, if you're someone who doesn't trust having data in the cloud, you can download and install the &lt;a href="http://downloads.sourceforge.net/keepass/KeePass-1.16.zip" target="_blank"&gt;Portable&lt;/a&gt; version of KeePass on a USB drive and carry it around. You can then plug it in to any computer you go to and retrieve your passwords safely.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Clipperz&lt;/h5&gt;
&lt;p&gt;&lt;a href="https://www.clipperz.com" target="_blank"&gt;Clipperz&lt;/a&gt; is a password repository in the cloud. It saves your data on its server and it (understandably) goes to great lengths to keep your data secure. For example, you cannot recover a lost password. If you forget it, you're out of luck. One great feature of Clipperz is that it has 'direct logins' so that once you've saved your credentials for a specific site it will create a one-click login link to it and place it in a easily accessible launch box. This means that you can use Clipperz as a launcher for all of your online sites without needing to worry about credentials at all.Obviously if you're someone who has issues with having your data saved in the cloud there is not much you can do in the way of alternatives. If you're the more paranoid kind, don't use Clipperz. But if you're not, it offers a nice centralized way of dealing with all of your web passwords.&lt;/p&gt;
</description><guid>http://teebes.com/blog/5/three-ways-to-manage-your-passwords-across-multiple-computers</guid></item><item><title>What the iPhone OS is still missing</title><link>http://teebes.com/blog/4/what-the-iphone-os-is-still-missing</link><description>&lt;p&gt;&lt;img src="http://teebes.com/images/iphone-os-3.0.png" alt="" width="209" height="98" align="right" /&gt;Last week was pretty big for me, on a personal level: I upgraded both my phone's hardware (ditched my 'old' 1st generation iPhone for the latest 3GS) and software (the latest 3.0 software). So, if I were to die tomorrow, could I say that I've seen all I wanted to see out of Steve Jobs' brain child? almost, but not quite yet.&lt;/p&gt;
&lt;p&gt;The single biggest improvement in the 3.0 OS for me was the landscape keyboard. I love being able to SMS and e-mail in landscape mode, it's seriously made me feel like the phone could really start to replace the netbook, at least for what I need. Spotlight is pretty cool, finally seeing copy and paste was like an old wound suddenly no longer itching, and the speed improvements are quite noticeable. So what's missing?&lt;/p&gt;
&lt;h3&gt;&lt;!--more--&gt; Wi-Fi, scheduled  syncing&lt;/h3&gt;
&lt;p&gt;I'm a big fan of software performing tasks that I would otherwise need to do. When I get up in the morning, I want my podcasts all synced up for when I go the gym and then to work. I don't want to have to worry about it. I should be able to not only sync the iPhone wirelessly, but also to schedule it. So if I want to sync it automatically at 6 a.m. (an hour after iTunes checked for podcast updates), I should be able to. Wirelessly.&lt;/p&gt;
&lt;h3&gt;A lot more control over the typing correction process&lt;/h3&gt;
&lt;p&gt;This may seem like a minor thing at first, but I use my iPhone to take all sorts of notes from ideas to blog posts to algorithms. I can summarize my beefs in three points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can't control what happens by default: if you type a word that is not in the dictionary and slightly off, when the correction box pop-up you have to touch it or it will auto-correct it. I understand that a lot of people like it that way, but I don't. I should be able to configure it so that the opposite happens: I have to confirm that I want to change it.&lt;/li&gt;
&lt;li&gt;You can't edit the dictionary: come on. Let me add some words that I use all the time that are not in the stock dictionary. It shouldn't be that difficult to implement.&lt;/li&gt;
&lt;li&gt;No visual indication when a word is not in the dictionary. It can be subtle, but I'd like to see (provided I can edit it). It would be a nice proofreading capability.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Safari functional improvements&lt;/h3&gt;
&lt;p&gt;I should probably say before anything else that Apple needs to allow other browsers on the platform. Only having Safari reminds me of the Microsoft era of forcing IE down everyone's throat. Anyway... here are the beefs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No ability to search the pages: I was pretty surprised by this given the spotlight and mail search additions. I'd like to be able to type keywords and see where in the page they occur. Basic browser functionality really.&lt;/li&gt;
&lt;li&gt;No ability to save the pages: if I know I'm about to hop on a 4 hour plane I'd like to be able to save a few long-ish articles to read while offline. Sometimes the cache expires on the web page quicker than I'd like and I can't view the pages I was looking at.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;More access to the OS&lt;/h3&gt;
&lt;p&gt;Overall, and I realize this is probably not something Apple will do for reasons I won't get into here, I wish I had more access to the OS. I wish I could open a terminal window and run quick python one-liners in the interpreter. I wish I could play with it more. I understand that a lot of this can be done via jailbreaking but I shouldn't have to. I should have a legal way of playing with the guts of the machine.&lt;/p&gt;
&lt;p&gt;That's about it really. In the grand scheme of things these are small things, but I don't think that any of them would be that difficult to implement.&lt;/p&gt;
</description><guid>http://teebes.com/blog/4/what-the-iphone-os-is-still-missing</guid></item><item><title>Review of Renegade: The Making of a President</title><link>http://teebes.com/blog/3/review-of-renegade-the-making-of-a-president</link><description>&lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/0307463125/ref=cm_rdp_product_img" target="_blank"&gt;&lt;img src="http://teebes.com/images/renegade-cover.png" alt="" width="156" height="236" align="right" /&gt;&lt;/a&gt;How will people speak of Barack Obama in two hundred years? Regardless of what else happens from now until the end of his life, the historic primaries and general election of 2008 will always be an important chapter of both his life and our country's history. Down the road, people will look to our contemporary's recollections of the facts to understand what happened, and why. Although this book will offer much valuable information, it will always be a one-sided account of these events.&lt;/p&gt;
&lt;p&gt;Renegade definitely has its moments. It gives great insight into how carefully the decision to run was taken, and has unique insider accounts of the most talked about moments of the campaign: the Wright controversy, the conflicts with the Clintons, the VP selection, the transition, Palin, the financial collapse, and much more.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Where the book was strongest to me was when it describes what it's like to run a presidential bid, just how demanding and almost inhuman it is. One particularly interesting bit describes how Obama wrote his famous &lt;a href="http://www.youtube.com/watch?v=pWe7wTVbLUU" target="_blank"&gt;speech on race in Americe&lt;/a&gt;, which the candidate wrote himself on the two nights before the speech:&lt;/p&gt;
&lt;blockquote&gt;"The next night, he took Favreau's outline and started writing after he put his daughters to bed. By three in the morning, he had finished half the speech. Five hours later, he was en route to Pennsylvania for a full day of campaign events.. At 10:00 p.m. on Monday night, he retired to his hotel room to write the second half of the speech that he would deliver the next morning. By 2:00 a.m. on Tuesday, he had e-mailed the speech to his senior staff."&lt;/blockquote&gt;
&lt;p&gt;What is unfortunate is that these factual accounts of the campaign constitute maybe half of the book. The other half seems like a puff piece aimed at justifying Obama policies during the campaign. There is a good Amazon review titled "&lt;a href="http://www.amazon.com/review/R1XJ12U9Y8BJKN/ref=cm_cr_rdp_perm" target="_blank"&gt;What do you call cheerleading after the game is over?&lt;/a&gt;" that echoes much of how I feel about the author's partisanship. Politico &lt;a href="http://www.politico.com/news/stories/0609/23252.html" target="_blank"&gt;ran a piece on the book&lt;/a&gt; before it was published where it explains how Wolffe never covered any of the McCain campaign. He became a close friend of the candidate, even being admitted into the close-knit circle of people Obama played Basketball with. The idea of the book itself was actually pitched by Obama, promising the journalist the access he would need to write it. As Wolffe himself says in the afterword:&lt;/p&gt;
&lt;blockquote&gt;"publishers want partisan screeds nowadays. They don't want reporting."&lt;/blockquote&gt;
&lt;p&gt;Indeed, and that is exactly what the author has given his publishers.&lt;/p&gt;
&lt;p&gt;One thing I was hoping to find in the book was insights into how the campaign managed to raise almost three quarters of a billion dollars. What did the then-candidate have to promise, and to whom, to get such incredible contributions? The notion that he raised so much from small online donation is clearly debunked by Wolffe who shows that first came the big sums from wealthy donors. The grassroots campaign in Iowa required very large amounts of money, way before the campaign gained the popularity that it did by the time the primaries were over. Although Obama may have broken some of the rules of presidential campaigns, I very much doubt that he managed to alter the fundamental rule that in these high circles of power, nothing is free. Nothing is given.&lt;/p&gt;
&lt;p&gt;I give the book a 3 out of 5, with great inside accounts of the campaign, but ultimately partisan enough so that I truly wonder how Richard Wolffe can call himself a Journalist.&lt;/p&gt;
</description><guid>http://teebes.com/blog/3/review-of-renegade-the-making-of-a-president</guid></item></channel></rss>
