<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
	<channel>
		<title>The Particle Lab</title>
		<description>a blog by Neutron Creations</description>
		<link>http://new.theparticlelab.com</link>
		
		
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/NeutronCreationsBlog" /><feedburner:info uri="neutroncreationsblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>NeutronCreationsBlog</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item>
				<title>Reserve your username</title>
				<description>&lt;p&gt;Several web application have launched recently with an invite system where you not only give them an email address to notify you but also reserve your username. Some even go a little further and ask you to connect with Facebook, Twitter etc. The latest one to do so is &lt;a href="http://connect.me/"&gt;connect.me&lt;/a&gt;, which I signed up for when I had heard about it.&lt;/p&gt;&lt;!-- more --&gt;

&lt;p&gt;This triggered a brief discussion with a friend about this:&lt;/p&gt;

&lt;blockquote class="twitter-tweet"&gt;&lt;p&gt;Don't know what connect.me really is, but I'm reserving my username now! &lt;a href="http://cxt.me/sKQC8g"&gt;http://cxt.me/sKQC8g&lt;/a&gt; &lt;a href="https://twitter.com/search/%23connectme"&gt;#connectme&lt;/a&gt;&lt;/p&gt;&amp;mdash; Marc Roberts (@marcroberts) &lt;a href="https://twitter.com/marcroberts/status/45786025091547136"&gt;March 10, 2011&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class="twitter-tweet" data-conversation="none"&gt;&lt;p&gt;@&lt;a href="https://twitter.com/marcroberts"&gt;marcroberts&lt;/a&gt; Why?&lt;/p&gt;&amp;mdash; Justin Stach (@juter) &lt;a href="https://twitter.com/juter/status/45786659513569280"&gt;March 10, 2011&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class="twitter-tweet" data-conversation="none"&gt;&lt;p&gt;@&lt;a href="https://twitter.com/juter"&gt;juter&lt;/a&gt; cos I'm a web app whore, and there are other 'marcroberts' out there. So on the off chance it takes off I want that to be me&lt;/p&gt;&amp;mdash; Marc Roberts (@marcroberts) &lt;a href="https://twitter.com/marcroberts/status/45787058207334400"&gt;March 10, 2011&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class="twitter-tweet" data-conversation="none"&gt;&lt;p&gt;@&lt;a href="https://twitter.com/marcroberts"&gt;marcroberts&lt;/a&gt; And you're happy to give them access to your Facebook/Twitter account based on that? Seems a little one-sided to me.&lt;/p&gt;&amp;mdash; Justin Stach (@juter) &lt;a href="https://twitter.com/juter/status/45787476496883712"&gt;March 10, 2011&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class="twitter-tweet" data-conversation="none"&gt;&lt;p&gt;@&lt;a href="https://twitter.com/juter"&gt;juter&lt;/a&gt; it can have access to twitter for now, if it does anything nefarious its gone though. Probably need to explain more via other means&lt;/p&gt;&amp;mdash; Marc Roberts (@marcroberts) &lt;a href="https://twitter.com/marcroberts/status/45789014225199105"&gt;March 10, 2011&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;This discussion couldn&amp;#039;t really continue on Twitter so has spawned this post.&lt;/p&gt;

&lt;p&gt;I am lucky in that my full name is not that common, however I have not been able to get my preferred username of &amp;#039;marcroberts&amp;#039; on both Facebook and Flickr. This has lead me to try to register with new web application early in order to secure my username, and why I described myself as a "web app whore" (I&amp;#039;ve also been an active member of &lt;a href="http://webapps.stackexchange.com/users/333/marc-roberts"&gt;Web Apps Stack Exchange&lt;/a&gt; site from conception, through its private beta to now so please don&amp;#039;t interpret &amp;#039;whore&amp;#039; as a bad thing!).&lt;/p&gt;

&lt;p&gt;But it&amp;#039;s not just about reserving a username, here at &lt;a href="http://neutroncreations.com"&gt;Neutron Creations&lt;/a&gt; we know the pains of launching a web app and so I like to encourage others. If this means signing up my email address, connecting with twitter or facebook then I&amp;#039;m ok with that assuming they don&amp;#039;t do anything unwanted. Now obviously I can&amp;#039;t know in advance whether they will, and I guess that&amp;#039;s a risk I&amp;#039;m willing to take to help and support fellow web application developers.&lt;/p&gt;

&lt;p&gt;As long as you haven&amp;#039;t given your Twitter/Facebook password to them (and you shouldn&amp;#039;t as &lt;a href="http://oauth.net/about/"&gt;OAuth&lt;/a&gt; should be used for both) you can remove that app from your &lt;a href="http://www.facebook.com/settings/?tab=applications"&gt;Facebook&lt;/a&gt; and &lt;a href="http://twitter.com/settings/connections"&gt;Twitter&lt;/a&gt; accounts easily and prevent any further access to your account. Of course this won&amp;#039;t remove any data the app has retained, the app itself should provide a way to remove this data or delete/remove your account. There are also term of use with the Twitter/Facebook APIs which specify data retention rules that the app should honour.&lt;/p&gt;

&lt;p&gt;There is one golden rule that all web apps that have connections to Facebook/Twitter should never EVER break, they should &lt;strong&gt;never post from your account without your explicit permission&lt;/strong&gt;. Now my first tweet in the conversation may seem like an &amp;#039;auto-tweet&amp;#039;, and in fact if you search twitter for &lt;a href="http://search.twitter.com/search?q=%23connectme"&gt;#connectme&lt;/a&gt; it seems like connect.me could be breaking this rule, however this was a purely optional step in the signup process (although heavily encouraged) and the text was editable (I did alter my tweet from the standard text).&lt;/p&gt;

&lt;p&gt;What are you thoughts on connect.me and this trend of reserving usernames, or encouraging connections with social networks? Drop a comment below or give me a mention on twitter.&lt;/p&gt;

&lt;h3&gt;More about connect.me&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://nakedsecurity.sophos.com/2011/03/09/connect-me-rush-risky-behaviour-social-networkers/"&gt;Connect.me sign-up rush exposes risky behaviour of social networkers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.equalsdrummond.name/?p=418"&gt;The story behind the Connect.Me beta invite launch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=gooERx7qB-g:gmdfVq040X0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=gooERx7qB-g:gmdfVq040X0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=gooERx7qB-g:gmdfVq040X0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=gooERx7qB-g:gmdfVq040X0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=gooERx7qB-g:gmdfVq040X0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=gooERx7qB-g:gmdfVq040X0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=gooERx7qB-g:gmdfVq040X0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/gooERx7qB-g" height="1" width="1"/&gt;</description>
				<pubDate>Thu, 10 Mar 2011 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/gooERx7qB-g/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/reserve-your-username/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/reserve-your-username/</feedburner:origLink></item>
		
			<item>
				<title>Good API Documentation</title>
				<description>&lt;p&gt;So you're a developer (you're not a developer? then maybe this post isn't for you, but feel free to read on), and as with most developers you spend a lot of your time looking up things on Google, Stack Overflow or various API documentation (API docs).&lt;/p&gt;&lt;!-- more --&gt;

&lt;p&gt;The quicker we can find the information we need in these API docs, the quicker we can build the next game-changing web app. So what makes good API docs? Read on and I'll tell you.&lt;/p&gt;

&lt;p&gt;We've all been there; you're "in the zone", just written loads of totally awesome code but can't remember the parameters for a function you need to use. No problem, you bring up Google, enter the name of the function (and maybe the language you're using or something else to get a better result, you know the drill) and BAM the top result sounds perfect. However it's not, the information is out of date and of course you don't find this out until you actual test your code some time later and it takes you hours to track it down to this one bad source of information.&lt;/p&gt;

&lt;p&gt;So let's start at the beginning.&lt;/p&gt;

&lt;h3&gt;Accurate &amp; Up to date&lt;/h3&gt;

&lt;p&gt;This is the most important aspect of any API docs and thankfully most producers also realise this and do their best to ensure theirs stay up to date. The best of these are any that are built automatically from the API source; javadoc and rdoc, for Java and Ruby respectively, are great examples.&lt;/p&gt;

&lt;p&gt;What about maintaining API docs for multiple versions? Well, either have the complete set of docs for each version available separately like the Java API docs (&lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/"&gt;1.4.2&lt;/a&gt; / &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/"&gt;1.5.0 &lt;/a&gt;) or show &lt;strong&gt;clearly&lt;/strong&gt; which version(s) a particular page/function/method applies to, check out the documentation for the jQuery &lt;a href="http://api.jquery.com/live/"&gt;.live()&lt;/a&gt; function.&lt;/p&gt;

&lt;h3&gt;Clear, concise and well structured&lt;/h3&gt;

&lt;p&gt;Having accurate and up to date documentation is next to useless if developers can't find anything. Unless the entire API docs take up barely more than one screen (exactly what 'one screen' or '&lt;a href="http://thereisnofold.com/"&gt;the fold&lt;/a&gt;' is can be saved for another time) there has to be a good excuse to not have a search function. Search has its place but some people prefer to navigate to what they're looking for so the API docs need to be very well organised, with a clear hierarchical system if necessary. Both of these need to be consistent, if something can be found via the search system it really should be able to be navigated to also, and vice versa.&lt;/p&gt;

&lt;p&gt;There are some great examples of this all around the web; the latest &lt;a href="http://api.jquery.com"&gt;jQuery API&lt;/a&gt; site is excellent, as is &lt;a href="http://apidock.com/"&gt;APIdock&lt;/a&gt; (which gets bonus points for also having archives of every past version) and the &lt;a href="http://developer.vzaar.com/docs/version_1.0"&gt;vzaar&lt;/a&gt; API docs.&lt;/p&gt;

&lt;h3&gt;Examples&lt;/h3&gt;

&lt;p&gt;Being well structured is all well and good if you know what you're looking for but for first time use the best place to start is with a tutorial or examples. Many times I have found that just looking over an example can be far more useful than pouring over pages and pages of documentation. It's also good to see a mixture of examples, ideally at least one simple example and a more complicated one.&lt;/p&gt;

&lt;p&gt;Open source projects are often full of good examples in the documentation. There's no better way to encourage developers to use (and potentially help develop in future) your latest open source project than to provide them with a plethora of examples. Here are a few I used recently: &lt;a href="https://github.com/binarylogic/authlogic"&gt;Authlogic&lt;/a&gt;, &lt;a href="https://github.com/javan/whenever"&gt;Whenever&lt;/a&gt; and &lt;a href="https://github.com/nkallen/cache-money"&gt;Cache Money&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Discussion&lt;/h3&gt;

