<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0"><id>tag:blogger.com,1999:blog-2246259935483101149</id><updated>2012-01-30T17:41:15.550-05:00</updated><title type="text">Bolinfest Changeblog</title><subtitle type="html">When bolinfest.com changes, you'll be the first to know.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default?start-index=26&amp;max-results=25" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>96</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/BolinfestChangeblogRSS" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="bolinfestchangeblogrss" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-8535372130701537030</id><published>2011-10-28T17:33:00.005-04:00</published><updated>2011-10-28T18:18:15.165-04:00</updated><title type="text">I want a magical operator to assuage my async woes (and a pony)</title><content type="html">Lately, I have spent a lot of time thinking about how I could reduce the tedium of async programming in JavaScript. For example, consider a typical implementation of using an &lt;code&gt;XMLHttpRequest&lt;/code&gt; to do a &lt;code&gt;GET&lt;/code&gt; that returns a Deferred (this example uses &lt;a href="http://api.jquery.com/category/deferred-object/"&gt;jQuery&lt;/a&gt;'s implementation of Deferred, but there are &lt;a href="http://mochi.github.com/mochikit/doc/html/MochiKit/Async.html"&gt;many&lt;/a&gt; &lt;a href="http://code.google.com/p/closure-library/source/browse/trunk/third_party/closure/goog/mochikit/async/deferred.js?r=195"&gt;other&lt;/a&gt; reasonable implementations, and there is a great need to settle on a standard API, but that is a subject for another post):&lt;pre class="prettyprint"&gt;/** @return {Deferred} */&lt;br /&gt;var simpleGet = function(url) {&lt;br /&gt;  var deferred = new $.Deferred();&lt;br /&gt;&lt;br /&gt;  var xhr = new XMLHttpRequest();&lt;br /&gt;  xhr.onreadystatechange = function() {&lt;br /&gt;    if (xhr.readyState == 4) {&lt;br /&gt;      if (xhr.status == 200) {&lt;br /&gt;        deferred.resolve(xhr.responseText);&lt;br /&gt;      } else {&lt;br /&gt;        deferred.reject(xhr.status);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;  xhr.open('GET', url, true /* async */);&lt;br /&gt;  xhr.send(null);  &lt;br /&gt;&lt;br /&gt;  return deferred;&lt;br /&gt;};&lt;/pre&gt;What I want is a magical &lt;code style="color:green"&gt;~&lt;/code&gt; operator that requires (and understands) an object that implements a well-defined Deferred contract so I can write my code in a linear fashion:&lt;pre class="prettyprint"&gt;/** @return {Deferred} */&lt;br /&gt;var getTitle = function(url) {&lt;br /&gt;  if (url.substring(0, 7) != 'http://') url = 'http://' + url;&lt;br /&gt;  var html = ~simpleGet(url);&lt;br /&gt;  var title = html.match(/&amp;lt;title&gt;(.*)&amp;lt;\/title&gt;/)[1];&lt;br /&gt;  return title;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;/** Completes asynchronously, but does not return a value. */&lt;br /&gt;var logTitle = function(url) {&lt;br /&gt;  try {&lt;br /&gt;    var title = ~getTitle(url);&lt;br /&gt;    console.log(title);&lt;br /&gt;  } catch (e) {&lt;br /&gt;    console.log('Could not extract title from ' + url);&lt;br /&gt;  }&lt;br /&gt;};&lt;/pre&gt;Unfortunately, to get this type of behavior today, I have to write something like the following (and even then, I am not sure whether the error handling is quite right):&lt;pre class="prettyprint"&gt;/** @return {Deferred} */&lt;br /&gt;var getTitle = function(url) {&lt;br /&gt;  if (url.substring(0, 7) != 'http://') url = 'http://' + url;&lt;br /&gt;&lt;br /&gt;  var deferred = new $.Deferred();&lt;br /&gt;  &lt;br /&gt;  simpleGet(url).then(function(html) {&lt;br /&gt;    var title = html.match(/&amp;lt;title&gt;(.*)&amp;lt;\/title&gt;/)[1];&lt;br /&gt;    deferred.resolve(title);&lt;br /&gt;  }, function(error) {&lt;br /&gt;    deferred.reject(error);&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt;  return deferred;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;/** Completes asynchronously, but does not return a value. */&lt;br /&gt;var logTitle = function(url) {&lt;br /&gt;  var deferred = getTitle(url);&lt;br /&gt;  deferred.then(function(title) {&lt;br /&gt;    console.log(title);&lt;br /&gt;  }, function(error) {&lt;br /&gt;    console.log('Could not extract title from ' + url);&lt;br /&gt;  })&lt;br /&gt;};&lt;/pre&gt;I am curious how difficult it would be to programmatically translate the first into the second. I spent some time playing with &lt;a href="https://developer.mozilla.org/en/New_in_JavaScript_1.7"&gt;generators in Firefox&lt;/a&gt;, but I could not seem to figure out how to emulate my desired behavior. I also spent some time looking at the &lt;a href="http://wiki.ecmascript.org/doku.php?id=strawman:async_functions&amp;s=async"&gt;ECMAScript&lt;/a&gt; &lt;a href="http://wiki.ecmascript.org/doku.php?id=strawman:deferred_functions"&gt;wiki&lt;/a&gt;, but it is unclear whether they are talking about exactly the same thing.&lt;br /&gt;&lt;br /&gt;In terms of modern alternatives, it appears that &lt;a href="http://blogs.msdn.com/b/csharpfaq/archive/2010/10/28/async.aspx"&gt;C#'s await and async keywords&lt;/a&gt; are the closest thing to what I want right now. Unfortunately, I want to end up with succinct JavaScript that runs in the browser, so I'm hoping that either &lt;a href="http://jashkenas.github.com/coffee-script/"&gt;CoffeeScript&lt;/a&gt; or &lt;a href="http://www.dartlang.org/"&gt;Dart&lt;/a&gt; will solve this problem, unless the ECMAScript committee gets to it first!&lt;br /&gt;&lt;br /&gt;Please feel free to add pointers to related resources in the comments. There is a lot out there to read these days (the Dart mailing list alone is fairly overwhelming), so there's a good chance that there is something important that I have missed.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update (Fri Oct 28, 6:15pm):&lt;/b&gt; I might be able to achieve what I want using &lt;a href="http://code.google.com/p/traceur-compiler/wiki/LanguageFeatures#Deferred_Functions"&gt;deferred functions&lt;/a&gt; in &lt;a href="http://code.google.com/p/traceur-compiler/"&gt;Traceur&lt;/a&gt;. Apparently I should have been looking at the &lt;a href="http://wiki.ecmascript.org/doku.php?id=strawman:deferred_functions"&gt;deferred functions&lt;/a&gt; strawman proposal more closely: I skimmed it and assumed it was only about defining a Deferred API.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn about a suite of tools to help manage a large JavaScript codebase? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-8535372130701537030?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/8535372130701537030/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/10/i-want-magical-operator-to-assuage-my.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/8535372130701537030" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/8535372130701537030" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/10/i-want-magical-operator-to-assuage-my.html" title="I want a magical operator to assuage my async woes (and a pony)" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-8543348108473016250</id><published>2011-08-02T13:44:00.000-04:00</published><updated>2011-08-02T13:44:21.341-04:00</updated><title type="text">An Examination of goog.base()</title><content type="html">A few weeks ago, I started working on &lt;a href="https://github.com/bolinfest/coffee-script"&gt;adding an option to CoffeeScript to spit out Closure-Compiler-friendly JavaScript&lt;/a&gt;. In the process, I discovered that calls to a superclass constructor in CoffeeScript look slightly different than they do in the Closure Library. For example, if you have a class &lt;code&gt;Foo&lt;/code&gt; and a subclass &lt;code&gt;Bar&lt;/code&gt;, then in CoffeeScript, the call [in the generated JavaScript] to invoke &lt;code&gt;Foo&lt;/code&gt;'s constructor from &lt;code&gt;Bar&lt;/code&gt;'s looks like:&lt;br /&gt;&lt;pre&gt;Bar.__super__.constructor.call(this, a, b);&lt;/pre&gt;whereas in the Closure Library, the canonical thing is to do the following, identifying the superclass function directly:&lt;br /&gt;&lt;pre&gt;Foo.call(this, a, b);&lt;/pre&gt;The two are functionally equivalent, though CoffeeScript's turns out to be slightly simpler to use as a developer because it does not require the author to know the name of the superclass when writing the line of code. In the case of CoffeeScript, where JavaScript code generation is being done, this localization of information makes the translation of CoffeeScript to JavaScript easier to implement.&lt;br /&gt;&lt;br /&gt;The only minor drawback to using the CoffeeScript form when using Closure (though note that you would have to use &lt;code&gt;superClass_&lt;/code&gt; instead of &lt;code&gt;__super__&lt;/code&gt;) is that the CoffeeScript call is more bytes of code. Unfortunately, the Closure Compiler does not know that &lt;code&gt;Bar.superClass_.constructor&lt;/code&gt; is equivalent to &lt;code&gt;Foo&lt;/code&gt;, so it does not rewrite it as such, though such logic could be added to the Compiler.&lt;br /&gt;&lt;br /&gt;This piqued my curiosity about how &lt;code&gt;goog.base()&lt;/code&gt; is handled by the Compiler, so I ended up taking a much deeper look at &lt;code&gt;goog.base()&lt;/code&gt; than I ever had before. I got so caught up in it that I ended up composing a new essay on what I learned: &lt;a href="http://bolinfest.com/essays/googbase.html"&gt;"An Examination of goog.base()."&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The upshot of all this is that in my CoffeeScript-to-Closure translation code, I am not going to translate any of CoffeeScript's &lt;code&gt;super()&lt;/code&gt; calls into &lt;code&gt;goog.base()&lt;/code&gt; calls because avoiding &lt;code&gt;goog.base()&lt;/code&gt; eliminates a couple of issues. I will still use &lt;code&gt;goog.base()&lt;/code&gt; when writing Closure code by hand, but if Closure code is being autogenerated anyway, then using &lt;code&gt;goog.base()&lt;/code&gt; is not as compelling.&lt;br /&gt;&lt;br /&gt;Finally, if you're wondering why I started this project a few weeks ago and have not made any progress on the code since then, it is because I got married and went on a honeymoon, so at least my wife and I would consider that a pretty good excuse!&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-8543348108473016250?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/8543348108473016250/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/08/examination-of-googbase.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/8543348108473016250" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/8543348108473016250" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/08/examination-of-googbase.html" title="An Examination of goog.base()" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-4960493779754010699</id><published>2011-07-01T16:10:00.000-04:00</published><updated>2011-07-01T16:10:08.006-04:00</updated><title type="text">Writing useful JavaScript applications in less than half the size of jQuery</title><content type="html">Not too long ago, I tried to &lt;a href="http://blog.bolinfest.com/2011/04/jquerycom-uses-only-34-of-jquery.html"&gt;bring attention to how little of the jQuery library many developers actually use&lt;/a&gt; and argue that frontend developers should consider what sort of improvements their users would see if they could compile their code with the &lt;a href="http://code.google.com/closure/compiler/docs/api-tutorial3.html"&gt;Advanced mode of the Closure Compiler&lt;/a&gt;. Today I would like to further that argument by taking a look at &lt;a href="https://chrome.google.com/webstore/detail/ifacldneidndihdbgfkglegmjohkplme"&gt;TargetAlert&lt;/a&gt;, my browser extension that I &lt;a href="http://blog.bolinfest.com/2011/06/triumphant-return-of-targetalert.html"&gt;re-released this week for Chrome&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;TargetAlert is built using &lt;a href="http://code.google.com/closure/"&gt;Closure&lt;/a&gt; and &lt;a href="http://www.plovr.com/"&gt;plovr&lt;/a&gt; using the &lt;a href="http://code.google.com/p/closure-chrome-extension-template/"&gt;template I created for developing a Chrome extension&lt;/a&gt;. The packaged version of the extension includes three JavaScript files:&lt;br /&gt;&lt;style&gt;.targetalert-js-stats {  border-collapse: collapse;}.targetalert-js-stats td, .targetalert-js-stats th {  border: 1px solid #CCC;}&lt;/style&gt;&lt;br /&gt;&lt;table border="1" class="targetalert-js-stats" cellpadding="3"&gt;  &lt;tr&gt;    &lt;th&gt;Name&lt;/th&gt;    &lt;th&gt;Size (bytes)&lt;/th&gt;    &lt;th&gt;Description&lt;/th&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;&lt;code&gt;targetalert.js&lt;/code&gt;&lt;/td&gt;    &lt;td style="text-align: right"&gt;19475&lt;/td&gt;    &lt;td&gt;content script that runs on every page&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;&lt;code&gt;options.js&lt;/code&gt;&lt;/td&gt;    &lt;td style="text-align: right"&gt;19569&lt;/td&gt;    &lt;td&gt;logic for the TargetAlert options page&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;&lt;code&gt;targetalert.js&lt;/code&gt;&lt;/td&gt;    &lt;td style="text-align: right"&gt;3590&lt;/td&gt;    &lt;td&gt;&lt;a href="http://code.google.com/chrome/extensions/background_pages.html"&gt;background page&lt;/a&gt; that channels information from the options to the content script&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td&gt;Total&lt;/td&gt;    &lt;td style="text-align: right"&gt;42634&lt;/td&gt;    &lt;td&gt;&amp;nbsp;&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;By comparison, &lt;a href="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"&gt;the minified version of jQuery 1.6&lt;/a&gt; is 91342 bytes, which is &lt;em&gt;more than twice the size of the code for TargetAlert&lt;/em&gt;. (The gzipped sizes are 14488 vs. 31953, so the relative sizes are the same, even when gzipped.)&lt;br /&gt;&lt;br /&gt;And to put things in perspective, here is the set of &lt;code&gt;goog.require()&lt;/code&gt; statements that appear in TargetAlert code, which reflects the extent of its dependencies:&lt;br /&gt;&lt;pre&gt;goog.require('goog.array');&lt;br /&gt;goog.require('goog.dispose');&lt;br /&gt;goog.require('goog.dom');&lt;br /&gt;goog.require('goog.dom.NodeIterator');&lt;br /&gt;goog.require('goog.dom.NodeType');&lt;br /&gt;goog.require('goog.dom.TagName');&lt;br /&gt;goog.require('goog.events');&lt;br /&gt;goog.require('goog.events.EventType');&lt;br /&gt;goog.require('goog.string');&lt;br /&gt;goog.require('goog.ui.Component');&lt;br /&gt;goog.require('goog.Uri');&lt;br /&gt;goog.require('soy');&lt;br /&gt;&lt;/pre&gt;I include this to demonstrate that there was no effort to re-implement parts of the Closure Library in order to save bytes. On the contrary, one of the primary reasons to use Closure is that you can write code in a natural, more readable way (which may be slightly more verbose), and make the Compiler responsible for minification. Although competitions like &lt;a href="http://js1k.com/"&gt;JS1K&lt;/a&gt; are fun and I'm amazed to see how small JS can get when it is hand-optimized, even the first winner of JS1K, &lt;a href="http://www.amazon.com/gp/product/1593272820/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217145&amp;creative=399369&amp;creativeASIN=1593272820"&gt;Marijn Haverbeke&lt;/a&gt;, admits, &lt;a href="http://marijnhaverbeke.nl/js1k/"&gt;"In terms of productivity, this is an awful way of coding."&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;When deploying a packaged browser extension, there are no caching benefits to consider: if your extension includes a copy of jQuery, then it adds an extra 30K to the user's download, even when gzipped. To avoid this, your extension could reference &lt;a href="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"&gt;https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js&lt;/a&gt; (or equivalent) from its code, but then it may not work when offline. Bear in mind that in some parts of the world (including the US! think about data plans for tablets), users have quotas for how much data they download, so you're helping them save a little money if you can package your resources more efficiently.&lt;br /&gt;&lt;br /&gt;Further, if your browser extension has a content script that runs on every page, keeping your JS small reduces the amount of code that will be executed on every page load, minimizing the impact of your extension on the user's browsing experience. As users may have many extensions installed, if everyone starts including an extra 30K of JS, then this additional tax can really start to add up! Maybe it's time you gave Closure a good look, if you haven't already.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-4960493779754010699?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/4960493779754010699/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/07/writing-useful-javascript-applications.html#comment-form" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/4960493779754010699" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/4960493779754010699" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/07/writing-useful-javascript-applications.html" title="Writing useful JavaScript applications in less than half the size of jQuery" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-5333082308853801798</id><published>2011-06-27T22:52:00.000-04:00</published><updated>2011-06-27T22:52:21.312-04:00</updated><title type="text">The Triumphant Return of TargetAlert!</title><content type="html">About seven years ago, my &lt;a href="http://people.csail.mit.edu/rcm/"&gt;adviser&lt;/a&gt; and I were sitting in his office Googling things as part of research for my thesis. I can't remember what we were looking for, but just after we clicked on a promising search result, the Adobe splash screen popped up. As if on cue, we both let out a groan in unison as we waited for the PDF plugin to load. In that instant, it struck me that I could build a small Firefox extension to make browsing the Web just a little bit better.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bolinfest.com/targetalert/version.html"&gt;Shortly thereafter&lt;/a&gt;, I created TargetAlert: a browser extension that would warn you when you were about to click on a PDF. It used the simple heuristic of checking whether the link ended in &lt;b&gt;pdf&lt;/b&gt;, and if so, it inserted a PDF icon at the end of the link &lt;a href="http://bolinfest.com/targetalert/"&gt;as shown on the original TargetAlert home page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;And that was it! My problem was solved. Now I was able to avoid inadvertently starting up Adobe Reader as I browsed the Web.&lt;br /&gt;&lt;br /&gt;But then I realized that there were other things on the Web that were irritating, too! Specifically, links that opened in new tabs without warning or those that started up Microsoft Office. Within a week, I added alerts for those types of links, as well.&lt;br /&gt;&lt;br /&gt;After adding those features, I should have been content with TargetAlert as it was and put it aside to focus on &lt;a href="http://groups.csail.mit.edu/uid/chickenfoot/index.php"&gt;my thesis&lt;/a&gt;, but then something incredible happened: &lt;a href="http://tech.slashdot.org/story/04/09/06/1228202/Exploring-Firefox-Extensions"&gt;I was Slashdotted&lt;/a&gt;! Suddenly, I had a lot more traffic to my site and many more users of TargetAlert, and I did not want to disappoint them, so I added a few more features and updated the web site. Bug reports came in (which I recorded), but it was my last year at MIT, and I was busy interviewing and &lt;a href="http://bolinfest.com/6.170/"&gt;TAing&lt;/a&gt; on top of my coursework and research, so updates to TargetAlert were sporadic after that. It wasn't until the summer between graduation and starting at Google that I had time to dig into TargetAlert again.&lt;br /&gt;&lt;br /&gt;Though the primary reason that TargetAlert development slowed is that &lt;em&gt;Firefox extension development should have been fun, but it wasn't&lt;/em&gt;. At the time, every time you made a change to your extension, you had to restart Firefox to pick up the change. As you can imagine, that made for a slow edit-reload-test cycle, inhibiting progress. Also, instead of using simple web technologies like HTML and JSON, Firefox encouraged the use of more obscure things, such as XUL and RDF. The bulk of my energy was spent on getting information into and out of TargetAlert's preferences dialog (because I actually tried to use XUL and RDF, as recommended by Mozilla), whereas the fun part of the extension was taking the user's preferences and applying them to the page.&lt;br /&gt;&lt;br /&gt;The #1 requested feature for TargetAlert was for users to be able to define their own alerts (as it were, users could only enable or disable the alerts that were built into TargetAlert). Conceptually, this was not a difficult problem, but realizing the solution in XUL and RDF was an incredible pain. As TargetAlert didn't generate any revenue and I had other personal projects (and &lt;a href="http://googleblog.blogspot.com/2006/04/its-about-time.html"&gt;work projects&lt;/a&gt;!) that were more interesting to me, I never got around to satisfying this feature request.&lt;br /&gt;&lt;br /&gt;Fast-forward to 2011 when I finally &lt;a href="http://twitter.com/#!/bolinfest/status/81162738306519040"&gt;decommissioned a VPS&lt;/a&gt; that I had been paying for since 2003. Even though I had rerouted all of its traffic to a new machine years ago and it was costing me money to keep it around, I put off taking it down because I knew that I needed to block out some time to get all of the important data off of it first, which included the original CVS repository for TargetAlert.&lt;br /&gt;&lt;br /&gt;As part of the data migration, I converted all of my CVS repositories to SVN and then to Hg, preserving all of the version history (it should have been possible to convert from CVS to Hg directly, but I couldn't get &lt;code&gt;hg convert&lt;/code&gt; to work with CVS). Once I had all of my code from MIT in a modern version control system, I started poking around to see which projects would still build and run. It turns out that I have been a stickler for creating &lt;code&gt;build.xml&lt;/code&gt; files for personal projects for quite some time, so I was able to compile more code than I would have expected!&lt;br /&gt;&lt;br /&gt;But then I took a look at TargetAlert. The JavaScript that I wrote in 2004 and 2005 looks gross compared to &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;the way I write JavaScript now&lt;/a&gt;. It's not even that it was totally disorganized -- it's just that I had been trying to figure out what the best practices were for Firefox/JavaScript development at the time, and they just didn't exist yet.&lt;br /&gt;&lt;br /&gt;Further, TargetAlert worked on pre-Firefox 1.0 releases through Firefox 2.0, so the code is full of hacks to make it work on those old versions of the browser that are now irrelevant. Oh, and what about XUL? Well, my go-to resource for XUL back in the day was &lt;a href="http://www.xulplanet.com/"&gt;xulplanet.com&lt;/a&gt;, though the site owners have decided to shut it down, which made making sense of that old code even more discouraging. Once again, digging into Firefox extension development to get TargetAlert to work on Firefox 4.0 did not appear to be much fun.&lt;br /&gt;&lt;br /&gt;Recently, I have been much more interested in building &lt;a href="http://code.google.com/chrome/extensions/docs.html"&gt;Chrome apps and extensions&lt;/a&gt; (Chrome is my primary browser, and unlike most people, I sincerely enjoy using a &lt;a href="http://en.wikipedia.org/wiki/Google_Chrome_OS#Cr-48_prototype_hardware"&gt;Cr-48&lt;/a&gt;), so I decided to port TargetAlert to Chrome. This turned out to be a fun project, especially because it forced me to touch a &lt;a href="http://code.google.com/chrome/extensions/content_scripts.html"&gt;number&lt;/a&gt; &lt;a href="http://code.google.com/chrome/extensions/background_pages.html"&gt;of&lt;/a&gt; &lt;a href="http://code.google.com/chrome/extensions/messaging.html"&gt;features&lt;/a&gt; of the Chrome API, so I ended up reading almost all of the documentation to get a complete view of what the API has to offer (hooray learning!).&lt;br /&gt;&lt;br /&gt;Compared to Firefox, the API for Chrome extension development seems much better designed and documented. Though to be fair, I don't believe that Chrome's API would be this good if it weren't able to leverage so many of the lessons learned from years of Firefox extension development. For example, &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/"&gt;Greasemonkey&lt;/a&gt; saw considerable success as a Firefox extension, which made it obvious that Chrome should make &lt;a href="http://code.google.com/chrome/extensions/content_scripts.html"&gt;content scripts&lt;/a&gt; an explicit part of its API. (It doesn't hurt that the creator of Greasemonkey, &lt;a href="http://www.aaronboodman.com/"&gt;Aaron Boodman&lt;/a&gt;, works on Chrome.) Also, where Firefox uses a &lt;a href="https://developer.mozilla.org/en/Chrome_Registration"&gt;wacky, custom manifest file format&lt;/a&gt; for one metadata file and an &lt;a href="https://developer.mozilla.org/en/install_manifests"&gt;ugly ass RDF format&lt;/a&gt; for another metadata file, Chrome uses &lt;a href="http://code.google.com/chrome/extensions/manifest.html"&gt;a single JSON file&lt;/a&gt;, which is a format that all web developers understand. (Though admittedly, having recently spent a bit of time with &lt;code&gt;manifest.json&lt;/code&gt; files for Chrome, I feel that the need for my &lt;a href="http://bolinfest.com/essays/json.html"&gt;suggested improvements to JSON&lt;/a&gt; is even more compelling.)&lt;br /&gt;&lt;br /&gt;As TargetAlert &lt;a href="https://chrome.google.com/webstore/detail/nofjbmcclkndhmnegkhkbakjgakchepg"&gt;was not the first Chrome extension I had developed&lt;/a&gt;, I already had some idea of how I would structure my new extension. I knew that I wanted to use both &lt;a href="http://code.google.com/closure/"&gt;Closure&lt;/a&gt; and &lt;a href="http://plovr.com/"&gt;plovr&lt;/a&gt; for development, which meant that there would be a quick build step so that I could benefit from the static checking of the Closure Compiler. Although changes to Chrome extensions do not require a restart to pick up any changes, they do often require navigating to &lt;code&gt;chrome://extensions&lt;/code&gt; and clicking the &lt;b&gt;Reload&lt;/b&gt; button for your extension. I decided that I wanted to eliminate that step, so I created a &lt;a href="http://code.google.com/p/closure-chrome-extension-template/"&gt;template for a Chrome extension that uses plovr&lt;/a&gt; in order to reduce the length of the edit-reload-test cycle. This enabled me to make fast progress and finally made extension development fun again! (The &lt;a href="http://code.google.com/p/closure-chrome-extension-template/source/browse/README.txt"&gt;README&lt;/a&gt; file for the template project has the details on how to use it to get up and running quickly.)&lt;br /&gt;&lt;br /&gt;I used the original code for TargetAlert as a guide (it had some workarounds for web page quirks that I wanted to make sure made it to the new version), and within a day, I had a &lt;a href="https://chrome.google.com/webstore/detail/ifacldneidndihdbgfkglegmjohkplme"&gt;new version of TargetAlert for Chrome&lt;/a&gt;! It had the majority of the features of the original TargetAlert (as well as some bug fixes), and I felt like I could finally check "resurrect TargetAlert" off of my list.&lt;br /&gt;&lt;br /&gt;Except I couldn't.&lt;br /&gt;&lt;br /&gt;A week after the release of my Chrome extension, I only had eight users according to my Chrome developer dashboard. Back in the day, TargetAlert had tens of thousands of users! This made me sad, so I decided that it was finally time to make the Chrome version of TargetAlert &lt;em&gt;better&lt;/em&gt; than the original Firefox verison: I was finally going to support user-defined alerts! Once I actually sat myself down to do the work, it was not very difficult at all. Because Chrome extensions have explicit support for an options page in HTML/JS/CSS that has access to &lt;code&gt;localStorage&lt;/code&gt;, building a UI that could read and write preferences was a problem that I have solved many times before. Further, being able to inspect and edit the &lt;code&gt;localStorage&lt;/code&gt; object from the JavaScript console in Chrome was much more pleasant than mucking with user preferences in &lt;code&gt;about:config&lt;/code&gt; in Firefox ever was.&lt;br /&gt;&lt;br /&gt;So after years of feature requests, Target Alert 0.6 for Chrome is my gift to you. Please &lt;a href="https://chrome.google.com/webstore/detail/ifacldneidndihdbgfkglegmjohkplme"&gt;install it&lt;/a&gt; and try it out! With the exception of the lack of translations in the Chrome version of TargetAlert (the Firefox version &lt;a href="http://bolinfest.com/targetalert/translation.html"&gt;had a dozen&lt;/a&gt;), the Chrome version is a significant improvement over the Firefox one: it's faster, supports user-defined alerts, and with the seven years of Web development experience that I've gained since the original, I fixed a number of bugs, too.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about web development and Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-5333082308853801798?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/5333082308853801798/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/06/triumphant-return-of-targetalert.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/5333082308853801798" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/5333082308853801798" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/06/triumphant-return-of-targetalert.html" title="The Triumphant Return of TargetAlert!" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-6303236780607500635</id><published>2011-06-09T14:00:00.186-04:00</published><updated>2011-06-09T15:36:39.876-04:00</updated><title type="text">It takes a village to build a Linux desktop</title><content type="html">&lt;b&gt;tl;dr&lt;/b&gt; Instead of trying to build your own Ubuntu PC, check out a site like &lt;a href="http://www.system76.com/"&gt;system76.com&lt;/a&gt; instead.&lt;br /&gt;&lt;p&gt;&lt;em&gt;In many ways, this post is more for me than for you&amp;mdash;I want to make sure I re-read this the next time I am choosing a desktop computer.&lt;/em&gt;&lt;br /&gt;&lt;p&gt;My Windows XP desktop from March 2007 finally died, so it was time for me to put together a new desktop for development. Because &lt;a href="http://blog.bolinfest.com/2010/12/i-got-new-computer-for-christmas-sorta.html"&gt;Ubuntu on my laptop&lt;/a&gt; had been working out so well, I decided that I would make my new machine an Ubuntu box, too. Historically, it was convenient to have a native Windows machine to test IE, Firefox, Chrome, Safari, and Opera, but Cygwin is a far cry from GNOME Terminal, so using Windows as my primary desktop environment had not been working out so well for me.&lt;br /&gt;&lt;p&gt;As a developer, I realize that my computer needs are different from an ordinary person's, but I didn't expect it would be so difficult to buy the type of computer that I wanted on the cheap (&amp;lt;$1000). Specifically, I was looking for:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;At least 8GB of RAM.&lt;br /&gt;&lt;li&gt;A 128GB solid state drive. (I would have been happy with 64GB because this machine is for development, not storing media, but &lt;a href="http://www.codinghorror.com/blog/2010/09/revisiting-solid-state-hard-drives.html"&gt;Jeff Atwood&lt;/a&gt; convinced me to go for 128GB, anyway.)&lt;br /&gt;&lt;li&gt;A video card that can drive two 24" vertical monitors (I still have the two that I used with my XP machine). Ideally, the card would also be able to power a third 24" if I got one at some point.&lt;br /&gt;&lt;li&gt;A decent processor and motherboard.&lt;br /&gt;&lt;/ul&gt;I also wanted to avoid paying for: &lt;ul&gt;&lt;li&gt;A Windows license.&lt;br /&gt;&lt;li&gt;A CD/DVD-ROM drive.&lt;br /&gt;&lt;li&gt;Frivolous things.&lt;br /&gt;&lt;/ul&gt;I did not think that I would need a CD-ROM drive, as I planned to install Ubuntu from a USB flash drive. &lt;p&gt;I expected to be able to go to Dell or HP's web site and customize something without much difficulty. Was I ever wrong. At first, I thought it was going to be easy as the first result for &lt;code&gt;[Dell Ubuntu]&lt;/code&gt; &lt;a href="http://www.dell.com/content/topics/segtopic.aspx/linux_3x?c=us&amp;l=en&amp;cs=19"&gt;looked very promising&lt;/a&gt;: it showed a tower starting at $650 with Ubuntu preinstalled. I started to customize it: upgrading from 4GB to 8GB of RAM increased the price by $120, which was reasonable (though &lt;a href="http://www.amazon.com/gp/product/B003ZDJ42O/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B003ZDJ42O"&gt;not quite as good as Amazon&lt;/a&gt;). However, I could not find an option to upgrade to an SSD, so buying my own off of Newegg would cost me $240. Finally, the only options Dell offered for video cards were ATI, and I have had some horrible experiences trying to get dual monitors to work with Ubuntu and ATI cards in the past (NVIDIA seems to be better about providing good Linux drivers). At this point, I was over $1000, and was not so sure about the video card, so I started asking some friends for their input. &lt;p&gt;Unfortunately, I have smart, capable friends who can build their own machines from parts who were able to convince me that I could, too. You see, in general, I &lt;em&gt;hate&lt;/em&gt; dealing with hardware. For me, hardware is simply an inevitable requirement for software. When software goes wrong, I have some chance of debugging it and can attack the problem right away. By comparison, when hardware goes wrong, I am less capable, and may have to wait until a new part from Amazon comes in before I can continue debugging the problem, which sucks. &lt;p&gt;At the same time, I realized that I should probably get past my aversion to dealing with hardware, so I started searching for blog posts about people who had built their own Ubuntu boxes. I found &lt;a href="http://www.saltycrane.com/blog/2011/02/decided-build-linux-desktop-pc-myself/"&gt;one post&lt;/a&gt; by a guy who built his PC for $388.95, which was far more than the Dell that I was looking at! Further, he itemized the parts that he bought, so at least I knew that if I followed his steps, I would end up with something that worked with Ubuntu (ending up with hardware that was not supported by Ubuntu was one of my biggest fears during this project).  I cross-checked this list with a friend who had recently put together a Linux machine with an &lt;a href="http://www.amazon.com/gp/product/B002A6G3V2/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B002A6G3V2"&gt;Intel i7&lt;/a&gt; chip, and he was really happy with it, so I ended up buying it and the &lt;a href="http://www.amazon.com/gp/product/B001ISJONM/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B001ISJONM"&gt;DX58SO motherboard&lt;/a&gt; that was recommended for use with the i7. This made things a bit pricier than they were in the blog post I originally looked at:&lt;style&gt;.parts {  border-collapse: collapse;}.parts td, .parts th {  vertical-align: top;  border: 1px solid #666;  padding: 2px;}&lt;/style&gt; &lt;table class="parts"&gt;&lt;tr&gt; &lt;td&gt;Motherboard&lt;/td&gt; &lt;td&gt;&lt;a href="http://www.amazon.com/gp/product/B001ISJONM/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B001ISJONM"&gt;Intel DX58SO Extreme Series X58 ATX Triple-channel DDR3 16GB SLI&lt;/a&gt;&lt;br /&gt;&lt;/td&gt; &lt;td&gt;$269.99&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;CPU&lt;/td&gt; &lt;td&gt;&lt;a href="http://www.amazon.com/gp/product/B002A6G3V2/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B002A6G3V2"&gt;Intel Core i7 950 3.06GHz 8M L3 Cache LGA1366 Desktop Processor&lt;/a&gt;&lt;br /&gt;&lt;/td&gt; &lt;td&gt;$234.99&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;RAM&lt;/td&gt; &lt;td&gt;&lt;a href="http://www.amazon.com/gp/product/B003ZDJ42O/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B003ZDJ42O"&gt;Corsair XMS3 4 GB 1333MHz PC3-10666 240-pin DDR3 Memory Kit for Intel Core i3 i5 i7 and AMD CMX4GX3M1A1333C9&lt;/a&gt;&lt;br /&gt;&lt;/td&gt; &lt;td&gt;&lt;nobr&gt;(2x$39.99) $79.98&lt;/nobr&gt;&lt;br /&gt;&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Case&lt;/td&gt; &lt;td&gt;&lt;a href="http://www.amazon.com/gp/product/B001TUYTZ2/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B001TUYTZ2"&gt;Cooler Master Elite 360 RC-360-KKN1-GP ATX Mid Tower/Desktop Case (Black)&lt;/a&gt;&lt;br /&gt;&lt;/td&gt; &lt;td&gt;$39.99&lt;br /&gt;&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Hard Drive&lt;/td&gt; &lt;td&gt;&lt;a href="http://www.amazon.com/gp/product/B0039SM0AS/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B0039SM0AS"&gt;Crucial Technology 128 GB Crucial RealSSD C300 Series Solid State Drive CTFDDAC128MAG-1G1&lt;/a&gt;&lt;br /&gt;&lt;/td&gt; &lt;td&gt;$237.49&lt;br /&gt;&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Power Supply&lt;/td&gt; &lt;td&gt;&lt;a href="http://www.amazon.com/gp/product/B0029F21LK/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B0029F21LK"&gt;Corsair CMPSU-750HX 750-Watt HX Professional Series 80 Plus Certified Power Supply compatible with Core i7 and Core i5&lt;/a&gt;&lt;br /&gt;&lt;/td&gt; &lt;td&gt;$109.99&lt;br /&gt;&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;Sales Tax&lt;/td&gt; &lt;td&gt;$55.47&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;&lt;/td&gt; &lt;th&gt;Total&lt;/th&gt; &lt;td&gt;$1027.90&lt;/td&gt; &lt;/tr&gt;&lt;/table&gt;&lt;p&gt;At this point, I should have acknowledged that what I had put together (on paper) was now in the price range of what I was originally looking at on Dell's web site. Unfortunately, I had mentally committed to being a badass and building a machine at this point, so I forged ahead with my purchase. &lt;p&gt;I also should have acknowledged that this list of parts did not include a video card... &lt;p&gt;In a few days, everything had arrived and I started putting everything together as best as I could figure out. I tried following the assembly instructions verbatim from the manuals, but that proved to be a huge mistake, as the suggested assembly order was not appropriate for my parts. For example, the case instructions recommended that I install the power supply first, then the motherboard, though that ended up making the SATA connectors inaccessible, so I had to remove the motherboard, then the power supply, plug in the SATA cables, and then put everything back together again. (This is one of many examples of exercises like this that I went through.)  &lt;p&gt;When I was close to having something that I thought would boot, I finally accepted the fact that I had failed to order a video card, so I tried using the one from my XP machine in hopes that it would allow me to kick off the Ubuntu installation process, and then I could walk to Best Buy to purchase a new video card. In the months leading up to the death of my XP machine, I had a lot of problems with my monitors, so it should have been no surprise that my installation screen looked like this: &lt;p&gt;&lt;img src="http://bolinfest.com/images/badvideocard.jpg" width="600"&gt; &lt;p&gt;Regardless, this allowed me to run the memory test (which runs forever, by the way&amp;mdash;I let this run for hours before I decided to investigate why it never stopped) while I went off to Best Buy. Because I had already convinced myself that an NVIDIA card would work better with Ubuntu than an ATI, I went ahead and bought this badass looking video card: the &lt;a href="http://www.amazon.com/gp/product/B004R9OVE6/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399701&amp;creativeASIN=B004R9OVE6"&gt;EVGA GeForce GTX 550 Ti&lt;/a&gt;. It was &lt;em&gt;not&lt;/em&gt; cheap ($250 at Best Buy, though it would have been $125 on Amazon), but I was a man on a mission, so nothing could stop me. &lt;p&gt;Once I got home and dropped the card in, I had a problem that I never would have anticipated: &lt;em&gt;the video card did not fit in my case&lt;/em&gt;. Specifically, the card fit in my case, but it was so close to the power supply that there was not enough room to plug in the separate power connector that the video card needed. At that point, I was both desperate and determined, so I took out the power supply and tried to wrench off the casing to create a big enough hole so there would be enough room behind the video card to plug it in. As you can see, I did succeed in removing quite a bit of metal from the power supply (and most definitely voided the warranty): &lt;p&gt;&lt;img src="http://bolinfest.com/images/powersupplymetal.jpg" width="600"&gt; &lt;p&gt;Despite my handiwork with a pair of pliers, I was unable to remove enough metal from the power supply to create enough space to power the video card, so it would have to go back to Best Buy. I decided to search for &lt;code&gt;[ubuntu 11.04 best video card]&lt;/code&gt; to find something that I could overnight from Amazon. I followed the links from &lt;a href="http://blog.sudobits.com/2011/05/29/best-graphics-card-for-ubuntu-11-04-10-10/"&gt;this blog post&lt;/a&gt;, and decided to go with the &lt;a href="http://www.amazon.com/gp/product/B003DTMLZW/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B003DTMLZW"&gt;ZOTAC nVidia GeForce 9800GT&lt;/a&gt;, which was $102.32 after tax and overnight shipping. One of the main selling points for me was the following comment in one of the Amazon reviews: "Another advantage of this card is that it DOES NOT require your power supply to have a video card power connector." Although I was originally hoping to get a card with two DVI inputs (instead of one DVI and one VGA), I really just wanted something that would work at that point. &lt;p&gt;While I was waiting for the new video card to arrive, I tried installing Linux on my SSD with my half-assed video card. Although it seemed like it was going to install off of the USB flash drive on Day 1, my PC did not seem to want to accept it on Day 2. I spent some time Googling for "@ogt error" that morning (because that is what I saw on my screen), until I realized that it was actually saying "boot error" and my video card was just garbling the characters. I rewrote the USB drive with all sorts of different ISOs, and I started to wonder whether buying the cheapest flash drive at Best Buy (it was $8 for 4GB!) was a mistake. I then tried the USB drive on another Windows machine I had lying around, and it took, at which point I was really stumped. Again, I asked some friends what they thought, and they recommended installing from a CD, as that was much more reliable. &lt;p&gt;As you may recall, buying a CD-ROM drive was something I had avoided, so what could I do? I tried reusing the one from my old Dell, but that turned out to be a non-starter because it required an IDE connector rather than a SATA one. Instead, I hoofed it back to Best Buy to "borrow" a drive for 24 hours or so. This was one of my better decisions during this entire process, as installing from a CD-R that I burned with an 11.04 ISO worked flawlessly. &lt;p&gt;Once my video card finally came in and Ubuntu was installed, I finally felt like I was almost done! Of course, I was wrong. Getting two 24" monitors working in portrait mode turned out to be quite a challenge. The first step was to install the NVIDIA drivers for Ubuntu. Originally I downloaded binaries from the web site, but that broke everything. Fortunately a friend helped me figure out how to uninstall the binary driver (&lt;code&gt;sudo ./NVIDIA-Linux-x86_64-270.41.19.run --uninstall&lt;/code&gt;) and replace it with a proper package (&lt;code&gt;sudo apt-get install nvidia-current&lt;/code&gt;). Now things were working fine with one monitor in landscape mode, but the jump to portrait was more challenging. &lt;p&gt;Initially, I tried to do everything through the NVIDIA GUI on Ubuntu, but it did not present an option to rotate the monitor. I found &lt;a href="http://www.chrisamiller.com/blog/2008/05/11/rotating-one-monitor-with-ubuntu/"&gt;a blog post&lt;/a&gt; that recommended adding &lt;code&gt;Option "Rotate" "CCW"&lt;/code&gt; to &lt;code&gt;/etc/X11/xorg.conf&lt;/code&gt;, which indeed helped me get the first monitor working in portrait mode. I was able to add the second monitor via the NVIDIA GUI and edited &lt;code&gt;xorg.conf&lt;/code&gt; again to rotate it. At this point, everything looked great &lt;em&gt;except&lt;/em&gt; that I could not drag windows from one monitor to another. To do that, I had to enable "TwinView" in the NVIDIA GUI, which did enable me to drag windows across screens, except NVIDIA insisted that the cursor flow from the bottom of the left monitor to the top of the right monitor instead of horizontally across. I did many Google searches to try to find a simple solution, but I had no luck. Ultimately, I ended up reading up on &lt;code&gt;xorg.conf&lt;/code&gt; until I understood it enough to edit it by hand to get things to work. At long last, everything was working! &lt;p&gt;The final step was to get everything in the case. This was a little tricky because my power supply came with a ton of cables, and wedging them in such that they did not block any of the three exposed fans was non-trivial. Further, the case did not have a proper slot for an SSD, so I ended up buying a &lt;a href="http://www.amazon.com/gp/product/B002BH3Z8E/ref=as_li_tf_tl?ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=217153&amp;creative=399349&amp;creativeASIN=B002BH3Z8E"&gt;3.5 to 2 X 2.5-Inch Bay Converter&lt;/a&gt; to hold the SSD in place. Unfortunately, the case had weird screw holes such that it was impossible to secure the converter in place, but fortunately the top fan prevents the bay from falling into the rest of the case, so it seems good enough. Considering that I already have a power supply with a gaping hole in it and a mess of cables, this did not seem like my biggest concern. &lt;p&gt;So what have I learned? Primarily, I learned that I should never do this again, but if I had to, I would be much less afraid to mess with hardware the next time around. Including the cost of the video card and the bay converter, I spent $1138.92 in cash and about two days worth of my time. Most of that two days was spent being angry and frustrated. When I was just about finished with the entire project, I noticed an ad on one of the blog posts I used for help to a site I had not heard of before: &lt;a href="http://www.system76.com/"&gt;system76.com&lt;/a&gt;. Apparently they sell Ubuntu laptops, desktops, and servers, and they have a product line called &lt;a href="http://www.system76.com/product_info.php?cPath=27&amp;products_id=98"&gt;Wildebeest Performance&lt;/a&gt; that I could customize to basically exactly what I said I wanted at the outset of this project. On system76.com, a machine with Ubuntu 11.04, an i7 processor, 8GB of RAM, a 120GB SSD, and an NVIDIA card with two inputs (one DVI, one VGA) costs $1117.00, which is less than what I paid when buying the parts individually. Obviously buying the machine directly would have been a huge time savings, and I'm sure the inside of the system76 PC would not be nearly as sloppy as mine. In this, and in many other things, I need to have more patience and do more research before diving into a project. It can save a lot of time and sanity in the long run.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-6303236780607500635?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/6303236780607500635/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/06/it-takes-village-to-build-linux-desktop.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/6303236780607500635" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/6303236780607500635" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/06/it-takes-village-to-build-linux-desktop.html" title="It takes a village to build a Linux desktop" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-727261243981147416</id><published>2011-05-27T15:49:00.000-04:00</published><updated>2011-05-27T15:49:12.566-04:00</updated><title type="text">GData: I can't take it anymore</title><content type="html">I have been playing with GData since I was on the Google Calendar team back in 2005/2006. My experiences with GData can be summarized by the following graph:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://bolinfest.com/images/gdata_600.jpg" width="600" height="493"&gt;&lt;br /&gt;&lt;br /&gt;There are many reasons why GData continues to infuriate me:&lt;br /&gt;&lt;ul&gt;  &lt;li&gt;GData is not about data&amp;mdash;it is an exercise in XML masturbation. If you look at the content of a GData feed, most of the bytes are dedicated to crap that does not matter due to a blind devotion to the Atom Publishing Protocol. In recent history, GData has become better in providing clean JSON data, but the equivalent XML it sends down is still &lt;a href="http://code.google.com/apis/latitude/v1/using_rest.html#LocationResource"&gt;horrifying by comparison&lt;/a&gt;. I understand the motivation to provide an XML wire format, but at least the original Facebook API had the decency to use &lt;a href="http://en.wikipedia.org/wiki/Plain_Old_XML"&gt;POX&lt;/a&gt; to reduce the clutter. Atom is the reason why, while I was at Google, the &lt;a href="http://googledataapis.blogspot.com/2006/11/calling-all-web-hackers-json-support.html"&gt;first GData JSON API we released&lt;/a&gt; had to have a bunch of dollar signs and crap in it, so that you could use it to construct the Atom XML equivalent when making a request to the server. When I wanted to add &lt;a href="http://googleblog.blogspot.com/2006/09/google-calendar-does-something-about.html"&gt;web content events&lt;/a&gt; to Calendar in 2006, most of my energy was spent on debating what the &lt;a href="http://www.google.com/support/calendar/bin/answer.py?answer=48528"&gt;semantically correct Atom representation should be&lt;/a&gt; rather than implementing the feature. Hitching GData to the Atom wagon was an exhausting waste of time and energy. Is it really that important to enable users to view their updates to a spreadsheet in Google Reader?&lt;br /&gt;  &lt;li&gt;REST APIs aren't cool. &lt;a href="http://dev.twitter.com/pages/streaming_api"&gt;Streaming APIs&lt;/a&gt; are cool. If we want to have a real time Web, then we need streaming APIs. PubSub can be used as a stopgap, but it's not as slick. For data that may change frequently (such as a user's location in Google Latitude), you have no choice but to poll aggressively using a REST API.&lt;br /&gt;  &lt;li&gt;GData has traditionally given JavaScript developers short shrift. Look at the API support for the GData &lt;a href="http://code.google.com/p/gdata-java-client/"&gt;Java client library&lt;/a&gt; or &lt;a href="http://code.google.com/p/gdata-python-client/"&gt;Python client library&lt;/a&gt; compared to the &lt;a href="http://code.google.com/p/gdata-javascript-client/"&gt;JavaScript client library&lt;/a&gt;. JavaScript is the &lt;i&gt;lingua franca&lt;/i&gt; of the Web: give it the attention it deserves.&lt;br /&gt;  &lt;li&gt;The notion of Atom forces the idea of "feeds" and "entries." That is fine for something like Google Calendar, but is less appropriate for hierarchical data, such as that stored in Google Tasks. Further, for data that does not naturally split into "entries," such as a Google Doc, the entire document becomes a single entry. Therefore, making a minor change to a Google Doc via GData requires &lt;em&gt;uploading the entire document rather than the diff&lt;/em&gt;. This is quite expensive if you want to create your own editor for a Google Doc that has autosave.&lt;br /&gt;  &lt;li&gt;Perhaps the biggest time sink when getting started with GData is wrapping your head around the authentication protocols. To play around with your data, the first thing you have to do is set up a bunch of crap to get an AuthSub token. &lt;b&gt;Why can't I just fill out a form on code.google.com and give myself one?&lt;/b&gt; Setting up AuthSub is not the compelling piece of the application I want to build&amp;mdash;interacting with my data is. Let me play with my data first and build a prototype so I can determine if what I'm trying to build is worth sharing with others and productionizing, and then I'll worry about authentication. &lt;a href="http://developers.facebook.com/docs/reference/javascript/"&gt;Facebook's JavaScript SDK&lt;/a&gt; does this exceptionally well. After registering your application, you can include one &lt;code&gt;&amp;lt;script&gt;&lt;/code&gt; tag on your page and start using the Facebook API &lt;em&gt;without writing any server code&lt;/em&gt;. It's much more fun and makes it easier to focus on the interesting part of your app.&lt;br /&gt;&lt;/ul&gt;If GData were great, then Google products would be built on top of GData. A quick look under the hood will reveal that no serious web application at Google (Gmail, Calendar, Docs, etc.) uses it. If GData isn't good enough for Google engineers, then why should we be using it?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-727261243981147416?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/727261243981147416/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/05/gdata-i-cant-take-it-anymore.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/727261243981147416" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/727261243981147416" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/05/gdata-i-cant-take-it-anymore.html" title="GData: I can't take it anymore" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-229897432922329628</id><published>2011-05-16T15:13:00.001-04:00</published><updated>2011-05-16T15:15:15.601-04:00</updated><title type="text">Reflecting on my Google I/O 2011 Talk</title><content type="html">This year was my first trip to Google I/O, both as an attendee and a speaker. The title of my talk was &lt;a href="http://www.google.com/events/io/2011/sessions/javascript-programming-in-the-large-with-closure-tools.html"&gt;JavaScript Programming in the Large with Closure Tools&lt;/a&gt;. As I have spoken to more and more developers, I have come to appreciate how jQuery and Closure are good for different things, and that Closure's true strength is when working in large JavaScript codebases, particularly for &lt;a href="http://en.wikipedia.org/wiki/Single-page_application"&gt;SPAs&lt;/a&gt;. With the increased interest in HTML5 and offline applications, I believe that JavaScript codebases will continue to grow, and that Closure will become even more important as we move forward, which is why I was eager to deliver my talk at I/O.&lt;br /&gt;&lt;br /&gt;Although it does not appear to be linked from the &lt;a href="http://www.google.com/events/io/2011/sessions.html"&gt;sessions page&lt;/a&gt; yet, the video of my I/O talk &lt;a href="http://www.youtube.com/user/GoogleDevelopers#p/u/11/M3uWx-fhjUc"&gt;is available on YouTube&lt;/a&gt;. I have also made my &lt;a href="http://bolinfest.com/talks/2011/googleio/"&gt;slides available online&lt;/a&gt;, though I made a concerted effort to put less text on the slides than normal, so they may not make as much sense without the narration from my talk.&lt;br /&gt;&lt;br /&gt;I was incredibly nervous, but I did watch all 57 minutes of the video to try to evaluate myself as a speaker. After observing myself, I'm actually quite happy with how things went! I was already aware that I sometimes list back and forth when speaking, and that's still a problem (fortunately, most of the video shows the slides, not me, so you may not be able to tell how bad my nervous habit is). My mumbling isn't as bad as it used to be (historically, I've been pretty bad about it during ordinary conversation, so mumbling during public speaking was far worse). It appears that when I'm diffident about what I'm saying (such as when I'm trying to make a joke that I'm not sure the audience will find funny), I often trail off at the end of the sentence, so I need to work on that. On the plus side, the word "like" appears to drop out of my vernacular when I step on a stage, and I knew my slides well enough that I was able to deliver all of the points I wanted to make without having to stare at them too much. (I never practiced the talk aloud before giving it&amp;mdash;I only played through what I wanted to say in my head. I can't take myself seriously when I try to deliver my talk to myself in front of a mirror.)&lt;br /&gt;&lt;br /&gt;If you pay attention during the talk, you'll notice that I switch slides using a real Nintendo controller. The week before, I was in Portland for &lt;a href="http://2011.jsconf.us/"&gt;JSConf&lt;/a&gt;, which had an 8-bit theme. There, I gave a &lt;a href="http://bolinfest.com/talks/2011/jsconf.us/"&gt;talk on a novel use of the &lt;code&gt;with&lt;/code&gt; keyword in JavaScript&lt;/a&gt;, but I never worked the 8-bit theme into my slides, so I decided to do so for Google I/O (you'll also note that Mario and Link make cameos in my I/O slides). Fortunately, I had messed around with my &lt;a href="http://www.retrousb.com/product_info.php?cPath=21&amp;products_id=28"&gt;USB NES RetroPort before&lt;/a&gt;, so I already had some sample Java code to leverage&amp;mdash;I ended up putting the whole NES navigation thing together the morning of my talk.&lt;br /&gt;&lt;br /&gt;For my &lt;code&gt;with&lt;/code&gt; talk the week before, I had already created my own presentation viewer in Closure/JavaScript so I could leverage things like &lt;a href="http://code.google.com/p/google-code-prettify/"&gt;prettify&lt;/a&gt;. In order to provide an API to the NES controller, I exported some JavaScript functions to navigate the presentation forward and backward (&lt;code&gt;navPresoForward()&lt;/code&gt; and &lt;code&gt;navPresoBack()&lt;/code&gt;). Then I embedded the URL to the presentation in an &lt;code&gt;org.eclipse.swt.browser.Browser&lt;/code&gt; and used &lt;a href="http://sourceforge.net/projects/javajoystick/"&gt;com.centralnexus.input.Joystick&lt;/a&gt; to process the input from the controller and convert right- and left-arrow presses into &lt;code&gt;browser.execute("navPresoForward()")&lt;/code&gt; and &lt;code&gt;browser.execute("navPresoBack()")&lt;/code&gt; calls in Java. (The one sticking point was discovering that joystick input had to be processed in a special thread scheduled by &lt;a href="http://www.jdocs.com/swt/3.2/org/eclipse/swt/widgets/Display.html?m=M-asyncExec%28Runnable%29#M-asyncExec%28Runnable%29"&gt;Display.asyncExec()&lt;/a&gt;.) Maybe it wasn't as cool as Marcin Wichary's Power Glove during his and Ryan's talk, &lt;a href="http://www.google.com/events/io/2011/sessions/the-secrets-of-google-pac-man-a-game-show.html"&gt;The Secrets of Google Pac-Man: A Game Show&lt;/a&gt;, but I thought theirs was the best talk of the entire conference, so they were tough to compete with.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-229897432922329628?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/229897432922329628/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/05/reflecting-on-my-google-io-2011-talk.html#comment-form" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/229897432922329628" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/229897432922329628" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/05/reflecting-on-my-google-io-2011-talk.html" title="Reflecting on my Google I/O 2011 Talk" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-1018816310214460825</id><published>2011-04-20T09:27:00.000-04:00</published><updated>2011-04-20T09:27:14.835-04:00</updated><title type="text">jquery.com uses only 34% of jQuery</title><content type="html">I created a Firefox extension, &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/jsbloat/"&gt;JsBloat&lt;/a&gt;, to help determine what fraction of the jQuery library a web page uses. It leverages &lt;a href="http://siliconforks.com/jscoverage/"&gt;JSCoverage&lt;/a&gt; to instrument jQuery and keep track of which lines of the library are executed (and how often). The table below is a small sample of sites that I tested with JsBloat. Here, "percentage used" means the fraction of lines of code that were executed in the version of the jQuery library that was loaded:&lt;br /&gt;&lt;br /&gt;&lt;table border="1" cellpadding="4"&gt;&lt;tbody&gt;&lt;tr&gt; &lt;td&gt;URL&lt;/td&gt; &lt;td&gt;jQuery version&lt;/td&gt; &lt;td&gt;% used on page load&lt;/td&gt; &lt;td&gt;% used after mousing around&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;&lt;a href="http://jquery.com/"&gt;jquery.com&lt;/a&gt;&lt;/td&gt; &lt;td&gt;1.4.2&lt;/td&gt; &lt;td&gt;18%&lt;/td&gt; &lt;td&gt;34%&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;&lt;a href="http://docs.jquery.com/Main_Page"&gt;docs.jquery.com/Main_Page&lt;/a&gt;&lt;/td&gt;   &lt;td&gt;1.4.4&lt;/td&gt; &lt;td&gt;23%&lt;/td&gt; &lt;td&gt;23%&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;&lt;a href="http://stackoverflow.com/"&gt;stackoverflow.com&lt;/a&gt;&lt;/td&gt; &lt;td&gt;1.5.1&lt;/td&gt; &lt;td&gt;30%&lt;/td&gt; &lt;td&gt;33%&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;&lt;a href="http://www.mozilla.com/en-US/firefox/new/?from=getfirefox"&gt;getfirefox.com&lt;/a&gt;&lt;/td&gt; &lt;td&gt;1.4.4&lt;/td&gt; &lt;td&gt;19%&lt;/td&gt; &lt;td&gt;23%&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;Note that three of the four sites exercise new code paths as a result of mousing around the page, so they do not appear to be using pure CSS for their hover effects. For example, on jquery.com, a single mouseover of the "Lightweight Footprint" text causes a mouseover animation that increases the percentage of jQuery used by 11%! Also, when jQuery is loaded initially on stackoverflow.com, it calls &lt;code&gt;$()&lt;/code&gt; 61 times, but after mousing around quite a bunch (which only increases the percentage of code used to 33%), the number of times that &lt;code&gt;$()&lt;/code&gt; is executed jumps to 9875! (Your results may vary, depending on how many elements you mouse over, but it took less than twenty seconds of mousing for me to achieve my result. See the &lt;b&gt;Postscript&lt;/b&gt; below to learn how to run JsBloat on any jQuery-powered page.) Although code coverage is admittedly a coarse metric for this sort of experiment, I still believe that the results are compelling.&lt;br /&gt;&lt;br /&gt;I decided to run this test because I was curious about how much jQuery users would stand to gain if they could leverage the &lt;a href="http://code.google.com/closure/compiler/docs/api-tutorial3.html"&gt;Advanced mode of the Closure Compiler&lt;/a&gt; to compile their code. JavaScript that is written for Advanced mode (such as the &lt;a href="http://code.google.com/closure/library/"&gt;Closure Library&lt;/a&gt;) can be compiled so that it is far smaller than its original source because the Closure Compiler will remove code that it determines is unreachable. Therefore, it will only include the lines of JavaScript code that you will actually use, whereas most clients of jQuery appear to be including much more than that.&lt;br /&gt;&lt;br /&gt;From these preliminary results, I believe that most sites that use jQuery could considerably reduce the amount of JavaScript that they serve by using Closure. As always, compiling custom JavaScript for every page must be weighed against caching benefits, though I suspect that the Compiler could find a healthy subset of jQuery that is universal to all pages on a particular site.&lt;br /&gt;&lt;br /&gt;If you're interested in learning more about using Closure to do magical things to your JavaScript, come find me at &lt;a href="http://2011.jsconf.us/#!/schedule"&gt;Track B of JSConf&lt;/a&gt; where I'm going to provide some mind-blowing examples of how to use the &lt;code&gt;with&lt;/code&gt; keyword effectively! And if I don't see you at JSConf, then hopefully I'll see you at Google I/O where I'll be talking about &lt;a href="http://www.google.com/events/io/2011/sessions.html"&gt;JavaScript Programming in the Large with Closure Tools&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Postscript: If you're curious how JsBloat works...&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;JsBloat works by intercepting requests to &lt;a href="http://code.google.com/apis/libraries/devguide.html"&gt;ajax.googleapis.com&lt;/a&gt; for jQuery and injecting its own instrumented version of jQuery into the response. The instrumented code looks something like:&lt;pre&gt;for (var i = 0, l = insert.length; (i &lt; l); (i++)) {&lt;br /&gt;  _$jscoverage['jquery-1.5.2.js'][5517]++;&lt;br /&gt;  var elems = ((i &gt; 0)? this.clone(true): this).get();&lt;br /&gt;  _$jscoverage['jquery-1.5.2.js'][5518]++;&lt;br /&gt;  (jQuery(insert[i])[original])(elems);&lt;br /&gt;  _$jscoverage['jquery-1.5.2.js'][5519]++;&lt;br /&gt;  ret = ret.concat(elems);&lt;br /&gt;}&lt;br /&gt;_$jscoverage['jquery-1.5.2.js'][5522]++;&lt;br /&gt;return this.pushStack(ret, name, insert.selector);&lt;br /&gt;&lt;/pre&gt;so that after each line of code is executed, the &lt;code&gt;_$jscoverage&lt;/code&gt; global increments its count for the (file, line number) pair. JSCoverage provides a simple HTML interface for inspecting this data, which JsBloat exposes.&lt;br /&gt;&lt;br /&gt;You can tell when JsBloat has intercepted a request because it injects a button into the upper-left-hand corner of the page, as shown below:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://bolinfest.com/javascript/jsbloat_before.png" width="561" width="321"&gt;&lt;br /&gt;&lt;br /&gt;Clicking on that button toggles the JSCoverage UI:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://bolinfest.com/javascript/jsbloat_after.png" width="561" width="321"&gt;&lt;br /&gt;&lt;br /&gt;Once JsBloat is installed, you can also configure it to intercept requests to any URL with jQuery. For example, &lt;a href="http://getfirefox.com/"&gt;getfirefox.com&lt;/a&gt; loads jQuery from &lt;a href="http://mozcom-cdn.mozilla.net/js/jquery/jquery.min.js"&gt;mozcom-cdn.mozilla.net&lt;/a&gt;, so you can set a preference in &lt;code&gt;about:config&lt;/code&gt; to serve the instrumented version of jQuery 1.4.4 when getfirefox.com loads jQuery from its CDN. More information for JsBloat users is available on the &lt;a href="http://code.google.com/p/jsbloat/"&gt;project page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you are interested in extending JsBloat to perform your own experiments, the &lt;a href="http://code.google.com/p/jsbloat/"&gt;source is available on Google Code under a GPL v2 license&lt;/a&gt;. (I don't ordinarily make my work available under the GPL, but because JSCoverage is available under the GPL, JsBloat must also be GPL'd.) For example, if you download JSCoverage and the source code for JsBloat, you can use JSCoverage to instrument your own JavaScript files and then include them in a custom build of JsBloat. Hopefully this will help you identify opportunities to trim down the JavaScript that you send to your users.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;amp;tag=bolinfestcom-20&amp;amp;link_code=as3&amp;amp;camp=211189&amp;amp;creative=373489&amp;amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-1018816310214460825?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/1018816310214460825/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/04/jquerycom-uses-only-34-of-jquery.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/1018816310214460825" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/1018816310214460825" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/04/jquerycom-uses-only-34-of-jquery.html" title="jquery.com uses only 34% of jQuery" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-6391700904662510262</id><published>2011-04-06T10:22:00.000-04:00</published><updated>2011-04-06T10:22:37.482-04:00</updated><title type="text">Suggested Improvements to JSON</title><content type="html">Today I'm publishing an essay about my &lt;a href="http://bolinfest.com/essays/json.html"&gt;suggested improvements to JSON&lt;/a&gt;. I really like JSON a lot, but I think that it could use a few tweaks to make it easier to use for the common man.&lt;br /&gt;&lt;br /&gt;One question some have asked is: even if these changes to JSON were accepted, how would you transition existing systems? I would lean towards what I call the Nike approach, which is: "just do it." That is, start updating the &lt;code&gt;JSON.parse()&lt;/code&gt; method in the browser to accept these extensions to JSON. What I am proposing is a strict superset of what's in &lt;a href="http://www.ietf.org/rfc/rfc4627.txt?number=4627"&gt;RFC 4627&lt;/a&gt; today (which you may recall claims that "[a] JSON parser MAY accept non-JSON forms or extensions"), so it would not break anyone who continued to use ES3 style JSON.&lt;br /&gt;&lt;br /&gt;At first, you may think that sounds ridiculous -- how could we update the spec without versioning? Well, if you haven't been paying attention to the HTML5 movement, we are already doing this sort of thing &lt;em&gt;all the time&lt;/em&gt;. Browser behavior continues to change/improve, and you just have to roll with the punches. It's not ideal, but yet the Web moves forward, and it's faster than waiting for standards bodies to agree on something.&lt;br /&gt;&lt;br /&gt;Though if the Nike approach is too heretical, then I think that versioning is also a reasonable option. In the browser, a read-only &lt;code&gt;JSON.version&lt;/code&gt; property could be added, though I don't imagine most developers would check it an runtime, anyway. Like most things on the web, a least-common-denominator approach would be used by those who want to be safe, which would ignore the version number. Only those who do &lt;a href="http://infrequently.org/2011/01/cutting-the-interrogation-short/"&gt;user-agent sniffing on the server&lt;/a&gt; would be able to serve a slimmer JavaScript library, though that is already true for many other browser features today. I trust &lt;a href="http://www.browserscope.org/"&gt;Browserscope&lt;/a&gt; and &lt;a href="http://caniuse.com/"&gt;caniuse.com&lt;/a&gt; much more than any sort of formal specification, anyway.&lt;br /&gt;&lt;br /&gt;Special thanks to &lt;a href="http://kushaldave.com/"&gt;Kushal&lt;/a&gt;, &lt;a href="http://minivishnu.net/"&gt;Dolapo&lt;/a&gt;, and &lt;a href="http://blog.persistent.info/"&gt;Mihai&lt;/a&gt; for providing feedback on my essay.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-6391700904662510262?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/6391700904662510262/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/04/suggested-improvements-to-json.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/6391700904662510262" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/6391700904662510262" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/04/suggested-improvements-to-json.html" title="Suggested Improvements to JSON" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-1942147936104854059</id><published>2011-04-04T10:19:00.001-04:00</published><updated>2011-04-04T11:11:46.300-04:00</updated><title type="text">What I learned from üjs</title><content type="html">For April Fool's, I released a mock JavaScript library, &lt;a href="http://üjs.com/"&gt;üjs&lt;/a&gt; (pronounced "umlaut JS"). The library was "mock" in the "mockumentary" Spin̈al Tap sense, not the unit testing sense. It took me about a day to create the site, and I learned some interesting lessons along the way, which I thought I would share:&lt;ul&gt;&lt;li&gt;In purchasing the domain name &lt;a href="http://üjs.com/"&gt;üjs.com&lt;/a&gt;, I learned about &lt;a href="http://en.wikipedia.org/wiki/Punycode"&gt;Punycode&lt;/a&gt;. Basically, when you type üjs.com into the browser, the browser will translate it to the Punycode equivalent, which is &lt;a href="http://xn--js-wka.com/"&gt;http://xn--js-wka.com/&lt;/a&gt;. On one hand, this is what prevents you from buying göögle.com and creating a giant phishing scheme; on the other hand, because most users are unfamiliar with Punycode, going to a legitimate domain like üjs.com looks like a giant phishing scheme due to the rewrite.&lt;br /&gt;&lt;li&gt;I only promoted üjs through three channels: &lt;a href="http://twitter.com/#!/bolinfest/status/53676791633690624"&gt;Twitter&lt;/a&gt;, &lt;a href="http://news.ycombinator.com/item?id=2396542"&gt;Hacker News&lt;/a&gt;, and &lt;a href="http://www.reddit.com/r/javascript/comments/ggice/faster_lighter_js_library_üjs/"&gt;Reddit&lt;/a&gt;. On Twitter, I discovered that using a Punycode domain as your punchline really limits its reach because instead of tweeting about üjs.com, many tweeted about xn--js-wka.com (because that's what you can copy from the location bar after you visit the site), or (somewhat ironically) a URL-shortened version of the domain. I suspect more people would have followed the shared link if they could see the original domain name.&lt;br /&gt;&lt;li&gt;According to Google Analytics, just over half of my traffic (50.04%) on April 1 was from Hacker News (where I made it to the front page!). Another 9.89% was from Reddit. Analytics also claims that only 1.57% of my traffic came from Twitter, though 29.76% of my traffic was "direct," so I assume that was from Twitter, as well. On April 1, I had 3690 visits, and then another 443 on April 2 (presumably from the eastern hemisphere who woke up on Saturday to an aftermath of Internet April Fools' pranks).&lt;br /&gt;&lt;li&gt;GoDaddy was not able to do a domain search for &lt;b&gt;üjs.com&lt;/b&gt;, presumably due to the non-ASCII characters. It turned out that &lt;a href="http://www.name.com/"&gt;name.com&lt;/a&gt; was able to, so I ended up going with them. (Presumably if I understood Punycode well enough at the outset of this project, I could have registered xn--js-wka.com through their site.)&lt;br /&gt;&lt;li&gt;I found a discount code for name.com, so my total cost out of pocket for this project was $8.75 (my hosting fees are a sunk cost).&lt;br /&gt;&lt;li&gt;Not that it was a substantial investment, but I was hopeful/curious whether I could break even in some way. I felt that traditional ads would have been a little over the top, so instead I decided to include two &lt;a href="https://affiliate-program.amazon.com/"&gt;Amazon Associates&lt;/a&gt; links. Although I produced 350 clicks to Amazon (!), I failed to generate any conversions, so I did not make any money off of my ads.&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.chartbeat.com/"&gt;Chartbeat&lt;/a&gt; is really, really cool. It made it much more fun to watch all of the web traffic to üjs.com during the day. (I wish that I generally had enough traffic to make it worth using Chartbeat all the time!) I believe that I had 144 simultaneous visitors during the peak of üjs, and I was amazed at how dispersed the traffic was from across the globe.&lt;br /&gt;&lt;li&gt;One thing that I did not realize is that Chartbeat does not do aggregate statistics. Fortunately, I set up Google Analytics in addition to Chartbeat, so I had access to both types of data.&lt;br /&gt;&lt;li&gt;Response times were about 1s on average during peak traffic times. At first, I thought that was horrendously slow, but then I realized that there were a large number of requests coming from outside the US, which increased the average. Most of the requests from the US loaded in the low hundreds of milliseconds, which made me feel good about &lt;a href="http://rimuhosting.com/"&gt;my hosting choice&lt;/a&gt; (who &lt;a href="http://twitter.com/#!/rimuhosting/status/52113750106443778"&gt;really is excellent&lt;/a&gt;, btw).&lt;br /&gt;&lt;li&gt;The &lt;b&gt;n̈&lt;/b&gt; in Spin̈al Tap is not a valid Unicode character. Instead, it is displayed by printing an &lt;b&gt;n&lt;/b&gt; followed by an umlaut, and what I can only assume is the kerning makes the umlaut display over the previous character. (The umlaut does not display correctly on my Chrome 10 on Windows, but it's fine on Linux.) Other characters, such as &lt;b&gt;ü&lt;/b&gt; are valid Unicode and can be displayed with a single code point. &lt;a href="http://en.wikipedia.org/wiki/Umlaut_(diacritic)"&gt;Wikipedia&lt;/a&gt; has a list of letters that "come with umlauts," so I used those characters whenever possible on üjs.com, but for others, I had to use the "letter followed by an umlaut" trick. &lt;br /&gt;&lt;li&gt;&lt;code&gt;var n\u0308;&lt;/code&gt; is a valid JavaScript variable declaration, but &lt;code&gt;var \u00fc;&lt;/code&gt; is not.&lt;br /&gt;&lt;li&gt;Initially, the GitHub badge on my page did not link to anything, as I just wanted to satirize the trend I've been seeing in open source lately. Though after a &lt;a href="http://twitter.com/#!/hongrich/status/53839762112516096"&gt;request from a coworker&lt;/a&gt;, I imported &lt;a href="https://github.com/bolinfest/umlautjs"&gt;all of the files I used to create üjs&lt;/a&gt; into GitHub. (Incidentally, when I tried to name the GitHub project &lt;b&gt;üjs&lt;/b&gt;, they replaced the &lt;b&gt;ü&lt;/b&gt; with a hyphen, so I renamed it &lt;b&gt;umlautjs&lt;/b&gt;).&lt;br /&gt;&lt;/ul&gt;In the end, even though I did not make my $8.75 back, I had a really great time with this project. Although it's clear that &lt;a href="http://news.ycombinator.com/item?id=2396936"&gt;some people on the Web don't enjoy April Fools&lt;/a&gt;, I think it is a nice opportunity to see some good satire. (My personal favorite was &lt;a href="http://www.atlassian.com/en/angrynerds"&gt;Angry Nerds&lt;/a&gt; by &lt;a href="http://www.atlassian.com/"&gt;Atlassian&lt;/a&gt;.) &lt;p&gt;Further, satire is not completely frivolous: it is (an arguably passive-aggressive) way of making a point. In üjs, mine was this: these tiny JavaScript libraries do not help move the needle when it comes to JavaScript development from the community standpoint. Instead of contributing a tiny library, why not focus on contributing a tiny fix to a big library? Think about how many more people you will affect with a bug fix to jQuery than you will by publishing a single JavaScript file with your favorite four helper functions? Or if you are going to create a new library, make sure that you are doing something substantially different than what else is out there. For example, Closure and jQuery are based on different design principles. Both have their use cases, and they serve very different classes of development, so it makes sense for those separate projects to exist and grow. &lt;p&gt;If you have been following my blog, you probably know that I'm really big on "JavaScript programming in the large," which will be the subject of &lt;a href="http://www.google.com/events/io/2011/"&gt;my talk at Google I/O this year&lt;/a&gt;. I hope to see you there! &lt;p&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-1942147936104854059?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/1942147936104854059/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/04/what-i-learned-from-ujs.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/1942147936104854059" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/1942147936104854059" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/04/what-i-learned-from-ujs.html" title="What I learned from üjs" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-8304743931302415345</id><published>2011-04-01T09:00:00.000-04:00</published><updated>2011-04-01T09:00:06.666-04:00</updated><title type="text">Awesome New JavaScript Library!</title><content type="html">Despite months of advocating Closure, I've finally given up and found a superior JavaScript library (and it's not jQuery!): &lt;a href="http://üjs.com/"&gt;http://üjs.com/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-8304743931302415345?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/8304743931302415345/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/04/awesome-new-javascript-library.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/8304743931302415345" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/8304743931302415345" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/04/awesome-new-javascript-library.html" title="Awesome New JavaScript Library!" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-3116006894795127791</id><published>2011-01-18T23:21:00.000-05:00</published><updated>2011-01-18T23:21:38.324-05:00</updated><title type="text">What are the most important classes for high school students to succeed in software engineering?</title><content type="html">What are the most important classes for high school students to succeed in software engineering? That is the question that I try to answer in an &lt;a href="http://bolinfest.com/essays/highschool.html"&gt;essay of same name&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Also, this is the first essay I have written using &lt;a href="http://docbookeditor.appspot.com/"&gt;NJEdit&lt;/a&gt;, which is the editing software that I built (and have since &lt;a href="http://code.google.com/p/docbookeditor/"&gt;open sourced&lt;/a&gt;) in order to write  &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide&lt;/a&gt;. It helps me focus more on content while worrying less about formatting, though it still has a ways to go before becoming my "one click" publishing solution.&lt;br /&gt;&lt;br /&gt;A unique feature of NJEdit is that when I produce the HTML to publish my essay, I also produce &lt;a href="http://bolinfest.com/essays/highschool.xml"&gt;the DocBook XML version as a by-product&lt;/a&gt;! It's not a big selling point today, but if I ever want to publish anything to print again, I'll be ready! For open-source projects that are slowly creating HTML documentation that they hope to publish as a print book one day, NJEdit might be the solution.&lt;br /&gt;&lt;br /&gt;And if it is, maybe someone will help me fix its bugs...&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-3116006894795127791?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/3116006894795127791/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/01/what-are-most-important-classes-for.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/3116006894795127791" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/3116006894795127791" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/01/what-are-most-important-classes-for.html" title="What are the most important classes for high school students to succeed in software engineering?" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-7677888229138902301</id><published>2011-01-12T23:47:00.000-05:00</published><updated>2011-01-12T23:47:24.647-05:00</updated><title type="text">My latest Closure talks</title><content type="html">Last Thursday, January 6, I had a doubleheader, as I gave one talk in the afternoon at an &lt;a href="http://6.470.scripts.mit.edu/2011/"&gt;undergraduate MIT Web programming competition (6.470)&lt;/a&gt;, and another talk in the evening at the &lt;a href="http://www.meetup.com/javascript-2/calendar/15696752/"&gt;Boston JavaScript Meetup&lt;/a&gt;. It was a long day (I took the train up from NYC that morning), but I got to talk to so many interesting people that I was far more energized at the end of the day than exhausted!&lt;br /&gt;&lt;br /&gt;I cleaned up the slide decks from both talks so they are suitable for sharing. Because I was informed that the MIT programming competition had a lot of freshmen in it, I didn't go into as much detail about Closure as I normally would have because I didn't want to overwhelm them. (I ended up taking so much out of my "standard" talk that I ended up finishing ~30 minutes early, though that was likely a welcome relief to the students as I was the last speaker in a full day of lecture.) As you can see, the MIT talk is only 17 slides:&lt;br /&gt;&lt;br /&gt;&lt;iframe src="https://docs.google.com/present/embed?id=ddkqzgzg_57cqqp7zfx" frameborder="0" width="410" height="342"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;The talk I prepared for the Boston JS Meetup was the most technical one I have given to date. It was my first time presenting for a group that actually has "JavaScript" in the name, so it was refreshing not to have to spend the first 15 minutes explaining to the audience that JavaScript is a real language and that you can do serious things with it. By comparison, my second talk went much longer (about an hour and a half?), as there was a lot more material to cover as well as tons of great questions from the (very astute!) audience during my presentation:&lt;br /&gt;&lt;br /&gt;&lt;iframe src="https://docs.google.com/present/embed?id=ddkqzgzg_64gn2dnnhg" frameborder="0" width="410" height="342"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;The one thing that these presentations do not capture is the &lt;a href="http://www.plovr.com/"&gt;plovr&lt;/a&gt; demo that I have been giving during my talks. (This was also the first time that I demoed using plovr with jQuery, as I had just attempted using jQuery with plovr for the first time myself the night before. I have an &lt;a href="http://groups.google.com/group/plovr/browse_thread/thread/fc19e59c2c51b706"&gt;open topic on the plovr discussion group about how to make plovr easier to use for jQuery developers&lt;/a&gt;, so please contribute if you have thoughts!) At some point, I'm planning to do a webcast with O'Reilly on Closure, so that might be a good opportunity to record a plovr demo that can be shared with everyone.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-7677888229138902301?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/7677888229138902301/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/01/my-latest-closure-talks.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/7677888229138902301" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/7677888229138902301" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/01/my-latest-closure-talks.html" title="My latest Closure talks" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-2314111514075225756</id><published>2011-01-11T23:42:00.000-05:00</published><updated>2011-01-11T23:42:45.252-05:00</updated><title type="text">Web Content Wizard is Fixed AGAIN!</title><content type="html">Back in 2006, I released this little tool for Google Calendar called the &lt;a href="http://blog.bolinfest.com/2006/12/web-content-wizard-for-google-calendar.html"&gt;Web Content Wizard&lt;/a&gt;. With it, you can add your own &lt;a href="http://googleblog.blogspot.com/2006/09/google-calendar-does-something-about.html"&gt;"web content events"&lt;/a&gt; in Google Calendar. Unfortunately, there is no way to add such events so via the Google Calendar UI, so normally your only alternative is to do some gnarly web programming to talk to &lt;a href="http://code.google.com/apis/calendar/"&gt;GData&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Things were going well until one day, the Web Content wizard just broke. The GData team changed something on their end which caused my poor little app to stop working. If I remember correctly, it was because they created this new thing about &lt;a href="http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html#register"&gt;registering your web application&lt;/a&gt;. I learned of the problem because some users wrote in, and  &lt;a href="http://blog.bolinfest.com/2009/03/web-content-wizard-is-fixed.html"&gt;eventually sometime I finally found the time to debug the problem and fix it&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Then things were going well again for awhile, but then in late 2009 (when I had some &lt;a href="http://blog.bolinfest.com/2009/10/4-years-2-months-and-1-day.html"&gt;newfound free time&lt;/a&gt;), I decided that it was finally time to migrate bolinfest.com from the Red Hat 9 server it had been running on since 2003! I had a VPS from &lt;a href="http://rimuhosting.com/"&gt;RimuHosting&lt;/a&gt; (who rocks btw!) that I acquired in 2006 for my &lt;a href="http://blog.bolinfest.com/2006/11/did-i-mention-mitcalendarscom.html"&gt;mitcalendars.com project&lt;/a&gt; with Mike Lambert, but I had never had the time to move my content from bolinfest.com over there. (Getting PHP 5 with its &lt;a href="http://www.php.net/manual/en/function.json-decode.php"&gt;native JSON parsing methods&lt;/a&gt; was what finally put my over the edge, as I repeatedly failed at compiling them myself for the version of PHP 4 that was running on Shrike.)&lt;br /&gt;&lt;br /&gt;As you can imagine, not everything worked when I did the migration in 2009. Honestly, I probably should have just gotten an even newer VPS then and migrated both servers, as now bolinfest.com is running on a dusty version of Ubuntu 8.04! (I'm running version 0.9.5 of Mercurial, which means that I don't even have the &lt;code&gt;rebase&lt;/code&gt; extension!) Anyway, when I did the migration, I was far more preoccupied with migrating my conf files for Apache from 1.3 to 2.0 than I was with getting the Web Content Wizard to work again. Every endeavor of mine involving GData always involves a minimum of 3 hours work and a lot of cursing, and I just wasn't up for it.&lt;br /&gt;&lt;br /&gt;Then today I got another polite email asking me why &lt;a href="http://www.bolinfest.com/webcontent/"&gt;the Web Content Wizard&lt;/a&gt; wasn't working. I have amassed quite a few of these over the years, and I generally star them in my inbox, yet never make the time to investigate what was wrong. But for some reason, tonight I felt motivated, so I dug in and decided to debug it. I [wrongly] assumed that GData had changed on me again, so I focused my efforts there first. Surprisingly, the bulk of my time was spent getting the right error message out of PHP, as all it told me was that my request to the GData server was returning a 400. Now, I know GData is pretty lousy at a lot of things, but when it gives you a 400, in my experience, at least it gives you a reason!&lt;br /&gt;&lt;br /&gt;I use cURL under the hood to communicate with GData from PHP, and that has never been pretty. I thought I remembered editing something in &lt;code&gt;/etc/php.ini&lt;/code&gt; (which was now at &lt;code&gt;/etc/php5/apache2/php.ini&lt;/code&gt;) to get cURL working correctly on the old bolinfest.com, but I could not remember what it was, nor did I appear to have documented it.&lt;br /&gt;&lt;br /&gt;The key insight occurred when I changed this call to &lt;code&gt;curl_setopt()&lt;/code&gt; to set the &lt;code&gt;CURLOPT_FAILONERROR&lt;/code&gt; option to &lt;code&gt;false&lt;/code&gt;:&lt;pre&gt;curl_setopt($ch, CURLOPT_FAILONERROR, false);&lt;/pre&gt;Suddenly, my requests to GData contained a real error message:&lt;pre&gt;The value following &amp;quot;version&amp;quot; in the XML declaration must be a quoted string&lt;/pre&gt;This did not make any sense because I was sure that I was sending well-formed XML to GData and it contained the &lt;code&gt;&amp;lt;?xml version="1.0"?&gt;&lt;/code&gt; preamble as it should. I decided to print it back out to make sure that things were being sent as I expected, but when I did so, I noticed that all of my double-quotes were inexplicably escaped with backslashes. That explained why the error message was complaining about well-formed XML, but not where the backslashes came from.&lt;br /&gt;&lt;br /&gt;Then it dawned on me: &lt;code&gt;php.ini&lt;/code&gt; has a bunch of stupid settings with the word &lt;code&gt;magic&lt;/code&gt; in them. Sure enough, I did a search for [&lt;a href="http://www.google.com/search?q=php+HTTP_POST_VARS+escape+quotes&amp;hl=en"&gt;php HTTP_POST_VARS escape quotes&lt;/a&gt;], which eventually led me to the documentation for the PHP function &lt;a href="http://php.net/manual/en/function.stripslashes.php"&gt;stripslashes()&lt;/a&gt;, which finally led me to the documentation for &lt;a href="http://www.php.net/manual/en/info.configuration.php#ini.magic-quotes-gpc"&gt;magic_quotes_gpc&lt;/a&gt;, which basically says: "Because the average PHP developer is so likely to &lt;a href="http://xkcd.com/327/"&gt;Little Bobby Tables himself&lt;/a&gt;, we put the coding behaviors for adults on a high shelf where little PHP developers can't reach them and automatically backslash escape quotes because said developers are so likely to cram that string directly into a SQL statement without escaping it."&lt;br /&gt;&lt;br /&gt;I immediately changed the line in &lt;code&gt;php.ini&lt;/code&gt; to &lt;code&gt;magic_quotes_gpc = Off&lt;/code&gt;, restarted Apache, and all of a sudden, the &lt;a href="http://www.bolinfest.com/webcontent/"&gt;Web Content Wizard was fixed AGAIN!&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;At least until my next server upgrade...&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-2314111514075225756?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/2314111514075225756/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/01/web-content-wizard-is-fixed-again.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/2314111514075225756" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/2314111514075225756" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/01/web-content-wizard-is-fixed-again.html" title="Web Content Wizard is Fixed AGAIN!" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-139331725402314161</id><published>2011-01-05T02:16:00.000-05:00</published><updated>2011-01-05T02:16:49.676-05:00</updated><title type="text">Released a new version of plovr so I can show it off at my two Closure lectures this Thursday</title><content type="html">I just released a &lt;a href="http://code.google.com/p/plovr/downloads/list"&gt;new version of plovr&lt;/a&gt; in time for the two talks on Closure I am giving this Thursday. The first is a guest lecture for an &lt;a href="web.mit.edu/6.470/"&gt;MIT web programming competition: 6.470&lt;/a&gt;. My understanding is that the course attracts a lot of freshmen, so my hope is that plovr can help those who are new to JavaScript leverage the Closure Compiler to help them identify their JavaScript errors (of which there are likely to be many!). This motivated me to fix some &lt;a href="http://code.google.com/p/plovr/issues/detail?id=3"&gt;annoying issues&lt;/a&gt; in plovr that have been lingering for awhile (which the early adopters have been nice enough to put up with for far too long :).&lt;br /&gt;&lt;br /&gt;The second talk I am going to give is part of the &lt;a href="http://www.meetup.com/javascript-2/calendar/15696752/"&gt;Boston JS Meetup&lt;/a&gt;, which I am very excited to attend for the first time. I'm looking forward to talking to the group and showing off what both Closure and plovr can offer to those in industry.&lt;br /&gt;&lt;br /&gt;Also, up until now, I have been making decisions autonomously about new plovr features and config options because I did not have a good way to gather feedback from users other than the bug reports I received. To that end, I finally decided to &lt;a href="http://groups.google.com/group/plovr"&gt;create a Google Group for plovr&lt;/a&gt; where users can help influence plovr development and ask questions about how to use plovr. As I &lt;a href="http://groups.google.com/group/closure-library-discuss/browse_thread/thread/7e0cb6296f898ebd"&gt;noted in the Closure Library group&lt;/a&gt;, I would like to extend plovr to help with CSS management and developing against jQuery (among other things), and I believe these features will only be useful to others if prospective users participate in the discussion. We'll see how all of that works out.&lt;br /&gt;&lt;br /&gt;Finally, one interesting engineering challenge that has plagued plovr since its inception (but has finally been slayed!) is the question of how to manage its dependency on the Closure Library, Closure Compiler, and Closure Templates. With a considerable amount of help from Ilia Mirkin (and a lot of SVN and Hg gymnastics), I finally have a system that I am happy with. Basically, it requires creating a branch for each dependent project to which updates are periodically added and then merged into the main repository. As this is a general problem for open-source projects, I documented it thoroughly on the &lt;a href="http://code.google.com/p/plovr/wiki/MaintainingMirrorsOfClosureCodeInHg"&gt;plovr wiki&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Although it was tricky to set up, it is now possible to build plovr without checking out the other Closure projects. (Previously, a new version of plovr would include my own local checkouts of the closure-library, closure-compiler, and closure-templates repositories, which were at somewhat arbitrary revisions with possibly even more arbitrary/undocumented patches of my own in them.) Further, because plovr has its own clones of the Closure projects in its repository, it is far easier to modify Closure code if it needs to be edited for plovr. For example, it was now &lt;a href="http://code.google.com/p/plovr/source/detail?r=36bb51c08922136d45c2dfdb9b216be1a02e8c1e"&gt;trivial to make the constructor of &lt;code&gt;com.google.javascript.jscomp.Result&lt;/code&gt;&lt;/a&gt; public, whereas previously I would have had to create a completely separate binary for the Closure Compiler with this one modification and then replace it with the one in plovr's &lt;code&gt;lib&lt;/code&gt; directory. Further, I would forever have to keep my checkout of the Closure Compiler with the modification around so that I could make further modifications in the future while no outside developer would have any insight into that process -- no more!&lt;br /&gt;&lt;br /&gt;This also allowed me to &lt;a href="http://code.google.com/p/plovr/source/detail?r=5f466701a04b9b7027e9368886d9ebbf30561367"&gt;clean up the build process for plovr&lt;/a&gt; and remove some unnecessary and/or duplicate dependencies. The release of plovr I did back in December was 11.7 MB, but the new release is down to 8.4 MB, which is the smallest plovr jar I have ever published! Things are definitely moving in the right direction for the project.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-139331725402314161?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/139331725402314161/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2011/01/released-new-version-of-plovr-so-i-can.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/139331725402314161" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/139331725402314161" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2011/01/released-new-version-of-plovr-so-i-can.html" title="Released a new version of plovr so I can show it off at my two Closure lectures this Thursday" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-5743938745033079727</id><published>2010-12-27T16:50:00.000-05:00</published><updated>2010-12-27T16:50:08.370-05:00</updated><title type="text">I got a new computer for Christmas! Sorta...</title><content type="html">About a year ago, I bought myself a Lenovo ThinkPad (T500). Because I had been working at Google from 2005-2009 and my work laptop was sufficient for surfing the Web while at home, my last personal laptop purchase was in 2004 when I bought an IBM ThinkPad (T42). I &lt;em&gt;loved&lt;/em&gt; my ThinkPad, as I spent many hours of quality time with it implementing my &lt;a href="http://groups.csail.mit.edu/uid/chickenfoot/"&gt;Master's thesis at MIT&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Therefore, when it came time to buy a new machine in 2009, I decided to look at ThinkPads. Yes, I had heard that the quality of the ThinkPad had dropped considerably ever since Lenovo had taken things over from IBM. Yes, all my friends were developing on Macs, but I found them to be irritating to type on and my primary development applications (Google Chrome and Eclipse) felt slow on OS X. And yes, Windows has more than its share of annoyances. At the time, ThinkPads were shipping with Vista (arguably one of the worst Windows releases ever), but they came with a voucher for a free copy of Windows 7, which was reported to be a substantial improvement.&lt;br /&gt;&lt;br /&gt;After weighing all of my options, I decided to save some cash and buck the trend of my contemporaries: I chose to be a PC instead of a Mac.&lt;br /&gt;&lt;br /&gt;From the abysmal experience of making my online purchase of a Lenovo ThinkPad, I should have known that my experience with the machine itself would also be a disaster. The Lenovo web site was slow and failed to communicate what I was buying. I inadvertently bought a machine with a 32-bit processor (I still don't know why any new machine in the fall of 2009 would not be 64-bit), which could not take advantage of the 4GB of RAM that I paid for. (Searching online forums, I do not appear to be the only one who has made this mistake.) Further, it took two to three weeks to get my laptop. If I had decided to buy a Mac, I could have just walked to the Apple store and had one in my hot little hands that day.&lt;br /&gt;&lt;br /&gt;Then there was Vista. Vista makes me angry. Sometimes when my laptop goes to sleep, it also turns off the wireless radio and fails to turn it back on when the machine wakes up. Sometimes the physical buttons to control the volume of the laptop fail to communicate with Vista. (Being unable to control the volume of your computer can be an embarrassing experience.) Sometimes the built-in video camera works, sometimes it doesn't, but either way, rebooting (the go-to solution for any Windows user) is an unreliable fix for the problem. The DVD drive is also finicky, as it inexplicably pops open from time to time. Overall, this is a horrible experience.&lt;br /&gt;&lt;br /&gt;But hey, what about that Windows 7 voucher? Apparently you had to redeem it by calling Lenovo customer support after Windows 7 was officially released, which was several weeks after I got my ThinkPad. "Call Lenovo" sat near the bottom of my to-do list for months (who is eager to call customer support?), so by the time I made time to call Lenovo, they told me that my voucher had expired. I complained and they said they would speak to someone and contact me with a response, but they never did.&lt;br /&gt;&lt;br /&gt;After all of this, I was incredibly unhappy with my ThinkPad. I wanted to try to put Linux on it, but I had a number of &lt;a href="http://blog.bolinfest.com/2010/10/upcoming-talks.html"&gt;speaking engagements&lt;/a&gt; coming up where I was going to present from my laptop, so it did not seem prudent to wipe my machine before embarking on a series of talks. Though even after my speaking series was over, it was hard to find the time to sit down and install Linux. I had never done it by myself before, and I wanted to take the time to read up on it so I could understand what I was doing and have a backup plan in case I was unsuccessful and needed to revert back.&lt;br /&gt;&lt;br /&gt;This year, on Christmas Day, I decided to just go for it and install Ubuntu on my ThinkPad. I copied the .iso onto a USB stick, wedged it in the side of my ThinkPad, and rebooted. I hit F1 to change the bootloader to run off the USB stick and made it to the Ubuntu installation screen. I spent a bit of time playing with options to repartition my hard disk so that I could preserve Vista and my files in case I needed to get them back (for me, this has always been the most confusing part when trying to step through a Linux installation), but I got fed up and decided that all of my important data was in the cloud (which includes Mercurial and SVN repos on other machines) and that I had no desire to go back to Vista anyway, so I decided to dedicate the entire hard disk to Ubuntu and abandon Vista forever.&lt;br /&gt;&lt;br /&gt;When I decided to blow away my operating system, I knew that it would likely be a rash decision that I would pay for later, but so far, it has been &lt;em&gt;FREAKING AWESOME!&lt;/em&gt; The physical volume buttons on the ThinkPad &lt;em&gt;just work&lt;/em&gt;. The &lt;a href="http://googletalk.blogspot.com/2010/08/use-linux-now-you-can-video-chat-too.html"&gt;Google Talk plugin for Linux&lt;/a&gt; also &lt;em&gt;just works&lt;/em&gt;. And instead of the dual, competing gaudy wireless managers in Vista (one provided by Windows and the other provided by Lenovo), there is now a single, simple icon/dropdown interface in the toolbar to select a wireless network, just like on OS X.&lt;br /&gt;&lt;br /&gt;Using my laptop is now enjoyable again. It feels like I got a brand new machine, so even though I didn't manage to get &lt;a href="http://www.google.com/chromeos/pilot-program-cr48.html"&gt;the new computer that I really wanted for Christmas&lt;/a&gt;, this certainly seems like the next best thing.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;amp;tag=bolinfestcom-20&amp;amp;link_code=as3&amp;amp;camp=211189&amp;amp;creative=373489&amp;amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-5743938745033079727?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/5743938745033079727/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2010/12/i-got-new-computer-for-christmas-sorta.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/5743938745033079727" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/5743938745033079727" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2010/12/i-got-new-computer-for-christmas-sorta.html" title="I got a new computer for Christmas! Sorta..." /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-5999403971486235416</id><published>2010-10-21T22:50:00.000-04:00</published><updated>2010-10-21T22:50:58.679-04:00</updated><title type="text">Slides from MIT Talk</title><content type="html">Last week, I gave two talks about Closure in Cambridge: one at Google and the other at MIT CSAIL. I had a great time and fielded lots of good questions from sharp individuals, so I'm looking forward to my next two speaking events in &lt;a href="http://blog.bolinfest.com/2010/10/upcoming-talks.html"&gt;New York City&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As someone requested that I upload my slides, I am more than happy to share them:&lt;br /&gt;&lt;br /&gt;&lt;iframe src="https://docs.google.com/present/embed?id=ddkqzgzg_35dhss95gx" frameborder="0" width="410" height="342"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;I would argue that one of the best parts of the talk is the &lt;a href="http://plovr.com/"&gt;plovr&lt;/a&gt; demo, which sadly is not communicated through the slides. Though if you are in the NYC area, you can come and see such a demo live if you come to one of my upcoming talks! Also, O'Reilly provides me with three copies of my book to give away at each event, so even if you aren't interested in plovr, you are probably interested in the chance to win a free book! (Though I am embarrassed to admit that I don't have the hang of how to run a "giveaway" yet, so at the MIT talk, I ended up giving books to those who hung out the longest to ask questions.)&lt;br /&gt;&lt;br /&gt;Though after spending quite a bit of time preparing my talks last week, I am pretty disappointed with my experience creating a presentation with Google Docs. Given that the UI is displayed using HTML and CSS, it should be dead simple to change the color palette of a presentation. For example, I am generally interested in three styles: slide title, plain text, and sample code. Every time I want to set text as sample code, I have to select the text, choose "Courier New" from the font dropdown and then select my code color from the font color picker. Under the hood, there are tons of &lt;code&gt;&amp;lt;div&gt;&lt;/code&gt;s with the same inline CSS styles whereas if I created my slides by hand in HTML, I would have many &lt;code&gt;&amp;lt;code&gt;&lt;/code&gt; tags that could easily be restyled with a single CSS rule.&lt;br /&gt;&lt;br /&gt;Trying to use different themes for Google and MIT for the same presentation would have taken hours because of all the time mousing around to select text and change all the styles, so I didn't even try. I know that Google Docs is nowhere near Keynote or Powerpoint in terms of its feature set, but I didn't realize how far behind it is. It only comes with 15 themes, and I'd argue that none of them are particularly good for a developer who wants to display code samples. I would switch to a Microsoft or an Apple solution, but I am often switching between computers, and the thought of emailing a slide deck around and clogging my inbox with twenty versions of the same presentation is depressing.&lt;br /&gt;&lt;br /&gt;There's a good chance that I'll build my own lightweight presentation editor in HTML that is tailored for creating talks by software engineers. I basically had to do something similar in order to write my book, but that's an exciting topic for a future blog post!&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-5999403971486235416?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/5999403971486235416/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2010/10/slides-from-mit-talk.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/5999403971486235416" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/5999403971486235416" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2010/10/slides-from-mit-talk.html" title="Slides from MIT Talk" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-2308041430725310519</id><published>2010-10-11T14:29:00.000-04:00</published><updated>2010-10-11T14:29:43.489-04:00</updated><title type="text">Upcoming Talks</title><content type="html">Over the next couple of months, I will be giving several talks about Closure:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;October 13: Getting the most out of Closure&lt;/b&gt;&lt;br /&gt;Google: Cambridge, MA&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;a href="http://www.csail.mit.edu/events/eventcalendar/calendar.php?show=event&amp;id=2745"&gt;October 15: Building a 100K LOC JavaScript application with Google Closure&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;MIT CSAIL: Cambridge, MA&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;a href="http://www.meetup.com/NYC-GTUG/calendar/14951278/?from=list&amp;offset=0""&gt;October 27: Introduction to Closure Tools and plovr&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;Google: New York, NY (as part of the &lt;a href="http://www.meetup.com/NYC-GTUG/"&gt;NYC-GTUG Meetup&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;a href="http://www.meetup.com/Web-Performance-NY/calendar/14393583/"&gt;November 10: Compiling your web site: Closure compiler&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;Location TBA: New York, NY (as part of the &lt;a href="http://www.meetup.com/Web-Performance-NY/"&gt;Web Performance NY Meetup&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;If you are in the area for any of these talks, please come and say hello! I'm also more than happy to sign books (and may even be giving some away!) at each of these talks.&lt;br /&gt;&lt;br /&gt;Hopefully, I will also get a chance to speak at both the Boston and NYC JavaScript meetups. I also hope to make it out to the Bay Area, Austin, Chicago, and Sydney (where I seem to have an &lt;a href="http://twitter.com/#!/sydjs/status/26551194261"&gt;open invitation&lt;/a&gt;!). As I have no official "book tour," my personal travel basically determines when and where I set up speaking events.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to build sophisticated web applications like Gmail and Google Maps!&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-2308041430725310519?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/2308041430725310519/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2010/10/upcoming-talks.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/2308041430725310519" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/2308041430725310519" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2010/10/upcoming-talks.html" title="Upcoming Talks" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-8643490245816378377</id><published>2010-10-06T01:04:00.000-04:00</published><updated>2010-10-06T01:04:56.485-04:00</updated><title type="text">Creating a 100K LOC Application in JavaScript with Closure and plovr</title><content type="html">Last night at an MIT infosession for &lt;a href="http://www.yext.com/recruiting/engineers/"&gt;Yext&lt;/a&gt;, I presented some statistics about the codebase for &lt;a href="http://www.yext.com/rep/"&gt;Yext Rep&lt;/a&gt;, which is our free, online reputation management system for local businesses. Rep is a substantial Rich Internet Application (RIA), topping out at just over 144K lines of JavaScript and Closure Templates, which amounts to over 4MB of JavaScript. In the spirit of a traditional RIA, Rep is a single web page that dynamically loads both code and data via AJAX (navigation state is stored in the URL fragment, just like in Gmail).&lt;br /&gt;&lt;br /&gt;The first challenge in writing a 100K LOC JavaScript application is managing the codebase. Fortunately, the &lt;a href="http://code.google.com/closure/library/"&gt;Closure Library&lt;/a&gt; provides both a comprehensive API and a meaningful way to organize dependencies in JavaScript. By using thoughtful namespaces and explicit dependencies, it is easy to find the functionality you are looking for and to build things up without creating cyclical dependencies.&lt;br /&gt;&lt;br /&gt;The second challenge, of course, is getting the application to load quickly even though there is 4380K of JavaScript to send down to the browser. Any solution that would prevent Rep from loading quickly would not be an option. As you may have guessed, leveraging Closure makes it possible for us to reduce that 4380K down to 73K, which is &lt;em&gt;less than 2% of the original size&lt;/em&gt;. Here is how it breaks down:&lt;br /&gt;&lt;br /&gt;&lt;table style="border: 1px solid #CCC" cellpadding="3"&gt;&lt;tr&gt;  &lt;th style="border: 1px solid #CCC"&gt;KB&lt;/th&gt;  &lt;th style="border: 1px solid #CCC"&gt;Technique&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;  &lt;td style="border: 1px solid #CCC"&gt;4380&lt;/td&gt;  &lt;td style="border: 1px solid #CCC"&gt;Original JavaScript&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;  &lt;td style="border: 1px solid #CCC"&gt;709&lt;/td&gt;  &lt;td style="border: 1px solid #CCC"&gt;Compilation using the &lt;a href="http://code.google.com/closure/compiler/docs/api-tutorial3.html"&gt;Advanced mode&lt;/a&gt; of the &lt;a href="http://code.google.com/closure/compiler/"&gt;Closure Compiler&lt;/a&gt; (84% reduction)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;  &lt;td style="border: 1px solid #CCC"&gt;215&lt;/td&gt;  &lt;td style="border: 1px solid #CCC"&gt;Dividing the compiled code into modules using &lt;a href="http://www.plovr.com/"&gt;plovr&lt;/a&gt; (70% reduction)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;  &lt;td style="border: 1px solid #CCC"&gt;73&lt;/td&gt;  &lt;td style="border: 1px solid #CCC"&gt;gzip (66% reduction)&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;One of the less commonly known techniques that we use is dividing JavaScript into modules. This is a feature of the Closure Compiler, but using &lt;a href="http://www.plovr.com/"&gt;plovr&lt;/a&gt; makes the module splitting feature of the Compiler far more accessible and manageable. Over the weekend, I worked on improving &lt;a href="http://code.google.com/p/plovr/source/detail?r=d4dfb48a172f286b6b6f4469c80ae8ae3bee925a"&gt;module&lt;/a&gt; &lt;a href="http://code.google.com/p/plovr/source/detail?r=50c0cbfd185b9543059c0f4ed4e3197cd978cb05"&gt;support&lt;/a&gt; in plovr. There are still a few bugs left to shake out, but I plan to do another release of plovr with more detailed documentation on modules soon.&lt;br /&gt;&lt;br /&gt;Want to learn more about Closure? Pick up a copy of my new book, &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Closure: The Definitive Guide (O'Reilly)&lt;/a&gt;, and learn how to leverage Closure to create your own RIA!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-8643490245816378377?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/8643490245816378377/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2010/10/creating-100k-loc-application-in.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/8643490245816378377" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/8643490245816378377" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2010/10/creating-100k-loc-application-in.html" title="Creating a 100K LOC Application in JavaScript with Closure and plovr" /><author><name>Bolinfeed</name><uri>http://www.blogger.com/profile/16203722926608871325</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-4104685158176443782</id><published>2010-09-27T23:21:00.008-04:00</published><updated>2010-09-28T00:34:47.135-04:00</updated><title type="text">Give a man a fish or teach a man to fish?</title><content type="html">&lt;div style="color: #333333; font-family: Georgia,serif; margin: 0px;"&gt;&lt;span class="Apple-style-span" style="border-collapse: separate;  font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span id="internal-source-marker_0.7958290819078684" style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;Ostensibly, &lt;/span&gt;&lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;amp;tag=bolinfestcom-20&amp;amp;link_code=as3&amp;amp;camp=211189&amp;amp;creative=373489&amp;amp;creativeASIN=1449381871"&gt;&lt;i&gt;Closure: The Definitive Guide&lt;/i&gt;&lt;/a&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt; is a book about &lt;/span&gt;&lt;a href="http://code.google.com/closure/"&gt;Closure&lt;/a&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;, a suite of JavaScript tools from Google, but I tried to make it about more than that. It also discusses some of the more subtle aspects of JavaScript, examines the clever aspects of Closure’s design, and espouses best practices for software engineering. That last point is of particular importance to me, because as I explain in the &lt;/span&gt;&lt;a href="http://my.safaribooksonline.com/9781449381882/preface"&gt;preface&lt;/a&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;, I expect the size of JavaScript codebases to grow quickly in the coming years (&lt;/span&gt;&lt;a href="http://engineering.twitter.com/2010/09/tech-behind-new-twittercom.html"&gt;if not sooner&lt;/a&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;), and developers will need Closure (or something like it) in order to do JavaScript programming &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="border-collapse: separate;  font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;“&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="border-collapse: separate;  font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;in the large.&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="border-collapse: separate;  font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;”&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;span class="Apple-style-span" style="border-collapse: separate;  font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;Therefore, even if you have adamantly committed yourself to jQuery, I think you will learn a lot from this book. As &lt;/span&gt;&lt;a href="http://blog.rebeccamurphey.com/on-jquery-large-applications"&gt;some members of the jQuery community have already begun to concede&lt;/a&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;, jQuery is not the right solution for large software projects. Perhaps jQuery will reach a point where it is redesigned for writing more complex applications, and if it does, I certainly hope it looks to Closure for guidance. If one wants to build something that rivals Gmail or Google Maps in frontend sophistication and performance, then why not look to the toolkit that made those products possible and learn from the design decisions that shaped it?&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="border-collapse: separate;  font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;An alternative interpretation of &lt;/span&gt;&lt;span style="background-color: transparent;  font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;Closure: The Definitive Guide&lt;/span&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt; is that it is just a braindump of everything I learned about web development while at Google. As I chose to spend four years at Google rather than in a PhD program, I often think of the book as my dissertation (though unlike a doctoral thesis, someone will actually read it -- zing!). However, the analogy does not quite fit, as I cannot claim Closure to be a product of my own research. At Google, I was far more often a consumer of Closure than a producer. Yet in the course of writing the book, the balance has shifted, and I have found more time to submit patches, file bug reports, and build tools on top of Closure such as &lt;/span&gt;&lt;a href="http://plovr.com/"&gt;plovr&lt;/a&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;. I think that it was only until I had really gotten into writing the book that I could fully appreciate the entire toolkit and confidently contribute back to all areas of it, knowing that my contributions would be consistent with its underlying principles.&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="border-collapse: separate;  font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;Which brings me to the title of this blog post: Give a man a fish or teach a man to fish? In a world filled with O’Reilly cookbooks and Stack Overflow articles, I often see developers focused on consuming the information that helps them fix the issue that they are currently debugging and then moving on to the next problem. The drawback of this approach is that when that developer encounters a problem that no one has blogged about, he is stuck. Because the Web has always given him fish, he has no idea how to fish when Google cannot find the answer.&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="border-collapse: separate;  font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;For this reason, &lt;/span&gt;&lt;span style="background-color: transparent;  font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;Closure: The Definitive Guide&lt;/span&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt; aims to be a “teach a man to fish” type of book. When I think about my favorite technical books, such as &lt;/span&gt;&lt;span style="background-color: transparent;  font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;Effective Java&lt;/span&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt; by Joshua Bloch, they are the ones that discuss principles as well as practice. When you have the correct mental model for a tool or programming language, it is possible to exploit that knowledge to solve new problems. By comparison, if you limit yourself to solutions that are pasted on message boards, then by definition, &lt;/span&gt;&lt;span style="background-color: transparent;  font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;you will never be solving problems that have not been solved before&lt;/span&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt; (how boring!). Rest assured that at 550 pages, &lt;/span&gt;&lt;span style="background-color: transparent;  font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;Closure: The Definitive Guide&lt;/span&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt; contains plenty of code samples and other practical information, but it also does its best to explain the problems that Closure is designed to solve and why the tools work the way they do.&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="border-collapse: separate;  font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;The book also touches upon other areas related to web development. As mentioned earlier, I care a lot about software engineering, so I include a surprisingly large discussion of &lt;/span&gt;&lt;a href="http://ant.apache.org/"&gt;Ant&lt;/a&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;, as I believe it is incredibly important to have scripts for automating common tasks (it is not important that they be written in Ant, just that they exist). I even went so far as to create &lt;/span&gt;&lt;a href="http://plovr.com/"&gt;plovr&lt;/a&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt; (which is introduced in Appendix C) because I did not feel that the edit-reload-test cycle that one gets out of the box with Closure was fast enough. The book also contains other such “digressions,” such as the section on &lt;/span&gt;&lt;span style="background-color: transparent; color: #6aa84f; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;goog.partial()&lt;/span&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt; that goes into considerable detail about function currying and the scope of a closure. In other parts of the book, you will find short asides on security vulnerabilities such as XSS and XSRF (which gave me an excuse to include &lt;/span&gt;&lt;a href="http://xkcd.com/327/"&gt;Exploits of a Mom&lt;/a&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt; on page 76!).&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="border-collapse: separate;  font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;In sum, &lt;/span&gt;&lt;span style="background-color: transparent;  font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;Closure: The Definitive Guide&lt;/span&gt;&lt;span style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt; aims to be much more than a manual. (The last thing I wanted to do was produce a tome that was a rehash of documentation that was already freely available online.) Instead, it tries to explain the tools and principles that you need &lt;a href="http://twitter.com/googlereader/status/25317297359"&gt;to build a rich web application&lt;/a&gt; in JavaScript while demonstrating how Closure employs those principles to be the best tool for the job.&lt;/span&gt; &lt;span id="internal-source-marker_0.7958290819078684" style="background-color: transparent;  font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"&gt;If you find that your latest web project is starting to collapse under its own weight, then perhaps it’s time you decided to learn what Closure is all about.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-4104685158176443782?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/4104685158176443782/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2010/09/give-man-fish-or-teach-man-to-fish.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/4104685158176443782" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/4104685158176443782" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2010/09/give-man-fish-or-teach-man-to-fish.html" title="Give a man a fish or teach a man to fish?" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-4980105001283025359</id><published>2010-09-22T20:19:00.003-04:00</published><updated>2010-09-24T08:47:14.554-04:00</updated><title type="text">Is Your Book Published Yet?</title><content type="html">Over the past few months, I have often been asked: “When is your book going to be published?” In today’s world, that is a surprisingly difficult question to answer.&lt;br /&gt;&lt;br /&gt;For example, shortly after the book went to print, the electronic version of the final draft became available for purchase on O’Reilly’s web site (while simultaneously appearing on &lt;a href="http://oreilly.com/roughcuts/"&gt;O’Reilly’s Rough Cuts page&lt;/a&gt;, implying that it is not yet complete). According to &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;Amazon&lt;/a&gt;, the publication date is September 27, though it is unclear whether that is when Amazon pre-orders will be shipped or when they will be received (or neither!). As if my eagerness had not already gotten the best of my patience, my editors at O’Reilly explained to me that the book will not appear in brick-and-mortar bookstores until two to three weeks after Amazon has it!&lt;br /&gt;&lt;br /&gt;So for myself, I decided to consider my book “published” as soon as I had a physical copy in my own hot little hands, which finally graced my doorstep today:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center"&gt;&lt;br /&gt;&lt;img src="http://bolinfest.com/closure/first_copy.jpg" width="300px" height="400px"&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;If you need me, I’ll be at home spilling champagne on my new book!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; I stopped by the Union Square Barnes and Noble in NYC and they said that they should have &lt;i&gt;Closure: The Definitive Guide&lt;/i&gt; in stock on Tuesday. Also, sometime in the last couple of days, Amazon changed from taking pre-orders to declaring the book "In Stock," so perhaps 9/27 is a real date for both online and brick-and-mortar sales!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-4980105001283025359?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/4980105001283025359/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2010/09/is-your-book-published-yet.html#comment-form" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/4980105001283025359" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/4980105001283025359" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2010/09/is-your-book-published-yet.html" title="Is Your Book Published Yet?" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-8244849744809480033</id><published>2010-09-07T22:21:00.003-04:00</published><updated>2010-09-07T23:09:22.571-04:00</updated><title type="text">Using Soy instead of PHP or JSP</title><content type="html">Traditionally, I have used PHP to create the basic template for &lt;a href="http://bolinfest.com/"&gt;bolinfest.com&lt;/a&gt;, but I never felt good about it. The mixture of presentation and content made my PHP files hard to organize, and having my URLs end in &lt;code&gt;.php&lt;/code&gt; was embarrassing (and would be a pain to set up redirects for if I ever wanted to change them).&lt;br /&gt;&lt;br /&gt;Nevertheless, PHP was dead simple to setup with Apache on Linux, and it was a convenient way to include a standard header and footer, along with a little logic to highlight the navigation tab for the page that was currently being displayed. At the same time, I found it annoying to duplicate the setup on Windows, which meant that I often ended up editing the files directly on the server rather than developing locally, checking into version control, and checking out on the server. It just encouraged bad habits all around.&lt;br /&gt;&lt;br /&gt;Fortunately, I eliminated this problem over the weekend by creating a dead simple web server that works with &lt;a href="http://code.google.com/closure/templates/"&gt;Closure Templates&lt;/a&gt;. For now, I'm calling it &lt;em&gt;soyweb&lt;/em&gt; (Soy is another name for Closure Templates), and it is available as an experimental feature of &lt;a href="http://blog.bolinfest.com/2010/08/plovr-lowering-barrier-to-entry-for.html"&gt;plovr&lt;/a&gt;. (You can &lt;a href="http://code.google.com/p/plovr/downloads/list"&gt;download&lt;/a&gt; the latest version of plovr/soyweb from its Google Code site.)&lt;br /&gt;&lt;br /&gt;Because Closure Templates are written in Java, so is my web server, which is fairly easy to do with the &lt;a href="http://download.oracle.com/javase/6/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/HttpServer.html"&gt;HttpServer&lt;/a&gt; class introduced in Java 1.6. Instead of having to set up a Windows service, I can run the following command to serve a directory of Soy files as HTML:&lt;pre&gt;java -jar plovr.jar soyweb --dir DIRECTORY_OF_SOY_FILES&lt;/pre&gt; Each Soy file that is served by soyweb must have a template named &lt;code&gt;.base&lt;/code&gt; that does not take any parameters. Currently, &lt;a href="http://plovr.com/"&gt;plovr.com&lt;/a&gt; is being served using soyweb, so you can see the Soy templates that power the web site &lt;a href="http://code.google.com/p/plovr/source/browse/www/"&gt;in the repository&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A file such as &lt;a href="http://code.google.com/p/plovr/source/browse/www/docs.soy"&gt;docs.soy&lt;/a&gt; is served via both &lt;a href="http://plovr.com/docs"&gt;http://plovr.com/docs&lt;/a&gt; and &lt;a href="http://plovr.com/docs.html"&gt;http://plovr.com/docs.html&lt;/a&gt;. Personally, I have been linking to the version with the &lt;code&gt;.html&lt;/code&gt; extension, but I can see where it may be desirable to not use any extension at all, so soyweb supports both. In either case, I am no longer exposing the underlying technology used to serve the site via the file extension.&lt;br /&gt;&lt;br /&gt;As Soy supports specifying &lt;a href="http://code.google.com/closure/templates/docs/java_usage.html#globals"&gt;compile time global variables&lt;/a&gt;, I have decided to expose this feature via a JSON config file. This appears to be more flexible than Soy's file format for globals, as the config file for soyweb will turn a JSON list into a &lt;a href="http://closure-templates.googlecode.com/svn/trunk/javadoc-complete/com/google/template/soy/data/SoyListData.html"&gt;SoyListData&lt;/a&gt; and a JSON map into a &lt;a href="http://closure-templates.googlecode.com/svn/trunk/javadoc-complete/com/google/template/soy/data/SoyMapData.html"&gt;SoyMapData&lt;/a&gt;. (Currently, comments are not allowed in the config file, but as soon as the fix for &lt;a href="http://code.google.com/p/google-gson/issues/detail?id=212"&gt;bug 212&lt;/a&gt; is released for &lt;a href="http://code.google.com/p/google-gson/"&gt;gson&lt;/a&gt;, I will upgrade plovr to allow comments in config files.) The use of globals has already proven useful as I use &lt;a href="http://code.google.com/p/plovr/source/browse/www-globals.js"&gt;one set of values&lt;/a&gt; when developing locally, and &lt;a href="http://code.google.com/p/plovr/source/browse/prod-globals.js"&gt;another set of values&lt;/a&gt; when developing in production.&lt;br /&gt;&lt;br /&gt;By default, soyweb recompiles all Soy files on every request, which is extremely convenient for development. (In reading about &lt;a href="http://nodejs.org/"&gt;node.js&lt;/a&gt;, it sounded like I would have to restart Node every time I edited a Soy file, which would be unacceptable for development. Nevertheless, I am very excited about the future of Node, and would love to see more Node/Closure integration down the road.) Obviously, dynamic recompilation is not a good feature for soyweb in production, so there is a &lt;code&gt;--static&lt;/code&gt; flag that can be used to disable it. The other flag of interest is &lt;code&gt;--port&lt;/code&gt;, which can be used to specify which port soyweb runs on (defaults to 9811), so I use the following command to run soyweb in production to serve &lt;a href="http://plovr.com/"&gt;plovr.com&lt;/a&gt;:&lt;pre&gt;java -jar plovr.jar soyweb --dir www --static --globals prod-globals.js&lt;/pre&gt; Because I am running Apache, I had to set up the following proxy from port 80:&lt;pre&gt;  ProxyRequests Off&lt;br /&gt;&lt;br /&gt;  &amp;lt;Proxy *&gt;&lt;br /&gt;    Order deny,allow&lt;br /&gt;    Allow from all&lt;br /&gt;  &amp;lt;/Proxy&gt;&lt;br /&gt;&lt;br /&gt;  ProxyPass        / http://plovr.com:9811/&lt;br /&gt;  ProxyPassReverse / http://plovr.com:9811/&lt;/pre&gt; Be aware that I produced that by a series of guess-and-check operations while reading the Apache documentation on &lt;a href="http://httpd.apache.org/docs/2.0/mod/mod_proxy.html"&gt;mod_proxy&lt;/a&gt;, so it is quite possible I am not using best practices in that respect.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-8244849744809480033?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/8244849744809480033/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2010/09/using-soy-instead-of-php-or-jsp.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/8244849744809480033" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/8244849744809480033" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2010/09/using-soy-instead-of-php-or-jsp.html" title="Using Soy instead of PHP or JSP" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-3097562305673362051</id><published>2010-08-31T22:13:00.004-04:00</published><updated>2010-08-31T23:11:33.637-04:00</updated><title type="text">Trying out the Amazon Associates Program</title><content type="html">As my book will be available for sale on Amazon soon (currently, you can only &lt;a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&amp;tag=bolinfestcom-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=1449381871"&gt;pre-order it&lt;/a&gt;), I have decided to try out their affiliates program. The idea is that I place an ad to a product on Amazon on my site, and I get some fraction of the sale, if one occurs. Although the concept is straightforward, setting up the ad is not.&lt;br /&gt;&lt;br /&gt;Setting up the ad is awkward, as the HTML snippet I am instructed to embed is:&lt;pre&gt;&amp;lt;a href="http://www.amazon.com/gp/product/1449381871?&lt;br /&gt;ie=UTF8&amp;tag=bolinfestcom-20&amp;linkCode=as2&amp;camp=1789&amp;&lt;br /&gt;creative=9325&amp;creativeASIN=1449381871"&gt;&lt;br /&gt;&lt;span style="color: #A00"&gt;&amp;lt;img border="0" src="517mVDCbOtL._SL160_.jpg"&gt;&lt;/span&gt;&lt;br /&gt;&amp;lt;/a&gt;&amp;lt;img src="http://www.assoc-amazon.com/e/ir?&lt;br /&gt;t=bolinfestcom-20&amp;l=as2&amp;o=1&amp;a=1449381871"&lt;br /&gt;width="1" height="1" border="0" alt=""&lt;br /&gt;style="border:none !important; margin:0px !important;" /&gt;&lt;/pre&gt;As I have highlighted in red, the source of the first image is a relative URL. That means you have to host the image yourself in order for this to work. Amazon has instructions that tell you as much:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;IMPORTANT:&lt;/b&gt; Save this image to your server, then cut and paste the HTML code in the lower left textbox into your web page.&lt;br /&gt;&lt;br /&gt;But...why? I can think of three possible reasons:&lt;ol&gt;&lt;li&gt;Amazon requires the image to be on a foreign server because they have some bizarre tracking JavaScript that requires it.&lt;br /&gt;  &lt;li&gt;Amazon is too cheap to host the image themselves.&lt;br /&gt;  &lt;li&gt;Amazon is being dumb.&lt;br /&gt;&lt;/ol&gt;Neither of the first two reasons make a lot of sense to me, so I'll have to assume it's the third. Having created embeddable advertising units for web pages and helped users set them up, I can say with certainty that this additional step will make embedding the ad substantially more difficult for the average web site owner.&lt;br /&gt;&lt;br /&gt;(Another nit: one of the image tags is self-closing while the other is not. Is Amazon trying to embed HTML or XHTML? I don't really care which, I'd just like them to be consistent!)&lt;br /&gt;&lt;br /&gt;The image that Amazon provided me with was &lt;a href="http://bolinfest.com/closure/517mVDCbOtL._SL160_-original.jpg"&gt;small and grainy&lt;/a&gt;, so I decided to use a different book cover image that I cropped from O'Reilly's web site (that is why it still has the Rough Cuts branding on it). At 56K, I really need to use &lt;a href="http://pmt.sourceforge.net/pngcrush/"&gt;Pngcrush&lt;/a&gt;, but I'll save that task for another day. By changing the source of the image tag from 517mVDCbOtL._SL160_.jpg to http://plovr.com/cover.png and embedding the HTML, everything seems to work just fine.&lt;br /&gt;&lt;br /&gt;Except...then I wanted to enable the fancy &lt;a href="https://affiliate-program.amazon.com/gp/associates/tips/t8"&gt;Product Previews&lt;/a&gt; feature that enables a user to mouse over the book cover to get a hovercard with more product details. I followed the instructions and added the script tag to enable Product Previews on my page:&lt;ul&gt;&lt;li&gt;On Firefox, the script works as intended.&lt;br /&gt;  &lt;li&gt;On IE 8, the hovercard appears briefly, triggering the progress bar. Once the progress bar finishes loading, the hovercard disappears again.&lt;br /&gt;  &lt;li&gt;On Webkit-based browsers (i.e., both Chrome and Safari), the hovercard appears, but it is empty. When I inspect it, the hovercard appears to contain an iframe with all of the appropriate content, except the CSS of the iframe is &lt;code&gt;visibility:hidden&lt;/code&gt;, which is why nothing is displayed.&lt;br /&gt;&lt;/ul&gt;Also, the snippet of JavaScript that you are instructed to use is:&lt;pre&gt;&amp;lt;script type="text/javascript" src="http://www.assoc-amazon.com/&lt;br /&gt;s/link-enhancer?tag=bolinfestcom-20&amp;o=1"&gt;&lt;/pre&gt;A blocking script tag? Gross! As &lt;a href="http://www.stevesouders.com/blog/2009/12/01/google-analytics-goes-async/"&gt;Steve Souders explains on his blog&lt;/a&gt;, Google Analytics has gone async and so should you! I wrote some JavaScript that would load Amazon's JavaScript asynchronously, but that did not work because Amazon's script contains a call to &lt;code&gt;document.write()&lt;/code&gt;, so it must be executed while the page is still being written.&lt;br /&gt;&lt;br /&gt;For a company like Amazon, this is pretty pathetic. Again, they need to:&lt;ol&gt;&lt;li&gt;Host the images for their products so the HTML snippet is easier to embed.&lt;br /&gt;  &lt;li&gt;Clean up the HTML snippet so it is consistent with its use of closing tags.&lt;br /&gt;  &lt;li&gt;Get a higher quality image for the cover of my book!&lt;br /&gt;  &lt;li&gt;Fix the JavaScript for the Product Previews feature so that it works on all modern browsers, including IE6 and later.&lt;br /&gt;  &lt;li&gt;Rewrite the Product Previews JavaScript so that it can be run after the page loads (i.e., so it does not depend on &lt;code&gt;document.write()&lt;/code&gt;).&lt;br /&gt;  &lt;li&gt;Change the Product Previews snippet that users have to embed so that it loads the Product Previews JavaScript asynchronously.&lt;br /&gt;&lt;/ol&gt;This is fairly basic stuff that could easily help Amazon drive more traffic to their site. What are they waiting for?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-3097562305673362051?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/3097562305673362051/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2010/08/trying-out-amazon-associates-program.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/3097562305673362051" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/3097562305673362051" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2010/08/trying-out-amazon-associates-program.html" title="Trying out the Amazon Associates Program" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-2643411369704716101</id><published>2010-08-09T22:57:00.003-04:00</published><updated>2010-08-09T23:45:52.305-04:00</updated><title type="text">plovr: Lowering the Barrier to Entry for Closure</title><content type="html">In &lt;a href="http://blog.bolinfest.com/2010/07/manuscript-submitted.html"&gt;drafting &lt;i&gt;Closure: The Definitive Guide&lt;/i&gt;&lt;/a&gt;, it became pretty obvious that it was not easy to get up and running with &lt;a href="http://code.google.com/closure/"&gt;Closure&lt;/a&gt;. Currently, using the full suite of Closure Tools requires checking out three projects from code.google.com via Subversion, and a combination of Python and Java to compile JavaScript code.&lt;br /&gt;&lt;br /&gt;Over the past month, I have been working on a build tool that integrates the Closure Compiler, Closure Library, and Closure Templates into a single download. It is named &lt;a href="http://plovr.com/"&gt;plovr&lt;/a&gt;, and can be &lt;a href="http://code.google.com/p/plovr/downloads/list"&gt;downloaded&lt;/a&gt; as a single jar file that can be run with the JRE.&lt;br /&gt;&lt;br /&gt;In creating plovr, I tried to address problems that frequently appeared in the &lt;a href="http://groups.google.com/group/closure-compiler-discuss"&gt;discussion&lt;/a&gt; &lt;a href="http://groups.google.com/group/closure-library-discuss"&gt;groups&lt;/a&gt;, such as:&lt;ul&gt;&lt;li&gt;&lt;a href="http://groups.google.com/group/closure-compiler-discuss/browse_thread/thread/e30fb46240095888/c887d3d191f09cce?lnk=gst&amp;q=%22no+plans%22#c887d3d191f09cce"&gt;having access to an open-source version of the Closure Compiler service&lt;/a&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://groups.google.com/group/closure-compiler-discuss/browse_thread/thread/992936a35b00c0c5/c148435bf8f5e78f?lnk=gst&amp;q=modules#c148435bf8f5e78f"&gt;help in using the Compiler to break a large JavaScript file into modules that can be dynamically loaded&lt;/a&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://groups.google.com/group/closure-compiler-discuss/browse_thread/thread/557a607ec5baa7c7/dd7806af09a14049?lnk=gst&amp;q=command+line#dd7806af09a14049"&gt;avoiding long lists of command line arguments&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;To this end, plovr can be run both as a server during development that dynamically recompiles Soy and JavaScript files when you reload a web application that depends on them. It can also be used as a command line build tool to generate compiled JavaScript. Because there are so many configurable options when using Closure, options are specified in a JSON config file rather than on the command line.&lt;br /&gt;&lt;br /&gt;Like many open-source projects, the &lt;a href="http://www.plovr.com/docs.html"&gt;documentation&lt;/a&gt; is still under development, though hopefully it is enough to get you started. I am also reluctant to commit to official documentation before getting feedback from more developers, so please download it and try it out! I have found plovr to be particularly effective in facilitating my Closure development, but I would like to &lt;a href="mailto:bolinfest@gmail.com"&gt;hear whether it is meeting your needs&lt;/a&gt; before I promote it from alpha to beta software.&lt;br /&gt;&lt;br /&gt;And if you are planning on entering the &lt;a href="http://js1k.com/home"&gt;contest to see what you can build with 1K of JavaScript&lt;/a&gt;, I &lt;strong&gt;strongly&lt;/strong&gt; urge you to use plovr to help you stay under the 1K limit. I am planning to release a new feature of plovr in the next week or two that will expose more features of the Compiler that will help you minify your code for the contest. Stay tuned!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-2643411369704716101?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/2643411369704716101/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2010/08/plovr-lowering-barrier-to-entry-for.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/2643411369704716101" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/2643411369704716101" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2010/08/plovr-lowering-barrier-to-entry-for.html" title="plovr: Lowering the Barrier to Entry for Closure" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2246259935483101149.post-4932999958089045340</id><published>2010-07-14T23:16:00.004-04:00</published><updated>2010-07-15T00:38:17.751-04:00</updated><title type="text">Manuscript Submitted!</title><content type="html">On Monday, I submitted my manuscript for &lt;i&gt;Closure: The Definitive Guide&lt;/i&gt; to O'Reilly for copyedit review. The past two months of revisions were extremely stressful, so I can't even begin to describe what a huge relief hitting that milestone has been for me. Honestly, I thought the draft was in reasonable shape when I finished it back in May, so I ended up doing a lot more work than I expected to get it into a state suitable for submitting for copyedit.&lt;br /&gt;&lt;br /&gt;Nevertheless, I am really excited with how much better the book has gotten in the past two months (it's probably one Amazon star better than it was before), though what is even more exciting is that I am already able to &lt;a href="http://my.safaribooksonline.com/9781449381882?portal=oreilly&amp;cid=orm-cat-readnow-9781449381882"&gt;share the latest version with you via O'Reilly's Rough Cuts program&lt;/a&gt;. What I submitted for copyedit on Monday was just made available on Rough Cuts today! (Apparently you can see the Table of Contents and read the Foreword and Preface without paying for the book.)&lt;br /&gt;&lt;br /&gt;Since the last update to Rough Cuts (mind you, that is when I thought I had a complete first draft), I have added an additional 91 pages for a whopping total of 546 pages. The following are some of the new sections that were added:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Detailed example of dividing compiled code into modules and loading them with the Closure Library (appears in Chapter 12).&lt;br /&gt;&lt;li&gt;Rewrite of the complex component example at the end of end of Chapter 8.&lt;br /&gt;&lt;li&gt;More hidden options in Chapter 14, which has been renamed to "Inside the Compiler."&lt;br /&gt;&lt;li&gt;An explanation of goog.events.KeyHandler in Chapter 6.&lt;br /&gt;&lt;li&gt;A section on goog.Uri in Chapter 7.&lt;br /&gt;&lt;li&gt;A section on mock objects in Chapter 15.&lt;br /&gt;&lt;li&gt;A new appendix on &lt;a href="http://plovr.org/"&gt;plovr&lt;/a&gt; (appears as Appendix C).&lt;br /&gt;&lt;li&gt;Acknowledgments in the Preface.&lt;br /&gt;&lt;/ul&gt;As part of the revising process, I (with the help of contributing author, Julie Parent) responded to all 100+ comments made on the Rough Cuts web site, which were a big help in improving the quality of the draft. Thank you so much to everyone who contributed so far! (Special thanks to &lt;a href="http://derekslager.com/blog/posts/2010/06/google-closure-introduction.ashx"&gt;Derek Slager&lt;/a&gt;, who filed the overwhelming majority of the corrections.) As the manuscript has not been finalized yet, you can still submit feedback now to help improve the final version of the book, so don't be shy!&lt;br /&gt;&lt;br /&gt;For the next month or so, the manuscript will bounce back and forth between O'Reilly and me. According to &lt;a href="http://www.amazon.com/Closure-Definitive-Guide-Michael-Bolin/dp/1449381871"&gt;Amazon&lt;/a&gt;, my book will be out on September 15, 2010, though I think that may be a little optimistic. Fortunately, the beauty of Rough Cuts is that you don't have to wait until the fall to get the help you need with Closure today!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2246259935483101149-4932999958089045340?l=blog.bolinfest.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bolinfest.com/feeds/4932999958089045340/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.bolinfest.com/2010/07/manuscript-submitted.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/4932999958089045340" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2246259935483101149/posts/default/4932999958089045340" /><link rel="alternate" type="text/html" href="http://blog.bolinfest.com/2010/07/manuscript-submitted.html" title="Manuscript Submitted!" /><author><name>Michael</name><uri>http://www.blogger.com/profile/14618340371367353616</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total></entry></feed>

