<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8124478401700361166</id><updated>2026-05-07T22:55:32.666-04:00</updated><category term="web dev"/><category term="JavaScript"/><category term="AngularJS"/><category term="Robot Chicken"/><category term="Star Wars"/><category term="bash"/><category term="Chrome"/><category term="Firefox"/><category term="HTML5"/><category term="OSX"/><category term="Page Visibility"/><category term="alias"/><category term="grep"/><category term="prerender"/><category term="reporting"/><category term="unix"/><title type='text'>The traveling hermit</title><subtitle type='html'>These are my musings, understandings and opinions on mainly tech.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>17</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-4233014527679143099</id><published>2013-08-15T12:00:00.000-04:00</published><updated>2013-08-23T12:52:34.518-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AngularJS"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="web dev"/><title type='text'>AngularJS Missing Directives: Native Form Resets Part 2</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;This is a followup to a &lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-missing-directives-native.html&quot; target=&quot;_blank&quot;&gt;previous post&lt;/a&gt; discussing a directive that supports native form resets. While that directive goes a long way for simplifying form resets with &lt;a href=&quot;http://angularjs.org/&quot; target=&quot;_blank&quot;&gt;AngularJS&lt;/a&gt;, it does have a glaring shortcoming. What if a form utilizes multiple models? AngularJS makes no judgement about this and actually there can be various reasons for why that is beneficial.&lt;br /&gt;
&lt;br /&gt;
The previous solution expected to have one and only one model. In fact it should throw an error if more than one model is passed. Here is the original code from the original reset directive.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;600&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/4fcCm/embedded/js&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt; &lt;br /&gt;
&lt;br /&gt;
To support multiple models in an updated directive lets first make a checklist for what would need to be updated.&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;A string format to pass in multiple models through the form attribute&lt;/li&gt;
&lt;li&gt;Create getter/setters for each model&lt;/li&gt;
&lt;li&gt;Create a master copy of each model&lt;/li&gt;
&lt;li&gt;Set each model to its respective master copy when the reset event fires&lt;/li&gt;
&lt;/ul&gt;The attribute string format is simple enough since there is an established format for specifying a list, namely comma separated. But since this list is also an &lt;code&gt;HTML&lt;/code&gt; attribute, it should include the format supported by lists contained in &lt;code&gt;HTML&lt;/code&gt; attributes, namely space separated (think CSS class attributes). This way either format will work if favored by a designer or a developer.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;250&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/RjU8Y/embedded/html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt; &lt;br /&gt;
&lt;br /&gt;
Since the attribute string will be a list, the directive will be dealing with an array of models to iterate over for creating both the getter/setters and the master copies of each model. Also it will iterate over the setters to set the corresponding models to their master copy during a reset event.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;900&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/RjU8Y/embedded/js&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt; &lt;br /&gt;
&lt;br /&gt;
And low and behold the result. It works! &lt;i&gt;It would have been a bit awkward if it hadn&#39;t.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
In the end there was nothing very tricky about it but it can be extremely useful.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;200&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/RjU8Y/embedded/result,js,html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;NOTE&lt;/b&gt;: I borrowed the &lt;code&gt;map&lt;/code&gt; function from the &lt;a href=&quot;http://code.angularjs.org/1.1.5/angular.js&quot; target=&quot;_blank&quot;&gt;AngularJS source&lt;/a&gt; since &lt;a href=&quot;http://caniuse.com/#search=map&quot; target=&quot;_blank&quot;&gt;IE 8 does not support Array.prototype.map&lt;/a&gt;. Once again the source code proves it&#39;s a great place to pick up some tips and tricks.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/4233014527679143099/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2013/08/angularjs-missing-directives-native_15.html#comment-form' title='28 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/4233014527679143099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/4233014527679143099'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2013/08/angularjs-missing-directives-native_15.html' title='AngularJS Missing Directives: Native Form Resets Part 2'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>28</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-4098606195414257632</id><published>2013-08-14T11:13:00.001-04:00</published><updated>2013-08-14T11:15:29.984-04:00</updated><title type='text'>AngularJS Missing Directives: Focus &amp; Blur Part 2</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In the &lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-missing-directives-focus-blur.html&quot; target=&quot;_blank&quot;&gt;first part of this series&amp;nbsp;&lt;/a&gt;about supporting &lt;code&gt;focus&lt;/code&gt; and &lt;code&gt;blur&lt;/code&gt; events I elected to follow the template in the &lt;a href=&quot;http://angularjs.org/&quot; target=&quot;_blank&quot;&gt;AngularJS&lt;/a&gt; source code for event directives. This proved to be quite insightful as the AngularJS team just released &lt;a href=&quot;http://blog.angularjs.org/2013/08/angularjs-120-rc1-spooky-giraffe.html&quot; target=&quot;_blank&quot;&gt;1.2.0rc1&lt;/a&gt;&amp;nbsp;which now supports these events natively in the same manner. This means you can just drop in the new version, remove the custom directives and any dependencies on these directives should still work! &lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-120rc1-now-supports-focus.html&quot; target=&quot;_blank&quot;&gt;Check it out&lt;/a&gt; for yourself!&lt;br /&gt;
&lt;br /&gt;
But to illustrate this point even further, lets continue with a previous discussion from the first part of this series. Namely the discussion about which adverb should be used for these event directives, &quot;on&quot; or &quot;while&quot;. In the first part the directives used the &quot;on&quot; approach for each event. But this doesn&#39;t mean we still can&#39;t support &quot;while&quot;. In fact, we already did in the original example by using both &quot;on&quot; events and storing the state in a scoped variable, &lt;code&gt;focused&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
We can extrapolate that concept and create a directive that will do this automatically. There are a few ways as always to skin this cat. We could simply just bind the events independently of the focus and blur directives, but that isn&#39;t much fun. Alternatively we can use those directives (no need to reinvent the wheel) and just build on top of them. The later approach will also lead us down a new path which will include learning about a couple of very cool tools. Plus, it&#39;s more fun.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;NOTE&lt;/b&gt;: This approach does have one possible issue: without specifying different scope variables to use between various elements within the same scope, they will of course share the same state indicator which would be very confusing. So, basically dumb in equals dumb out.&lt;br /&gt;
&lt;br /&gt;
Okay, on to the fun! First give it a try so you know I am not full of it.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;200&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/d3P2a/embedded/result,js,html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Boom! Still works. Now lets dig in. The first difference is the new directive &lt;code&gt;myng-focused&lt;/code&gt; has replaced both &lt;code&gt;myng-focus&lt;/code&gt; and &lt;code&gt;myng-blur&lt;/code&gt;&amp;nbsp;on the element. Also, only the scope variable &lt;code&gt;focused&lt;/code&gt; is passed now and not an AngularJS expression. Once last warning, remember to specify unique scope variables to track states for different elements unless you want to have a field day tracking down that bug.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;150&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/d3P2a/embedded/html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Now for the juicy parts but if you need to review how the previous directives work, go take a look &lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-missing-directives-focus-blur.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;myngFocused&lt;/code&gt; directive is basically doing four things.&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Getting the scope variable name from the attribute&lt;/li&gt;
&lt;li&gt;Removing the attribute (this is important due to number 4)&lt;/li&gt;
&lt;li&gt;Sets the attributes for the &lt;code&gt;focus&lt;/code&gt; and &lt;code&gt;blur&lt;/code&gt; directives&lt;/li&gt;
&lt;li&gt;Recompiles the element&lt;/li&gt;
&lt;/ol&gt;
Steps 1-3 are fairly straightforward although steps 2 &amp;amp; 3 do introduce &lt;a href=&quot;http://docs.angularjs.org/api/ng.$compile.directive.Attributes&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;$set&lt;/code&gt;&lt;/a&gt;. The trick here is that if no value is specified, the attribute is removed. Useful for step 4.&lt;br /&gt;
&lt;br /&gt;
Step 4 is the key. Without it, the &lt;code&gt;HTML&lt;/code&gt; is updated but AngularJS takes no notice of the new attributes on the element. That is where &lt;a href=&quot;http://docs.angularjs.org/api/ng.$compile&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;$compile&lt;/code&gt;&lt;/a&gt; comes into play. It&#39;s a fairly powerful and useful service in the AngularJS world and used quite extensively in the source code. We use it here to force Angular to recompile the element which triggers it to recognize the new directives, &lt;code&gt;myngFocus&lt;/code&gt; and &lt;code&gt;myngBlur&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;IMPORTANT!&lt;/b&gt; This is where removing the directive for &lt;code&gt;myngFocused&lt;/code&gt; is crucial. If we omitted this step, when the element is recompiled AngularJS would see that directive and would attempt to initiate it. This would put it into a loop. Bad. It&#39;s why nearly all examples only use &lt;code&gt;$compile&lt;/code&gt; on children of an element. Consider yourself warned.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;550&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/d3P2a/embedded/js&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
And there you go. It should really be apparent now how directives are the building blocks of AngularJS.&lt;br /&gt;
&lt;br /&gt;
Finally, as promised, here is the same directive using the new hotness of 1.2.0rc1. As you can see the directive is identical aside from using the new native &lt;code&gt;focus&lt;/code&gt; and &lt;code&gt;blur&lt;/code&gt; directives.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;200&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/7CvxZ/embedded/result,js,html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/4098606195414257632/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2013/08/angularjs-missing-directives-focus-blur_14.html#comment-form' title='26 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/4098606195414257632'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/4098606195414257632'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2013/08/angularjs-missing-directives-focus-blur_14.html' title='AngularJS Missing Directives: Focus &amp; Blur Part 2'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>26</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-8952939511767024061</id><published>2013-08-14T09:26:00.001-04:00</published><updated>2013-08-14T10:12:03.671-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AngularJS"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="web dev"/><title type='text'>AngularJS 1.2.0rc1 Now Supports Focus &amp; Blur Events</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
A quick update on a &lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-missing-directives-focus-blur.html&quot; target=&quot;_blank&quot;&gt;previous post&lt;/a&gt; about &lt;code&gt;focus&lt;/code&gt; and &lt;code&gt;blur&lt;/code&gt; directives. The &lt;a href=&quot;http://angularjs.org/&quot; target=&quot;_blank&quot;&gt;AngularJS&lt;/a&gt; team has released &lt;a href=&quot;http://blog.angularjs.org/2013/08/angularjs-120-rc1-spooky-giraffe.html&quot; target=&quot;_blank&quot;&gt;1.2.0rc1&lt;/a&gt;, which among a host of great changes now supports focus and blur events natively.&lt;br /&gt;
&lt;br /&gt;
The best thing is since I used the existing event code from the AngularJS source code as a template for these directives there is no need to change any code that utilized these methods! You will see another example of this in a &lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-missing-directives-focus-blur_13.htm&quot; target=&quot;_blank&quot;&gt;post&lt;/a&gt; that I am editing that was scheduled to be posted later today.&lt;br /&gt;
&lt;br /&gt;
Here is the updated example from my previous post with the only modification being the switch to use the native &lt;code&gt;focus&lt;/code&gt; and &lt;code&gt;blur&lt;/code&gt; directives in 1.2.0rc1.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;100&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/TwrSZ/embedded/result,js,html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;150&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/TwrSZ/embedded/html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;100&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/TwrSZ/embedded/js&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Once again using the source code as a guide and a reference has proven to be both enlightening and also a savior since I won&#39;t have to make any extensive edits to switch to the native event support in the new release.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/8952939511767024061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2013/08/angularjs-120rc1-now-supports-focus.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/8952939511767024061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/8952939511767024061'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2013/08/angularjs-120rc1-now-supports-focus.html' title='AngularJS 1.2.0rc1 Now Supports Focus &amp; Blur Events'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-6906824437570287078</id><published>2013-08-12T10:03:00.000-04:00</published><updated>2013-08-26T12:47:53.032-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AngularJS"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="web dev"/><title type='text'>AngularJS Missing Directives: Focus &amp; Blur</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;After my last post discussing the missing&amp;nbsp;&lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-missing-directives-native.html&quot; target=&quot;_blank&quot;&gt;native form reset directive&lt;/a&gt;, I was asked what other missing directives there might be. Which is a great question.&amp;nbsp;&lt;a href=&quot;http://angularjs.org/&quot; target=&quot;_blank&quot;&gt;AngularJS&lt;/a&gt;&amp;nbsp;provides so many great directives out of the box, what could it possibly be missing?&lt;br /&gt;
&lt;br /&gt;
Well, lets start with simple user input events. &lt;a href=&quot;http://code.angularjs.org/1.1.5/angular.js&quot; target=&quot;_blank&quot;&gt;AngularJS 1.1.5&lt;/a&gt; supports the following events as &lt;a href=&quot;http://docs.angularjs.org/guide/directive&quot; target=&quot;_blank&quot;&gt;ng-directives&lt;/a&gt;:&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngClick&quot; target=&quot;_blank&quot;&gt;click&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngDblclick&quot; target=&quot;_blank&quot;&gt;dblclick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngMousedown&quot; target=&quot;_blank&quot;&gt;mousedown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngMouseup&quot; target=&quot;_blank&quot;&gt;mouseup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngMouseover&quot; target=&quot;_blank&quot;&gt;mouseover&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngMouseout&quot; target=&quot;_blank&quot;&gt;mouseout&lt;/a&gt;&amp;nbsp;&lt;i&gt;* apparently this one is not documented yet&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngMousemove&quot; target=&quot;_blank&quot;&gt;mousemove&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngMouseenter&quot; target=&quot;_blank&quot;&gt;mouseenter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngMouseleave&quot; target=&quot;_blank&quot;&gt;mouseleave&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngKeydown&quot; target=&quot;_blank&quot;&gt;keydown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngKeyup&quot; target=&quot;_blank&quot;&gt;keyup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngKeypress&quot; target=&quot;_blank&quot;&gt;keypress&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;i&gt;* There is of course also &lt;a href=&quot;http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngSubmit&quot; target=&quot;_blank&quot;&gt;ng-submit&lt;/a&gt;, which was mentioned in the &lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-missing-directives-native.html&quot; target=&quot;_blank&quot;&gt;last post&lt;/a&gt;.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
While that list is pretty extensive there are two glaring omissions, namely blur and focus events. Luckily these can be easily added as you will see below.&lt;br /&gt;
&lt;br /&gt;
Since there is nothing uniquely special about these two events compared to the list above, we can use the AngularJS &lt;a href=&quot;http://code.angularjs.org/1.1.5/angular.js&quot; target=&quot;_blank&quot;&gt;source code&lt;/a&gt;&amp;nbsp;as a template for adding two directives to support these events. The bonus for doing that is extrapolating and slightly modifying the code for our goals is quick and painless. Lesson to be learned: always look to the source for tips and guidance.&lt;br /&gt;
&lt;br /&gt;
First, a note discussing this approach. I have read various development threads discussing how a focus and/or a blur directive should work. The main debate seems to boil down to a disagreement between essentially adverbs. Specifically &#39;on&#39; and &#39;while&#39;. The latter assigns a state indicator that can be monitored to determine if an element is currently blurred or focused. The former, which is the approach the following example will use, follows the native browser event listeners for firing when an event occurs.&lt;br /&gt;
&lt;br /&gt;
Got it? Good. Lets jump in then. Click on the input element to give it focus and then click outside of it to blur it.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;200&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/dAkNb/embedded/result,js,html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
It works, but how? Focusing on the &lt;code&gt;HTML&lt;/code&gt; tab we can see the directives are simply passing an &lt;a href=&quot;http://docs.angularjs.org/guide/expression&quot; target=&quot;_blank&quot;&gt;AngularJS expression&lt;/a&gt; that sets the scope variable &lt;code&gt;focused&lt;/code&gt; to either&amp;nbsp;&lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; if the input element is focused or blurred respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;150&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/dAkNb/embedded/html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Looking at the directive code on the &lt;code&gt;JavaScript&lt;/code&gt; tab, the first thing you will notice is rather than repeating identical code for each directive I use an &lt;a href=&quot;http://docs.angularjs.org/api/angular.forEach&quot; target=&quot;_blank&quot;&gt;angular.forEach&lt;/a&gt;&amp;nbsp;to loop through both events and dynamically create a directive for each one.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;350&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/dAkNb/embedded/js&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Again, there is nothing very special about the code in this directive. In fact anything of interest was discussed in the last post (review &lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-missing-directives-native.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; if you need to). Basically the directive will evaluate whatever the AngularJS expression is when the event fires.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;NOTE&lt;/b&gt;: If you want to do something only when an element has focus (or blurred) you would need to use both directives to monitor the element as this example does.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;UPDATE (2013-08-13)&lt;/b&gt;: &lt;a href=&quot;http://code.angularjs.org/1.2.0rc1/angular.js&quot; target=&quot;_blank&quot;&gt;AngularJS 1.2.0rc1&lt;/a&gt; now includes blur and focus events! Wooohoooo! Check out the &lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-120rc1-now-supports-focus.html&quot; target=&quot;_blank&quot;&gt;follow-up post&lt;/a&gt; to see how it compares.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;UPDATE (2013-8-26)&lt;/b&gt;: &lt;a href=&quot;http://blog.jasoncust.com/2013/08/angularjs-missing-directives-focus-blur_14.html&quot; target=&quot;_blank&quot;&gt;Part 2&lt;/a&gt; - Supporting the &#39;while&#39; adverb through a directive.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/6906824437570287078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2013/08/angularjs-missing-directives-focus-blur.html#comment-form' title='25 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/6906824437570287078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/6906824437570287078'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2013/08/angularjs-missing-directives-focus-blur.html' title='AngularJS Missing Directives: Focus &amp; Blur'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>25</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-327850276322395609</id><published>2013-08-09T15:20:00.000-04:00</published><updated>2013-08-13T10:30:26.366-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AngularJS"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="web dev"/><title type='text'>AngularJS Missing Directives: Native Form Resets</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
If you haven&#39;t played around with &lt;a href=&quot;http://angularjs.org/&quot; target=&quot;_blank&quot;&gt;AngularJS&lt;/a&gt; yet, I highly advise you give it a try. There are plenty of introductory articles and videos out there that will whet your appetite and get you up and running (&lt;a href=&quot;http://www.youtube.com/watch?v=i9MHigUZKEM&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;, &lt;a href=&quot;http://www.youtube.com/watch?v=8ILQOFAgaXE&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://www.egghead.io/&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; to name a few). Go ahead, go play. This post will still be here later when you need it. And you will need it.&lt;br /&gt;
&lt;br /&gt;
Okay, for everyone else, after the excitement and wonderment of two-way binding begins to wane you will start to notice some of the peculiarities about AngularJS. And then after that you will start to wonder why it doesn&#39;t do some of the things you would expect to it to do (the simple answer is generally the great dev team working on AngularJS hasn&#39;t gotten around to it yet). I refer to these missing functionalities as missing directives. Which means we can write our own directives to add any desired functionality!&lt;br /&gt;
&lt;br /&gt;
Lets jump in and take a look at form resets. If you have been developing for the web for any period of time you know that there is a native &lt;code&gt;INPUT&lt;/code&gt; element type that will reset all form input elements (including selects, textareas, etc.) to their original values. The following &lt;a href=&quot;http://jsfiddle.net/GlobalDomestic/V6rRv&quot; target=&quot;_blank&quot;&gt;fiddle&lt;/a&gt; has a simple form that includes a native reset button and an Angular controller to initialize the model (&quot;myModel&quot;).&lt;br /&gt;
&lt;br /&gt;
Try editing the fields and hitting reset.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;200&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/V6rRv/embedded/result,js,html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Wait. What happened? The expected behavior is for the form to reset the values of the input fields (&#39;foo&#39; and &#39;bar&#39;) to their original values (&#39;Boop&#39; and &#39;Beep&#39; respectively). But as you can see this does not happen. What gives?&lt;br /&gt;
&lt;br /&gt;
First, two underlying points:&lt;br /&gt;
&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Since AngularJS forms do not use the value attribute to specify original values for forms, the native form elements therefore have no initial values outside of the AngularJS world.&lt;/li&gt;
&lt;li&gt;AngularJS forms do not understand which model is being used to manage the two-way binding for the input fields (this example uses &#39;myModel&#39;).&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
Second, you might be wondering why this is how AngularJS behaves. I did and so at a recent AngularJS meetup I asked &lt;a href=&quot;https://plus.google.com/104744871076396904202&quot; target=&quot;_blank&quot;&gt;Igor Minar&lt;/a&gt;, a core Google Angular developer, why it hadn&#39;t been implemented and he responded that the team just hadn&#39;t done it yet.&lt;br /&gt;
&lt;br /&gt;
So, that leads to the question of what would be needed to make it work. Interestingly enough it&#39;s not a difficult thing to do once you have a firm understanding of AngularJS and its concept of directives. If you don&#39;t know what a &lt;a href=&quot;http://docs.angularjs.org/guide/directive&quot; target=&quot;_blank&quot;&gt;directive&lt;/a&gt; is, this should be a good example.&lt;br /&gt;
&lt;br /&gt;
Based on the points mentioned above, the directive would need to be:&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;aware of which model is represented by the form&lt;/li&gt;
&lt;li&gt;what the original input element values are (the original state of the model)&lt;/li&gt;
&lt;li&gt;capturing the native form reset event and preventing it from reseting the inputs to no values&lt;/li&gt;
&lt;li&gt;restoring the original values to the form input elements from the model on the form reset event&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
Since directives can be attributes of an element, the directive can be an attribute of the form element itself similar to the &lt;a href=&quot;http://docs.angularjs.org/api/ng.directive:ngSubmit&quot; target=&quot;_blank&quot;&gt;ngSubmit&lt;/a&gt; directive. We then can use the attribute value to pass in the model for the form. This placement will also allow the capture of the native form reset event.&lt;br /&gt;
&lt;br /&gt;
Lets preview with the finished directive before we dissect it. Once again, edit the form and hit the reset button.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;200&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/4fcCm/embedded/result,js,html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Whew! It works.&lt;br /&gt;
&lt;br /&gt;
If you notice on the HTML tab, the directive is invoked by applying an attribute to the form element with the value set to the model. The directive is then called and executes the returned function.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;200&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/4fcCm/embedded/html&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
From the JavaScript tab we can see how the directive accomplishes our goals.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;600&quot; src=&quot;http://jsfiddle.net/GlobalDomestic/4fcCm/embedded/js&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
But first, some background on some of the key pieces used in the directive:&lt;br /&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.angularjs.org/api/ng.$parse&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;$parse&lt;/code&gt;&lt;/a&gt; - since you can&#39;t pass the model object itself through the attribute (at least without creating an isolated scope), you can use this handy dandy service to get the model object from the attribute value string&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.angularjs.org/api/angular.copy&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;angular.copy&lt;/code&gt;&lt;/a&gt; - creates a deep copy of an object or an array&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.angularjs.org/api/ng.$rootScope.Scope&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;scope.$apply&lt;/code&gt;&lt;/a&gt; - executes the model assignment in an AngularJS context&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.angularjs.org/1.1.1/docs/api/ng.directive:form.FormController&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;$setPristine&lt;/code&gt;&lt;/a&gt; - used for setting the form to a &#39;pristine&#39; state indication&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
We can see the first thing the directive linking function does is to create a getter/setter for the passed model and then it creates a deep copy of this model. This deep copy will be the value that the model is reset to on any form reset events. Since &lt;code&gt;$parse&lt;/code&gt; will only return an object with method &lt;code&gt;assign&lt;/code&gt; if it is a model, we can use that to error check the input from the attribute.&lt;br /&gt;
&lt;br /&gt;
The next step binds the custom reset handler to the native form reset event. When the native form reset event is fired, the custom reset handler prevents the default action (resetting the input elements to their non-existent value attributes), sets the model to the original copied state and finally updates the form state to indicate it is pristine.&lt;br /&gt;
&lt;br /&gt;
That&#39;s it. That&#39;s all it takes. Nothing more. If you haven&#39;t realized it yet, while two-way binding is magical, directives are the building blocks of AngularJS.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/327850276322395609/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2013/08/angularjs-missing-directives-native.html#comment-form' title='322 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/327850276322395609'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/327850276322395609'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2013/08/angularjs-missing-directives-native.html' title='AngularJS Missing Directives: Native Form Resets'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>322</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-5235688993799936390</id><published>2012-04-26T15:07:00.000-04:00</published><updated>2012-04-26T15:07:00.672-04:00</updated><title type='text'>The Lion Who Was An Insomniac</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Sometime over the last couple weeks my MacBook Pro running OS X Lion (10.7.3) decided it no longer needed to sleep. While peculiar, I didn&#39;t worry about it since I had just installed some software recently and figured that might be the reason for my laptop&#39;s new found bout of insomnia. Well, two weeks later and my poor little guy hadn&#39;t slept a wink.&lt;br /&gt;
&lt;br /&gt;
Everyday I&#39;d close the lid and go about my business only to find it still purring along waiting for my return. While I do love when my pets are excited to see me, this seemed to be a bit of a waste. So, like any good techie, I backtracked everything I had done over the last two weeks to see what could be the pea in my laptop&#39;s mattress. I checked the software I installed (nope), the updates I applied (nope), the accessories I had plugged in (nope), the second monitor (nope), on and on but nothing seemed to be the thorn in his side.&lt;br /&gt;
&lt;br /&gt;
Now, I could have just restarted my computer to see if that resolved the issue. Two things prevented me from doing this:&lt;br /&gt;
1. I am not on Windows.&lt;br /&gt;
2. Where is the fun in that?&lt;br /&gt;
&lt;br /&gt;
So, digging into my techie war chest it was time to hit the old Terminal and get under the hood. First things first, &lt;code&gt;&lt;a href=&quot;http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/pmset.1.html&quot; target=&quot;_blank&quot;&gt;pmset&lt;/a&gt;&lt;/code&gt; is a godsend for this exact issue. If something is keeping your mac awake, &lt;b&gt;&lt;i&gt;BAM!&lt;/i&gt;&lt;/b&gt; you know what it is. Why I didn&#39;t just do this from the beginning I don&#39;t know aside from maybe I am a masochist.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ pmset -g assertions
4/25/12 11:35:13 PM EDT 
Assertion status system-wide:
   ChargeInhibit                           0
   PreventUserIdleDisplaySleep             0
   PreventUserIdleSystemSleep              1
   NoRealPowerSources_debug                0
   CPUBoundAssertion                       0
   EnableIdleSleep                         1
   PreventSystemSleep                      1
   DisableInflow                           0
   DisableLowPowerBatteryWarnings          0
   ExternalMedia                           0

Listed by owning process:
  pid 50: [0x0000012c00000032] PreventSystemSleep named: &quot;org.cups.cupsd&quot;
&lt;/pre&gt;&lt;br /&gt;
The last line in the output is the smoking gun. A &lt;a href=&quot;http://www.cups.org/documentation.php/ref-cupsd-conf.html&quot; target=&quot;_blank&quot;&gt;cupsd&lt;/a&gt; process was preventing my laptop from taking a much deserved nap.&lt;br /&gt;
&lt;br /&gt;
Who, what and why? &lt;a href=&quot;http://en.wikipedia.org/wiki/CUPS&quot; target=&quot;_blank&quot;&gt;CUPS&lt;/a&gt; allows macs to act as print servers essentially allowing the computer to queue up print jobs. So, more than likely there was a stalled print job that was keeping the process open, waiting to complete, before allowing the system to go to sleep (which makes logical sense to prevent a print job from being interrupted).&lt;br /&gt;
&lt;br /&gt;
I opened up my print queue from the preferences pane and &lt;i&gt;voila&lt;/i&gt;, a stalled job was sitting there mocking me. After quickly killing this unruly guest and checking &lt;code&gt;pmset&lt;/code&gt; again:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ pmset -g assertions
4/25/12 11:37:05 PM EDT 
Assertion status system-wide:
   ChargeInhibit                           0
   PreventUserIdleDisplaySleep             0
   PreventUserIdleSystemSleep              0
   NoRealPowerSources_debug                0
   CPUBoundAssertion                       0
   EnableIdleSleep                         1
   PreventSystemSleep                      0
   DisableInflow                           0
   DisableLowPowerBatteryWarnings          0
   ExternalMedia                           0
&lt;/pre&gt;&lt;br /&gt;
No more processes or assertion statuses indicating the system was preventing itself from sleeping. So, with baited breath I closed the lid and waited a couple seconds...pure silence. No purring, no whirling, just silence. My laptop had finally been cured of it&#39;s insomnia.&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/5235688993799936390/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2012/04/lion-who-was-insomniac.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/5235688993799936390'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/5235688993799936390'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2012/04/lion-who-was-insomniac.html' title='The Lion Who Was An Insomniac'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-8946103088634884017</id><published>2012-01-11T11:35:00.004-05:00</published><updated>2013-01-07T18:19:05.673-05:00</updated><title type='text'>The problem with document.cookie</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Accessing/modifying &lt;a href=&quot;http://en.wikipedia.org/wiki/HTTP_cookie&quot; target=&quot;_blank&quot;&gt;HTTP cookies&lt;/a&gt; with JavaScript is possible through the &lt;a href=&quot;https://developer.mozilla.org/en/DOM/document.cookie&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;document.cookie&lt;/code&gt;&lt;/a&gt; interface which permits two operations: retrieving all of the cookies that are accessible from the current document domain and path; and setting/updating an individual cookie. This interface is limited and can cause unexpected behavior if used without a thorough understanding of the interface and the W3C spec for HTTP cookies.&lt;br /&gt;
&lt;br /&gt;
Browser access/management of cookies is specified by the&amp;nbsp;&lt;a href=&quot;http://www.w3.org/Protocols/rfc2109/rfc2109&quot; target=&quot;_blank&quot;&gt;RFC 2109&lt;/a&gt;&amp;nbsp;spec which states:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Cookie uniqueness is controlled by the combination of the cookie&#39;s name, domain, and path.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;When setting a cookie with a specified domain, it must begin with a dot (.jasoncust.com).&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;The domain will default to the host domain&#39;s location (www.jasoncust.com).&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;The path will default to host domain&#39;s path up to but not including the right most &#39;/&#39;.&lt;/li&gt;
&lt;li&gt;Cookie access is restricted to the cookie path being a prefix of the document&#39;s full path (since paths were originally directory structures, cookies were accessible only from subdirectories).&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Similarly, cookie access is restricted to the cookie domain being a suffix of the document&#39;s domain. Note this is only true for cookie domains starting with the required dot (.jasoncust.com). If a cookie domain was set by default to say &#39;jasoncust.com&#39;, the cookie would not be accessible from any subdomains.&lt;/li&gt;
&lt;/ul&gt;That last two points could use a little more elaboration as people tend to gloss over them. Regarding just domains (assuming all cookie paths are set to &#39;/&#39;), if your document location is &#39;www.jasoncust.com&#39;, only cookies with domains set to either &#39;www.jasoncust.com&#39; or &#39;.jasoncust.com&#39; are accessible. Any other subdomains including &#39;jasoncust.com&#39; are not accessible. Combining paths with domains, if the document location is &#39;www.jasoncust.com/some/path/&#39;, only cookies with a domain of &#39;www.jasoncust.com&#39; (or &#39;.jasoncust.com&#39;) and paths of &#39;/&#39;, &#39;/some&#39; or &#39;/some/path&#39; are accessible.&lt;br /&gt;
&lt;br /&gt;
Retrieving cookie values is easy enough through the interface, simply go to &lt;a href=&quot;http://www.nytimes.com/&quot; target=&quot;_blank&quot;&gt;www.nytimes.com&lt;/a&gt;, open a console and then enter &lt;code&gt;document.cookie&lt;/code&gt; to see a string containing all of the cookies accessible from the current host domain and path. It will look something like:&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;&amp;gt; document.cookie
&quot;RMID=0734175263b54f0d07f7801a; adxcs=s*2b53d=0:1; adxcl=t*2b53d=4f32014f:1326254071|ti=4f32014f:1326254071&quot;
&lt;/pre&gt;The format of the returned string is &lt;code&gt;name=value&lt;/code&gt; with multiple cookies joined by a semicolon + space (&#39;&lt;code&gt;; &#39;&lt;/code&gt;). So in order to get a particular cookie&#39;s value, a bit of parsing is required. One major caveat is only the cookie&#39;s value is returned.&amp;nbsp;The cookie&#39;s path, domain, secure setting, and the expiration/max-age are not returned through the interface which can be problematic.&lt;br /&gt;
&lt;br /&gt;
Why is this problematic? Lets walk through an example, albeit a trivial one, to illuminate this issue. Starting with two cookies:&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;tabular&quot;&gt;&lt;tbody&gt;
&lt;tr&gt; &lt;th&gt;name&lt;/th&gt; &lt;th&gt;value&lt;/th&gt; &lt;th&gt;domain&lt;/th&gt; &lt;th&gt;path&lt;/th&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;uuid&lt;/td&gt; &lt;td&gt;19691231&lt;/td&gt; &lt;td&gt;.nytimes.com&lt;/td&gt; &lt;td&gt;/&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;xid&lt;/td&gt; &lt;td&gt;42&lt;/td&gt; &lt;td&gt;.nytimes.com&lt;/td&gt; &lt;td&gt;/&lt;/td&gt; &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
If the document location is &#39;www.nytimes.com&#39;, then the returned cookies will be:&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;&amp;gt; document.cookie
&quot;uuid=19691231; xid=42&quot;&lt;/pre&gt;To set or update a cookie a string representing the cookie is assigned to &lt;code&gt;document.cookie&lt;/code&gt;. For example, to update the &#39;xid&#39; cookie to &#39;lorum&#39;, a first approach might look like:&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;&amp;gt; document.cookie = &quot;xid=lorum&quot;;
&lt;/pre&gt;Checking the cookie value returns:&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;&amp;gt; document.cookie
&quot;uuid=19691231; xid=42; xid=lorum&quot;
&lt;/pre&gt;What happened? There are now two cookies with the name &#39;xid&#39;. Looking at the table again we see why this happened:&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;tabular&quot;&gt;&lt;tbody&gt;
&lt;tr&gt; &lt;th&gt;name&lt;/th&gt; &lt;th&gt;value&lt;/th&gt; &lt;th&gt;domain&lt;/th&gt; &lt;th&gt;path&lt;/th&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;uuid&lt;/td&gt; &lt;td&gt;19691231&lt;/td&gt; &lt;td&gt;.nytimes.com&lt;/td&gt; &lt;td&gt;/&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;xid&lt;/td&gt; &lt;td&gt;42&lt;/td&gt; &lt;td&gt;.nytimes.com&lt;/td&gt; &lt;td&gt;/&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;xid&lt;/td&gt; &lt;td&gt;lorum&lt;/td&gt; &lt;td&gt;www.nytimes.com&lt;/td&gt; &lt;td&gt;/&lt;/td&gt; &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
The two cookies while sharing the same name have a different subdomain. Since the domain wasn&#39;t specified in the assignment, it defaulted to the host domain (&#39;www.nytimes.com&#39;) in the example. So in order to update an existing cookie the same domain and path originally assigned to the cookie must be used. However no meta data is returned via &lt;code&gt;document.cookie&lt;/code&gt;. This means any cookies that are to be manipulated by JavaScript need to have a set domain and path (probably the root domain and path) that both the server and the JavaScript code use when setting cookies. This also applies to any optional cookie settings such as the expiration and the secure flag.&lt;br /&gt;
&lt;br /&gt;
Assuming for some reason it was desirable to have two cookies with the same name but a different subdomain and/or path, how do you know which cookie is what? Since &lt;code&gt;document.cookies&lt;/code&gt; does not return the domain or path of the cookies, there is no way to tell. This may seem trivial, but what if an operation needed to clear or overwrite the &#39;xid&#39; cookie with the value &#39;lorum&#39;? How would it do so?&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;tl;dr&lt;/h3&gt;When using the &lt;code&gt;document.cookie&lt;/code&gt; interface, the lack of any meta data about the cookies returned can cause issues if not properly designed around. Two key points to remember are:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Cookies with the same name but different subdomains and/or paths are allowed but this meta information is not returned by the interface. So there is no way to tell which cookie value is for what domain/path.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;When updating a cookie, the original cookie is replaced if the name/domain/path match. If they don&#39;t match, a new cookie with the same name but different domain/path will be created. Also, when replacing a cookie, any other meta data is overwritten even if not explicitly stated.&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;Should there be an update to the interface for ECMA5? I&#39;m not positive since cookies should only be used if the data needs to be included in every request to/from the server. Otherwise newer APIs such as the &lt;a href=&quot;https://developer.mozilla.org/en/DOM/Storage&quot; target=&quot;_blank&quot;&gt;Storage APIs&lt;/a&gt; probably are a better design choice and don&#39;t have this issue. Still, it would be a nice addition to have access to the meta data for each cookie when you do need to work with them from both the client and the server.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/8946103088634884017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2012/01/problem-with-documentcookie.html#comment-form' title='30 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/8946103088634884017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/8946103088634884017'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2012/01/problem-with-documentcookie.html' title='The problem with document.cookie'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>30</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-8144006723628914076</id><published>2012-01-06T21:56:00.001-05:00</published><updated>2013-08-10T21:15:07.626-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="web dev"/><title type='text'>IIFEs, Closures, Unit Testing and Privacy</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
One of the toughest decisions within JavaScript code design is the choice between testability and privacy. It&#39;s definitely one of the most frustrating aspects of JavaScript for me personally (and that is definitely saying something). This choice can lead to a less than ideal design if unit testing is important to you... which it should be.&lt;br /&gt;
&lt;br /&gt;
Lets start with an example to see how this choice can affect design. We are going to create an object that will represent a basic parallelogram that requires as input the measurements of two adjacent sides and an angle. Assuming the second measurement is the base, the object will internally compute the height and area of the parallelogram from the inputs on object creation exposing the original measurements and the two new computed values.&lt;br /&gt;
&lt;br /&gt;
A typical design pattern for an object in JavaScript is to use an &lt;a href=&quot;http://benalman.com/news/2010/11/immediately-invoked-function-expression/&quot; target=&quot;_blank&quot;&gt;IIFE&lt;/a&gt; (Immediately Invoked Function Expression) to create a &lt;a href=&quot;https://developer.mozilla.org/en/JavaScript/Guide/Closures&quot; target=&quot;_blank&quot;&gt;closure&lt;/a&gt; so we don&#39;t pollute the global namespace. A simple design (we aren&#39;t concerning ourselves with optimizations for this discussion) could look like:&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;(function( win, undef ) {
  // Global namespace object name
  var objName = &#39;myAwesomeParallelogram&#39;;

  // Compute the height of a parallelogram where length is the adjacent side
  // of the base and the angle is acute in radians length * sin(angle)
  function height( length, angle ) {
    return length * Math.sin( Math.min( angle, 180 - angle ) * Math.PI/180 );
  }

  // Compute the area of a parallelogram b * h
  function area( base, height ) {
    return base * height;
  }

  var obj = function( sideA, sideB, angle ) {
    if ( angle &amp;lt; 0 || angle &amp;gt; 180 ) {
      throw new SyntaxError(&#39;Angle is not between 0 and 180 degrees&#39;);
    }

    this.sideA = sideA;
    this.sideB = sideB;
    this.angle = angle;

    this.height = height( sideA, angle );
    this.area = area( sideB, this.height );
  };

  win[ objName ] = obj;
})(window);
&lt;/pre&gt;
Sample usage:&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;&amp;gt; var p = new myAwesomeParallelagram( 15, 12, 45);
undefined
&amp;gt; p.area
127.27922061357853
&amp;gt; p.height
10.606601717798211
&lt;/pre&gt;
Great! But aside from testing the object itself, how would we be able to test if &lt;code&gt;height&lt;/code&gt; or &lt;code&gt;area&lt;/code&gt; work correctly? How would we test the code flow? We could redesign our code to expose these functions and we could likewise include some logging calls to console to show the flow. But, is this what we want in production? Is it necessary?&lt;br /&gt;
&lt;br /&gt;
There have been a few clever ways of handling this&amp;nbsp;dilemma&amp;nbsp;(I know I rolled a few of my own over the years) but &lt;a href=&quot;http://www.crockford.com/&quot; target=&quot;_blank&quot;&gt;Mr. Douglas Crockford&lt;/a&gt; (the author of &lt;a href=&quot;http://www.amazon.com/gp/product/0596517742/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=behindthedump-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0596517742&quot; target=&quot;_blank&quot;&gt;JavaScript: The Good Parts&lt;/a&gt; and &lt;a href=&quot;http://jslint.com/&quot; target=&quot;_blank&quot;&gt;JSLint&lt;/a&gt;) has just released a new tool to help end the Sophie&#39;s choice between good design/security and testability. It&#39;s called &lt;a href=&quot;https://github.com/douglascrockford/JSDev&quot; target=&quot;_blank&quot;&gt;JSDev&lt;/a&gt; and it allows you to include specially formated comments that can be transformed into development unit testing code and then removed through normal minification processes.&lt;br /&gt;
&lt;br /&gt;
To use the new tool we first need to download the &lt;a href=&quot;https://github.com/douglascrockford/JSDev/blob/master/jsdev.c&quot; target=&quot;_blank&quot;&gt;raw file&lt;/a&gt; (or use git) and compile it locally for a CLI.&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ curl &#39;https://raw.github.com/douglascrockford/JSDev/master/jsdev.c&#39; -o jsdev.c &amp;amp;&amp;amp; gcc jsdev.c -o jsdev
&lt;/pre&gt;
Now that we have a compiled version of &lt;code&gt;jsdev&lt;/code&gt; we can edit our original object code to include development only code for unit testing.&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;(function( win, undef ) {
  // Global namespace object name
  var objName = &#39;myAwesomeParallelogram&#39;;

  // Compute the height of a parallelogram where length is the adjacent side
  // of the base and the angle is acute in radians length * sin(angle)
  function height( length, angle ) {
    return length * Math.sin( Math.min( angle, 180 - angle ) * Math.PI/180 );
  }

  // Compute the area of a parallelogram b * h
  function area( base, height ) {
    return base * height;
  }

  var obj = function( sideA, sideB, angle ) {
    if ( angle &amp;lt; 0 || angle &amp;gt; 180 ) {
      throw new SyntaxError(&#39;Angle is not between 0 and 180 degrees&#39;);
    }

    this.sideA = sideA;
    this.sideB = sideB;
    this.angle = angle;

    this.height = height( sideA, angle );
    this.area = area( sideB, this.height );
  };

  // JSDev code comments
  /*dev
    obj.height = height;
    obj.area = area;
  */
 
  win[ objName ] = obj;
})(window);
&lt;/pre&gt;
Now if we process it through or handy dandy new tool:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ ./jsdev dev -comment &quot;Development Version&quot; &amp;lt; input.js &amp;gt; output-dev.js
&lt;/pre&gt;
The new file, &lt;code&gt;output-dev.js&lt;/code&gt; looks like:&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;// Development Version
(function( win, undef ) {
  // Global namespace object name
  var objName = &#39;myAwesomeParallelogram&#39;;

  // Compute the height of a parallelogram where length is the adjacent side
  // of the base and the angle is acute in radians length * sin(angle)
  function height( length, angle ) {
    return length * Math.sin( Math.min( angle, 180 - angle ) * Math.PI/180 );
  }

  // Compute the area of a parallelogram b * h
  function area( base, height ) {
    return base * height;
  }

  var obj = function( sideA, sideB, angle ) {
    if ( angle &amp;lt; 0 || angle &amp;gt; 180 ) {
      throw new SyntaxError(&#39;Angle is not between 0 and 180 degrees&#39;);
    }

    this.sideA = sideA;
    this.sideB = sideB;
    this.angle = angle;

    this.height = height( sideA, angle );
    this.area = area( sideB, this.height );
  };

  // JSDev code comments
  {
    obj.height = height;
    obj.area = area;
  }
 
  win[ objName ] = obj;
})(window);
&lt;/pre&gt;
Before was continue there are a few things to notice between the input files, the command and the output file:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;The comment from the CLI is now a header comment in the output file. This is handy to always include the type of output file in the file.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;The other arguments for the CLI specify which multi line comments that match the format &lt;code&gt;/*&amp;lt;argument&amp;gt; &amp;lt;code&amp;gt;*/&lt;/code&gt; to include in the output file. This is handy if you have different testing levels or needs. &lt;i&gt;Note that there cannot be a space between the opening comment notation and the argument.&lt;/i&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Single line comment notations (&quot;//&quot;) are unaffected by the jsdev tool as well as multi line comments that don&#39;t match the specified format above.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;
Now lets see if we can access our development code private functions using a console.&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;&amp;gt; myAwesomeParallelogram.area
  function area( base, height ) {
    return base * height;
  }
&amp;gt; myAwesomeParallelogram.height
  function height( length, angle ) {
    return length * Math.sin( Math.min( angle, 180 - angle ) * Math.PI/180 );
  }
&lt;/pre&gt;
And when we minify the original code we get this (using &lt;a href=&quot;https://github.com/douglascrockford/JSMin&quot; target=&quot;_blank&quot;&gt;JSMin&lt;/a&gt; for this example):&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;(function(win,undef){var objName=&#39;myAwesomeParallelogram&#39;;function height(length,angle){return length*Math.sin(Math.min(angle,180-angle)*Math.PI/180);}
function area(base,height){return base*height;}
var obj=function(sideA,sideB,angle){if(angle&amp;lt;0||angle&amp;gt;180){throw new SyntaxError(&#39;Angle is not between 0 and 180 degrees&#39;);}
this.sideA=sideA;this.sideB=sideB;this.angle=angle;this.height=height(sideA,angle);this.area=area(sideB,this.height);};win[objName]=obj;})(window);&lt;/pre&gt;
We no longer need to sacrifice our design and security for testability. Is it the most elegant solution? Maybe not, but it works well and can be easily added to a build process for testing and will not impact your current build process for production.&lt;br /&gt;
&lt;br /&gt;
For more information and more usage info, please refer to the &lt;a href=&quot;https://github.com/douglascrockford/JSDev/blob/master/README&quot; target=&quot;_blank&quot;&gt;README&lt;/a&gt; file on github.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/8144006723628914076/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2012/01/iifes-closures-unit-testing-and-privacy.html#comment-form' title='47 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/8144006723628914076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/8144006723628914076'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2012/01/iifes-closures-unit-testing-and-privacy.html' title='IIFEs, Closures, Unit Testing and Privacy'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>47</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-1156726563120411349</id><published>2012-01-05T20:24:00.003-05:00</published><updated>2012-01-09T13:56:06.513-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Chrome"/><category scheme="http://www.blogger.com/atom/ns#" term="prerender"/><category scheme="http://www.blogger.com/atom/ns#" term="reporting"/><category scheme="http://www.blogger.com/atom/ns#" term="web dev"/><title type='text'>Chrome, Prerender and Site Stats</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
As &lt;a href=&quot;https://www.google.com/chrome&quot; target=&quot;_blank&quot;&gt;Chrome&lt;/a&gt; keeps inching towards market dominance, it is definitely helping to &lt;a href=&quot;https://plus.google.com/100585555255542998765/posts&quot; target=&quot;_blank&quot;&gt;move the web forward&lt;/a&gt; and speeding it up along the way. One of the interesting ways they have helped speed up the web (as of Chrome 13)&amp;nbsp;is through the use of &quot;&lt;a href=&quot;http://code.google.com/chrome/whitepapers/prerender.html&quot; target=&quot;_blank&quot;&gt;prerendering&lt;/a&gt;&quot;. Basically, prerendering will begin to download a page that is linked from the current page via&amp;nbsp;&lt;code&gt;link&lt;/code&gt; declarations on the current page like so:&lt;br /&gt;
&lt;pre class=&quot;brush:html&quot;&gt;&amp;lt;link rel=&quot;prerender&quot; href=&quot;/someOtherAwesomeness.html&quot;&amp;gt;
&amp;lt;link rel=&quot;prerender&quot; href=&quot;http://anotherAwesome.website.com&quot;&amp;gt;
&lt;/pre&gt;
These pages will now be downloaded in the background while the user is currently on the page theses are linked from. When/if the user follows a link to one of those pages, it will feel nearly instantaneous to the user since the page is already loaded.&lt;br /&gt;
&lt;br /&gt;
Pretty interesting trick&amp;nbsp;on Google&#39;s part&amp;nbsp;to make Chrome feel faster. As one can imagine, to avoid&amp;nbsp;unnecessary&amp;nbsp;overhead to websites linked from one page, discretion should be practiced in selecting what if any pages should be prerendered from the current page being viewed. Easy candidates are next-type pages and other links that have a high click through rate.&lt;br /&gt;
&lt;br /&gt;
How does this impact site stats? It depends on whether you can truly count a page view if a page was prerendered but then never seen? If you just care about inflated numbers, then this is probably a welcome exploit to increasing views across a larger number of pages even though a person&amp;nbsp;truly&amp;nbsp;only viewed one page. But, if you do care about them for being able to understand user interaction with your content, this could skew your numbers in a horrible way. These false views will increase the&amp;nbsp;denominator&amp;nbsp;in your analytics formulas without a chance of increase to the numerator, thus making your impact for user actions on those pages seem less effective.&lt;br /&gt;
&lt;br /&gt;
The good news is that individual sites will have to enable this in their code for now and hence&amp;nbsp;adoption will be slow. But with&amp;nbsp;&lt;a href=&quot;http://googleblog.blogspot.com/2011/06/knocking-down-barriers-to-knowledge.html&quot; target=&quot;_blank&quot;&gt;Google search results pages utilizing this feature already&lt;/a&gt; and the&amp;nbsp;&lt;a href=&quot;http://chrome.blogspot.com/2012/01/speed-and-security.html&quot; target=&quot;_blank&quot;&gt;Chrome Beta released today enabling it from the omnibox&lt;/a&gt;, these false views will quickly start to accumulate.&lt;br /&gt;
&lt;br /&gt;
So how do you prevent that from happening? Depends on how you determine page views.&lt;br /&gt;
&lt;br /&gt;
If you use Google Analytics, you are already covered with the &lt;a href=&quot;http://code.google.com/apis/analytics/community/gajs_changelog.html&quot; target=&quot;_blank&quot;&gt;2011-07 release (July 26, 2011)&lt;/a&gt;&amp;nbsp;as it uses the &lt;a href=&quot;http://www.w3c-test.org/webperf/specs/PageVisibility/&quot; target=&quot;_blank&quot;&gt;Page Visibility API&lt;/a&gt; (but only with the webkit prefix, hopefully they keep up with other vendor releases as well).&lt;br /&gt;
&lt;br /&gt;
If not, you can roll your own by using the&amp;nbsp;Page Visibility API yourself. Continuing on form the example from my &lt;a href=&quot;http://blog.jasoncust.com/2012/01/can-you-see-me-can-you-see-me-now.html&quot; target=&quot;_blank&quot;&gt;previous post&lt;/a&gt;, we can build a simple check for determining whether or not a page has been prerendered:&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;var visibilityAPI = ( typeof document.hidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;visibilitystate&#39;: &#39;visibilitystate&#39;, &#39;visibilitychange&#39;: &#39;visibilitychange&#39; } ) || ( typeof document.webkitHidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;visibilitystate&#39;: &#39;webkitvisibilitystate&#39;, &#39;visibilitychange&#39;: &#39;webkitvisibilitychange&#39; } ) || ( typeof document.mozHidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;visibilitystate&#39;: &#39;mozvisibilitystate&#39;, &#39;visibilitychange&#39;: &#39;mozvisibilitychange&#39; } ) || ( typeof document.msHidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;hidden&#39;: &#39;msHidden&#39;, &#39;visibilitystate&#39;: &#39;msvisibilitystate&#39;, &#39;visibilitychange&#39;: &#39;msvisibilitychange&#39; } );

(function pageVisibilityChanged() {
    if ( document[ visibilityAPI.visibilitystate ] === &quot;prerender&quot; ) {
        // Application has been prerendered
        // Add listener to fire when page is no longer in prerender state
        document.addEventListener( visibilityAPI.visibilitychange, pageVisibilityChanged, false );
    }
    else {
        // Page is no longer in presender state
        // A page view can be counted now
        document.removeEventListener( visibilityAPI.visibilitychange, pageVisibilityChanged, false );
    }
})();&lt;/pre&gt;
There is still one problem with this example: the person could still never see your site though even after the follow one of the links. For example, they could open the page in a new tab behind their current one and then close it before they view it. Yes, this is probably an edge case, but you could easily expand the previous example to only count when the page is truly viewed.&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;var visibilityAPI = ( typeof document.hidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;hidden&#39;: &#39;hidden&#39;, &#39;visibilitystate&#39;: &#39;visibilitystate&#39;, &#39;visibilitychange&#39;: &#39;visibilitychange&#39; } ) || ( typeof document.webkitHidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;hidden&#39;: &#39;webkitHidden&#39;, &#39;visibilitystate&#39;: &#39;webkitvisibilitystate&#39;, &#39;visibilitychange&#39;: &#39;webkitvisibilitychange&#39; } ) || ( typeof document.mozHidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;hidden&#39;: &#39;mozHidden&#39;, &#39;visibilitystate&#39;: &#39;mozvisibilitystate&#39;, &#39;visibilitychange&#39;: &#39;mozvisibilitychange&#39; } ) || ( typeof document.msHidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;hidden&#39;: &#39;msHidden&#39;, &#39;visibilitystate&#39;: &#39;msvisibilitystate&#39;, &#39;visibilitychange&#39;: &#39;msvisibilitychange&#39; } );

(function pageVisibilityChanged() {
    if ( document[ visibilityAPI.visibilitystate ] === &quot;prerender&quot; ) {
        // Application has been prerendered
        // Add listener to fire when page is no longer in prerender state
        document.addEventListener( visibilityAPI.visibilitychange, pageVisibilityChanged, false );
    }
    else if ( !document[ visibilityAPI.hidden ] ) {
        // Page is no longer in presender state or hidden
        // A true page view can be counted now
        document.removeEventListener( visibilityAPI.visibilitychange, pageVisibilityChanged, false );
    }
})();&lt;/pre&gt;
Now you are one small step closer to more accurate reporting!&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/1156726563120411349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2012/01/is-chrome-skewing-my-site-stats.html#comment-form' title='29 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/1156726563120411349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/1156726563120411349'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2012/01/is-chrome-skewing-my-site-stats.html' title='Chrome, Prerender and Site Stats'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>29</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-2376999005649104891</id><published>2012-01-01T14:49:00.001-05:00</published><updated>2012-01-10T11:25:11.872-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="HTML5"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="Page Visibility"/><category scheme="http://www.blogger.com/atom/ns#" term="web dev"/><title type='text'>Can you see me? Can you see me now?</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
What is the&amp;nbsp;&lt;a href=&quot;http://www.w3c-test.org/webperf/specs/PageVisibility/&quot; target=&quot;_blank&quot;&gt;Page Visibility API&lt;/a&gt;&amp;nbsp;and what is it good for?&amp;nbsp;It is a JavaScript API that allows developers to check if their page/application is visible and to attach an event listener for visibility state changes. What is that useful for? Well, lets discuss web applications in general for a moment to see how and why it is something developers should be concerned with.&lt;br /&gt;
&lt;br /&gt;
Every window or tab a user opens for services like Google+, Facebook, Gmail, Twitter, and Reddit increases the resource requirement for each application to poll, update, animate and perform other intensive operations.&amp;nbsp;The big question is do end-users really have to pay the resource costs for each of these web applications?&lt;br /&gt;
&lt;br /&gt;
To answer this question, we can ask a few more questions about web applications in general:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Do they need to&amp;nbsp;constantly run?&lt;/li&gt;
&lt;li&gt;What operations need to be run and at what rate?&lt;/li&gt;
&lt;li&gt;Can these operation rates be altered based on interaction types/levels?&lt;/li&gt;
&lt;li&gt;What does user interaction entail?&lt;/li&gt;
&lt;/ol&gt;
First, let me state this is &lt;b&gt;not&lt;/b&gt; a blanket solution &lt;b&gt;nor&lt;/b&gt; an advocacy that all applications should be handled in this manner. That said, developers should think about not only how their applications are actively used but also what they need to do when not in active use.&lt;br /&gt;
&lt;br /&gt;
Second, lets use a simple example to provide a context for pondering these questions. A basic news or message feed would work well enough. It is based around a data stream that is updated both&amp;nbsp;asynchronously and irregularly so some form of polling and updating the stream with new items is required. Also, dynamic animation showing new items will be used to display these updates to an end-user.&lt;br /&gt;
&lt;br /&gt;
Finally, the first three questions are in one way or another predicated on the answer for the fourth so we will begin there.&lt;br /&gt;
&lt;br /&gt;
What does user interaction entail? The obvious cases are when a user is interacting with the application using an input device (mouse, keyboard, mic, camera, finger, stylus, etc.). But can someone use an application without interacting with it directly? What if it is open and updating with new items that the user reads as the show up on the screen? So, the commonality amongst all of these use cases for our application is that the user needs to see the application in order to use it.&lt;br /&gt;
&lt;br /&gt;
Thinking about that a bit more generally, consider all of the tabs a user has open at any given moment. If only one is visible at a time, why do the rest need to do anything at all (aside from pending operations a user has queued up)? Shouldn&#39;t the visible tab be the sole active window?&lt;br /&gt;
&lt;br /&gt;
So a more general question arises: if a user cannot see our application, does it need to run if at all?&lt;br /&gt;
&lt;br /&gt;
For our application at least, this makes perfect sense. Why waste resources continuously polling and updating (especially with animations) if the user can&#39;t see it? Couldn&#39;t we fall back to less frequent polling with no special effects for updating the page? Seems reasonable with little to no user impact. In fact, since the user can&#39;t see the page, how would they know.&lt;br /&gt;
&lt;br /&gt;
The only problem is how do we know if the user can see the page? Well, thanks to the&amp;nbsp;&lt;a href=&quot;http://www.w3c-test.org/webperf/specs/PageVisibility/&quot; target=&quot;_blank&quot;&gt;Page Visibility API&lt;/a&gt;&amp;nbsp;proposal, we can do this today (through browser prefixes at the moment). The API is available with a webkit prefix in &lt;a href=&quot;http://code.google.com/chrome/whitepapers/pagevisibility.html&quot; target=&quot;_blank&quot;&gt;Chrome 13+&lt;/a&gt;, a ms prefix in &lt;a href=&quot;http://blogs.msdn.com/b/ie/archive/2011/07/08/using-pc-hardware-more-efficiently-in-html5-new-web-performance-apis-part-2.aspx&quot; target=&quot;_blank&quot;&gt;IE10+&lt;/a&gt;&amp;nbsp;and a moz prefix in &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/rev/c7b4452ef1d2&quot; target=&quot;_blank&quot;&gt;FF10+&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
For the example below, using the non-prefix version (which will be the standard) and only checking if the document is visible or not.&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;function pageVisibilityChanged() {
  if ( document.hidden ) {
    // Application is not visible to the user
    // Adjust polling rates and display update for inactive display mode
  }
  else {
    // Application is visible to the user
    // Adjust polling rates and display update for active display mode
  }
}

document.addEventListener( &#39;visibilitychange&#39;, pageVisibilityChanged, false );
&lt;/pre&gt;
That&#39;s it. Really. That&#39;s all you need to do to be a great application neighbor and also help reduce energy use by reducing&amp;nbsp;unnecessary resource use by your&amp;nbsp;application.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;UPDATE:&lt;/b&gt; Here is a browser prefix version for those who want to play today with Chrome 13+, IE10+ and FF10+ and any browser that implements the standard.&lt;br /&gt;
&lt;pre class=&quot;brush:js&quot;&gt;var visibilityAPI = ( typeof document.hidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;hidden&#39;: &#39;hidden&#39;, &#39;visibilitychange&#39;: &#39;visibilitychange&#39; } ) || ( typeof document.webkitHidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;hidden&#39;: &#39;webkitHidden&#39;, &#39;visibilitychange&#39;: &#39;webkitvisibilitychange&#39; } ) || ( typeof document.mozHidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;hidden&#39;: &#39;mozHidden&#39;, &#39;visibilitychange&#39;: &#39;mozvisibilitychange&#39; } ) || ( typeof document.msHidden != &#39;undefined&#39; &amp;amp;&amp;amp; { &#39;hidden&#39;: &#39;msHidden&#39;, &#39;visibilitychange&#39;: &#39;msvisibilitychange&#39; } );

function pageVisibilityChanged() {
  if ( document[ visibilityAPI.hidden ] ) {
    // Application is not visible to the user
    // Adjust polling rates and display update for inactive display mode
  }
  else {
    // Application is visible to the user
    // Adjust polling rates and display update for active display mode
  }
}

document.addEventListener( visibilityAPI.visibilitychange, pageVisibilityChanged, false );
&lt;/pre&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/2376999005649104891/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2012/01/can-you-see-me-can-you-see-me-now.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/2376999005649104891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/2376999005649104891'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2012/01/can-you-see-me-can-you-see-me-now.html' title='Can you see me? Can you see me now?'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-7174330792869192411</id><published>2011-12-29T22:11:00.002-05:00</published><updated>2012-01-09T12:36:54.354-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bash"/><category scheme="http://www.blogger.com/atom/ns#" term="unix"/><title type='text'>Bash sort... what the hell is up with tab separators?</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;b&gt;UPDATE:&lt;/b&gt; I have updated the &lt;a href=&quot;http://en.wikipedia.org/wiki/Sort_(Unix)&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;sort&lt;/code&gt;&lt;/a&gt; Wiki page to include an example for tab separated sorting.&lt;br /&gt;
&lt;br /&gt;
In the world of &lt;a href=&quot;http://en.wikipedia.org/wiki/Unix_shell&quot; target=&quot;_blank&quot;&gt;Unix shells&lt;/a&gt; there exists a very common one called &lt;a href=&quot;http://en.wikipedia.org/wiki/Bash_(Unix_shell)&quot; target=&quot;_blank&quot;&gt;Bash&lt;/a&gt;. And within the Bash shell there are a whole host of commands that can be used both on the command line and in a script file. These commands run the gamut for what they can do, but there is a small subset that most people find their daily lives centered around when it comes to programming or hacking out solutions such as &lt;a href=&quot;http://en.wikipedia.org/wiki/Sed_(Unix)&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;sed&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;http://en.wikipedia.org/wiki/Cut_(Unix)&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;cut&lt;/code&gt;&lt;/a&gt;, and &lt;code&gt;sort&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Fairly common inputs to these commands are &lt;a href=&quot;http://en.wikipedia.org/wiki/Delimiter-separated_values&quot; target=&quot;_blank&quot;&gt;delimited-separated values&lt;/a&gt; such as &lt;a href=&quot;http://en.wikipedia.org/wiki/Comma-separated_values&quot; target=&quot;_blank&quot;&gt;CSVs&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Tab-separated_values&quot; target=&quot;_blank&quot;&gt;TSVs&lt;/a&gt;. Thankfully almost every command that you would use for these formats allows you to specify the delimiter.&lt;br /&gt;
&lt;br /&gt;
For instance, say we have a TSV file called &lt;code&gt;phonebook&lt;/code&gt; that contains the name and number for each contact:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ cat phonebook 
Smith, Brett 555-4321
Doe, John 555-1234
Doe, Jane 555-3214
Avery, Cory 555-4132
Fogarty, Suzie 555-2314
&lt;/pre&gt;
With &lt;code&gt;cut&lt;/code&gt; you could get just the names if you wanted:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ cut -f1 phonebook 
Smith, Brett
Doe, John
Doe, Jane
Avery, Cory
Fogarty, Suzie
&lt;/pre&gt;
How did it know what the delimiter was? Luckily with &lt;code&gt;cut&lt;/code&gt; the default delimiter is the tab character. What if it&#39;s not though? Looking at the &lt;a href=&quot;http://en.wikipedia.org/wiki/Man_page&quot; target=&quot;_blank&quot;&gt;man page&lt;/a&gt; for &lt;code&gt;cut&lt;/code&gt; gives us:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ man cut
...
     -d delim
             Use delim as the field delimiter character instead of the tab character.
...
&lt;/pre&gt;
So, say you wanted just the last name for everyone. Well, you can pipe the output from the first command to a second command to do just that! The only difference is now we are going to specify the delimiter to be a comma for the second command.&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ cut -f1 phonebook | cut -f1 -d &#39;,&#39;
Smith
Doe
Doe
Avery
Fogarty
&lt;/pre&gt;
Nice! Now, lets check out &lt;code&gt;sort&lt;/code&gt;. First, lets sort our phonebook by last name:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ sort -k1,1 phonebook 
Avery, Cory 555-4132
Doe, Jane 555-3214
Doe, John 555-1234
Fogarty, Suzie 555-2314
Smith, Brett 555-4321
&lt;/pre&gt;
That works well enough. Now lets sort by phone numbers:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ sort -k2,2 phonebook 
Smith, Brett 555-4321
Avery, Cory 555-4132
Doe, Jane 555-3214
Doe, John 555-1234
Fogarty, Suzie 555-2314
&lt;/pre&gt;
Well... that&#39;s not right. It sorted by first name instead. Hmmmmm. Looking at the man page we see:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ man sort
...
       -t, --field-separator=SEP
              use SEP instead of non-blank to blank transition
...
&lt;/pre&gt;
OK, lets add our trusty tab character:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ sort -k2,2 -t &#39;\t&#39; phonebook 
sort: multi-character tab `\\t&#39;
&lt;/pre&gt;
Uhhhhh... multi-character? Looks like &lt;code&gt;sort&lt;/code&gt; doesn&#39;t interpret &#39;\t&#39; as a tab character, but instead a literal &#39;\&#39; and &#39;t&#39;. In another way:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ echo -n &#39;\t&#39; | hexdump -c
0000000   \   t                                                        
0000002
&lt;/pre&gt;
So, how do we set the separator to be a tab character? The &lt;a href=&quot;http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_03.html#sect_03_03_05&quot; target=&quot;_blank&quot;&gt;beginner&#39;s bash guide&lt;/a&gt; provides some guidance on that:&lt;br /&gt;
&lt;blockquote&gt;
3.3.5. ANSI-C quoting&lt;br /&gt;
&lt;br /&gt;
Words in the form &quot;$&#39;STRING&#39;&quot; are treated in a special way. The word expands to a string, with backslash-escaped characters replaced as specified by the ANSI-C standard. Backslash escape sequences can be found in the Bash documentation.&lt;/blockquote&gt;
Using our &lt;code&gt;echo&lt;/code&gt; example again:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ echo -n $&#39;\t&#39; | hexdump -c
0000000  \t                                                            
0000001
&lt;/pre&gt;
Yup, one character. Trying out phone number sort again:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ sort -k2,2 -t $&#39;\t&#39; phonebook 
Doe, John 555-1234
Fogarty, Suzie 555-2314
Doe, Jane 555-3214
Avery, Cory 555-4132
Smith, Brett 555-4321
&lt;/pre&gt;
BINGO! Now we are truly sorting on the second column.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
tl;dr&lt;/h3&gt;
Turns out &lt;code&gt;sort&lt;/code&gt; is similar to &lt;code&gt;echo&lt;/code&gt; in that by default escaped characters are interpreted as two character literals rather than the intended escaped character. While &lt;code&gt;echo -e&lt;/code&gt; does provide a means to do so, &lt;code&gt;sort&lt;/code&gt; does not. So we must use the ANSI-C quoting (&lt;a href=&quot;https://www.google.com/search?&amp;amp;ie=UTF-8&amp;amp;q=bash+sort+tab&quot; target=&quot;_blank&quot;&gt;or some other means&lt;/a&gt;).&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/7174330792869192411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2011/12/bash-sort-what-hell-is-up-with-tabs.html#comment-form' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/7174330792869192411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/7174330792869192411'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2011/12/bash-sort-what-hell-is-up-with-tabs.html' title='Bash sort... what the hell is up with tab separators?'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-6667246260987603876</id><published>2011-07-09T15:59:00.000-04:00</published><updated>2011-07-09T15:59:01.803-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Robot Chicken"/><category scheme="http://www.blogger.com/atom/ns#" term="Star Wars"/><title type='text'>Robot Chicken - Star Wars Episode III</title><content type='html'>&lt;a imageanchor=&quot;1&quot; target=&quot;_blank&quot;  href=&quot;http://www.amazon.com/Robot-Chicken-Star-Wars-III/dp/B004SMTO62?ie=UTF8&amp;tag=machinewashsa-20&amp;link_code=bil&amp;camp=213689&amp;creative=392969&quot;&gt;&lt;img alt=&quot;Robot Chicken: Star Wars III&quot; src=&quot;http://ws.amazon.com/widgets/q?MarketPlace=US&amp;ServiceVersion=20070822&amp;ID=AsinImage&amp;WS=1&amp;Format=_SL160_&amp;ASIN=B004SMTO62&amp;tag=machinewashsa-20&quot; /&gt;&lt;/a&gt;&lt;img src=&quot;http://www.assoc-amazon.com/e/ir?t=machinewashsa-20&amp;l=bil&amp;camp=213689&amp;creative=392969&amp;o=1&amp;a=B004SMTO62&quot; width=&quot;1&quot; height=&quot;1&quot; border=&quot;0&quot; alt=&quot;&quot; style=&quot;border:none !important; margin:0px !important; padding: 0px !important&quot; /&gt;Here we go again. I really can not get enough of these guys. I won&#39;t break it down this time. Just sit back and enjoy as Palpatine looks back on his life: &lt;a href=&quot;http://goo.gl/VdorT&quot;&gt;full video at adultswim.com&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/6667246260987603876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2011/07/robot-chicken-star-wars-episode-iii.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/6667246260987603876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/6667246260987603876'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2011/07/robot-chicken-star-wars-episode-iii.html' title='Robot Chicken - Star Wars Episode III'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-1086269103031887693</id><published>2010-09-29T16:23:00.000-04:00</published><updated>2010-09-29T16:23:38.381-04:00</updated><title type='text'>Kindle for the web!</title><content type='html'>&lt;iframe src=&quot;http://rcm.amazon.com/e/cm?t=machinewashsa-20&amp;o=1&amp;p=8&amp;l=bpl&amp;asins=1594488843&amp;fc1=000000&amp;IS2=1&amp;lt1=_blank&amp;m=amazon&amp;lc1=0000FF&amp;bc1=000000&amp;bg1=FFFFFF&amp;f=ifr&quot; style=&quot;align:left;padding-top:5px;width:131px;height:245px;padding-right:10px;&quot;align=&quot;left&quot; scrolling=&quot;no&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;Amazon has rolled out a new feature that allows people more easily preview a book online, but more importantly to anyone who has a blog, a very easy way to embed a preview of their favorite books into their blog. Pretty nifty if you ask me.&lt;br /&gt;
&lt;br /&gt;
For instance, I just finished this book by Daniel Pink, &#39;Drive: The Surprising Truth About What Motivates Us&#39;. And I have to say, it was an incredible read that provided an invaluable background firmly rooted in a variety of studies and academic research. I have already been working on various ways of employing intrinsic motivation at my work, but this has really helped to focus those plans and to create convincing arguments for why we should embrace this type of approach.&lt;br /&gt;
&lt;br /&gt;
Now with this new Kindle preview, I can embed a preview on this blog for the book, so you can see how it works!&lt;br /&gt;
&lt;br /&gt;
&lt;div id=&quot;kindleReaderDiv&quot;&gt;&lt;/div&gt;&lt;script src=&quot;http://kindleweb.s3.amazonaws.com/app/KindleReader-min.js&quot; type=&quot;text/javascript&quot;&gt;
&lt;/script&gt;&lt;script&gt;
KindleReader.LoadSample({containerID: &#39;kindleReaderDiv&#39;, asin: &#39;B002DW92T8&#39;, width: &#39;650&#39;, height: &#39;600&#39;, assoctag: &#39;behindthedump-20&#39;});
&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/1086269103031887693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2010/09/kindle-for-web.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/1086269103031887693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/1086269103031887693'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2010/09/kindle-for-web.html' title='Kindle for the web!'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-8097894461374209679</id><published>2010-05-11T09:41:00.001-04:00</published><updated>2010-05-11T17:14:29.452-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Robot Chicken"/><category scheme="http://www.blogger.com/atom/ns#" term="Star Wars"/><title type='text'>Robot Chicken - Star Wars Episode II</title><content type='html'>&lt;a href=&quot;http://www.amazon.com/Robot-Chicken-Star-Wars-Episode/dp/B001YXXQYE?ie=UTF8&amp;amp;tag=machinewashsa-20&amp;amp;link_code=bil&amp;amp;camp=213689&amp;amp;creative=392969&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot; target=&quot;_blank&quot;&gt;&lt;img alt=&quot;Robot Chicken: Star Wars - Episode II&quot; src=&quot;http://ws.amazon.com/widgets/q?MarketPlace=US&amp;amp;ServiceVersion=20070822&amp;amp;ID=AsinImage&amp;amp;WS=1&amp;amp;Format=_SL160_&amp;amp;ASIN=B001YXXQYE&amp;amp;tag=machinewashsa-20&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Seth_Green&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;1&quot; src=&quot;http://www.assoc-amazon.com/e/ir?t=machinewashsa-20&amp;amp;l=bil&amp;amp;camp=213689&amp;amp;creative=392969&amp;amp;o=1&amp;amp;a=B001YXXQYE&quot; style=&quot;border: none !important; margin: 0px !important; padding: 0px !important;&quot; width=&quot;1&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://bit.ly/9CE5LM&quot;&gt;Seth Green&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href=&quot;http://bit.ly/bkmOjC&quot;&gt;Matt Senreich&lt;/a&gt;&amp;nbsp;are back at it again with their take on the Star Wars Universe. They&amp;nbsp;definitely take a similar angle on how I looked at the conclusion of the original trilogy: what about the rest of the Galactic Empire? How did blowing up the Death Star again finally defeat the whole Empire? &lt;a href=&quot;http://bit.ly/d7M0V0&quot;&gt;Well, it&#39;s in the rules of course&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
My absolute favorite skit is &lt;a href=&quot;http://bit.ly/cXPhw2&quot;&gt;Palpatine&#39;s trip&lt;/a&gt;. I completely empathize with him in his traveling woes.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Lost bag: check&lt;/li&gt;
&lt;li&gt;Wearing ridiculous clothing while waiting for your bag to be found: check&lt;/li&gt;
&lt;li&gt;Spilling a drink in your lap because of a shoddy tray table: check&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Once again, you can watch the &lt;a href=&quot;http://bit.ly/dfsMBC&quot;&gt;full video at adultswim.com&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/8097894461374209679/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2010/05/robot-chicken-star-wars-episode-ii.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/8097894461374209679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/8097894461374209679'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2010/05/robot-chicken-star-wars-episode-ii.html' title='Robot Chicken - Star Wars Episode II'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-2246021053123963389</id><published>2010-05-02T15:39:00.003-04:00</published><updated>2010-05-11T17:12:54.008-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Robot Chicken"/><category scheme="http://www.blogger.com/atom/ns#" term="Star Wars"/><title type='text'>Robot Chicken - Star Wars</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;a href=&quot;http://www.amazon.com/Robot-Chicken-Star-Candace-Bailey/dp/B0012Q3T6S?ie=UTF8&amp;amp;tag=machinewashsa-20&amp;amp;link_code=bil&amp;amp;camp=213689&amp;amp;creative=392969&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot; target=&quot;_blank&quot;&gt;&lt;img alt=&quot;Robot Chicken: Star Wars&quot; src=&quot;http://ws.amazon.com/widgets/q?MarketPlace=US&amp;amp;ServiceVersion=20070822&amp;amp;ID=AsinImage&amp;amp;WS=1&amp;amp;Format=_SL160_&amp;amp;ASIN=B0012Q3T6S&amp;amp;tag=machinewashsa-20&quot; /&gt;&lt;/a&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;1&quot; src=&quot;http://www.assoc-amazon.com/e/ir?t=machinewashsa-20&amp;amp;l=bil&amp;amp;camp=213689&amp;amp;creative=392969&amp;amp;o=1&amp;amp;a=B0012Q3T6S&quot; style=&quot;border: none !important; margin: 0px !important; padding: 0px !important;&quot; width=&quot;1&quot; /&gt;Hopefully most of you have seen this many times over by now, but this treatment of the Star Wars movies still cracks me up no matter how often I watch it.&lt;br /&gt;
&lt;br /&gt;
My absolute two favorite skits are the&amp;nbsp;&lt;a href=&quot;http://bit.ly/dd2jk8&quot;&gt;Emperor talking to Darth Vader on the phone&lt;/a&gt;&amp;nbsp;and what really happened with&amp;nbsp;&lt;a href=&quot;http://bit.ly/96GIjg&quot;&gt;Ponda Baba in the Cantina scene&lt;/a&gt;. &quot;But, I&#39;m ambidextrous. See?&quot; Ahahhahahaha :-P&lt;br /&gt;
&lt;br /&gt;
You can check out the &lt;a href=&quot;http://bit.ly/bagUGm&quot;&gt;full show on adultswim.com&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/2246021053123963389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2010/05/robot-chicken-star-wars.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/2246021053123963389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/2246021053123963389'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2010/05/robot-chicken-star-wars.html' title='Robot Chicken - Star Wars'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-243756460530275691</id><published>2010-04-30T08:06:00.005-04:00</published><updated>2012-01-09T12:39:51.454-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="alias"/><category scheme="http://www.blogger.com/atom/ns#" term="bash"/><category scheme="http://www.blogger.com/atom/ns#" term="grep"/><title type='text'>Passing bash arguments to an alias</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
A buddy of mine was trying to figure out why he could only pass &lt;code&gt;$@&lt;/code&gt; to the end of an alias and not somewhere before. His failed attempt looked something like this:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ alias psgrep=&#39;ps wwax | grep $@ | grep -v grep&#39;
$ psgrep Terminal
Usage: grep [OPTION]... PATTERN [FILE]...
Try `grep --help&#39; for more information.
grep: Terminal: No such file or directory
&lt;/pre&gt;
His successful attempt was swapping the two greps as such:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ alias psgrep=&#39;ps wwax | grep -v grep | grep $@&#39;
$ psgrep Terminal
98893   ??  R      0:00.48 /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal -psn_0_3666815
&lt;/pre&gt;
At first glance, this does seem odd, how could it properly interpret &lt;code&gt;$@&lt;/code&gt; in the second by not the first? Well, to answer that question, we first need to ask if it is really doing just that. Is &lt;code&gt;$@&lt;/code&gt; properly interpreted in the second attempt? Lets find out:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ alias psgrep=&#39;echo &quot;ps wwax | grep -v grep | grep [$@]&quot;&#39;
$ psgrep Terminal
ps wwax | grep -v grep | grep [] Terminal
&lt;/pre&gt;
Hmmmm, that definitely does not appear to be the case. So, what about the first attempt then:&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ alias psgrep=&#39;echo &quot;ps wwax | grep [$@] | grep -v grep&quot;&#39;
$ psgrep Terminal
ps wwax | grep [] | grep -v grep Terminal
&lt;/pre&gt;
Now that is very enlightening. The error from the first attempt is not from the first grep as it appeared to be, but the second grep. Since grep can take a list of files, it assumed the now appended &#39;Terminal&#39; argument was another file to search through.&lt;br /&gt;
&lt;br /&gt;
That solves our question of whether or not it is interpreted at all, but not why it doesn&#39;t work. Well, it does, but only when it was first assigned to the alias. So, when the set alias command is executed, &lt;code&gt;$@&lt;/code&gt; is interpreted (correctly) as undefined, and therefore the alias is set as such. When the alias is then executed later, $@ has already been interpreted and any arguments are appended to the aliased command. That is why the second attempt appeared to work correctly.&lt;br /&gt;
&lt;br /&gt;
So how would do something like this? One approach would be to use a bash function:&lt;br /&gt;
&lt;br /&gt;
(first a little cleanup from the previous examples)&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ unalias psgrep
&lt;/pre&gt;
(now the actual example)&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ function psgrep() { ps wwax | grep $@ | grep -v grep ; }
$ psgrep Terminal
98893   ??  R      0:01.83 /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal -psn_0_3666815
&lt;/pre&gt;
(finally, to unset the function if you wish)&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ unset psgrep
&lt;/pre&gt;
Enjoy!&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;UPDATE&lt;/b&gt;: In yet another case of RTFM, I decided to read up on the &lt;a href=&quot;http://bit.ly/bj915n&quot; target=&quot;_blank&quot;&gt;man page for sh&lt;/a&gt;. It literally states &quot;There is no mechanism for using arguments in the replacement text. If arguments are needed, a shell function should be used (see FUNCTIONS below)&quot;. So, there you go. At least my conclusion was in line with the documentation. :-P&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/243756460530275691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2010/04/passing-bash-arguments-to-alias.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/243756460530275691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/243756460530275691'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2010/04/passing-bash-arguments-to-alias.html' title='Passing bash arguments to an alias'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8124478401700361166.post-1973731928835187650</id><published>2010-04-24T12:55:00.016-04:00</published><updated>2012-01-09T12:41:14.549-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Firefox"/><category scheme="http://www.blogger.com/atom/ns#" term="OSX"/><category scheme="http://www.blogger.com/atom/ns#" term="web dev"/><title type='text'>10 steps to running multiple versions of Firefox simultaneously on OS X 10.6</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKnGsF1JtACBx_m4_hlM9EXqrNJLy-GAZnoQOHoJd54Wv06gY5D1xkEl4KRbxYtoqcgpvZ7iNhs6yMAjlI_kAcKJA4ygf-nNpP6UH9s-dk2bFKi-zz57MQVZdkSSs9X4BNEfPo_L9EQBYd/s1600/Screen+shot+2010-04-23+at+3.27.05+PM.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;313&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKnGsF1JtACBx_m4_hlM9EXqrNJLy-GAZnoQOHoJd54Wv06gY5D1xkEl4KRbxYtoqcgpvZ7iNhs6yMAjlI_kAcKJA4ygf-nNpP6UH9s-dk2bFKi-zz57MQVZdkSSs9X4BNEfPo_L9EQBYd/s320/Screen+shot+2010-04-23+at+3.27.05+PM.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;b&gt;UPDATE&lt;/b&gt;: I have verified this works on OSX 10.4 and 10.5&lt;br /&gt;
&lt;b&gt;UPDATE 2&lt;/b&gt;: I have verified this works with FF 4.0 as well&lt;br /&gt;
&lt;b&gt;UPDATE 3&lt;/b&gt;: I have verified this works on OSX 10.7 as well as with FF 5.0+ (and the auto updating to new versions)&lt;br /&gt;
&lt;br /&gt;
When I need to debug web apps in multiple versions of Firefox on a Mac, a major hurdle is not being able to concurrently run more than one version from the Applications folder. Simply adding the different versions under different names to the folder doesn&#39;t work. The main issue in doing so is the various versions will share the same profile directory which is a big no-no. The problems range from constant messages about upgrades to incompatible plugins and extensions, but most importantly, not actually being able to run them simultaneously.&lt;br /&gt;
&lt;br /&gt;
So, off to the alter of the &lt;a href=&quot;http://bit.ly/9RQyqQ&quot; target=&quot;_blank&quot;&gt;sage of search&lt;/a&gt; I went to figure out how to get around this. I quickly found a few ways to achieve this goal, all of which are great if they meet your individual needs. For mine, I tried both&amp;nbsp;&lt;a href=&quot;http://bit.ly/cpz8c5&quot; target=&quot;_blank&quot;&gt;John Resig&lt;/a&gt;&#39;s &lt;a href=&quot;http://bit.ly/aCMqsV&quot; target=&quot;_blank&quot;&gt;method&lt;/a&gt;&amp;nbsp;(of the &lt;a href=&quot;http://bit.ly/apU20j&quot; target=&quot;_blank&quot;&gt;jQuery&lt;/a&gt; fame) and &lt;a href=&quot;http://bit.ly/azSdEx&quot; target=&quot;_blank&quot;&gt;Devon Govet&lt;/a&gt;&#39;s slightly more involved&amp;nbsp;&lt;a href=&quot;http://bit.ly/adRSbV&quot; target=&quot;_blank&quot;&gt;method&lt;/a&gt;. While they are essentially the same, I prefer Devon&#39;s approach since the corresponding profiles are auto loaded.&lt;br /&gt;
&lt;br /&gt;
Why would you want to have the profiles auto loaded? Without autoloading, every time you launch a version of Firefox, it will prompt you to select the profile you want to use before launching the browser. Seems simple enough, but one caveat is a profile currently in use by one version cannot be used by another version (there are some other issues with sharing profiles across versions as well). So you would need to have as many profiles for as many versions that you wish to run simultaneously. For example, since I am running three versions of Firefox, I would need three profiles.&lt;br /&gt;
&lt;br /&gt;
Again this seems simple enough. The main issue with selecting a profile every time a different version is launched (aside from being easily annoyed by popups) is installing add-ons like &lt;a href=&quot;http://bit.ly/ccD5F7&quot; target=&quot;_blank&quot;&gt;Firebug&lt;/a&gt; (an essential for debugging web apps). Firebug is &lt;a href=&quot;http://bit.ly/bsgc0y&quot; target=&quot;_blank&quot;&gt;version dependent&lt;/a&gt;&amp;nbsp;upon the version of Firefox it is installed. When you install an add-on, Firefox installs (at least some) dependent files for the add-on in the profile directory. Essentially this means you must always use the same profile for the same version of Firefox or risk screwing it up.&lt;br /&gt;
&lt;br /&gt;
Once more, seems simple enough. Name your profiles to match the versions of Firefox they are to be used with. Then when you launch a version and are prompted to select a profile, just select the corresponding profile. Up to this point, there is no significant difference between the methods suggested by John and Devon, however this is where John&#39;s approach ends and Devon continues a step further to automate the selection.&lt;br /&gt;
&lt;br /&gt;
What is the advantage for John&#39;s approach? Either the profile prompt is not a big deal for you or&amp;nbsp;you have a need to have multiple profiles per version of Firefox. However, if you only plan to have one profile per version, why waste the time (and add the risk of error) selecting a profile every time you launch the browser? For me this was the case, and is the main reason I prefer Devon&#39;s approach.&lt;br /&gt;
&lt;br /&gt;
Now we finally get to the HOW do you do all of this.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
HOW TO&lt;/h3&gt;
&lt;i&gt;&lt;b&gt;Note 1:&lt;/b&gt; For most of the steps, you could do them from either a Finder window or from a Terminal screen. One particular step will require you to use Terminal to enter some commands. Therefore I will only include Terminal instructions since you will need to repeat some steps to get to the point where you would need to execute the commands if you used Finder.&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;b&gt;Note 2:&lt;/b&gt; This tutorial assumes a fresh install of all versions of Firefox. If you are using an existing installation of one version of Firefox, ignore step two for the existing version installed only. Also, you can not simply rename the default profile as on of the version specific profiles. The underlying profile directory associated with the default profile will still be default and therefore initially be used automatically by all other versions when first loaded. I haven&#39;t tested this yet, but I believe you should be able to create the new version specific profile, copy the contents from the default directory to the new one and then remove the default profile in both the Profile Manager and the profiles folder.&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;b&gt;Note 3:&lt;/b&gt; Proceed at your own discretion.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Say you wanted to run the latest versions of Firefox 3.0, 3.5 and 3.6. First download them from mozilla.org:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://bit.ly/bXh5WK&quot; target=&quot;_blank&quot;&gt;Firefox 3.0.19&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://bit.ly/9JGN18&quot; target=&quot;_blank&quot;&gt;Firefox 3.5.9&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://bit.ly/cZyqnK&quot; target=&quot;_blank&quot;&gt;Firefox 3.6.3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
Part 1&lt;/h3&gt;
Setting up Firefox to run multiple versions simultaneously&lt;br /&gt;
&lt;br /&gt;
1. Open a Terminal screen (if you are stumped on this step, you might want to reconsider continuing on until you have familiarized yourself with terminal commands in general)&lt;br /&gt;
&lt;br /&gt;
2. Install Firefox into the Applications folder as usual (or wherever you prefer to put it but I will use the Applications folder as the installed directory)&lt;br /&gt;
&lt;br /&gt;
3. Go to the Applications folder within Terminal&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ cd /Applications
&lt;/pre&gt;
4. Rename Firefox to Firefox3.0 (or whatever you prefer)&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ mv Firefox.app Firefox3.0.app
&lt;/pre&gt;
5. &lt;b&gt;IMPORTANT&lt;/b&gt;: Launch the renamed Firefox from the Applications folder and then quit once it is fully loaded&lt;br /&gt;
&lt;br /&gt;
6. Go to the Firefox application contents folder in Terminal&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ cd Firefox3.0.app/Contents/MacOS
&lt;/pre&gt;
7. Add the profile for this version (save time and add them all now)&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ ./firefox-bin -Profilemanager
&lt;/pre&gt;
This command should launch the following dialog box:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF1Y4j4m3xVMoV7LiChKRwNOGZIvAq-zBLCIH60TPe-3M019-JK947zWTQu4VQ7KwCM2qfB1wrle0Rdvk2dkez_PlQ1tGfQpYnBUYf_pABnygANu5OpLqLTdZ-16e2Up1rX5CblY88pFTR/s1600/Screen+shot+2010-04-23+at+5.46.59+PM.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;166&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF1Y4j4m3xVMoV7LiChKRwNOGZIvAq-zBLCIH60TPe-3M019-JK947zWTQu4VQ7KwCM2qfB1wrle0Rdvk2dkez_PlQ1tGfQpYnBUYf_pABnygANu5OpLqLTdZ-16e2Up1rX5CblY88pFTR/s200/Screen+shot+2010-04-23+at+5.46.59+PM.jpg&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
- Click &quot;Create Profile&quot; and name your profile something unique per version (for example &quot;Firefox3.0&quot;)&lt;br /&gt;
- Once you have added your profiles, click &quot;Exit&quot;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Part 2 (optional)&lt;/h3&gt;
Setting up Firefox to auto select the correct profile on launch&lt;br /&gt;
&lt;br /&gt;
8. Copy the existing firefox-bin to a new file (for example &quot;firefox-bin_3_0&quot;)&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ cp firefox-bin firefox-bin_3_0
&lt;/pre&gt;
9. Edit firefox-bin to select the profile that was created for this version (&quot;Firefox3.0&quot;)&lt;br /&gt;
&lt;pre class=&quot;brush:bash&quot;&gt;$ echo -e &#39;#!/bin/bash\n/Applications/Firefox3.0.app/Contents/MacOS/firefox-bin_3_0 -P Firefox3.0 &amp;amp;&#39; &amp;gt; firefox-bin
&lt;/pre&gt;
10. You&#39;re done! Now launch Firefox3.0 from the Applications folder and save to dock if you wish.&lt;br /&gt;
&lt;br /&gt;
Repeat these steps for each version (3.5 and 3.6 in this example) noting to change the names to something corresponding to the version you want to install in the appropriate steps.&lt;br /&gt;
&lt;br /&gt;
Enjoy!&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jasoncust.com/feeds/1973731928835187650/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.jasoncust.com/2010/04/osx-106-running-multiple-versions-of.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/1973731928835187650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8124478401700361166/posts/default/1973731928835187650'/><link rel='alternate' type='text/html' href='http://blog.jasoncust.com/2010/04/osx-106-running-multiple-versions-of.html' title='10 steps to running multiple versions of Firefox simultaneously on OS X 10.6'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/14850542077776403164</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKnGsF1JtACBx_m4_hlM9EXqrNJLy-GAZnoQOHoJd54Wv06gY5D1xkEl4KRbxYtoqcgpvZ7iNhs6yMAjlI_kAcKJA4ygf-nNpP6UH9s-dk2bFKi-zz57MQVZdkSSs9X4BNEfPo_L9EQBYd/s72-c/Screen+shot+2010-04-23+at+3.27.05+PM.jpg" height="72" width="72"/><thr:total>0</thr:total></entry></feed>