&lt;p&gt;Examples are great, but it's not feasible to come up with every possible use for the API. Why not let the developers do this, provide them with a great discussion environment; forums, mailing lists, IRC etc. This will also reflect well for prospective developers who will see an active community as a positive thing.&lt;/p&gt;

&lt;p&gt;The PHP API docs are a great example of this, see the page for the &lt;a href="http://uk3.php.net/strpos"&gt;strpos()&lt;/a&gt; function for example. Facebook has been making great progress on its API docs lately, but before that the best place to get information was in their &lt;a href="http://forum.developers.facebook.net/"&gt;developer forums&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Sandbox &amp; Playground&lt;/h3&gt;

&lt;p&gt;For very large APIs its helpful to have a developer area where you can try out API calls with different inputs and see their outputs. Flickr have really nailed this with their &lt;a href="http://www.flickr.com/services/api/explore/?method=flickr.photos.search"&gt;API Explorer&lt;/a&gt; where you can try out any of their API with full debug output.&lt;/p&gt;

&lt;p&gt;Going one step further, it would be really helpful to have access to a sandboxed version of the API. This allows creation of data that is completely separate and isolated from the live data but still operates in the same manner. For some application this is essential, particularly when developing with financial transactions. Luckily PayPal have a full &lt;a href="https://developer.paypal.com/"&gt;sandbox environment&lt;/a&gt; where you can create accounts, balances, card and bank account all that respond to the public API.&lt;/p&gt;

&lt;h3&gt;Anything else?&lt;/h3&gt;

&lt;p&gt;I'd love to hear what else you think good API documentation needs and your opinions on any great or terrible API docs out &lt;div style="display: none"&gt;&lt;a href='http://ccialisonlinee.com' title='cialis in uk'&gt;cialis in uk&lt;/a&gt;&lt;/div&gt; there, drop me a comment below or contact me on twitter.&lt;/p&gt;

&lt;h3&gt;More Reading&lt;/h3&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/2001899/creating-great-api-documentation-tools-and-techniques"&gt;API Docs Tools &amp; Techniques&lt;/a&gt; (stackoverflow)&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/469161/how-do-you-define-a-good-or-bad-api"&gt;How to define good or bad API&lt;/a&gt; (stackoverflow)&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.scribd.com/doc/33655/How-to-Design-a-Good-API-and-Why-it-Matters"&gt;How to design good API and why&lt;/a&gt; (scribd)&lt;/li&gt;
&lt;/ul&gt;
&lt;cite&gt;Photo credit: &lt;a href='http://www.flickr.com/photos/43740025@N03/4184481846'&gt;casa.pokayoke&lt;/a&gt;&lt;/cite&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=WUjNSG8c2l8:xHpQuE073gs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=WUjNSG8c2l8:xHpQuE073gs:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=WUjNSG8c2l8:xHpQuE073gs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=WUjNSG8c2l8:xHpQuE073gs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=WUjNSG8c2l8:xHpQuE073gs:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=WUjNSG8c2l8:xHpQuE073gs:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=WUjNSG8c2l8:xHpQuE073gs:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/WUjNSG8c2l8" height="1" width="1"/&gt;</description>
				<pubDate>Mon, 07 Feb 2011 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/WUjNSG8c2l8/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/good-api-documentation/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/good-api-documentation/</feedburner:origLink></item>
		
			<item>
				<title>Building a Custom HTML5 Audio Player with jQuery</title>
				<description>&lt;p&gt;We recently built an HTML5 audio player for &lt;a href="http://maxvoltar.com/"&gt;Tim Van Damme&lt;/a&gt;&amp;#039;s &lt;a href="http://thebox.maxvoltar.com/"&gt;The Box&lt;/a&gt;, a new podcast where he interviews people who make cool stuff. Tim wanted an HTML5 audio player on the site, and we put together some &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; to hook up the player interface he designed. In this article we&amp;#039;ll run through the code to explain how it works, covering a few caveats along the way.&lt;/p&gt;&lt;!-- more --&gt;

&lt;p&gt;Here&amp;#039;s the player interface, and the markup for it.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/2010/player-85c8d6ab50abe9f21160979334704d29.png" alt="" title="HTML5 audio player from The Box" width="400" height="49" class="aligncenter" /&gt;&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;player&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;playtoggle&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;gutter&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;loading&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;handle&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ui-slider-handle&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;timeleft&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, we have a few span elements for each component of the interface:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;playtoggle&lt;/strong&gt; is the play/pause toggle button.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;gutter&lt;/strong&gt; is the timeline track&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;loading&lt;/strong&gt; is the bar indicating the loading or buffering progress&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;handle&lt;/strong&gt; is the circular element that serves as the playhead, indicating current position in the audio file, and also acting as a drag handle to move to a different point of the audio&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;timeleft&lt;/strong&gt; is the play time remaining, in minutes and seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We won&amp;#039;t cover the CSS for the player here, but if you want to see how it&amp;#039;s styled you can inspect the styles on the &lt;a href="http://thebox.maxvoltar.com/"&gt;live site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We&amp;#039;re going to use jQuery to detect support for HTML5 audio, and if it&amp;#039;s supported, we&amp;#039;ll insert the audio player markup and the audio tag itself. This means that browsers that don&amp;#039;t have HTML5 audio support won&amp;#039;t see an audio player interface they can&amp;#039;t use. You could optionally fall back to a Flash based player, but as Tim is already providing a direct link to the MP3 file elsewhere on the page, the audio player is seen as progressive experience enrichment here.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;audio&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;canPlayType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;p class=&amp;quot;player&amp;quot;&amp;gt; ... &amp;lt;/p&amp;gt;\&lt;/span&gt;
&lt;span class="s1"&gt;    &amp;lt;audio&amp;gt;\&lt;/span&gt;
&lt;span class="s1"&gt;      &amp;lt;source src=&amp;quot;/path/to/episode1.ogg&amp;quot; type=&amp;quot;audio/ogg&amp;quot;&amp;gt;&amp;lt;/source&amp;gt;\&lt;/span&gt;
&lt;span class="s1"&gt;      &amp;lt;source src=&amp;quot;/path/to/episode1.mp3&amp;quot; type=&amp;quot;audio/mpeg&amp;quot;&amp;gt;&amp;lt;/source&amp;gt;\&lt;/span&gt;
&lt;span class="s1"&gt;      &amp;lt;source src=&amp;quot;/path/to/episode1.wav&amp;quot; type=&amp;quot;audio/x-wav&amp;quot;&amp;gt;&amp;lt;/source&amp;gt;\&lt;/span&gt;
&lt;span class="s1"&gt;    &amp;lt;/audio&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;insertAfter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#listen .photo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The detection code on line 1 is taken from a &lt;a href="http://diveintohtml5.org/everything.html"&gt;guide to feature detection&lt;/a&gt; on the superb &lt;a href="http://diveintohtml5.org"&gt;Dive Into HTML5&lt;/a&gt; site. If support is detected, we go right ahead and insert the player code (abridged at the "..." in this code), and the audio tag. &lt;/p&gt;

&lt;p&gt;Your audio tag should contain three source formats. The first is OGG Audio which is an open standard, supported by Firefox and Chrome. The second is our old friend MPEG-1 layer 3 (MP3) which is a commercial proprietary format supported by Safari. The third is a plain old WAVE file, which is what Opera wants to hear. Browsers will attempt to use sources in markup order, so for example Safari would fail to read the OGG format source, and use the MP3 source instead, while Opera will fail on the first two sources and use the third WAVE source.&lt;/p&gt;

&lt;p&gt;Now we need to start adding functionality to our player:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="nx"&gt;audio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.player audio&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;loadingIndicator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.player #loading&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;positionIndicator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.player #handle&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;timeleft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.player #timeleft&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffered&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;progress&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;loadingIndicator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;loadingIndicator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We first save variable references to several key elements of our player so that we can refer to them quickly without querying the DOM each time.&lt;/p&gt;

&lt;p&gt;We then look for support of the buffered property on our audio tag. This should contain information about how much of the audio file has been buffered. At the time of writing, Firefox doesn&amp;#039;t provide the buffered property at all, while Opera has the property but doesn&amp;#039;t put anything in it. For browsers that do (and for future versions of Firefox and Opera that have full support for the buffered property), we set up an event handler for the &amp;#039;progress&amp;#039; event (fired as loading progress is made). As the audio file loads, we calculate the amount of the file that has been loaded as a percentage, and then use this value for the width of our loading indicator. If buffered progress support isn&amp;#039;t available, we can simply remove the redundant loading indicator element from the DOM.&lt;/p&gt;

&lt;p&gt;Now we&amp;#039;ll write an event handler for the &amp;#039;timeupdate&amp;#039; event, which is fired whenever the current play time is updated, either as we&amp;#039;re playing the audio normally, or when we seek to a new position within the audio file.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;timeupdate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;rem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;mins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rem&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;secs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rem&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;mins&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
			
  &lt;span class="nx"&gt;timeleft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;mins&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secs&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;secs&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;secs&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;manualSeek&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;positionIndicator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
				
    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.player #gutter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;orientation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;horizontal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;min&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;animate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;					
      &lt;span class="nx"&gt;slide&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;							
        &lt;span class="nx"&gt;manualSeek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;manualSeek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;					
        &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First we calculate the play time remaining (in seconds), the position of the playhead as a percentage of total duration, and the minutes and seconds remaining.&lt;/p&gt;

&lt;p&gt;We then update the displayed time remaining in the player interface, taking care to insert a leading 0 if necessary to the seconds figure.&lt;/p&gt;

&lt;p&gt;If we haven&amp;#039;t triggered this event by manually seeking (determined a bit further on in the code), we move the playhead position indicator along the gutter track. This basically just slides the indicator along as we listen, unless we&amp;#039;re using the slider mechanism to move the playing position, in which case we don&amp;#039;t want to interfere with the position of the indicator because the slider code will handle that for us.&lt;/p&gt;

&lt;p&gt;Until the audio has started to load, the duration of the audio file is not available. We therefore check to see if the audio file has started to load, and proceed to set up the slider for the draggable playhead control. We&amp;#039;re using &lt;a href="http://jqueryui.com"&gt;jQuery UI&lt;/a&gt; for our slider here, and the basic configuration options should be self explanatory. We add two event handlers; one on slide where we set a flag that we are manually seeking to a new position, and stop, to unset the flag and tell the browser&amp;#039;s audio player that it needs to move to our new position in the audio file.&lt;/p&gt;

