<?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-2121043255321350518</id><updated>2011-07-07T20:02:54.599-07:00</updated><category term='Google Maps'/><category term='i18n'/><category term='IE6'/><category term='air'/><category term='debugging'/><category term='migrating'/><category term='localization'/><category term='l10n'/><category term='file promises'/><category term='dialogs'/><category term='wireshark'/><category term='drag and drop'/><category term='TabContainer'/><category term='plugd'/><category term='adobe air'/><category term='jslint'/><category term='dair'/><category term='dojo 1.5'/><category term='dojo builds'/><category term='firefox'/><category term='jquery'/><category term='dojo dijit'/><category term='internationalization'/><category term='dojo'/><category term='dojo.behavior'/><category term='webkit'/><category term='scrollbars'/><category term='git'/><category term='plugin'/><category term='static code analysis'/><category term='browser'/><category term='IE8'/><category term='dijit'/><category term='gjslint'/><category term='source control'/><category term='regular expressions'/><category term='IE'/><category term='performance'/><category term='Internet Explorer'/><category term='dnd'/><category term='testing'/><category term='dojo.connect'/><category term='usability'/><category term='svn'/><category term='enter key'/><category term='screen resolutions'/><title type='text'>whatsthepointeh</title><subtitle type='html'>HTML, CSS, JavaScript, Adobe AIR, dojo and all things browser.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>28</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-6338626331031580344</id><published>2010-10-11T09:50:00.000-07:00</published><updated>2010-10-11T09:50:53.264-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webkit'/><category scheme='http://www.blogger.com/atom/ns#' term='scrollbars'/><category scheme='http://www.blogger.com/atom/ns#' term='adobe air'/><title type='text'>Adobe AIR Scrollbars in Mac OSX</title><content type='html'>If you are just getting started with Adobe AIR development, it might come as a bit of a surprise to see the scrollbar implementation on OSX. They're not even close to the native WebKit implementation. Check it out:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_XWpZ-T4KXPk/TLD05dDHAXI/AAAAAAAAAZI/9So817XZ4PA/s1600/Screen+shot+2010-10-09+at+3.57.09+PM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 33px; height: 250px;" src="http://3.bp.blogspot.com/_XWpZ-T4KXPk/TLD05dDHAXI/AAAAAAAAAZI/9So817XZ4PA/s400/Screen+shot+2010-10-09+at+3.57.09+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5526186010670989682" /&gt;&lt;/a&gt;&lt;br /&gt;Fugly ... I don't know where these scrollbars came from. I wonder if they're the default Flash styled scrollbars. Or if they're using the default windows scrollbars and didn't bother making specific scrollbars for mac AIR users.&lt;br /&gt;&lt;br /&gt;Regardless, with some WebKit specific CSS declarations, you can actually manage to style the scrollbars:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;::-webkit-scrollbar-button:horizontal {&lt;br /&gt; background-image:;&lt;br /&gt; background-repeat: repeat;&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar-button:horizontal:decrement {&lt;br /&gt; background-image: url(../images/chrome/scroll_arrow-left.png), url(../images/chrome/scroll_arrowbox-horizontal.png);&lt;br /&gt; background-repeat: no-repeat, repeat;&lt;br /&gt; background-position: 2px 2px, 0 0;&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar-button:horizontal:increment {&lt;br /&gt; background-image: url(../images/chrome/scroll_arrow-right.png), url(../images/chrome/scroll_arrowbox-horizontal.png);&lt;br /&gt; background-repeat: no-repeat, repeat;&lt;br /&gt; background-position: 2px 2px, 0 0;&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar-button:vertical:decrement {&lt;br /&gt; background-image: url(../images/chrome/scroll_arrow-up.png), url(../images/chrome/scroll_arrowbox-vertical.png);&lt;br /&gt; background-repeat: no-repeat, repeat;&lt;br /&gt; background-position: 2px 3px, 0 0;&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar-button:vertical:increment {&lt;br /&gt; background-image: url(../images/chrome/scroll_arrow-down.png), url(../images/chrome/scroll_arrowbox-vertical.png);&lt;br /&gt; background-repeat: no-repeat, repeat;&lt;br /&gt; background-position: 2px 3px, 0, 0;&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar {&lt;br /&gt; width: 12px;&lt;br /&gt; height: 12px;&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar-track-piece:horizontal {&lt;br /&gt; background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#F6F6F6), to(#CBCBCB), color-stop(0,#CBCBCB), color-stop(.08,#D2D2D2), color-stop(.15,#DBDBDB), color-stop(.23,#E2E2E2), color-stop(.3,#E9E9E9), color-stop(.38,#EFEFEF), color-stop(.45,#F4F4F4), color-stop(.53,#F8F8F8), color-stop(.6,#FBFBFB), color-stop(.68,#FCFCFC), color-stop(.76,#FDFDFD), color-stop(.85,#FAFAFA), color-stop(.93,#F6F6F6));&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar-track-piece:vertical {&lt;br /&gt; background: -webkit-gradient(linear, 100% 0%, 0% 0%, from(#F6F6F6), to(#CBCBCB), color-stop(.93,#CBCBCB), color-stop(.85,#D2D2D2), color-stop(.76,#DBDBDB), color-stop(.68,#E2E2E2), color-stop(.6,#E9E9E9), color-stop(.53,#EFEFEF), color-stop(.45,#F4F4F4), color-stop(.38,#F8F8F8), color-stop(.3,#FBFBFB), color-stop(.23,#FCFCFC), color-stop(.15,#FDFDFD), color-stop(.08,#FAFAFA), color-stop(0,#F6F6F6));&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar-button:end:decrement,::-webkit-scrollbar-button:end:increment {&lt;br /&gt; height: 15px;&lt;br /&gt; display: block;&lt;br /&gt; background-color: #CCC;&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar-thumb:vertical {&lt;br /&gt; background: -webkit-gradient(linear, 100% 0, 0 0, from(#2E4DBE), to(#5D5D5D), color-stop(0,#2E4DBE),color-stop(.08,#9CB5E4),color-stop(.15,#AFC6EA),color-stop(.23,#A8C0E9),color-stop(.3,#A2BEE9),color-stop(.38,#729CE4),color-stop(.45,#83AAED),color-stop(.53,#95BBFA),color-stop(.6,#A4C9FD),color-stop(.68,#B4D9FD),color-stop(.76,#BDE3FE),color-stop(.85,#B1D6FD),color-stop(.93,#5D5D5D));&lt;br /&gt; border: 1px solid #666;&lt;br /&gt; height: 50px;&lt;br /&gt; -webkit-border-radius: 6px;&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar-thumb:horizontal {&lt;br /&gt; height: 50px;&lt;br /&gt; background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#2E4DBE), to(#5D5D5D), color-stop(0,#2E4DBE),color-stop(.08,#9CB5E4),color-stop(.15,#AFC6EA),color-stop(.23,#A8C0E9),color-stop(.3,#A2BEE9),color-stop(.38,#729CE4),color-stop(.45,#83AAED),color-stop(.53,#95BBFA),color-stop(.6,#A4C9FD),color-stop(.68,#B4D9FD),color-stop(.76,#BDE3FE),color-stop(.85,#B1D6FD),color-stop(.93,#5D5D5D));&lt;br /&gt; border: 1px solid #666;&lt;br /&gt; -webkit-border-radius: 6px;&lt;br /&gt;}&lt;br /&gt;::-webkit-scrollbar-corner {&lt;br /&gt; background: #EEE;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Here's the end result:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_XWpZ-T4KXPk/TLD2XrmxPmI/AAAAAAAAAZQ/EqWDtVCOE24/s1600/Screen+shot+2010-10-09+at+4.09.42+PM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 52px; height: 262px;" src="http://2.bp.blogspot.com/_XWpZ-T4KXPk/TLD2XrmxPmI/AAAAAAAAAZQ/EqWDtVCOE24/s400/Screen+shot+2010-10-09+at+4.09.42+PM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5526187629486358114" /&gt;&lt;/a&gt;&lt;br /&gt;Not 100% perfect, but they're definitely closer to a native Mac OSX scrollbar. Might be an idea to prefix these css declarations with some kind of mac OSX identifier so Linux/Windows users don't get Mac specific scrollbars in the application.&lt;br /&gt;&lt;br /&gt;Thanks to Vasken at Eye-Fi for helping me figure out some of the gradations and background images.&lt;br /&gt;&lt;br /&gt;Some useful links:&lt;br /&gt;&lt;a href="http://almaer.com/blog/creating-custom-scrollbars-with-css-how-css-isnt-great-for-every-task"&gt;http://webkit.org/blog/363/styling-scrollbars/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://beautifulpixels.com/goodies/create-custom-webkit-scrollbar/"&gt;http://beautifulpixels.com/goodies/create-custom-webkit-scrollbar/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://almaer.com/blog/creating-custom-scrollbars-with-css-how-css-isnt-great-for-every-task"&gt;http://almaer.com/blog/creating-custom-scrollbars-with-css-how-css-isnt-great-for-every-task&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-6338626331031580344?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/6338626331031580344/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/10/adobe-air-scrollbars-in-mac-osx.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6338626331031580344'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6338626331031580344'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/10/adobe-air-scrollbars-in-mac-osx.html' title='Adobe AIR Scrollbars in Mac OSX'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TLD05dDHAXI/AAAAAAAAAZI/9So817XZ4PA/s72-c/Screen+shot+2010-10-09+at+3.57.09+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-5679027986514863518</id><published>2010-09-19T20:07:00.000-07:00</published><updated>2010-09-20T14:55:32.666-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dojo.behavior'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>dojo.behavior and jquery live</title><content type='html'>My &lt;a href="http://whatsthepointeh.blogspot.com/2010/09/dojoconnect-vs-dojobehavior.html"&gt;last blog post&lt;/a&gt; mentioned that dojo.behavior was modeled after &lt;a href="http://bennolan.com/"&gt;Ben Nolan&lt;/a&gt;'s plugin which in turn has since &lt;a href="http://bennolan.com/behaviour/"&gt;been replaced by jquery.live&lt;/a&gt;. After reading this, I made the assumption that jquery's live and dojo.behavior were analagous. Even after coding up a quick example using dojo.behavior to mirror the jquery live() example, I came to the conclusion that these two API methods offered pretty much the same functionality.&lt;br /&gt;&lt;br /&gt;However, after a quick discussion on the #dojo IRC channel, &lt;a href="http://twitter.com/brianarn"&gt;Brian Arnold&lt;/a&gt; pointed out a subtle but important difference between the two APIs. Apparently, jquery's live method listens for bubbled events on the document element and then maps these events back to jquery.live declarations. dojo.behaviour is a little different. It listens for events fired from the selector on which the behavior is added in the first place. So, if I changed my previous example to look like this:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.behavior.add({&lt;br /&gt; 'p': {&lt;br /&gt;  onclick: function(evt) {&lt;br /&gt;   dojo.place(&amp;quot;&amp;lt;p&amp;gt;Another paragraph!&amp;lt;/p&amp;gt;&amp;quot;, dojo.query('body')[0]);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;});&lt;br /&gt;dojo.behavior.apply();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;New paragraphs would &lt;span style="font-style:italic;"&gt;only&lt;/span&gt; be added when the user clicked on the first 'Click Me' paragraph. However, with jquery, if I altered the original example to look like this:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;$(&amp;quot;p&amp;quot;).live(&amp;quot;click&amp;quot;, function(){&lt;br /&gt; $(&amp;quot;body&amp;quot;).after(&amp;quot;&amp;lt;p&amp;gt;Another paragraph!&amp;lt;/p&amp;gt;&amp;quot;);&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Even clicking on the 'Another paragraph' would result in new p dom nodes being added to the document.&lt;br /&gt;&lt;br /&gt;Cheers for pointing out this difference &lt;a href="http://www.randomthink.net/"&gt;Brian&lt;/a&gt;. Also - see the comments below for one other point by Brian.&lt;br /&gt;&lt;br /&gt;One other difference between the two APIs was pointed out by &lt;a href="http://higginsforpresident.net/"&gt;Pete Higgins&lt;/a&gt; - dojo.behavior includes a 'found' function which operates a little like this:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;var myBehavior = {&lt;br /&gt;    // all &amp;lt;span&amp;gt; nodes&lt;br /&gt;    &amp;quot;span&amp;quot; : {&lt;br /&gt;        // for each:&lt;br /&gt;        found: function(n){&lt;br /&gt;            console.log('this is the first time I have encountered ', n);&lt;br /&gt;        }&lt;br /&gt;     }&lt;br /&gt;};&lt;br /&gt;dojo.behavior.add(myBehavior);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-5679027986514863518?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/5679027986514863518/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/09/dojobehavior-and-jquery-live.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/5679027986514863518'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/5679027986514863518'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/09/dojobehavior-and-jquery-live.html' title='dojo.behavior and jquery live'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-3756914006256326360</id><published>2010-09-14T09:06:00.000-07:00</published><updated>2010-09-14T09:06:00.531-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dojo.behavior'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo.connect'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>dojo.connect vs. dojo.behavior</title><content type='html'>Having used dojo for the past few years, I've become very familiar with using dojo.connect to list for events being fired on objects. The API is simple, clear and concise:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;var h = dojo.connect(dojo.byId(&amp;quot;myNode&amp;quot;), &amp;quot;onclick&amp;quot;, myInstance, &amp;quot;reactToNodeClick&amp;quot;);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://docs.dojocampus.org/quickstart/events"&gt;Plenty&lt;/a&gt; of &lt;a href="http://docs.dojocampus.org/dojo/connect"&gt;documentation&lt;/a&gt; already exists on &lt;a href="http://api.dojotoolkit.org/jsdoc/1.5/dojo.connect"&gt;dojo.connect&lt;/a&gt;, so I'm not going to rehash it out here. Reading a &lt;a href="http://davidwalsh.name/dojo-behavior"&gt;recent article&lt;/a&gt; on &lt;a href="http://api.dojotoolkit.org/jsdoc/1.5/dojo.behavior"&gt;dojo.behavior&lt;/a&gt; and couldn't help but agree that there are an enormous amount of similarities between dojo.connect and dojo.behavior. Apparently, dojo.behavior uses the dojo.connect plumbing under the covers, so I was curious to figure out why there was a separate dojo API entry-point to this exact same functionality. dojo.behavior must be offering something above and beyond dojo.connect, or at least it must expose new functionality for connecting to dom and custom events. According to comments on &lt;a href="http://dojocampus.org/content/2008/03/26/cleaning-your-markup-with-dojobehavior/"&gt;this article&lt;/a&gt;, dojo.behavior is based on &lt;a href="http://twitter.com/bnolan"&gt;Ben Nolan&lt;/a&gt;'s &lt;a href="http://bennolan.com/behaviour/"&gt;plugin&lt;/a&gt; which seems to have been deprecated in favor of jquery's &lt;a href="http://api.jquery.com/live/"&gt;live method&lt;/a&gt;. Taking a look at the docs for jquery, I took a shot at converting their example to use dojo.behavior:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;    &amp;lt;head&amp;gt;&lt;br /&gt;        &amp;lt;style&amp;gt;&lt;br /&gt;            p {&lt;br /&gt;        background: yellow;&lt;br /&gt;      font-weight: bold;&lt;br /&gt;      cursor: pointer;&lt;br /&gt;      padding: 5px;&lt;br /&gt;            }&lt;br /&gt;            p.over {&lt;br /&gt;      background: #ccc;&lt;br /&gt;            }&lt;br /&gt;            span {&lt;br /&gt;      color: red;&lt;br /&gt;            }&lt;br /&gt;        &amp;lt;/style&amp;gt;&lt;br /&gt;        &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;dojotoolkit/dojo/dojo.js&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;/script&amp;gt;&lt;br /&gt;        &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;            dojo.require(&amp;quot;dojo.behavior&amp;quot;);&lt;br /&gt;        &amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;/head&amp;gt;&lt;br /&gt;    &amp;lt;body&amp;gt;&lt;br /&gt;        &amp;lt;p&amp;gt;&lt;br /&gt;            Click me!&lt;br /&gt;        &amp;lt;/p&amp;gt;&lt;br /&gt;        &amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;        &amp;lt;script&amp;gt;&lt;br /&gt;            dojo.addOnLoad(function() {&lt;br /&gt;      dojo.behavior.add({&lt;br /&gt;        'p': {&lt;br /&gt;          onclick: function(evt) {&lt;br /&gt;            dojo.place(&amp;quot;&amp;lt;p&amp;gt;Another paragraph!&amp;lt;/p&amp;gt;&amp;quot;, dojo.query(&amp;quot;p&amp;quot;)[0]);&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;      });&lt;br /&gt;      dojo.behavior.apply();&lt;br /&gt;            });&lt;br /&gt;        &amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;So, it seems that dojo.bevahior's main advantages over dojo.connect are:&lt;br /&gt;1. behaviors are added to dom elements which have yet to be inserted into the overall document. This allows the developer to organize all the behavior for a page in one place and also allows for quickly adding behavior to dynamically added dom elements. &lt;a href="http://stackoverflow.com/questions/748076/using-live-benefits-similar-to-bind"&gt;This stackoverflow question&lt;/a&gt; has a pretty nice explanation of this.&lt;br /&gt;2. You can add multiple behaviors to elements all in the one statement. Example:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.behavior.add({&lt;br /&gt;    '.foo' : {&lt;br /&gt;        found: function(node) {&lt;br /&gt;            // do something with each node found&lt;br /&gt;        }&lt;br /&gt;    },&amp;#160;&lt;br /&gt;    // shortcut for found&lt;br /&gt;    '.foo2' : function(node) {&lt;br /&gt;        // do something with the node&lt;br /&gt;    }&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;although, it could be argued that a combination of dojo.forEach and dojo.query would allow a developer to declare multiple dojo.connects in one statement. For example:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.forEach([&amp;quot;.foo&amp;quot;, &amp;quot;.foo2&amp;quot;], function(item) {&lt;br /&gt;  dojo.query(item).connect(&amp;quot;onclick&amp;quot;, function(node) {&lt;br /&gt;    // do something with the node.&lt;br /&gt;  });&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Unless I'm missing something in the dojo.behavior API I don't see a way to disconnect all the previously added behaviors. I use dojo.disconnect quite a bit when using dojo.connect to clean up after myself (phiggins article again). dojo.behavior.add doesn't return an array of handles so you cannot remove behavior once it has been added. A global suspend/resume API might be a useful addition to the dojo.behavior API to allow a developer to stop/start behaviors. It could even look like this:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.behavior.suspend(selector);&lt;br /&gt;dojo.behavior.suspendAll();&lt;br /&gt;dojo.behavior.resume(selector)&lt;br /&gt;dojo.behavior.resumeAll();&lt;br /&gt;&lt;br /&gt;// possible usage&lt;br /&gt;dojo.behavior.add({&lt;br /&gt;  'p': {&lt;br /&gt;    onclick: function(evt) {&lt;br /&gt;      dojo.place(&amp;quot;&amp;lt;p&amp;gt;Another paragraph!&amp;lt;/p&amp;gt;&amp;quot;, dojo.query(&amp;quot;p&amp;quot;)[0]);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;});&lt;br /&gt;dojo.behavior.apply();&lt;br /&gt;// stop all behavior on p elements that were just applied in the previous statement.&lt;br /&gt;dojo.behavior.suspend('p');&lt;br /&gt;// allow behaviors on p elements again&lt;br /&gt;dojo.behavior.resume('p');&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I dunno … that seems hackish when compared with dojo.connect/dojo.disconnect.&lt;br /&gt;&lt;br /&gt;Even after this exportation into dojo.behavior, I'm not sure I'd ever use it over dojo.connect/disconnect. dojo.connect/dojo.disconnect gives me a level of granularity that I'm comfortable with. Even searching through the dojo source code for references to dojo.behavior, I don't really see it being used at all in internal framework code (dojo.connect/dojo.disconnect is used EVERYWHERE though).&lt;br /&gt;&lt;br /&gt;Can you see any other uses for dojo.behavior - why would you chose to use it over dojo.connect/dojo.disconnect?&lt;br /&gt;&lt;br /&gt;Some other useful links:&lt;br /&gt;&lt;a href="http://paulirish.com/2010/on-jquery-live/"&gt;http://paulirish.com/2010/on-jquery-live/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://forum.jquery.com/topic/jquery-live-jquery-fn-live-discussion"&gt;http://forum.jquery.com/topic/jquery-live-jquery-fn-live-discussion&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-3756914006256326360?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/3756914006256326360/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/09/dojoconnect-vs-dojobehavior.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3756914006256326360'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3756914006256326360'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/09/dojoconnect-vs-dojobehavior.html' title='dojo.connect vs. dojo.behavior'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-2853336441138968506</id><published>2010-09-06T19:23:00.000-07:00</published><updated>2010-09-07T10:47:54.300-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gjslint'/><category scheme='http://www.blogger.com/atom/ns#' term='jslint'/><category scheme='http://www.blogger.com/atom/ns#' term='static code analysis'/><title type='text'>First impressions of gjslint</title><content type='html'>I saw a blog post from the &lt;a href="http://closuretools.blogspot.com/2010/08/introducing-closure-linter.html"&gt;Closure Tools Blog&lt;/a&gt; recently and it piqued my interest enough to give it a try. I've been running &lt;a href="http://http://jslint.com/"&gt;jslint&lt;/a&gt; on a codebase for the past 6 months or so and I've been a little frustrated with some of the 'errors' reported by it (I don't think I'm the &lt;a href="http://ajaxian.com/archives/warning-jslint-may-continue-to-hurt-your-feelings"&gt;only&lt;/a&gt; &lt;a href="http://webreflection.blogspot.com/2010/02/jslint-bad-part.html"&gt;one&lt;/a&gt;). jslint is not without its warts, but it has definitely forced me to take a second look at my code and made me rethink one or two coding decisions. Some of the 'issues' my source code continuously bumps into with jslint include:&lt;br /&gt;&lt;br /&gt;1. Not using the &lt;a href="http://yuiblog.com/blog/2006/11/13/javascript-we-hardly-new-ya/"&gt;new operator&lt;/a&gt;:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;Lint at line 89 character 53: Do not use 'new' for side effects.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;2. Misuse of Ternary operators:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;Lint at line 145 character 112: Expected an assignment or function call and instead saw an expression.&lt;br /&gt;!disabled ? dojo.removeClass(el, 'disabledButton') : dojo.addClass(el, 'disabledButton');&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;3. Unexpected use of &gt;&gt; &lt;&lt; &amp;amp; | &lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;return (transferMode &amp;gt;&amp;gt; 0) &amp;amp; 3;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;4. Use of === vs. ==&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;if (range.text == '') {&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Anyways, I'm always interested in seeing some static analysis of my code, so I downloaded and installed &lt;a href="http://code.google.com/closure/utilities/docs/linter_howto.html"&gt;gjslint&lt;/a&gt;. Running it is pretty simple - just point it at a directory full of .js files:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;gjslint -r path/to/my/directory&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Oddly, none of the issues which kept on showing up in the regular jslint checker appeared in the results of gjslint. I guess this is because gjslint is more focused on closure's code formatting rules more so than strictly correct (at least in the eyes of jslint) JavaScript?&lt;br /&gt;&lt;br /&gt;After running gjslint on my codebase, I saw a a _lot_ of these types of errors:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;Line 54, E:0200: Invalid JsDoc tag: name&lt;br /&gt;Line 55, E:0200: Invalid JsDoc tag: function&lt;br /&gt;Line 56, E:0200: Invalid JsDoc tag: memberOf&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Apparently, the gjslint checker only allows a certain subset of &lt;a href="http://groups.google.com/group/closure-linter-discuss/browse_thread/thread/9365fb032f814d5"&gt;certain JsDoc rules&lt;/a&gt;. I tried using the --nojsdoc flag in the hope that all these 'errors' would be suppressed, but no dice.&lt;br /&gt;&lt;br /&gt;Also - 80 characters seems to be the limit that gjslint allows for JavaScript files. Would be nice if this rule could be suppressible or at least configurable. Not even sure why its a rule in the first place.&lt;br /&gt;&lt;br /&gt;For kicks, I tried passing the --strict flag to see the output. I seem to get a lot of indentation errors:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;Line 342, E:0006: (New error) Wrong indentation: expected any of {4, 16} but got 8&lt;br /&gt;Line 343, E:0006: (New error) Wrong indentation: expected any of {4, 16} but got 8&lt;br /&gt;Line 344, E:0006: (New error) Wrong indentation: expected any of {4, 16} but got 8&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I've never been a huge fan of being overtly strict when it comes to the number of spaces which represents an indentation. I use 4 for each indent - gjslint advises you to use 2 - no big deal in my books.&lt;br /&gt;&lt;br /&gt;Included in this release of gjslint, there's a handy tool called fixjsstyle which will help you automatically fix any styling issues in your code. One point of caution when running this tool though - make sure your code is under source control before you run it. Stepping through your code after running fixjsstyle reveals some more gjslint rules. For example, running fixjsstyle on a code base will convert all double quotes to single quotes. Wonder why that's in the style guide? Actually, it'd be great to see some explanations behind some of the rules.&lt;br /&gt;&lt;br /&gt;Overall - I've always felt that static code analysis of code is useful to a point. It'll definitely keep you keen when it comes to coding standards and it is useful in that it'll catch any errors that any self-respecting compiler would catch, but it'll only get you so far. I've always found gathering and understanding runtime analysis of a codebase more difficult to achieve (I still have a lot to learn in this field), but more rewarding in the end.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-2853336441138968506?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/2853336441138968506/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/09/first-impressions-of-gjslint.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/2853336441138968506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/2853336441138968506'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/09/first-impressions-of-gjslint.html' title='First impressions of gjslint'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-3671675946015768789</id><published>2010-08-30T13:19:00.000-07:00</published><updated>2010-09-05T21:38:46.974-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drag and drop'/><category scheme='http://www.blogger.com/atom/ns#' term='file promises'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><category scheme='http://www.blogger.com/atom/ns#' term='adobe air'/><title type='text'>File Promises and dojo drag and drop</title><content type='html'>When Adobe pushed AIR 2.0 out via auto update, one of the first new API features that I wanted to try to take advantage of is the File Promises API. Armed with &lt;a href="http://twitter.com/cantrell"&gt;Christian Cantrell&lt;/a&gt;'s &lt;a href="https://www.adobe.com/devnet/air/flex/articles/using_file_promises.html"&gt;article&lt;/a&gt;, I went about integrating File Promises into my application.&lt;br /&gt;&lt;br /&gt;Drag and drop isn't a new interaction paradigm in the application I work on. Users have always been able to drag and drop items in the application thanks to &lt;a href="http://docs.dojocampus.org/dojo/dnd"&gt;dojo's drag and drop APIs&lt;/a&gt;. With AIR 2.0 and File Promises though, I wanted to give the user the ability to drag files internally in the application and also out of the application and onto their desktop. To allow both internal and external drag and drop operations, I needed to override the default dojo.dnd.Avatar. Overriding the default Avatar for dojo dnd operations is really easy - the constructor for your dnd source should include the following line (basically, you need some way of telling the dnd manager to use a different function when creating the avatar):&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.dnd.manager().makeAvatar = dojo.hitch(this, &amp;quot;makeAvatar&amp;quot;);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;You'll need a makeAvatar function which returns your custom Avatar:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;makeAvatar: function() {&lt;br /&gt;    return new my.widget.FileListAvatar(dojo.dnd.manager());&lt;br /&gt;},&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Once you've told the dojo dnd manager to use your custom dnd avatar, you'll need to figure out when the avatar in question is being dragged close to the edge of the application. See the _handleMouseMove function below:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto;width:100%"&gt;&lt;code&gt;&lt;br /&gt;dojo.declare(&lt;br /&gt;    &amp;quot;my.widget.FileListAvatar&amp;quot;,&lt;br /&gt;    [dojo.dnd.Avatar],&lt;br /&gt;    {    &lt;br /&gt;        _mouseOverConnects: [],&lt;br /&gt;            &lt;br /&gt;        construct: function() {&lt;br /&gt;            // override and cancel the autoScroll function to prevent whitespace from appearing&lt;br /&gt;            // in the application when the user is dragging files near the border.&lt;br /&gt;            dojo.dnd.autoScroll = function(){};&lt;br /&gt;            this._mouseOverConnects.push(dojo.connect(this.manager, &amp;quot;onMouseMove&amp;quot;, this, &amp;quot;_handleMouseMove&amp;quot;));&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        _handleMouseMove: function() {&lt;br /&gt;            var pos, viewport = dojo.window.getBox();&lt;br /&gt;            if(this.node) {&lt;br /&gt;                pos = dojo.position(this.node);&lt;br /&gt;                if(viewport.w &amp;lt;= pos.x + pos.w &amp;#124;&amp;#124; pos.x &amp;lt;= 0&lt;br /&gt;                &amp;#124;&amp;#124; viewport.h &amp;lt;= pos.y + pos.h &amp;#124;&amp;#124; pos.y + pos.h &amp;lt;= 0) {&lt;br /&gt;                // at this stage we know that the user is dragging the avatar close to the application's boundaries.&lt;br /&gt;                // time to make a switch from an internal dnd operation to an external dnd.&lt;br /&gt;                this. _handleExternalDnd();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The _handleExternalDnd is the point in the application where we start using the AIR 2.0 file promises API. In the following code, all the items in my dnd source have a store and item property which contain information on the url and file name. The url property from each dnd item is passed to a new URLFilePromise object.&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;_handleExternalDnd: function() {&lt;br /&gt;    var f, url, cb = new air.Clipboard(), promises = [], vp = dojo.window.getBox(), isDraggedOut = false;&lt;br /&gt;    for(var j = 0, k = this.manager.nodes.length; j &amp;lt; k; j++) {&lt;br /&gt;        f = this.manager.source.getItem(this.manager.nodes[j].id).data;&lt;br /&gt;        url = f.store.getValue(f.item, &amp;quot;Url&amp;quot;);&lt;br /&gt;        if(url !== &amp;quot;&amp;quot;) {&lt;br /&gt;            var fp = new air.URLFilePromise();&lt;br /&gt;            fp.request = new air.URLRequest(url);&lt;br /&gt;            fp.relativePath = f.store.getValue(f.item, &amp;quot;FileName&amp;quot;);&lt;br /&gt;            promises.push(fp);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    cb.setData(air.ClipboardFormats.FILE_PROMISE_LIST_FORMAT, promises);&lt;br /&gt;    // the next line causes the switch from internal dnd to external dnd&lt;br /&gt;    air.NativeDragManager.doDrag(window.htmlLoader, cb);&lt;br /&gt;    air.NativeApplication.nativeApplication.activeWindow.stage.addEventListener(window.runtime.flash.events.NativeDragEvent.NATIVE_DRAG_UPDATE, function(evt) {&lt;br /&gt;        if(!isDraggedOut &amp;amp;&amp;amp; evt.localX &amp;gt; vp.w) {&lt;br /&gt;            isDraggedOut = true;&lt;br /&gt;        } else if (isDraggedOut &amp;amp;&amp;amp; evt.localX &amp;lt; vp.w) {&lt;br /&gt;            // TODO - this is the case where the file has been dragged out of the application and the user is dragging the file back in.&lt;br /&gt;            // would be nice at this stage to revert to revert back to an internal dnd operation.&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;    dojo.style(this.node, "display", "none");&lt;br /&gt;},&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I've left some commented code in there which might give you hints of what I'd like to do when the user drags files back into the application after initially dragging them out. I haven't figured out how to cancel a native drag and drop operation just yet - if I do, I'll be sure to update this blog post.&lt;br /&gt;&lt;br /&gt;That's pretty much it. Once the user drags files out of the application and into their native file system browser, the urls included in the URLFilePromise object will be downloaded. Check out some of the events you can &lt;a href="http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/flash/desktop/IFilePromise.html"&gt;connect to&lt;/a&gt; if you want to give your users a progress indicator of where their files are in terms of being downloaded. One point to note when using File Promises - users are only allowed to drag files from an AIR application into the default file browser. For example, the user cannot drag files directly from an Adobe AIR application into iPhoto or Lightroom. They need to drag the files into Finder first, wait for the files to be downloaded and then drag them into the file management application of their choice.&lt;br /&gt;&lt;br /&gt;Looking to the future, it looks like this type of functionality will start to become available natively in the browser.&lt;br /&gt;&lt;br /&gt;Some useful links which might help with further reading:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.sitepen.com/blog/2008/06/10/dojo-drag-and-drop-1/"&gt;Getting started with dojo dnd&lt;/a&gt;&lt;br /&gt;&lt;a href="http://docs.dojocampus.org/dojo/dnd"&gt;dojocampus' dnd articles&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.sitepen.com/blog/2008/10/24/inside-dojo-dnd-drag-handles/"&gt;Advanced dojo dnd tutorial&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.thecssninja.com/javascript/drag-and-drop-upload"&gt;CSS Ninja article on drag and drop&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.thecssninja.com/javascript/gmail-dragout"&gt;CSS Ninja article how gmail's dnd works&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-3671675946015768789?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/3671675946015768789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/08/file-promises-and-dojo-drag-and-drop.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3671675946015768789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3671675946015768789'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/08/file-promises-and-dojo-drag-and-drop.html' title='File Promises and dojo drag and drop'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-806509605756607617</id><published>2010-08-23T13:56:00.000-07:00</published><updated>2010-08-26T08:11:38.600-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dojo dijit'/><title type='text'>dojo.connect Widget gotcha</title><content type='html'>I'm not too sure this small issue I ran into actually constitutes a full blog post, but here goes all the same. After reading &lt;a href="http://twitter.com/phiggins"&gt;phiggins'&lt;/a&gt; &lt;a href="http://higginsforpresident.net/2010/01/widgets-within-widgets/"&gt;article&lt;/a&gt; on how to dispose of programatically created dijits, I started keeping track of the handles returned from dojo.connects and dojo.subscribes. I figured that these return values should be disconnected/unsubscribed just before a widget was destroyed to aid with garbage collection. Hence, a lot of my widgets started to look like this:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.declare(&lt;br /&gt; "my.widget.AccountCreator",&lt;br /&gt; [dijit._Widget, dijit._Templated],&lt;br /&gt; {&lt;br /&gt;   templatePath: dojo.moduleUrl("my.widget", "templates/AccountCreator.html"),&lt;br /&gt;   widgetsInTemplate: true,&lt;br /&gt;   _subscriptions: [],&lt;br /&gt;   _connects: [],&lt;br /&gt;  &lt;br /&gt;   postCreate: function() {&lt;br /&gt;     this._connects.push(dojo.connect(dijit.byId("accountCreatorDialog"), "hide", this, "_handleHideDialog"));&lt;br /&gt;     this._subscriptions.push(dojo.subscribe("/acct/created", this, "_handleAccountCreated"));&lt;br /&gt;   },&lt;br /&gt;&lt;br /&gt;   destroy: function() {&lt;br /&gt;     // first clean up my connects and handles&lt;br /&gt;     dojo.forEach(this._subscriptions, dojo.unsubscribe);&lt;br /&gt;     dojo.forEach(this._connects, dojo.disconnect);&lt;br /&gt;     this._subscriptions = [];&lt;br /&gt;     this._connects = [];&lt;br /&gt;     // now go ahead and delete the rest of the widget&lt;br /&gt;     this.inherited(arguments);&lt;br /&gt;   }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Seems relatively simple, right? Being a good memory citizen and cleaning up just before the widget gets destroyed seems like a win-win situation. However, notice that my collection of dojo.connect handles is named _connects. Check out the source code for &lt;a href="http://docs.dojocampus.org/dijit/_Widget"&gt;dijit._Widget&lt;/a&gt; (my.widget.AccountCreator inherits from dijit._Widget):&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;  create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){&lt;br /&gt;   // summary:&lt;br /&gt;   //    Kick off the life-cycle of a widget&lt;br /&gt;   // params:&lt;br /&gt;   //    Hash of initialization parameters for widget, including&lt;br /&gt;   //    scalar values (like title, duration etc.) and functions,&lt;br /&gt;   //    typically callbacks like onClick.&lt;br /&gt;   // srcNodeRef:&lt;br /&gt;   //    If a srcNodeRef (DOM node) is specified:&lt;br /&gt;   //      - use srcNodeRef.innerHTML as my contents&lt;br /&gt;   //      - if this is a behavioral widget then apply behavior&lt;br /&gt;   //        to that srcNodeRef&lt;br /&gt;   //      - otherwise, replace srcNodeRef with my generated DOM&lt;br /&gt;   //        tree&lt;br /&gt;   // description:&lt;br /&gt;   //    Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,&lt;br /&gt;   //    etc.), some of which of you'll want to override. See http://docs.dojocampus.org/dijit/_Widget&lt;br /&gt;   //    for a discussion of the widget creation lifecycle.&lt;br /&gt;   //&lt;br /&gt;   //    Of course, adventurous developers could override create entirely, but this should&lt;br /&gt;   //    only be done as a last resort.&lt;br /&gt;   // tags:&lt;br /&gt;   //    private&lt;br /&gt;&lt;br /&gt;   // store pointer to original DOM tree&lt;br /&gt;   this.srcNodeRef = dojo.byId(srcNodeRef);&lt;br /&gt;&lt;br /&gt;   // For garbage collection.  An array of handles returned by Widget.connect()&lt;br /&gt;   // Each handle returned from Widget.connect() is an array of handles from dojo.connect()&lt;br /&gt;   this._connects = [];&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Having a property of _connects on a custom dijit which inherits from dijit._Widget landed me in trouble. When my.widget.AccountCreator's destroy method was called, the call to dojo.disconnect was throwing an odd looking error:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;Result of expression '([dojo._listener, del, node_listener][listener])' [undefined] is not an object&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;As pointed out by both &lt;a href="http://twitter.com/neonstalwart"&gt;@neonstalwart&lt;/a&gt; and &lt;a href="http://twitter.com/cb1kenobi"&gt;@cb1kenobi&lt;/a&gt; (thanks!), there's a simple solution to all this - just use the connect method in dijit._Widget.&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;this.connect(dijit.byId(&amp;quot;accountCreatorDialog&amp;quot;), &amp;quot;hide&amp;quot;, &amp;quot;_handleHideDialog&amp;quot;);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;dijit._Widget's connect method will also take care of automatically disconnect each dojo.connect handle when the widget is destroyed. As a result, you don't need to override the destroy method of your custom widget to clean up any lingering handles.&lt;br /&gt;&lt;br /&gt;Have you ever stumbled into an issue like this - are there any other 'common' internal dojo variable names that you've unconsciously clobbered?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-806509605756607617?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/806509605756607617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/08/dojoconnect-widget-gotcha.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/806509605756607617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/806509605756607617'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/08/dojoconnect-widget-gotcha.html' title='dojo.connect Widget gotcha'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-619424126196128015</id><published>2010-08-18T20:47:00.001-07:00</published><updated>2010-08-19T08:26:35.541-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dojo 1.5'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>Migrating to dojo 1.5</title><content type='html'>The application I work on had been running the 1.3 version of dojo for the &lt;a href="http://whatsthepointeh.blogspot.com/2009/05/migrating-to-dojo-13.html"&gt;past 18 months or so&lt;/a&gt;. I procrastinated with upgrading to the 1.4 version as I was busy migrating an existing web based application to an Adobe AIR app. When &lt;a href="http://www.sitepen.com/blog/2010/07/22/dojo-1-5-ready-to-power-your-web-app/"&gt;dojo 1.5 rolled around&lt;/a&gt; I had some spare cycles, so I decided to make the jump directly from 1.3 to 1.5. As with any software upgrade there were some hiccups along the way - here's the few issues I stumbled across:&lt;br /&gt;&lt;br /&gt;1. &lt;a href="http://docs.dojocampus.org/dijit/layout/TabContainer"&gt;dijit.layout.TabContainer's&lt;/a&gt; default controllerWidget seems to have switched from dijit.layout.TabController to dijit.layout.ScrollingTabController. This caused some issues for me as I had declared some custom CSS stylings specifically for dijit.layout.TabController which didn't apply to dijit.layout.ScrollingTabController. Pretty simple workaround for this issue though - I just needed to specify a controllerWidget property when programatically creating new dijit.layout.TabContainer widgets.&lt;br /&gt;&lt;br /&gt;2. dojox.layout.RotatorContainer/dojox.layout.RotatorPager have been replaced by a combination of &lt;a href="http://docs.dojocampus.org/dojox/widget/AutoRotator"&gt;dojox.widget.AutoRotator&lt;/a&gt;/dojox.widget.rotator.Controller. Given that I had hacked into a lot of 'private' APIs in the 1.3 version of dojox.layout.RotatorContainer I was a little apprehensive about making the move to dojox.widget.AutoRotator. &lt;a href="http://twitter.com/cb1kenobi"&gt;@cb1kenobi&lt;/a&gt; on the dojo IRC helped out a lot with the migration and my application code is a lot cleaner looking as a result.&lt;br /&gt;&lt;br /&gt;3. &lt;a href="http://www.sitepen.com/blog/2010/05/03/robust-promises-with-dojo-deferred-1-5/"&gt;dojo.deferred got a makeover&lt;/a&gt; in the 1.5 version of dojo. Previously, I had code which looked like this:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojox.rpc.transportRegistry.register(&lt;br /&gt;"JSONP",&lt;br /&gt;function(str){ return str === "JSONP"; },&lt;br /&gt;{&lt;br /&gt;  fire: function(r) {&lt;br /&gt;      var headers = [], url, def;&lt;br /&gt;      url = r.target + ((r.target.indexOf("?") === -1) ? '?' : '&amp;amp;') + r.data;&lt;br /&gt;      // dair.xhr.send returns a new dojo.Deferred&lt;br /&gt;      def = dair.xhr.send({&lt;br /&gt;        url: url,&lt;br /&gt;        method: "POST",&lt;br /&gt;        headers: headers,&lt;br /&gt;        checkHeaders: true&lt;br /&gt;      });&lt;br /&gt;      def.addCallback(this, "parseResults");&lt;br /&gt;      return def;&lt;br /&gt;  },&lt;br /&gt;&lt;br /&gt;  parseResults: function(obj) {&lt;br /&gt;      obj = obj.data;&lt;br /&gt;      var result = dojo.fromJson(obj);&lt;br /&gt;      if(result &amp;amp;&amp;amp; result.Error) {&lt;br /&gt;        var errCode = new Error(result.Error.Code);&lt;br /&gt;        errCode.displayMsg = result.Error.Message;&lt;br /&gt;        return errCode;&lt;br /&gt;      }&lt;br /&gt;      return result;&lt;br /&gt;  },&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;A short explanation of the code above:&lt;br /&gt;&lt;br /&gt;1. Make the network call (dair.xhr.send).&lt;br /&gt;2. Parse the results with a dojo.fromJson call in parseResults.&lt;br /&gt;3. If while parsing the results of a network call I found an Error property on the json object, treat this response as an error.&lt;br /&gt;4. Returning an Error object from the parseResults method resulted in the error callback being invoked.&lt;br /&gt;&lt;br /&gt;However, with the 1.5 version of dojo, this doesn't fly. After pestering &lt;a href="http://twitter.com/novemberborn"&gt;@novemberborn&lt;/a&gt; on the #dojo IRC channel, he was able to point me in the right direction - I needed an extra dojo.deferred (note the promise variable is what I invoke the callback/errback on in the example below):&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojox.rpc.transportRegistry.register(&lt;br /&gt;"JSONP",&lt;br /&gt;function(str){ return str === "JSONP"; },&lt;br /&gt;{&lt;br /&gt;  fire: function(r) {&lt;br /&gt;      var headers = [], url, def, promise = new dojo.Deferred();&lt;br /&gt;      url = r.target + ((r.target.indexOf("?") === -1) ? '?' : '&amp;amp;amp;') + r.data;&lt;br /&gt;      def = dair.xhr.send({&lt;br /&gt;        url: url,&lt;br /&gt;        method: "POST",&lt;br /&gt;        headers: headers,&lt;br /&gt;        checkHeaders: true&lt;br /&gt;      });&lt;br /&gt;      def.then(dojo.hitch(this, function(obj) {&lt;br /&gt;        this.parseResults(obj, promise);&lt;br /&gt;      }));&lt;br /&gt;      return promise;&lt;br /&gt;  },&lt;br /&gt;&lt;br /&gt;  parseResults: function(obj, promise) {&lt;br /&gt;     obj = obj.data;&lt;br /&gt;     result = dojo.fromJson(obj);&lt;br /&gt;     if(result &amp;amp;amp;&amp;amp;amp; result.Error) {&lt;br /&gt;        resErr = new Error(result.Error.Code);&lt;br /&gt;        resErr.displayMsg = result.Error.Message;&lt;br /&gt;        promise.errback(resErr);&lt;br /&gt;        return;&lt;br /&gt;     }&lt;br /&gt;     if(promise.fired !== 1) {&lt;br /&gt;        promise.callback(result);&lt;br /&gt;     }&lt;br /&gt;  },&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Apart from some other styling bits and bobs, those 3 issues were the only pieces that slowed me down when doing the migration. Having migrated dojo applications from 0.43 -&gt; 0.9 -&gt; 1.0.2 -&gt; 1.3 -&gt; 1.5, I have to say that this migration ranks among the easier of all migrations. This speaks volumes for the dojo dev team - keeping true to an API is definitely changeling at times, but it makes my life a hellava lot easier. I always find that migration time is always a good time to take a second look at how you're using a toolkit. For me, I started using the &lt;a href="https://www.dojotoolkit.org/reference-guide/releasenotes/1.5.html#new-get-set-and-deprecated-attr"&gt;.set/.get APIs&lt;/a&gt; instead of the .attr API. I also converted by dojo.deferred addCallback/addErrback methods to the more concise &lt;a href="https://www.dojotoolkit.org/reference-guide/releasenotes/1.5.html#dojo-deferred-dojo-when"&gt;dojo.then&lt;/a&gt;. I also migrated some custom code to use the new dojox dnd &lt;a href="https://www.dojotoolkit.org/reference-guide/releasenotes/1.5.html#dnd"&gt;BoundingBoxController and Selector APIs&lt;/a&gt; (shameless plug there). When the dust settled after the migration, I figured out that I was able to get rid of ~250 lines of custom code as I had found alternatives in the dojo toolkit - I'm always glad to get rid of code. All in all, I reckon the migration took around 4 days of work - not too bad considering the application I work on is ~11000 lines of code.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://www.dojotoolkit.org/reference-guide/releasenotes/1.6.html"&gt;dojo 1.6&lt;/a&gt; seems to be coming up pretty soon - I reckon I won't leave it another 18 months for my next dojo upgrade. &lt;a href="http://bugs.dojotoolkit.org/ticket/8578"&gt;http://bugs.dojotoolkit.org/ticket/8578&lt;/a&gt; looks particularly interesting - I've been looking for some CI implementation for dojo doh testing and this ticket looks like it could prove very useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-619424126196128015?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/619424126196128015/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/08/migrating-to-dojo-15.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/619424126196128015'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/619424126196128015'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/08/migrating-to-dojo-15.html' title='Migrating to dojo 1.5'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-5387957928674941617</id><published>2010-03-15T18:16:00.000-07:00</published><updated>2010-03-15T21:37:04.747-07:00</updated><title type='text'>Javascript unit testing and code coverage</title><content type='html'>Back in the day, when I earned my crust as a Java developer, I wouldn't dare commit a new change to a code base without some unit tests. &lt;a href="http://www.junit.org/"&gt;Junit&lt;/a&gt; and &lt;a href="http://emma.sourceforge.net/"&gt;emma&lt;/a&gt; proved to be useful tools to make sure that any new additions to a codebase didn't adversely affect existing functionality.&lt;br /&gt;&lt;br /&gt;Moving to developing in JavaScript - I guess I got lazy with unit tests. Not having a code coverage tool to let me know which sections of my code were being exercised by test code meant that I didn't have that 'green bar' motivator.&lt;br /&gt;&lt;br /&gt;Then I found out about &lt;a href="http://siliconforks.com/jscoverage/"&gt;jscoverage&lt;/a&gt;. For an example of what this tool can produce, check out their sample reports for &lt;a href="http://siliconforks.com/jscoverage/instrumented-dojo/util/doh/runner.html?testModule=dojo.tests.module"&gt;dojo&lt;/a&gt;. Getting jscoverage up and running on mac can be a bit of a pain though - here's the steps I followed:&lt;br /&gt;&lt;br /&gt;1. There's no auto-built dmg for mac, so you have to download and install &lt;a href="http://www.macports.org/"&gt;macports&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;2. Once you have that up and running, open a terminal and execute:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;sudo port install jscoverage&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;jscoverage should now be installed on your machine.&lt;br /&gt;&lt;br /&gt;3. Check out the jscoverage &lt;a href="http://siliconforks.com/jscoverage/manual.html"&gt;documentation&lt;/a&gt; for examples on how to run jscoverage.&lt;br /&gt;&lt;br /&gt;4. I use dojo's doh testing framework to run my tests. If you do too, you need to edit the runner.html file in the doh directory to include the following buton:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;lt;h3 style=&amp;quot;margin: 5px 5px 0px 5px; float: left;&amp;quot;&amp;gt;D.O.H.: The Dojo Objective Harness&amp;lt;/h3&amp;gt;&lt;br /&gt;&amp;lt;button style=&amp;quot;margin-top: 5px; float: left;&amp;quot; onclick=&amp;quot;window.open('../../jscoverage.html');&amp;quot;&amp;gt;Coverage Report&amp;lt;/button&amp;gt;&lt;br /&gt;&amp;lt;img src=&amp;quot;small_logo.png&amp;quot; height=&amp;quot;40&amp;quot; style=&amp;quot;margin: 0px 5px 0px 5px; float: right;&amp;quot;&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;5. All you need to do then is point your browser at whatever file you use to run your dojo tests and you should be all set. Just click on the "Coverage Report" button once your tests have run.&lt;br /&gt;&lt;br /&gt;Its that simple ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-5387957928674941617?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/5387957928674941617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/03/javascript-unit-testing-and-code.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/5387957928674941617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/5387957928674941617'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/03/javascript-unit-testing-and-code.html' title='Javascript unit testing and code coverage'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-9214838921036786333</id><published>2010-03-04T08:11:00.000-08:00</published><updated>2010-03-04T09:04:29.766-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><category scheme='http://www.blogger.com/atom/ns#' term='air'/><category scheme='http://www.blogger.com/atom/ns#' term='adobe air'/><title type='text'>Javascript testing in Adobe AIR</title><content type='html'>I'm in the process of migrating a web application to an Adobe AIR application and I finally got around to trying to migrate some of my JavaScript unit tests to run in AIR. I'd previously written some &lt;a href="http://docs.dojocampus.org/quickstart/doh"&gt;doh&lt;/a&gt; tests and wanted to get them up and running in AIR. Turns out it was relatively simple. All you need to do is:&lt;br /&gt;&lt;br /&gt;In your apache configuration file, add an entry to point to the base of your AIR project:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;lt;VirtualHost *&amp;gt;&lt;br /&gt;   ServerName air-testing.my.domain&lt;br /&gt;   DocumentRoot /path/to/my/trunk/htdocs&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;(The htdocs directory here should have a subdirectory called dojo which has the 4 dojo subdirectories (dojo, dijit, dojox, util)&lt;br /&gt;&lt;br /&gt;Then, in the .html file where you previously had a &amp;lt;meta&amp;gt; redirect (lets call this runTests.html - it probably looked something like this)&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&amp;gt;&lt;br /&gt;&amp;lt;html lang="en-US" dir="ltr" xmlns="http://www.w3.org/1999/xhtml"&amp;gt;&lt;br /&gt;   &amp;lt;head&amp;gt;&lt;br /&gt;       &amp;lt;title&amp;gt;My Unit Tests&amp;lt;/title&amp;gt;&lt;br /&gt;       &amp;lt;meta http-equiv="REFRESH" content="0; url=../../dojo/util/doh/runner.html?testModule=my.module.testAll&amp;amp;registerModulePath=mydomain,../../mydomain"&amp;gt;&lt;br /&gt;   &amp;lt;/head&amp;gt;&lt;br /&gt;   &amp;lt;body&amp;gt;&lt;br /&gt;       Redirecting to D.O.H runner.&lt;br /&gt;   &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;... change this file to look like this:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&amp;gt;&lt;br /&gt;&amp;lt;html lang="en-US" dir="ltr" xmlns="http://www.w3.org/1999/xhtml"&amp;gt;&lt;br /&gt;   &amp;lt;head&amp;gt;&lt;br /&gt;       &amp;lt;title&amp;gt;My Unit Tests&amp;lt;/title&amp;gt;&lt;br /&gt;       &amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;           var addOnLoad = function() {&lt;br /&gt;               window.location = "http://air-testing.my.domain/dojo/util/doh/runner.html?testModule=my.module.testAll&amp;amp;registerModulePath=mydomain,../../mydomain"";&lt;br /&gt;           }&lt;br /&gt;       &amp;lt;/script&amp;gt;&lt;br /&gt;   &amp;lt;/head&amp;gt;&lt;br /&gt;   &amp;lt;body onload="addOnLoad();"&amp;gt;&lt;br /&gt;       Redirecting to D.O.H runner.&lt;br /&gt;   &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I found it useful to have a testing application descriptor xml file in the same directory as my runTests.html test file:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;&lt;br /&gt;&amp;lt;application xmlns="http://ns.adobe.com/air/application/1.5"&amp;gt;&lt;br /&gt;   &amp;lt;id&amp;gt;tests&amp;lt;/id&amp;gt;&lt;br /&gt;   &amp;lt;filename&amp;gt;runTests&amp;lt;/filename&amp;gt;&lt;br /&gt;   &amp;lt;name&amp;gt;&lt;br /&gt;       &amp;lt;text xml:lang="en_US"&amp;gt;Run Tests&amp;lt;/text&amp;gt;&lt;br /&gt;   &amp;lt;/name&amp;gt;&lt;br /&gt;   &amp;lt;version&amp;gt;0.0001&amp;lt;/version&amp;gt;&lt;br /&gt;   &amp;lt;description&amp;gt;&lt;br /&gt;       &amp;lt;text xml:lang="en_US"&amp;gt;Run Tests&amp;lt;/text&amp;gt;&lt;br /&gt;   &amp;lt;/description&amp;gt;&lt;br /&gt;   &amp;lt;initialWindow&amp;gt;&lt;br /&gt;       &amp;lt;title&amp;gt;TESTS&amp;lt;/title&amp;gt;&lt;br /&gt;       &lt;b&gt;&amp;lt;content&amp;gt;runTests.html&amp;lt;/content&amp;gt;&lt;/b&gt;&lt;br /&gt;       &amp;lt;systemChrome&amp;gt;standard&amp;lt;/systemChrome&amp;gt;&lt;br /&gt;       &amp;lt;transparent&amp;gt;false&amp;lt;/transparent&amp;gt;&lt;br /&gt;       &amp;lt;visible&amp;gt;true&amp;lt;/visible&amp;gt;&lt;br /&gt;       &amp;lt;width&amp;gt;1250&amp;lt;/width&amp;gt;&lt;br /&gt;       &amp;lt;height&amp;gt;900&amp;lt;/height&amp;gt;&lt;br /&gt;       &amp;lt;x&amp;gt;20&amp;lt;/x&amp;gt;&lt;br /&gt;       &amp;lt;y&amp;gt;20&amp;lt;/y&amp;gt;&lt;br /&gt;       &amp;lt;minimizable&amp;gt;true&amp;lt;/minimizable&amp;gt;&lt;br /&gt;       &amp;lt;maximizable&amp;gt;true&amp;lt;/maximizable&amp;gt;&lt;br /&gt;       &amp;lt;minSize&amp;gt;850 636&amp;lt;/minSize&amp;gt;&lt;br /&gt;       &amp;lt;resizable&amp;gt;true&amp;lt;/resizable&amp;gt;&lt;br /&gt;   &amp;lt;/initialWindow&amp;gt;&lt;br /&gt;&amp;lt;/application&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Running adl on this new test application descriptor file should kick off your doh unit tests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-9214838921036786333?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/9214838921036786333/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/03/javascript-testing-in-adobe-air.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/9214838921036786333'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/9214838921036786333'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/03/javascript-testing-in-adobe-air.html' title='Javascript testing in Adobe AIR'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-4086310499222969492</id><published>2010-01-17T18:29:00.000-08:00</published><updated>2010-01-17T19:39:37.174-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><category scheme='http://www.blogger.com/atom/ns#' term='source control'/><title type='text'>First impressions of Github</title><content type='html'>Tried out &lt;a href="http://github.com" target="_blank"&gt;github&lt;/a&gt; for the first time today - bit late to the party, but better late than never.&lt;br /&gt;&lt;br /&gt;On initial impressions, I don't immediately see the advantages of github over something like SVN/trac. Given that the tagline for Github is social coding, perhaps I'd need to be involved in a project with multiple committers to get the full github experience. Can't say that I've many complaints with svn - its a huge improvement over Rational Clearcase (which I had to use in a previous job). One definite plus of github is the ability to change code snippets through the web interface.&lt;br /&gt;&lt;br /&gt;Regardless, &lt;a href="http://github.com/seanoshea" target="_blank"&gt;http://github.com/seanoshea&lt;/a&gt; is where I'll be posting code snippets from now on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-4086310499222969492?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/4086310499222969492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/01/first-impressions-of-github.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/4086310499222969492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/4086310499222969492'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/01/first-impressions-of-github.html' title='First impressions of Github'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-6816358100941856289</id><published>2010-01-09T21:23:00.000-08:00</published><updated>2010-01-09T21:46:10.501-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dnd'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>Extension to dojo dnd Selector API</title><content type='html'>I've had the good fortune of using some dojo drag and drop in a recent project and have been very impressed with the flexibility offered by the APIs. The documentation and tests have been a real help for me in getting drag and drop functionality up and running quickly in my project.&lt;br /&gt;&lt;br /&gt;After posting a &lt;a href="http://stackoverflow.com/questions/1713241/dojo-drag-and-drop-and-key-handlers"&gt;quick question&lt;/a&gt; to the stackoverflow forums, I tried my hand at what &lt;a href="http://twitter.com/uhop"&gt;Eugene&lt;/a&gt; suggested. Here's my attempt:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.provide(&amp;quot;my.ext.SelectorMixin&amp;quot;);&lt;br /&gt;&lt;br /&gt;dojo.require(&amp;quot;dojo.dnd.Selector&amp;quot;);&lt;br /&gt;&lt;br /&gt;dojo.declare(&lt;br /&gt;  &amp;quot;my.ext.SelectorMixin&amp;quot;,&lt;br /&gt;  [dojo.dnd.Selector],&lt;br /&gt;  {&lt;br /&gt;    // summary: a mixin for the dojo.dnd.Selector class which adds functionality&lt;br /&gt;    // to shift the currently selected index backwards and forwards. One possible&lt;br /&gt;    // use for this mixin would be to allow a user select different dnd items using&lt;br /&gt;    // the right and left keys.&lt;br /&gt;    &lt;br /&gt;    shift: function(offset, shiftKey) {&lt;br /&gt;      // summary: shifts the currently selected dnd item&lt;br /&gt;      // offset: int: the amount to bump the selection by.&lt;br /&gt;      // shiftKey: Boolean: whether or not this new selection happened when the user was holding&lt;br /&gt;      // down the shift key&lt;br /&gt;      var selectedNodes = this.getSelectedNodes();&lt;br /&gt;      if(selectedNodes &amp;amp;&amp;amp; selectedNodes.length) {&lt;br /&gt;        // only delegate to _selectNode if at least one node is selected. If multiple nodes are selected&lt;br /&gt;        // assume that we go with the last selected node.&lt;br /&gt;        this._selectNode(this._getNodeId(selectedNodes[selectedNodes.length - 1].id, offset), shiftKey);&lt;br /&gt;      }&lt;br /&gt;    },&lt;br /&gt;    &lt;br /&gt;    _selectNode: function(nodeId, shiftKey) {&lt;br /&gt;      // summary: selects a node based on nodeId&lt;br /&gt;      // nodeId: String: the id of the node to select&lt;br /&gt;      // shiftKey: Boolean: whether or not this new selection happened when the user was holding&lt;br /&gt;      // down the shift key&lt;br /&gt;      if(!shiftKey) {&lt;br /&gt;        // only clear the selection if the user was not holding down the shift key&lt;br /&gt;        this.selectNone();&lt;br /&gt;      }&lt;br /&gt;      this._addItemClass(dojo.byId(nodeId), &amp;quot;Selected&amp;quot;);&lt;br /&gt;      this.selection[nodeId] = 1;&lt;br /&gt;    },&lt;br /&gt;    &lt;br /&gt;    _getNodeId: function(nodeId, offset) {&lt;br /&gt;      // summary: selects a node based on nodeId&lt;br /&gt;      // nodeId: String: the id of the node to select&lt;br /&gt;      // offset: int: the number of nodes to shift the current selection by&lt;br /&gt;      var allNodes = this.getAllNodes(), newId = nodeId;&lt;br /&gt;      for(var i = 0, l = allNodes.length; i &amp;lt; l; i++) {&lt;br /&gt;        var node = allNodes[i]; &lt;br /&gt;        if(node.id == nodeId) {&lt;br /&gt;          // have a match ... make sure we're not at the start or the end of the dnd set&lt;br /&gt;          if(!((offset == -1 &amp;amp;&amp;amp; i == 0) &amp;#124;&amp;#124; (i == l - 1 &amp;amp;&amp;amp; offset == 1))) {&lt;br /&gt;            // we should be fine to go with the id the user has requested.&lt;br /&gt;            newId = allNodes[i + offset].id;&lt;br /&gt;          }&lt;br /&gt;          break;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      // if we don't get a match, the newId defaults to the currently selected node&lt;br /&gt;      return newId;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One very simple use of this extension would be to add key handlers to dojo.doc to listen for right and left keys:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.connect(dojo.doc, &amp;quot;onkeyup&amp;quot;, dojo.hitch(this, function(evt) {&lt;br /&gt;  if(evt.keyCode == dojo.keys.RIGHT_ARROW) {&lt;br /&gt;    this.shift(1, evt.shiftKey);&lt;br /&gt;  }&lt;br /&gt;  if(evt.keyCode == dojo.keys.LEFT_ARROW) {&lt;br /&gt;    this.shift(-1, evt.shiftKey);&lt;br /&gt;  }&lt;br /&gt;}));&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I'll give a shot at providing some tests for this extension in my next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-6816358100941856289?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/6816358100941856289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/01/extension-to-dojo-dnd-selector-api.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6816358100941856289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6816358100941856289'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2010/01/extension-to-dojo-dnd-selector-api.html' title='Extension to dojo dnd Selector API'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-6645959725278545044</id><published>2009-11-14T11:59:00.000-08:00</published><updated>2010-01-09T22:04:57.066-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dijit'/><category scheme='http://www.blogger.com/atom/ns#' term='TabContainer'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>Disabling Tabs in a dijit.layout.TabContainer</title><content type='html'>I've started using tab containers a little more recently. Looking through the &lt;a href="http://docs.dojocampus.org/dijit/layout/TabContainer"&gt;dojocampus articles&lt;/a&gt; and &lt;a href="http://api.dojotoolkit.org/jsdoc/1.3.2/dijit.layout.TabContainer"&gt;dojo api docs&lt;/a&gt;, it looks like there isn't an API for disabling clicks on a particular tab. I'm not sure why this is the case - perhaps it has something to do with accessibility (I've always had intentions of learning more about accessibility on the web, but I've never really had the time)&lt;br /&gt;&lt;br /&gt;At any rate, here's a quick and simple solution to disabling tabs in a tab container:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.forEach(dijit.byId(&amp;quot;myTabContainer&amp;quot;).tablist.getChildren(), dojo.hitch(this, function(item, index, array) {&lt;br /&gt; dojo.attr(item, &amp;quot;disabled&amp;quot;, true);&lt;br /&gt; item.onClick = function() {};&lt;br /&gt;}));&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Unfortunately, there doesn't seem to be a simple way to re-enable these clicks. Also, it'd be really sweet if you could simply hide some of the tabs in a tab container, but it sounds like you can only &lt;a href="http://www.dojotoolkit.org/forum/dijit-dijit-0-9/dijit-support/dijit-layout-tabcontainer"&gt;add/remove them&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-6645959725278545044?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/6645959725278545044/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/11/disabling-tabs-in-dijitlayouttabcontain.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6645959725278545044'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6645959725278545044'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/11/disabling-tabs-in-dijitlayouttabcontain.html' title='Disabling Tabs in a dijit.layout.TabContainer'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-5326083750315974901</id><published>2009-09-13T14:16:00.000-07:00</published><updated>2009-09-13T14:28:33.786-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drag and drop'/><category scheme='http://www.blogger.com/atom/ns#' term='dnd'/><category scheme='http://www.blogger.com/atom/ns#' term='dair'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><category scheme='http://www.blogger.com/atom/ns#' term='air'/><title type='text'>dojo updates</title><content type='html'>You gotta give the &lt;a href="http://dojotoolkit.org"&gt;dojo&lt;/a&gt; guys credit. Thanks to &lt;a href="http://www.twitter.com/uhop"&gt;@uhop&lt;/a&gt; for following up on &lt;a href="http://whatsthepointeh.blogspot.com/2009/09/dojo-drag-and-drop-in-air.html"&gt;my previous blog post&lt;/a&gt;. Even though my bug was pretty edge case, its great to see the dojo community going the extra mile to fix it. Its small things like this which make all the difference.&lt;br /&gt;&lt;br /&gt;On another dojo related note, I attended the DDD at AOL in Mountain View on Thursday. Always good times hanging out with the dojo crew. Some really cool stuff in the pipeline too. It was great to get some samples of the &lt;a href="http://code.google.com/p/dojango/"&gt;dojo/django collaboration&lt;/a&gt; happening with &lt;a href="http://twitter.com/tklipstein"&gt;Tobias&lt;/a&gt; and &lt;a href="http://twitter.com/clubajax"&gt;Mike's&lt;/a&gt; drawing samples were really cool too - I didn't know stuff like that was possible in the browser. Looking forward to &lt;a href="http://docs.dojocampus.org/releasenotes/1.4"&gt;dojo 1.4&lt;/a&gt; already. &lt;a href="http://twitter.com/robchristensen"&gt;Rob Christiansen&lt;/a&gt; from &lt;a href="http://labs.adobe.com/"&gt;Adobe AIR&lt;/a&gt; popped in too - sounds like there's a lot in the pipeline for AIR 2.0. Maybe the &lt;a href="http://sitepen.com/labs/dair/"&gt;dair&lt;/a&gt; dojo project will get rejuvenated with the arrival of AIR 2.0 and dojo 1.4&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-5326083750315974901?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/5326083750315974901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/09/dojo-updates.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/5326083750315974901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/5326083750315974901'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/09/dojo-updates.html' title='dojo updates'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-6586380540486634306</id><published>2009-09-02T13:25:00.000-07:00</published><updated>2010-01-09T22:57:19.982-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drag and drop'/><category scheme='http://www.blogger.com/atom/ns#' term='dnd'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><category scheme='http://www.blogger.com/atom/ns#' term='air'/><title type='text'>dojo drag and drop in AIR</title><content type='html'>Looking at common.js in the dojo.dnd package, I see this nice utility function:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;Paste your text here.dojo.dnd._isMac = navigator.appVersion.indexOf(&amp;quot;Macintosh&amp;quot;) &amp;gt;= 0;&lt;br /&gt;dojo.dnd._copyKey = dojo.dnd._isMac ? &amp;quot;metaKey&amp;quot; : &amp;quot;ctrlKey&amp;quot;;&lt;br /&gt;&lt;br /&gt;dojo.dnd.getCopyKeyState = function(e) {&lt;br /&gt; // summary: abstracts away the difference between selection on Mac and PC,&lt;br /&gt; // and returns the state of the &amp;quot;copy&amp;quot; key to be pressed.&lt;br /&gt; // e: Event: mouse event&lt;br /&gt; return e[dojo.dnd._copyKey]; // Boolean&lt;br /&gt;};&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;However, this doesn't seem to work in AIR (only tested running on mac so far).&lt;br /&gt;&lt;br /&gt;Switching the code to look like:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.dnd._isMac = navigator.appVersion.indexOf(&amp;quot;Macintosh&amp;quot;) &amp;gt;= 0;&lt;br /&gt;dojo.dnd._isAir = navigator.appVersion.indexOf(&amp;quot;AdobeAIR&amp;quot;) &amp;gt;= 0;&lt;br /&gt;dojo.dnd._copyKey = dojo.dnd._isMac &amp;amp;&amp;amp; !dojo.dnd._isAir ? &amp;quot;metaKey&amp;quot; : &amp;quot;ctrlKey&amp;quot;;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;seems to do the trick&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-6586380540486634306?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/6586380540486634306/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/09/dojo-drag-and-drop-in-air.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6586380540486634306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6586380540486634306'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/09/dojo-drag-and-drop-in-air.html' title='dojo drag and drop in AIR'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-7116895806346785077</id><published>2009-06-16T18:35:00.000-07:00</published><updated>2009-06-16T22:04:13.778-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IE8'/><category scheme='http://www.blogger.com/atom/ns#' term='Internet Explorer'/><category scheme='http://www.blogger.com/atom/ns#' term='IE6'/><category scheme='http://www.blogger.com/atom/ns#' term='browser'/><title type='text'>IE 6 usage ... boooournn</title><content type='html'>I was curious to see how the &lt;a href="http://blogs.msdn.com/ie/archive/2009/04/10/prepare-for-automatic-update-distribution-of-ie8.aspx"&gt;announcement that IE 8 would be included in an automated update&lt;/a&gt; would affect the Google Analytics for the application I work on. I took a months sample of IE usage before and after the announcement just to see how the number stacked up. Here's what I found:&lt;br /&gt;&lt;br /&gt;BEFORE:&lt;br /&gt;7.0                 68.18% &lt;br /&gt;6.0            20.50%&lt;br /&gt;8.0                 11.27%&lt;br /&gt;&lt;br /&gt;AFTER:&lt;br /&gt;7.0             58.39% &lt;br /&gt;6.0           21.96%&lt;br /&gt;8.0                19.59%&lt;br /&gt;&lt;br /&gt;Unfortunately, I don't see IE 6 disappearing any time soon. Neither does &lt;a href="http://www.quirksmode.org/blog/archives/2009/06/state_of_the_br_1.html"&gt;ppk&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Heh ... unless &lt;a href="http://ie6update.com/"&gt;this&lt;/a&gt; tricks a few users into upgrading.&lt;br /&gt;&lt;br /&gt;In other browser news, I just saw this site springing up: &lt;a href="http://dowebsitesneedtolookexactlythesameineverybrowser.com/"&gt;http://dowebsitesneedtolookexactlythesameineverybrowser.com/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-7116895806346785077?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/7116895806346785077/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/05/ie-6-usage-boooournn.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/7116895806346785077'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/7116895806346785077'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/05/ie-6-usage-boooournn.html' title='IE 6 usage ... boooournn'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-1903625303027068445</id><published>2009-06-03T21:31:00.000-07:00</published><updated>2010-01-17T15:49:12.569-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>Idle Handler with dojo</title><content type='html'>After reading &lt;a href="http://www.nczonline.net/"&gt;Nicholas Zakas&lt;/a&gt;' &lt;a href="http://www.nczonline.net/blog/2009/06/02/detecting-if-the-user-is-idle-with-javascript-and-yui-3/"&gt;blog posting&lt;/a&gt; about handling idle listeners with YUI, I decided to take a bash at porting his logic to dojo. It turned out to be pretty simple - here's the code:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.declare(&amp;quot;my.IdleListener&amp;quot;, [], {&lt;br /&gt; &lt;br /&gt; _mouseMoveHandle: null,&lt;br /&gt; _keyDownHandle: null,&lt;br /&gt; _timeout: 30000,&lt;br /&gt; &lt;br /&gt;  isRunning: function() {&lt;br /&gt;        return this._enabled;&lt;br /&gt;    },&lt;br /&gt;&lt;br /&gt;    isIdle: function() {&lt;br /&gt;        return this._idle;&lt;br /&gt;    },&lt;br /&gt;&lt;br /&gt;    start: function(newTimeout) {&lt;br /&gt;        this._enabled = true;&lt;br /&gt;        this._idle = false;&lt;br /&gt;        if (typeof newTimeout == &amp;quot;number&amp;quot;) {&lt;br /&gt;            this._timeout = newTimeout;&lt;br /&gt;        }&lt;br /&gt;        this._mouseMoveHandle = dojo.connect(dojo.doc, &amp;quot;onmousemove&amp;quot;, this, &amp;quot;_handleUserEvent&amp;quot;);&lt;br /&gt;        this._keyDownHandle = dojo.connect(dojo.doc, &amp;quot;onkeydown&amp;quot;, this, &amp;quot;_handleUserEvent&amp;quot;);&lt;br /&gt;        // set a timeout to toggle state&lt;br /&gt;        this._idleTimeout = setTimeout(dojo.hitch(this, &amp;quot;_toggleIdleState&amp;quot;), this._timeout);&lt;br /&gt;    },&lt;br /&gt;&lt;br /&gt;    stop: function() {&lt;br /&gt;        this._enabled = false;&lt;br /&gt;        // clear any pending timeouts&lt;br /&gt;        clearTimeout(this._idleTimeout);&lt;br /&gt;        // detach the event handlers&lt;br /&gt;        dojo.forEach([this._mouseMoveHandle, this._keyDownHandle], function(item, index, array) {&lt;br /&gt;         if(item) {&lt;br /&gt;          dojo.disconnect(item);&lt;br /&gt;         }&lt;br /&gt;        });&lt;br /&gt;    },&lt;br /&gt;    &lt;br /&gt; _handleUserEvent: function() {&lt;br /&gt;     // clear any existing timeout&lt;br /&gt;     clearTimeout(this._idleTimeout);&lt;br /&gt;     if (this._enabled) {&lt;br /&gt;      // if the user is just waking us up again, toggle the idle state.&lt;br /&gt;      // otherwise, reset the timeout with a new timeout&lt;br /&gt;         this._idle ? this._toggleIdleState() : this._idleTimeout = setTimeout(dojo.hitch(this, &amp;quot;_toggleIdleState&amp;quot;), this._timeout);&lt;br /&gt;     }&lt;br /&gt; },&lt;br /&gt; &lt;br /&gt; _toggleIdleState: function() {&lt;br /&gt;     this._idle = !this._idle;&lt;br /&gt;     this._idle ? dojo.publish(&amp;quot;idle&amp;quot;, []) : dojo.publish(&amp;quot;active&amp;quot;, []); &lt;br /&gt; }&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;To start your idle handler with a default timeout of 30 seconds, all you need to do is:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt; var idleListener = new my.IdleListener();&lt;br /&gt; idleListener.start(30000);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Then in whatever parts of your application deal with polling network resources, all you need to do is:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;dojo.subscribe(&amp;quot;idle&amp;quot;, function() {&lt;br /&gt;&lt;br /&gt;});&lt;br /&gt;dojo.subscribe(&amp;quot;active&amp;quot;, function() {&lt;br /&gt;&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;For &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; users, &lt;a href="http://paulirish.com/"&gt;Paul Irish&lt;/a&gt; has &lt;a href="http://twitter.com/paul_irish/status/2023445074"&gt;ported the code&lt;/a&gt;. too.&lt;br /&gt;&lt;br /&gt;Just posted an update of this code to github. &lt;a href="http://github.com/seanoshea/dojo-idle-listener/"&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/2121043255321350518-1903625303027068445?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/1903625303027068445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/06/idle-handler-with-dojo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/1903625303027068445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/1903625303027068445'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/06/idle-handler-with-dojo.html' title='Idle Handler with dojo'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-6790065778446782041</id><published>2009-05-19T18:00:00.000-07:00</published><updated>2009-05-19T18:43:03.963-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='migrating'/><category scheme='http://www.blogger.com/atom/ns#' term='dair'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><category scheme='http://www.blogger.com/atom/ns#' term='air'/><title type='text'>Migrating to dojo 1.3</title><content type='html'>Recently enough I just pushed some software which included an upgrade to &lt;a href="http://www.dojotoolkit.org/book/dojo-1-3-release-notes"&gt;dojo 1.3&lt;/a&gt;. Prior to this, I was using &lt;a href="http://build.dojotoolkit.org/1.0.2/"&gt;dojo 1.0.2&lt;/a&gt;, so this migration to the latest version of dojo represented around 2 years of work from the dojo guys.&lt;br /&gt;&lt;br /&gt;Having previously migrated from &lt;a href="www.dojotoolkit.org/releaseNotes/0.4.3"&gt;dojo 0.4.3&lt;/a&gt; to a dev build of &lt;a href="www.dojotoolkit.org/2007/07/03/dojo-0-9-lands-softly"&gt;dojo 0.9&lt;/a&gt; to dojo 1.0.2, I've had my fair share of experience with dojo upgrades. Having previously worked on framework code in a prior job, I'm always ready to try out the latest a greatest versions of frameworks. My main reasons behind the migration to dojo 1.3 were:&lt;br /&gt;&lt;br /&gt;1. Official support for &lt;a href="www.google.com/chrome"&gt;Chrome&lt;/a&gt; and &lt;a href="www.microsoft.com/windows/Internet-explorer/default.aspx"&gt;IE 8&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;2. Speed speed speed. I'm not sure what the numbers are like in taskspeed for dojo 1.0.2, but the 1.3 numbers &lt;a href="dante.dojotoolkit.org/taskspeed/report/charts.html"&gt;stack up pretty well&lt;/a&gt; against some of the major JS toolkit vendors on the market at the moment.&lt;br /&gt;&lt;br /&gt;3. I wanted to start exploring some of the new APIs available in dojo 1.3 such as &lt;a href="http://docs.dojocampus.org/dojo/data"&gt;dojo.data&lt;/a&gt;, &lt;a href="http://docs.dojocampus.org/dojox/form/BusyButton"&gt;BusyButton&lt;/a&gt;, &lt;a href="http://docs.dojocampus.org/dojo/place"&gt;dojo.place&lt;/a&gt; and &lt;a href="http://docs.dojocampus.org/dojo/create"&gt;dojo.create&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;4. Start exploring the &lt;a href="http://www.sitepen.com/labs/dair/"&gt;dair&lt;/a&gt; extensions for integration between dojo and &lt;a href="http://www.adobe.com/products/air/"&gt;Adobe AIR&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;5. Testing. One of the areas where the application I work on is definitely deficient is automated testing. I wanted to start looking into &lt;a href="http://www.dojotoolkit.org/book/dojo-book-0-9/part-4-meta-dojo/d-o-h-unit-testing"&gt;DOH&lt;/a&gt; and the &lt;a href="http://www.dojotoolkit.org/2008/08/11/doh-robot-automating-web-ui-unit-tests-real-user-events"&gt;dojo robot&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So, I had plenty of reasons to migrate and the move couldn't have gone easier. I honestly only got stung once where once of my custom widgets had a member variable called _created which stepped on the _created member variable in &lt;a href="http://api.dojotoolkit.org/jsdoc/1.3/dijit._Widget"&gt;dijit._Widget&lt;/a&gt;. The vast majority of headaches were more related to svn and importing the 1.3 version of dojo into my repository than actually migrating my code to use the new dojo APIs. Kudos to the dojo dev team for keeping such a clean and consistent API.&lt;br /&gt;&lt;br /&gt;Already looking forward to the &lt;a href="http://www.dojotoolkit.org/book/dojo-1-4-release-notes"&gt;1.4 version of dojo&lt;/a&gt; ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-6790065778446782041?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/6790065778446782041/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/05/migrating-to-dojo-13.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6790065778446782041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6790065778446782041'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/05/migrating-to-dojo-13.html' title='Migrating to dojo 1.3'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-8438623611443447605</id><published>2009-05-06T16:30:00.000-07:00</published><updated>2009-05-06T16:50:50.125-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='plugd'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>plugd and Google Maps</title><content type='html'>I just started looking into integrating &lt;a href="http://maps.google.com/"&gt;Google Maps&lt;/a&gt; into an application. Given the breath of features offered by Google Maps, its no surprise that the JavaScript download is pretty hefty -&gt; ~72KB with an empty cache for main.js. Also, including Google Maps in a page brings down around 20 extra images (depending on the default size of your map).&lt;br /&gt;&lt;br /&gt;So, I started looking into ways for deterministically loading Google Maps as there are certain sections of the application I work on where Google Maps is not needed. Looking at the &lt;a href="http://code.google.com/apis/ajax/documentation/"&gt;Google Ajax APIs&lt;/a&gt;, this seems like the easiest way to load Google Maps (and other Google APIs for that matter):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&lt;br /&gt;       src="http://www.google.com/jsapi?key=ABCDEFG"&gt;&amp;lt;/script&gt;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;    google.load("maps", "2");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Adding &lt;a href="http://code.google.com/p/plugd/"&gt;plugd&lt;/a&gt; into the mix, you can also cut down on loading the Google Ajax API JavaScript using the &lt;a href="http://code.google.com/p/plugd/source/browse/trunk/script.js"&gt;addScript&lt;/a&gt; method:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;dojo.addScript("http://www.google.com/jsapi?key=" + key, dojo.hitch(this, function() {&lt;br /&gt; var mapsLoaded = function() {&lt;br /&gt;  dojo.publish("/gmaps/loaded", []);&lt;br /&gt; }&lt;br /&gt; var langArray = dojo.locale.split("-");&lt;br /&gt; google.load("maps", "2", {"callback" : mapsLoaded, "language": langArray[0] + "_" + langArray[1].toUpperCase()});&lt;br /&gt;}));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And code which depends on Google Maps being loaded should listen to the "/gmaps/loaded" topic before trying to access any Google Maps APIs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-8438623611443447605?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/8438623611443447605/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/05/plugd-and-google-maps.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/8438623611443447605'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/8438623611443447605'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/05/plugd-and-google-maps.html' title='plugd and Google Maps'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-3800640362484756540</id><published>2009-04-14T18:42:00.000-07:00</published><updated>2009-04-14T19:22:34.628-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='dijit'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>Connecting labels to inputs using dojo</title><content type='html'>When using dijit, you have to be careful not to include any id elements in your template HTML file. Otherwise, you can end up with elements with the same id in your document which can lead to a lot of head scratching. Having been bitten by the "multiple elements having the same id in my document" bug, I started stripping out all ids from my template files, replacing them with dojoAttachPoint attributes.&lt;br /&gt;&lt;br /&gt;All was going fine and I wasn't getting mixed up in id clashing anymore. However, there was one drawback - my labels wouldn't work anymore. Code which previously looked like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;li&gt;&lt;br /&gt; &amp;lt;input id="simple" type="radio" dojoattachevent="onclick: simpleClicked"/&gt;&lt;br /&gt; &amp;lt;label for="simple"&gt;${statics.i18n.simple}&amp;lt;/label&gt;&lt;br /&gt;&amp;lt;/li&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;no longer worked because it had changed to this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;li&gt;&lt;br /&gt; &amp;lt;input dojoAttachPoint="simple" type="radio" dojoattachevent="onclick: simpleClicked"/&gt;&lt;br /&gt; &amp;lt;label for="simple"&gt;${statics.i18n.simple}&lt;/label&gt;&lt;br /&gt;&amp;lt;/li&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The label's for &lt;a href="http://www.w3.org/TR/html401/interact/forms.html#adef-for" target="_blank"&gt;attribute&lt;/a&gt; expects to reference an id and not a dojoAttachPoint. Previously I was able to toggle the 'simple' checkbox input by clicking on the label. I always tend to click on labels instead of radio/checkboxes (mostly because they're bigger and easier to click), so I saw this move away from id-based inputs as a degradation in the usability of the web application.&lt;br /&gt;&lt;br /&gt;So, I hacked together a piece of code which is invokable after every widget is set up and appended to the dom:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;connectLabelsToInputs: function(query, widget) {&lt;br /&gt;dojo.query(query, widget.domNode).forEach(function(item, index, array) {&lt;br /&gt; if(widget[item.getAttributeNode("for").value] &amp;&amp; (widget[item.getAttributeNode("for").value].type == "checkbox" || widget[item.getAttributeNode("for").value].type == "radio")) {&lt;br /&gt;  var attrs = widget[item.getAttributeNode("for").value].attributes;&lt;br /&gt;  for(var i = 0; i &amp;lt; attrs.length; i++) {&lt;br /&gt;   if(attrs[i].value.indexOf("onclick") != -1) {&lt;br /&gt;    dojo.addClass(item, "clickable");&lt;br /&gt;    dojo.connect(item, 'onclick', widget, dojo.trim(attrs[i].value.substring(attrs[i].value.indexOf(":") + 1, attrs[i].value.length)));&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if(!dojo.hasClass(item, "clickable")) {&lt;br /&gt;   dojo.addClass(item, "clickable");&lt;br /&gt;   dojo.connect(item, "onclick", dojo.hitch(widget, function() {&lt;br /&gt;    widget[item.getAttributeNode("for").value].checked = widget[item.getAttributeNode("for").value].checked == false ? true : false;  &lt;br /&gt;   }));&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}, widget);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This code makes the assumption that you have a class called 'clickable' which looks something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;.clickable {&lt;br /&gt; cursor: pointer;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, if I know that a widget I just created contains labels and checkboxes or radio buttons, all I need to do is invoke:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; common.connectLabelsToInputs("fieldset ol li label", this);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and my labels are clickable again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-3800640362484756540?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/3800640362484756540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/04/connecting-labels-to-inputs-using-dojo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3800640362484756540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3800640362484756540'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/04/connecting-labels-to-inputs-using-dojo.html' title='Connecting labels to inputs using dojo'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-4808268811872178380</id><published>2009-04-12T17:46:00.000-07:00</published><updated>2009-04-12T18:04:01.268-07:00</updated><title type='text'>dijit.Dialog's underlay</title><content type='html'>dijit.Dialogs are really handy for getting the user's attention. However, they can be a little intrusive if they're overused (think annoying JavaScript popups) as they take control away from the user.&lt;br /&gt;&lt;br /&gt;Its always advisable to give the user a way out of the dialog. A cancel button, or the little x in the top right hand corner of the dialog should be visible at all times. For whatever reason though (more than likely my CSS skills suck), the little x at the top of the dialog box sometimes disappears behind a scroll bar in IE after I resize the dialog.&lt;br /&gt;&lt;br /&gt;One escape route which should always be visible to the user is the underlying web page. Should they click on the underlay, you could interpret that interaction as the user wanting the dialog to disappear. Here's some dojo code I cooked up to allow that interaction to happen:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;handleOverlayClick: function(dialogName) {&lt;br /&gt; if(!this._dialogHandles) {&lt;br /&gt;  this._dialogHandles = new dojox.collections.Dictionary();&lt;br /&gt; }&lt;br /&gt; if(!this._dialogHandles.item(dialogName)) {&lt;br /&gt;  this._dialogHandles.add(dialogName, dojo.connect(dojo.byId(dialogName + "_underlay"), "onclick", dijit.byId(dialogName), "hide"));&lt;br /&gt;  dojo.connect(dijit.byId(dialogName), "hide", dojo.hitch(this, function() {&lt;br /&gt;   // cleanup&lt;br /&gt;   dojo.disconnect(this._dialogHandles.item(dialogName));&lt;br /&gt;   this._dialogHandles.remove(dialogName); &lt;br /&gt;  }));&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Thanks to &lt;a href="http://www.twitter.com/phiggins"&gt;@phiggins&lt;/a&gt; on the dojo IRC channel for help with this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-4808268811872178380?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/4808268811872178380/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/04/dijitdialogs-underlay.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/4808268811872178380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/4808268811872178380'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/04/dijitdialogs-underlay.html' title='dijit.Dialog&apos;s underlay'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-5191983335902134177</id><published>2009-03-29T13:18:00.000-07:00</published><updated>2009-03-29T13:46:18.346-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IE8'/><category scheme='http://www.blogger.com/atom/ns#' term='Internet Explorer'/><category scheme='http://www.blogger.com/atom/ns#' term='IE'/><category scheme='http://www.blogger.com/atom/ns#' term='IE6'/><title type='text'>Internet Explorer fun and games</title><content type='html'>This is a kind of mixed bag posting on a few IE issues I came across recently:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;IE 8 Released&lt;/span&gt;&lt;br /&gt;So, IE 8 was &lt;a href="http://arstechnica.com/microsoft/news/2009/03/mix09-internet-explorer-8-released-progress-unmistakable.ars"&gt;released&lt;/a&gt; recently. At the SXSW CSS3 wars talk, &lt;a href="http://sxsw.com/interactive/talks/panels/?action=bio&amp;id=200987"&gt;Sylvain Galineau&lt;/a&gt; of Microsoft spoke about the ~7000 w3c CSS2 tests MS have passed as part of their standards push for IE 8. Internet Explorer gets a lot of bad press (man, I'm sick of basing IE), but hopefully this will be a turning point for future releases of Internet Explorer. Cool to hear that IE 8 is now the most CSS 2.1 compliant browser on the market. Wonder when it'll be pushed as an automatic update.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;IE 8 Released - my pages look like sh1t&lt;/span&gt;&lt;br /&gt;Buy yourself some time with the &lt;a href="http://blogs.msdn.com/ie/archive/2008/06/10/introducing-ie-emulateie7.aspx"&gt;IE 7 meta-tag&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;     &amp;lt;meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Checkboxes&lt;/span&gt;&lt;br /&gt;So, dynamically creating checkboxes in IE 6 is a little different to every other browser I develop for. For whatever reason, I couldn't get my checkbox to be checked by default. That is until I found out about the &lt;a href="http://msdn.microsoft.com/en-us/library/ms533715(VS.85).aspx"&gt;defaultChecked&lt;/a&gt; property.&lt;br /&gt;&lt;br /&gt;Apparently, if you try to set the checked property of a checkbox before you add it to the DOM in IE 6, IE6 drops the checked property and sets the checkbox as unchecked by default. But, if you use the defaultChecked property, it'll heed your intentions and hey presto, your checkbox is checked.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Web SuperPreview&lt;/span&gt;&lt;br /&gt;Speaking of IE, &lt;a href="http://blogs.msdn.com/xweb/archive/2009/03/18/Microsoft-Expression-Web-SuperPreview-for-Windows-Internet-Explorer.aspx"&gt;Web Super Preview&lt;/a&gt; looks like a good tool. Kinda like IE on steroids.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-5191983335902134177?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/5191983335902134177/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/03/internet-explorer-fun-and-games.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/5191983335902134177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/5191983335902134177'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/03/internet-explorer-fun-and-games.html' title='Internet Explorer fun and games'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-3660804446250905036</id><published>2009-03-13T23:05:00.000-07:00</published><updated>2009-03-18T20:16:16.952-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='debugging'/><category scheme='http://www.blogger.com/atom/ns#' term='wireshark'/><category scheme='http://www.blogger.com/atom/ns#' term='air'/><title type='text'>Running Wireshark on MAC OSX</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/wikipedia/commons/d/db/Wireshark_Icon.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 200px;" src="http://upload.wikimedia.org/wikipedia/commons/d/db/Wireshark_Icon.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If I have to open &lt;a href="http://www.wireshark.org/"&gt;Wireshark&lt;/a&gt; at all, I know I'm in trouble. Its a fantastic tool, but as a web developer, I default to Firebug. I've only ever really needed Wireshark for debugging IE network issues.&lt;br /&gt;&lt;br /&gt;I've been taking a look at &lt;a href="http://www.adobe.com/products/air/"&gt;Adobe AIR&lt;/a&gt; recently which has a very similar Firebug like console called &lt;a href="http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ade46-7ed2.html"&gt;AIRIntrospector&lt;/a&gt;. However, when attempting to execute Jsonp requests in Adobe AIR, my timeout callbacks were being invoked and the requests weren't showing up in the "XHR" tab in AIRIntrospector. This was despite the fact that I could see the apache logs filling up with requests on the API server I was trying to hit.&lt;br /&gt;&lt;br /&gt;So, I tried firing up Wireshark to listen and inspect the response from the API server only to be greeted with:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;The capture session could not be initiated ((no devices found) /dev/bpf0: Permission denied).&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;After a few quick Googles, I found this helpful &lt;a href="http://www.gofixit.com/?p=144"&gt;post&lt;/a&gt;. Apparently, you need to start Wireshark as root. So,&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;$cd /Applications/Wireshark.app/Contents/MacOS&lt;br /&gt;$sudo ./Wireshark&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;After that quick sudo, I could listen for API responses without needing to rely on the AIRIntrospector.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-3660804446250905036?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/3660804446250905036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/03/running-wireshark-on-mac-osx.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3660804446250905036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3660804446250905036'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/03/running-wireshark-on-mac-osx.html' title='Running Wireshark on MAC OSX'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-1441057825441696501</id><published>2009-03-13T10:52:00.000-07:00</published><updated>2009-03-13T11:04:59.360-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>Listening for pasted input</title><content type='html'>A lot of the input forms I work on defaults the "Submit" button to disabled. Added to client side validation, this default usually prevents the user from submitting forms without required fields. I usually attach onkeyup events to each of the form fields to see whether I should enable the "Submit" button for a particular form.&lt;br /&gt;&lt;br /&gt;However, onkeyup doesn't catch everything. If the user pastes input into a field using their mouse, onkeyup doesn't catch that event. So, the user is left with an input field which appears filled, but the "Submit" button remains disabled.&lt;br /&gt;&lt;br /&gt;The solution is pretty simple. You can attach something like this to your input fields:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;input type="text" dojoAttachEvent="onkeyup: checkSubmitButton, oninput: checkSubmitButton, onpaste: checkSubmitButton"&gt;&lt;/input&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;More on &lt;a href="https://developer.mozilla.org/en/XUL/Attribute/oninput"&gt;oninput&lt;/a&gt;  and &lt;a href="https://developer.mozilla.org/en/DOM/element.onpaste"&gt;onpaste&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-1441057825441696501?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/1441057825441696501/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/03/listening-for-pasted-input.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/1441057825441696501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/1441057825441696501'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/03/listening-for-pasted-input.html' title='Listening for pasted input'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-3666621517973985644</id><published>2009-03-06T18:53:00.000-08:00</published><updated>2009-03-06T21:45:38.852-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='regular expressions'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo builds'/><title type='text'>dojo builds and regexp</title><content type='html'>Just saw a &lt;a href="http://twitter.com/slicknet/status/1281909597" target="_blank"&gt;tweet fly by&lt;/a&gt; which made me think of a possible blog entry.&lt;br /&gt;&lt;br /&gt;I'm more inclined to use Firebug and console.* statements to debug my client side dojo code. I find it easier than rebuilding my code base every time I want to make a change. The one downside to this is my client side code has console statements scattered throughout the code, thus making my the download heavier.&lt;br /&gt;&lt;br /&gt;So, as part of my build process, I use the following code to strip out all console.* statements and replace them with a semi-colon.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;my @generated_files = (&lt;br /&gt; "htdocs/my-build/$revision/dojo/dojo/main.js",&lt;br /&gt; "htdocs/my-build/$revision/dojo/dojo/registration.js",&lt;br /&gt; "htdocs/my-build/$revision/dojo/dojo/poller.js",&lt;br /&gt; "htdocs/my-build/$revision/dojo/dojo/extras.js"&lt;br /&gt;);&lt;br /&gt;foreach my $generatedFileName (@generated_files) {&lt;br /&gt; my $in_file = $generatedFileName;&lt;br /&gt; my $out_file = $generatedFileName . ".stripped.js";&lt;br /&gt; open DATA, "$in_file" or die "can't open $in_file $!";&lt;br /&gt; my @file_lines = &lt;DATA&gt;;&lt;br /&gt; close (DATA);&lt;br /&gt; open DATAOUT, "&gt;$out_file" or die "can't open $out_file $!";&lt;br /&gt; foreach my $line (@file_lines) {&lt;br /&gt;  $line =~ s/(?&amp;lt;!:)console\.(?:warn|log|error|con|info)(\((?:(?&gt;[^()]+)|(?{1}))*\));/;/gi;&lt;br /&gt;  print DATAOUT "$line";&lt;br /&gt; }&lt;br /&gt; close (DATAOUT);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The good people over at &lt;a href="http://regexadvice.com/" target="_blank"&gt;http://regexadvice.com/&lt;/a&gt; helped me out with that expression and saved me ~5KB after GZIP compression - not too shabby.&lt;br /&gt;&lt;br /&gt;This regular expression does not filter &lt;i&gt;every&lt;/i&gt; console.* statement out of your code. For example if the regexp parser encounters:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;test ? console.warn("Success") : console.warn("Failure");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;it will not strip it down to:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;test ? ; : ; ;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As that isn't valid JavaScript.&lt;br /&gt;&lt;br /&gt;I'm pretty sure this problem could be fixed for the latest and greatest versions of dojo with a new parameter which you can pass automatically to the build script, but I'm still on &lt;a href="http://build.dojotoolkit.org/1.0.2/"&gt;1.0.2&lt;/a&gt; so I'll continue with this until I upgrade to 1.3.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-3666621517973985644?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/3666621517973985644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/03/dojo-builds-and-regexp.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3666621517973985644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3666621517973985644'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/03/dojo-builds-and-regexp.html' title='dojo builds and regexp'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-9137980493936583320</id><published>2009-03-02T18:52:00.000-08:00</published><updated>2009-03-02T19:02:18.136-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='dialogs'/><category scheme='http://www.blogger.com/atom/ns#' term='dijit'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>Catching dijit's Dialog close event</title><content type='html'>I tend to use dijit Dialogs whenever I need the user to make a decision before proceeding with any other actions. They're especially useful for server error conditions and asking the user how they'd like to react to something like an API failure.&lt;br /&gt;&lt;br /&gt;However, popping up dialogs can be annoying to users. I know I'm guilty of scanning dialog text and just hitting the 'x' button and ignoring the message. There are two ways you can prevent the user from doing this with dojo.&lt;br /&gt;&lt;br /&gt;1. The most obvious solution: Hide the 'x' button. This is easily done with some simple CSS:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#myDialog .dijitDialogTitleBar .dijitDialogCloseIcon {&lt;br /&gt;    display:none;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The one drawback to this is that the user can hit the escape button and that'll close the dialog.&lt;br /&gt;&lt;br /&gt;2. Attach the following two callbacks to your dialog:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;this._userClosedDialogHandle = dojo.connect(dijit.byId("myDialog"), "hide", this, "_handleCloseDialog");&lt;br /&gt;this._userClosedEscDialogHandle = dojo.connect(dijit.byId("myDialog").containerNode, 'onkeypress', function (evt) {&lt;br /&gt; key = evt.keyCode;&lt;br /&gt; if(key == dojo.keys.ESCAPE) {&lt;br /&gt;  this._handleCloseDialog();&lt;br /&gt; }&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The one caveat with this method is that you need to disconnect both handles in the _handleCloseDialog method. Otherwise, you'll find yourself in an infinite loop pretty quickly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-9137980493936583320?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/9137980493936583320/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/03/catching-dijits-dialog-close-event.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/9137980493936583320'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/9137980493936583320'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/03/catching-dijits-dialog-close-event.html' title='Catching dijit&apos;s Dialog close event'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-4595339932320098592</id><published>2009-02-17T18:24:00.000-08:00</published><updated>2009-02-17T18:35:23.199-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='screen resolutions'/><title type='text'>Handy Firefox plugin</title><content type='html'>This was pointed out to me today - &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/1985"&gt;Window Resizer&lt;/a&gt; allows you view your own browser in different resolutions. Not everyone has a massive 30" display!! I've definitely made the mistake in the past to develop for just my own resolution. Would be really cool if automated testing frameworks like D'OH or Selinium could be integrated with this plugin so you could run your test suits against a list of resolutions you need to support.&lt;br /&gt;&lt;br /&gt;Google Analytics has a sub tab which lets you know the screen resolutions of your visitors. Its under Visitors -&gt; Browser Capabilities -&gt; Screen Resolutions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-4595339932320098592?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/4595339932320098592/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/02/handy-firefox-plugin.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/4595339932320098592'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/4595339932320098592'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/02/handy-firefox-plugin.html' title='Handy Firefox plugin'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-6403558199026284202</id><published>2009-02-08T20:26:00.000-08:00</published><updated>2009-02-08T20:33:22.900-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='enter key'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>dojo and the enter key</title><content type='html'>Attaching a listener for the enter key on a form can help a lot from a usability perspective. If your users are more familiar with tabbing through a forms using only keys, asking them to use the mouse just to submit the form can be a little irritating. dojo comes to the rescue with a simple dojoattachevent. All you need to do is add the a dojoAttachEvent to your template file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;dojoattachevent="onkeyup: checkForEnter"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then, in your widget logic, all you need to do is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;checkForEnter: function(keyEvent) {&lt;br /&gt; if(keyEvent.keyCode == 13) {&lt;br /&gt;  if(!dojo.isFF) {&lt;br /&gt;   if(this._validateForm()) {&lt;br /&gt;    this._createAccount();&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Oddly enough, Firefox is able to recognize the enter key and submits the form all by itself. &lt;br /&gt;&lt;br /&gt;&lt;a href="http://docs.dojocampus.org/dojo/keys"&gt;dojo campus&lt;/a&gt; has quite a few keys defined so you can capture a lot of what your users are trying to achieve with your web application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-6403558199026284202?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/6403558199026284202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/02/dojo-and-enter-key.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6403558199026284202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/6403558199026284202'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/02/dojo-and-enter-key.html' title='dojo and the enter key'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2121043255321350518.post-3384256932194380814</id><published>2009-01-19T22:13:00.000-08:00</published><updated>2009-06-24T21:09:39.992-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internationalization'/><category scheme='http://www.blogger.com/atom/ns#' term='localization'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='l10n'/><category scheme='http://www.blogger.com/atom/ns#' term='dojo'/><title type='text'>dojo internationalization: easy as 1 .. 2 .. 7</title><content type='html'>The &lt;a href="http://api.dojotoolkit.org/jsdoc/dojo/1.2/dojo.i18n" target="_blank"&gt;internationalization APIs&lt;/a&gt; in dojo are pretty similar to the internationalization support in Java - at least I found more similarities between dojo i18n and Java than I did between dojo i18n and PHP's gettext. However, getting up and running with dojo i18n support can be a little tricky. Here's a few simple steps I followed to get started with dojo internationalization.&lt;br /&gt;&lt;br /&gt;1. Include the list of locales you want to support in your build script. I use Perl to build my dojo application, so it ended up looking a like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;system("sh build.sh profileFile=$trunk/build/dojo/profile.js releaseDir=../../../build/$revision/ action=release version=$revision localeList=en-us,ja-jp");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2. Set the locale in the djConfig at the top of your page:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   var djConfig = {&lt;br /&gt;   parseOnLoad: true,&lt;br /&gt;   usePlainJson: true,&lt;br /&gt;   locale: determineUserLocale()};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The determineUserLocale method here simply returns a locale in the format "&lt;span class="br0"&gt;en-us&lt;/span&gt;&lt;span class="br0"&gt;", "&lt;/span&gt;&lt;span class="br0"&gt;ja-jp&lt;/span&gt;&lt;span class="br0"&gt;" based on the user's preferences.&lt;br /&gt;&lt;br /&gt;3. Wherever your dojo.require's usually are, add the following&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span class="me1"&gt;&lt;/span&gt;&lt;span class="st0"&gt;dojo.requireLocalization("my.class", "i18n");&lt;/span&gt;&lt;br /&gt;&lt;span class="br0"&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="br0"&gt;This is responsible for downloading a resource bundle called i18n.js which is located under the my.class module.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;4. Create an I18n base class (any widget I use which needs internationalization support needs to extend from this widget):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;dojo.provide("my.class.I18n");&lt;br /&gt;             &lt;br /&gt;dojo.require("dijit._Widget");&lt;br /&gt;dojo.requireLocalization("my.class", "i18n");&lt;br /&gt;&lt;br /&gt;dojo.declare(&lt;br /&gt;   "my.class.I18n",&lt;br /&gt;   [dijit._Widget],&lt;br /&gt;   {&lt;br /&gt;           statics: { i18n: {}},&lt;br /&gt;&lt;br /&gt;           preamble: function() {&lt;br /&gt;                   this.statics.i18n = dojo.i18n.getLocalization("my.class", "i18n");&lt;br /&gt;           },&lt;br /&gt;&lt;br /&gt;           translate: function(key, params) {&lt;br /&gt;                   params = dojo.isArray(params) ? params : [params];&lt;br /&gt;                   return !params ? this.statics.i18n[key] : dojo.string.substitute(this.statics.i18n[key], params);&lt;br /&gt;           }&lt;br /&gt;   }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;5.  Included my.class.I18n in any widget which required internationalization support:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;dojo.provide("my.class.TopNav");&lt;br /&gt;&lt;br /&gt;dojo.require("my.class.I18n");&lt;br /&gt;&lt;br /&gt;dojo.declare(&lt;br /&gt;   "my.class.TopNav",&lt;br /&gt;   [my.class.I18n, dijit._Templated],&lt;br /&gt;   { &lt;br /&gt;           templatePath: dojo.moduleUrl("my.class", "templates/TopNav.html"),&lt;br /&gt;                    &lt;br /&gt;           postCreate: function() {&lt;br /&gt;                   ...&lt;br /&gt;           },&lt;br /&gt;&lt;br /&gt;           onClick: function() {&lt;br /&gt;                   ...&lt;br /&gt;           },&lt;br /&gt;&lt;br /&gt;           _postApiCall: &lt;span class="kw2"&gt;function&lt;/span&gt;&lt;span class="br0"&gt;(&lt;/span&gt;res&lt;span class="br0"&gt;)&lt;/span&gt; &lt;span class="br0"&gt;{&lt;/span&gt;&lt;br /&gt;                   &lt;span class="kw1"&gt;this&lt;/span&gt;.&lt;span class="me1"&gt;successNode&lt;/span&gt;.&lt;span class="me1"&gt;innerHTML&lt;/span&gt; = &lt;span class="kw1"&gt;this&lt;/span&gt;.&lt;span class="me1"&gt;translate&lt;/span&gt;&lt;span class="br0"&gt;(&lt;/span&gt;&lt;span class="st0"&gt;"success"&lt;/span&gt;, &lt;span class="br0"&gt;[&lt;/span&gt;res&lt;span class="br0"&gt;]&lt;/span&gt;&lt;span class="br0"&gt;)&lt;/span&gt;;&lt;br /&gt;           &lt;span class="br0"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;6. Altered my TopNav.html file to be automatically populated by internationalized strings:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;${statics.i18n.homeLink}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;7. Included internationalized strings in a file called i18n.js under the my.class directory:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   homeLink: &lt;span class="st0"&gt;"Home"&lt;/span&gt;,&lt;br /&gt;   success: &lt;span class="st0"&gt;"Success ${0} !"&lt;/span&gt;,&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Allowing different languages in the same application should be easy from here on in. All you need to do is set up your directory structure as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   my&lt;br /&gt;            class&lt;br /&gt;                   nls&lt;br /&gt;                           en-us&lt;br /&gt;                                   i18n.js&lt;br /&gt;                           ja-jp&lt;br /&gt;                                   i18n.js&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One option you have for switching between languages is to refresh the full page with the new locale passed in as a query string variable:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;loadNewLanguage: function(newLocale) {&lt;br /&gt; var oldSearch = "";&lt;br /&gt; if(window.location.search.length &gt; 0) {&lt;br /&gt;  if(window.location.search.indexOf("?Locale=") != -1) {&lt;br /&gt;   var localeToRemove = window.location.search.substring(window.location.search.indexOf("?Locale="), (window.location.search.indexOf("?Locale=") + 14));&lt;br /&gt;   oldSearch = "&amp;" + window.location.search.replace(localeToRemove, "");&lt;br /&gt;  } else {&lt;br /&gt;   oldSearch = "&amp;" + window.location.search.substring(1, window.location.search.length);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; window.location = ((window.location.href.substring(0, window.location.href.indexOf(window.location.search)) + "?Locale=" + newLocale + oldSearch + window.location.hash));&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The determineUserLocale() function mentioned at the top of this article will need to be able to prioritize and Locale query string parameters.&lt;br /&gt;&lt;br /&gt;While these steps should get you up and running with dojo's internationalization APIs, there's plenty of i18n and l10n resources available directly on the dojo website. I found these links useful:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The &lt;a href="http://api.dojotoolkit.org/jsdoc/dojo/1.2/dojo.cldr" target="_blank"&gt;cldr documentation&lt;/a&gt; Useful for understanding how &lt;a href="http://unicode.org/cldr/"&gt;cldr formats&lt;/a&gt; can influence how dates and currencies are displayed with respect to the user's locale.&lt;/li&gt;&lt;li&gt;Date formatting using &lt;a href="http://api.dojotoolkit.org/jsdoc/dojo/1.2/dojo.date.locale" target="_blank"&gt;dojo.date.locale&lt;/a&gt;&lt;/li&gt;&lt;li&gt;The &lt;a href="http://dojotoolkit.org/book/dojo-book-0-9/part-3-programmatic-dijit-and-dojo/i18n" target="_blank"&gt;dojo book&lt;/a&gt; guide on internationalization.&lt;/li&gt;&lt;li&gt;Comprehensive &lt;a href="http://www.ibm.com/developerworks/web/library/wa-dojo/" target="_blank"&gt;IBM tutorial&lt;/a&gt; on internationalizing a dojo application.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2121043255321350518-3384256932194380814?l=whatsthepointeh.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whatsthepointeh.blogspot.com/feeds/3384256932194380814/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/01/dojo-internationalization-easy-as-1-2-7.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3384256932194380814'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2121043255321350518/posts/default/3384256932194380814'/><link rel='alternate' type='text/html' href='http://whatsthepointeh.blogspot.com/2009/01/dojo-internationalization-easy-as-1-2-7.html' title='dojo internationalization: easy as 1 .. 2 .. 7'/><author><name>Sean</name><uri>http://www.blogger.com/profile/00017593633000833820</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_XWpZ-T4KXPk/TIWh6kJ9z0I/AAAAAAAAAYM/wpL-8nQm4iY/S220/profile.jpg'/></author><thr:total>3</thr:total></entry></feed>
