<?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: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-7300166937153376029</id><updated>2011-12-16T16:30:24.661-05:00</updated><category term='mobile'/><category term='alistapart'/><category term='user experience'/><category term='jQuery'/><category term='javascript'/><category term='mysql'/><category term='plugin manager'/><category term='loops'/><category term='ajax'/><category term='URL'/><category term='wii'/><category term='event'/><category term='tricky'/><category term='URI'/><category term='fox'/><category term='cost-of-living'/><category term='los angeles'/><category term='observer'/><category term='css'/><category term='survey'/><category term='plugin'/><category term='browser'/><category term='hashchange'/><category term='IE8'/><category term='nintendo'/><category term='micro-optimization'/><category term='stylesheets'/><category term='asp.net'/><category term='performance'/><category term='charlotte'/><category term='code'/><category term='testing'/><category term='programs'/><category term='recursion'/><title type='text'>Aaron Heckmann</title><subtitle type='html'>Stuff from my head</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>24</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-5428086047234238979</id><published>2010-10-12T17:02:00.001-04:00</published><updated>2010-10-12T17:03:46.843-04:00</updated><title type='text'>Javascript RegExp</title><content type='html'>&lt;a href='https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/RegExp' title='JS RegExp ignoreCase'&gt;&lt;img src='http://static.jsconf.us/promotejsv.gif' height='280' width='160' alt='JS RegExp ignoreCase'/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-5428086047234238979?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/5428086047234238979/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=5428086047234238979' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/5428086047234238979'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/5428086047234238979'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2010/10/promote-js.html' title='Javascript RegExp'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-3639355985738888988</id><published>2010-10-08T21:15:00.006-04:00</published><updated>2010-10-08T21:21:17.377-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programs'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><title type='text'>What are Programs?</title><content type='html'>To quote the wise &lt;a href="http://groups.google.com/group/nodejs/msg/16b93876a125192e"&gt;Isaac Schlueter&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Some programs are community centers, and you want to entice people to &lt;br /&gt;come inside. &lt;br /&gt;&lt;br /&gt;Some programs are factories, and you want to make sure the workers &lt;br /&gt;don't hurt themselves. &lt;br /&gt;&lt;br /&gt;Some programs are shops, and you want people to visit, but only a few &lt;br /&gt;go in the back.&lt;br /&gt; &lt;br /&gt;And then some programs are art, and while you might hope that some &lt;br /&gt;people find them beautiful or perhaps even useful, and contributions &lt;br /&gt;may be accepted and feedback always welcome, the real purpose is just &lt;br /&gt;to hang it in your house and let it bring you joy. &lt;br /&gt;&lt;br /&gt;There is not just one kind of thing in this world. &lt;br /&gt;&lt;br /&gt;--i &lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-3639355985738888988?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/3639355985738888988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=3639355985738888988' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/3639355985738888988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/3639355985738888988'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2010/10/what-are-programs.html' title='What are Programs?'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-7484713751935293111</id><published>2010-01-23T15:05:00.009-05:00</published><updated>2010-01-23T15:14:37.008-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='recursion'/><category scheme='http://www.blogger.com/atom/ns#' term='micro-optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='loops'/><title type='text'>Micro-optimization: Javascript Recursion vs. Loops</title><content type='html'>&lt;p&gt;I was just reading &lt;a href="http://perfectionkills.com/"&gt;Juriy Zaytsev's&lt;/a&gt; excellent &lt;a href="http://perfectionkills.com/understanding-delete/"&gt;post&lt;/a&gt; about the &lt;code&gt;delete&lt;/code&gt; operator and got to this statement:&lt;/p&gt;&lt;br /&gt;&lt;blockquote&gt;"Even if function is calling itself recursively, a new execution context is being entered with every invocation."&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;This surprises me. I figured that javascript engines were able to detect recursion and reuse the same execution context somehow. So is recursion in javascript even more expensive than I thought? I knew that calling a function on each iteration of a loop was slow (compared to straight up loops) but how does recursion compare? To find out I set up a quick experiment to benchmark three different options:&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Recursion&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A function called on each iteration of a loop (function loop)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A vanilla loop (no function calls)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;Each benchmark will run a maximum of 490 iterations per test with each test running 400 times per page load. The test page will load 50 times in each browser. A maximum of 490 iterations is used because Safari 3.2 begins throwing "too much recursion" exceptions at 500 and there are a few overhead calls for each test. Here are is the code I used:&lt;/p&gt;&lt;h4&gt;Recursion&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;function () {&lt;br /&gt;    var i = 0,&lt;br /&gt;        rec = function () {&lt;br /&gt;            i++;&lt;br /&gt;            if ( i &lt; 490 ) rec();&lt;br /&gt;        }&lt;br /&gt;    ;      &lt;br /&gt;    rec();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;h4&gt;Function loop&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;function () {&lt;br /&gt;    var i = 0,&lt;br /&gt;        fn = function () {&lt;br /&gt;            i++;                &lt;br /&gt;        }&lt;br /&gt;    ;&lt;br /&gt;&lt;br /&gt;    for (; i &lt; 490; ) fn();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;h4&gt;Vanilla loop&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;function () {&lt;br /&gt;    var i = 0;&lt;br /&gt;    for (; i &lt; 490; ) {&lt;br /&gt;        i++;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;What were the results? Here's the breakdown by browser type. Green background means it's the fastest loop method when functions are involved. Bold text means it's the fasted loop method overall.&lt;/p&gt;&lt;style type="text/css"&gt;.bmresults th{ text-align:left;} .bmresults td{ padding: 2px 5px; } .bmresults .best { font-weight: bold; color: #073F44; }.bmresults .recbest {  background-color: #AFA;}&lt;/style&gt;&lt;br /&gt;&lt;table class="bmresults"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Browser&lt;/th&gt;&lt;th&gt;recursion avg&lt;/th&gt;&lt;th&gt;fn loop avg&lt;/th&gt;&lt;th&gt;loop avg&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firefox 3.5.7 Mac OSX 10.5.8&lt;/td&gt;&lt;td class="recbest"&gt;92.440&lt;/td&gt;&lt;td&gt;200.400&lt;/td&gt;&lt;td class="best"&gt;1.240&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Safari 3.2 Mac OSX&lt;/td&gt;&lt;td&gt;70.620&lt;/td&gt;&lt;td class="recbest"&gt;39.980&lt;/td&gt;&lt;td class="best"&gt;7.660&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Safari 4 Mac OSX 10.5.8&lt;/td&gt;&lt;td&gt;6.500&lt;/td&gt;&lt;td class="recbest"&gt;4.420&lt;/td&gt;&lt;td class="best"&gt;0.600&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firefox 3.5.7 WinXP&lt;/td&gt;&lt;td class="recbest"&gt;76.680&lt;/td&gt;&lt;td&gt;179.940&lt;/td&gt;&lt;td class="best"&gt;2.580&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firefox 3.0.7 WinXP&lt;/td&gt;&lt;td&gt;63.180&lt;/td&gt;&lt;td class="recbest"&gt;43.020&lt;/td&gt;&lt;td class="best"&gt;6.460&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firefox 2 WinXP&lt;/td&gt;&lt;td&gt;861.340&lt;/td&gt;&lt;td class="recbest"&gt;775.600&lt;/td&gt;&lt;td class="best"&gt;14.300&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Chrome 4.0.288.1 WinXP&lt;/td&gt;&lt;td class="recbest"&gt;1.720&lt;/td&gt;&lt;td class="recbest"&gt;1.720&lt;/td&gt;&lt;td class="best"&gt;0.140&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Safari 4 WinXP&lt;/td&gt;&lt;td&gt;4.780&lt;/td&gt;&lt;td class="recbest"&gt;3.780&lt;/td&gt;&lt;td class="best"&gt;1.540&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Internet Explorer8 WinXP&lt;/td&gt;&lt;td&gt;137.880&lt;/td&gt;&lt;td class="recbest"&gt;66.260&lt;/td&gt;&lt;td class="best"&gt;3.800&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Internet Explorer7 WinXP&lt;/td&gt;&lt;td&gt;418.980&lt;/td&gt;&lt;td class="recbest"&gt;388.180&lt;/td&gt;&lt;td class="best"&gt;18.380&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Internet Explorer6 WinXP&lt;/td&gt;&lt;td&gt;512.260&lt;/td&gt;&lt;td class="recbest"&gt;479.860&lt;/td&gt;&lt;td class="best"&gt;16.640&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Opera 10.10 WinXP&lt;/td&gt;&lt;td class="recbest"&gt;22.860&lt;/td&gt;&lt;td&gt;23.400&lt;/td&gt;&lt;td class="best"&gt;8.460&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Safari 3.2.3 WinXP&lt;/td&gt;&lt;td&gt;93.020&lt;/td&gt;&lt;td class="recbest"&gt;52.220&lt;/td&gt;&lt;td class="best"&gt;12.900&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Internet Explorer8 Vista&lt;/td&gt;&lt;td&gt;192.760&lt;/td&gt;&lt;td class="recbest"&gt;124.700&lt;/td&gt;&lt;td class="best"&gt;9.360&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="font-weight:bold;"&gt;Total&lt;/td&gt;&lt;td&gt;2555.020&lt;/td&gt;&lt;td class="recbest"&gt;2383.480&lt;/td&gt;&lt;td class="best"&gt;104.060&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;Function loops were faster overall when nothing was happening on each iteration. Let's test again with a little more going on; hopefully somewhat closer to a real world application. The code:&lt;/p&gt;&lt;h4&gt;Recursion&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;function () {&lt;br /&gt;    var i = 0,&lt;br /&gt;        rec = function () {&lt;br /&gt;            var c = i + "a" + (Math.random() / i);&lt;br /&gt;            i++;&lt;br /&gt;            if ( i &lt; 490 ) rec();&lt;br /&gt;        }&lt;br /&gt;    ;      &lt;br /&gt;    rec();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;h4&gt;Function loop&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;function () {&lt;br /&gt;    var i = 0,&lt;br /&gt;        fn = function () {&lt;br /&gt;            var c = i + "a" + (Math.random() / i);&lt;br /&gt;            i++;                &lt;br /&gt;        }&lt;br /&gt;    ;&lt;br /&gt;&lt;br /&gt;    for (; i &lt; 490; ) fn();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;h4&gt;Vanilla loop&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;function () {&lt;br /&gt;    var i = 0;&lt;br /&gt;    for (; i &lt; 490; ) {&lt;br /&gt;        var c = i + "a" + (Math.random() / i);&lt;br /&gt;        i++;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;table class="bmresults"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Browser&lt;/th&gt;&lt;th&gt;recursion avg&lt;/th&gt;&lt;th&gt;fn loop avg&lt;/th&gt;&lt;th&gt;loop avg&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firefox 3.5.7 Mac OSX 10.5.8&lt;/td&gt;&lt;td class="best recbest"&gt;1030.120&lt;/td&gt;&lt;td&gt;1542.200&lt;/td&gt;&lt;td&gt;1174.200&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Safari 3.2 Mac OSX&lt;/td&gt;&lt;td&gt;854.480&lt;/td&gt;&lt;td class="recbest"&gt;764.040&lt;/td&gt;&lt;td class="best"&gt;705.580&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Safari 4 Mac OSX 10.5.8&lt;/td&gt;&lt;td&gt;523.380&lt;/td&gt;&lt;td class="best recbest"&gt;519.420&lt;/td&gt;&lt;td&gt;534.000&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firefox 3.5.7 WinXP&lt;/td&gt;&lt;td class="recbest"&gt;750.020&lt;/td&gt;&lt;td&gt;1125.260&lt;/td&gt;&lt;td class="best"&gt;712.780&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firefox 3.0.7 WinXP&lt;/td&gt;&lt;td class="best recbest"&gt;776.860&lt;/td&gt;&lt;td&gt;787.260&lt;/td&gt;&lt;td&gt;1270.620&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firefox 2 WinXP&lt;/td&gt;&lt;td class="recbest"&gt;2458.480&lt;/td&gt;&lt;td&gt;2726.200&lt;/td&gt;&lt;td class="best"&gt;1506.620&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Chrome 4.0.288.1 WinXP&lt;/td&gt;&lt;td class="best recbest"&gt;436.360&lt;/td&gt;&lt;td&gt;448.200&lt;/td&gt;&lt;td&gt;451.060&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Safari 4 WinXP&lt;/td&gt;&lt;td&gt;370.180&lt;/td&gt;&lt;td class="recbest"&gt;367.380&lt;/td&gt;&lt;td class="best"&gt;365.240&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Internet Explorer8 WinXP&lt;/td&gt;&lt;td&gt;691.160&lt;/td&gt;&lt;td class="recbest"&gt;583.160&lt;/td&gt;&lt;td class="best"&gt;450.840&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Internet Explorer7 WinXP&lt;/td&gt;&lt;td&gt;1753.160&lt;/td&gt;&lt;td class="recbest"&gt;1711.140&lt;/td&gt;&lt;td class="best"&gt;1153.140&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Internet Explorer6 WinXP&lt;/td&gt;&lt;td&gt;1357.240&lt;/td&gt;&lt;td class="recbest"&gt;1289.140&lt;/td&gt;&lt;td class="best"&gt;841.480&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Opera 10.10 WinXP&lt;/td&gt;&lt;td&gt;682.420&lt;/td&gt;&lt;td class="recbest"&gt;656.040&lt;/td&gt;&lt;td class="best"&gt;626.000&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Safari 3.2.3 WinXP&lt;/td&gt;&lt;td&gt;897.320&lt;/td&gt;&lt;td class="recbest"&gt;846.180&lt;/td&gt;&lt;td class="best"&gt;752.440&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Internet Explorer8 Vista&lt;/td&gt;&lt;td&gt;723.300&lt;/td&gt;&lt;td class="recbest"&gt;618.580&lt;/td&gt;&lt;td class="best"&gt;481.060&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="font-weight:bold;"&gt;Total&lt;/td&gt;&lt;td class="recbest"&gt;13304.48&lt;/td&gt;&lt;td&gt;13984.199&lt;/td&gt;&lt;td class="best"&gt;11025.06&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;Keep in mind these tests did not test looping speeds only, they were effectively testing each browsers Math.random(), string concatenation speeds, etc.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;So what does this tell us?&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;The results aren't very clear in terms of execution times. Strict tests with nothing going on in each iteration tended to lean toward function loops being faster than recursion. Tests with more going on generally leaned toward recursion being faster in browsers that have tracing techniques enabled. Firefox optimized more for recursion than in other browsers. In Internet Explorer and Safari function loops were always fastest. Opera and Chrome were pretty much a wash.&lt;/p&gt;&lt;p&gt;If we think of "fastest" not in terms of overall execution time but also in terms of how many actual users would benefit from either technique, then function looping is the winner since IE still has the most market share.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;The take away on this is that it doesn't matter if you use recursion or function loops, if you &lt;em&gt;really&lt;/em&gt; want to be fast, inline your code and refrain from any function execution at all.&lt;/p&gt;&lt;br /&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-7484713751935293111?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/7484713751935293111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=7484713751935293111' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/7484713751935293111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/7484713751935293111'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2010/01/micro-optimization-javascript-recursion.html' title='Micro-optimization: Javascript Recursion vs. Loops'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-217928054858380408</id><published>2010-01-15T18:13:00.005-05:00</published><updated>2010-01-15T18:18:26.390-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin manager'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='URI'/><category scheme='http://www.blogger.com/atom/ns#' term='URL'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>Writing a jQuery plugin manager - Part 3 - Fully resolving URIs cross browser</title><content type='html'>&lt;p&gt;I recently started thinking about a &lt;a href="http://github.com/aheckmann/jQuery.use"&gt;plugin manager&lt;/a&gt; for &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;, something that could integrate with a server side file rollup service like the &lt;a href="http://developer.yahoo.com/yui/3/configurator/"&gt;YUI Configurator&lt;/a&gt;. I looked around a bit and didn't see anything quite like I wanted so I decided to roll my own.&lt;/p&gt;&lt;h3&gt;Surprises&lt;/h3&gt;&lt;p&gt;There were a few surprises along the way that I wanted to document, because I don't want to forget them and hopefully you'll find them helpful too.&lt;/p&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-1.html"&gt;Detecting when stylesheets are loaded&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-2.html"&gt;Detecting when downloaded javascript was parsed by the browser&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-3.html"&gt;Fully resolving URIs cross-browser&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;p&gt;This post will talk about fully resolving URIs cross-browser.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Overview&lt;/h3&gt;&lt;p&gt;If you've ever spent time reading URIs from anchors, scripts, or links, you probably know what this post is about. Reading these attributes cross-browser isn't straight-forward. For example, let's start with the following script tag:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;script src="/myscript.js" id="myscript"&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Now let's read the src attribute, simple right?&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;var script = document.getElementById("myscript");&lt;br /&gt;alert(script.src);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;In Firefox, Safari, Chrome, and Opera we see &lt;code&gt;http://absolute/path/to/myscript.js&lt;/code&gt;. But in Internet Explorer we see &lt;code&gt;myscript.js&lt;/code&gt;. So how do we go about absolutely resolving URIs cross-browser?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Solution&lt;/h3&gt;&lt;p&gt;It turns out that if we first create an anchor element using &lt;code&gt;.innerHTML&lt;/code&gt; and subsequently read it's &lt;code&gt;href&lt;/code&gt; attribute, it will be absolutely resolved.&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;var resolve = (function () {&lt;br /&gt;    var div = document.createElement("div");&lt;br /&gt;    return function (URI) {&lt;br /&gt;        div.innerHTML = "&amp;lt;a href='" + URI + "'&amp;gt;&amp;lt;/a&amp;gt;";&lt;br /&gt;        return div.firstChild.href;&lt;br /&gt;    }&lt;br /&gt;})();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Now let's adjust our original example to use our new &lt;code&gt;resolve&lt;/code&gt; function and we'll be all set.&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;var script = document.getElementById("myscript");&lt;br /&gt;alert( resolve(script.src) );&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;p&gt;Hopefully this was somewhat helpful, and if not, thanks for reading anyway.&lt;/p&gt;&lt;p&gt;If you're interested in using the jQuery plugin manager in your own project or are just curious, there is a demo available &lt;a href="http://aaronheckmann.net:8080/examples/jQuery.use/lib/1-0-0/"&gt;here&lt;/a&gt; and the source code is up on &lt;a href="http://github.com/aheckmann/jQuery.use"&gt;Github&lt;/a&gt;.&lt;br /&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-217928054858380408?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/217928054858380408/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=217928054858380408' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/217928054858380408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/217928054858380408'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-3.html' title='Writing a jQuery plugin manager - Part 3 - Fully resolving URIs cross browser'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-1088979418925401013</id><published>2010-01-09T13:35:00.007-05:00</published><updated>2010-01-15T18:20:48.835-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin manager'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>Writing a jQuery plugin manager - Part 2 - Detecting when downloaded javascript was parsed by the browser</title><content type='html'>&lt;p&gt;I recently started thinking about a &lt;a href="http://github.com/aheckmann/jQuery.use"&gt;plugin manager&lt;/a&gt; for &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;, something that could integrate with a server side file rollup service like the &lt;a href="http://developer.yahoo.com/yui/3/configurator/"&gt;YUI Configurator&lt;/a&gt;. I looked around a bit and didn't see anything quite like I wanted so I decided to roll my own.&lt;/p&gt;&lt;h3&gt;Surprises&lt;/h3&gt;&lt;p&gt;There were a few surprises along the way that I wanted to document, because I don't want to forget them and hopefully you'll find them helpful too.&lt;/p&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-1.html"&gt;Detecting when stylesheets are loaded&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-2.html"&gt;Detecting when downloaded javascript was parsed by the browser&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-3.html"&gt;Fully resolving URIs cross-browser&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;p&gt;This post will talk about detecting when downloaded javascript was parsed by the browser.&lt;/p&gt;&lt;h3&gt;Getting Started&lt;/h3&gt;&lt;p&gt;You may be wondering why I bother writing a post about detecting when downloaded javascript has been parsed by the browser. Well, the reason is that in my testing I found that Internet Explorer rarely threw exceptions when using globals that should have been available when my success callback fired. So I came up with a work around that continually tests whether all of the properties specified are available in the DOM.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;var _providesAvailable = function (provides) {&lt;br /&gt;    var available = true,&lt;br /&gt;        i = 0, &lt;br /&gt;        len = provides.length,&lt;br /&gt;        names,&lt;br /&gt;        nLen,&lt;br /&gt;        ns,&lt;br /&gt;        j&lt;br /&gt;    ;&lt;br /&gt;&lt;br /&gt;    while ( available &amp;&amp; i &lt; len ) {&lt;br /&gt;        names = provides[i].split('.');&lt;br /&gt;        nLen = names.length;&lt;br /&gt;        ns = window;&lt;br /&gt;        j = 0;&lt;br /&gt;&lt;br /&gt;        for (; j &lt; nLen; j++) { &lt;br /&gt;            if ( !(names[j] in ns) ) {&lt;br /&gt;                available = false;&lt;br /&gt;                break;&lt;br /&gt;            }&lt;br /&gt;            ns = ns[names[j]];&lt;br /&gt;        } &lt;br /&gt;        i++;&lt;br /&gt;    }&lt;br /&gt;    return available;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;var arrayOfExpectedProps =  [&lt;br /&gt;        'foo.bar', &lt;br /&gt;        'something.completely.different'&lt;br /&gt;    ],&lt;br /&gt;    cb = function () {&lt;br /&gt;        if (!_providesAvailable(arrayOfExpectedProps)) {&lt;br /&gt;            return setTimeout(cb, 30);&lt;br /&gt;        }&lt;br /&gt;        console.log("done");&lt;br /&gt;    }&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;cb();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While not perfect (if I pass in anything other than an array to &lt;code&gt;_providesAvailable&lt;/code&gt; it will fail) it's fine for what I'm doing. I've since switched to using jQuery's &lt;code&gt;ajax&lt;/code&gt; method and haven't had any additional problems with this bug but I still left the &lt;code&gt;_providesAvailable&lt;/code&gt; method enabled for now just to be safe.&lt;/p&gt;&lt;h3&gt;Plugin Registration&lt;/h3&gt;&lt;p&gt;With a way to now detect when global properties are in the DOM, we can specify which properties that must be available before our success callback is fired for any of our registered plugins:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;jQuery.use.add({&lt;br /&gt;    name: 'jquery-myplugin',&lt;br /&gt;    file: ['someFile.js', 'somestyles.css'],&lt;br /&gt;    provides: 'jQuery.myPlugin.sweetness'&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;jQuery.use('jquery-myplugin', function ($) {&lt;br /&gt;    // window.jQuery.myPlugin.sweetness is now available&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;In the above example we use the &lt;code&gt;jQuery.use.add&lt;/code&gt; method to register our plugin. &lt;code&gt;name&lt;/code&gt; determines which unique key you'll use to pull in all of the assets you specify, &lt;code&gt;file&lt;/code&gt; determines which file[s] to request, and &lt;code&gt;provides&lt;/code&gt; determines the global properties that need to be available in the DOM before firing your callback. For more information about what options are available, see the project on &lt;a href="http://github.com/aheckmann/jQuery.use"&gt;Github&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;p&gt;We've seen why we need a method to detect when dynamically created scripts are appended to the DOM and how to go about detecting when they're truly ready for use. Hopefully this was somewhat helpful, and if not, thanks for reading anyway.&lt;/p&gt;&lt;p&gt;If you're interested in using the jQuery plugin manager in your own project or are just curious, there is a demo available &lt;a href="http://aaronheckmann.net:8080/examples/jQuery.use/lib/1-0-0/"&gt;here&lt;/a&gt; and the source code is up on &lt;a href="http://github.com/aheckmann/jQuery.use"&gt;Github&lt;/a&gt;. Have fun!&lt;/p&gt;&lt;h3&gt;What's Next&lt;/h3&gt;&lt;p&gt;In my next post I'll cover &lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-3.html"&gt;fully resolving URIs cross-browser&lt;/a&gt;.&lt;/p&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-1088979418925401013?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/1088979418925401013/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=1088979418925401013' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/1088979418925401013'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/1088979418925401013'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-2.html' title='Writing a jQuery plugin manager - Part 2 - Detecting when downloaded javascript was parsed by the browser'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-7158874083804329551</id><published>2010-01-06T09:45:00.023-05:00</published><updated>2010-01-19T08:28:50.501-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin manager'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='stylesheets'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Writing a jQuery plugin manager - Part 1 - Detecting when stylesheets are loaded</title><content type='html'>&lt;p&gt;I recently started thinking about a &lt;a href="http://github.com/aheckmann/jQuery.use"&gt;plugin manager&lt;/a&gt; for &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;, something that could integrate with a server side file rollup service like the &lt;a href="http://developer.yahoo.com/yui/3/configurator/"&gt;YUI Configurator&lt;/a&gt;. I looked around a bit and didn't see anything quite like I wanted so I decided to roll my own.&lt;/p&gt;&lt;h3&gt;Surprises&lt;/h3&gt;&lt;p&gt;There were a few surprises along the way that I wanted to document, because I don't want to forget them and hopefully you'll find them helpful too.&lt;/p&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-1.html"&gt;Detecting when stylesheets are loaded&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-2.html"&gt;Detecting when downloaded javascript was parsed by the browser&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-3.html"&gt;Fully resolving URIs cross-browser&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;p&gt;This post will talk about detecting when stylesheets are loaded. See a &lt;a href="http://aaronheckmann.net:8080/examples/stylesheetsLoaded/gist-271241/stylesheetLoadDetection.html"&gt;demo&lt;/a&gt; or fork the &lt;a href="http://gist.github.com/271241"&gt;Gist&lt;/a&gt; on Github.&lt;/p&gt;&lt;h3&gt;Getting Started&lt;/h3&gt;&lt;p&gt;In order to get things moving we'll need a link tag pointed at some css:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;var css = document.createElement('link');&lt;br /&gt;css.type = 'text/css';&lt;br /&gt;css.rel = 'stylesheet';&lt;br /&gt;css.href = "http://groups.google.com/groups/style.css?ig=1&amp;stock=1&amp;av=4&amp;hl=en&amp;v=639";&lt;br /&gt;&lt;br /&gt;// I chose a different domain here because I want to &lt;br /&gt;// support loading stylesheets from any environment.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Implementation&lt;/h3&gt;&lt;p&gt;Now on to the good stuff. Surprise! In Internet Explorer and Opera life is easy. You can detect when the stylesheet loads by attaching an event listener directly to the element and listening for it's &lt;code&gt;load&lt;/code&gt; event. This even works cross-domain.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;jQuery(css).bind('load', function () {&lt;br /&gt;    // this stylesheet loaded!&lt;br /&gt;});&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;But this doesn't cut it for other browsers since they don't implement the &lt;code&gt;load&lt;/code&gt; event on stylesheets. Really, they don't. So I took a trip down a path of possible workarounds.&lt;/p&gt;&lt;p&gt;In Safari and Chrome we can detect when the stylesheet is loaded as soon as it's &lt;code&gt;sheet.cssRules&lt;/code&gt; properties are available.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;(function sheetLoaded () {&lt;br /&gt;    try {&lt;br /&gt;        css.sheet.cssRules;&lt;br /&gt;   &lt;br /&gt;    } catch (e) {&lt;br /&gt;        setTimeout(sheetLoaded, 50);&lt;br /&gt;        return;&lt;br /&gt;   &lt;br /&gt;    }&lt;br /&gt;    // if we got here the stylesheet is loaded&lt;br /&gt;})();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;So far so good. This even works on stylesheets loaded from other domains. But what about Firefox? Unfortunately, I could only get Firefox to cooperate with this technique when loading stylesheets from the same domain. If anyone knows a work around feel free to share it in the comments below or grab the &lt;a href="http://gist.github.com/271241"&gt;Gist&lt;/a&gt; and fork away. For now, I've left detecting when stylesheets are loaded out of my &lt;a href="http://github.com/aheckmann/jQuery.use"&gt;plugin manager&lt;/a&gt; since I want to provide consistent behavior across all supported browsers.&lt;/p&gt;&lt;h3&gt;Update&lt;/h3&gt;&lt;p&gt;&lt;a href="http://github.com/jaubourg"&gt;Julian Aubourg&lt;/a&gt; has been working on rewriting the jQuery ajax logic and discovered a &lt;a href="http://github.com/jaubourg/jquery/blob/master/src/transports/css.js"&gt;solution&lt;/a&gt; to detecting when stylesheets load in Firefox. &lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;(function sheetLoaded () {&lt;br /&gt;    try {&lt;br /&gt;        css.sheet.cssRules;&lt;br /&gt;   &lt;br /&gt;    } catch (e) {&lt;br /&gt;&lt;br /&gt;        if ( "NS_ERROR_DOM_SECURITY_ERR" != e.name ) { &lt;br /&gt;            setTimeout(sheetLoaded, 50);&lt;br /&gt;            return;&lt;br /&gt;      &lt;br /&gt;        } &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // if we got here the stylesheet is loaded&lt;br /&gt;&lt;br /&gt;})();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Here's how it works. Gecko (Firefox's layout engine) throws an NS_ERROR_DOM_INVALID_ACCESS_ERR exception if you try to access the &lt;code&gt;cssRules&lt;/code&gt; property while the link element is loading. Once the stylesheet loads it either stops throwing exceptions (in the case of same domain requests) or throws a different NS_ERROR_DOM_SECURITY_ERR exception. By watching for the security exception we can safely determine when the stylesheet is loaded.&lt;/p&gt;&lt;p&gt;I updated the &lt;a href="http://gist.github.com/271241"&gt;Gist&lt;/a&gt; and plan on implementing his great technique soon.&lt;/p&gt;&lt;br/&gt;&lt;h2&gt;Tested Browsers&lt;/h2&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Safari 4 Mac&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Firefox 3.5.7 Mac&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Chrome 3.0.195.33 WinXP&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Firefox 3.5.1 WinXP&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Opera 9.64 WinXP&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Opera 10.10 WinXP&lt;/li&gt;&lt;br /&gt;&lt;li&gt;IE 6-8 WinXP&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Safari 4 Vista&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Firefox 3.5.2 Vista&lt;/li&gt;&lt;br /&gt;&lt;li&gt;IE 7, 8 Vista&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;h3&gt;What's Next&lt;/h3&gt;&lt;p&gt;In my next post I'll cover &lt;a href="http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-2.html"&gt;detecting when downloaded javascript is parsed by the browser&lt;/a&gt;.&lt;/p&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-7158874083804329551?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/7158874083804329551/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=7158874083804329551' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/7158874083804329551'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/7158874083804329551'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2010/01/writing-jquery-plugin-manager-part-1.html' title='Writing a jQuery plugin manager - Part 1 - Detecting when stylesheets are loaded'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-4266123040530875072</id><published>2010-01-05T11:35:00.000-05:00</published><updated>2010-01-07T09:43:30.059-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><title type='text'>Auto-silence Mobile Devices?</title><content type='html'>&lt;div&gt;Maybe this is a bit too &lt;a href="http://en.wikipedia.org/wiki/Big_Brother_(Nineteen_Eighty-Four)"&gt;Big Brother&lt;/a&gt; but I think that places like libraries and movie theaters should broadcast a signal, that when detected by a mobile device, the device switches automatically to silent mode.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;This could work for airlines too. An airline could broadcast a signal that forces devices to either shut down or switch to an acceptable mode during critical times like take-off and landing.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Too big brother? Maybe. But providing greater context-sensitivity to our mobile world has huge potential.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-4266123040530875072?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/4266123040530875072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=4266123040530875072' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/4266123040530875072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/4266123040530875072'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2010/01/auto-silence-mobile-devices.html' title='Auto-silence Mobile Devices?'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-2142584709894573949</id><published>2009-12-13T07:05:00.001-05:00</published><updated>2009-12-13T07:05:25.233-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>Save MySQL</title><content type='html'>http://monty-says.blogspot.com/2009/12/help-saving-mysql.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-2142584709894573949?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/2142584709894573949/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=2142584709894573949' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/2142584709894573949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/2142584709894573949'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2009/12/save-mysql.html' title='Save MySQL'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-3744145554101073470</id><published>2009-07-24T07:25:00.004-04:00</published><updated>2009-07-24T07:29:41.063-04:00</updated><title type='text'>Legislate the poor into prosperity</title><content type='html'>&lt;blockquote&gt;&lt;span class="Apple-style-span"   style=" border-collapse: collapse;  font-family:arial;font-size:13px;"&gt;"You cannot legislate the poor into prosperity by legislating the wealthy out of prosperity. What one person receives without working for, another person must work for without receiving. The government cannot give to anybody anything that the government does not first take from somebody else.  When half of the people get the idea that they do not have to work because the other half is going to take care of them, and when the other half gets the idea that it does no good to work because somebody else is going to get what they work for, that my dear friend, is the beginning of the end of any nation. You cannot multiply wealth by dividing it."&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;Adrian Rogers, 1931&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-3744145554101073470?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/3744145554101073470/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=3744145554101073470' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/3744145554101073470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/3744145554101073470'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2009/07/legislate-poor-into-prosperity.html' title='Legislate the poor into prosperity'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-2678105776278031871</id><published>2009-05-04T20:43:00.006-04:00</published><updated>2009-07-24T07:32:25.523-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='event'/><category scheme='http://www.blogger.com/atom/ns#' term='hashchange'/><category scheme='http://www.blogger.com/atom/ns#' term='IE8'/><title type='text'>Internet Explorer 8 hashchange slowness</title><content type='html'>Is it just me or does IE8 perform terribly when trying to set window.location.hash to anything? &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I need to do some more testing but so far it varies between 500 - 3500ms just to update it. What's up with that? I actually disabled my history management completely in IE8. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="WHITE-SPACE: pre-wrap;font-family:-webkit-monospace;font-size:13;" class="Apple-style-span"&gt;&lt;pre&gt;&lt;code&gt;window.location.hash = '#somethingElse';&lt;/code&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I am using &lt;a href="http://www.asual.com/jquery/address/"&gt;http://www.asual.com/jquery/address/&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'll post again if I find any work arounds. If you find anything please post them here too.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight:bold"&gt;Update:&lt;/div&gt;&lt;div&gt;This seems to have been caused by some plugin or something on my windows box. I have not been able to recreate this issue testing on others. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-2678105776278031871?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/2678105776278031871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=2678105776278031871' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/2678105776278031871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/2678105776278031871'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2009/05/internet-explorer-8-and-browser-history.html' title='Internet Explorer 8 hashchange slowness'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-3015354845560633804</id><published>2008-09-02T20:46:00.005-04:00</published><updated>2008-09-02T21:13:04.194-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='browser'/><title type='text'>Javascript Performance</title><content type='html'>If you're interested in web browsers and javascript you're probably aware of both &lt;a href="http://www.google.com/chrome"&gt;Google Chrome&lt;/a&gt; and &lt;a href="http://ejohn.org/blog/tracemonkey/"&gt;Firefox 3 beta 1&lt;/a&gt; (tracing enabled). I won't go into the details about them except for what results I found using &lt;a href="http://dromaeo.com/"&gt;Dromaeo&lt;/a&gt; by &lt;a href="http://ejohn.org/"&gt;John Resig&lt;/a&gt; , an excellent tool that performs javascript performance testing.&lt;br /&gt;&lt;br /&gt;The results are truly amazing, Chrome absolutely stomps on Firefox 3 beta 1 in just about every catagory in the test...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;FF3beta1(tracing) VS Chrome:&lt;br /&gt;&lt;a href="http://dromaeo.com/?id=25058,25118"&gt;http://dromaeo.com/?id=25058,25118&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;"&gt;Note: Chrome shows up as Safari/525.13 in dromaeo&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;VS todays latest browsers:&lt;br /&gt;&lt;a href="http://dromaeo.com/?id=237,24882,25206,232,25058,25118"&gt;http://dromaeo.com/?id=237,24882,25206,232,25058,25118&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;"&gt;Note: Chrome shows up as Safair/525.13 - the rightmost column&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:78%;"&gt;Note: tests perfomed on Windows XP&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-3015354845560633804?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/3015354845560633804/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=3015354845560633804' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/3015354845560633804'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/3015354845560633804'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/09/javascript-speed.html' title='Javascript Performance'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-8992813309544194394</id><published>2008-08-27T16:51:00.002-04:00</published><updated>2008-08-27T16:59:11.368-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IE8'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>IE8 b2 - CSS Expressions</title><content type='html'>"CSS Expressions are no longer supported in Internet Explorer 8 Standards Mode&lt;br /&gt;&lt;br /&gt;Also known as 'Dynamic Properties', CSS expressions are a proprietary extension to CSS with a high performance cost. As of Internet Explorer 8 Beta 2, CSS expressions are not supported in IE8 standards mode. They are still supported in IE7 Strict mode and Quirks mode for backward compatibility."&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.microsoft.com/windows/internet-explorer/beta/default.aspx"&gt;http://www.microsoft.com/windows/internet-explorer/beta/default.aspx&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-8992813309544194394?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/8992813309544194394/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=8992813309544194394' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/8992813309544194394'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/8992813309544194394'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/08/ie8-b2-css-expressions.html' title='IE8 b2 - CSS Expressions'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-7844908952382626022</id><published>2008-08-27T09:35:00.003-04:00</published><updated>2008-08-27T09:44:45.257-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='browser'/><title type='text'>Cross Browser Testing</title><content type='html'>I came across a &lt;a href="http://www.crossbrowsertesting.com/"&gt;site&lt;/a&gt; the other day called CrossBrowserTesting.com. It provides free access (limited to five minute intervals) to several different operating systems and browser configurations. I kind of like it. It's like a virtual running in your browser. Once your OS launches, you just open the browser you want to test in and type in your public url. That's it! You even get to test out your page functionality since it really is a running instance of the browser and not just a screen shot.&lt;br /&gt;&lt;br /&gt;Here are their operating systems:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Windows 98 &lt;img src="http://www.crossbrowsertesting.com/images/win9815.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;li&gt;Windows XP &lt;img src="http://www.crossbrowsertesting.com/images/winxp15.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;li&gt;Windows Vista &lt;img src="http://www.crossbrowsertesting.com/images/vista15.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;li&gt;Ubuntu Linux &lt;img src="http://www.crossbrowsertesting.com/images/ubuntu15.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;li&gt;Apple OS X &lt;img src="http://www.crossbrowsertesting.com/images/x.png" height="15" width="12" /&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;and the browsers:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Firefox &lt;img src="http://www.crossbrowsertesting.com/images/firefox15.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;li&gt;Internet Explorer &lt;img src="http://www.crossbrowsertesting.com/images/ie15.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;li&gt;Mozilla &lt;img src="http://www.crossbrowsertesting.com/images/mozilla15.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;li&gt;Netscape &lt;img src="http://www.crossbrowsertesting.com/images/netscape15.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;li&gt;Opera &lt;img src="http://www.crossbrowsertesting.com/images/opera15.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;li&gt;Safari &lt;img src="http://www.crossbrowsertesting.com/images/safari15.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;li&gt;AOL &lt;img src="http://www.crossbrowsertesting.com/images/aolexpl.png" height="15" width="15" /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Paid priority access to the virtuals is also available. Check out their &lt;a href="http://www.crossbrowsertesting.com/demo.php"&gt;demo&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-7844908952382626022?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/7844908952382626022/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=7844908952382626022' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/7844908952382626022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/7844908952382626022'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/08/cross-browser-testing.html' title='Cross Browser Testing'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-365168059360745799</id><published>2008-08-13T13:15:00.004-04:00</published><updated>2008-08-13T13:37:16.010-04:00</updated><title type='text'>Fire Eagle Walk-Through Trouble</title><content type='html'>So today I was going through the walk-through for Yahoo's &lt;a href="http://fireeagle.yahoo.net/"&gt;Fire Eagle&lt;/a&gt;. If you don't know, it's a way to share your location with others people / apps. It seemed pretty cool so I thought I'd try it out. If you're not on Windows XP and running Apache then you'll probably not be interested in this post. If you are and intend on going through the PHP Fire Eagle &lt;a href="http://fireeagle.yahoo.net/developer/documentation/php_walkthru"&gt;walk-through&lt;/a&gt; then this may help you get through it.&lt;br /&gt;&lt;br /&gt;The walk-through states that you will need a copy of "curl-ca-bundle.crt" and add it into fireeagle.php. However, the link provided in the walk through did not provide the .crt file required  by Fire Eagle to do any SSL curl calls. Apparently it used to. So that left me searching on Yahoo for any other posts from anyone in the same situation. I couldn't find the file anywhere so I ended up adding this to fireeagle.php:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;   curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I added it after this line:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;   curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;See &lt;a href="http://us2.php.net/manual/sl/function.curl-setopt.php"&gt;here&lt;/a&gt; for more details on this option. I didn't have any trouble with the rest of the walk-through. Hopefully neither will you.&lt;br /&gt;&lt;br /&gt;Overall Fire Eagle is a pretty cool idea, I encourage you to &lt;a href="http://fireeagle.yahoo.net/gallery"&gt;check it out&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-365168059360745799?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/365168059360745799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=365168059360745799' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/365168059360745799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/365168059360745799'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/08/fire-eagle-walk-through-trouble.html' title='Fire Eagle Walk-Through Trouble'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-5996780833629482086</id><published>2008-08-07T20:59:00.006-04:00</published><updated>2008-08-07T21:14:22.947-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='user experience'/><category scheme='http://www.blogger.com/atom/ns#' term='wii'/><category scheme='http://www.blogger.com/atom/ns#' term='nintendo'/><title type='text'>Nintendo Wii Points Order Form Nightmare</title><content type='html'>So tonight I wanted to buy a new Nintendo Wii Ware game but I needed to buy some Wii Points first (in case you don't know it's fake money that you pay for which can only be used to buy Wii stuff). So I got online with my Wii, got into the place to buy points, filled in my credit card type, card number, experation date, billing address, and number of points - only to receive an error message informing me that my address could not be validated (invalid zip). So I clicked OK, fully expecting to return to the form with all of my data pre-filled, so I could simply adjust the zip code. But guess what I got instead? I was kicked all the way out and had to start over from the beginning - I even had to enter the number of points I wanted to purchase again! I couldn't believe it.&lt;br /&gt;&lt;br /&gt;I guess I gave Nintendo to much credit. I like the interface of the Wii in general. I like the interface on nintendo.com. They just totally failed at their ordering experience. It's as if a completely seperate group of evil ninjas broke into Nintendo and implemented it in the middle of the night. Oh well. Nobody's perfect, we all make mistakes. Just remind me next time I want to buy some Wii points to go to the store instead.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-5996780833629482086?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/5996780833629482086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=5996780833629482086' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/5996780833629482086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/5996780833629482086'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/08/nintendo-wii-points-order-form.html' title='Nintendo Wii Points Order Form Nightmare'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-6944875481182808729</id><published>2008-08-07T15:33:00.003-04:00</published><updated>2008-08-07T16:12:24.886-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cost-of-living'/><category scheme='http://www.blogger.com/atom/ns#' term='los angeles'/><category scheme='http://www.blogger.com/atom/ns#' term='fox'/><category scheme='http://www.blogger.com/atom/ns#' term='charlotte'/><title type='text'>Cost of Living</title><content type='html'>I recently turned down a dream job offer from &lt;a href="http://www.fimcareers.com/"&gt;Fox Interactive Media&lt;/a&gt; in Los Angeles that would have paid 120K a year. Some of you may be scratching your head thinking "Idiot! What were you thinking?!". Well let me tell you that it wasn't an easy decision and there were a few not so obvious factors involved.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;#1) Cost-of-living&lt;/span&gt;&lt;br /&gt;A quick search for "&lt;a href="http://search.yahoo.com/search?p=cost+of+living"&gt;cost of living&lt;/a&gt;" will return a few online calculators that you can use to compare Charlotte, NC with LA. You will quickly see the huge &lt;a href="http://cgi.money.cnn.com/tools/costofliving/costofliving.html?step=result&amp;amp;current_salary=60000&amp;amp;fromStateMenu=NC&amp;amp;from_city=Charlotte+NC&amp;amp;toStateMenu=CA&amp;amp;to_city=Los+Angeles-Long+Beach+CA&amp;amp;x=45&amp;amp;y=18"&gt;difference&lt;/a&gt;, most noticeably housing at 220.8% higher. After further research it seemed to me that to live in a relatively safe area and have a house similar to my current one would cost me about &lt;span style="font-weight: bold;"&gt;$900,000&lt;/span&gt;! Obviously that's just ridiculous! I have a family and really did not want to alter our lifestyle at all - which brings me to my next point...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;#2) Family&lt;/span&gt;&lt;br /&gt;I am unwilling to change my family's lifestyle and quality of life just to go work my dream job somewhere, anywhere for that matter. I can provide much, much more for my children here in Charlotte than I ever would in LA: a big house with a big yard in a quiet neighborhood with low crime. Also, my wife would need to stop working too which would have thrown in another financial curve.&lt;br /&gt;&lt;br /&gt;120K in LA doesn't get you very far compared to Charlotte. In fact, after my research I'm quite surprised not to hear of more people bailing on California in general. And I didn't even mention earthquakes, fires, or traffic yet! These days after I drive my &lt;span style="font-style: italic;"&gt;10 minutes&lt;/span&gt; home from work I have plenty of time to hang out with my family, every now and then stopping to think... I've got it made.&lt;br /&gt;&lt;br /&gt;Well, almost! I'd still love to be working a fulltime hardcore object oriented javascript/web 2.0 type job but maybe one will turn up around here sometime soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-6944875481182808729?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/6944875481182808729/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=6944875481182808729' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/6944875481182808729'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/6944875481182808729'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/08/cost-of-living.html' title='Cost of Living'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-8570606204114482621</id><published>2008-08-05T11:19:00.000-04:00</published><updated>2008-08-05T11:20:19.653-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='alistapart'/><category scheme='http://www.blogger.com/atom/ns#' term='survey'/><title type='text'>The Web Design Survey</title><content type='html'>&lt;a href="http://www.alistapart.com/articles/survey2008" title="The Web Design Survey, 2008"&gt;&lt;br /&gt;&lt;img src="http://aneventapart.com/webdesignsurvey/templates/ala/images/i-took-the-2008-survey.gif" alt=""/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-8570606204114482621?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/8570606204114482621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=8570606204114482621' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/8570606204114482621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/8570606204114482621'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/08/web-design-survey.html' title='The Web Design Survey'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-8566649534992841386</id><published>2008-08-04T08:45:00.003-04:00</published><updated>2008-08-04T09:15:51.708-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='tricky'/><title type='text'>Tricky Code</title><content type='html'>So here's what I think is some interesting but tricky javascript:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   window.prop || (window.prop = 'value');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If window.prop does not yet exist it evaluates to false and the interpreter continues on to the right side of the OR operator, which then assigns a "value" to it. A very terse way of ensuring a variable has a value assigned to it.&lt;br /&gt;&lt;br /&gt;I often use a similar pattern when assigning default values during the initialization phase of object instantiation:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   var MyObj = function (property) {&lt;br /&gt;       this.property = property || 'defaultValue';&lt;br /&gt;   }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here I think this code is obvious but I'm not convinced that the tricky code in the first example is obvious enough to use on it's own. It's really only slightly more concise than using the ternary operator:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   window.prop = window.prop ? window.prop : 'value';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I find this much easier to understand and should be much more familiar to non-javascripters due to it's availability in many other languages as well.&lt;br /&gt;&lt;br /&gt;So while I think it's cool code, I don't think I 'll use it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-8566649534992841386?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/8566649534992841386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=8566649534992841386' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/8566649534992841386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/8566649534992841386'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/08/tricky-code.html' title='Tricky Code'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-6568923643863267420</id><published>2008-08-04T08:43:00.003-04:00</published><updated>2008-08-04T08:45:17.650-04:00</updated><title type='text'>New Look</title><content type='html'>I decided that it was easier to read black text on a white background than vice &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;versa&lt;/span&gt; - so here's the new look. Hope you like it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-6568923643863267420?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/6568923643863267420/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=6568923643863267420' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/6568923643863267420'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/6568923643863267420'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/08/new-look.html' title='New Look'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-6189706089111357527</id><published>2008-07-21T12:54:00.010-04:00</published><updated>2008-08-14T10:02:59.702-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='observer'/><title type='text'>js Observer updated</title><content type='html'>I've been using the &lt;a href="http://aaronheckmann.net:8080/js/Observer.js"&gt;Observer&lt;/a&gt; class quite a bit and come to realize that I really wanted the publish() method to return boolean. That way if any subscriber returns false I'll know about it. Example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;var widget = new Widget();&lt;br /&gt;widget.onSomethingHappened = new Observer();&lt;br /&gt;widget.onBeforeSomethingHappened&lt;/span&gt;&lt;span style="font-family:courier new;"&gt; = new Observer();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;&lt;br /&gt;widget.onBeforeSomethingHappened.subscribe(function(){&lt;br /&gt;return false; &lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;// new&lt;/span&gt;&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;widget.onSomethingHappened&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;.subscribe(function(){&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    alert('Something just happened!');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;if ( !&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt; widget.onBeforeSomethingHappened&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;.publish() ) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;  // the following will not published b/c&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;  &lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;  &lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;// onBeforeSomethingHappened&lt;/span&gt;&lt;span style="font-family:courier new;"&gt; &lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;returned false&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;  &lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;  &lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;widget.onSomethingHappened&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;.publish();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Works for me. Thoughts?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-6189706089111357527?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/6189706089111357527/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=6189706089111357527' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/6189706089111357527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/6189706089111357527'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/07/js-observer-updated.html' title='js Observer updated'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-7906324834187453668</id><published>2008-07-15T15:41:00.013-04:00</published><updated>2008-08-14T10:03:45.186-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='observer'/><title type='text'>js Observer</title><content type='html'>&lt;div style="overflow: auto;"&gt;&lt;br /&gt;I don't think this is anything new but I thought I'd share it with you. While working on some client-side widgets I realized the Observer pattern would come in handy. So I put a little &lt;a href="http://aaronheckmann.net:8080/js/Observer.js"&gt;script&lt;/a&gt; together for it that you can use. Example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family:georgia;"&gt;var widget = new Widget();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:georgia;"&gt;widget.onSomethingHappened = new Observer();&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:georgia;"&gt;function alertTheWorld () {&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:georgia;"&gt;      alert('Hello World!');&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:georgia;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:georgia;"&gt;widget.onSomethingHappened.subscribe(alertTheWorld);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:georgia;"&gt;widget.onSomethingHappened.publish(); ==&gt; 'Hello World!'&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Besides passing in a function to the subscribe method, you may also pass in a context object as the second argument: e.g. an object in which whose context the function will be executed (think function.call(context) ). Example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;var myContext = { details: "Hello, I'm the details!" };&lt;br /&gt;&lt;br /&gt;var alertMyDetails = function () {&lt;br /&gt;alert(this.details);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;widget.onSomethingHappened.subscribe(alertMyDetails , myContext);&lt;br /&gt;widget.onSomethingHappened.publish() ==&gt; "Hello, I'm the details!"&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;You may also pass in an event object to the publish method which will be passed to each subscribed function. Example:&lt;br /&gt;&lt;pre&gt;&lt;span style="font-size:100%;"&gt;var alertObjectDetails = function (obj) {&lt;br /&gt;alert(obj.details);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;widget.onSomethingHappened.subscribe(alertObjectDetails);&lt;br /&gt;widget.onSomethingHappened.publish({ details: 'These are the details!'}) ==&gt; "These are the details!"&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yes, you can also unsubscribe: widget.onSomethingHappened.unsubscribe(alertObjectDetails, context). Just make sure if you subscribed with a certain context that you pass that same context into the unsubscribe method too.&lt;br /&gt;&lt;br /&gt;That's about it. &lt;a href="http://aaronheckmann.net:8080/js/Observer.js"&gt;Download&lt;/a&gt; and play around with it and let me know if it's missing anything or can be improved.&lt;br /&gt;&lt;br /&gt;UPDATE: This is kind of like a simple version of &lt;a href="http://developer.yahoo.com/yui/"&gt;YUI's&lt;/a&gt; &lt;a href="http://developer.yahoo.com/yui/event/"&gt;custom events&lt;/a&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-7906324834187453668?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/7906324834187453668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=7906324834187453668' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/7906324834187453668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/7906324834187453668'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/07/js-observer.html' title='js Observer'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-1291964223679217826</id><published>2008-07-12T09:14:00.004-04:00</published><updated>2008-07-12T13:22:15.462-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IE8'/><category scheme='http://www.blogger.com/atom/ns#' term='asp.net'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>New ASP.NET Ajax Roadmap ... **yawn**</title><content type='html'>The ASP.NET Ajax guys recently published their &lt;a href="http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=14924"&gt;roadmap&lt;/a&gt;. As someone who's used ASP.NET I must say it peaked my interest. But, after reading it, I realized that I just don't care anymore. I've completely given up on waiting for Microsoft to get Ajax right. Sure there are some good ideas in the roadmap but I just can't shake the feeling that they're wasting their time on the client-side library. Most of the features they're talking about adding are good but commonplace on the web today in other &lt;a href="http://jquery.com/"&gt;libraries&lt;/a&gt;, the &lt;a href="http://www.prototypejs.org/"&gt;libraries &lt;/a&gt;that we client-side developers already committed to using. Are we going to switch back for the same feature set? I won't.&lt;br /&gt;&lt;br /&gt;I don't want to rag on MS like so many others do, there are a lot of smart people working for them, people much smarter than myself. But when it comes to web development, I'm always feeling confused with the role MS plays. I see MS as desktop and server side development only. Even if their new Ajax features came out tomorrow and were the most amazing, simple, powerful, wonderful tools for web development ever made, I would hesitate to use them. I guess it's their reputation.&lt;br /&gt;&lt;br /&gt;This is why I'm having a hard time:&lt;br /&gt;&lt;br /&gt;The ASP.NET framework was not really designed to be client-side friendly. Getting client-side element ids is annoying and the first Ajax library they released was very weak. While the MS team didn't do much of anything (that I know of - feel free to correct me), other popular client-side &lt;a href="http://developer.yahoo.com/yui/"&gt;libraries&lt;/a&gt; continued to innovate, pushed forward with new features, and built up great &lt;a href="http://plugins.jquery.com/"&gt;communities&lt;/a&gt;. On top of Microsoft's historical lack of commitment to client-side web development tools, Internet Explorer has been public enemy number one for a long time.&lt;br /&gt;&lt;br /&gt;Yes, I know, things have been turning around this year. We've got &lt;a href="http://www.microsoft.com/windows/products/winfamily/ie/ie8/readiness/default.htm"&gt;IE8&lt;/a&gt; looking pretty good now and this Ajax &lt;a href="http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=14924"&gt;Roadmap&lt;/a&gt; is a big step too. But I want MS to stop playing catch-up. If they want my respect, they need to get on the forefront and stay there for a long time, publicly pushing, encouraging, and educating developers in client-side web development. Then I'll start to come around.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-1291964223679217826?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/1291964223679217826/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=1291964223679217826' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/1291964223679217826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/1291964223679217826'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/07/new-aspnet-ajax-roadmap-yawn.html' title='New ASP.NET Ajax Roadmap ... **yawn**'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-7638016346790828214</id><published>2008-07-10T22:05:00.003-04:00</published><updated>2008-07-10T22:32:22.661-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jQuery 1.2.6</title><content type='html'>I love jQuery. I was just checking out the latest version and noticed this small trick:&lt;br /&gt;&lt;br /&gt;function now(){&lt;br /&gt;   return +new Date;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;A funny little short cut for new Date().getTime().&lt;br /&gt;&lt;br /&gt;Nice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-7638016346790828214?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/7638016346790828214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=7638016346790828214' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/7638016346790828214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/7638016346790828214'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/07/jquery-126.html' title='jQuery 1.2.6'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7300166937153376029.post-2445679354507308527</id><published>2008-07-10T21:29:00.001-04:00</published><updated>2008-07-10T21:31:27.448-04:00</updated><title type='text'>My blog: it's about time</title><content type='html'>So I finally got my blog set up. Hope you enjoy it. Now I just have to think of something to post about...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7300166937153376029-2445679354507308527?l=aaronheckmann.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aaronheckmann.blogspot.com/feeds/2445679354507308527/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7300166937153376029&amp;postID=2445679354507308527' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/2445679354507308527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7300166937153376029/posts/default/2445679354507308527'/><link rel='alternate' type='text/html' href='http://aaronheckmann.blogspot.com/2008/07/my-blog-its-about-time.html' title='My blog: it&apos;s about time'/><author><name>Aaron Heckmann</name><uri>http://www.blogger.com/profile/16593937728891710208</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='26' src='http://bp0.blogger.com/_mWIg3lITSJE/SHitTbOpsFI/AAAAAAAAABM/e8AZdYztWKA/S220/blog.jpg'/></author><thr:total>0</thr:total></entry></feed>
