<?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">
  <title>benv.ca</title>
  <link rel="alternate" type="text/html" href="http://benv.ca" />
  
  <id>http://benv.ca/atom.xml</id>
  <updated>2013-01-17T10:27:13Z</updated>
  <subtitle>Ben Vinegar's weblog</subtitle>
 
  
  
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/benlog_org" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="benlog_org" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <title>You are probably misusing DOM text methods</title>
    <link rel="alternate" type="text/html" href="/2012/10/4/you-are-probably-misusing-DOM-text-methods/" />
    <id>tag:benlog.org:/2012/10/4/you-are-probably-misusing-DOM-text-methods</id>
    <updated>2012-10-04T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;&lt;a href="http://www.flickr.com/photos/nfsa/4580070895/" title="RCA Microphone by Brooke Shannon (CC)"&gt;&lt;img src="/images/rca-microphone-brooke-shannon.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a public service announcement.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;element.textContent&lt;/code&gt; and &lt;code&gt;document.createTextNode&lt;/code&gt; are dangerous &lt;span class="caps"&gt;DOM&lt;/span&gt; methods. They can easily lead to &lt;span class="caps"&gt;XSS&lt;/span&gt; vulnerabilities in your application.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;ll find out how by looking at two common &amp;#8211; but flawed &amp;#8211; applications of these methods: stripping and escaping &lt;span class="caps"&gt;HTML&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Stripping tags with element.textContent&lt;/h2&gt;
&lt;p&gt;What does element.textContent do? (element.innerText in old IE) From &lt;span class="caps"&gt;MDN&lt;/span&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gets or sets the text content of a node and its descendants.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here&amp;#8217;s an example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var div = document.createElement('div');
div.innerHTML = 'Hello &amp;lt;a href="http://bob.com"&amp;gt;Bob&amp;lt;/a&amp;gt;!';
console.log(div.textContent);
// Hello Bob!;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Many developers use this method to strip &lt;a href="http://stackoverflow.com/questions/822452/strip-html-from-text-javascript"&gt;&lt;span class="caps"&gt;HTML&lt;/span&gt; from a string&lt;/a&gt;, as I&amp;#8217;ve just done here.&lt;/p&gt;
&lt;p&gt;But what most people don&amp;#8217;t realize is that this will also convert &lt;span class="caps"&gt;HTML&lt;/span&gt; entities into regular unicode characters:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var div = document.createElement('div');
div.innerHTML = 
  'Hello &amp;lt;a&amp;gt;&amp;amp;lt;script&amp;amp;gt;alert(&amp;amp;quot;!&amp;amp;quot;)&amp;amp;lt;/script&amp;amp;gt;&amp;lt;/a&amp;gt;!';
console.log(div.textContent);
// Hello &amp;lt;script&amp;gt;alert("!")&amp;lt;/script&amp;gt;!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My purpose here was to strip the &amp;lt;a&amp;gt; tag from this string. But in the process, I created wholly new tags. Possibly malicious ones.&lt;/p&gt;
&lt;p&gt;If you were to ever use element.textContent to strip previously-escaped user content, and insert that seemingly-&lt;span class="caps"&gt;HTML&lt;/span&gt;-safe content somewhere else, you&amp;#8217;ve just introduced an &lt;span class="caps"&gt;XSS&lt;/span&gt; vulnerability (&lt;a href="http://jsfiddle.net/bdXCd/1/"&gt;example&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Now if you&amp;#8217;re smart, you probably recognize that this should be escaped a second time. But I suspect many people are unaware of this vulnerability, and probably didn&amp;#8217;t.  I&amp;#8217;ll be the first to admit it – I&amp;#8217;ve made this mistake before. I suspect you or members of your team have too.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a modified helper function from Stack Overflow which escapes the final &amp;#8220;stripped&amp;#8221; output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function strip(html) {
   var tmp = document.createElement("DIV");
   tmp.innerHTML = html;
   return escapeHtml(tmp.textContent||tmp.innerText);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that this function is not guaranteed to preserve the input. By placing the content in a &lt;span class="caps"&gt;DOM&lt;/span&gt; element – even temporarily – the browser will attempt to be helpful by &amp;#8220;fixing&amp;#8221; incomplete entities. If you want this to be perfect, you&amp;#8217;ll need a pure &lt;span class="caps"&gt;HTML&lt;/span&gt; parser.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve left the escapeHtml implementation blank for now, but you can imagine what it does: convert dangerous characters used in script injection into harmless &lt;span class="caps"&gt;HTML&lt;/span&gt; entities.&lt;/p&gt;
&lt;p&gt;But wait a minute. Did you know &lt;span class="caps"&gt;DOM&lt;/span&gt; text nodes can also be used to escape &lt;span class="caps"&gt;HTML&lt;/span&gt;? Let&amp;#8217;s find out more.&lt;/p&gt;
&lt;h2&gt;Escaping &lt;span class="caps"&gt;HTML&lt;/span&gt; with text nodes&lt;/h2&gt;
&lt;p&gt;We just looked at how accessing element.textContent returns the text content &amp;#8211; without tags &amp;#8211; of an element and its children.&lt;/p&gt;
&lt;p&gt;But what if we do the opposite, and assign a string – with tags – to element.textContent?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var div = document.createElement('div');
div.textContent = '&amp;lt;span&amp;gt;Foo &amp;amp; bar&amp;lt;/span&amp;gt;';
console.log(div.innerHTML)
// &amp;amp;lt;span&amp;amp;gt;Foo &amp;amp;amp; bar&amp;amp;lt;/span&amp;amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool! By assigning &lt;span class="caps"&gt;HTML&lt;/span&gt; to element.textContent, it escaped the &amp;lt; &amp;gt; and &amp;amp; characters.&lt;/p&gt;
&lt;p&gt;The same can be done by using document.createTextNode:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var div = document.createElement('div');
div.appendChild(document.createTextNode('&amp;lt;span&amp;gt;Foo &amp;amp; bar&amp;lt;/span&amp;gt;'));
console.log(div.innerHTML)
// &amp;amp;lt;span&amp;amp;gt;Foo &amp;amp;amp; bar&amp;amp;lt;/span&amp;amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It almost seems that the browser knows what characters are safe and which aren&amp;#8217;t. Which is why many developers have taken to using this technique to escape their user strings.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s an example implementation pulled from the web:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function escapeHtml(str) {
    var div = document.createElement('div');
    div.appendChild(document.createTextNode(str));
    return div.innerHTML;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here&amp;lsquo;s that function in action:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var username = '&amp;lt;img src="herp:/" onerror=alert("derp")&amp;gt;';