&lt;p&gt;The only thing left is the play/pause toggle button.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;play&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#playtoggle&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;playing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;		
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pause ended&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#playtoggle&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;removeClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;playing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;		
&lt;span class="p"&gt;});&lt;/span&gt;		
		
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#playtoggle&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;			
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;play&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;			
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First we set up a couple of event handlers. When we start playing the audio, we add a class of &amp;#039;playing&amp;#039; to the button so that it switches to the pause state. We then remove that class if we pause the audio playback, or reach the end of the file which fires the &amp;#039;ended&amp;#039; event.&lt;/p&gt;

&lt;p&gt;Our click handler for the play/pause button is very simple; play the audio if we&amp;#039;re currently paused, otherwise pause it.&lt;/p&gt; 

&lt;p&gt;So that&amp;#039;s it for a super simple audio player using lovely web standards instead of Flash. By going down this route, Tim&amp;#039;s visitors can use his audio player on mobile devices like the iPhone and iPad, and the loading performance of the player is hopefully better than would have been achievable with a plugin based solution. Go and listen to &lt;a href="http://thebox.maxvoltar.com"&gt;The Box&lt;/a&gt;, and if you have any questions or ideas for improving the player, please leave a comment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; Fixed the code for Opera, providing the WAVE source and a more detailed check for support of the buffered property.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE 2:&lt;/strong&gt; Fixed a typo binding the play event handler, which should be &lt;span class="highlight"&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;/span&gt; not &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;/span&gt;. Thanks to &lt;a href="http://twitter.com/patdryburgh"&gt;@patdryburgh&lt;/a&gt; for helping find that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Useful Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://html5doctor.com/native-audio-in-the-browser/"&gt;HTML5 Doctor&amp;#039;s article on audio&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://diveintohtml5.org/everything.html"&gt;Dive Into HTML5&amp;#039;s All-In-One Almost-Alphabetical No-Bullshit Guide To Detecting Everything&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://developer.apple.com/safari/library/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/AudioandVideoTagBasics/AudioandVideoTagBasics.html"&gt;Safari Reference Guide to Audio and Video&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://dev.opera.com/articles/view/html5-audio-radio-player/"&gt;Opera Dev article creating an HTML5 player&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.happyworm.com/jquery/jplayer/"&gt;jPlayer - a ready made jQuery audio player plugin&lt;/a&gt;&lt;/li&gt; 
&lt;/ul&gt;

&lt;cite&gt;Photo credit: &lt;a href="http://www.flickr.com/photos/miikka_skaffari/3943529390/in/photostream/"&gt;Miikka Skaffari&lt;/a&gt;&lt;/cite&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=IsgA1slT9tI:3gCNxVbnIB8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=IsgA1slT9tI:3gCNxVbnIB8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=IsgA1slT9tI:3gCNxVbnIB8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=IsgA1slT9tI:3gCNxVbnIB8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=IsgA1slT9tI:3gCNxVbnIB8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=IsgA1slT9tI:3gCNxVbnIB8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=IsgA1slT9tI:3gCNxVbnIB8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/IsgA1slT9tI" height="1" width="1"/&gt;</description>
				<pubDate>Thu, 02 Sep 2010 00:00:00 +0100</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/IsgA1slT9tI/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/building-a-custom-html5-audio-player-with-jquery/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/building-a-custom-html5-audio-player-with-jquery/</feedburner:origLink></item>
		
			<item>
				<title>Single-Line vs Multi-Line CSS, plus TextMate tips</title>
				<description>&lt;p&gt;Now that the stormy debate around &lt;a title="Elliot Jay Stocks starting a furore over a Tweet" href="http://elliotjaystocks.com/blog/web-designers-who-cant-code/"&gt;whether or not designers should be able to code what they produce&lt;/a&gt; has died down, the new debate du jour is here! Or rather, it&amp;#039;s back again. This one has been around before, and it&amp;#039;s about whether laying out your CSS code on single lines (one selector per line), or multiple lines is better.&lt;/p&gt;&lt;!-- more --&gt;

&lt;p&gt;In the red corner; &lt;a title="Ordered List&amp;#039;s article arguing for single-line CSS" href="http://orderedlist.com/our-writing/resources/html-css/single-line-css/"&gt;single-line CSS code formatting&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="css"&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;float&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;155px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;opacity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;border-bottom-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the blue corner; multiple-line CSS code formatting:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="css"&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;float&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;155px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;opacity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;border-bottom-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The principal argument for coding on single lines is that you end up with a file that has all your selectors stacked up on top of each other, allowing for easier eye scanning when hunting for a specific selector.&lt;/p&gt;

&lt;p&gt;The most common argument against is that when you&amp;#039;re working in a source controlled environment (which of course we all are because we&amp;#039;d be idiots not to, right?) and a change is made to the CSS, there&amp;#039;s no way with the single-line approach to quickly see which property was changed and  how. Here&amp;#039;s a modification to some multi-line CSS code:&lt;/p&gt;

&lt;img class="aligncenter" title="A diff on a change to multiple-line CSS" src="/assets/2010/diff-multi-77836b3fd8e80fbf71c7d65b96733581.png" alt="" width="417" height="106" /&gt;

&lt;p&gt;And here&amp;#039;s how the same modification would look if this was a single line statement:&lt;/p&gt;

&lt;img class="aligncenter" title="A diff on a change to single-line CSS" src="/assets/2010/diff-single-4dfceb95f3232c42b3e7ebe23856f09c.png" alt="" width="600" height="86" /&gt;

&lt;p&gt;It&amp;#039;s obvious from the multiple-line example that the background-color property has been changed from transparent to "#fff", but in the single-line example the actual change isn&amp;#039;t highlighted because differentiators only work on a per-line basis, so it just reports that the whole line has changed. It will be up to you to work out which property was modified. Now talk to me about "easier to scan".&lt;/p&gt;

&lt;p&gt;For the most part, and complexities during source control diffs aside, this is simply a case of personal preference. &lt;a title="Simon Collison for single-line CSS" href="http://twitter.com/simoncollison/status/10927863846"&gt;Opinion&lt;/a&gt; &lt;a title="Drew McLellan for multi-line CSS" href="http://twitter.com/drewm/status/10928572542"&gt;is divided&lt;/a&gt; &lt;a title="Cole Henley for single-line CSS" href="http://twitter.com/cole007/status/10929313876"&gt;right&lt;/a&gt; &lt;a title="Paul Stanton for multi-line CSS" href="http://coffeepowered.co.uk/2010/01/css-coding-standards/"&gt;down&lt;/a&gt; the middle as to which is better, with no fence sitters because really you just have to pick your preferred option (as a team if there&amp;#039;s more than one CSS coder involved on a project) and stick to it for consistency. Saying that it&amp;#039;s best practise to use one technique or the other isn&amp;#039;t really helpful — just figure out what works for your situation and environment and stick with that.&lt;/p&gt;

&lt;p&gt;My preference is for multiple-line CSS, because that&amp;#039;s how I&amp;#039;ve always done it, I use source control on every single project (even solo projects), and my process for designing and front-end coding involves inspecting elements in &lt;a href="http://getfirebug.com"&gt;Firebug&lt;/a&gt;, testing modifications to markup and style, then using Find &lt;span class="kb-shortcut"&gt;⌘F&lt;/span&gt; to jump immediately to the relevant selector I want to change. This involves no visual scanning for selectors in my code editor, and once you have the required keystrokes wired into your brain I guarantee that this is far quicker than scro&lt;lling and scanning for selectors, even with single-line CSS formatting.&lt;/p&gt;

&lt;p&gt;But enough of this petty bickering debate! Because I&amp;#039;m lucky enough to be a &lt;a href="http://macromates.com/"&gt;TextMate&lt;/a&gt; user, this is all completely academic. Here are two quick TextMate tips for multiple-line CSS coding to help with finding selectors, if you want the best of both worlds.&lt;/p&gt;

&lt;p&gt;When editing a CSS file, you&amp;#039;ll have a "Symbol Navigator" in right side of the status bar, which displays the currently focused selector based on cursor position. If you click the Symbol Navigator, a menu appears which displays all your selectors in a very easily scannable list. Simply click on one to jump straight to it. &lt;/p&gt;

&lt;p&gt;You can also use the shortcut &lt;span class="kb-shortcut"&gt;⌘⇧T&lt;/span&gt; to pull up a "Go To Symbol" dialog, where you can type part of your selector and it will suggest matches. Use the arrow keys to move up and down the matches, and the Enter key to jump to that selector in your file. Hat tip to &lt;a href="http://dougneiner.com"&gt;Doug Neiner&lt;/a&gt; for that one.&lt;/p&gt;

&lt;img class="aligncenter" title="Symbol Navigation in TextMate" src="/assets/2010/symbolnav-600x326-636e0e6620aa2d1e088188a1eb94e6ae.png" alt="TextMate screenshot showing Symbol Navigation" width="600" height="326" /&gt;

&lt;p&gt;There&amp;#039;s also a quick way to jump between what is effectively a selector only view and your standard multiple-line format, by making use of Code Folding. While editing normally and laying out code using the multiple-line technique, your code will look something like this:&lt;/p&gt;

&lt;img class="aligncenter" title="Editing CSS in TextMate, with no Code Folding" src="/assets/2010/unfolded-57c203e7497a4f51b809b0d0bf24195d.png" alt="" width="578" height="529" /&gt;

&lt;p&gt;But if you use Code Folding to collapse your code on all levels (and with vanilla CSS there will only be one level anyway) using the shortcut &lt;span class="kb-shortcut"&gt;⌘⌥0&lt;/span&gt; then you&amp;#039;ll reduce your display of your code to this:&lt;/p&gt;

&lt;img class="aligncenter" title="CSS in Code Folded mode in TextMate" src="/assets/2010/folded-9471c837136fe920e7bbe2c415d77ed8.png" alt="" width="535" height="391" /&gt;

&lt;p&gt;This view will help you scan selectors quickly without the attributes getting in the way. When you&amp;#039;ve found the selector you want, pushing &lt;span class="kb-shortcut"&gt;⌘⌥0&lt;/span&gt; again will return you to the unfolded view.&lt;/p&gt;

&lt;p&gt;Notice by the line numbers that I&amp;#039;m not afraid of large CSS files, and why should anyone care about that? It&amp;#039;s 2010 and we have smarter text editors these days than the days where a 2000 line file was a big deal because of all the scrolling we had to do. &lt;a title="YUI Compressor for CSS and JavaScript minification" href="http://developer.yahoo.com/yui/compressor/"&gt;CSS should be minified&lt;/a&gt; as part of your deployment process to your production environment anyway, so this is a non-issue for your end users who are served the file.&lt;/p&gt;

&lt;p&gt;So, go forth and code in whatever style you prefer. Hopefully this shows you that if you have the appropriate tools for the job, smaller details like code formatting start to matter a lot less which allows us to spend our energies on coding more and better things, instead of arguing about how we code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; &lt;a href="http://neutroncreations.com/blog/single-line-vs-multi-line-css-plus-textmate-tips/#comment-174"&gt;Sean Gaffney commented&lt;/a&gt; to tell us about another TextMate shortcut: &lt;span class="kb-shortcut"&gt;^⌥Q&lt;/span&gt; which properly converts multiple-line CSS to single-line. Thanks, Sean!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 2:&lt;/strong&gt; Another killer TextMate tip, this one from &lt;a href="http://neutroncreations.com/blog/single-line-vs-multi-line-css-plus-textmate-tips/comment-page-1/#comment-246"&gt;Doug Neiner&lt;/a&gt; - Hitting &lt;span class="kb-shortcut"&gt;⌘⇧T&lt;/span&gt; while editing a CSS file will bring up a "Go To Symbol" dialog with a list of all your selectors, which is much cleaner than my method for searching using Find. This "easier to scan selectors" argument for single-line CSS is really shot to pieces if you&amp;#039;re a TM user now. Thanks Doug! &lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=HvPLmWrZs3E:jt931Dtmg_Q:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=HvPLmWrZs3E:jt931Dtmg_Q:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=HvPLmWrZs3E:jt931Dtmg_Q:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=HvPLmWrZs3E:jt931Dtmg_Q:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=HvPLmWrZs3E:jt931Dtmg_Q:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=HvPLmWrZs3E:jt931Dtmg_Q:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=HvPLmWrZs3E:jt931Dtmg_Q:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/HvPLmWrZs3E" height="1" width="1"/&gt;</description>
				<pubDate>Tue, 23 Mar 2010 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/HvPLmWrZs3E/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/single-line-vs-multi-line-css-plus-textmate-tips/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/single-line-vs-multi-line-css-plus-textmate-tips/</feedburner:origLink></item>
		
			<item>
				<title>A simple technique for web app maintenance modes using Apache</title>
				<description>&lt;p&gt;Sometimes we need to take our web apps offline temporarily (usually for scheduled major updates or emergency repair work), whereby regular users are shown a simple static page notifying them that the site is unavailable but still giving developers/admins full access to the site to test changes. Of course you could implement this in your application, but wouldn&amp;#039;t it be nice if you could get your web server to do it all for you?&lt;/p&gt;&lt;!-- more --&gt;

&lt;p&gt;Its not as hard as it sounds thanks to some great &lt;a href="http://httpd.apache.org/"&gt;Apache&lt;/a&gt; modules, most notably &lt;a href="http://httpd.apache.org/docs/2.2/rewrite/"&gt;mod_rewrite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are the basic rules we regularly use at Neutron Creations (I&amp;#039;ll go in to more detail in a moment), these go inside our .htaccess file, but with some modification could go in the main httpd.conf file if you&amp;#039;d prefer.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="apache"&gt;&lt;span class="c"&gt;# Part I&lt;/span&gt;
&lt;span class="nb"&gt;RewriteCond&lt;/span&gt; APP_ROOT/maintenance.txt -f
&lt;span class="nb"&gt;RewriteRule&lt;/span&gt; SUPER_SECRET_URL / [R,L,CO=AllowMaintenance:1:YOUR-DOMAIN.COM]

&lt;span class="c"&gt;# Part II&lt;/span&gt;
&lt;span class="nb"&gt;RewriteCond&lt;/span&gt; APP_ROOT/maintenance.txt -f
&lt;span class="nb"&gt;RewriteCond&lt;/span&gt; %{REQUEST_URI} !^/maintenance/
&lt;span class="nb"&gt;RewriteCond&lt;/span&gt; %{HTTP_COOKIE} !AllowMaintenance
&lt;span class="nb"&gt;RewriteRule&lt;/span&gt; .* &lt;span class="sx"&gt;/maintenance/&lt;/span&gt; [L]

&lt;span class="c"&gt;# Part III&lt;/span&gt;
&lt;span class="nb"&gt;RewriteCond&lt;/span&gt; APP_ROOT/maintenance.txt -f
&lt;span class="nb"&gt;RewriteRule&lt;/span&gt; ^(.*)$ - [env=MAINTENANCE:1]

&lt;span class="nb"&gt;Header&lt;/span&gt; set cache-control &lt;span class="s2"&gt;&amp;quot;max-age=0,must-revalidate,post-check=0,pre-check=0&amp;quot;&lt;/span&gt; env=MAINTENANCE
&lt;span class="nb"&gt;Header&lt;/span&gt; set Expires -1 env=MAINTENANCE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This technique uses the presence of a file (&lt;strong&gt;APP_ROOT/maintenance.txt&lt;/strong&gt;) to enforce a set of rules. To enter maintenance mode simply create this file with &lt;span class="highlight"&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; APP_ROOT/maintenance.txt&lt;/span&gt;, to leave maintenance mode remove this file with &lt;span class="highlight"&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; APP_ROOT/maintenance.txt&lt;/span&gt;. You&amp;#039;ll need to replace &lt;strong&gt;APP_ROOT&lt;/strong&gt; with whatever makes sense in your application environment.&lt;/p&gt;

&lt;p&gt;&lt;strike&gt;&lt;em&gt;I tried to set this relative to Apache&amp;#039;s &lt;strong&gt;DOCUMENT_ROOT&lt;/strong&gt;&lt;/em&gt;&lt;em&gt; but could not get this to work, if anyone knows how to do this please do let me know in the comments.&lt;/em&gt;&lt;/strike&gt; Thanks to &lt;a href='#comment-383'&gt;ianjs&lt;/a&gt; for pointing out the correct syntax for this, you can use &lt;span class="highlight"&gt;&lt;span class="err"&gt;%{DOCUMENT_ROOT}&lt;/span&gt;&lt;/span&gt; in place of an absolute value for &lt;strong&gt;APP_ROOT&lt;/strong&gt; to reference your document root&lt;/p&gt;


&lt;h3&gt;Part I&lt;/h3&gt;
&lt;p&gt;This provides developers (or anyone who knows the secret URL) with continued access to the app by setting a cookie that will be remembered for the session, and used in later rules. You will need to change &lt;strong&gt;YOUR-DOMAIN.COM&lt;/strong&gt; to be the correct domain for your app, you could also add an expire time on this cookie if you wish (more info in the &lt;a href="http://httpd.apache.org/docs/2.2/rewrite/rewrite_flags.html#flags"&gt;mod_rewrite flags docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You will also need to ensure the &lt;strong&gt;SUPER_SECRET_URL&lt;/strong&gt; is set to something suitable, complex enough to be unguessable by users but easily read out if case you need to give it out over the phone.&lt;/p&gt;

&lt;h3&gt;Part II&lt;/h3&gt;
This is the key set of rules for what we&amp;#039;re trying to achieve, and reads like this: &lt;strong&gt;IF&lt;/strong&gt; we are in maintenance mode &lt;strong&gt;AND&lt;/strong&gt; the request is not for the maintenance page &lt;strong&gt;AND&lt;/strong&gt; the AllowMaintenance cookie is not set &lt;strong&gt;THEN&lt;/strong&gt; redirect the request to the maintenance page.

&lt;p&gt;The maintenance page must be completely self contained within this maintenance directory, as any sub-requests outside of this path (for any CSS/JS assets used on the maintenance page for example) will be redirected to the maintenance page themselves.&lt;/p&gt;

&lt;p&gt;With this directive, Apache will serve an internal redirect so the user will still see their original URL in their address bar, allowing them to refresh to try again later. If you want an external redirect so that the user sees the &amp;#039;/maintenance&amp;#039; URL instead, then just add the R flag to this RewriteRule.&lt;/p&gt;

&lt;h3&gt;Part III&lt;/h3&gt;
&lt;p&gt;This part could be quite important depending on how aggressively you cache HTTP requests in your application.  Using an environment variable that&amp;#039;s only set if you&amp;#039;re in maintenance mode, we override any cache related headers sent from your app, making all requests expire immediately and telling proxies/caches to avoid caching anything going forward. You&amp;#039;ll need &lt;a href="http://httpd.apache.org/docs/2.2/mod/mod_headers.html"&gt;mod_headers&lt;/a&gt; enabled for this part to work.&lt;/p&gt;

&lt;p&gt;So there you have it, a very simple method to gracefully shift your web app into maintenance mode, completely independent of your application code itself. I&amp;#039;m sure this could be expanded upon and I&amp;#039;d love to see any ideas you might have for building on this technique, so drop a comment here and let me know.&lt;/p&gt;
&lt;cite&gt;Photo credit: &lt;a href=&amp;#039;http://www.flickr.com/photos/tweek/139509551/&amp;#039;&gt;tweek&lt;/a&gt;&lt;/cite&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Its important to make sure that if you are NOT using an external redirect in Part II that you need to be careful linking to any assets as any relative paths will be relative to the url that user was trying to reach. Try to use absolute links where possible. I wasn&amp;#039;t clear about this before, thanks to &lt;a href="#comment-275"&gt;Dasha&lt;/a&gt; for pointing this out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 2:&lt;/strong&gt; I&amp;#039;ve been asked if it would be possible to serve a 503 (or similar) response to search engines to prevent them indexing the maintenance page. I have an alternate solution for this, if you serve an external redirect with a status code &amp;#039;307 - Temporary Redirect&amp;#039;, by adding the R=307 flag in Part II, this will tell search engines to ignore the response and continue to index the original page. In addition you can add a &amp;#039;Retry-After&amp;#039; header to inform them when to try again, just add this after the other Header directives &lt;span class="highlight"&gt;&lt;span class="nb"&gt;Header&lt;/span&gt; set Retry-After &lt;span class="m"&gt;1800&lt;/span&gt; env=MAINTENANCE&lt;/span&gt; which says to try again after 30 minutes.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=3_bDx8ySqcA:dVRHQywNMFc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=3_bDx8ySqcA:dVRHQywNMFc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=3_bDx8ySqcA:dVRHQywNMFc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=3_bDx8ySqcA:dVRHQywNMFc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=3_bDx8ySqcA:dVRHQywNMFc:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=3_bDx8ySqcA:dVRHQywNMFc:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=3_bDx8ySqcA:dVRHQywNMFc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/3_bDx8ySqcA" height="1" width="1"/&gt;</description>
				<pubDate>Thu, 18 Mar 2010 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/3_bDx8ySqcA/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/a-simple-technique-for-web-app-maintenance-modes-using-apache/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/a-simple-technique-for-web-app-maintenance-modes-using-apache/</feedburner:origLink></item>
		
			<item>
				<title>Easier print CSS coding using Firebug and Web Developer Toolbar</title>
				<description>&lt;p&gt;Print stylesheets are an aspect of front-end engineering that rarely get any love, and they&amp;#039;ll often be ignored until the last possible moment before launching a site. Sometimes they might even be forgotten altogether, but you should always make time to write at least a basic stylesheet that formats things neatly, and reveals any elements that only make sense on paper while hiding things like interactive controls that will be useless when printed.&lt;/p&gt;&lt;!-- more --&gt;

&lt;p&gt;But how to make the process of creating a print stylesheet as painless as possible? The obvious (and painful) way of working goes something like this:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Open/refresh your site in a browser&lt;/li&gt;
	&lt;li&gt;Pull up a Print Preview&lt;/li&gt;
	&lt;li&gt;Make changes to your print CSS in your editor&lt;/li&gt;
	&lt;li&gt;Close the Print Preview&lt;/li&gt;
	&lt;li&gt;Go to 1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It&amp;#039;s only going to take a few laborious cycles through that loop before you&amp;#039;re ready to give up and work on something more rewarding.&lt;/p&gt;

&lt;p&gt;But wait, there&amp;#039;s a better way! By using &lt;a title="Firefox download site" href="http://getfirefox.com"&gt;Firefox&lt;/a&gt; with its two close friends; &lt;a title="Firebug homepage" href="http://getfirebug.com"&gt;Firebug&lt;/a&gt; and &lt;a title="Web developer toolbar plugin page" href="https://addons.mozilla.org/en-US/firefox/addon/60"&gt;Web Developer Toolbar&lt;/a&gt; (you&amp;#039;re all using these daily for front-end engineering anyway, right?), we can make working on a print stylesheet as simple as one for the screen. Observe.&lt;/p&gt;

&lt;p&gt;First, open your website in Firefox, then we&amp;#039;ll ask Web Developer Toolbar to render the page using your print rather than screen CSS by choosing &lt;strong&gt;CSS → Display CSS By Media Type → Print&lt;/strong&gt; from the Web Developer Toolbar:&lt;/p&gt;

&lt;img class="aligncenter" title="Selecting display using print CSS from the Web Developer Toolbar" src="/assets/2010/print-0ecc007a0b92ddb12ef723e53357518e.png" alt="" width="618" height="261" /&gt;

&lt;p&gt;Now what you&amp;#039;ll see is your website rendered using your print media type CSS. We could just launch Firebug and start attacking the DOM and our styles at this point, but if you need to reload the page to pull in changes made to your code via your text editor, you&amp;#039;ll have to choose to view the print CSS again.&lt;/p&gt;

&lt;p&gt;Thankfully there&amp;#039;s an option for that; &lt;strong&gt;Options → Persist Features&lt;/strong&gt;, which will retain our CSS media type preference across page reloads:&lt;/p&gt;

&lt;img class="aligncenter size-full wp-image-398" title="Persisting features in Web Developer Toolbar between refreshes" src="/assets/2010/persist-d4d6e59d40e4cebcc04c8497b978d1a0.png" alt="" width="468" height="327" /&gt;

&lt;p&gt;Now we can start using Firebug to inspect our page and play with styling and markup as normal, and if you need to reload the page, you&amp;#039;ll still be viewing the print CSS afterwards.&lt;/p&gt;

&lt;img class="aligncenter size-full wp-image-399" title="Editing using Firebug" src="/assets/2010/firebug-bb7fba546267d9a7d35d32582b2d5803.png" alt="" width="614" height="289" /&gt;

&lt;p&gt;So now that you have no excuses for not producing sensible print CSS, go and give this a try and patch up your site. We still need to do ours — I&amp;#039;ll get around to that some day.&lt;/p&gt;

&lt;p&gt;The site you see in the background here is &lt;a title="Eric Meyer on print stylesheets at A List Apart" href="http://www.alistapart.com/articles/goingtoprint/"&gt;Eric Meyer&amp;#039;s 2002 article on print stylesheets&lt;/a&gt;, which despite being 8 years old is still relevant today, and even made use of a CSS3 attribute selector for some super progressive enhancement before we even knew what that term was.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=vPHOFOS799U:ZUcHr5f7C40:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=vPHOFOS799U:ZUcHr5f7C40:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=vPHOFOS799U:ZUcHr5f7C40:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=vPHOFOS799U:ZUcHr5f7C40:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=vPHOFOS799U:ZUcHr5f7C40:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=vPHOFOS799U:ZUcHr5f7C40:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=vPHOFOS799U:ZUcHr5f7C40:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/vPHOFOS799U" height="1" width="1"/&gt;</description>
				<pubDate>Mon, 15 Mar 2010 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/vPHOFOS799U/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/easy-print-css-coding-using-firebug-and-web-developer-toolbar/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/easy-print-css-coding-using-firebug-and-web-developer-toolbar/</feedburner:origLink></item>
		
			<item>
				<title>Redesigning interfaces: learn from the old before you throw it away</title>
				<description>&lt;p&gt;Central heating, as a domestic utility, has not changed much over the decades. We burn fuel, our homes stay warm when its cold outside, and we generally regulate the service in one of two ways — either it&amp;#039;s thermostatically controlled to maintain a constant room temperature, or scheduled according to rudimentary on/off time programming.&lt;/p&gt;&lt;!-- more --&gt;

&lt;p&gt;The interfaces to the latter control system however, have changed considerably over the years. Here&amp;#039;s an old control interface, still in use in many homes (but I would guess no longer manufactured for sale in any new systems today), based on a mechanical rotary &lt;a title="Wikipedia article on time switches" href="http://en.wikipedia.org/wiki/Time_switch"&gt;time switch&lt;/a&gt;.&lt;/p&gt;

&lt;img class="aligncenter" title="Worcester-Bosch timeswitch" src="/assets/2010/timeswitch-38bab9b69cf1891b2237d592490d8731.jpg" alt="A timeswitch interface for a central heating system" width="400" height="377" /&gt;

&lt;p&gt;To set the on/off periods, you move the small plastic tappet switches to either an inner (on) or an outer (off) position. The example here is set to run constantly from 10am to 10pm (hour 10 to hour 22). You can set the current hour simply by turning the inner dial, and you can switch between timed, and constantly on or off with the large switch in the lower right.&lt;/p&gt;

&lt;p&gt;Compare this to a more modern interface, with an LCD display, and multi-function buttons.&lt;/p&gt;

&lt;img class="aligncenter" title="Horstmann H47XL control panel" src="/assets/2010/h47large-62c0810f9de19b4938025a99cc494cb9.jpg" alt="Control panel for a Horstmann H47XL heating programmer" width="400" height="275" /&gt;

&lt;p&gt;These are just some of the buttons on this system. If you slide down the face plate, a further five are revealed which are used for actually modifying the schedule.&lt;/p&gt;

&lt;p&gt;If you compare the manuals for these two systems, the manual for the rotary timeswitch is about as extensive as my description of it above. Just set the time by turning the dial, then put the switches into position corresponding to your desired on and off times, set the thing to timer mode, then go and get on with your life.&lt;/p&gt;

&lt;p&gt;The manual for setting up a schedule on the digital system involves ten pages of A4, including (I kid you not) no less than four flow charts. This system supports per-day scheduling, so to set it you start with the current day, then you&amp;#039;re forced to set up four discreet active periods of heating operation (if you want fewer than four, you have to set some of them back to back). Times are set (and the clock) using Plus, Minus and Enter keys, and when you&amp;#039;re done setting one day you can copy and paste the times (no really) to other days, or set them individually. It&amp;#039;s not hard once you&amp;#039;ve got the hang of it, but even the most basic schedule set up will involve about 20 key presses.&lt;/p&gt;

&lt;p&gt;Now let&amp;#039;s say that winter is passing into spring, but it&amp;#039;s still pretty cold at the start and the end of the day so you want the heating on at those times but off while the sun is radiating you nicely during the day. If you have the timeswitch control, you can reach straight for the day time tappets and nudge them all to the off position with a swipe or two of one finger and then go back to training your cat to make a &lt;a href="http://en.wikipedia.org/wiki/Reuben_sandwich"&gt;Reuben sandwich&lt;/a&gt;, or whatever it is you enjoy doing when you&amp;#039;re not programming central heating systems. If you have the newer digital system, first switch the system to Edit mode, push enter repeatedly until the blinking digits represent the day, and then the time you want to change, then use the Plus and Minus keys to ... well you get the idea.&lt;/p&gt;

&lt;p&gt;The timeswitch interface is perfectly simple, both aesthetically and in terms of functionality. When you come back to it months after setting a programme and needing to make an alteration, you can see at a glance the current schedule on the  familiar clock face layout, and even complete reconfiguration will take a few touches and mere moments. It does have its downsides of course. You don&amp;#039;t get per-day scheduling, and with moving clock parts inside it&amp;#039;s neither as silent nor as reliable as its digital replacement, but its certainly a lot easier to &lt;em&gt;actually use&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;As far interface design goes, we&amp;#039;ve taken a big leap backwards with the digital heating control. When you&amp;#039;re redesigning an interface for anything, please make sure you look at (and use!) what you&amp;#039;re replacing to make sure you&amp;#039;re not throwing away something that did the job far better than your proposed solution. You may have newer technology to hand which makes the old design look antiquated, but there will always be lessons to learn from any design you&amp;#039;re inheriting and hopefully improving upon. Ignore them, and our lives will be full of endless Edit/Run mode, Plus, Minus and Enter button experiences, supplemented by multi-page manuals stuffed with flow charts.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=lq_xnZfSFfw:XQuNbTTRkJw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=lq_xnZfSFfw:XQuNbTTRkJw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=lq_xnZfSFfw:XQuNbTTRkJw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=lq_xnZfSFfw:XQuNbTTRkJw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=lq_xnZfSFfw:XQuNbTTRkJw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=lq_xnZfSFfw:XQuNbTTRkJw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=lq_xnZfSFfw:XQuNbTTRkJw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/lq_xnZfSFfw" height="1" width="1"/&gt;</description>
				<pubDate>Tue, 09 Mar 2010 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/lq_xnZfSFfw/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/redesigning-interfaces-learn-from-the-old-before-you-throw-it-away/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/redesigning-interfaces-learn-from-the-old-before-you-throw-it-away/</feedburner:origLink></item>
		
			<item>
				<title>Why TextMate?</title>
				<description>&lt;p&gt;I&amp;#039;m often asked "what text editor do you use?", and I respond &lt;a href="http://macromates.com"&gt;TextMate&lt;/a&gt;. I&amp;#039;m usually then asked "so, why do you use TextMate?". There&amp;#039;s many reasons I use it, and I&amp;#039;ve heard of developers switching from a PC to a Mac in order to use it. Can a text editor be so good that it can make a developer switch operating systems? Hopefully this article will introduce you to how powerful TextMate is and you can decide for yourself.&lt;/p&gt;&lt;!-- more --&gt;

&lt;!--more--&gt;
&lt;h3&gt;&lt;img src="/assets/2010/TM-ProjectDrawer-0476142b9f0ed01d73dca8c1d792c912.png" alt="" title="Project Drawer" width="257" height="397" class="alignright" /&gt;File Management&lt;/h3&gt;
&lt;p&gt;Lets start at the beginning with file management. As with most &lt;abbr title="Integrated Development Environment"&gt;IDE&lt;/abbr&gt;s you have a list of files and/or folders, and TextMate is no different. This is displayed in the &amp;#039;Project Drawer&amp;#039; (see right). This in itself is nothing special, anyone who has used or seen OSX knows that many apps have these &amp;#039;drawers&amp;#039;. What &lt;em&gt;is&lt;/em&gt; special about TextMate, at least for me, is that there is a much faster way of finding the files you want called &amp;#039;Go to file&amp;#039;, found under the &amp;#039;Navigation&amp;#039; menu or on the keyboard shortcut &lt;span class="kb-shortcut"&gt;⌘T&lt;/span&gt;. This brings up a small dialog window listing all the files in the current project which is filtered as you type. As you can see from the screenshot below, this not only searches for exact matches but also any filename that contains the letters you enter in that order. &lt;/p&gt;

&lt;img src="/assets/2010/TM-GoToFile-db484b8b69eef3597b99cbec9bb1fff1.png" alt="Go To File" width="528" height="213" class="aligncenter" /&gt;

&lt;p&gt;You can scroll through this list with the arrow keys (the first one is selected by default) and select a file to open with the enter key. TextMate will remember which files you have chosen for each entry and weight its list accordingly which allows you to create shortcuts to certain files, for example in nearly all of my Ruby on Rails projects entering &amp;#039;rou&amp;#039; will select the &amp;#039;config/routes.rb&amp;#039; file which has gotten me in to the habit of bringing up the routes file with the following keystrokes &lt;span class="kb-shortcut"&gt;⌘T, R, O, U, ↵&lt;/span&gt;&lt;/p&gt;


&lt;h3&gt;Editing&lt;/h3&gt;

&lt;p&gt;Once you have open the file you want to edit there are numerous ways TextMate can help you get things done quickly. One of my favourite is the ability to edit multiple lines at the same time. For example if you had the following code in your project somewhere and wanted to shorten the name of the variables.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Button_Next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;button.next&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;Button_Prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;button.prev&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;Button_Cancel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;button.cancel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;Button_Finish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;button.finish&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You could use find/replace to replace &amp;#039;Button_&amp;#039; with &amp;#039;btn&amp;#039;, or you could use TextMate&amp;#039;s column selection, hold &lt;span class="kb-shortcut"&gt;⌥&lt;/span&gt; while selecting with the mouse, to select all those columns and overwrite them with &amp;#039;btn&amp;#039; by typing normally.&lt;/p&gt;

&lt;img src="/assets/2010/TM-Column_Selection-ca220baaa19bde916d037e84b5986422.png" alt="Column Selection" width="600" height="174" class="aligncenter7" /&gt;


&lt;p&gt;How many times have you found yourself omitting line-termination semicolons in a language that requires them (JavaScript, Java, PHP, C etc) after a long period of coding in a language that doesn&amp;#039;t (Ruby, Python etc)? TextMate has a great feature which you&amp;#039;ll find very useful called &amp;#039;Edit Each Line in Selection&amp;#039; found under the &amp;#039;Text&amp;#039; menu or the shortcut &lt;span class="kb-shortcut"&gt;⌥⌘A&lt;/span&gt;. Select the text, press the shortcut and then add a semicolon, TextMate will add that character the end of all lines in your selection. Voila!&lt;/p&gt;

&lt;p&gt;Here&amp;#039;s a few more quick time savers&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Generate a &amp;#039;lorem ipsum&amp;#039; paragraph by typing &amp;#039;lorem&amp;#039; and pressing tab&lt;/li&gt;
	&lt;li&gt;From the command line open a folder with the &amp;#039;mate&amp;#039; command&lt;/li&gt;
	&lt;li&gt;Inline/incremental search, activate with &lt;span class="kb-shortcut"&gt;⌃S&lt;/span&gt; and type, press the shortcut again to go to the next match&lt;/li&gt;
	&lt;li&gt;Close the currently open HTML tag with &lt;span class="kb-shortcut"&gt;⌥⌘.&lt;/span&gt;&lt;/li&gt;
	&lt;li&gt;Toggle commenting on the current line/selection with &lt;span class="kb-shortcut"&gt;⌘/&lt;/span&gt;&lt;/li&gt;
	&lt;li&gt;Send a selection of text to &lt;a href="http://pastie.org/"&gt;pastie&lt;/a&gt; or any other application that will accept text with &lt;span class="kb-shortcut"&gt;⌃⌥⇧V&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;img src="/assets/2010/TM-paste_selection_online-fb7b9ef8077942c3a3b6c8f4c51c3ce6.png" alt="Paste Selection Online" height="352" width="330" class="aligncenter"/&gt;

&lt;h3&gt;Bundles&lt;/h3&gt;

&lt;p&gt;Most of the functionality of TextMate is provided by Bundles. These are extensions that add coding snippets, language support or interfaces to command line scripts. TextMate itself comes with over thirty of these bundles supporting a wide variety of programming languages and other features. Most are specific to a particular programming language, but some of them are more general.&lt;/p&gt;

&lt;p&gt;The TODO bundle has just one command &amp;#039;Show TODO List&amp;#039; &lt;span class="kb-shortcut"&gt;⌃⇧T&lt;/span&gt; which scans through all your project files and displays all TODO, FIXME and CHANGED notes in your code with links to the lines within those files. These tags are customisable in the bundle configuration through regular expressions&lt;/p&gt;

&lt;img src="/assets/2010/TM-TODO-31d102ae733821116a1a92dcd30bba20.png" alt="TextMate TODO" width="600" height="339" class="aligncenter" /&gt;

&lt;p&gt;The Math bundle allows you to do quick and simple calculations or base conversions (Dec, Hex &amp; Oct). There&amp;#039;s just one shortcut to remember - &lt;span class="kb-shortcut"&gt;⌃⇧C&lt;/span&gt;. Select some text and press the shortcut keys, this will then present you with the following menu allowing you to select what you want to do  with the selected maths expression. Option 2 (Evaluate and Replace Expression) is extremely useful for replacing inline expressions (ie, 60*60*24) with their evaluated value (86400).&lt;/p&gt;

&lt;img src="/assets/2010/TM-math_bundle-98aa8d2cc7dfd5d703987930b7daee32.png" alt="Math Bundle" class="aligncenter" height="231" width="344"/&gt;

&lt;p&gt;The Blogging bundle allows you to edit blog posts in most blog engines that support the MetaWeblog API, including Wordpress, Movable Type, Drupal and Typo among others. There&amp;#039;s a great screencast over on the &lt;a href="http://blog.macromates.com/2006/blogging-from-textmate/"&gt;TextMate blog&lt;/a&gt; that covers using this bundle.&lt;/p&gt;

&lt;h3&gt;Add-ons&lt;/h3&gt;

&lt;p&gt;As well as these built-in bundles there are a number of third party bundles which can increase your productivity. Just drop any of these .tmbundle packages in to your ~/Library/Application Support/TextMate/Bundles directory.&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://github.com/protocool/ack-tmbundle"&gt;Ack in project&lt;/a&gt; bundle provides just one feature, with the schortcut &lt;span class="kb-shortcut"&gt;⇧⌘A&lt;/span&gt;, a &lt;abbr title="Graphical User Interface"&gt;GUI&lt;/abbr&gt; to the &lt;a href="http://betterthangrep.com/"&gt;ack&lt;/a&gt; command. Ack is much like grep but is aware of programming languages and the files associated with them, so it won&amp;#039;t waste time searching through log files, core dumps or any other files not related to the project.&lt;/p&gt;

&lt;img src="/assets/2010/TM-ack-cf55a0d728d6e8197a90e26614759fdb.png" alt="Ack" class="aligncenter" height="246" width="581"/&gt;

&lt;p&gt;The &lt;a href="http://github.com/johnmuhl/javascript-tools-tmbundle"&gt;Javascript Tools&lt;/a&gt; bundle provides a quick way to run &lt;a href="http://crockford.com/"&gt;Douglas Crockford&lt;/a&gt;&amp;#039;s excellent &lt;a href="http://www.jslint.com/"&gt;JSLint&lt;/a&gt; on your javascript code. By default this runs validation when you save a javascript file with the keyboard shortcut &lt;span class="kb-shortcut"&gt;⌘S&lt;/span&gt; and provides error/warning counts in a small tooltip. It also has a more thorough validation summary available on this shortcut, &lt;span class="kb-shortcut"&gt;⌃⇧V&lt;/span&gt;.&lt;/p&gt;

&lt;img src="/assets/2010/TM-jslint-24a3dc1aab7422bce26aa79aace07209.png" alt="Jslint" class="aligncenter" height="43" width="200"/&gt;

&lt;p&gt;This &lt;a href="http://github.com/jcf/git-tmbundle"&gt;Git&lt;/a&gt; bundle adds a great simple menu on the &lt;span class="kb-shortcut"&gt;⌃⇧G&lt;/span&gt; shortcut for working with git repos. You&amp;#039;re all using &lt;a href="http://whygitisbetterthanx.com/"&gt;git&lt;/a&gt; right ?&lt;/p&gt;

&lt;p&gt;Finally the &lt;a href="http://code.google.com/p/zen-coding/"&gt;Zen Coding&lt;/a&gt; bundles (Zen CSS &amp; Zen HTML) add loads and loads of snippets to make working with CSS/HTML much quicker. &lt;a href="http://twitter.com/boagworld"&gt;Paul Boag&lt;/a&gt; gives a nice review of this over on the &lt;a href="http://boagworld.com/reviews/zen-coding"&gt;boagworld&lt;/a&gt; website.&lt;/p&gt;

&lt;p&gt;If you have any must-have bundles, please drop a link to them in the comments and I&amp;#039;ll check them out.&lt;/p&gt;

&lt;h3&gt;Is that it?&lt;/h3&gt;

&lt;p&gt;These are only the features that I use regularly without looking up and just touch the surface of what is possible with TextMate. If you want to find out more about what you can do with TextMate I&amp;#039;d highly recommend "TextMate: Power Editing for the Mac" by James Edward Gray II (buy it from &lt;a href="http://www.amazon.co.uk/gp/product/097873923X?ie=UTF8&amp;tag=neutrcreat-21&amp;linkCode=as2&amp;camp=1634&amp;creative=19450&amp;creativeASIN=097873923X"&gt;amazon&lt;/a&gt;). It has almost 200 pages of great information, including expert features such as creating your own bundles.&lt;/p&gt;

&lt;p&gt;As always if you have any thoughts or criticism, either positive or negative, please leave a message in the comments.&lt;/p&gt;

&lt;cite&gt;Photo credit: &lt;a href="http://www.flickr.com/photos/batega/1596898776/"&gt;batega&lt;/a&gt;&lt;/cite&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=GaB9zs6vOCQ:gLxXdou2hKs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=GaB9zs6vOCQ:gLxXdou2hKs:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=GaB9zs6vOCQ:gLxXdou2hKs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=GaB9zs6vOCQ:gLxXdou2hKs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=GaB9zs6vOCQ:gLxXdou2hKs:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=GaB9zs6vOCQ:gLxXdou2hKs:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=GaB9zs6vOCQ:gLxXdou2hKs:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/GaB9zs6vOCQ" height="1" width="1"/&gt;</description>
				<pubDate>Wed, 17 Feb 2010 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/GaB9zs6vOCQ/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/why-textmate/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/why-textmate/</feedburner:origLink></item>
		
			<item>
				<title>The value of remarkable customer experiences</title>
				<description>&lt;p&gt;A couple of weeks ago I was out for a friend&amp;#039;s birthday, in a bar called &lt;a href="http://www.thelexington.co.uk/"&gt;The Lexington&lt;/a&gt;. It was a Saturday night, so we arrived early so we could order some food before the place got too crowded. After placing our order, 45 minutes passed, and I let it get to a whole hour before I queried the order at the bar. They apologised and said that the kitchen was really busy, and indeed the bar was getting busier by the minute with a few tables placing orders for lots of food.&lt;/p&gt;&lt;!--more--&gt;

&lt;p&gt;After about an hour and a half in total since placing the order, I went to the bar, by now intending to cancel the order and ask for a refund. I was promptly told that "due to the unusual circumstances, the kitchen has been closed." That&amp;#039;s right, they closed the kitchen, and didn&amp;#039;t notify people who had unserved orders. I was refunded, but with barely an apology or any offer of compensation like a free or discounted meal on another occasion. So that&amp;#039;s the last time I&amp;#039;ll be going back to The Lexington, and I&amp;#039;ll be sure to mention this story to anyone who asks about that bar, or for a recommendation for a bar in that area, and tell them to avoid it like the plague.&lt;/p&gt;

&lt;p&gt;After leaving The Lexington, by now beyond mere hunger, we stepped into a burrito shop a short walk away called &lt;a href="http://www.chilango.co.uk/"&gt;Chilango&lt;/a&gt;. The guy who served us looked exhausted (it was only ten minutes before they closed at midnight after what must be one of their peak times for the week), but he greeted us like long lost friends, asked if we&amp;#039;d been before and offered to explain the ordering process when we said it was our first time there. As we ordered he made welcome suggestions about which options would work best with what we&amp;#039;d picked so far, introducing ingredients like they were members of his band, and their origins.&lt;/p&gt;

&lt;p&gt;While we were eating, another member of staff asked if we&amp;#039;d mind filling in a feedback card as they were new and wanted to know how they were being received. I gladly obliged, unable to think of anything to criticise, but I suggested that they offer a some varieties of salsa instead of just one (hey it was late, give me a break). When we left, we were thanked by all the staff at once, and the guy who was wiping tables near the door told us to enjoy the rest of our weekend.&lt;/p&gt;

&lt;p&gt;Fast forward a week, and I get an email back from someone at Chilango, personally thanking me for my feedback and saying how much it means to them to hear that I enjoyed the atmosphere and the friendliness of their staff. They even said they&amp;#039;d look into finding some other kinds of salsa. As a result of that email and my remarkable experience, I&amp;#039;m writing this blog post which you&amp;#039;re now reading, and I&amp;#039;d urge you to go and check them out if you&amp;#039;re near one of their locations in the UK (London&amp;#039;s Upper Street and Fleet Street, and Sheffield&amp;#039;s Meadowhall, at the time of writing).&lt;/p&gt;

&lt;p&gt;Do you see the difference there?&lt;/p&gt;

&lt;p&gt;If you&amp;#039;re in charge of selling a product or service, whether you own it or if you&amp;#039;re just the person on the front lines talking to customers, please take a good look at the experience your customers are getting. You might be able to get by offering a mediocre experience if you&amp;#039;re hosting live music and serving niche products like The Lexington (they specialise in bourbons and American whiskey), but wouldn&amp;#039;t you rather provide an experience that&amp;#039;s going to inspire your customers to write blog posts like this one, and tell all their friends about you? Which approach is going to grow you a customer base of fans instead of regular patrons?&lt;/p&gt;

&lt;p&gt;If you want to know more about remarkable experiences and why they&amp;#039;re so important, you need to read the works of &lt;a title="Seth Godin on Wikipedia" href="http://en.wikipedia.org/wiki/Seth_Godin"&gt;Seth Godin&lt;/a&gt; - especially &lt;a href="http://www.amazon.co.uk/gp/product/014101640X?ie=UTF8&amp;tag=neutrcreat-21&amp;linkCode=as2&amp;camp=1634&amp;creative=19450&amp;creativeASIN=014101640X"&gt;Purple Cow&lt;/a&gt;, but really &lt;a href="http://www.amazon.co.uk/gp/redirect.html?ie=UTF8&amp;location=http%3A%2F%2Fwww.amazon.co.uk%2Fs%3Fie%3DUTF8%26redirect%3Dtrue%26search-type%3Dss%26index%3Dbooks-uk%26field-author%3DSeth%2520Godin&amp;tag=neutrcreat-21&amp;linkCode=ur2&amp;camp=1634&amp;creative=19450"&gt;everything he&amp;#039;s written&lt;/a&gt;, including &lt;a title="Seth Godin&amp;#039;s blog" href="http://sethgodin.typepad.com/"&gt;his blog&lt;/a&gt;.&lt;/p&gt;

&lt;cite&gt;Photo credit: &lt;a href="http://www.flickr.com/photos/striatic/2192192956/"&gt;Striatic&lt;/a&gt;&lt;/cite&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=PE7k9gzFtjE:r-PLlfuV3G0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=PE7k9gzFtjE:r-PLlfuV3G0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=PE7k9gzFtjE:r-PLlfuV3G0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=PE7k9gzFtjE:r-PLlfuV3G0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=PE7k9gzFtjE:r-PLlfuV3G0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=PE7k9gzFtjE:r-PLlfuV3G0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=PE7k9gzFtjE:r-PLlfuV3G0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/PE7k9gzFtjE" height="1" width="1"/&gt;</description>
				<pubDate>Fri, 12 Feb 2010 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/PE7k9gzFtjE/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/the-value-of-remarkable-customer-experiences/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/the-value-of-remarkable-customer-experiences/</feedburner:origLink></item>
		
			<item>
				<title>Send contact form submissions to Highrise</title>
				<description>&lt;p&gt;While adding a few features the back-end of our contact form logic I had an interesting thought; Wouldn&amp;#039;t it be nice if these messages not only arrived in our email mailbox, but were also automatically entered into our &lt;a href="http://highrisehq.com"&gt;Highrise&lt;/a&gt; account. Highrise is a web-based &lt;abbr title="Customer Relationship Manager"&gt;CRM&lt;/abbr&gt; from &lt;a href="http://37signals.com"&gt;37signals&lt;/a&gt; which we use for tracking contacts and project leads.&lt;/p&gt;&lt;!-- moe --&gt;

&lt;p&gt;When the contact form is submitted, we do a little processing (with &lt;a href="http://php.net"&gt;PHP&lt;/a&gt;) and then fire off an email to our contact address as normal. Highrise supports forwarding emails to a &amp;#039;dropbox&amp;#039; address (&lt;a title="Highrise help article on dropboxes" href="http://highrisehq.com/help/email#dropbox_address_explanation"&gt;more on dropboxes&lt;/a&gt;) which will then be imported as a message, creating the original sender as a new contact, or attaching it to an existing one if the email address matches with one of your contacts. All we have to do is add an additional step to reformat the email as a forwarded message, and send it to our dropbox address. Here&amp;#039;s the code we use:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="php"&gt;&lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;New Contact Form Submission&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// build message body&lt;/span&gt;
&lt;span class="sd"&gt;/***** &amp;lt;snip&amp;gt; ****/&lt;/span&gt;

&lt;span class="c1"&gt;// sanitise email/name&lt;/span&gt;
&lt;span class="nv"&gt;$name_safe&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mail_header_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;contactName&amp;amp;#039;]);&lt;/span&gt;
&lt;span class="nv"&gt;$email_safe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mail_header_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;email&amp;amp;#039;]);&lt;/span&gt;

&lt;span class="c1"&gt;// send to our contact list&lt;/span&gt;
&lt;span class="nb"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$contact_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;Contact Form&amp;amp;#039;, $body, &amp;quot;From: $contact_addr\r\nReply-to: \&amp;quot;$name_safe\&amp;quot; &amp;lt; $email_safe&amp;gt;\r\n&amp;quot;);&lt;/span&gt;

&lt;span class="c1"&gt;// build highrise body&lt;/span&gt;
&lt;span class="nv"&gt;$new_body&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;---------- Forwarded message ----------&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$new_body&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;From: &lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;$name_safe&lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt; &lt;/span&gt;&lt;span class="si"&gt;$email_safe&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$new_body&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// send to highrise&lt;/span&gt;
&lt;span class="nb"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dropbox_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;Fwd: Contact Form&amp;amp;#039;, $new_body, &amp;quot;From: $contact_addr&amp;quot;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will also need to make sure that the &lt;strong&gt;from address&lt;/strong&gt; in these emails (&lt;span class="highlight"&gt;&lt;span class="x"&gt;$contact_addr&lt;/span&gt;&lt;/span&gt; in our code) is registered to a user on your Highrise account, otherwise the message will be discarded by the Highrise importing process. See the &lt;a href="http://highrisehq.com/help/email#who_can_send_emails"&gt;Highrise help page&lt;/a&gt; for more information on this.&lt;/p&gt;

&lt;p&gt;As usual, if you&amp;#039;ve found this useful or have any improvement please leave a comment!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=YInoaIC5pi8:YqWU_drfeFc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=YInoaIC5pi8:YqWU_drfeFc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=YInoaIC5pi8:YqWU_drfeFc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=YInoaIC5pi8:YqWU_drfeFc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=YInoaIC5pi8:YqWU_drfeFc:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=YInoaIC5pi8:YqWU_drfeFc:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=YInoaIC5pi8:YqWU_drfeFc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/YInoaIC5pi8" height="1" width="1"/&gt;</description>
				<pubDate>Tue, 09 Feb 2010 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/YInoaIC5pi8/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/send-contact-form-submissions-to-highrise/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/send-contact-form-submissions-to-highrise/</feedburner:origLink></item>
		
			<item>
				<title>How to track traffic exiting your site</title>
				<description>&lt;p&gt;Thanks to the great work Google have been putting in, we can now gather an enormous amount of information about our site visitors for free using &lt;a title="Google Analytics" href="http://google.com/analytics" target="_blank"&gt;Google Analytics&lt;/a&gt;. One of the metrics that I have always been curious about is the 'Exit Page'. Sadly, all visitors do eventually leave our sites and this metric shows what was the last page they viewed before leaving. Even more sadly, that's as far as we can get with a basic Google Analytics deployment. Wouldn't it be great if we could see where our visitors went after leaving our site?&lt;/p&gt;&lt;!-- more --&gt;
&lt;p&gt;Well, with a little bit of javascript magic we can.&lt;/p&gt;

&lt;h3&gt;What you'll need on your site&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Google Analytics (I'm using the new &lt;a href='http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html'&gt;Asynchronous Tracking&lt;/a&gt; code but with a little modification it'll work with the standard tracking code)&lt;/li&gt;
&lt;li&gt;&lt;a href='http://jquery.com'&gt;jQuery&lt;/a&gt; (again with some modification this can be done without, but you're all &lt;a href="http://usejquery.com/"&gt;using jQuery&lt;/a&gt; anyway right?)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;How do we do it&lt;/h3&gt;
&lt;p&gt;We're going to be using the &lt;a href="http://code.google.com/apis/analytics/docs/tracking/eventTrackerOverview.html"&gt;Event Tracking&lt;/a&gt; functionality of Google Analytics to track whenever someone follows an external link on our site. Firstly we need a way of determining which links are actually external. We could add additional classes to any links you want to track or we can use a little jQuery tickery to determine that automatically.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Haven't used jQuery before? Don't know what on earth the &lt;span class="highlight"&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt; function is? Check out the &lt;a href="http://docs.jquery.com/Tutorials:Getting_Started_with_jQuery"&gt;getting started guide&lt;/a&gt;, you'll thank me later&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^mailto:/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;&lt;cite&gt;&lt;a href='http://jquery-howto.blogspot.com/2009/06/find-select-all-external-links-with.html'&gt;jQuery Howto&lt;/a&gt;&lt;/cite&gt;&lt;/div&gt;

&lt;p&gt;This adds the :external filter allowing us to use &lt;span class="highlight"&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a:external&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt; to select external links.&lt;/p&gt;

&lt;p&gt;All we need to do now is to use this selector to track events when these links are clicked. The &lt;a href="http://code.google.com/apis/analytics/docs/tracking/eventTrackerGuide.html"&gt;documentation&lt;/a&gt; shows that the &lt;span class="highlight"&gt;&lt;span class="nx"&gt;_trackEvent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt; method takes four parameters; Category, Action, Label and Value. Of these four, only the Category and Action are mandatory. We'll be using the optional Label parameter to pass the URL of the external site back to Google Analytics.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a:external&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Outbound&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Link&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Taking it further&lt;/h3&gt;

&lt;p&gt;We're using a Category of 'Outbound', as these events are all related to off-site events, and an Action of 'Link' as we're looking at links. We can expand on this using a little more jQuery to change this Action based on some conditions, for example if the link is within another element, if it has a particular class or if the url contains a string.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a:external&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Link&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;closest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ul.comments&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Comment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;amazon&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Affiliate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/pastrami/i&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Tasty&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Outbound&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You could also this technique for other similar events, that might not otherwise be tracked by Google Analytics, such as for downloads&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="c1"&gt;// track downloads of Acrobat and ZIP files&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a:not(:external)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\.(pdf|zip)$/i&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastIndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackEvent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Download&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;All done&lt;/h3&gt;
&lt;p&gt;Once that is all in place and you've had some events tracked by Google Analytics you'll be able to see the events showing up in the reports. Here's a shot of the events shortly after the launch of the &lt;a href="http://neutroncreations.com"&gt;Neutron Creations&lt;/a&gt; site on the 19th January 2010.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/2010/AnalyticsEvents-77fc5e7e602c3ad21c6f88d9c43a07cd.png" alt="" title="Analytics Events" width="600" height="386" class="aligncenter" /&gt;&lt;/p&gt;

&lt;p&gt;So that's it. Now you can track where your visitors are going when they eventually leave your site. If you have any improvements or other uses for this sound off about them in the comments.&lt;/p&gt;
&lt;div&gt;&lt;cite&gt;Photo: &lt;a href='http://www.flickr.com/photos/22741426@N05/2373060918/'&gt;lansakit&lt;/a&gt;&lt;/cite&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=nh9V_TXD6Co:KJCmg3z2PQ4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=nh9V_TXD6Co:KJCmg3z2PQ4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=nh9V_TXD6Co:KJCmg3z2PQ4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=nh9V_TXD6Co:KJCmg3z2PQ4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=nh9V_TXD6Co:KJCmg3z2PQ4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=nh9V_TXD6Co:KJCmg3z2PQ4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=nh9V_TXD6Co:KJCmg3z2PQ4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/nh9V_TXD6Co" height="1" width="1"/&gt;</description>
				<pubDate>Wed, 20 Jan 2010 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/nh9V_TXD6Co/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/how-to-track-traffic-exiting-your-site/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/how-to-track-traffic-exiting-your-site/</feedburner:origLink></item>
		
			<item>
				<title>New year, new site</title>
				<description>&lt;p&gt;As the dust settles on 2009, everyone is busy putting their plans for 2010 into action. For us, we&amp;#039;re kicking our plans off with the unveiling of something we&amp;#039;ve been planning and building for a little while now - a &lt;a href="http://neutroncreations.com"&gt;new website&lt;/a&gt; for Neutron Creations.&lt;/p&gt;&lt;!-- more --&gt;

&lt;p&gt;Our first site, designed by Tim Van Damme, was built to communicate a small amount of information about who we were and what we did. The resulting one page design turned a lot of heads for its striking appearance, and it fulfilled its duty brilliantly by bringing us some of our first clients. As we found our feet with the business and completed our first projects, we knew it was time to expand the site into something more detailed. Looking back, it&amp;#039;s clear that we owe a lot to Tim&amp;#039;s design, and it&amp;#039;s with mixed feelings that we move it to our &lt;a title="Neutron Creations - original site" href="http://rewind.neutroncreations.com/2009/"&gt;little museum&lt;/a&gt; where it can be admired for years to come.&lt;/p&gt;

&lt;p&gt;For the new site, we approached &lt;a href="http://elliotjaystocks.com"&gt;Elliot Jay Stocks&lt;/a&gt; who we&amp;#039;d already worked with, and told him we wanted something unconventional that didn&amp;#039;t abandon the feel of the original site completely. It took us some time to get used to the radical ideas he came out with, but we believe he&amp;#039;s really outdone himself and we&amp;#039;re over the moon with our new home on the web. We&amp;#039;d love to hear what you think too, so please leave us your feedback in the comments, especially if you&amp;#039;ve spotted any teething problems. The new site is also home to our portfolio (which we&amp;#039;ll be adding to soon) and a bit more blurb about what we&amp;#039;re all about, as well as the blog you&amp;#039;re reading now which we&amp;#039;ve dubbed The Particle Lab.&lt;/p&gt;

&lt;p&gt;Our lab techs will bring you news on what we&amp;#039;re up to, and will also share plenty of ideas, techniques and tricks we discover as we continue building web applications for ourselves and for our clients. We&amp;#039;ll be talking about anything that pops into our heads that we think someone might find useful, from snippets of code from our weapons of choice such as Ruby (on Rails), HTML, CSS and jQuery, but also less technical topics such as our views on the state of our industry, or ideas for marketing your products and services. One thing we can guarantee is that there will be absolutely no top ten lists of any kind. On that you have our solemn word.&lt;/p&gt;

&lt;p&gt;So please subscribe to this channel via &lt;a title="The Particle Lab feed" href="http://feeds.feedburner.com/NeutronCreationsBlog"&gt;our feed&lt;/a&gt; or &lt;a title="The Particle Lab email update service" href="http://feedburner.google.com/fb/a/mailverify?uri=NeutronCreationsBlog&amp;loc=en_US"&gt;email&lt;/a&gt;, follow us on &lt;a title="Neutron Creations on Twitter" href="http://twitter.com/neutronuk"&gt;Twitter&lt;/a&gt;, or just stop by once in a while for the latest post. We look forward to welcoming you back here soon.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=S6S3kVDcMSo:ZJRtp1v8ag8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=S6S3kVDcMSo:ZJRtp1v8ag8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=S6S3kVDcMSo:ZJRtp1v8ag8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=S6S3kVDcMSo:ZJRtp1v8ag8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=S6S3kVDcMSo:ZJRtp1v8ag8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?i=S6S3kVDcMSo:ZJRtp1v8ag8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?a=S6S3kVDcMSo:ZJRtp1v8ag8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/NeutronCreationsBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NeutronCreationsBlog/~4/S6S3kVDcMSo" height="1" width="1"/&gt;</description>
				<pubDate>Thu, 14 Jan 2010 00:00:00 +0000</pubDate>
				<link>http://feedproxy.google.com/~r/NeutronCreationsBlog/~3/S6S3kVDcMSo/</link>
				<guid isPermaLink="false">http://new.theparticlelab.com/new-year-new-site/</guid>
			<feedburner:origLink>http://new.theparticlelab.com/new-year-new-site/</feedburner:origLink></item>
		
	</channel>
</rss>