var profileLink = '&amp;lt;a href="/profile"&amp;gt;' + escapeHtml(username) + '&amp;lt;/a&amp;gt;';
var div = document.getElementById('target');
div.innerHTML = profileLink;
// &amp;lt;a href="/profile"&amp;gt;&amp;amp;lt;img src="herp:/" onerror=alert("derp")&amp;amp;gt;&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this situation, escapeHtml is effective. It prevents the malicious img tag from being inserted.&lt;/p&gt;
&lt;p&gt;But if you paid close attention, you&amp;#8217;ll notice that it didn&amp;#8217;t escape every character. Both double-quotes and single-quotes were left untouched. And not escaping these characters can lead to &lt;span class="caps"&gt;XSS&lt;/span&gt; vulnerabilities when assigning a value to an attribute:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var userWebsite = '" onmouseover="alert(\'derp\')" "';
var profileLink = '&amp;lt;a href="' + escapeHtml(userWebsite) + '"&amp;gt;Bob&amp;lt;/a&amp;gt;';
var div = document.getElementById('target');
div.innerHtml = profileLink;
// &amp;lt;a href="" onmouseover="alert('derp')" ""&amp;gt;Bob&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Congratulations – you&amp;lsquo;re fucked.&lt;/p&gt;
&lt;p&gt;I took that implementation from an article called &lt;a href="http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript"&gt;Foolproof &lt;span class="caps"&gt;HTML&lt;/span&gt; escaping in Javascript&lt;/a&gt;. In that article, the author refers to this technique is referred to as “the best way to escape &lt;span class="caps"&gt;HTML&lt;/span&gt; in JavaScript&amp;quot;. Unfortunately, it&amp;#8217;s probably the worst.&lt;/p&gt;
&lt;p&gt;He&amp;lsquo;s not alone, of course. Here&amp;lsquo;s a &lt;a href="http://stackoverflow.com/a/1090465"&gt;bunch&lt;/a&gt; &lt;a href="http://stackoverflow.com/a/5251551"&gt;of&lt;/a&gt; &lt;a href="http://www.timvasil.com/blog14/post/2008/12/28/HTML-escape-and-HTML-unescape-JavaScript.aspx"&gt;articles&lt;/a&gt; and Stack Overflow questions in which this technique is suggested, all by different authors.&lt;/p&gt;
&lt;p&gt;Choice quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are using createTextNode, you are safe.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use jQuery? The same vulnerable technique is also possible using .text(), and is endorsed in &lt;a href="http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery"&gt;these&lt;/a&gt; &lt;a href="http://debuggable.com/posts/encode-html-entities-with-jquery:480f4dd6-13cc-4ce9-8071-4710cbdd56cb"&gt;jQuery&lt;/a&gt; &lt;a href="http://www.naveen.com.au/javascript/jquery/encode-or-decode-html-entities-with-jquery/289"&gt;solutions&lt;/a&gt;. A few Stack Overflow users mention the vulnerability, but their comments are buried.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s even &lt;a href="http://jsperf.com/encode-html-entities"&gt;several&lt;/a&gt; &lt;a href="http://jsperf.com/escape-html-special-chars/2"&gt;JSPerf&lt;/a&gt; &lt;a href="http://jsperf.com/jojo-escape-html/2"&gt;entry&lt;/a&gt; that compares this same technique against other more tried-and-true methods of escaping &lt;span class="caps"&gt;HTML&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Now – there&amp;#8217;s a counter argument here. Which is, if you know the context in which you are injecting the value (i.e. not attributes), then this method is safe. My argument is that developers don&amp;#8217;t understand those contexts well enough, and sooner or later they will get it wrong.&lt;/p&gt;
&lt;p&gt;If you want to escape &lt;span class="caps"&gt;HTML&lt;/span&gt;, use the tried and true method: string replacement. It&amp;#8217;s boring, but it works (provided &lt;a href="http://wonko.com/post/html-escaping"&gt;you always use quoted attributes and specify charsets&lt;/a&gt;, that is).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function escapeHtml(str) {
    return String(str)
        .replace(/&amp;amp;/g, "&amp;amp;amp;")
        .replace(/&amp;lt;/g, "&amp;amp;lt;")
        .replace(/&amp;gt;/g, "&amp;amp;gt;")
        .replace(/"/g, "&amp;amp;quot;")
        .replace(/'/g, "&amp;amp;#039;")
        .replace(/\//g, "&amp;amp;#x2F;")
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a simple implementation. There are &lt;a href="https://github.com/janl/mustache.js/blob/master/mustache.js#L55"&gt;better ones&lt;/a&gt; out there.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;NOTE&lt;/span&gt;: This also escapes forward slashes, which is &lt;a href="https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content"&gt;recommended practice by &lt;span class="caps"&gt;OWASP&lt;/span&gt;&lt;/a&gt;. My understanding is that it is to handle cases where you are embedding user values inside a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;h2&gt;Fight the good fight&lt;/h2&gt;
&lt;p&gt;In summary, &lt;span class="caps"&gt;HTML&lt;/span&gt; is scary, and &lt;span class="caps"&gt;DOM&lt;/span&gt; text methods present some very easy ways to get bitten. Worst of all, there is plenty of literature on the web which endorses their use in potentially vulnerable situations.&lt;/p&gt;
&lt;p&gt;So if you&amp;#8217;ve made it this far, I&amp;#8217;ve got two things to ask of you.&lt;/p&gt;
&lt;p&gt;First: it probably goes without saying, but audit your codebase to see if you&amp;#8217;re messing around with element.textContent or document.createTextNode. They might be indicative of an &lt;span class="caps"&gt;XSS&lt;/span&gt; vulnerability. It can&amp;#8217;t hurt to read &lt;a href="https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content"&gt;OWASP&amp;#8217;s &lt;span class="caps"&gt;XSS&lt;/span&gt; cheat sheet either&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Second: if you come across these anti-patterns – on Twitter, on Stack Overflow, or on the web in general – please do your part and shout/vote them down. You&amp;#8217;ll be doing the web a favour.&lt;/p&gt;
&lt;p&gt;Good night, and good luck.&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=aUZA0Q9Bjdw:VJw2VEWLZoo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=aUZA0Q9Bjdw:VJw2VEWLZoo:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=aUZA0Q9Bjdw:VJw2VEWLZoo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=aUZA0Q9Bjdw:VJw2VEWLZoo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/yW8cpMKbmtI" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>Subdomain tunneling with jQuery and document.domain</title>
    <link rel="alternate" type="text/html" href="/2011/3/7/subdomain-tunneling-with-jquery-and-document-domain/" />
    <id>tag:benlog.org:/2011/3/7/subdomain-tunneling-with-jquery-and-document-domain</id>
    <updated>2011-03-07T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;The problem: you&amp;#8217;re serving part of your web app on a subdomain (sub.example.com), from which you want to make &lt;span class="caps"&gt;AJAX&lt;/span&gt; requests to your app&amp;#8217;s base domain (example.com). You can&amp;#8217;t just make &lt;span class="caps"&gt;AJAX&lt;/span&gt; requests between those domains, because of the browser&amp;#8217;s same-origin policy.&lt;/p&gt;
&lt;p&gt;Now, you can solve this by converting all your endpoints to support &lt;a href="http://stackoverflow.com/questions/2067472/please-explain-jsonp/2067584#2067584"&gt;&lt;span class="caps"&gt;JSONP&lt;/span&gt;&lt;/a&gt;, or use a cross-domain messaging library like &lt;a href="https://github.com/oyvindkinsey/easyXDM"&gt;easyXDM&lt;/a&gt;. Definitely two solid options.&lt;/p&gt;
&lt;p&gt;But! There&amp;#8217;s an even simpler solution: host a bare-bones &lt;span class="caps"&gt;HTML&lt;/span&gt; file on your base domain that includes jQuery, and serve the page via an iframe from your subdomain. If you set &lt;a href="https://developer.mozilla.org/en/document.domain"&gt;document.domain&lt;/a&gt; equal to &amp;#8216;example.com&amp;#8217; on both pages, your subdomain page can reach inside the iframe, grab the base-domain-friendly jQuery object, and make &lt;span class="caps"&gt;AJAX&lt;/span&gt; requests at your leisure.&lt;/p&gt;
&lt;h3&gt;Not anything new&lt;/h3&gt;
&lt;p&gt;Nope, not new. But, I thought you might find it neat that you can wholesale replace the subdomain&amp;#8217;s instance of $.ajax with the base-domain-friendly copy from the tunnel. This means your subdomain&amp;#8217;s JS can be entirely ignorant of the cross-domain wackiness happening in the background.&lt;/p&gt;
&lt;script src="https://gist.github.com/859940.js"&gt; &lt;/script&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=Vx4MlvGjdfw:laygVMxa7AU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=Vx4MlvGjdfw:laygVMxa7AU:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=Vx4MlvGjdfw:laygVMxa7AU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=Vx4MlvGjdfw:laygVMxa7AU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/of4R_m8nqEc" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>What it's like to live in San Francisco</title>
    <link rel="alternate" type="text/html" href="/2011/1/31/what-its-like-to-live-in-san-francisco/" />
    <id>tag:benlog.org:/2011/1/31/what-its-like-to-live-in-san-francisco</id>
    <updated>2011-01-31T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;&lt;em&gt;Background: In Spring of last year, I moved to San Francisco from Toronto to join &lt;a href="http://disqus.com"&gt;Disqus&lt;/a&gt;, a commenting startup. &lt;a href="/2010/4/8/new-morning-changing-weather/"&gt;More here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/fritography/5162434063/"&gt;&lt;img src="/images/2011-01-31-san-francisco.jpg"/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Living in San Francisco, I often meet up with Toronto startup folks who are in town on business. Inevitably the conversation goes from industry news, to what&amp;#8217;s going on back home (&amp;#8220;&lt;a href="http://en.wikipedia.org/wiki/Rob_Ford"&gt;Rob Ford&lt;/a&gt; is ruining everything!&amp;#8221;), to settling on &amp;#8220;How do you like living here?&amp;#8221; I get the impression startup people in Toronto are genuinely curious: is San Francisco all it&amp;#8217;s made out to be?&lt;/p&gt;
&lt;p&gt;After 9 months here, I feel like I can comfortably generate an opinion on the matter. Short answer: it&amp;#8217;s a great place, and I&amp;#8217;ve definitely enjoyed my stay here &amp;#8212; but like any city, it&amp;#8217;s not without its faults.&lt;/p&gt;
&lt;p&gt;For the long answer, please consult this jumble of thoughts I&amp;#8217;ve put together:&lt;/p&gt;
&lt;h3&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;San Francisco attracts people from all over the US &amp;#8212; and the world. I feel like Disqus and other SF startups host a pretty diverse-crowd of non-locals; even our small roster of 15 people includes folks from roughly 9 US states and 4 countries. You won&amp;#8217;t feel like an outsider, because everybody is.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;The meetup scene here feels unparalleled. On any given week, I can go to a &lt;a href="http://www.meetup.com/sfruby/"&gt;Ruby meetup&lt;/a&gt;, a &lt;a href="https://github.com/blog/759-github-meetup-sf-23"&gt;GitHub meetup&lt;/a&gt;, our own &lt;a href="http://www.meetup.com/San-Francisco-JavaScript-Pub-Night/"&gt;JS Pub Nite&lt;/a&gt;, and countless others. They&amp;#8217;re well attended, and the crowd is pretty incredible. It&amp;#8217;s nice to rub elbows with employees from all your favourite Bay Area start-ups and companies.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;There are no shortage of visitors. San Francisco hosts so many conferences, business meetings, and just straight-up tourists, that it&amp;#8217;s common for me to meet up with friends and acquaintances 2-3 times a month.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;San Francisco has a terrific food scene. It&amp;#8217;s a little more on the expensive side, and I miss my cheap Toronto Chinatown eats, but otherwise I can&amp;#8217;t complain. The city&amp;#8217;s Mission district hosts some of the best Latin-American food I&amp;#8217;ve ever had. And if you&amp;#8217;re a real foodie, &amp;#8220;neighbouring&amp;#8221; Napa Valley also hosts some incredible Michelin-starred restaurants.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;The weather is always mild. Right now it&amp;#8217;s late January, and the temperature&amp;#8217;s a comfortable 15 degrees. And it will be like that all year, basically. Some people tell me they find the lack of season change boring, but after 20+ years of snowy winters, I can make that sacrifice.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Wash&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;Industry talk dominates the city. You&amp;#8217;ll hear it on public transit, inside cafes, and just walking on the street. It&amp;#8217;s a surprising change coming from a finance-dominated city, but can sometimes feel tiresome. For me, at least.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;The transit&amp;#8217;s okay. Subway coverage is very light, which isn&amp;#8217;t surprising for a city with a history of earthquakes, so you&amp;#8217;re largely dependent on buses and streetcars. On the plus side, all the transit authorities are well connected, and getting up-to-the-minute schedule information via your phone is easy.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;The rent. I&amp;#8217;m not a big fan of commuting, so we&amp;#8217;ve chosen to live in San Francisco&amp;#8217;s &lt;span class="caps"&gt;SOMA&lt;/span&gt; district, which is home to a huge collection of start-ups and the Disqus offices. But, you sure do pay for it: 1 bedroom apartments here start at $2100/month. And there&amp;#8217;s no rent control either; landlords are free to arbitrarily raise your rent after your lease is up.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;The city itself is reasonably safe, but there are areas that are really uncomfortable to walk through. Toronto has its share of &lt;a href="http://en.wikipedia.org/wiki/Jane_and_finch"&gt;rough&lt;/a&gt; &lt;a href="http://en.wikipedia.org/wiki/Regent_Park"&gt;neighborhoods&lt;/a&gt;, but the upside is that you&amp;#8217;d never want to go there &amp;#8212; whereas in San Francisco, the &lt;a href="http://en.wikipedia.org/wiki/Tenderloin,_San_Francisco#Crime"&gt;Tenderloin&lt;/a&gt; and Mission districts are in the middle of the city and host some great restaurants and bars.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Cabs are cheap, but also scarce; a confusing economic situation. In Toronto, you can&amp;#8217;t walk a block at night without being hounded by a cab or three. Here you&amp;#8217;ve got to get lucky or wait 20-30 minutes. I&amp;#8217;d rather pay the extra $3-4 I&amp;#8217;m used to than sit on a creepy street corner alone at 2 AM.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;There is a terrible homeless problem in San Francisco. I recognize it&amp;#8217;s common of many West Coast cities, but I&amp;#8217;ve been to Vancouver and Seattle, and my anecdotal experience says that San Francisco has it worse.&lt;/li&gt;
&lt;/ul&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=wwhmBvIybAI:Liy4vmEq0wk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=wwhmBvIybAI:Liy4vmEq0wk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=wwhmBvIybAI:Liy4vmEq0wk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=wwhmBvIybAI:Liy4vmEq0wk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/of2V7J4tXtg" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>Set JavaScript breakpoints programatically</title>
    <link rel="alternate" type="text/html" href="/2010/8/8/set-javascript-breakpoints-programatically/" />
    <id>tag:benlog.org:/2010/8/8/set-javascript-breakpoints-programatically</id>
    <updated>2010-08-08T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;When I&amp;#8217;m debugging JavaScript, I&amp;#8217;ve sometimes commented on how quicker things could be if I could set breakpoints programatically from the console, instead of fishing around in FireBug&amp;#8217;s slow script browser.&lt;/p&gt;
&lt;p&gt;Turns out it&amp;#8217;s not that difficult to do. Here&amp;#8217;s &lt;a href="http://gist.github.com/514679"&gt;the code&lt;/a&gt;:&lt;/p&gt;
&lt;script src="http://gist.github.com/514679.js"&gt; &lt;/script&gt;&lt;p&gt;Appreciate any feedback, thoughts.&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=j-YI3oxE3D0:ihSYmiwee5g:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=j-YI3oxE3D0:ihSYmiwee5g:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=j-YI3oxE3D0:ihSYmiwee5g:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=j-YI3oxE3D0:ihSYmiwee5g:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/GBnv-Dp_1uM" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>Belated project announcements: Malt, CouchDB-xd</title>
    <link rel="alternate" type="text/html" href="/2010/7/18/belated-project-announcements/" />
    <id>tag:benlog.org:/2010/7/18/belated-project-announcements</id>
    <updated>2010-07-18T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;h3&gt;Malt.js&lt;/h3&gt;
&lt;p&gt;Last year I released &lt;a href="http://github.com/benvinegar/malt"&gt;Malt.js&lt;/a&gt;, an asynchronous JavaScript dependency manager. It&amp;#8217;s basically a packaging system that lets you declare modules and include them on-demand.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s an example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Malt.module('facebox', 'js/facebox.js', 'css/facebox.css', function() {
  // initialize facebox
});

Malt.require('facebox', function() {
  // execute after all resources loaded, initialized
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I think the syntax is particularly nice and minimal, but, admittedly, I haven&amp;#8217;t used this library on any production apps. I came to conclude that it&amp;#8217;s more beneficial to front-load cached scripts before a page renders than to load them as-you-go &amp;#8212; at least for the kind of web applications I&amp;#8217;ve been building. It would probably fit in better with a single-page application (i.e. GMail); if that&amp;#8217;s your game, I encourage you to take a look.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Aside&lt;/strong&gt;: If you&amp;#8217;re just interested in loading scripts asynchronously, I recommend looking at &lt;a href="http://labjs.com/"&gt;LABjs&lt;/a&gt;. It&amp;#8217;s a project that has basically figured out the optimal way to load scripts in parallel, adjusting for every browser.&lt;/p&gt;
&lt;h3&gt;CouchDB-xd&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://github.com/benvinegar/couchdb-xd"&gt;CouchDB-xd&lt;/a&gt; is a cross-domain &lt;span class="caps"&gt;AJAX&lt;/span&gt; library for &lt;a href="http://couchdb.apache.org/"&gt;CouchDB&lt;/a&gt; that provides a standard &lt;span class="caps"&gt;CRUD&lt;/span&gt; interface for Couch&amp;#8217;s &lt;a href="http://wiki.apache.org/couchdb/HTTP_database_API"&gt;database&lt;/a&gt; and &lt;a href="http://wiki.apache.org/couchdb/HTTP_Document_API"&gt;document APIs&lt;/a&gt;. It basically gives you the power to read and write to Couch databases from JavaScript, anywhere on the web. I think it makes for some interesting application possibilities, made easier with &lt;a href="http://couch.io"&gt;Couch.io&lt;/a&gt; and other &amp;#8220;cloud&amp;#8221; Couch hosts around the corner.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Aside&lt;/strong&gt;: CouchDB already exposes a &lt;span class="caps"&gt;JSONP&lt;/span&gt; interface for its records (which is largely undocumented), but unfortunately, you&amp;#8217;re limited to &lt;span class="caps"&gt;GET&lt;/span&gt; queries, which makes modifying records a no-go.&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=0OC5iMtKxuc:I5DZ34WnR7Y:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=0OC5iMtKxuc:I5DZ34WnR7Y:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=0OC5iMtKxuc:I5DZ34WnR7Y:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=0OC5iMtKxuc:I5DZ34WnR7Y:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/Urq3BprrMeQ" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>How do you sync your dotfiles?</title>
    <link rel="alternate" type="text/html" href="/2010/7/6/how-do-you-sync-your-dot-files/" />
    <id>tag:benlog.org:/2010/7/6/how-do-you-sync-your-dot-files</id>
    <updated>2010-07-06T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;
  &lt;img src="http://a1.twimg.com/profile_images/892306562/Disqus_avatar_2_bigger.png" class="twitter-avatar"&gt;Idea: symlink and sync bashrc/profile scripts via Dropbox. Anyone doing this? &amp;mdash; &lt;a href="http://twitter.com/bentlegen"&gt;@bentlegen&lt;/a&gt;&lt;br style="clear:both"/&gt;
&lt;p&gt;
  &lt;img src="http://a3.twimg.com/profile_images/95658349/Picture_4_bigger.png" class="twitter-avatar"/&gt;@bentlegen I have a private Github repository for my .* config files (gitconfig, bash_profile, etc.) &amp;mdash; &lt;a href="http://twitter.com/bleikamp"&gt;@bleikamp&lt;/a&gt;&lt;br style="clear:both"/&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;img src="http://a3.twimg.com/profile_images/359099095/mangatar_bigger.png" class="twitter-avatar"&gt;@bentlegen I use svn to manage my rc scripts, since I find I need them places where I can't install dropbox. &amp;mdash; &lt;a href="http://twitter.com/mendel"&gt;@mendel&lt;/a&gt;&lt;br style="clear:both"/&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;img src="http://a1.twimg.com/profile_images/298165582/5456_97339709692_502719692_1875133_3880589_n_bigger.jpg" class="twitter-avatar"&gt;@bentlegen No I just use homesick &lt;a href="http://github.com/technicalpickles/homesick"&gt;http://github.com/technicalpickles/homesick&lt;/a&gt; &amp;mdash; &lt;a href="http://twitter.com/j_dan_williams"&gt;@j_dan_williams&lt;/a&gt;&lt;br style="clear:both"/&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;img src="http://a1.twimg.com/profile_images/648468922/brett_bigger.jpg" class="twitter-avatar"&gt;@bentlegen I keep all dotfiles in git because I like history and submodules. &amp;mdash; &lt;a href="http://twitter.com/bretthoerner"&gt;@bretthoerner&lt;/a&gt;&lt;br style="clear:both"/&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;img src="http://a1.twimg.com/profile_images/813392856/rtsavestransitcity__1__bigger.jpg" class="twitter-avatar"&gt;@bentlegen shouldn't be too hard, and you can sync app preferences too: &lt;a href="http://bit.ly/bePSt7"&gt;http://bit.ly/bePSt7&lt;/a&gt; I just do the github+dotfiles thing, though.  &amp;mdash; &lt;a href="http://twitter.com/rtlechow"&gt; @rtlechow&lt;/a&gt;&lt;br style="clear:both"/&gt;
&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=_bbdGSTF9s8:VISBhWnKxo8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=_bbdGSTF9s8:VISBhWnKxo8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=_bbdGSTF9s8:VISBhWnKxo8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=_bbdGSTF9s8:VISBhWnKxo8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/-F_N1oTyflc" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>A new morning, changing weather</title>
    <link rel="alternate" type="text/html" href="/2010/4/8/new-morning-changing-weather/" />
    <id>tag:benlog.org:/2010/4/8/new-morning-changing-weather</id>
    <updated>2010-04-08T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;This past week marked the end of a huge, important chapter in my life. After 3 fantastic years working at &lt;a href="http://www.freshbooks.com"&gt;FreshBooks&lt;/a&gt;, I find myself moving on. Not just to a new employer – but to a whole new city, new country, and in some ways, a whole new life.&lt;/p&gt;
&lt;p&gt;The story goes something like this. While wrapping up her PhD, my long-time partner was extended an amazing opportunity to work full-time as a researcher at Stanford University. We’d often discussed how fantastic it would be to live in another city for a few years, so it seemed like a perfect situation: a world-class university located right in the Bay Area and Silicon Valley. Needless to say, she took the gig, and I followed suit, which leads to my next announcement.&lt;/p&gt;
&lt;p&gt;Starting this Monday, I’ll be the newest team member at &lt;a href="http://www.disqus.com"&gt;Disqus&lt;/a&gt;, the web’s favourite commenting platform. I’m pretty excited: it’s a product I use personally and enjoy, it’s backed by a terrific team and world-class investors, and the technology has some pretty demanding JavaScript requirements that I’m eagerly looking forward to contributing to.&lt;/p&gt;
&lt;p&gt;So, wow! Pretty crazy stuff. On one level, I’m certainly sad to leave Toronto, and all the friends and acquaintances I’ve made over the years. More so than I thought I would be, given the furious past few weeks of well wishing and goodbyes. But on the other hand, I’m really amped to touch down in San Francisco, and take up the adventure ahead. Wish me luck!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Daniel at Disqus writes about my &lt;a href="http://blog.disqus.com/post/515233996/ben-vinegar"&gt;joining the team&lt;/a&gt; on the official blog.&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=k1EFAP5jVmc:9TqZfrGRaSc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=k1EFAP5jVmc:9TqZfrGRaSc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=k1EFAP5jVmc:9TqZfrGRaSc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=k1EFAP5jVmc:9TqZfrGRaSc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/PKDIV6tOGTk" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>Five tips for better JavaScript applications</title>
    <link rel="alternate" type="text/html" href="/2010/1/24/5-tips-for-better-javascript-applications/" />
    <id>tag:benlog.org:/2010/1/24/5-tips-for-better-javascript-applications</id>
    <updated>2010-01-24T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;Last Fall I gave an Ignite presentation at &lt;a href="http://refresh-events.ca/"&gt;Refresh Events&lt;/a&gt; on &amp;#8220;5 Tips for Better JavaScript Applications&amp;#8221;. If you&amp;#8217;re not familiar with the format, an &lt;a href="http://en.wikipedia.org/wiki/Ignite_%28event%29"&gt;Ignite presentation&lt;/a&gt; is 5 minutes, 20 slides, with 15 seconds per slide. That&amp;#8217;s not much time, but I thought it would be fun to try to present complex technical material under such tight constraints.&lt;/p&gt;
&lt;p&gt;Long story short, it took some good &amp;#8216;old fashioned fast talking, but I like to think the presentation was a success. The slides are located &lt;a href="http://www.slideshare.net/RefreshEvents/ben-vinegar-5-tips-for-better-javascript-applications"&gt;on Slideshare&lt;/a&gt;, but viewing them on their own without my stumbling narration might not do you much good. So I thought I&amp;#8217;d summarize them again here:&lt;/p&gt;
&lt;h3&gt;1. Embeddable JavaScript Templates&lt;/h3&gt;
&lt;p&gt;I really dislike mangling together strings in JavaScript to produce markup, so I&amp;#8217;ve taken to using templating libraries instead. Think erb, Liquid – but in JavaScript. My favourite solution so far is &lt;a href="http://json-template.googlecode.com/svn/trunk/doc/Introducing-JSON-Template.html"&gt;&lt;span class="caps"&gt;JSON&lt;/span&gt; Template&lt;/a&gt;, which has the upside of being cross-platform, but there are plenty of other choices: &lt;a href="http://beebole.com/pure/documentation/get-started/"&gt;Pure&lt;/a&gt;, &lt;a href="http://embeddedjs.com/"&gt;&lt;span class="caps"&gt;EJS&lt;/span&gt;&lt;/a&gt;, and &lt;a href="http://jtemplates.tpython.com/"&gt;jTemplates&lt;/a&gt; are just a few worth looking at.&lt;/p&gt;
&lt;p&gt;One problem with JavaScript templates is choosing where to store them. Storing templates as JavaScript strings isn&amp;#8217;t really any better, and individual files (fetched each time) seems overkill. My trick is to embed templates directly into &lt;span class="caps"&gt;HTML&lt;/span&gt; by hiding them inside a &amp;lt;script type=&amp;quot;text/html&amp;quot;/&amp;gt; tag. These tags are ignored by the browser (since text/html is an unsupported script type), so they can live happily in the page until you need to render them. Credit to John Resig who mentioned &lt;a href="http://ejohn.org/blog/javascript-micro-templating/"&gt;this technique&lt;/a&gt; on his blog.&lt;/p&gt;
&lt;h3&gt;2. Classical Inheritance Libraries&lt;/h3&gt;
&lt;p&gt;JavaScript is a &lt;a href="http://en.wikipedia.org/wiki/Prototype-based_programming"&gt;prototype-based object-oriented language&lt;/a&gt;, but with some clever manipulation, it can also be made to support classical inheritance as well (think Java, Ruby, etc.). I like using classical inheritance in my apps because it&amp;#8217;s a well-understood pattern that any of my server-side-inclined colleagues can follow, quickly.&lt;/p&gt;
&lt;p&gt;There are a handful of libraries that will help you get started. I&amp;#8217;ve used both &lt;a href="http://dean.edwards.name/weblog/2006/03/base/"&gt;Dean Edwards&lt;/a&gt; and &lt;a href="http://ejohn.org/blog/simple-javascript-inheritance/"&gt;John Resig&amp;#8217;s&lt;/a&gt; solutions, but &lt;a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/docs/closure_goog_base.js.html"&gt;Google&amp;#8217;s implementation&lt;/a&gt;, as part of the Closure library, is worth a look too.&lt;/p&gt;
&lt;h3&gt;3. The Debugger Keyword&lt;/h3&gt;
&lt;p&gt;Did you know you that the &amp;#8220;debugger&amp;#8221; keyword will automatically set a breakpoint in any active JavaScript debugger in your browser? I find it&amp;#8217;s a real time saver; instead of hunting down code in your debugger, you can set breakpoints from your editor. It&amp;#8217;s even more helpful when you&amp;#8217;re debugging across multiple environments; all the major browser support it.&lt;/p&gt;
&lt;p&gt;I threw this into my original presentation as a one-slide means of saving time, but it&amp;#8217;s still a great tip, and to my surprise, not exactly common knowledge.&lt;/p&gt;
&lt;h3&gt;4. Custom Events&lt;/h3&gt;
&lt;p&gt;You can &lt;a href="http://dailyjs.com/2009/11/14/jquery-custom-events/"&gt;trigger your own events&lt;/a&gt; in JavaScript, and bind to them as well, just like you can regular UI events (i.e. onclick). It&amp;#8217;s a helpful tool in reducing coupling. Instead of calling a function directly, you trigger an event, and then other parts of your application can bind to that event and act accordingly.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re a plugin developer, this is an ideal way to expose hooks to your plugin. Instead of having to inherit some base class and override an existing method, try triggering events people can bind to instead.&lt;/p&gt;
&lt;h3&gt;5. Preserving the Browser&amp;#8217;s Back Button&lt;/h3&gt;
&lt;p&gt;Preserving the back button is easier than it looks. The jist of it is this: give your various application states a unique &lt;span class="caps"&gt;URL&lt;/span&gt; prefixed with the pound sign (#), then implement a &amp;#8220;router&amp;#8221; that can restore to these states when the page is loaded or the &lt;span class="caps"&gt;URL&lt;/span&gt; changes. That last part where it gets tricky &amp;#8211; and where a plugin like &lt;a href="http://www.mikage.to/jquery/jquery_history.html"&gt;jQuery history&lt;/a&gt; will help you out.&lt;/p&gt;
&lt;p&gt;Other browser history libraries worth checking out: &lt;a href="http://code.google.com/p/reallysimplehistory/"&gt;ReallySimpleHistory&lt;/a&gt; and &lt;a href="http://www.asual.com/jquery/address/"&gt;jQuery Address&lt;/a&gt;.&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=x-U-TGpePVI:xsDh4rPuxgE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=x-U-TGpePVI:xsDh4rPuxgE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=x-U-TGpePVI:xsDh4rPuxgE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=x-U-TGpePVI:xsDh4rPuxgE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/vq4HBpX55Ww" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>merb_has_json_flash: JavaScript accessible Flash for Merb</title>
    <link rel="alternate" type="text/html" href="/2009/10/12/merb-has-json-flash/" />
    <id>tag:benlog.org:/2009/10/12/merb-has-json-flash</id>
    <updated>2009-10-12T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;Several months ago, I began using Michael Ivey&amp;#8217;s &lt;a href="http://github.com/ivey/merb_has_flash"&gt;merb_has_flash&lt;/a&gt; gem on &lt;a href="http://www.guestlistapp.com"&gt;Guestlist&lt;/a&gt;. The gem, which provides Rails-like &lt;a href="http://api.rubyonrails.org/classes/ActionController/Flash.html"&gt;flash&lt;/a&gt; accessors to &lt;a href="http://www.merbivore.com"&gt;Merb&lt;/a&gt;, works as advertised, until I wanted to manipulate the flash contents via JavaScript. The use case: setting a success message and then reloading the page, all client-side. It was difficult because, although the Gem stores the flash contents in the user&amp;#8217;s cookies, it&amp;#8217;s stored as a Ruby serialized hash that seems unreadable via JavaScript.&lt;/p&gt;
&lt;p&gt;The solution? A fork called &lt;a href="http://github.com/bentlegen/merb_has_json_flash"&gt;merb_has_json_flash&lt;/a&gt;, which instead of serializing the flash in Ruby, does so using &lt;span class="caps"&gt;JSON&lt;/span&gt;. Now you can set the success message in Ruby or JavaScript, and the behaviour is identical. The only downside is that the flash store is no longer obfuscated, so it&amp;#8217;s user-readable &amp;#8211; not a big deal.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s an example, using the &lt;a href="http://plugins.jquery.com/project/cookie"&gt;jQuery cookie plugin&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type="text/javascript"&amp;gt;
  $.cookie('flash', JSON.stringify({
      notice: 'Ticket successfully saved.'
    }), { path: '/' }
  );
  window.location.reload();
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anyways, that&amp;#8217;s it. I hope someone else finds this useful &amp;#8211; although it seems like Merb users are far and few between these days.&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=IeU7ec6H1_M:S6_iS7IRN6M:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=IeU7ec6H1_M:S6_iS7IRN6M:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=IeU7ec6H1_M:S6_iS7IRN6M:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=IeU7ec6H1_M:S6_iS7IRN6M:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/9rQxWyvs-K4" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>Blog now powered by Jekyll</title>
    <link rel="alternate" type="text/html" href="/2009/10/8/blog-now-powered-by-jekyll/" />
    <id>tag:benlog.org:/2009/10/8/blog-now-powered-by-jekyll</id>
    <updated>2009-10-08T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;Huzzah. I&amp;#8217;ve successfully migrated this blog from an ancient version of &lt;a href="http://mephistoblog.com/download"&gt;Mephisto&lt;/a&gt; to &lt;a href="http://wiki.github.com/mojombo/jekyll"&gt;Jekyll&lt;/a&gt;, a static site generator written in Ruby. Since it&amp;#8217;s statically generated, I don&amp;#8217;t need a fancy hosting platform anymore, so I&amp;#8217;ve cancelled my $20/mo &lt;a href="http://www.slicehost.com"&gt;Slicehost&lt;/a&gt; account and begun hosting the content out of a cheap &lt;a href="http://www.dreamhost.com"&gt;Dreamhost&lt;/a&gt; account. Yeah, that Dreamhost.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re thinking of making the move yourself, I&amp;#8217;ve got a couple tips. First off, New Bamboo has an article that goes over &lt;a href="http://blog.new-bamboo.co.uk/2009/2/20/migrating-from-mephisto-to-jekyll"&gt;the migration process&lt;/a&gt;. After that, the biggest pain is preserving inbound links because Mephisto and Jekyll generate their pretty URLs differently. I followed New Bamboo&amp;#8217;s lead and forked &lt;a href="http://github.com/bentlegen/jekyll"&gt;my own version&lt;/a&gt; of Jekyll &lt;a href="http://github.com/bentlegen/jekyll/commit/6a8e3a2c4a69a1a6146dad0367d04fc6f5ad2584"&gt;that patches this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As for the deploy process, I&amp;#8217;ve got it down pretty simple: I build the entire site locally, then &lt;a href="http://henrik.nyh.se/2009/04/jekyll"&gt;rsync the result&lt;/a&gt; to Dreamhost. I&amp;#8217;ve read about some &lt;a href="http://tatey.com/2009/04/29/jekyll-meets-dreamhost-automated-deployment-for-jekyll-with-git.html"&gt;complicated setups&lt;/a&gt; that use GitHub post-commit hooks to have your server check out the latest version and build it in place, but I think rsync does the trick just fine. It also means my Dreamhost account doesn&amp;#8217;t need Ruby, the latest gems, nothing &amp;#8211; just a dumb folder system where I can dump static content. So far, pretty happy about it.&lt;/p&gt;
&lt;p&gt;Now, just to do something about this design &amp;#8230;&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=itwOej6i4bI:ABSw6rRU5Bs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=itwOej6i4bI:ABSw6rRU5Bs:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=itwOej6i4bI:ABSw6rRU5Bs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=itwOej6i4bI:ABSw6rRU5Bs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/i88dnThe85w" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>FreshBooks.rb now maintained by Ben Curren</title>
    <link rel="alternate" type="text/html" href="/2009/4/4/freshbooks-rb-now-maintained-by-ben-curren/" />
    <id>tag:benlog.org:/2009/4/4/freshbooks-rb-now-maintained-by-ben-curren</id>
    <updated>2009-04-04T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;I&amp;#8217;ve been late to announce this, but Ben Curren (of &lt;a href="http://outright.com"&gt;Outright&lt;/a&gt;) has been maintaining the &lt;a href="http://github.com/bcurren/freshbooks.rb/tree/master"&gt;canonical copy of FreshBooks.rb&lt;/a&gt; for a few months now. He&amp;#8217;s improved the library ten fold, so if you&amp;#8217;re a FreshBooks/Ruby integrator, I encourage you to take a look.&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=yymbNR1GYk0:oUp9Io8iLgw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=yymbNR1GYk0:oUp9Io8iLgw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=yymbNR1GYk0:oUp9Io8iLgw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=yymbNR1GYk0:oUp9Io8iLgw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/SvE0CHliojA" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>Revised date, datetime helpers for Merb 1.x</title>
    <link rel="alternate" type="text/html" href="/2009/1/31/revised-date-and-datetime-form-helpers-for-merb-1-x/" />
    <id>tag:benlog.org:/2009/1/31/revised-date-and-datetime-form-helpers-for-merb-1-x</id>
    <updated>2009-01-31T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;I&amp;#8217;ve expanded on both mine and Jamie Macey&amp;#8217;s original &lt;a href="http://www.benlog.org/2008/6/29/bare-bones-date-picker-for-merb"&gt;date helper code&lt;/a&gt; to support Merb 1.x, and added a datetime helper to the mix.&lt;/p&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%= form_for @person %&amp;gt;

  &amp;lt;%= date_field :birth_date :label =&amp;gt; 'Birthdate' %&amp;gt;

  &amp;lt;%= datetime_field :birth_date_and_time, :label =&amp;gt; 'Birthdate and time' %&amp;gt;

&amp;lt;% end =%&amp;gt;
&lt;/pre&gt;&lt;p&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You&amp;#8217;ll find the &lt;a href="http://gist.github.com/55584"&gt;code here&lt;/a&gt;, via GitHub. I generally name this file date_field.rb, and stick it in app/helpers.&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=JI3tloB2TD4:PMB7mR4QxGQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=JI3tloB2TD4:PMB7mR4QxGQ:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=JI3tloB2TD4:PMB7mR4QxGQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=JI3tloB2TD4:PMB7mR4QxGQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/aqlZk7FzLD4" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>GitHub Pages for project docs, demos</title>
    <link rel="alternate" type="text/html" href="/2008/12/29/github-pages-for-project-docs-demos/" />
    <id>tag:benlog.org:/2008/12/29/github-pages-for-project-docs-demos</id>
    <updated>2008-12-29T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;Have you seen &lt;a href="http://github.com/blog/272-github-pages"&gt;GitHub Pages&lt;/a&gt;? As of mid-December, &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt; now hosts complete web pages that are backed onto GitHub-hosted Git repositories. To start, just define an alternate ‘gh-pages’ branch for your repo, add your web content and push, and it will appear at yourname.github.com/repo-name. It&amp;#8217;s perfect for small one-off &lt;a href="http://defunkt.github.com/ambition/"&gt;project pages&lt;/a&gt;, or even &lt;a href="http://mojombo.github.com/"&gt;complete blogs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’ve already migrated my project documentation pages over (see: &lt;a href="http://bentlegen.github.com/abbreviator/demo"&gt;Abbreviator&lt;/a&gt;, &lt;a href="http://bentlegen.github.com/labelize/demo"&gt;Labelize&lt;/a&gt;). I like the idea that these pages are accessible at the same location as their corresponding &lt;a href="http://github.com/bentlegen/abbreviator"&gt;source&lt;/a&gt; &lt;a href="http://github.com/bentlegen/labelize"&gt;code&lt;/a&gt;, with complete revision history, even if my blog is down or – dare I say it – long defunct.&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=4ZrH_r0HkD8:PuCBV07JH5Q:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=4ZrH_r0HkD8:PuCBV07JH5Q:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=4ZrH_r0HkD8:PuCBV07JH5Q:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=4ZrH_r0HkD8:PuCBV07JH5Q:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/iTQGttyHc1M" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>Abbreviator: a jQuery plugin for fitting chunky content</title>
    <link rel="alternate" type="text/html" href="/2008/12/22/abbreviator-a-jquery-plugin-for-fitting-chunky-content/" />
    <id>tag:benlog.org:/2008/12/22/abbreviator-a-jquery-plugin-for-fitting-chunky-content</id>
    <updated>2008-12-22T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;Inside &lt;a href="http://www.freshbooks.com"&gt;FreshBooks&lt;/a&gt; (the application), there&amp;#8217;s a few places where we abbreviate long-ish text with ellipses to make it fit inside small table cells. This is done in &lt;span class="caps"&gt;PHP&lt;/span&gt; by a single function, whose two parameters are a string and the desired character length.&lt;/p&gt;
&lt;p&gt;Of course, not all characters are made equal. We estimate appropriate character lengths as best we can, but it doesn&amp;#8217;t always work. If users have a penchant for typing in capital letters, or use text zoom, our little abbreviating function can fail. And of course, every design change to these areas require new character length settings &amp;#8211; a task that is easily forgotten.&lt;/p&gt;
&lt;p&gt;So, after some sanity checking/brainstorming with my co-workers Taavi and Justin, I put together a little jQuery library for accomplishing this feat in Javascript. It&amp;#8217;s called &lt;a href="http://bentlegen.github.com/abbreviator/demo"&gt;Abbreviator&lt;/a&gt;, and it adjusts for font size, browser rendering differences, padding, and more. Take a look at the &lt;a href="http://bentlegen.github.com/abbreviator/demo"&gt;project page&lt;/a&gt; for more.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s it for me for the year &amp;#8211; Happy holidays!&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=c9qq_2FoZPU:6e64xsAoRpE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=c9qq_2FoZPU:6e64xsAoRpE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=c9qq_2FoZPU:6e64xsAoRpE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=c9qq_2FoZPU:6e64xsAoRpE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/F1UxOUUY5wM" height="1" width="1"/&gt;</content>
  </entry>
  
  
  <entry>
    <title>Labelize: a jQuery plugin for making big honking labels</title>
    <link rel="alternate" type="text/html" href="/2008/8/21/labelize-a-jquery-plugin-for-making-big-honking-labels/" />
    <id>tag:benlog.org:/2008/8/21/labelize-a-jquery-plugin-for-making-big-honking-labels</id>
    <updated>2008-08-21T00:00:00Z</updated>
    <updated />
 
    <author>
      <name>Ben Vinegar</name>
      <uri>http://benv.ca</uri>
      <email>ben@benlog.org</email>
    </author>
 
    <summary />
    <content type="html" xml:lang="en" xml:base="http://benv.ca">
      &lt;p&gt;&lt;a href="http://bentlegen.github.com/labelize/demo"&gt;Labelize&lt;/a&gt; is a handy &lt;a href="http://www.jquery.com"&gt;jQuery&lt;/a&gt; plugin that lets you give input-containing elements &amp;lt;label/&amp;gt; like properties, so that clicking on the container activates the input inside. The goal? To improve usability by giving hard-to-click input elements like radio buttons and checkboxes &lt;a href="http://www.37signals.com/svn/posts/1103-generous-link-targets-in-the-library"&gt;generous target areas&lt;/a&gt;. Surprisingly, this can&amp;#8217;t be done with a &amp;lt;label/&amp;gt; element alone &amp;#8212; if you want it supported in IE6, that is.&lt;/p&gt;
&lt;p&gt;Say we have the following markup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class="myLabel"&amp;gt;
  &amp;lt;input type="checkbox"/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make the &amp;#8220;myLabel&amp;#8221; div become a label-like container for the checkbox its holding, we simply do the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  $('.myLabel').labelize()
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s it! Now if we click anywhere on &amp;#8220;myLabel&amp;#8221;, the checkbox is clicked &amp;#8212; and its onclick() event is fired too. Huzzah.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;d like to see the plugin in action, I&amp;#8217;ve whipped up &lt;a href="http://bentlegen.github.com/labelize/demo"&gt;a quick project page&lt;/a&gt; with some working examples. Please have a look and let me know what you think.&lt;/p&gt;
    &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=HlTGrSSkcjo:1_8VUEslaE8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=HlTGrSSkcjo:1_8VUEslaE8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/benlog_org?a=HlTGrSSkcjo:1_8VUEslaE8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/benlog_org?i=HlTGrSSkcjo:1_8VUEslaE8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/benlog_org/~4/qywBlHUcHTA" height="1" width="1"/&gt;</content>
  </entry>
  
</feed>
