<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
    <channel>
        <title>arguments.callee</title>
        <link>http://arguments.callee.info/</link>
        <description>AJAX, JavaScript, PHP, and other web development technologies.</description>
        <language>en-us</language>
        <copyright>Copyright 2008 HB Stone</copyright>
            <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/callee" /><feedburner:info uri="callee" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>callee</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Fcallee" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fcallee" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Fcallee" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/callee" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fcallee" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2Fcallee" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2Fcallee" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fcallee" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:browserFriendly>Don't forget to come visit the site to view complete posts and add your comments!</feedburner:browserFriendly><item>
                <guid isPermaLink="false">http://arguments.callee.info/2011/05/05/javascript-optimization-eliminate-nested-functions</guid>
                <title>JavaScript Optimization: Eliminate Nested Functions</title>
                <link>http://feedproxy.google.com/~r/callee/~3/YkFt6ud60Rg/javascript-optimization-eliminate-nested-functions</link>
                <pubDate>Thu, 05 May 2011 04:00:00 +0000</pubDate>
                <description>&lt;h2&gt;Encapsulation: Friend AND Foe?&lt;/h2&gt;
&lt;p&gt;In programming, &lt;strong&gt;&lt;a href="http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)"&gt;encapsulation&lt;/a&gt;&lt;/strong&gt; means &lt;em&gt;combining data with the methods that operate on that data&lt;/em&gt;. It's an integral part of object oriented programming, and commonly used in modern JavaScript. For example, say you create a &lt;a href="http://developer.yahoo.com/ypatterns/selection/carousel.html"&gt;Carousel&lt;/a&gt;-style widget. You might write one &lt;code&gt;Carousel&lt;/code&gt; object that tracks the pictures it's using and provides methods to get and set those picture, or cycle through them programmatically. This is good, because it allows &lt;a href="/2009/07/09/javascript-design-patterns-module-singleton/"&gt;JavaScript to be modular&lt;/a&gt;, reducing dependencies and namespace clutter. Encapsulation is our friend, but &lt;a href="https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope#Efficiency_considerations"&gt;excessive nesting can cause unnecessary closures which negatively affect your JavaScript performance&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;In the following example, &lt;code&gt;inner&lt;/code&gt; exists only within the scope of the function &lt;code&gt;main&lt;/code&gt;. Every time you call &lt;code&gt;main()&lt;/code&gt; a new &lt;code&gt;inner&lt;/code&gt; is created, and every time &lt;code&gt;main&lt;/code&gt; finishes running, that &lt;code&gt;inner&lt;/code&gt; is destroyed.&lt;/p&gt;
&lt;pre&gt;
function main() {
    function inner(str) {
        return { name: str };
    }
    return inner('myName');
}
&lt;/pre&gt;
&lt;p&gt;Contrast the above with the following code, where there is only ever one &lt;code&gt;outer&lt;/code&gt;, no matter how many times you call &lt;code&gt;main()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;
function outer(str) {
    return { name: str };
}
function main() {
    return outer('myName');
}
&lt;/pre&gt;
&lt;p&gt;The difference is small in the above example, because the functions are so simple. But consider the following (admittedly contrived) comparison:&lt;/p&gt;
&lt;pre&gt;
// shared variables for performance testing
var i, time, numLoops = 100000;

// with nested functions
function inners() {
    function nestOne(str)   { return str + ' + nestOne';   }
    function nestTwo(str)   { return str + ' + nestTwo';   }
    function nestThree(str) { return str + ' + nestThree'; }
    function nestFour(str)  { return str + ' + nestFour';  }
    function nestFive(str)  { return str + ' + nestFive';  }

    var data = '';
    time = (+new Date); // start timing
    for (i = 0; i &lt; numLoops; i++) {
        // call everything
        data = nestOne(nestTwo(nestThree(nestFour(nestFive('testing')))));
    }
    time = (+new Date) - time; // stop timing
    alert('inners() took ' + time + ' milliseconds');
}

// no nested functions
function one(str)   { return str + ' one';   }
function two(str)   { return str + ' two';   }
function three(str) { return str + ' three'; }
function four(str)  { return str + ' four';  }
function five(str)  { return str + ' five';  }
function outers() {
    var data = '';
    time = (+new Date); // start timing
    for (i = 0; i &lt; numLoops; i++) {
        // call everything
        data = one(two(three(four(five('testing')))));
    }
    time = (+new Date) - time; // stop timing
    alert('outers() took ' + time + ' milliseconds');
}

// run tests
inners();
outers();
&lt;/pre&gt;
&lt;button type="button" id="run-example"&gt;Try the above example!&lt;/button&gt;
&lt;script&gt;
// shared variables for performance testing
var i, time, numLoops = 100000;

// with nested functions
function inners() { 
    function nestOne(str)   { return str + ' + nestOne';   }
    function nestTwo(str)   { return str + ' + nestTwo';   }
    function nestThree(str) { return str + ' + nestThree'; }
    function nestFour(str)  { return str + ' + nestFour';  }
    function nestFive(str)  { return str + ' + nestFive';  }

    var data = '';
    time = (+new Date); // start timing
    for (i = 0; i &lt; numLoops; i++) {
        // call everything
        data = nestOne(nestTwo(nestThree(nestFour(nestFive('testing')))));
    } 
    time = (+new Date) - time; // stop timing
    alert('inners() took ' + time + ' milliseconds');
} 

// no nested functions
function one(str)   { return str + ' one';   }
function two(str)   { return str + ' two';   }
function three(str) { return str + ' three'; }
function four(str)  { return str + ' four';  }
function five(str)  { return str + ' five';  }
function outers() {
    var data = '';
    time = (+new Date); // start timing
    for (i = 0; i &lt; numLoops; i++) {
        // call everything
        data = one(two(three(four(five('testing')))));
    }
    time = (+new Date) - time; // stop timing
    alert('outers() took ' + time + ' milliseconds');
}
document.getElementById('run-example').onclick = function() {
    inners();
    outers();
}
&lt;/script&gt;
&lt;h2&gt;Test Results&lt;/h2&gt;
&lt;p&gt;When I run the above tests in &lt;a href="http://getfirefox.com/"&gt;Firefox 3.6&lt;/a&gt; &lt;code&gt;outers()&lt;/code&gt; is about 30% faster than &lt;code&gt;inners()&lt;/code&gt;, and in &lt;a href="http://www.microsoft.com/ie/"&gt;Internet Explorer 8&lt;/a&gt;, &lt;code&gt;outers()&lt;/code&gt; is about 20% faster initially. The difference is staggering when you consider that the functions barely do anything! Imagine an actual web app, where you might be doing intense calculations, manipulating DOM elements, or performing other tasks that take real time and memory. &lt;a href="http://google.com/chrome/"&gt;Chrome&lt;/a&gt; takes about the same amount of time in each case, but that's because it has internal processes that modify your scripts before running them to improve performance.&lt;/p&gt;
&lt;h2&gt;Context is King&lt;/h2&gt;
&lt;p&gt;It's important to note that there is not only a performance difference here, there is a scope difference. When you pull nested functions out of their parent function, they now become available outside of their original scope. Depending on the structure of your application this may cause other side effects. Take a look at the big picture and determine if the nested functions really do need to be nested, but understand that they might then be recreated many times over the life of your app. In some cases if the parent function runs for a long time, there could be several copies of each inner function running simultaneously, causing potential memory issues.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=YkFt6ud60Rg:-i6nUkTIQ2E:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=YkFt6ud60Rg:-i6nUkTIQ2E:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=YkFt6ud60Rg:-i6nUkTIQ2E:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=YkFt6ud60Rg:-i6nUkTIQ2E:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=YkFt6ud60Rg:-i6nUkTIQ2E:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=YkFt6ud60Rg:-i6nUkTIQ2E:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=YkFt6ud60Rg:-i6nUkTIQ2E:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=YkFt6ud60Rg:-i6nUkTIQ2E:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/YkFt6ud60Rg" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[performance]]></category>
            <feedburner:origLink>http://arguments.callee.info/2011/05/05/javascript-optimization-eliminate-nested-functions</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2011/01/26/webcomic-wednesday-learned-2-play</guid>
                <title>Webcomic Wednesday: Learned 2 Play</title>
                <link>http://feedproxy.google.com/~r/callee/~3/HbhO1t3eRfw/webcomic-wednesday-learned-2-play</link>
                <pubDate>Wed, 26 Jan 2011 05:00:00 +0000</pubDate>
                <description>&lt;div&gt;
    &lt;a href="http://nerfnow.com/comic/450"&gt;&lt;img src="/img/blog/webcomics/nerfnow-450.jpg" alt="How I used to play MMORPGs versus how I play now."&gt;&lt;/a&gt;
    &lt;span&gt;This Webcomic Wednesday is brought to you by &lt;a href="http://nerfnow.com/"&gt;Nerf NOW!!&lt;/a&gt;, copyright Josu&amp;eacute; Pereira.&lt;/span&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=HbhO1t3eRfw:S_RPHjZl8FY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HbhO1t3eRfw:S_RPHjZl8FY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HbhO1t3eRfw:S_RPHjZl8FY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HbhO1t3eRfw:S_RPHjZl8FY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HbhO1t3eRfw:S_RPHjZl8FY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HbhO1t3eRfw:S_RPHjZl8FY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HbhO1t3eRfw:S_RPHjZl8FY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HbhO1t3eRfw:S_RPHjZl8FY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/HbhO1t3eRfw" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[webcomic wednesday]]></category>
            <feedburner:origLink>http://arguments.callee.info/2011/01/26/webcomic-wednesday-learned-2-play</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/10/26/the-new-yahoo-mail-inside-out</guid>
                <title>The New Yahoo Mail - Inside Out</title>
                <link>http://feedproxy.google.com/~r/callee/~3/byU_6G7b7fQ/the-new-yahoo-mail-inside-out</link>
                <pubDate>Tue, 26 Oct 2010 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
	I haven't been able to say much about what I've been working on since &lt;a href="/2010/06/12/my-first-week-at-yahoo/"&gt;joining Yahoo&lt;/a&gt;, but now that &lt;a href="http://mail.yahoo.com/"&gt;Mail has opened its public beta&lt;/a&gt;, a post is finally in order. If you're interested in the technical side, this is &lt;a href="#questions-comments"&gt;more than just another announcement&lt;/a&gt;.
&lt;/p&gt;
&lt;div&gt;
	&lt;a href="/img/blog/yahoo-mail/ymail-messages.jpg"&gt;&lt;img src="/img/blog/yahoo-mail/ymail-messages.jpg"&gt;&lt;/a&gt;
	&lt;span&gt;Yahoo Mail's new user interface. Yes, there will be themes if you don't like purple.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
	You can get some info from last month's reports on &lt;a href="http://mashable.com/2010/09/16/yahoo-mail-beta/"&gt;Mashable&lt;/a&gt; or the
	&lt;a href="http://www.ymailblog.com/blog/2010/09/get-ready-yahoo-mail-beta-is-coming/"&gt;official YMail blog&lt;/a&gt;, or get a full summary 
	of what's new from the &lt;a href="http://features.mail.yahoo.com/howitworks"&gt;Yahoo Mail feature site&lt;/a&gt;, but I figured it would be 
	interesting to discuss Yahoo Mail from the perspective of someone who worked on it.
&lt;/p&gt;
&lt;h2&gt;So What Did I Do?&lt;/h2&gt;
&lt;p&gt;
	We have a really great team with focused roles, so as a developer if you have any questions about a piece of code, you can find people with 
	a complete understanding of that area. Even the interns (who were only here for about three months) jumped right into the code and took 
	charge of pretty major tasks. Though I've committed bug fixes to many different areas of the new Mail, mostly I focused on 
	&lt;strong&gt;Compose&lt;/strong&gt;, which includes everything between hitting "Compose Message" and the actual "Send".
&lt;/p&gt;
&lt;div&gt;
	&lt;a href="/img/blog/yahoo-mail/ymail-compose.jpg"&gt;&lt;img src="/img/blog/yahoo-mail/ymail-compose.jpg"&gt;&lt;/a&gt;
	&lt;span&gt;The Yahoo Mail compose tab, where I spent most of my development time.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
	I also worked on rich attachment features that utilized &lt;a href="http://browserplus.yahoo.com/"&gt;BrowserPlus&lt;/a&gt;. Unfortunately the 
	BrowserPlus features are disabled for the initial release, but when they're available they provide an enhanced interface for uploading 
	multiple files, seeing snapshots of your uploads, and being able to dynamically resize or crop images right in YMail.
&lt;/p&gt;
&lt;h2&gt;Architecture&lt;/h2&gt;
&lt;p&gt;
	This version of Mail is a complete overhaul from the last. &lt;a href="http://developer.yahoo.com/yui/3/"&gt;YUI 3&lt;/a&gt; plays an integral 
	role in the code design, with Mail components being created as YUI modules and loaded via &lt;a href="http://developer.yahoo.com/yui/3/api/YUI.html#method_use"&gt;YUI().use&lt;/a&gt;. 
	There is a strong &lt;a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller"&gt;MVC&lt;/a&gt; 
	leaning, which allows us to more easily swap out code as new features become available. For example, right now the attachment 
	uploader uses a basic HTML form component, but in the future it can be automatically replaced by Flash or BrowserPlus at 
	runtime depending on the user's system and settings.
&lt;/p&gt;
&lt;h2&gt;Testing, Build, and Deployment&lt;/h2&gt;
&lt;p&gt;
	Every developer has his or her own personal development server with a complete instance of Yahoo Mail running on it. We use
	&lt;a href="http://subversion.apache.org/"&gt;SVN&lt;/a&gt; for version control. Unit tests are part of the Mail codebase and run on each
	development server, and we run &lt;a href="http://seleniumhq.org/projects/"&gt;Selenium&lt;/a&gt; smoke tests before each commit.
&lt;/p&gt;
&lt;div&gt;
	&lt;a href="/img/blog/yahoo-mail/sunny-hudson.jpg"&gt;&lt;img src="/img/blog/yahoo-mail/sunny-hudson.jpg"&gt;&lt;/a&gt;
	&lt;span&gt;All's well according to the Hudson smoke tests.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
	Mail is built using &lt;a href="/amazon/Continuous-Integration-Improving-Software-Reducing/"&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=argumentscall-20&amp;l=as2&amp;o=1&amp;a=0321336380" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;"&gt;continuous integration&lt;/a&gt;
	with &lt;a href="http://hudson-ci.org/"&gt;Hudson&lt;/a&gt; &amp;mdash; a build is kicked off immediately after every commit, and a secondary 
	build runs at regular intervals to produce (hopefully) stable builds which get pushed out to staging and production servers.
&lt;/p&gt;
&lt;h2&gt;
&lt;h2 id="questions-comments"&gt;Questions or Comments?&lt;/h2&gt;
&lt;p&gt;
	Feel free to ask me anything about the new incarnation of Yahoo Mail, whether it's about the application itself or how it's written or built. 
	I'll answer to the best of my knowledge and ability, keeping in mind that I can't discuss certain things for security/legal reasons...
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=byU_6G7b7fQ:cSolLM4Xslo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=byU_6G7b7fQ:cSolLM4Xslo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=byU_6G7b7fQ:cSolLM4Xslo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=byU_6G7b7fQ:cSolLM4Xslo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=byU_6G7b7fQ:cSolLM4Xslo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=byU_6G7b7fQ:cSolLM4Xslo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=byU_6G7b7fQ:cSolLM4Xslo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=byU_6G7b7fQ:cSolLM4Xslo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/byU_6G7b7fQ" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[design patterns]]></category>
		<category><![CDATA[personal]]></category>
		<category><![CDATA[review]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/10/26/the-new-yahoo-mail-inside-out</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/08/25/webcomic-wednesday-working-at-home</guid>
                <title>Webcomic Wednesday: Working at Home</title>
                <link>http://feedproxy.google.com/~r/callee/~3/SesXRjmi2ao/webcomic-wednesday-working-at-home</link>
                <pubDate>Wed, 25 Aug 2010 04:00:00 +0000</pubDate>
                <description>&lt;div&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/01.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/01.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/02.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/02.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/03.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/03.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/04.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/04.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/05.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/05.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/06.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/06.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/07.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/07.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/08.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/08.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/09.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/09.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/10.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/10.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/11.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/11.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/12.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/12.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;a href="/img/blog/webcomics/working-at-home/13.png"&gt;&lt;img src="/img/blog/webcomics/working-at-home/13.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;span&gt;This Webcomic Wednesday is by &lt;a href="http://theoatmeal.com/"&gt;The Oatmeal&lt;/a&gt;.&lt;br&gt;See this comic in its &lt;a href="http://theoatmeal.com/comics/working_home"&gt;natural habitat&lt;/a&gt;.&lt;/span&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=SesXRjmi2ao:iWNvpZdLGdA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SesXRjmi2ao:iWNvpZdLGdA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=SesXRjmi2ao:iWNvpZdLGdA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SesXRjmi2ao:iWNvpZdLGdA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=SesXRjmi2ao:iWNvpZdLGdA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SesXRjmi2ao:iWNvpZdLGdA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SesXRjmi2ao:iWNvpZdLGdA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=SesXRjmi2ao:iWNvpZdLGdA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/SesXRjmi2ao" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[webcomic wednesday]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/08/25/webcomic-wednesday-working-at-home</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/08/17/review-yui-2-8-learning-the-library</guid>
                <title>Review: YUI 2.8 Learning the Library</title>
                <link>http://feedproxy.google.com/~r/callee/~3/p98k24aEXVc/review-yui-2-8-learning-the-library</link>
                <pubDate>Tue, 17 Aug 2010 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    &lt;a href="http://arguments.callee.info/amazon/YUI-2.8-Learning-the-Library/"&gt;YUI 2.8: Learning the Library&lt;/a&gt;
    is a new book by Daniel Barreiro and Dan Wellman covering the ins and outs of the 
    &lt;a href="http://developer.yahoo.com/yui/"&gt;Yahoo User Interface Library&lt;/a&gt; (hereafter "YUI"). YUI is both a set of
    common JavaScript utility functions and an ever-growing collection of widgets for building rich internet applications.
    I have studied both &lt;a href="http://developer.yahoo.com/yui/2/"&gt;YUI 2&lt;/a&gt; and &lt;a href="http://developer.yahoo.com/yui/3/"&gt;YUI 3&lt;/a&gt;
    from a &lt;a href="http://fatfreejelo.com/"&gt;JavaScript library developer&lt;/a&gt; point of view, but obviously &lt;a href="/2010/06/12/my-first-week-at-yahoo/"&gt;working at Yahoo&lt;/a&gt;
    has given me much more exposure to YUI on a practical level.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/amazon/YUI-2.8-Learning-the-Library"&gt;&lt;img src="/img/blog/review/yui-2-8/front-cover.png"&gt;&lt;/a&gt;
    &lt;span&gt;Find this book on &lt;a href="/amazon/YUI-2.8-Learning-the-Library"&gt;Amazon&lt;/a&gt; or &lt;a href="https://www.packtpub.com/yahoo-user-interface-yui-2-8-learning-library/book"&gt;the publisher's site&lt;/a&gt;.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Why YUI 2.x?&lt;/h2&gt;
&lt;p&gt;
    Since YUI3 is available, you may wonder why &lt;a href="https://www.packtpub.com/yahoo-user-interface-yui-2-8-learning-library/book"&gt;Packt&lt;/a&gt;
    would want to publish a new book on YUI2. &lt;a href="http://www.yuiblog.com/blog/2010/07/20/yui-2-8-learning-the-library/"&gt;The author&lt;/a&gt;
    does a good job of explaining why. Development has not stopped for YUI2 &amp;mdash; gallery widgets are being added and improved
    consistently. YUI3's &lt;code&gt;use(...)&lt;/code&gt; statement can even load YUI2 components, so you get lot of interoperability between versions.
&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;
    &lt;strong&gt;Learning the Library&lt;/strong&gt; assumes familiarity with HTML and JavaScript, but not necessarily with YUI.
    Chapter 1 teaches you how to get YUI 2.8 on your page, what license it uses and why, where else you can find help, and a bit about the
    library's overall history. There are also a few code snippets which serve as syntax examples, but then you jump right in and
    write an interactive Calendar widget. This is a practical example which includes writing a support class for dealing with date math.
&lt;/p&gt;
&lt;h2&gt;CSS and DOM Utilities&lt;/h2&gt;
&lt;p&gt;
    The next chapter covers &lt;strong&gt;reset&lt;/strong&gt;, &lt;strong&gt;base&lt;/strong&gt;, &lt;strong&gt;fonts&lt;/strong&gt;, and &lt;strong&gt;grid&lt;/strong&gt;; 
    these are CSS features which take the headaches out of consistent cross-browser design. The grid system allows nested grids
    of varying size, and it's easy to see how you can rapidly mock up a page layout with it.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/review/yui-2-8/grids.png"&gt;&lt;img src="/img/blog/review/yui-2-8/grids.png"&gt;&lt;/a&gt;
    &lt;span&gt;YUI 2.8's CSS grids break a page into customizable "building blocks"&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
    Chapters 3 through 5 cover several features which are now considered common across nearly all JavaScript libraries:
    normalizing DOM access across various browsers, handling user interface events, calling the server using AJAX,
    and animating CSS attributes. Via another practical example, you build an Accordion widget to put everything you've
    learned thus far to use.
&lt;/p&gt;
&lt;h2&gt;Putting the UI into YUI&lt;/h2&gt;
&lt;p&gt;
    The second half of the book really dives into the widgets and controls available in YUI. As modern browsers converge on
    features and standardization, this is really where the biggest differences tend to be from one JavaScript library to the next.
    Chapter 6 introduces the generic containers - Module, Overlay, Panel, Tooltip, and Dialog. These are "virtual windows" which
    are suited to different purposes. For example, if your widget accepts user input, you may want to wrap it in a Dialog. If you
    need to drag and drop your widget, start with a Panel.
&lt;/p&gt;
&lt;p&gt;
    Navigation menus comprise Chapter 7, which is &lt;a href="https://www.packtpub.com/sites/default/files/0707-chapter-7-menus.pdf"&gt;available as a free download from the publisher&lt;/a&gt;.
    The next few sections cover data-intensive controls such as trees, tables, autocomplete, how to link front-end UI 
    components with back-end data sources, and how YUI handles drag and drop operations.
    The authors devote an entire chapter to the Rich Text Editor, and walk you through building an e-mail editor complete with formatting,
    input validation, sanitization against hacks and attacks, and CSS styling to make your editor look like a unified widget.
&lt;/p&gt;
&lt;h2&gt;Debugging, Performance, and Wrap Up&lt;/h2&gt;
&lt;p&gt;
    &lt;em&gt;Everyday Tools&lt;/em&gt;, the final chapter of the book, covers several developer tools. You see how to debug classes
    and events using YUI's Logger control, and how to use external tools such as &lt;a href="http://jslint.com/"&gt;JSLint&lt;/a&gt;
    and &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;YUI Compressor&lt;/a&gt; to improve the quality and performance of your code.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/review/yui-2-8/logger-console.png"&gt;&lt;img src="/img/blog/review/yui-2-8/logger-console.png"&gt;&lt;/a&gt;
    &lt;span&gt;YUI's Logger Console displays live debug information right on your webpage.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
    All in all, this book is great for getting up to speed with YUI 2. The examples are useful and complete, not simple
    snippets, and they are provided from an object-oriented and "best practice" point of view, which is to be expected
    if you are familiar with &lt;a href="http://developer.yahoo.com/"&gt;Yahoo's Developer Network&lt;/a&gt;.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=p98k24aEXVc:w-aiE6mBXGA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=p98k24aEXVc:w-aiE6mBXGA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=p98k24aEXVc:w-aiE6mBXGA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=p98k24aEXVc:w-aiE6mBXGA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=p98k24aEXVc:w-aiE6mBXGA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=p98k24aEXVc:w-aiE6mBXGA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=p98k24aEXVc:w-aiE6mBXGA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=p98k24aEXVc:w-aiE6mBXGA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/p98k24aEXVc" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[design patterns]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[review]]></category>
		<category><![CDATA[user experience]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/08/17/review-yui-2-8-learning-the-library</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/07/08/recursive-find-in-files-on-linux-or-unix</guid>
                <title>Recursive Find-in-Files on Linux or Unix</title>
                <link>http://feedproxy.google.com/~r/callee/~3/BFA9erI6SnM/recursive-find-in-files-on-linux-or-unix</link>
                <pubDate>Thu, 08 Jul 2010 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    I find myself needing this more and more often lately, so I figured this is as good a place as any to keep track of it. Here's how to recursively search for files containing a particular string or regular expression. This should work on most Linux or UNIX systems, depending on your configuration. 
&lt;/p&gt;
&lt;pre&gt;
    find -type f -name "*" | xargs grep &lt;em&gt;'word, phrase, or regular expression'&lt;/em&gt;
&lt;/pre&gt;
&lt;p&gt;Summary for the curious:
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;find&lt;/strong&gt; returns the names of one or more files, but not their contents.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;-type f&lt;/strong&gt; tells &lt;em&gt;find&lt;/em&gt; to only look at files, not directories.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;-name "*"&lt;/strong&gt; tells &lt;em&gt;find&lt;/em&gt; to examine everything. You can replace this with &lt;strong&gt;"*.js"&lt;/strong&gt; to only search JavaScript files, for example.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;|&lt;/strong&gt; "pipes" the output of the command on the left to the input of the command on the right.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;xargs&lt;/strong&gt; creates and executes a command based on the arguments you pass to it.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;grep&lt;/strong&gt; searches the contents of files and returns each line that is a match.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;'...'&lt;/strong&gt; is a &lt;a href="http://www.cyberciti.biz/faq/grep-regular-expressions/"&gt;regular expression&lt;/a&gt; (or just a word, if you prefer) that indicates what you are searching for.&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=BFA9erI6SnM:KoCQ48ktbBM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=BFA9erI6SnM:KoCQ48ktbBM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=BFA9erI6SnM:KoCQ48ktbBM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=BFA9erI6SnM:KoCQ48ktbBM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=BFA9erI6SnM:KoCQ48ktbBM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=BFA9erI6SnM:KoCQ48ktbBM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=BFA9erI6SnM:KoCQ48ktbBM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=BFA9erI6SnM:KoCQ48ktbBM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/BFA9erI6SnM" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[regular expressions]]></category>
		<category><![CDATA[personal]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/07/08/recursive-find-in-files-on-linux-or-unix</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/07/04/unselectable-text-using-css-and-javascript</guid>
                <title>Unselectable Text Using CSS and JavaScript</title>
                <link>http://feedproxy.google.com/~r/callee/~3/qntLmZo4MEw/unselectable-text-using-css-and-javascript</link>
                <pubDate>Sun, 04 Jul 2010 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    Here's a quick, reusable way to make text unselectable within one or 
    more HTML elements. Actually, here's TWO ways, both useful in 
    certain situations.
&lt;/p&gt;
&lt;h2&gt;Adding &lt;del&gt;and Removing&lt;/del&gt; a CSS Class&lt;/h2&gt;
&lt;p&gt;
    The good news with this method is that it's handled completely within
    the CSS, if you count IE's proprietary &lt;a href=""&gt;behavior&lt;/a&gt;
    property. That means you can just add 
    &lt;code&gt;class="unselectable"&lt;/code&gt; and you're done.
&lt;/p&gt;
&lt;p&gt;
    The bad news is that you can't just remove the class when you change your mind. Most browsers are fine, but IE's behavior sticks even when the class is gone. If you want to make something selectable again later, &lt;a href="#redux"&gt;check the next section.&lt;/a&gt;
&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;!-- save this file as unselectable.htc and remember where you put it --&amp;gt;
    &amp;lt;public:component lightweight="true"&amp;gt;
        &amp;lt;public:attach event="ondocumentready" onevent="unselectable()" /&amp;gt;
        &amp;lt;script type="text/javascript"&amp;gt;
            function unselectable(){
                element.onselectstart = function(){ return false; };
                element.setAttribute('unselectable', 'on', 0);
            }
        &amp;lt;/script&amp;gt;
    &amp;lt;/public:component&amp;gt;
&lt;/pre&gt;
&lt;pre&gt;
    .unselectable {
        user-select: none;
        -moz-user-select: none;
        -khtml-user-select: none;
        behavior: url(unselectable.htc); /* change this path as needed */
    }
&lt;/pre&gt;
&lt;h2 id="redux"&gt;CSS via JavaScript&lt;/h2&gt;
&lt;p&gt;
    This method is library agnostic, just
    replace &lt;code&gt;setStyle&lt;/code&gt; with the way your preferred library
    modifies CSS properties. If you don't use a library, then you
    can copy this &lt;code&gt;setStyle&lt;/code&gt; to your own code.
&lt;/p&gt;
&lt;pre&gt;
    function setStyle(element, property, value) {
        property = property.replace(/-(\w)/g, function(match, letter) {
             return letter.toUpperCase();
        });
        element.style[property] = value;
    }
    function selectable(element, bool) {
        // most browsers:
        setStyle(el, 'user-select', bool ? '' : 'none');
        setStyle(el, '-moz-user-select', bool ? '' : 'none');
        setStyle(el, '-khtml-user-select', bool ? '' : 'none');
        
        // IE:
        element.onselectstart = bool ? null : function() { return false; };
        
        // IE and Opera:
        element.setAttribute('unselectable', bool ? '' : 'on', 0);
    }
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=qntLmZo4MEw:Hvkzrk4FJXs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qntLmZo4MEw:Hvkzrk4FJXs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=qntLmZo4MEw:Hvkzrk4FJXs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qntLmZo4MEw:Hvkzrk4FJXs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=qntLmZo4MEw:Hvkzrk4FJXs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qntLmZo4MEw:Hvkzrk4FJXs:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qntLmZo4MEw:Hvkzrk4FJXs:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=qntLmZo4MEw:Hvkzrk4FJXs:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/qntLmZo4MEw" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[html]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/07/04/unselectable-text-using-css-and-javascript</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/06/12/my-first-week-at-yahoo</guid>
                <title>My First Week at Yahoo!</title>
                <link>http://feedproxy.google.com/~r/callee/~3/HFpdi-KA30o/my-first-week-at-yahoo</link>
                <pubDate>Sat, 12 Jun 2010 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    As of June 1, I'm officially a Front End Engineer at Yahoo! My official title is &lt;strong&gt;Technical Yahoo! 
    Software Applications Development Engineer, Sr.&lt;/strong&gt; but Front End Engineer is plenty descriptive
    (and a hell of a lot easier to say). I'm a little late for a "first week" post, but I've been so busy
    getting set up as a new hire that it still seems about right.
&lt;/p&gt;
&lt;h2&gt;Day One&lt;/h2&gt;
&lt;p&gt;
    Orientation on the 1st was pretty packed, it turns out that was the beginning of "intern season" as well,
    so there were a few dozen new hires and a few dozen interns. Unfortunately the AC was not working, and
    that many people in the same room for an entire day makes for a lot of body heat. Fortunately it was
    a beautiful day outside with a nice cool breeze, which made meeting &lt;a href="http://blog.chrissaari.com/"&gt;my new supervisor&lt;/a&gt;
    for lunch that much better. &lt;em&gt;Update: Chris is already planning to leave Yahoo! Not my fault, I swear... Thanks and good luck, Chris!&lt;/em&gt;
&lt;/p&gt;
&lt;h2&gt;Fun Stuff&lt;/h2&gt;
&lt;div&gt;
    &lt;a href="/img/blog/yahoo/urls-foosball.jpg"&gt;&lt;img src="/img/blog/yahoo/urls-foosball.jpg" alt="foosball table"&gt;&lt;/a&gt;
    &lt;span&gt;There's air hockey, pool, and foosball right outside URLs, the main cafeteria at Yahoo!&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;a href="/img/blog/yahoo/music-on-the-green.jpg"&gt;&lt;img src="/img/blog/yahoo/music-on-the-green.jpg" alt="music on the green"&gt;&lt;/a&gt;
    &lt;span&gt;Music on the Green is a series of free concerts Yahoo! holds during the summer. There are also weekly barbecues with a great variety of different food on the grill each week. And check out those purple inflatable chairs, gotta get me one of those.&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;a href="/img/blog/yahoo/purple-mini-cooper.jpg"&gt;&lt;img src="/img/blog/yahoo/purple-mini-cooper.jpg" alt="purple mini cooper"&gt;&lt;/a&gt;
    &lt;span&gt;There's a lot of purple stuff at Yahoo!, like &lt;em&gt;a lot&lt;/em&gt;. This purple Mini Cooper was just sitting outside on a walkway. I think later it turned out to be an ad for insurance or something.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;A More Technical Note&lt;/h2&gt;
&lt;p&gt;
    Generally I'm working on JavaScript for the new versions of &lt;a href="http://messenger.yahoo.com/"&gt;Messenger&lt;/a&gt;
    and &lt;a href="http://mail.yahoo.com/"&gt;Mail&lt;/a&gt;. Don't have much to show yet but both products have some
    big stuff in store so watch for announcements.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=HFpdi-KA30o:losrpfR5dnU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HFpdi-KA30o:losrpfR5dnU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HFpdi-KA30o:losrpfR5dnU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HFpdi-KA30o:losrpfR5dnU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HFpdi-KA30o:losrpfR5dnU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HFpdi-KA30o:losrpfR5dnU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HFpdi-KA30o:losrpfR5dnU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HFpdi-KA30o:losrpfR5dnU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/HFpdi-KA30o" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[personal]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/06/12/my-first-week-at-yahoo</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/05/06/importing-custom-blog-comments-to-disqus</guid>
                <title>Importing Blog Comments to Disqus</title>
                <link>http://feedproxy.google.com/~r/callee/~3/cPjTBnvaiWk/importing-custom-blog-comments-to-disqus</link>
                <pubDate>Thu, 06 May 2010 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    &lt;a href="http://arguments.callee.info/"&gt;This site&lt;/a&gt; is running completely custom blog code, meaning both its features and its flaws are up to me. While I like the flexibility and freedom that comes with, it's often difficult (or impossible) to use third-party blog plugins written for Wordpress, Blogger, or another common platform.
&lt;/p&gt;
&lt;p&gt;
    I recently decided migrate my custom comment system to Disqus. I love its user experience on &lt;strong&gt;other&lt;/strong&gt; people's blogs, so why not run it here? Fortunately they provide an &lt;a href="http://groups.google.com/group/disqus-dev/web/api-1-1"&gt;API&lt;/a&gt; which makes migrating comments a relatively painless process. Figured I'd post my own steps in case it helps others with their own custom blog code. Since custom blogs are by definition unique, there are a couple of assumptions (described in the comments) that depend on how you actually implemented your blog.
&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php
    // this page's output is for our eyes only, set easy debug options
    error_reporting(E_ALL);
    ini_set('display_errors', true);
    set_time_limit(0);
    header('Content-Type: text/plain');

    /**
     * Send a request to the Disqus API and return the response.
     * @param {String} fn     : Disqus API function name
     * @param {String} method : 'get' or 'post', HTTP request method
     * @param {Array}  params : key/value pairs required by the API call
     */
    function makeRequest($fn, $method='get', $params='') {
        $key = '&lt;a href="http://disqus.com/api/get_my_key/"&gt;your Disqus User API key goes here&lt;/a&gt;';
        $url = "http://disqus.com/api/$fn/?user_api_key=$key&amp;api_version=1.1";

        // "URLify" the parameters
        if (is_array($params) &amp;&amp; count($params)) {
            $temp = array();
            foreach ($params as $k =&gt; $v) {
                $k = urlencode($k);
                $v = urlencode($v);
                $temp[] = "$k=$v";
            }
            $params = implode('&amp;', $temp);
        }

        // make the API request
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        if (preg_match('/post/i', $method)) {
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
        } else {
            if (!empty($params)) {
                $url = $url . '&amp;' . $params;
            }
            curl_setopt($ch, CURLOPT_URL, $url . '&amp;' . $params);
        }
        $response = curl_exec($ch);

        if (empty($response)) {
            // something went wrong, debug the response
            foreach (array(
                CURLINFO_EFFECTIVE_URL, // the target address
                CURLINFO_HTTP_CODE,     /* If $response is empty, this is likely &lt;strong&gt;400&lt;/strong&gt;
                                         * which means "bad request". Make sure your
                                         * &lt;a href="http://groups.google.com/group/disqus-dev/web/api-1-1"&gt;function name is listed&lt;/a&gt;.
                                         */
                CURLINFO_TOTAL_TIME,    // how long it took the request
                CURLINFO_HEADER_OUT     // the complete headers
            ) as $option) {
                echo curl_getinfo($ch, $option) . "\r\n";
            }
        }

        // return as an associative array
        return json_decode($response, true);
    }

    // fetch your Disqus Forum ID
    $response = makeRequest('get_forum_list');
    $forum_id = $response['message'][0]['id'];

    // fetch your Forum API Key (different from your user API key!)
    $response = makeRequest('get_forum_api_key', 'get', array(
        'forum_id' =&gt; $forum_id
    ));
    $forum_api_key = $response['message'];

    /**
     * NOTE: $posts is assumed to be an array of all of your blog posts, doesn't 
     * matter what format.Depending on how you implemented your blog, you may be 
     * looping through filenames, database IDs, etc.
     */
    $posts = array();
    foreach ($posts as $post) {
        /**
         * NOTE: $comments is assumed to be an associative array of this post's 
         * comments. Your array keys may be different, just make sure they're
         * reflected that in &lt;a href="#params"&gt;$params&lt;/a&gt; below.
         */
        $comments = array(
            'comment',  // required: the comment we want to import
            'name',     // required: person who commented
            'email',    // required: commenter's email address
            'datetime', // optional: comment timestamp, converted to Disqus' preferred format below 
            'ip',       // optional: commenter's IP address
            'url'       // optional: commenter's own website address
        );
        if (!empty($comments)) {
            // example identifier: "/2010/05/06/importing-blog-comments-to-disqus/"
            $identifier = $post['permalink'];

            // create this post's corresponding Disqus thread...
            $thread = makeRequest('thread_by_identifier', 'post', array(
                'identifier' =&gt; $identifier,
                'forum_api_key' =&gt; $forum_api_key,
                'title' =&gt; ''
            ));
            $thread_id = $thread['message']['thread']['id']; // ...and get its new ID

            // now we can finally import this blog post's comments
            $numComments = 0;
            &lt;span id="#params"&gt;foreach ($comments as $comment) {&lt;/span&gt;
                $params = array(
                    'thread_id' =&gt; $thread_id,
                    'message' =&gt; $comment['comment'],
                    'author_name' =&gt; $comment['name'],
                    'author_email' =&gt; $comment['email'],
                    'forum_api_key' =&gt; $forum_api_key,
                    'created_at' =&gt; date('Y-m-d\TH:i', strtotime($comment['datetime'])),
                    'ip_address' =&gt; $comment['ip'],
                    'author_url' =&gt; $comment['url'],
                    'state' =&gt; 'approved'
                );
                $disqus = makeRequest('create_post', 'post', $params);
                print_r($params); // useful for debugging
                print_r($disqus); // useful for debugging
                $numComments++;
            }
            echo "$numComments comments processed for $identifier\r\n\r\n";
        }
    }
    echo "Done.\r\n";
    exit;
    
?&amp;gt;
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=cPjTBnvaiWk:UDMD4gL7MpQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cPjTBnvaiWk:UDMD4gL7MpQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=cPjTBnvaiWk:UDMD4gL7MpQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cPjTBnvaiWk:UDMD4gL7MpQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=cPjTBnvaiWk:UDMD4gL7MpQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cPjTBnvaiWk:UDMD4gL7MpQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cPjTBnvaiWk:UDMD4gL7MpQ:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=cPjTBnvaiWk:UDMD4gL7MpQ:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/cPjTBnvaiWk" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[personal]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/05/06/importing-custom-blog-comments-to-disqus</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/04/20/running-apache-and-node-js-together</guid>
                <title>Running Apache and Node.JS Together</title>
                <link>http://feedproxy.google.com/~r/callee/~3/Mr2bQej2FiQ/running-apache-and-node-js-together</link>
                <pubDate>Tue, 20 Apr 2010 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    &lt;a href="http://nodejs.org/api.html"&gt;Node.JS&lt;/a&gt; is a fantastic server-side JavaScript solution by
    &lt;a href="http://jsconf.eu/2009/video_nodejs_by_ryan_dahl.html"&gt;Ryan Dahl&lt;/a&gt;. One of its common examples
    is how easy it is to build an &lt;a href="http://gist.github.com/231213#file_web.js"&gt;asynchronous HTTP web server&lt;/a&gt;. 
    The problem with integrating Node into most real-world shared hosts is &lt;strong&gt;the host already has a web server listening on port 80&lt;/strong&gt;
    &amp;mdash; probably Apache &amp;mdash; and it's already serving your existing content.
&lt;/p&gt;
&lt;p&gt;
    Yes, your Node.JS server can use another port, 
    but then you have to point people to something like &lt;code&gt;http://your.website.com:8000/&lt;/code&gt;
    instead of &lt;code&gt;http://your.website.com/&lt;/code&gt;. This makes transitioning to Node.JS feel a little
    less "real". 
&lt;/p&gt;
&lt;p&gt;
    I'm going to show you how to transparently pass individual paths to Node.JS, without changing either your
    URLs or the way your existing content is served.
&lt;/p&gt;
&lt;pre&gt;
    
    /**
     * This is our Node.JS code, running server-side.
     */
    var sys = require('sys'),
        http = require('http'),
        port = 8000; // TODO: set your actual port here
    http.createServer(function(request, response) {
        response.writeHead(200, {
            'Content-Type': 'text/html'
        });
        
        /**
         * Here you can route or process the page based on &lt;strong&gt;request.url&lt;/strong&gt;,
         * or you may want to use a module such as &lt;a href="http://github.com/creationix/node-router"&gt;node-router&lt;/a&gt;.
         */
         
        response.end();
    }).listen(port);
    sys.puts('Server listening on port ' + port);
    
&lt;/pre&gt;
&lt;p&gt;
    So far, so good. Run &lt;em&gt;&lt;code&gt;node http.js &amp;amp;&lt;/code&gt;&lt;/em&gt; (or whatever you called the above file) and your server
    should be up and running. The "&lt;code&gt;&amp;amp;&lt;/code&gt;" at the end 
    &lt;a href="http://lowfatlinux.com/linux-processes.html"&gt;runs node in the background&lt;/a&gt;. 
    Now for some easy &lt;code&gt;.htaccess&lt;/code&gt; magic:
&lt;/p&gt;
&lt;pre&gt;
    # This is the &lt;a href="http://www.newearthonline.co.uk/index.php?page=article&amp;article=68"&gt;.htaccess file&lt;/a&gt; in our document root.
    Options +FollowSymLinks -Indexes -MultiViews
    
    &amp;lt;IfModule mod_rewrite.c&amp;gt;
        
        RewriteEngine on
        
        # Simple URL redirect:
        RewriteRule ^test.html$ http://arguments.callee.info:8000/test/ [P]
        
        # More complicated (the user sees only "benchmark.html" in their address bar)
        RewriteRule ^benchmark.html$ http://arguments.callee.info:8000/node?action=benchmark [P]
        
        # Redirect a whole subdirectory:
        RewriteRule ^node/(.+) http://arguments.callee.info:8000/$1 [P]
        
    &amp;lt;/IfModule&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
    &lt;code&gt;Request.url&lt;/code&gt; is basically equivalent to PHP's &lt;code&gt;$_SERVER['REQUEST_URI']&lt;/code&gt;, meaning
    if you visit &lt;code&gt;http://your.website.com/node/testing/someFile.html&lt;/code&gt; given the above code, 
    Node.JS will provide "&lt;code&gt;/testing/someFile.html&lt;/code&gt;" as &lt;code&gt;request.url&lt;/code&gt;. Query strings are also included.
&lt;/p&gt;
&lt;p&gt;
    The amount of control this gives you allows you to phase in support for Node.JS as quickly or as slowly
    as you like, and from your visitors' point of view nothing changes. Anything you don't explicitly pass
    to Node will be handled by Apache (or your current web server).
&lt;/p&gt;
&lt;p&gt;
    Do you already use &lt;strong&gt;Node.JS&lt;/strong&gt; alongside an existing web server? What projects are you running 
    on Node, or what do you plan to run?
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=Mr2bQej2FiQ:DMw7r_-7sEs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Mr2bQej2FiQ:DMw7r_-7sEs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Mr2bQej2FiQ:DMw7r_-7sEs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Mr2bQej2FiQ:DMw7r_-7sEs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Mr2bQej2FiQ:DMw7r_-7sEs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Mr2bQej2FiQ:DMw7r_-7sEs:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Mr2bQej2FiQ:DMw7r_-7sEs:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Mr2bQej2FiQ:DMw7r_-7sEs:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/Mr2bQej2FiQ" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[htaccess]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/04/20/running-apache-and-node-js-together</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/04/13/mysql-fetch-all-phps-missing-function</guid>
                <title>Mysql_fetch_all: PHP's Missing Function</title>
                <link>http://feedproxy.google.com/~r/callee/~3/JgyEUiZNPjY/mysql-fetch-all-phps-missing-function</link>
                <pubDate>Tue, 13 Apr 2010 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
	PHP has a ton of built-in functions (&lt;a href="http://www.php.net/quickref.php"&gt;over 5700&lt;/a&gt; as of this blog post),
	almost 50 of which deal with &lt;a href="http://php.net/manual/en/book.mysql.php"&gt;MySQL database manipulation&lt;/a&gt;.
	I don't think I normally use more than about 3 or 4 &lt;code&gt;mysql_...&lt;/code&gt; functions, but one task I perform
	often is gather all of the returned rows into an array.
&lt;/p&gt;
&lt;pre&gt;
	// assume $conn is my &lt;a href="http://us2.php.net/mysql_connect"&gt;database connection&lt;/a&gt;
	$query = "SELECT * FROM someTable WHERE someField = 'Some Value'";
	$result = mysql_query($q, $conn);
	$rows = array();
	while($row = mysql_fetch_assoc($r)) {
		$rows[] = $row;
	}
	// $rows now holds all of my results
&lt;/pre&gt;
&lt;p&gt;
	If I just have to do one query, that's fine. But on large projects I may have several queries on a single
	page. Any time I have to type something more than twice I quickly start looking for ways to abstract it 
	into a reusable function, so here goes:
&lt;/p&gt;
&lt;pre&gt;
	function mysql_fetch_all($query, $conn) {
		$result = mysql_query($query, $conn);
		$rows = array();
		while($row = mysql_fetch_assoc($result)) {
			$rows[] = $row;
		}
		return $rows;
	}
	
	// all future queries become one-liners
	$employees = mysql_fetch_all("SELECT * FROM Employees", $conn);
&lt;/pre&gt;
&lt;p&gt;
	Nothing Earth-shattering, but fewer keystrokes means fewer chances for error. What's &lt;strong&gt;your&lt;/strong&gt;
	favorite PHP or SQL shortcut?
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=JgyEUiZNPjY:-XVR75IxxvA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=JgyEUiZNPjY:-XVR75IxxvA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=JgyEUiZNPjY:-XVR75IxxvA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=JgyEUiZNPjY:-XVR75IxxvA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=JgyEUiZNPjY:-XVR75IxxvA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=JgyEUiZNPjY:-XVR75IxxvA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=JgyEUiZNPjY:-XVR75IxxvA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=JgyEUiZNPjY:-XVR75IxxvA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/JgyEUiZNPjY" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/04/13/mysql-fetch-all-phps-missing-function</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/03/26/convert-zip-code-to-yahoo-woeid</guid>
                <title>Convert ZIP code to a Yahoo! WOEID</title>
                <link>http://feedproxy.google.com/~r/callee/~3/lIQm6xF_RU8/convert-zip-code-to-yahoo-woeid</link>
                <pubDate>Fri, 26 Mar 2010 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    If you want to get the current weather (or weather forecast) for a given location, you might be
    tempted to find that information by &lt;a href="http://en.wikipedia.org/wiki/ZIP_code"&gt;ZIP code&lt;/a&gt;. 
    Until this week, you would be able to use &lt;a href="http://developer.yahoo.com/weather/"&gt;Yahoo's Weather RSS feed&lt;/a&gt;. 
    A URL like &lt;code&gt;http://weather.yahooapis.com/forecastrss?p=92260&lt;/code&gt; would return 
    weather information for the ZIP code 92260. Easy!
&lt;/p&gt;
&lt;p&gt;
    If you've &lt;strong&gt;already&lt;/strong&gt; done that, you may have noticed your service no longer works.
    This is because Yahoo! recently switched to only allow "Where On Earth" IDs (WOEIDs) as input
    for their weather API. The reasoning behind this is better integration with Yahoo's family of
    &lt;a href="http://developer.yahoo.com/geo/"&gt;geo technologies&lt;/a&gt; like
    &lt;a href="http://developer.yahoo.com/geo/placemaker/"&gt;Placemaker&lt;/a&gt;,
    &lt;a href="http://developer.yahoo.com/geo/geoplanet/"&gt;GeoPlanet&lt;/a&gt;,
    &lt;a href="http://developer.yahoo.com/geo/geoplanet/data/"&gt;GeoPlanet Data&lt;/a&gt;,
    &lt;a href="http://fireeagle.yahoo.net/developer"&gt;Fire Eagle&lt;/a&gt;,
    and &lt;a href="http://developer.yahoo.com/maps/"&gt;Yahoo! Maps&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
    But don't fret, it's easy to convert your ZIP code to a Yahoo! WOEID.
&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php
    function getWOEID($zip) {
        // use YQL to find the WOEID
        $q = "select woeid from geo.places where text='$zip' limit 1";
        
        // execute the YQL query we just built
        $ch = curl_init('http://query.yahooapis.com/v1/public/YQL?format=json&amp;q=' . urlencode($q));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $response = curl_exec($ch);
        
        // if we got something back
        if ($response) {
			try {
	            $response = json_decode($response, true);               // convert JSON to an array
            	return $response['query']['results']['place']['woeid']; // path to the WOEID value
			} catch(Exception $ex) {
				return 0; // invalid response, we may have hit the rate limit (see next section)
			}
        }
        
        return 0; // we received no response at all
    }
    
    // change "?p=" to "?w=" in your URL, that's it!
    $url = "http://weather.yahooapis.com/forecastrss&lt;strong&gt;&lt;em&gt;?w=&lt;/em&gt;&lt;/strong&gt;" . getWOEID($_GET['zip']);
    
    // fetch Yahoo's weather RSS feed
    $ch = curl_init($url);
    curl_exec($ch);
    curl_close($ch);
?&amp;gt;
&lt;/pre&gt;
&lt;h2&gt;Show Me the Cache&lt;/h2&gt;
&lt;p&gt;
	One important thing to note is that WOEIDs will not change, at least not in the forseeable future.
	Once you convert one zip code, there's no reason to ever call that same query again. To avoid possibly
	hitting the YQL public API rate limit
	we can cache the responses we get back. You can do so in a database, but here's a quick and easy
	file-based cache which works just as well: 
&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php
    function getWOEID($loc) {

        &lt;strong&gt;$cache = "./cache/$loc.txt";
        // if the cache directory doesn't already exist, make it
        if (!is_dir('cache')) {
            mkdir('cache');
        }
        if (file_exists($cache)) {
            return file_get_contents($cache);
        }&lt;/strong&gt;
		
		// same query as in the first example
        $q = "select woeid from geo.places where text='$loc' limit 1";
        $ch = curl_init('http://query.yahooapis.com/v1/public/yql?format=json&amp;q=' . urlencode($q));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $response = curl_exec($ch);
        if ($response) {
            try {
                $response = json_decode($response, true);
                $response = intval($response['query']['results']['place']['woeid']);
				
				&lt;strong&gt;// this block is new, we store the response locally
                if (intval($response)) { // only cache valid responses
                    file_put_contents($cache, $response);
                    return $response;
                }&lt;/strong&gt;
				
            } catch(Exception $ex) {
                return intval(0);
            }
        }
        return intval(0);
    }
?&amp;gt;
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=lIQm6xF_RU8:XpLFsozhecM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=lIQm6xF_RU8:XpLFsozhecM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=lIQm6xF_RU8:XpLFsozhecM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=lIQm6xF_RU8:XpLFsozhecM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=lIQm6xF_RU8:XpLFsozhecM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=lIQm6xF_RU8:XpLFsozhecM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=lIQm6xF_RU8:XpLFsozhecM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=lIQm6xF_RU8:XpLFsozhecM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/lIQm6xF_RU8" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/03/26/convert-zip-code-to-yahoo-woeid</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/02/21/multiple-curl-requests-with-php</guid>
                <title>Multiple cURL Requests with PHP</title>
                <link>http://feedproxy.google.com/~r/callee/~3/pOWgccJKZU0/multiple-curl-requests-with-php</link>
                <pubDate>Sun, 21 Feb 2010 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a&gt;(jump to the practical example)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
	PHP has a set of &lt;a href="http://php.net/cURL"&gt;&lt;code&gt;cURL&lt;/code&gt;&lt;/a&gt; functions to let your script download other
	webpages. If you use &lt;code&gt;cURL&lt;/code&gt; to scrape data or
	build mashups,
	you may need to fetch more than one page. This could create a massive performance problem, adding seconds to your 
	own script's runtime because you have to wait for several individual &lt;code&gt;cURL&lt;/code&gt; requests to come back.
&lt;/p&gt;
&lt;p&gt;
	Enter &lt;a href="http://php.net/curl_multi_init"&gt;&lt;code&gt;curl_multi_init&lt;/code&gt;&lt;/a&gt;. This family of functions
	allows you to combine &lt;code&gt;cURL&lt;/code&gt; handles and execute them simultaneously.
&lt;/p&gt;	
&lt;pre&gt;
	// this example does NOT use simultaneous requests, it must wait for each response
	
	// request 1
	$ch = curl_init('http://webservice.one.com/');  // initialize the request
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // store the page contents
	$response_1 = curl_exec($ch);                   // actually make the request
	
	// request 2
	$ch = curl_init('http://webservice.two.com/');
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	$response_2 = curl_exec($ch);
	
	// normally you would process your results here
	echo "$response_1 $response_2";
&lt;/pre&gt;
&lt;pre&gt;
	// with curl_multi, you only have to wait for the longest-running request
	
	// build the individual requests as above, but do not execute them
	$ch_1 = curl_init('http://webservice.one.com/');
	$ch_2 = curl_init('http://webservice.two.com/');
	curl_setopt($ch_1, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch_2, CURLOPT_RETURNTRANSFER, true);
	
	// build the multi-curl handle, adding both $ch
	$mh = curl_multi_init();
	curl_multi_add_handle($mh, $ch_1);
	curl_multi_add_handle($mh, $ch_2);
	
	// execute all queries simultaneously, and continue when all are complete
	$running = null;
	do {
		curl_multi_exec($mh, $running);
	} while ($running);
	
	// all of our requests are done, we can now access the results
	$response_1 = curl_multi_getcontent($ch_1);
	$response_2 = curl_multi_getcontent($ch_2);
	echo "$response_1 $response_2"; // same output as first example
	
&lt;/pre&gt;
&lt;p&gt;
	If both websites take one second to return, we literally cut our page load time in half by using the second
	example instead of the first. Sweet!
&lt;/p&gt;
&lt;h2 id="wheat"&gt;In Action: Twitter&lt;/h2&gt;
&lt;p&gt;
	Here's an example where we run multiple &lt;a href="http://twitter.com/"&gt;Twitter&lt;/a&gt; searches and combine 
	the results to display them on our own site.
&lt;/p&gt;
&lt;p&gt;
	As a bonus, it also caches the results for 1 minute so we avoid hitting Twitter's rate limit if we get 
	a ton of visitors at the same time. You can change &lt;code&gt;$minutes&lt;/code&gt; to any number you feel 
	comfortable with, but it's important to include because you will end up with a complete blank 
	list if your page gets a lot of hits, which is precisely the worst time to kill your content.
&lt;/p&gt;
&lt;pre&gt;
function tweets() {
    
    // check cache
    $cache = 'twitter-search.txt';
    if (file_exists($cache)) {
        clearstatcache();
        $minutes = 1; // how long to wait before refreshing the cache
        if (filemtime($cache) &amp;gt; (time() - (60 * $minutes)) {
            return file_get_contents($cache);
        }
    }
    
    // we are going to search for tweets mentioning these keywords
    $keywords = array(
        'javascript',
        'html5',
        'css3'
    );
    
    // build the requests
    $ch = array();
    $mh = curl_multi_init();
    for ($i = 0; $i &amp;lt; count($keywords); $i++) {
        $keyword = $keywords[$i];
        $ch[$i] = curl_init();
        curl_setopt($ch[$i], CURLOPT_URL, 
                'http://search.twitter.com/search.json?rpp=3&amp;amp;q=' . $keyword);
        curl_setopt($ch[$i], CURLOPT_USERAGENT, 
                'Twitter requires you to set a user agent, any value works here.');
        curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch[$i], CURLOPT_HEADER, false);
        curl_multi_add_handle($mh, $ch[$i]);
    }
    
    // execute the requests simultaneously
    $running = 0;
    do {
        curl_multi_exec($mh, $running);
    } while ($running &amp;gt; 0);
    
    // display the results
    $output = '';
    for ($i = 0; $i &amp;lt; count($keywords); $i++) {
        // $results contains this keyword's tweets as an associative array
        $results = reset(json_decode(curl_multi_getcontent($ch[$i]), true));
        $resultCount = count($results);
        
        // link to our keyword
        $output .= '&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;&amp;lt;a href="http://search.twitter.com/search?' . $keywords[$i] . '&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;';
        
        // dump the search results
        for ($j = 0; $j &amp;lt; $resultCount; $j++) {
            $id = $results[$j]['id'];                          // twitter user ID
            $user = $results[$j]['from_user'];                 // twitter user name
            $tweet = $results[$j]['text'];                     // tweet text
            $url = "http://www.twitter.com/$user/status/$id/"; // link to the tweet
            
            $output .= '&amp;lt;a href="' . $url . '"&amp;gt;' . $tweet . ' &amp;amp;mdash; ' . $user . '&amp;lt;/a&amp;gt;';
        }
        $output .= '&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;';
    }
    file_put_contents($cache, $output); // store in local cache for performance boost
    return $output;
}
echo tweets();
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=pOWgccJKZU0:x30A7gAuvXw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=pOWgccJKZU0:x30A7gAuvXw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=pOWgccJKZU0:x30A7gAuvXw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=pOWgccJKZU0:x30A7gAuvXw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=pOWgccJKZU0:x30A7gAuvXw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=pOWgccJKZU0:x30A7gAuvXw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=pOWgccJKZU0:x30A7gAuvXw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=pOWgccJKZU0:x30A7gAuvXw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/pOWgccJKZU0" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/02/21/multiple-curl-requests-with-php</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/02/17/webcomic-wednesday-google-buzz-trick</guid>
                <title>Webcomic Wednesday: Google Buzz Trick</title>
                <link>http://feedproxy.google.com/~r/callee/~3/UPZJ6s27NbI/webcomic-wednesday-google-buzz-trick</link>
                <pubDate>Wed, 17 Feb 2010 05:00:00 +0000</pubDate>
                <description>&lt;div&gt;
    &lt;a href="http://www.thedoghousediaries.com/?p=1267"&gt;&lt;img src="/img/blog/webcomics/thedoghousediaries/google-buzz-trick.png" alt="Google Buzz is like Facebook but with my actual friends."&gt;&lt;/a&gt;
    &lt;span&gt;This Webcomic Wednesday is brought to you by &lt;a href="http://www.thedoghousediaries.com/"&gt;thedoghousediaries&lt;/a&gt;.&lt;br&gt;Check the authors out on &lt;a href="http://twitter.com/willrayraf"&gt;Twitter&lt;/a&gt; or &lt;a href="http://www.facebook.com/thedoghousediaries"&gt;Facebook&lt;/a&gt;.&lt;/span&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=UPZJ6s27NbI:hmZVOb_xtyw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=UPZJ6s27NbI:hmZVOb_xtyw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=UPZJ6s27NbI:hmZVOb_xtyw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=UPZJ6s27NbI:hmZVOb_xtyw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=UPZJ6s27NbI:hmZVOb_xtyw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=UPZJ6s27NbI:hmZVOb_xtyw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=UPZJ6s27NbI:hmZVOb_xtyw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=UPZJ6s27NbI:hmZVOb_xtyw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/UPZJ6s27NbI" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[webcomic wednesday]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/02/17/webcomic-wednesday-google-buzz-trick</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/02/05/easy-css-minification-on-client-and-server</guid>
                <title>Easy CSS Minification on Client and Server</title>
                <link>http://feedproxy.google.com/~r/callee/~3/Bvo1Z1mI1vs/easy-css-minification-on-client-and-server</link>
                <pubDate>Fri, 05 Feb 2010 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    While developing a website, it may help to add whitespace
    to your CSS to make it easier to read and maintain. However, when serving CSS to your actual
    visitors, it's often best to &lt;strong&gt;minify&lt;/strong&gt; it, which involves removing that empty space. 
    This makes the filesize smaller, reducing your bandwidth usage and improving page load time. You could 
    see large improvements in filesize and load time, depending on your input.
&lt;/p&gt;
&lt;p&gt;
    The PHP example minifies your code on the server, before the page is sent to your visitor. The JavaScript 
    example does the same thing, but after the visitor is already on the page. Obviously to reap the bandwidth 
    and speed benefits you need to minify CSS on the server. However, by implementing this in 
    JavaScript you can provide an easy cut-and-paste minification service to others. 
&lt;/p&gt;
&lt;h2&gt;Minifying CSS on the Server (PHP)&lt;/h2&gt;
&lt;pre&gt;
    // load your unminified CSS code
    $input = file_get_contents('/path/to/your/stylesheet.css');
    
    // minify it
    $output = preg_replace('/[\s\n]+/', ' ', $input);
    
    // on your actual site, you can refer to "minified.css"
    file_put_contents('/path/to/your/minified.css', $output);
&lt;/pre&gt;
&lt;h2&gt;Minifying CSS on the Client (JavaScript)&lt;/h2&gt;
&lt;pre&gt;
    // the regex is the same
    var output = input.replace(/[\s\n]+/g, ' ');
&lt;/pre&gt;
&lt;p&gt;
	Simply paste a block of CSS in the text area below and press &lt;strong&gt;Go&lt;/strong&gt;, and your code will be minified!
&lt;/p&gt;
&lt;textarea id="20100205-textarea" style="width: 80%; height: 10em; margin: 0 10%;"&gt;
body {
    margin: 0;
    padding: 0;
}
p {
    margin: 1em;
}
&lt;/textarea&gt;
&lt;button id="20100205-button" type="button" style="margin-left: 10%;"&gt;Go!&lt;/button&gt;
&lt;script type="text/javascript"&gt;
    (function() {
        var t = document.getElementById('20100205-textarea'), b = document.getElementById('20100205-button');
        b.onclick = function() {
            t.value = t.value.replace(/[\s\n]+/g, ' ');
        };
    })();
&lt;/script&gt;
&lt;p&gt;
    There are many additional measures you take if you want to squeeze every last byte out of your code, but
    even this is enough to noticeably improve your website's response time.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=Bvo1Z1mI1vs:Ipa-I3VWJxM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Bvo1Z1mI1vs:Ipa-I3VWJxM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Bvo1Z1mI1vs:Ipa-I3VWJxM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Bvo1Z1mI1vs:Ipa-I3VWJxM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Bvo1Z1mI1vs:Ipa-I3VWJxM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Bvo1Z1mI1vs:Ipa-I3VWJxM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Bvo1Z1mI1vs:Ipa-I3VWJxM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Bvo1Z1mI1vs:Ipa-I3VWJxM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/Bvo1Z1mI1vs" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/02/05/easy-css-minification-on-client-and-server</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2010/01/10/javascript-design-patterns-lazy-initialization</guid>
                <title>JavaScript Design Patterns: Lazy Initialization</title>
                <link>http://feedproxy.google.com/~r/callee/~3/aieKifKhQpY/javascript-design-patterns-lazy-initialization</link>
                <pubDate>Sun, 10 Jan 2010 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    &lt;strong&gt;Lazy initialization&lt;/strong&gt; (also called &lt;strong&gt;lazy function definition&lt;/strong&gt; or &lt;strong&gt;lazy loading&lt;/strong&gt;)
    is a &lt;a href="http://en.wikipedia.org/wiki/Creational_pattern"&gt;creational design pattern&lt;/a&gt; which often 
    boosts performance. It's how you can avoid repeating an "expensive" process &amp;mdash; creating a 
    large or complex object, performing a difficult calculation, or spinning in a long loop. Lets take a 
    look at a few variations of this design pattern.
&lt;/p&gt;
&lt;h2&gt;On-Demand Script Loading&lt;/h2&gt;
&lt;p&gt;
    On-demand loading is becoming more widespread across JavaScript libraries. One big example is &lt;a href="http://developer.yahoo.com/yui/yuiloader/"&gt;Yahoo's YUI3&lt;/a&gt;;
    their library begins small and loads additional chunks as required. By  
    reducing the initial payload,
    on-demand loading can make your site load faster and be more responsive.
&lt;/p&gt;
&lt;p&gt;
    Outside of libraries, there are many ways to dynamically load scripts. &lt;a href="http://twitter.com/slicknet/"&gt;Nicholas Zakas&lt;/a&gt;
    suggests this as the &lt;a href="http://www.nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/"&gt;best method for loading external JavaScript files&lt;/a&gt;:
&lt;/p&gt;
&lt;pre&gt;
    function loadScript(url, callback){
        var script = document.createElement("script");
        script.type = "text/javascript";
        if (script.readyState) { // IE
            script.onreadystatechange = function() {
                if (script.readyState == "loaded" || script.readyState == "complete") {
                    script.onreadystatechange = null;
                    callback();
                }
            };
        } else { // not IE
            script.onload = function() {
                callback();
            };
        }
        script.src = url;
        document.getElementsByTagName("head")[0].appendChild(script);
    }
&lt;/pre&gt;
&lt;pre&gt;
    &amp;lt;!-- loadScript.js contains loadScript() as defined above --&amp;gt;
    &amp;lt;script type="text/javascript" src="loadScript.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type="text/javascript"&amp;gt;
        loadScript('/js/BigScript.js', function() {
            var o = new BigObject(); // assume this constructor is defined in BigScript.js
        });
    &amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
    By deferring &lt;code&gt;BigScript.js&lt;/code&gt;, the page appears to finish loading before that file is even fetched.
    The file loads "behind the scenes", letting your visitor start interacting with your site sooner.
&lt;/p&gt;
&lt;h2&gt;Lazy Factory&lt;/h2&gt;
&lt;p&gt;
    A &lt;a href="http://en.wikipedia.org/wiki/Factory_method_pattern"&gt;Factory&lt;/a&gt;
    generates an object when it is needed. A &lt;strong&gt;Lazy Factory&lt;/strong&gt; actually &lt;em&gt;generates the factory&lt;/em&gt;.
    One common Factory example is the &lt;code&gt;XMLHttpRequest&lt;/code&gt; object used to make AJAX calls:
&lt;/p&gt;
&lt;pre&gt;
    function getHttp() {
        if (typeof XMLHttpRequest != 'undefined') {
            return new XMLHttpRequest();
        } else if (typeof ActiveXObject != 'undefined') {
            return new ActiveXObject("MSXML2.XMLHttp");
        }
    }
    var http = getHttp();
&lt;/pre&gt;
&lt;p&gt;
    Every time you call &lt;code&gt;getHttp()&lt;/code&gt;, your code checks for the existance of &lt;code&gt;XMLHttpRequest&lt;/code&gt;,
    and then possibly checks for the existance of &lt;code&gt;ActiveXObject&lt;/code&gt;. Really, these checks only need to
    be made &lt;strong&gt;once&lt;/strong&gt;, because the results will always be the same under normal circumstances. Lazy
    Factory to the rescue:
&lt;/p&gt;
&lt;pre&gt;
    function lazyHttp() {
        if (typeof XMLHttpRequest != 'undefined') {
            lazyHttp = function() { return new XMLHttpRequest(); }
        } else if (typeof ActiveXObject != 'undefined') {
            lazyHttp = function() { return new ActiveXObject("MSXML2.XMLHttp"); }
        }
        return lazyHttp();
    }
    var http = lazyHttp();
&lt;/pre&gt;
&lt;p&gt;
    The first time &lt;code&gt;lazyHttp&lt;/code&gt; is called, it actually redefines itself based on the current environment.
    The second time it is called, &lt;code&gt;lazyHttp&lt;/code&gt; returns the proper object directly, no checks required.
    More complex factories might skip whole blocks of code, resulting in increased savings.
&lt;/p&gt;
&lt;h2&gt;Lazy Function Definition&lt;/h2&gt;
&lt;p&gt;
    Closely related to the Lazy Factory is &lt;strong&gt;Lazy Function Definition&lt;/strong&gt;. The first time a lazy
    function is called, it redefines itself based on a one-time calculation or evaluation. Often, JavaScript
    libraries implement this pattern when retrieving CSS style information:
&lt;/p&gt;
&lt;pre&gt;
    // these IF statements will be tested every time getStyle is called
    function getStyle(element, property){
        if (document.defaultView &amp;&amp; document.defaultView.getComputedStyle) { // not IE
            return document.defaultView.getComputedStyle(el, '')[property];
        } else if (element.currentStyle) { // IE
            return element.currentStyle[property];
        }
    }
    
    // these IF statements will be tested only the FIRST time getStyle is called
    function getStyle(element, property) {
        if (document.defaultView &amp;&amp; document.defaultView.getComputedStyle) {
            getStyle = function(element, property) {
                return document.defaultView.getComputedStyle(element, '')[property];
            };
        } else if (element.currentStyle) { // IE
            getStyle = function(element, property) {
                return element.currentStyle[property];
            };
        }
        return getStyle(element, property);
    }
&lt;/pre&gt;
&lt;p&gt;
    Because a Factory is technically just a function, these two implementations are basically identical. The difference
    is a conceptual one &amp;mdash; factory methods typically return an object, either through &lt;code&gt;new&lt;/code&gt; or 
    the object literal &lt;code&gt;{}&lt;/code&gt;. Generic lazy functions may return any type.
&lt;/p&gt;
&lt;h2&gt;Extreme LFD, or "But when will I really USE this?"&lt;/h2&gt;
&lt;p&gt;
    JavaScript is a powerful language, and can represent powerful constructs such as the &lt;strong&gt;Y combinator&lt;/strong&gt;:
&lt;/p&gt;
&lt;pre&gt;
var Y = function(F) {
    return (function(x) {
        return F(function(y) {
            return (x(x))(y);
        });
    })(function(x) {
        return F(function(y) {
            return (x(x))(y);
        });
    });
};
&lt;/pre&gt;
&lt;p&gt;
    It's way beyond the scope of this particular post to explain how the Y Combinator is derived and used,
    but here's a &lt;a href="http://matt.might.net/articles/implementation-of-recursive-fixed-point-y-combinator-in-javascript-for-memoization/"&gt;fantastic explanation&lt;/a&gt;
    along with a &lt;a href="http://matt.might.net/articles/implementation-of-recursive-fixed-point-y-combinator-in-javascript-for-memoization/#sec4"&gt;demonstration&lt;/a&gt;
    which calculates the 100th &lt;a href="http://mathworld.wolfram.com/FibonacciNumber.html"&gt;Fibonacci number&lt;/a&gt;
    &lt;strong&gt;instantly&lt;/strong&gt;, though a recursive (and non-lazy) implementation would take... well, in most 
    browsers it would never return.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=aieKifKhQpY:YLRX6D74eDQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=aieKifKhQpY:YLRX6D74eDQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=aieKifKhQpY:YLRX6D74eDQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=aieKifKhQpY:YLRX6D74eDQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=aieKifKhQpY:YLRX6D74eDQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=aieKifKhQpY:YLRX6D74eDQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=aieKifKhQpY:YLRX6D74eDQ:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=aieKifKhQpY:YLRX6D74eDQ:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/aieKifKhQpY" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[design patterns]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2010/01/10/javascript-design-patterns-lazy-initialization</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/12/25/quick-tip-regarding-chrome-frame</guid>
                <title>Quick Tip Regarding Google Chrome Frame</title>
                <link>http://feedproxy.google.com/~r/callee/~3/jzOMNxwGUCc/quick-tip-regarding-chrome-frame</link>
                <pubDate>Fri, 25 Dec 2009 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;
	When setting Internet Explorer's &lt;code&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/cc288325%28VS.85%29.aspx"&gt;X-UA-Compatible&lt;/a&gt;&lt;/code&gt;
	header, you can tell if it worked because the compatibility view button disappears. This indicates IE is using
	your chosen value, whether that's &lt;code&gt;ie=7&lt;/code&gt; for "IE7 Standards" mode, &lt;code&gt;ie=8&lt;/code&gt; for 
	"Industry Standards" mode (as close as IE8 gets, anyway), or &lt;code&gt;chrome=1&lt;/code&gt; for 
	&lt;a href="http://code.google.com/chrome/chromeframe/"&gt;Google Chrome Frame&lt;/a&gt;.
&lt;/p&gt;
&lt;div&gt;
	&lt;img src="/img/blog/internet-explorer-compatibility-view.png" alt="test"&gt;
	&lt;span&gt;Left: A typical IE toolbar. Right: The same page with the &lt;code&gt;X-UA-Compatible&lt;/code&gt; header (or meta tag) properly set.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
	With the introduction of Chrome Frame for IE, many webmasters would like their sites to load in Chrome Frame if
	available, and IE8 Standards mode otherwise. Some advise using two meta tags to accomplish this, with the first
	specifying &lt;code&gt;chrome=1&lt;/code&gt; and the second specifying &lt;code&gt;ie=8&lt;/code&gt;. However, according to &lt;a href="http://msdn.microsoft.com/en-us/library/cc288325%28VS.85%29.aspx"&gt;Microsoft's
	documentation&lt;/a&gt;, IE completely ignores any &lt;code&gt;X-UA-Compatible&lt;/code&gt; header beyond the first on a page, so
	if your visitor does not have Chrome Frame installed, your site will probably be viewed according to your
	&lt;code&gt;DOCTYPE&lt;/code&gt;, which may be IE8 Standards mode, IE7 Standards mode, or IE5 (quirks) mode. Also,
	the visitor's own Compatibility View setting can override your &lt;code&gt;DOCTYPE&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
	The proper way to specify more than one compatibility mode is to use &lt;strong&gt;one&lt;/strong&gt; meta tag, with
	the values separated by semicolons:
&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;!-- render in Chrome Frame if available, or IE8 Industry Standard mode otherwise --&amp;gt;
	&amp;lt;meta http-equiv="X-UA-Compatible" content="chrome=1;ie=8"&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
	As always, it's best to &lt;a href="http://arguments.callee.info/2009/01/26/choosing-a-doctype/"&gt;specify a &lt;code&gt;DOCTYPE&lt;/code&gt;&lt;/a&gt;
	to ensure cross-browser compatibility, but using Microsoft's &lt;code&gt;X-UA-Compatible&lt;/code&gt; meta tag
	correctly gives you an edge in the battle against IE's ever unpredictable Quirks Mode.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=jzOMNxwGUCc:2yL6zk9sgl4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jzOMNxwGUCc:2yL6zk9sgl4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=jzOMNxwGUCc:2yL6zk9sgl4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jzOMNxwGUCc:2yL6zk9sgl4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=jzOMNxwGUCc:2yL6zk9sgl4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jzOMNxwGUCc:2yL6zk9sgl4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jzOMNxwGUCc:2yL6zk9sgl4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=jzOMNxwGUCc:2yL6zk9sgl4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/jzOMNxwGUCc" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[html]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/12/25/quick-tip-regarding-chrome-frame</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/12/09/webcomic-wednesday-design-hell</guid>
                <title>Webcomic Wednesday: Design Hell</title>
                <link>http://feedproxy.google.com/~r/callee/~3/rxKo2y-WqXs/webcomic-wednesday-design-hell</link>
                <pubDate>Wed, 09 Dec 2009 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;
	This one's a biggie, but a goodie. Copyright &amp;copy; 2009 
	&lt;a href="http://0at.org/"&gt;Matthew Inman&lt;/a&gt;.
	Find more awesome comics at &lt;a href="http://theoatmeal.com/"&gt;http://theoatmeal.com/&lt;/a&gt;.
&lt;/p&gt;
&lt;div&gt;
	&lt;a href="/img/blog/webcomics/design-hell/1.png"&gt;&lt;img src="/img/blog/webcomics/design-hell/1.png"&gt;&lt;/a&gt;
	&lt;a href="/img/blog/webcomics/design-hell/2.jpg"&gt;&lt;img src="/img/blog/webcomics/design-hell/2.jpg"&gt;&lt;/a&gt;
	&lt;a href="/img/blog/webcomics/design-hell/3.jpg"&gt;&lt;img src="/img/blog/webcomics/design-hell/3.jpg"&gt;&lt;/a&gt;
	&lt;a href="/img/blog/webcomics/design-hell/4.png"&gt;&lt;img src="/img/blog/webcomics/design-hell/4.png"&gt;&lt;/a&gt;
	&lt;a href="/img/blog/webcomics/design-hell/5.png"&gt;&lt;img src="/img/blog/webcomics/design-hell/5.png"&gt;&lt;/a&gt;
	&lt;a href="/img/blog/webcomics/design-hell/6.png"&gt;&lt;img src="/img/blog/webcomics/design-hell/6.png"&gt;&lt;/a&gt;
	&lt;a href="/img/blog/webcomics/design-hell/7.png"&gt;&lt;img src="/img/blog/webcomics/design-hell/7.png"&gt;&lt;/a&gt;
	&lt;a href="/img/blog/webcomics/design-hell/8.png"&gt;&lt;img src="/img/blog/webcomics/design-hell/8.png"&gt;&lt;/a&gt;
	&lt;a href="/img/blog/webcomics/design-hell/9.jpg"&gt;&lt;img src="/img/blog/webcomics/design-hell/9.jpg"&gt;&lt;/a&gt;
	&lt;span&gt;See this bad boy in its &lt;a href="http://theoatmeal.com/comics/design_hell"&gt;natural habitat&lt;/a&gt;.&lt;/span&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=rxKo2y-WqXs:6rK62YQN3e0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=rxKo2y-WqXs:6rK62YQN3e0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=rxKo2y-WqXs:6rK62YQN3e0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=rxKo2y-WqXs:6rK62YQN3e0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=rxKo2y-WqXs:6rK62YQN3e0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=rxKo2y-WqXs:6rK62YQN3e0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=rxKo2y-WqXs:6rK62YQN3e0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=rxKo2y-WqXs:6rK62YQN3e0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/rxKo2y-WqXs" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[webcomic wednesday]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/12/09/webcomic-wednesday-design-hell</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/12/02/soudersize-the-web</guid>
                <title>Soudersize the Web</title>
                <link>http://feedproxy.google.com/~r/callee/~3/VaTMewScvFU/soudersize-the-web</link>
                <pubDate>Wed, 02 Dec 2009 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;
	If you're a web developer, you probably already know about &lt;a href="http://search.twitter.com/search?q=souders"&gt;Steve Souders&lt;/a&gt;.
	He's quite possibly the most influential proponent of high performance websites out there.
	
&lt;/p&gt;
&lt;div&gt;
	&lt;a href="/img/blog/souders/the-man.jpg"&gt;&lt;img src="/img/blog/souders/the-man.jpg"&gt;&lt;/a&gt;
	&lt;span&gt;Steve Souders&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Steve's Stuff&lt;/h2&gt;
&lt;p&gt;
	Steve Souders has founded and participated in a number of projects, most of which involve making 
	websites faster, making software faster, or promoting other open source projects and
	developers. See how many of these you already use, and try those you don't &amp;mdash; you 
	might even find a new favorite!
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://www.browserscope.org/"&gt;Browserscope&lt;/a&gt;: &lt;/strong&gt;Profiles actual web browsers to track performance, support, adherence to web standards, and a host of other features.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://stevesouders.com/compare.php"&gt;Compare&lt;/a&gt;: &lt;/strong&gt;Pits any two websites against each other in a performance battle.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://cs193h.stevesouders.com/"&gt;CS193H&lt;/a&gt;: &lt;/strong&gt;Lectures from the Stanford course Steve taught. Uses &lt;a href="http://www.amazon.com/gp/product/0596529309?ie=UTF8&amp;tag=argumentscall-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596529309"&gt;High Performance Web Sites&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=argumentscall-20&amp;l=as2&amp;o=1&amp;a=0596529309" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;"&gt; as the textbook.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://stevesouders.com/cuzillion/"&gt;Cuzillion&lt;/a&gt;: &lt;/strong&gt;Quickly test a variety of components (images, stylesheets, scripts) in any order, and determine that order's effect on page load time.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://stevesouders.com/episodes/"&gt;Episodes&lt;/a&gt;: &lt;/strong&gt;An open source "framework for timing web pages", including Web 2.0 apps. &lt;em&gt;(Starting to see a trend?)&lt;/em&gt;&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://stevesouders.com/hammerhead/"&gt;Hammerhead&lt;/a&gt;: &lt;/strong&gt;A &lt;a href="http://www.getfirebug.com/"&gt;Firebug&lt;/a&gt; add-on which provides more precise website load time measurement and stress testing.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://stevesouders.com/linkwalker.php"&gt;Link Walker&lt;/a&gt;: &lt;/strong&gt;Loop through and open a list of URLs extracted from your current webpage.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://spriteme.org/"&gt;SpriteMe&lt;/a&gt;: &lt;/strong&gt;Automatically generates CSS sprites using your website's images.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://en.oreilly.com/velocity2010"&gt;Velocity&lt;/a&gt;: &lt;/strong&gt;Annual web performance and operations conference.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://www.youtube.com/view_play_list?p=689D6EE903ED5CB6"&gt;Web Exponents&lt;/a&gt;: &lt;/strong&gt;Speaker series covering web development innovations and best practices.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt;: &lt;/strong&gt;A &lt;a href="http://www.getfirebug.com/"&gt;Firebug&lt;/a&gt; add-on which tests websites against several high performance best practices. Also incorporates a ton of other dev-friendly tools.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Yahoo!&lt;/h2&gt;
&lt;p&gt;
	In 2004, Steve became "Chief Performance Yahoo!" at, well, Yahoo! His goal was to measure and improve
	performance across every Yahoo-run product. It was then that he discovered that over 80% of the
	time spent waiting for a webpage to load is spent on the front end, 
	not the back end. 
	This research led Souders to create &lt;a href="http://stevesouders.com/hpws/rules.php"&gt;14 Rules for Faster-Loading Web Sites&lt;/a&gt;,
	the cornerstone for what would become Yahoo's own 
	&lt;a href="http://developer.yahoo.com/performance/"&gt;Exceptional Performance&lt;/a&gt; and &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt;.
&lt;/p&gt;
&lt;div&gt;
	&lt;iframe src="http://rcm.amazon.com/e/cm?t=argumentscall-20&amp;o=1&amp;p=8&amp;l=as1&amp;asins=0596529309&amp;fc1=000000&amp;IS2=1&amp;lt1=_blank&amp;m=amazon&amp;lc1=0000FF&amp;bc1=000000&amp;bg1=FFFFFF&amp;f=ifr" style="width:120px;height:240px;margin:1em;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"&gt;&lt;/iframe&gt;
	&lt;iframe src="http://rcm.amazon.com/e/cm?t=argumentscall-20&amp;o=1&amp;p=8&amp;l=as1&amp;asins=0596522304&amp;fc1=000000&amp;IS2=1&amp;lt1=_blank&amp;m=amazon&amp;lc1=0000FF&amp;bc1=000000&amp;bg1=FFFFFF&amp;f=ifr" style="width:120px;height:240px;margin:1em;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"&gt;&lt;/iframe&gt;
	&lt;span&gt;Two books on website performance by Steve Souders.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Google&lt;/h2&gt;
&lt;p&gt;
	In January of 2008, Souders left Yahoo for Google. They have since released many of their own products
	and enhancements based on improving website performance. Coincidence? Perhaps. Although Google has had
	a history of shaving bytes to save their own bandwidth, there hadn't been a major
	effort to improve website performance &lt;strong&gt;everywhere&lt;/strong&gt; until about a year ago. 
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;New Slogan: &lt;/strong&gt;&lt;a href="http://googleblog.blogspot.com/2009/06/lets-make-web-faster.html"&gt;Let's&lt;/a&gt; &lt;a href="http://code.google.com/speed/articles/tech-talks.html"&gt;make&lt;/a&gt; &lt;a href="http://googleresearch.blogspot.com/2009/06/speed-matters.html"&gt;the&lt;/a&gt; &lt;a href="http://code.google.com/speed/articles/"&gt;web&lt;/a&gt; &lt;a href="http://www.webpronews.com/topnews/2009/11/13/google-page-speed-may-be-a-ranking-factor-in-2010"&gt;faster&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://googlecode.blogspot.com/2009/12/google-analytics-launches-asynchronous.html"&gt;Asynchronous Analytics&lt;/a&gt;: &lt;/strong&gt;Ending the reign of a notoriously picky and occasionally sluggish analytics script.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://code.google.com/closure/"&gt;Closure&lt;/a&gt;: &lt;/strong&gt;A JavaScript optimizer/compiler and a modular JavaScript library, as well as a set of templates for dynamically generating HTML.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://code.google.com/speed/page-speed/"&gt;Page Speed&lt;/a&gt;: &lt;/strong&gt;A &lt;a href="http://www.getfirebug.com/"&gt;Firebug&lt;/a&gt; add-on which evaluates website performance against a huge variety of factors.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://dev.chromium.org/spdy"&gt;SPDY&lt;/a&gt;: &lt;/strong&gt;An experimental transport protocol (like HTTP and FTP) to reduce website latency. Their target is to be twice as fast as HTTP.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;&lt;a href="http://code.google.com/speed/articles/"&gt;Tutorials and Articles&lt;/a&gt;: &lt;/strong&gt;An ever-growing list of ways to reduce bandwidth usage and page load time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;"Soudersize"? That's not even a word.&lt;/h2&gt;
&lt;p&gt;
	No, you got me. But with the "speed up the web" mantra being thrown around these days, it seems
	appropriate to credit one of the movement's key players. How do you Soudersize your website? Are
	there any best practices or performance hacks you use to keep it running smoothly?
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=VaTMewScvFU:pXNisFknpRM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=VaTMewScvFU:pXNisFknpRM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=VaTMewScvFU:pXNisFknpRM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=VaTMewScvFU:pXNisFknpRM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=VaTMewScvFU:pXNisFknpRM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=VaTMewScvFU:pXNisFknpRM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=VaTMewScvFU:pXNisFknpRM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=VaTMewScvFU:pXNisFknpRM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/VaTMewScvFU" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[performance]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/12/02/soudersize-the-web</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/11/11/digital-signage-application-launch</guid>
                <title>Digital Signage Application Launch</title>
                <link>http://feedproxy.google.com/~r/callee/~3/SMjfgs-PuQs/digital-signage-application-launch</link>
                <pubDate>Wed, 11 Nov 2009 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;
	Today we launched the second major version of &lt;a href="http://www.dcsimedia.com/"&gt;DCSI&lt;/a&gt;'s 
	digital out-of-home media player. The first version, written primarily in &lt;a href="http://www.adobe.com/products/flex/"&gt;Adobe Flex&lt;/a&gt;
	on &lt;a href="http://www.adobe.com/products/air/"&gt;Adobe AIR&lt;/a&gt;, has been going strong for over a year.
&lt;/p&gt;
&lt;p&gt;
	The PHP back end automates video and image conversion, playlist building, and content pushing, and monitors 
	each player's health and actual play logs. Weather, sports scores, and news feeds are all pulled live, with 
	caching built in (on server AND client) to reduce bandwidth requirements and improve reliability. Since there 
	are hundreds of active locations with thousands more contracted, we've put a lot of effort into streamlining 
	the way this application works.
&lt;/p&gt;
&lt;div&gt;
	&lt;a href="/img/blog/dcsi/legacy-player.png"&gt;&lt;img src="/img/blog/dcsi/legacy-player.png"&gt;&lt;/a&gt;
	&lt;span&gt;&lt;a href="http://www.dcsimedia.com/"&gt;DCSI&lt;/a&gt;'s legacy player has one fixed state, but can display a wide variety of content.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Two point oh&lt;/h2&gt;
&lt;p&gt;
	Version two is written primarily in JavaScript on the &lt;a href="http://www.appcelerator.com/products/titanium-desktop/"&gt;Appcelerator Titanium&lt;/a&gt;
	platform. It also allows new states to be defined and added to the player, so panels can be any size, anywhere on
	the screen. The player smoothly transitions from state to state, which in itself can be more visually engaging
	and draw the eye.
&lt;/p&gt;
&lt;div&gt;
	&lt;a href="/img/blog/dcsi/new-player.gif"&gt;&lt;img src="/img/blog/dcsi/new-player.gif"&gt;&lt;/a&gt;
	&lt;span&gt;Sample states for the new player (current count is over a dozen). In "real life" the panels smoothly resize 
	and reposition themselves based on state and content. The logo, weather, and time are also regular panels, and 
	can also change size and position, or even be replaced with any video, image, or RSS feed.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Difference in development&lt;/h2&gt;
&lt;p&gt;
	It can be both exciting and harrowing developing on Titanium. At &lt;a href="http://www.geekworksllc.com/"&gt;Geekworks&lt;/a&gt;
	we're used to working with bleeding edge technologies, but there are times when Titanium's beta status can be risky for 
	production code. Some days &lt;a href="http://www.appcelerator.com/appcelerator-platform/titanium-architecture/"&gt;Appcelerator's cloud&lt;/a&gt;
	goes down, and that means I may not be able to compile 
	until it comes back up. 
&lt;/p&gt;
&lt;p&gt;
	Fortunately the bad times are few and far between, and the support team is always quick to respond. Actually, 
	because Titanium is an open source project, it's often easier to get fast support than it can be with Adobe AIR.
&lt;/p&gt;
&lt;h2&gt;And the winner is...?&lt;/h2&gt;
&lt;p&gt;
	I don't think either AIR or Titanium will become the One True Answer for cross-platform development and deployment.
	Each has its strengths and weaknesses, and the goals driving each platform appear significantly different. I 
	will continue to use AIR for quick personal applications and things I want to make easy for "the masses". It's 
	easier and faster to deploy an AIR app, and you can run the same installer on Windows, Mac, or Linux.
&lt;/p&gt;
&lt;p&gt;
	Titanium has become a better choice at work, despite its beta status. I expect we will continue using it for 
	many client projects to come. It always seemed like we were fighting AIR's sandbox issues and other 
	OS restrictions, which makes Titanium the clear choice for enterprise software and kiosk or other 
	"locked down" systems. All this may change as both platforms evolve.
&lt;/p&gt;
&lt;h2&gt;Roll out!&lt;/h2&gt;
&lt;p&gt;
	Today's launch will be about 5 venues in Phoenix, AZ. After that we will begin rolling out to over a hundred 
	locations across the United States. Where do you live? I'd love to get some comments from people who have seen
	either version in action. Post your location, and I'll let you know where to find the nearest sign!
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=SMjfgs-PuQs:WoPkcicp8Hk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SMjfgs-PuQs:WoPkcicp8Hk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=SMjfgs-PuQs:WoPkcicp8Hk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SMjfgs-PuQs:WoPkcicp8Hk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=SMjfgs-PuQs:WoPkcicp8Hk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SMjfgs-PuQs:WoPkcicp8Hk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SMjfgs-PuQs:WoPkcicp8Hk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=SMjfgs-PuQs:WoPkcicp8Hk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/SMjfgs-PuQs" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[adobe air]]></category>
		<category><![CDATA[personal]]></category>
		<category><![CDATA[titanium]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/11/11/digital-signage-application-launch</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/11/11/basic-server-authentication-with-php</guid>
                <title>Basic Server Authentication with PHP</title>
                <link>http://feedproxy.google.com/~r/callee/~3/NiQUtpqUzfc/basic-server-authentication-with-php</link>
                <pubDate>Wed, 11 Nov 2009 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    Sometimes you want to secure a particular webpage, but don't want to manage users or accounts, write a separate
    login page, etc. The easy way to authenticate is to use HTTP's &lt;code&gt;WWW-Authenticate&lt;/code&gt; response header:
&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php
    
    $nouser = empty($_SERVER['PHP_AUTH_USER']) || ($_SERVER['PHP_AUTH_USER'] != 'MyUsername');
    $nopass = empty($_SERVER['PHP_AUTH_PW']) || ($_SERVER['PHP_AUTH_PW'] != 'MyPassword');
    
    if ($nouser || $nopass) {
        header('WWW-Authenticate: Basic realm="Geekworks Unlimited, LLC."');
        header('HTTP/1.0 401 Unauthorized');
        echo '&amp;lt;h1&amp;gt;HTTP/1.0 401 Unauthorized&amp;lt;/h1&amp;gt;';
    } else {
?&amp;gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;My Secret Website&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        Your website content goes here.
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&amp;lt;?php    
    }
    
    exit;
    
?&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
    This checks against a single (hardcoded) username and password, which is fine in simple cases. If you 
    &lt;strong&gt;do&lt;/strong&gt; need to give each user his or her own username and password, you can replace the first 
    two lines with a loop that checks &lt;code&gt;$_SERVER['PHP_AUTH_USER']&lt;/code&gt; and &lt;code&gt;$_SERVER['PHP_AUTH_PW']&lt;/code&gt;
    against values in your user database.
&lt;/p&gt;
&lt;p&gt;
    One note: You will want to change where it says &lt;strong&gt;realm="..."&lt;/strong&gt;. This message is shown in the
    popup that asks the visitor to input a username and password. It also serves as a possible set of pages to
    authenticate against. If you include the same &lt;code&gt;realm&lt;/code&gt; on multiple pages, the user will only have 
    to log in once in order to visit all of the pages.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=NiQUtpqUzfc:-UCEkNzaAzY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=NiQUtpqUzfc:-UCEkNzaAzY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=NiQUtpqUzfc:-UCEkNzaAzY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=NiQUtpqUzfc:-UCEkNzaAzY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=NiQUtpqUzfc:-UCEkNzaAzY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=NiQUtpqUzfc:-UCEkNzaAzY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=NiQUtpqUzfc:-UCEkNzaAzY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=NiQUtpqUzfc:-UCEkNzaAzY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/NiQUtpqUzfc" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/11/11/basic-server-authentication-with-php</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/10/28/quick-query-string-parsing-with-javascript</guid>
                <title>Quick Query String Parsing with JavaScript</title>
                <link>http://feedproxy.google.com/~r/callee/~3/P-F6dsjUOkM/quick-query-string-parsing-with-javascript</link>
                <pubDate>Wed, 28 Oct 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a&gt;(skip to the parser)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
    The "query string" is a way to embed variables in a URL. For example, if you search 
    for "test" in &lt;a href=""&gt;Yahoo!&lt;/a&gt;, you might see a URL like this: 
&lt;/p&gt;
&lt;pre&gt;
    http://search.yahoo.com/search?p=test&amp;toggle=1&amp;cop=mss&amp;ei=UTF-8&amp;fr=yfp-t-701
&lt;/pre&gt;
&lt;p&gt;
    The query string is everything after and including the question mark, in this case 
    &lt;strong&gt;?p=test&amp;toggle=1&amp;cop=mss&amp;ei=UTF-8&amp;fr=yfp-t-701&lt;/strong&gt;. As you can see, 
    the query string contains key-value pairs, split by ampersands (&amp;).In PHP you can
    easily grab those values using the built-in &lt;a href="http://php.net/get"&gt;&lt;code&gt;$_GET&lt;/code&gt; superglobal&lt;/a&gt;:
&lt;/p&gt;
&lt;pre&gt;
    // assume the same query string as above: ?p=test&amp;toggle=1&amp;cop=mss&amp;ei=UTF-8&amp;fr=yfp-t-701
    
    echo $_GET['p'];      // test
    echo $_GET['toggle']; // 1
    echo $_GET['cop'];    // mss
    
    echo $_GET['invalid']; // null or empty, no such key exists
&lt;/pre&gt;
&lt;p&gt;
    JavaScript sees the query string as a plain string, called &lt;code&gt;window.location.search&lt;/code&gt;.
    While there are many methods out there to parse &lt;code&gt;location.search&lt;/code&gt; and make its
    key-value pairs available, I feel that most of them are overkill for what should be simple
    behavior.
&lt;/p&gt;
&lt;pre id="wheat"&gt;
    // JavaScript equivalent of PHP's $_GET superglobal
    function $_GET(key) {
        try {
            return window.location.search.match(new RegExp('[\?&amp;]' + key + '=([^&amp;#]+)'))[1];
        } catch (err) {
            return '';
        }
    }
    
    alert( $_GET('p') );      // test
    alert( $_GET('toggle') ); // 1
    alert( $_GET('cop') );    // mss
    
    alert( $_GET('invalid'); // empty string '', no such key exists
&lt;/pre&gt;
&lt;p&gt;
    Notice that the JavaScript requires parentheses: &lt;strong&gt;&lt;code&gt;$_GET('p')&lt;/code&gt;&lt;/strong&gt;, while
    PHP requires brackets: &lt;strong&gt;&lt;code&gt;$_GET['p']&lt;/code&gt;&lt;/strong&gt;. This is because the JavaScript
    version is a function, while the PHP version is an array. To avoid the confusion you could parse
    out every variable on page load and store it in a single array, but I find this method suitable
	enough for my own needs. Hope you find it useful!
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=P-F6dsjUOkM:yW_kbEvj5kw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=P-F6dsjUOkM:yW_kbEvj5kw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=P-F6dsjUOkM:yW_kbEvj5kw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=P-F6dsjUOkM:yW_kbEvj5kw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=P-F6dsjUOkM:yW_kbEvj5kw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=P-F6dsjUOkM:yW_kbEvj5kw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=P-F6dsjUOkM:yW_kbEvj5kw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=P-F6dsjUOkM:yW_kbEvj5kw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/P-F6dsjUOkM" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/10/28/quick-query-string-parsing-with-javascript</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/10/12/the-three-cs-of-fast-css-and-javascript</guid>
                <title>The Three Cs of Fast CSS and JavaScript</title>
                <link>http://feedproxy.google.com/~r/callee/~3/73R5M22hQNU/the-three-cs-of-fast-css-and-javascript</link>
                <pubDate>Mon, 12 Oct 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    One major "best practice" from both &lt;a href="http://developer.yahoo.com/yslow/"&gt;Yahoo! YSlow&lt;/a&gt; and 
    &lt;a href="http://code.google.com/speed/page-speed/"&gt;Google Page Speed&lt;/a&gt;
    is &lt;strong&gt;minimizing HTTP requests&lt;/strong&gt;. Yahoo! indicates that "80% of the end-user response time is 
    spent on the front end. Most of this time is tied up in downloading all the components in the page: 
    images, stylesheets, scripts, Flash, etc." &lt;sup&gt;&lt;a href="http://developer.yahoo.com/performance/rules.html"&gt;cite&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;
&lt;p&gt;
    What does this mean? The fewer calls your page makes to the server, the faster your page is. Here 
    are three ways to drastically improve your site response time:
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Combine&lt;/strong&gt; your stylesheets and scripts so that you only have to load one CSS file and one JS file.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Compress&lt;/strong&gt; and minify the combined files so you save bandwidth and transfer time.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Cache&lt;/strong&gt; the compressed output so your server doesn't have to repeat the process for each new visitor.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
    Here's one way to accomplish this:
&lt;/p&gt;
&lt;pre&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;link rel="stylesheet" type="text/css" href="load.php?type=css"&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        ... website content ...
        &amp;lt;script type="text/javascript" src="load.php?type=js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;pre&gt;
&amp;lt;?php
    
    /**
     * The following directory structure is assumed:
     * css/
     *     reset.css
     *     style.css
     * js/
     *     jQuery.js
     *     main.js
     * index.php
     * ...        (the rest of your webpages)
     * load.php   (THIS code block)
     */ 
    
    error_reporting(0);       // supress errors (browsers will see them as invalid CSS/JS)
    ob_start('ob_gzhandler'); // compress with GZIP
    
    $isJavaScript = ($_GET['type'] == 'js');
    if ($isJavaScript) {
        $type = 'javascript'; // content type
        $ext = 'js';          // file extension
        $files = array(       // TODO: modify this list based on your own JS files
            'jQuery',
            'main'
        );
    } else {
        $type = $ext = 'css'; // content type is the same as the extension
        $files = array(       // TODO: modify this list based on your own CSS files
            'reset',
            'style'
        );
    }
    
    header("Content-Type: text/$type"); // set the appropriate content type
    $cache = "$ext/cache.$ext";         // for example, css/cache.css
    if (!file_exists($cache)) {         // see if we've already created a cache
        $toCache = '';
        foreach ($files as $f) {
            $f = "$ext/$f.$ext";        // for example, css/style.css
            if (file_exists($f)) {
                $c = file_get_contents($f);
                if ($isJavaScript) {
                    include_once('jsmin.php');     // &lt;a href="http://code.google.com/p/jsmin-php/"&gt;get JSMin here&lt;/a&gt;
                    $toCache .= JSMin::minify($c); // slow, but only called when there is no cache
                } else {
                    // quick and easy way to minify CSS, removes multiple spaces and blank lines
                    $toCache .= preg_replace(array('/\s\s+/', '/\n/'), array(' ', ' '), $c);
                }
            }
        }
        file_put_contents($cache, $toCache);
    }
    readfile($cache);
    exit;
    
?&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
    The first time the script is called (ever), it may take a few seconds to generate the cache file, 
    but every time after that it will load extremely quickly due to the "three Cs" implemented above.
    Since minification is part of the process, you can work directly with unminified and commented
    code on the server, and let the script minify it for you. If you normally load 3+ stylesheets
	or scripts, you will see a massive performance boost with this type of script.
&lt;/p&gt;
&lt;p&gt;
    If and when you change any of your CSS or JavaScript, just delete the cached file and 
    everything is automatically updated on your site's next visit.
&lt;/p&gt;
&lt;p&gt;
    Do you have a favorite method of combining scripts to improve website performance? Please
    share it in a comment!
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=73R5M22hQNU:Abtc0VOBG54:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=73R5M22hQNU:Abtc0VOBG54:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=73R5M22hQNU:Abtc0VOBG54:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=73R5M22hQNU:Abtc0VOBG54:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=73R5M22hQNU:Abtc0VOBG54:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=73R5M22hQNU:Abtc0VOBG54:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=73R5M22hQNU:Abtc0VOBG54:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=73R5M22hQNU:Abtc0VOBG54:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/73R5M22hQNU" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/10/12/the-three-cs-of-fast-css-and-javascript</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/09/26/adobe-air-vs-appcelerator-titanium-round-two</guid>
                <title>Adobe AIR vs Appcelerator Titanium: Round Two</title>
                <link>http://feedproxy.google.com/~r/callee/~3/cLZU4kcXxw4/adobe-air-vs-appcelerator-titanium-round-two</link>
                <pubDate>Sat, 26 Sep 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    Well, it's been a few months since 
    &lt;a href="http://arguments.callee.info/2009/06/11/adobe-air-vs-appcelerator-titanium-round-one/"&gt;my last 
    AIR/Titanium comparison&lt;/a&gt;, and the game has changed. Though AIR has only updated from 1.5.1 to
    1.5.2, that upgrade brought &lt;a href="http://www.adobe.com/support/documentation/en/air/1_5_2/releasenotes_developers.html"&gt;a ton of bug fixes and enhancements&lt;/a&gt;,
    especially for the Mac and Linux platforms. Titanium has moved from 0.4.1 to 0.6.2, which is
	a lot of revision.
&lt;/p&gt;
&lt;h2&gt;What's the same?&lt;/h2&gt;
&lt;p&gt;
    The development environments for both have not changed. I would like to clarify one point I made about packaging
    your applications in Adobe AIR versus Appcelerator Titanium &amp;mdash; I mentioned the Titanium process was easier
    becuase you don't have to deal with certificiates. This is true, but the nice thing about AIR's packaging process
    is that you build &lt;strong&gt;one&lt;/strong&gt; installer, and it runs on all platforms. With Titanium, if you want
    to run on Windows, Mac, and Linux, you must build three different packages.  
&lt;/p&gt;
&lt;h2&gt;What's new in Titanium?&lt;/h2&gt;
&lt;p&gt;
    Some of the &lt;a href="http://www.codestrong.com/titanium/api/"&gt;Titanium Desktop API&lt;/a&gt; has changed since Round One:
&lt;/p&gt;
&lt;pre&gt;
    
    // OPENING AND READING FROM A LOCAL FILE
    var TF = Titanium.Filesystem; // for convenience
    var r = TF.getRootDirectories()[0]; // typically "C:\" on Windows, "/" on Mac and Linux
    var file = TF.getFile(r, 'path', 'to', 'myFile.txt');
    var stream = TF.getFileStream(file); // open the file...
    stream.open(TF.MODE_READ);           // ... for reading only ...
    var fileContents = stream.read();    // ... retrieve the entire contents ...
    stream.close();                      // ... and close the stream
    /**
     * NOTE: getFile() automatically glues arguments together with the correct
     * file separator for the operating system it runs on. "\" for Windows,
     * "/" for Mac/Linux.
     */
    
    
    // RUNNING AN EXTERNAL PROGRAM
    var notepad = Titanium.Process.createProcess({
        args : ['C:\\Windows\\notepad.exe']
    });
    notepad.launch(); // open the program
    notepad.kill();   // close the program
    
    
    // RESTARTING YOUR APPLICATION PROGRAMMATICALLY
    Titanium.Process.createProcess({
        args : [Titanium.App.path]   // automatically uses your program's executable
    }).launch();                     // inline launching
    Titanium.App.exit();             // close the "old" running copy
    
&lt;/pre&gt;
&lt;h2&gt;Where to go from here?&lt;/h2&gt;
&lt;p&gt;
	Adobe AIR is still a more mature, more stable platform, but Titanium is rapidly growing and becoming a serious
	competitor. There are things each suite can do that the other can't, and there may never be a clear winner.
	If you want to learn more about Titanium, they just released a series of &lt;a href="http://www.appcelerant.com/new-titanium-zero-to-app-videos-posted.html"&gt;"Zero-to-App"&lt;/a&gt;
	videos along with a roadmap for future videos. Appcelerator seems to be focused heavily on the iPhone and Android phone
	markets, so if you're a desktop or kiosk developer (like me) you may not get as much from those videos, but
	it's a start.
&lt;/p&gt;
&lt;h2&gt;Join the discussion!&lt;/h2&gt;
&lt;p&gt;
	Do you use Adobe AIR? Appcelerator Titanium? Both? Please leave a comment, let me know what you like or 
	don't like about these technologies. 
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=cLZU4kcXxw4:kGs87Tcrnzo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cLZU4kcXxw4:kGs87Tcrnzo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=cLZU4kcXxw4:kGs87Tcrnzo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cLZU4kcXxw4:kGs87Tcrnzo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=cLZU4kcXxw4:kGs87Tcrnzo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cLZU4kcXxw4:kGs87Tcrnzo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cLZU4kcXxw4:kGs87Tcrnzo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=cLZU4kcXxw4:kGs87Tcrnzo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/cLZU4kcXxw4" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[adobe air]]></category>
		<category><![CDATA[review]]></category>
		<category><![CDATA[titanium]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/09/26/adobe-air-vs-appcelerator-titanium-round-two</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/09/23/webcomic-wednesday-behind-schedule</guid>
                <title>Webcomic Wednesday: Behind Schedule</title>
                <link>http://feedproxy.google.com/~r/callee/~3/kWMtx7AphB0/webcomic-wednesday-behind-schedule</link>
                <pubDate>Wed, 23 Sep 2009 04:00:00 +0000</pubDate>
                <description>&lt;div&gt;
    &lt;a href="http://notinventedhe.re/on/2009-9-22"&gt;&lt;img src="/img/blog/webcomics/2009-09-22-not-invented-here.jpg" alt="Desmond: The project is behind schedule. It was inevitable, you change the spec every five minutes. Owen: Don't exaggerate. Anyway, here's the latest revision. Desmond: Does it include the changes from five minutes ago? Owen: Yes."&gt;&lt;/a&gt;
    &lt;span&gt;This Webcomic Wednesday is brought to you by &lt;a href="http://notinventedhe.re/"&gt;Not Invented Here&lt;/a&gt;, a brand new webcomic from the creators of &lt;a href="http://www.unshelved.com/"&gt;Unshelved&lt;/a&gt; and &lt;a href="http://www.uglyhill.com/"&gt;Ugly Hill&lt;/a&gt;.&lt;/span&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=kWMtx7AphB0:pN1urJoz0Hk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=kWMtx7AphB0:pN1urJoz0Hk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=kWMtx7AphB0:pN1urJoz0Hk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=kWMtx7AphB0:pN1urJoz0Hk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=kWMtx7AphB0:pN1urJoz0Hk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=kWMtx7AphB0:pN1urJoz0Hk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=kWMtx7AphB0:pN1urJoz0Hk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=kWMtx7AphB0:pN1urJoz0Hk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/kWMtx7AphB0" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[webcomic wednesday]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/09/23/webcomic-wednesday-behind-schedule</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/09/05/wampserver-2-and-amfphp-service-browser</guid>
                <title>Wampserver 2 and AMFPHP Service Browser</title>
                <link>http://feedproxy.google.com/~r/callee/~3/BZ0yQahx4RA/wampserver-2-and-amfphp-service-browser</link>
                <pubDate>Sat, 05 Sep 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    I installed &lt;a href="http://www.wampserver.com/en/"&gt;Wampserver 2&lt;/a&gt;
    and &lt;a href="http://sourceforge.net/projects/amfphp/files/"&gt;AMFPHP&lt;/a&gt;
    on a computer at work to run some tests, and instead ran into an error
    when trying to use the &lt;a href="http://www.amfphp.org/docs/servicebrowser.html"&gt;Service Browser&lt;/a&gt;.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/amfphp/wampserver-amfphp-eregi-error.png"&gt;&lt;img src="/img/blog/amfphp/wampserver-amfphp-eregi-error.png" alt="Error retrieving service info: Function eregi_replace() is deprecated C:\wamp\www\amfphp\core\shared\util\MethodTable.php on line 506"&gt;&lt;/a&gt;
    &lt;span&gt;Error retrieving service info:&lt;br&gt;&lt;br&gt;Function eregi_replace() is deprecated&lt;br&gt;C:\wamp\www\amfphp\core\shared\util\MethodTable.php on line 506&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
    Fortunately, it's easy to fix this problem. Open 
    &lt;code&gt;/amfphp/core/shared/util/MethodTable.php&lt;/code&gt; and find
    the following lines:
&lt;/p&gt;
&lt;pre&gt;
        $comment = eregi_replace("\n[ \t]+", "\n", trim($comment));
        $comment = str_replace("\n", "\\n", trim($comment));
        $comment = eregi_replace("[\t ]+", " ", trim($comment));
&lt;/pre&gt;
&lt;p&gt;
    Modify those lines as follows (feel free to store a backup copy of &lt;code&gt;MethodTable.php&lt;/code&gt; first):
&lt;/p&gt;
&lt;pre&gt;
        $comment = preg_replace("#\n[ \t]+#U", "\n", trim($comment));
        $comment = str_replace("\n", "\\n", trim($comment));
        $comment = preg_replace("#[\t ]+#U", " ", trim($comment));
&lt;/pre&gt;
&lt;p&gt;
    That's it! The AMFPHP Service Browser can now interact with your AMFPHP services.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=BZ0yQahx4RA:pt3PV867yLY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=BZ0yQahx4RA:pt3PV867yLY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=BZ0yQahx4RA:pt3PV867yLY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=BZ0yQahx4RA:pt3PV867yLY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=BZ0yQahx4RA:pt3PV867yLY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=BZ0yQahx4RA:pt3PV867yLY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=BZ0yQahx4RA:pt3PV867yLY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=BZ0yQahx4RA:pt3PV867yLY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/BZ0yQahx4RA" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[personal]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[flash and actionscript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/09/05/wampserver-2-and-amfphp-service-browser</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/08/31/saving-drawn-images-with-canvas-and-cake.js</guid>
                <title>Saving Drawn Images with Canvas and Cake.js</title>
                <link>http://feedproxy.google.com/~r/callee/~3/qBguei1wj5A/saving-drawn-images-with-canvas-and-cake.js</link>
                <pubDate>Mon, 31 Aug 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    The code on this page &lt;strong&gt;will not&lt;/strong&gt; work in Internet
    Explorer. IE does not support the HTML5 &lt;code&gt;canvas&lt;/code&gt; tag.
    Someone might be able to adapt this to detect IE and use VML
    instead, but that is out of the scope of this post.
&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;a href="#convenience-functions"&gt;Convenience functions&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#creating-the-canvas-and-scene"&gt;Creating the canvas and scene&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#creating-the-brush"&gt;Creating the brush&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#serializing-the-canvas-image"&gt;Serializing the canvas image&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#storing-the-image-with-php"&gt;Storing the image with PHP&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#code-all-together"&gt;"Code all together"&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="convenience-functions"&gt;Convenience functions&lt;/h2&gt;
&lt;p&gt;
    The only external code this example relies on is &lt;a href="http://cakejs.googlecode.com/svn/trunk/src/cake.js"&gt;Cake.js&lt;/a&gt;,
    a library that abstracts and automates a lot of the steps required 
    in using the &lt;a href="https://developer.mozilla.org/en/Canvas_tutorial"&gt;HTML5 &lt;code&gt;canvas&lt;/code&gt;&lt;/a&gt; tag.
&lt;/p&gt;
&lt;pre&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;style type="text/css"&amp;gt;
            #canvas {
                width: 600px;
                height: 400px;
            }
        &amp;lt;/style&amp;gt;
        &amp;lt;title&amp;gt;Canvas Experiment&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;div id="canvas"&amp;gt;
            &amp;lt;!-- This container will hold our canvas element. --&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;button type="button"&amp;gt;Save Image&amp;lt;/button&amp;gt;
        &amp;lt;div id="saved"&amp;gt;
            &amp;lt;!-- We will output our images here so we know they got saved. --&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;!-- &lt;a href="http://cakejs.googlecode.com/svn/trunk/src/cake.js"&gt;download and host on your own site&lt;/a&gt; --&amp;gt;
        &amp;lt;script type="text/javascript" src="cake.js"&amp;gt;&amp;lt;/script&amp;gt;

        &amp;lt;!-- main.js includes all of the javascript in this blog post --&amp;gt;
        &amp;lt;script type="text/javascript" src="main.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;pre&gt;
    // you can use your favorite library's CSS getStyle method here...
    function getStyle(element, property) {
        if (typeof element == 'string')
            element = document.getElementById(element);
        
        if (element.style[property]) {
            // inline style property
            return element.style[property];
        } else if (element.currentStyle) {
            // external stylesheet for Explorer
            return element.currentStyle[property];
        } else if (document.defaultView &amp;&amp; document.defaultView.getComputedStyle) {
            // external stylesheet for Mozilla and Safari 1.3+
            property = property.replace(/([A-Z])/g, "-$1");
            property = property.toLowerCase();
            return document.defaultView.getComputedStyle(element, "").getPropertyValue(property);
        } else {
            return null;
        }
    }
    
    // we'll be calling this later to erase drawn images
    function resetCanvas() {
        canvas.clear = true;
        setTimeout(function() {
            canvas.clear = false;
        }, 100);
    }

&lt;/pre&gt;
&lt;h2 id="creating-the-canvas-and-scene"&gt;Creating the canvas and scene&lt;/h2&gt;
&lt;pre&gt;
    // get the container's dimensions and create a new Cake.js Canvas
    var w = parseInt(getStyle('canvas', 'width'), 10);
    var h = parseInt(getStyle('canvas', 'height'), 10);
    var canvas = new Canvas(document.getElementById('canvas'), w, h);
    
    // setup our canvas
    canvas.frameDuration = 10;
    canvas.addEventListener('mousedown', function(e) {
        e.preventDefault();
    }, false);
    resetCanvas();
    
    /**
     * A CanvasNode can be used to group other nodes, but essentially
     * it is a drawing building block. In this case we will only need
     * one node.
     */
    var scene = new CanvasNode();
&lt;/pre&gt;
&lt;h2 id="creating-the-brush"&gt;Creating the brush&lt;/h2&gt;
&lt;pre&gt;
    // Polygon is another Cake.js class
    var brush = new Polygon([], {
        closePath : false
    });
    
    // configure the brush size and color
    brush.strokeWidth = 2;
    brush.stroke = '#f60';
    
    // Frame frequency is based on frameDuration (which we set above)
    brush.addFrameListener(function() {
        var moves = this.root.mouseEvents.allWith(2).take(0, 1).flatten()
        if (moves.length &gt; 0 || this.root.mouseDown) {
            
            // this block draws a series of line segments following the mouse's path
            this.segments = (this.segments.length) ? this.segments.slice(-2).concat(moves) : moves;
            if (this.segments.length == 2) {
                this.segments = this.segments.concat(this.segments);
            }
            this.visible = (this.segments.length &gt; 0)

        } else {
            if (this.segments.length &gt; 0) {
                this.segments = [];
            }
            this.strokeStarted = false;
            this.visible = false;
        }
    });

    // append elements
    scene.append(brush);
    canvas.append(scene);

&lt;/pre&gt;
&lt;h2 id="serializing-the-canvas-image"&gt;Serializing the canvas image&lt;/h2&gt;
&lt;pre&gt;
    var button = document.getElementsByTagName('button')[0];
    button.addEventListener('click', function() {
        var data = canvas.canvas.toDataURL('image/png').split(/,/)[1];
        var query = 'data=' + encodeURIComponent(data);
        
        // you can use your favorite library's AJAX request method 
        var http = new XMLHttpRequest();
        http.open('POST', 'saveImage.php', true);
        http.onreadystatechange = function() {
            if (http.readyState === 4) { // XMLHttpRequest code for "Done"
                if (http.status === 200) { // HTTP status code for "OK"
                    // see below for what saveImage.php outputs
                    var img = document.createElement('img');
                    img.src = http.responseText;
                    
                    // append the new image
                    var saved = document.getElementById('saved');
                    saved.insertBefore(img, saved.childNodes[0]);
                    
                    // clear the drawing area
                    resetCanvas();
                }
            }
        };
        http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        http.setRequestHeader("Content-length", query.length);
        http.setRequestHeader("Connection", "close");
        http.send(query);
    }, false);
&lt;/pre&gt;
&lt;h2 id="storing-the-image-with-php"&gt;Storing the image with PHP&lt;/h2&gt;
&lt;pre&gt;
&amp;lt;?php
    $filename = md5($_POST['data']) . '.png';
    $data = base64_decode($_POST['data']);
    file_put_contents("./saved/$filename", $data);
    echo "./saved/$filename";
    exit;
?&amp;gt;
&lt;/pre&gt;
&lt;h2 id="code-all-together"&gt;"Code all together"&lt;/h2&gt;
&lt;p&gt;
    In case you're wondering, this section title is paraphrased from
    a &lt;a href="http://en.wikipedia.org/wiki/Mitch_Hedberg"&gt;Mitch Hedberg&lt;/a&gt;
    joke from the comedy album &lt;a href="http://en.wikipedia.org/wiki/Strategic_Grill_Locations"&gt;Strategic Grill Locations&lt;/a&gt;.
&lt;/p&gt;
&lt;pre&gt;
(function() {
    function getStyle(element, property) {
        if (typeof element == 'string')
            element = document.getElementById(element);
        if (element.style[property]) {
            return element.style[property];
        } else if (element.currentStyle) {
            return element.currentStyle[property];
        } else if (document.defaultView &amp;&amp; document.defaultView.getComputedStyle) {
            property = property.replace(/([A-Z])/g, "-$1");
            property = property.toLowerCase();
            return document.defaultView.getComputedStyle(element, "").getPropertyValue(property);
        } else {
            return null;
        }
    }
    function resetCanvas() {
        canvas.clear = true;
        setTimeout(function() {
            canvas.clear = false;
        }, 100);
    }
    var w = parseInt(getStyle('canvas', 'width'), 10);
    var h = parseInt(getStyle('canvas', 'height'), 10);
    var canvas = new Canvas(document.getElementById('canvas'), w, h);
    canvas.frameDuration = 10;
    canvas.addEventListener('mousedown', function(e) {
        e.preventDefault();
    }, false);
    resetCanvas();
    var scene = new CanvasNode();
    var brush = new Polygon([], {
        closePath : false
    });
    brush.strokeWidth = 2;
    brush.stroke = '#f60';
    brush.addFrameListener(function() {
        var moves = this.root.mouseEvents.allWith(2).take(0, 1).flatten()
        if (moves.length &gt; 0 || this.root.mouseDown) {
            this.segments = (this.segments.length) ? this.segments.slice(-2).concat(moves) : moves;
            if (this.segments.length == 2) {
                this.segments = this.segments.concat(this.segments);
            }
            this.visible = (this.segments.length &gt; 0)
        } else {
            if (this.segments.length &gt; 0) {
                this.segments = [];
            }
            this.strokeStarted = false;
            this.visible = false;
        }
    });
    scene.append(brush);
    canvas.append(scene);
    var button = document.getElementsByTagName('button')[0];
    button.addEventListener('click', function() {
        var data = canvas.canvas.toDataURL('image/png').split(/,/)[1];
        var query = 'data=' + encodeURIComponent(data);
        var http = new XMLHttpRequest();
        http.open('POST', 'saveImage.php', true);
        http.onreadystatechange = function() {
            if (http.readyState === 4) {
                if (http.status === 200) {
                    var img = document.createElement('img');
                    img.src = http.responseText;
                    var saved = document.getElementById('saved');
                    saved.insertBefore(img, saved.childNodes[0]);
                    resetCanvas();
                }
            }
        };
        http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        http.setRequestHeader("Content-length", query.length);
        http.setRequestHeader("Connection", "close");
        http.send(query);
    }, false);
})();
&lt;/pre&gt;
&lt;p&gt;
    If you copy the HTML, JavaScript, and PHP to your server,
    you should have a fully working image saver. Let me know if you
    have any trouble getting this set up and I'll see what I can do.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=qBguei1wj5A:-ulZ41N4YTc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qBguei1wj5A:-ulZ41N4YTc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=qBguei1wj5A:-ulZ41N4YTc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qBguei1wj5A:-ulZ41N4YTc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=qBguei1wj5A:-ulZ41N4YTc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qBguei1wj5A:-ulZ41N4YTc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qBguei1wj5A:-ulZ41N4YTc:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=qBguei1wj5A:-ulZ41N4YTc:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/qBguei1wj5A" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/08/31/saving-drawn-images-with-canvas-and-cake.js</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/08/24/introduction-to-amfphp-web-services</guid>
                <title>Introduction to AMFPHP Web Services</title>
                <link>http://feedproxy.google.com/~r/callee/~3/iv3lkhgUgvs/introduction-to-amfphp-web-services</link>
                <pubDate>Mon, 24 Aug 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    &lt;a&gt;(skip installation process)&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    I had my first run-in with AMFPHP this past week, and thought I'd
    share what I learned. AMFPHP is a technology that allows ActionScript
    (the programming language behind Flash and Flex applications) to talk
    to PHP web services. Most of the tutorials I found were for older
    versions of AMFPHP (current as of this post is &lt;a href="http://sourceforge.net/projects/amfphp/files/"&gt;AMFPHP 1.9 beta 20080120&lt;/a&gt;),
    and unfortunately there has not been enough backwards compatibility
    to get much use out of those old tutorials.
&lt;/p&gt;
&lt;p&gt;
    NOTE: This tutorial covers the back-end process only, future 
    tutorials will cover writing front-end applications that
    connect to these types of services.
&lt;/p&gt;
&lt;h2&gt;Getting Started: Downloading AMFPHP&lt;/h2&gt;
&lt;p&gt;
    Easy enough. Visit &lt;a href="http://sourceforge.net/projects/amfphp/files/"&gt;AMFPHP on sourceforge&lt;/a&gt;,
    click the download link under "Newest Files", and save to disk.
&lt;/p&gt;
&lt;h2&gt;Getting Started: Installing AMFPHP&lt;/h2&gt;
&lt;p&gt;
    Step 2 is about as easy as step 1. When you 
    &lt;a href="http://rarlab.com/download.htm"&gt;unzip the file you downloaded&lt;/a&gt;,
    you will see a folder called "amfphp". Copy that folder as-is to 
    your PHP-enabled web host.
&lt;/p&gt;
&lt;p&gt;
    To verify everything's fine so far, you'll want to check &lt;code&gt;gateway.php&lt;/code&gt;
    in the folder you just copied. If you moved everything to &lt;code&gt;http://localhost/amfphp/&lt;/code&gt;
    then open a browser and check &lt;code&gt;http://localhost/amfphp/gateway.php&lt;/code&gt; &amp;mdash;
    you should see something like the following screenshot:
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/amfphp/amfphp-gateway.png"&gt;&lt;img src="/img/blog/amfphp/amfphp-gateway.png" alt="AMFPHP Default Gateway"&gt;&lt;/a&gt;
    &lt;span&gt;AMFPHP Default Gateway&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
    If you click the link labeled "Load the service browser", you will 
    see a page that lists all running AMFPHP web services. Right now,
    that only includes the default service which actually makes this
    page work: &lt;strong&gt;DiscoveryService&lt;/strong&gt;.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/amfphp/amfphp-service-browser.png"&gt;&lt;img src="/img/blog/amfphp/amfphp-service-browser.png" alt="AMFPHP Service Browser"&gt;&lt;/a&gt;
    &lt;span&gt;AMFPHP Service Browser&lt;/span&gt;
&lt;/div&gt;
&lt;h2 id="wheat"&gt;Writing Our First Web Service&lt;/h2&gt;
&lt;p&gt;
    Under the &lt;code&gt;/amfphp/services/&lt;/code&gt; folder on your web server,
    create a new folder called &lt;code&gt;EchoService&lt;/code&gt;, then create a file 
    called &lt;code&gt;EchoService.php&lt;/code&gt; &amp;mdash; our service is simply going
    to spit back any string we send it.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/amfphp/echo-directory-structure.png"&gt;&lt;img src="/img/blog/amfphp/echo-directory-structure.png" alt="Directory structure for the Echo AMFPHP web service."&gt;&lt;/a&gt;
    &lt;span&gt;Directory structure for the Echo AMFPHP web service.&lt;/span&gt;
&lt;/div&gt;
&lt;pre&gt;
&amp;lt;?php
    // EchoService.php example 1
    class EchoService {
        
        public function __construct() {
            // nothing yet, see the next example for more...
        }
        
        public function doEcho($strInput = '') {
            return "You said: $strInput";
        }
        
    }
?&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
    If we refresh our Service Browser, we now see EchoService listed.
    Go ahead and click EchoService, and you should see the name of the
    public function we just created, &lt;code&gt;doEcho&lt;/code&gt;. Enter any
    text in the input field next to &lt;code&gt;strInput&lt;/code&gt;, then press
    the Call button. This actually sends a call from the Service Browser
    to our new service, and displays the results. If everything is
    running smoothly, you should see &lt;em&gt;"You said: ..."&lt;/em&gt; in the Results
    pane, along with whatever you sent as input.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/amfphp/testing-echo-web-service.gif"&gt;&lt;img src="/img/blog/amfphp/testing-echo-web-service.gif" alt="Testing our new AMFPHP echo web service."&gt;&lt;/a&gt;
    &lt;span&gt;Testing our new AMFPHP echo web service.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Adding Functionality&lt;/h2&gt;
&lt;pre&gt;
&amp;lt;?php
    // EchoService, example 2
    
    session_start(); // allows us to persist data across multiple calls
    
    class EchoService {
        
        var $samplePrivateVariable;
        
        public function __construct() {
            /* Any initialization can be done here, such as setting up 
             * database connections or initializing private variables.
             */
            $this-&gt;samplePrivateVariable = 'unused private variable, just an example';
        }
        
        public function doEcho($strInput = '') {
            return "You said: $strInput"; // same as example 1
        }
        
        /**
         * Every time this function is called, it silently stores the input.
         */
        public function addData($strInput) {
            if (!empty($strInput)) {
                $_SESSION['data'][] = $strInput;
            }
            return "Stored $strInput";
        }
        
        /**
         * This method echoes ALL stored input, then clears the buffer.
         */
        public function flushData() {
            $data = $_SESSION['data'];
            $_SESSION['data'] = array();
            return $data;
        }
        
    }
?&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
    Give it a spin in the Service Browser (don't forget to refresh
    first!). You'll also notice that the comments immediately before
    our new public methods are automatically parsed as descriptions
    in the Service Browser. It's a good habit to document your code
    for yourself as well as other developers.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/amfphp/echo-sessions.gif"&gt;&lt;img src="/img/blog/amfphp/echo-sessions.gif" alt="Storing data across calls using PHP sessions."&gt;&lt;/a&gt;
    &lt;span&gt;Storing data across calls using PHP sessions.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
    AMFPHP is pretty straightforward, especially for those with any
    Object-Oriented Programming experience in PHP. Please leave
    a comment to let me know if this helped, or if there's anything
    more you'd like to see about AMFPHP.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=iv3lkhgUgvs:qpiq3dRn6m0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=iv3lkhgUgvs:qpiq3dRn6m0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=iv3lkhgUgvs:qpiq3dRn6m0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=iv3lkhgUgvs:qpiq3dRn6m0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=iv3lkhgUgvs:qpiq3dRn6m0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=iv3lkhgUgvs:qpiq3dRn6m0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=iv3lkhgUgvs:qpiq3dRn6m0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=iv3lkhgUgvs:qpiq3dRn6m0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/iv3lkhgUgvs" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[php]]></category>
		<category><![CDATA[flash and actionscript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/08/24/introduction-to-amfphp-web-services</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/08/19/webcomic-wednesday-basic-truths</guid>
                <title>Webcomic Wednesday: Basic Truths</title>
                <link>http://feedproxy.google.com/~r/callee/~3/Bfn2jjrXPT8/webcomic-wednesday-basic-truths</link>
                <pubDate>Wed, 19 Aug 2009 04:00:00 +0000</pubDate>
                <description>&lt;div&gt;
    &lt;a href="http://www.kopozky.net/basic-truths/"&gt;&lt;img src="/img/blog/webcomics/basic_truths.png" alt="HTML5 is SO much better than XHTML..."&gt;&lt;/a&gt;
    &lt;span&gt;Sponsored in part by recent posts elseblog that spurred no small amount of nerd rage. This week's webcomic is from &lt;a href="http://www.kopozky.net/"&gt;Kopozky Web Solutions&lt;/span&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=Bfn2jjrXPT8:jh66hwmoglE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Bfn2jjrXPT8:jh66hwmoglE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Bfn2jjrXPT8:jh66hwmoglE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Bfn2jjrXPT8:jh66hwmoglE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Bfn2jjrXPT8:jh66hwmoglE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Bfn2jjrXPT8:jh66hwmoglE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Bfn2jjrXPT8:jh66hwmoglE:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Bfn2jjrXPT8:jh66hwmoglE:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/Bfn2jjrXPT8" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[webcomic wednesday]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/08/19/webcomic-wednesday-basic-truths</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/07/21/virtual-desktops-with-virtuawin</guid>
                <title>Virtual Desktops with VirtuaWin</title>
                <link>http://feedproxy.google.com/~r/callee/~3/JkLu1FGcjPM/virtual-desktops-with-virtuawin</link>
                <pubDate>Tue, 21 Jul 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    Just wanted to share &lt;a href="http://virtuawin.sourceforge.net/"&gt;VirtuaWin&lt;/a&gt;,
    a virtual desktop manager I came across recently. Microsoft has a 
    "PowerToy" that allows for desktop switching, but I found it just
    got in the way more often than not, and often applications would 
    ignore the PowerToy and sit on every desktop at the same time.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="http://virtuawin.sourceforge.net/icon_sets.php"&gt;&lt;img src="/img/blog/virtuawin/virtuawin-tray-icon.png"&gt;&lt;/a&gt;
    &lt;span&gt;VirtuaWin lets you &lt;a href="http://virtuawin.sourceforge.net/icon_sets.php"&gt;swap its tray icon image&lt;/a&gt; to suit your taste.&lt;/span&gt;
&lt;/div&gt;&lt;p&gt;
    VirtuaWin not only stays out of the way, but it's customizable;
    you can assign hotkeys to easily switch windows, send programs
    to other windows, even automatically switch to the destination
    window when moving an app (or not, if you prefer).
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="http://virtuawin.sourceforge.net/modules.php"&gt;&lt;img src="/img/blog/virtuawin/third-party-modules.png"&gt;&lt;/a&gt;
    &lt;span&gt;Developers can add functionality by writing their own plug-in modules.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
    A lot of this functionality is native to *nix platforms, but
    it's nice to be able to do the same on Windows. Post a comment
    if you've used this or another virtual desktop manager, or just 
    &lt;a href="http://virtuawin.sourceforge.net/downloads.php"&gt;download VirtuaWin&lt;/a&gt;.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=JkLu1FGcjPM:595ymRzD9CI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=JkLu1FGcjPM:595ymRzD9CI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=JkLu1FGcjPM:595ymRzD9CI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=JkLu1FGcjPM:595ymRzD9CI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=JkLu1FGcjPM:595ymRzD9CI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=JkLu1FGcjPM:595ymRzD9CI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=JkLu1FGcjPM:595ymRzD9CI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=JkLu1FGcjPM:595ymRzD9CI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/JkLu1FGcjPM" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[personal]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/07/21/virtual-desktops-with-virtuawin</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/07/14/corrupted-encryptedlocalstore-in-adobe-air</guid>
                <title>Corrupted EncryptedLocalStore in Adobe AIR</title>
                <link>http://feedproxy.google.com/~r/callee/~3/wPIg6azdny0/corrupted-encryptedlocalstore-in-adobe-air</link>
                <pubDate>Tue, 14 Jul 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    &lt;a href="http://www.adobe.com/products/air/"&gt;Adobe AIR&lt;/a&gt; has a
    feature called &lt;a href="http://www.adobe.com/devnet/air/ajax/articles/encrypted_local_store.html"&gt;EncryptedLocalStore&lt;/a&gt;,
    which is where apps can store potentially sensitive data that needs
    to be secure or safeguarded from other applications. Examples of
    things you might want to store there are user passwords, SSL 
    connection data, or even something slightly more mundane such as
    e-mail addresses.
&lt;/p&gt;
&lt;p&gt;
    According to the &lt;a href="http://help.adobe.com/en_US/AIR/1.5/jslr/"&gt;Adobe AIR 1.5 JavaScript Language Reference&lt;/a&gt;,
    accessing an EncryptedLocalStore should only throw an error if you
    don't pass it an item name to look for:
&lt;/p&gt;
&lt;pre&gt;
    var els      = air.EncryptedLocalStore;   // for convenience
    var password = els.getItem('MyPassword'); // returns string (or null)
    var blank    = els.getItem();             // error
&lt;/pre&gt;
&lt;p&gt;
    However, sometimes you may find that calls to &lt;code&gt;EncryptedStore.getItem&lt;/code&gt;
    or &lt;code&gt;EncryptedLocalStore.setItem&lt;/code&gt; are failing, and catching
    the error doesn't produce any useful information. In this case,
    the application's EncryptedLocalStore may have become corrupted.
    After installation, if you change the contents of any installed 
    files, that application's signature will have changed, and it will
    no longer be able to access its EncryptedLocalStore. 
&lt;/p&gt;
&lt;p&gt;
    This is actually a pretty handy security feature, especially since
    HTML AIR apps have their source code available in plain text
    after installation. This safeguards against users or other programs
    changing your code to get sensitive data from your EncryptedLocalStore.
&lt;/p&gt;
&lt;p&gt;
    I just wanted to mention this problem because it is not (yet) a 
    thoroughly documented error situation. Reinstalling the application
    will fix the problem.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=wPIg6azdny0:1MtFFtkQi_I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=wPIg6azdny0:1MtFFtkQi_I:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=wPIg6azdny0:1MtFFtkQi_I:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=wPIg6azdny0:1MtFFtkQi_I:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=wPIg6azdny0:1MtFFtkQi_I:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=wPIg6azdny0:1MtFFtkQi_I:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=wPIg6azdny0:1MtFFtkQi_I:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=wPIg6azdny0:1MtFFtkQi_I:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/wPIg6azdny0" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[adobe air]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/07/14/corrupted-encryptedlocalstore-in-adobe-air</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/07/09/javascript-design-patterns-module-singleton</guid>
                <title>JavaScript Design Patterns: Module, Singleton</title>
                <link>http://feedproxy.google.com/~r/callee/~3/YTauSpA4Jxw/javascript-design-patterns-module-singleton</link>
                <pubDate>Thu, 09 Jul 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a&gt;(jump to the code)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
    You can &lt;a href="http://www.klauskomenda.com/code/javascript-programming-patterns/"&gt;read about the evolution of these patterns at KlausKomenda&lt;/a&gt;, 
    along with an example that solves the same task using a number 
    of different patterns so you can see the progression. The 
    patterns described here are the &lt;strong&gt;Module&lt;/strong&gt; and the 
    &lt;strong&gt;Singleton&lt;/strong&gt;.
&lt;/p&gt;
&lt;p&gt;
    The &lt;strong&gt;Module&lt;/strong&gt; (more specifically, the &lt;a href="http://www.wait-till-i.com/2007/08/22/again-with-the-module-pattern-reveal-something-to-the-world/"&gt;Revealing Module&lt;/a&gt;)
    is a design pattern that provides encapsulation for data members 
    and/or functions. &lt;strong&gt;Why do we care?&lt;/strong&gt; Here are some benefits:
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Encapsulation avoids naming conflicts with other code on the page.&lt;/li&gt;
    &lt;li&gt;Encapsulated data can only be accessed via public methods (if provided), which lets the module perform any necessary validation before changing data. This is not "hack proof", nor does it intend to be. It simply means that private data is scoped to the module itself, and does not exist in the "public" world. See the following code for examples.&lt;/li&gt;
    &lt;li&gt;Encourages coding behavior that produces &lt;a href="/2009/05/18/javascript-design-patterns--mediator/"&gt;high cohesion and loose coupling&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Improves code readibility and reuse. You can often copy well-written modules to other projects without changing a line of code.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id="wheat"&gt;
    var RevealingModule = function() {
        
        // private variables and functions go here
        var myPrivateVar = 'test';
        function getVar() {
            return myPrivateVar;
        }
        function setVar(newVar) {
            myPrivateVar = newVar;
        }

        return {
            // public methods are revealed here
            getVar: getVar,
            setVar: setVar
        };
         
    };
    
    // each instance gets its own copies of private data
    var modFirst = new RevealingModule();
    var modSecond = new RevealingModule();
    
    // changing one instance does not affect other instances
    modSecond.setVar('new value');
    alert(modSecond.getVar()); // 'new value'
    alert(modFirst.getVar());  // 'test'
&lt;/pre&gt;
&lt;p&gt;
    A &lt;strong&gt;Singleton&lt;/strong&gt; is an object that only has 
    &lt;strong&gt;one instance&lt;/strong&gt;. The easy way to accomplish this
    in JavaScript is to start with the Module pattern, then immediately
    instantiate it by using inline execution.
&lt;/p&gt;
&lt;pre&gt;
    // Dude looks like a Module...
    var Dude = function() {
        // private members and functions
    	var name = "";
    	var age = 0;
    	function modifyAge(years) {
    		age = age + years;
    	}
    	
    	// these methods will be revealed below
    	function getAge() { return age; }
    	function getName() { return name; }
    	function setAge(newAge) { age = newAge; }
    	function setName(newName) { name = newName; }
    	
    	return {
    		getName: getName,
    		setName: setName,
    		getAge: getAge,
    		setAge: setAge,
    		
    		// public names do NOT have to match private names
    		changeName: setName,
    		
    		// you can also return additional public methods
    		haveBirthday: function() {
    			modifyAge(1);
    			alert(name + ' is now ' + age + ' years old!');
    		},
    		sayHello: function() {
    			// other public methods can be called using "this"
    			alert(this.getName() + ' says hello!');
    		}
    	}
    	
    }(); // inline execution turns this Module into a Singleton
    
    // cannot instantiate Dudes, there can be only one
    var myDude = new Dude(); // TypeError: Dude is not a constructor
    
    /*
     * We can't make a new instance using "var myDude = new Dude();"
     * because Dude is an Object, not a Function. Instead, access 
     * Dude's public functions directly, like this:
     */
    Dude.setName('John');
    Dude.setAge(36);
    Dude.haveBirthday(); // alerts 'John is now 37 years old!'
    
    // private data will not be overwritten, it is bound immediately at creation
    Dude.name = 'Jim';     // this makes a new, PUBLIC property called "name"
    alert(Dude.getName()); // 'John' (public method accessing private property)
    alert(Dude.name);      // 'Jim' (public property we just created)
    alert(Dude.age);       // 'undefined' (only exists in private scope)
&lt;/pre&gt;
&lt;p&gt;
    Modules and Singletons can be great for code reuse, because they can
    provide a simple public interface which doesn't have to change
    when its inner workings change. Some Singleton examples:
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="http://arguments.callee.info/2009/01/13/synchronous-sqlite-singleton/"&gt;Synchronous SQLite Singleton&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://arguments.callee.info/2009/01/09/adobe-air-1.5-gui-less-application-updates/"&gt;Updating Adobe AIR Applications (with no UI)&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://arguments.callee.info/2008/11/25/seamless-application-updates-with-adobe-air-1.5/"&gt;Updating Adobe AIR Applications (with default UI)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=YTauSpA4Jxw:XbSxMbHeLoU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=YTauSpA4Jxw:XbSxMbHeLoU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=YTauSpA4Jxw:XbSxMbHeLoU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=YTauSpA4Jxw:XbSxMbHeLoU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=YTauSpA4Jxw:XbSxMbHeLoU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=YTauSpA4Jxw:XbSxMbHeLoU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=YTauSpA4Jxw:XbSxMbHeLoU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=YTauSpA4Jxw:XbSxMbHeLoU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/YTauSpA4Jxw" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[design patterns]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/07/09/javascript-design-patterns-module-singleton</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/06/24/webcomic-wednesday-high-five</guid>
                <title>Webcomic Wednesday: High-Five</title>
                <link>http://feedproxy.google.com/~r/callee/~3/6loxQQHVW24/webcomic-wednesday-high-five</link>
                <pubDate>Wed, 24 Jun 2009 04:00:00 +0000</pubDate>
                <description>&lt;div&gt;
    &lt;a href="http://www.bradcolbow.com/archive.php/?p=16"&gt;&lt;img src="/img/blog/webcomics/the_brads_004.jpg" alt="Client: I know this project has a tight time frame and a really low budget, but this is the beginning of a relationship. I have a lot more work for you boys to do in the future. Sweet! More crap projects down the road! Developers: Bankruptcy for the win!"&gt;&lt;/a&gt;
    &lt;span&gt;For my personal safety, consider this one a general observation and not representative of any particular project... Comic courtesy of &lt;a href="http://www.bradcolbow.com/"&gt;Brad Colbow&lt;/a&gt;.&lt;/span&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=6loxQQHVW24:kAUPN6bWiFQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=6loxQQHVW24:kAUPN6bWiFQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=6loxQQHVW24:kAUPN6bWiFQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=6loxQQHVW24:kAUPN6bWiFQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=6loxQQHVW24:kAUPN6bWiFQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=6loxQQHVW24:kAUPN6bWiFQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=6loxQQHVW24:kAUPN6bWiFQ:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=6loxQQHVW24:kAUPN6bWiFQ:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/6loxQQHVW24" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[webcomic wednesday]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/06/24/webcomic-wednesday-high-five</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/06/11/adobe-air-vs-appcelerator-titanium-round-one</guid>
                <title>Adobe AIR vs Appcelerator Titanium: Round One</title>
                <link>http://feedproxy.google.com/~r/callee/~3/ERiq_U1SZY4/adobe-air-vs-appcelerator-titanium-round-one</link>
                <pubDate>Thu, 11 Jun 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;
    There's been a fair amount of recent hype about &lt;a href="http://www.appcelerator.com/products/titanium-desktop/"&gt;Titanium&lt;/a&gt;,
    at least in certain circles. We use &lt;a href="http://www.adobe.com/products/air/"&gt;Adobe AIR&lt;/a&gt;
    on a daily basis at work, so my coworkers and I have been very eager to see how the new &lt;a href="http://arguments.callee.info/2009/06/04/appcelerator-titanium-beta-launch/"&gt;Titanium beta&lt;/a&gt;
    compares, for better or worse. You can download the &lt;a href="http://www.appcelerator.com/products/download-titanium/"&gt;Titanium Desktop developer app here&lt;/a&gt;.
    Since this is still beta software, there are wrinkles the Appcelerator team is ironing out on 
    a daily basis, but what I've seen so far is very promising.
&lt;/p&gt;
&lt;p&gt;&lt;em style="color: #999"&gt;(click screenshots for full-sized images)&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Titanium Developer&lt;/h2&gt;
&lt;p&gt;
    The modestly named Titanium Developer is where you create, launch, and
    package apps. It also ties in the Appcelerator's cloud services so
    you can distribute (and update) your app to Windows, Mac, and Linux 
    platforms, track download statistics, connect to the Titanium web
    community, and more. However, it is &lt;strong&gt;not&lt;/strong&gt; where
    you actually write your code.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/titanium/titanium-edit-project.jpg"&gt;&lt;img src="/img/blog/titanium/th/titanium-edit-project.jpg"&gt;&lt;/a&gt;
    &lt;span&gt;Titanium Developer's Edit Project tab, which generates an application descriptor in a format similar to Adobe AIR.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;
    Once your project is created, you have to hop over to an editor 
    such as Notepad or &lt;a href="http://aptana.com/"&gt;Aptana&lt;/a&gt;,
    then hop back to Titanium Developer to test and package the application. 
    Not so convenient. However, &lt;a href="http://www.arcaner.com/"&gt;Marshall Culpepper&lt;/a&gt;
    has created an &lt;a href="http://community.appcelerator.org/groups/titanium/blog/2008/12/29/titanium-eclipse-plugin"&gt;Eclipse plugin&lt;/a&gt;
    which helps integrate the Titanium SDK with Eclipse-based IDEs.
    According to the comments on that page, it may not play nicely with
    Aptana, so I haven't tried it yet.
&lt;/p&gt;
&lt;h2&gt;Packaging Cross-Browser Applications&lt;/h2&gt;
&lt;p&gt;
    Building your application is easier than in Adobe AIR, as you don't
    have to deal with certificates during development or production.
    More importantly, &lt;strong&gt;no Adobe AIR red dialogs of doom&lt;/strong&gt;.
    In my opinion, this can tip the scales strongly in Titanium's favor
    with regard to audience adoption &amp;mdash; I've said before that only a developer
    would see a bunch of big red warnings and still hit Install; end
    users don't know enough about how their computer works to understand
    that it's OK if your desktop application actually runs like a desktop application.
&lt;/p&gt;
&lt;div&gt;
    &lt;img src="/img/blog/adobe-air-doom.jpg"&gt;
    &lt;span&gt;No bigger screenshot here, sorry. Trust me, this AIR dialog sucks.&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;a href="/img/blog/titanium/cross-browser-package.jpg"&gt;&lt;img src="/img/blog/titanium/th/cross-browser-package.jpg"&gt;&lt;/a&gt;
    &lt;span&gt;Versatile packaging options include whether you prefer to include the Titanium runtime in your installer, whether you want your application to be available to the general public, and whether you want updates to be distributed to existing users.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Life in the Appcelerator Cloud&lt;/h2&gt;
&lt;p&gt;
    Titanium Developer packages and distributes your application in
    &lt;a href="http://cloudstatus.appcelerator.com/"&gt;Appcelerator's cloud&lt;/a&gt;.
    &lt;a href="http://en.wikipedia.org/wiki/Cloud_computing"&gt;Cloud computing&lt;/a&gt;
    is becoming more and more prevalent in the modern software development
    life cycle, and it's nice to have this functionality built in.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/titanium/appcelerator-cloud.jpg"&gt;&lt;img src="/img/blog/titanium/th/appcelerator-cloud.jpg"&gt;&lt;/a&gt;
    &lt;span&gt;According to the Quotas tab, resources are "free during beta". No word quite yet on what the limits or rates will be once Titanium is out of beta, but presumably you will still be able to package and distribute applications on your own via the command line interface.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;It Takes a Village&lt;/h2&gt;
&lt;p&gt;
    Built-in community features abound in Titanium Developer. You can
    read what people are saying via Twitter and FriendFeed right in the
    Developer application, and there's also an integrated IRC client
    where you can talk to the Appcelerator team and other Titanium
    developers to get help or just chat.
&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/titanium/twitter-feed.jpg"&gt;&lt;img src="/img/blog/titanium/th/twitter-feed.jpg"&gt;&lt;/a&gt;
    &lt;span&gt;Integrated Twitter and FriendFeed results talking about Titanium.&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;a href="/img/blog/titanium/irc-client.jpg"&gt;&lt;img src="/img/blog/titanium/th/irc-client.jpg"&gt;&lt;/a&gt;
    &lt;span&gt;Integrated IRC client to get realtime answers.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;So How Does Titanium Stack Up?&lt;/h2&gt;
&lt;p&gt;
    Since Titanium is barely into its beta phase, there's still work to be
    done before a fair and complete comparison can be made. That said, here 
    are some things I really like about Titanium:
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;
        &lt;strong&gt;No Sandbox&lt;/strong&gt; &amp;mdash;
        Your desktop app is now a &lt;a href="http://www.imdb.com/title/tt0032910/"&gt;real boy&lt;/a&gt;.
        No untrusted content, no sandbox bridges. However, this opens
        Titanium apps up to scrutiny by the programmng public; because
        the source files are freely available on the end user's computer,
        there is a risk that malicious third party applications can edit your source.
        While this is technically true of applications written in ANY language,
        the fact that we're dealing with interpreted (rather than compiled) languages can raise a few eyebrows.
    &lt;/li&gt;
    &lt;li&gt;
        &lt;strong&gt;Multilingual&lt;/strong&gt; &amp;mdash;
        JavaScript works in HTML, no surprise there. However, now you can
        write Python and Ruby code, right in your HTML's &lt;code&gt;script&lt;/code&gt;
        tags. Interaction between JavaScript, Python, and Ruby methods is
        pretty seamless, and extensions to Titanium's own code can be written
        in C++ in addition to the above languages. Future releases will
        include support for PHP, Java, and possibly other languages.
    &lt;/li&gt;
    &lt;li&gt;
        &lt;strong&gt;Run external programs&lt;/strong&gt; &amp;mdash;
        Adobe AIR provides no built-in methods of running a third-party
        application, unless you count opening the system's default browser.
        Titanium provides two: &lt;em&gt;Titanium.Desktop.openApplication(path)&lt;/em&gt;
        and &lt;em&gt;Titanium.Process.launch(command)&lt;/em&gt;. Very cool.
    &lt;/li&gt;
    &lt;li&gt;
        &lt;strong&gt;Titanium.Process.restart()&lt;/strong&gt; &amp;mdash;
        Short and sweet, that's how your program restarts itself. Since
        our deliverables at work typically end up on unattended systems that may
        run (and consume resources) 24/7, we have had to come up with
        strange and creative ways for our applications to restart
        automatically. In Titanium it's a non-issue.
    &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What Do YOU Think?&lt;/h2&gt;
&lt;p&gt;
    Do you use Adobe AIR? Appcelerator Titanium? Both? Please leave
    a comment, let me know what you like or don't like about these
    technologies.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=ERiq_U1SZY4:kReoJesC5ug:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=ERiq_U1SZY4:kReoJesC5ug:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=ERiq_U1SZY4:kReoJesC5ug:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=ERiq_U1SZY4:kReoJesC5ug:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=ERiq_U1SZY4:kReoJesC5ug:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=ERiq_U1SZY4:kReoJesC5ug:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=ERiq_U1SZY4:kReoJesC5ug:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=ERiq_U1SZY4:kReoJesC5ug:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/ERiq_U1SZY4" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[adobe air]]></category>
		<category><![CDATA[review]]></category>
		<category><![CDATA[titanium]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/06/11/adobe-air-vs-appcelerator-titanium-round-one</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/06/04/appcelerator-titanium-beta-launch</guid>
                <title>Appcelerator Titanium Beta Launch</title>
                <link>http://feedproxy.google.com/~r/callee/~3/gIbMJCKKLgU/appcelerator-titanium-beta-launch</link>
                <pubDate>Thu, 04 Jun 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a href="http://titaniumapp.com/"&gt;Titanium&lt;/a&gt;, &lt;a href="http://appcelerator.org/"&gt;Appcelerator's&lt;/a&gt; 
open source contender in the "web app as desktop app" arena, is moving
to &lt;a href="http://bit.ly/5YAv"&gt;beta&lt;/a&gt; on Tuesday, June 9, 2009. To celebrate, they are throwing a 
product launch party at &lt;a href="http://developer.apple.com/WWDC/"&gt;Apple's 
World Wide Developer Conference&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;
    &lt;a href="http://titaniumapp.com/"&gt;&lt;img src="/img/blog/titanium/homepage.jpg"&gt;&lt;/a&gt;
    &lt;span&gt;Get more information about Titanium on their homepage, or&lt;br&gt;&lt;a href="http://twitter.com/appcelerator"&gt;follow @appcelerator on Twitter&lt;/a&gt; for the latest news.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;I wish I could join the Appcelerator team at 
&lt;a href="http://www.jilliansbilliards.com/"&gt;Jillian's&lt;/a&gt; in San 
Francisco (right across from Moscone), but due to a big product 
deadline of my own I won't be able to make it.&lt;/p&gt;
&lt;p&gt;They've also been hinting at a big surprise announcement that will
be made at the party, but haven't offered many clues as to what that
might be... Any guesses?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=gIbMJCKKLgU:nWVtxrjsD4Q:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gIbMJCKKLgU:nWVtxrjsD4Q:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=gIbMJCKKLgU:nWVtxrjsD4Q:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gIbMJCKKLgU:nWVtxrjsD4Q:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=gIbMJCKKLgU:nWVtxrjsD4Q:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gIbMJCKKLgU:nWVtxrjsD4Q:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gIbMJCKKLgU:nWVtxrjsD4Q:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=gIbMJCKKLgU:nWVtxrjsD4Q:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/gIbMJCKKLgU" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[personal]]></category>
		<category><![CDATA[titanium]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/06/04/appcelerator-titanium-beta-launch</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/05/27/pad-strings-in-php-or-javascript</guid>
                <title>Pad Strings in PHP and JavaScript</title>
                <link>http://feedproxy.google.com/~r/callee/~3/08fky3cwack/pad-strings-in-php-or-javascript</link>
                <pubDate>Wed, 27 May 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;"Padding" a string ensures that its length meets or exceeds a desired
minimum. PHP has a built-in function aptly named &lt;code&gt;&lt;a href="http://php.net/str_pad"&gt;str_pad&lt;/a&gt;&lt;/code&gt;
to pad strings to the left, to the right, or even on both sides. The
default is to pad to the right with spaces, but you can choose to pad
using any character, or even multiple characters.&lt;/p&gt;
&lt;pre&gt;
    $input = "test";
    
    echo str_pad($input, 8, "x");               // testxxxx
    echo str_pad($input, 8, "x", STR_PAD_LEFT); // xxxxtest
    echo str_pad(23, 3, "0", STR_PAD_LEFT);     // 023, useful for aligning numbers
    echo str_pad(1000, 4);                      // 1000, no change
&lt;/pre&gt;
&lt;p&gt;JavaScript has no built-in function to do the same, but here's an
extension to the &lt;code&gt;String&lt;/code&gt; object that works the same way:&lt;/p&gt;
&lt;pre&gt;
    // arguments are "length", "character", and "direction"
    String.prototype.pad = String.prototype.pad || function(len, chr, dir) {
        var str = this;
        len = (typeof len == 'number') ? len : 0;
        chr = (typeof chr == 'string') ? chr : ' ';
        dir = (/left|right|both/i).test(dir) ? dir : 'right';
        var repeat = function(c, l) { // inner "character" and "length"
            var repeat = '';
            while (repeat.length &lt; l) {
                repeat += c;
            }
            return repeat.substr(0, l);
        }
        var diff = len - str.length;
        if (diff &gt; 0) {
            switch (dir) {
                case 'left':
                    str = '' + repeat(chr, diff) + str;
                    break;
                case 'both':
                    var half = repeat(chr, Math.ceil(diff / 2));
                    str = (half + str + half).substr(1, len);
                    break;
                default: // and "right"
                    str = '' + str + repeat(chr, diff);
            }
        }
        return str;
    };

    var input = 'Alien';
    alert(input.pad(10));               // "Alien     "
    alert(input.pad(10, '-=', 'left')); // "-=-=-Alien"
    alert(input.pad(10, '_', 'both'));  // "__Alien___"
    alert(input.pad(6, '___'));         // "Alien_"
&lt;/pre&gt;
&lt;p&gt;My solution was adapted from &lt;a href="http://phpjs.org/functions/str_pad:525"&gt;PHP.js&lt;/a&gt;.
Their port has an unused variable and does not produce the same output as native PHP 
&lt;code&gt;str_pad&lt;/code&gt; for all of the examples in 
&lt;a href="http://php.net/str_pad"&gt;PHP's own documentation&lt;/a&gt;, 
but the above code should.&lt;/p&gt;
&lt;p&gt;It can be dangerous to extend native objects in JavaScript, because
changes to the language itself can produce unexpected results &amp;mdash;
If a newer version of JavaScript includes a native &lt;code&gt;String.pad&lt;/code&gt;,
you will suddenly be overriding that native function! If you prefer,
you can avoid extending native &lt;code&gt;String&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;
    function str_pad(str, chr, len, dir) {
        str = str || '';
        /* the rest of the function body is the same */
    }
    alert(str_pad('Alien', 10, '-=', 'left')); // "-=-=-Alien"
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=08fky3cwack:JccP9_vt8to:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=08fky3cwack:JccP9_vt8to:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=08fky3cwack:JccP9_vt8to:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=08fky3cwack:JccP9_vt8to:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=08fky3cwack:JccP9_vt8to:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=08fky3cwack:JccP9_vt8to:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=08fky3cwack:JccP9_vt8to:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=08fky3cwack:JccP9_vt8to:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/08fky3cwack" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/05/27/pad-strings-in-php-or-javascript</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/05/18/javascript-design-patterns--mediator</guid>
                <title>JavaScript Design Patterns: Mediator</title>
                <link>http://feedproxy.google.com/~r/callee/~3/49ifCLWL2mI/javascript-design-patterns--mediator</link>
                <pubDate>Mon, 18 May 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a&gt;(skip to the code example)&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;The Mediator&lt;/b&gt; Design Pattern&lt;/h2&gt;
&lt;p&gt;The Mediator&lt;/b&gt; is a &lt;dfn title="Behavioral design patterns identify 
communication methods between software components."&gt;behavioral design 
pattern in which objects, instead of communicating directly with
each other, communicate only through a central message passing object 
(the Mediator&lt;/b&gt;). The Mediator&lt;/b&gt; pattern facilitates both loose couple and 
high cohesion.&lt;/p&gt;

&lt;h2&gt;Loose Coupling&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Coupling_(computer_science)"&gt;Coupling 
measures the degree to which program modules rely on other modules&lt;/a&gt;.
&lt;strong&gt;Loose coupling&lt;/strong&gt; implies each component can operate or
be tested independently of other components. &lt;strong&gt;Tight coupling&lt;/strong&gt; 
implies each component "knows" the details or inner workings of other 
components. The Mediator&lt;/b&gt; almost entirely eliminates coupling between
participating objects by becoming the only outside point of contact.
All a module needs to know is how to broadcast messages through the
Mediator&lt;/b&gt; &amp;mdash; it doesn't matter whether 0, 1, or even 100 other 
modules act on those messages.&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;What's good about loose coupling?&lt;/strong&gt; 
        Loose coupling facilitates rapid prototyping by getting rid
        of code dependencies. Components can broadcast and/or listen
        for messages without worrying about the rest of the system. 
        Messages can be completely ignored, or handled by any number
        of other components at once. You can add and remove listening
        objects without changing anything about how those messages 
        are broadcast. It is typically much easier to add new 
        features to software when its components are loosely coupled.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;What's bad about loose coupling?&lt;/strong&gt; By inserting
        the Mediator&lt;/b&gt; between objects, components always communicate
        indirectly, which may cause a very slight 
        performance hit. Also, due to the very definition of
        loose coupling, there's no way to tell how the system might
        react to a message by only looking at the point of broadcast.
        For some, this may take a shift in thinking. This is actually
        good, though &amp;mdash; If components call each other directly, 
        for example using &lt;code&gt;myObject.someFunction(args)&lt;/code&gt;,
        then one change in how your program works may cause a ripple
        effect of changes through several modules. TL;DR:

        &lt;strong&gt;tight coupling causes headaches&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;High Cohesion&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Cohesion_(computer_science)"&gt;Cohesion 
is a measure of how focused a piece of code is&lt;/a&gt;. &lt;strong&gt;High cohesion&lt;/strong&gt;
implies a component's properties and methods are strongly related, and
work toward one or more closely related tasks. &lt;strong&gt;Low cohesion&lt;/strong&gt;
implies a component may handle two or more unrelated or dissimilar tasks.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;What's good about high cohesion?&lt;/strong&gt; Lower maintenance 
        cost, whether that means money, time, stress, or some combination thereof.
        Software modifications tend not to affect other parts of the program,
        which means they can be more easily and with more confidence.
        Reading and understanding your code also becomes easier when
        related tasks are grouped. High cohesion also facilitates
        code reuse, which again saves time and money. If you need to
        perform the same task in another application, you know where
        to go for the solution, and you know unrelated or unnecessary
        code won't "tag along".&lt;/li&gt;

    &lt;li&gt;&lt;strong&gt;What's bad about high cohesion?&lt;/strong&gt; I could come
        up with no obvious downsides to high cohesion &amp;mdash; if you
        know any, please &lt;a href="#comments"&gt;leave a comment&lt;/a&gt; for
        the sake of completeness!&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="wheat"&gt;The following example was built on &lt;a href="http://www.fancybread.com/blog/post.cfm/mediator-pattern-applied-to-javascript"&gt;ideas 
presented by Paul Marcotte&lt;/a&gt;. I especially like his comparison between the Mediator&lt;/b&gt; pattern and the &lt;a href="http://en.wikipedia.org/wiki/Observer_pattern"&gt;Observer pattern&lt;/a&gt;: 

&lt;blockquote&gt;"Instead of using the Observer pattern to explicitly set many-to-many listeners and events, Mediator&lt;/b&gt; allows you to broadcast events globally across colleagues." &lt;span&gt;&amp;mdash; Paul Marcotte&lt;/span&gt;&lt;/blockquote&gt;&lt;/p&gt;
&lt;pre&gt;
    Mediator = function() {
        
        var debug = function() {
            // console.log or air.trace as desired
        };
        
        var components = {};
        
        var broadcast = function(event, args, source) {
            var e = event || false;
            var a = args || [];
            if (!e) {
                return;
            }
            //debug(["Mediator received", e, a].join(' '));
            for (var c in components) {
                if (typeof components[c]["on" + e] == "function") {
                    try {
                        //debug("Mediator calling " + e + " on " + c);
                        var s = source || components[c];
                        components[c]["on" + e].apply(s, a);
                    } catch (err) {
                        debug(["Mediator error.", e, a, s, err].join(' '));
                    }
                }
            }
        };
        
        var addComponent = function(name, component, replaceDuplicate) {
            if (name in components) {
                if (replaceDuplicate) {
                    removeComponent(name);
                } else {
                    throw new Error('Mediator name conflict: ' + name);
                }
            }
            components[name] = component;
        };
        
        var removeComponent = function(name) {
            if (name in components) {
                delete components[name];
            }
        };
        
        var getComponent = function(name) {
            return components[name] || false;
        };
        
        var contains = function(name) {
            return (name in components);
        };
        
        return {
            name      : "Mediator",
            broadcast : broadcast,
            add       : addComponent,
            rem       : removeComponent,
            get       : getComponent,
            has       : contains
        };
    }();
&lt;/pre&gt;
&lt;p&gt;And here's how to use it:&lt;/p&gt;
&lt;pre&gt;
    Mediator&lt;/b&gt;.add('TestObject', function() {
        
        var someNumber = 0; // sample variable
        var someString = 'another sample variable';
        
        return {
            onInitialize: function() {
                // this.name is automatically assigned by the Mediator&lt;/b&gt;
                alert(this.name + " initialized.");
            },
            onFakeEvent: function() {
                someNumber++;
                alert("Handled " + someNumber + " times!");
            },
            onSetString: function(str) {
                someString = str;
                alert('Assigned ' + someString);
            }
        }
    }());
    Mediator.broadcast("Initialize");                 // alerts "TestObject initialized"
    Mediator.broadcast('FakeEvent');                  // alerts "Handled 1 times!" (I know, bad grammar)
    Mediator.broadcast('SetString', ['test string']); // alerts "Assigned test string"
    Mediator.broadcast('FakeEvent');                  // alerts "Handled 2 times!"
    Mediator.broadcast('SessionStart');               // this call is safely ignored
    Mediator.broadcast('Translate', ['this is also safely ignored']);
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=49ifCLWL2mI:L1TPBCfTjRY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=49ifCLWL2mI:L1TPBCfTjRY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=49ifCLWL2mI:L1TPBCfTjRY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=49ifCLWL2mI:L1TPBCfTjRY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=49ifCLWL2mI:L1TPBCfTjRY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=49ifCLWL2mI:L1TPBCfTjRY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=49ifCLWL2mI:L1TPBCfTjRY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=49ifCLWL2mI:L1TPBCfTjRY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/49ifCLWL2mI" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[design patterns]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/05/18/javascript-design-patterns--mediator</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/04/30/exceptional-performance-yslow-update</guid>
                <title>Exceptional Performance: YSlow Update</title>
                <link>http://feedproxy.google.com/~r/callee/~3/_crb1OVgAeY/exceptional-performance-yslow-update</link>
                <pubDate>Thu, 30 Apr 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt;, &lt;a href="http://developer.yahoo.com/"&gt;Yahoo&lt;/a&gt;'s developer plugin for Firefox, just &lt;a href="http://developer.yahoo.net/blog/archives/2009/04/yslow_update.html"&gt;autoupdated yesterday&lt;/a&gt;, and what a massive overhaul it was! 
I've been playing around with it and love the new interface and features. Here's a quick walkthrough of the new
interface.&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/img/blog/yslow/full/yslow-grade.png"&gt;&lt;img src="/img/blog/yslow/yslow-grade.gif" alt=""&gt;&lt;/a&gt;
    &lt;span&gt;&lt;p&gt;YSlow's main panel, your website's "Grade". Several rules were added to the new release, including tips
    on improving AJAX call performance, reducing the number of DOM elements to speed up render time, and
    warnings about HTML-scaled images.&lt;/p&gt;&lt;p&gt;The Ruleset dropdown (at the top of the panel) lets you choose
    between the original set of rules, the new and expanded set of rules, or a "small site" ruleset which excludes
    some evaluations such as the CDN requirement, which can drag down otherwise highly performant websites.
    This screenshot was taken on my &lt;a href="/"&gt;homepage&lt;/a&gt;... Looking good at 93%!&lt;/p&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;a href="/img/blog/yslow/full/yslow-components.png"&gt;&lt;img src="/img/blog/yslow/yslow-components.gif" alt=""&gt;&lt;/a&gt;
    &lt;span&gt;&lt;p&gt;YSlow's components panel, very informative about the pieces that fit together to build your website,
    grouped by component type (document, script, stylesheet, inline vs CSS image, AJAX document, or redirect).
    &lt;/p&gt;&lt;p&gt;You can examine the gzipped and uncompressed sizes of appropriate files, whether or not they used cookies, even
    examine the headers sent when the browser made its request. Red highlights any of your images which were not found 
    (HTTP 404) or have missing &lt;a href="http://developer.yahoo.net/blog/archives/2007/07/high_performanc_11.html"&gt;ETags or Expires headers&lt;/a&gt;.&lt;/p&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;a href="/img/blog/yslow/full/yslow-statistics.png"&gt;&lt;img src="/img/blog/yslow/yslow-statistics.png" alt=""&gt;&lt;/a&gt;
    &lt;span&gt;&lt;p&gt;YSlow's Statistics panel with its pretty pie charts. Pretty similar to &lt;a href="http://arguments.callee.info/2008/11/13/exceptional-performance-image-optimization/"&gt;YSlow v1's pie charts&lt;/a&gt;,
    just a bit reorganized. High performance websites have small numbers on the "primed cache" graph. To improve your
    statistics here, &lt;a href="http://arguments.callee.info/2008/11/17/htaccess-cache-rules-everything-around-me/"&gt;set your cache headers using .htaccess or PHP&lt;/a&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;a href="/img/blog/yslow/full/yslow-integrated-tools.png"&gt;&lt;img src="/img/blog/yslow/yslow-integrated-tools.png" alt=""&gt;&lt;/a&gt;
    &lt;span&gt;&lt;p&gt;The big new toy for this release is YSlow's Tools panel. Here you can &lt;a href="http://jslint.com/"&gt;validate the current page's JavaScript&lt;/a&gt;,
    show only JavaScript or CSS in easy-to-read formats, &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;minify JavaScript&lt;/a&gt;, run &lt;a href="http://smush.it/"&gt;Smush.it&lt;/a&gt;
    on all of the current page's images, or get a for-print version of YSlow's various panels. Very cool.&lt;/p&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;The Verdict?&lt;/h2&gt;
&lt;p&gt;Overall I think this is an excellent update to an already excellent web development tool. The new customization
options will eliminate the need to "hack" the plugin via &lt;code&gt;about:config&lt;/code&gt;, and the integrated tools panel
will improve both speed to production and code review. Thanks Yahoo!&lt;/p&gt;
&lt;h2&gt;How to Start Using YSlow&lt;/h2&gt;
&lt;ol&gt;
    &lt;li&gt;Install &lt;a href="http://getfirefox.com/"&gt;Firefox&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;Install &lt;a href="http://getfirebug.com/"&gt;Firebug&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;Install &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/5369"&gt;YSlow&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=_crb1OVgAeY:KMknKuP8PGw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=_crb1OVgAeY:KMknKuP8PGw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=_crb1OVgAeY:KMknKuP8PGw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=_crb1OVgAeY:KMknKuP8PGw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=_crb1OVgAeY:KMknKuP8PGw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=_crb1OVgAeY:KMknKuP8PGw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=_crb1OVgAeY:KMknKuP8PGw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=_crb1OVgAeY:KMknKuP8PGw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/_crb1OVgAeY" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[performance]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/04/30/exceptional-performance-yslow-update</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/04/24/scan-web-design-principles-n-is-for-navigation</guid>
                <title>SCAN Web Design Principles: N is for Navigation</title>
                <link>http://feedproxy.google.com/~r/callee/~3/_DH0wYmlwck/scan-web-design-principles-n-is-for-navigation</link>
                <pubDate>Fri, 24 Apr 2009 04:00:00 +0000</pubDate>
                <description>&lt;h2&gt;What is Website Navigation?&lt;/h2&gt;
&lt;p&gt;Website navigation answers these questions for your visitors:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Where am I?&lt;/strong&gt; Offering feedback on the current location helps visitors feel confident they're on the right page. Everyone runs into misleading links out there &amp;mdash; people want to know that the link they clicked leads to information they really need.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Where else can I go?&lt;/strong&gt; &lt;a href="http://www.usability.gov/pdfs/chapter7.pdf"&gt;Usability.gov&lt;/a&gt; suggests that we "&lt;q&gt;do not create or direct users into pages that have no navigational options.&lt;/q&gt;" Don't trap your visitors, on purpose or inadvertently.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;What else can I do?&lt;/strong&gt; Even basic websites are actually applications. Navigation includes messages that guide your visitors through any tasks they need or want to accomplish on your site. Friendly and useful error messages, obvious sign-up / log-in / log-out links, clearly labeled forms, prominent "buy now" buttons, etc.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;How do I get back?&lt;/strong&gt; "Undo" features also inspire confidence. If visitors feel they can easily navigate backward, they're more likely to move forward.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Consistency is Key&lt;/h2&gt;
&lt;p&gt;No matter what your design, navigation is easy to use when it's consistent. Put navigation elements in the same place on each page whenever possible. Sometimes you'll want to repeat some of your navigation, maybe summarized further down on the page. If you do this, make sure the links appear in the same order in both locations, whatever that order may be.&lt;/p&gt;
&lt;div&gt;
    &lt;a href="http://www.adobe.com/"&gt;&lt;img src="/img/blog/scan/navigation-order.png" alt="Screenshot of navigation on Adobe's website."&gt;&lt;/a&gt;
    &lt;span&gt;&lt;a href="http://www.adobe.com/"&gt;Adobe&lt;/a&gt; repeats their menu's product list further down the page. The bottom list summarizes the more common products, but both lists are in the same overall order (alphabetical).&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;A Picture is Worth a Thousand Words&lt;/h2&gt;
&lt;p&gt;If there's one lesson I've learned over and over from interface design, it's this: &lt;strong&gt;people don't read&lt;/strong&gt;. 
If possible and appropriate for your site, give your main navigation icons that describe their links. Add meaningful 
pictures that actually indicate what clicking them will do (and make them clickable!).&lt;/p&gt;
&lt;p&gt;Use appropriate &lt;a href="http://www.cs.tut.fi/~jkorpela/html/alt.html"&gt;&lt;code&gt;alt&lt;/code&gt; attributes&lt;/a&gt; for 
accessibility &amp;mdash; if your image has a caption, don't repeat the whole caption in the &lt;code&gt;alt&lt;/code&gt; attribute 
or screen readers will say the same thing twice. Conversely, the &lt;a href="http://webdesign.about.com/od/htmltags/a/aa101005.htm"&gt;&lt;code&gt;title&lt;/code&gt; attribute&lt;/a&gt; 
aids usability by showing a tooltip when a visitor hovers over the image. For clickable images, this can tell your
visitor what will happen when they click the image.&lt;/p&gt;
&lt;p&gt;There's a subtle but important difference between &lt;code&gt;alt&lt;/code&gt; and &lt;code&gt;title&lt;/code&gt;. &lt;code&gt;Alt&lt;/code&gt; attributes
should provide useful text when an image &lt;strong&gt;is not visible&lt;/strong&gt;, whether that means the visitor is blind,
the image file is missing, images are turned off in the browser, or the visitor's connection is unbearably slow.
&lt;code&gt;Title&lt;/code&gt; attributes should provide useful text when an image &lt;strong&gt;IS visible&lt;/strong&gt; &amp;mdash; don't bother
describing obvious images, but do provide additional information that you don't want or need to include in the
body text.&lt;/p&gt;
&lt;div&gt;
    &lt;a href="http://twitter.com/"&gt;&lt;img src="/img/blog/scan/img-title-tooltip.png" alt="Tooltip on Twitter's logo"&gt;&lt;/a&gt;
    &lt;span&gt;&lt;a href="http://twitter.com/"&gt;Twitter&lt;/a&gt;'s logo has a navigational tooltip, courtsey of the &lt;code&gt;title&lt;/code&gt; attribute.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Sitemaps&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.google.com/webmasters/tools/docs/en/protocol.html"&gt;Sitemaps&lt;/a&gt; can be both the most and 
least important navigational tool. Search engines do their best to spider your entire site, but a quality XML sitemap 
can ensure no page falls through the cracks. "Real" visitors may or may not use sitemaps, depending on your site's 
target audience, amount of information, and actual content. If you offer tons of products, services, galleries, 
or other collections, visitors may find HTML sitemaps useful in finding what they need efficiently and effectively.&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/sitemap/"&gt;&lt;img src="/img/blog/scan/sitemap.png" alt="Sitemap for this AJAX blog."&gt;&lt;/a&gt;
    &lt;span&gt;The &lt;a href="http://arguments.callee.info/sitemap/"&gt;arguments.callee sitemap&lt;/a&gt; is dynamically generated with PHP, so any new posts are immediately visible to visitors (and Google's robot army).&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;More in my SCAN web design series:&lt;/h2&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;a href="/2009/03/28/scan-web-design-principles/"&gt;SCAN Introduction&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/03/scan-web-design-principles-s-is-for-scanning/"&gt;S is for SCANNING&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/10/scan-web-design-principles-c-is-for-content/"&gt;C is for CONTENT&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/17/scan-web-design-principles-a-is-for-accessibility/"&gt;A is for ACCESSIBILITY&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/24/scan-web-design-principles-n-is-for-navigation/"&gt;N is for NAVIGATION&lt;/a&gt; (current page)&lt;/li&gt;
&lt;/ol&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=_DH0wYmlwck:1eS7sG3w8NU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=_DH0wYmlwck:1eS7sG3w8NU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=_DH0wYmlwck:1eS7sG3w8NU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=_DH0wYmlwck:1eS7sG3w8NU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=_DH0wYmlwck:1eS7sG3w8NU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=_DH0wYmlwck:1eS7sG3w8NU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=_DH0wYmlwck:1eS7sG3w8NU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=_DH0wYmlwck:1eS7sG3w8NU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/_DH0wYmlwck" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[user experience]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/04/24/scan-web-design-principles-n-is-for-navigation</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/04/17/scan-web-design-principles-a-is-for-accessibility</guid>
                <title>SCAN Web Design Principles: A is for Accessibility</title>
                <link>http://feedproxy.google.com/~r/callee/~3/1D00jV_OeyA/scan-web-design-principles-a-is-for-accessibility</link>
                <pubDate>Fri, 17 Apr 2009 04:00:00 +0000</pubDate>
                <description>&lt;h2&gt;What is Accessibility?&lt;/h2&gt;
&lt;p&gt;
    &lt;strong&gt;Website Accessibility&lt;/strong&gt;
    means making sure the widest possible audience can reach your content. An 
    important distinction lies between making content &lt;strong&gt;accessible&lt;/strong&gt;
    and making it &lt;strong&gt;identical&lt;/strong&gt;
    &amp;mdash; you &lt;strong&gt;do not&lt;/strong&gt;
    need to make sure every single visitor receives the exact same user experience 
    when visiting your site.
&lt;/p&gt;
&lt;h2&gt;Why Design for Accessibility?&lt;/h2&gt;
&lt;p&gt;
    Accessibility is not just for handicapped visitors; millions of people surf the web from work, school, public libraries, 
    or other public locations. Many of these locations have restrictive firewalls. You cannot know what a particular establishment  
    may or may not allow &amp;mdash; some corporate visitors might not only have JavaScript turned off for them, but they may also be 
    unable to change that setting themselves.
&lt;/p&gt;
&lt;p&gt;
    Another reason to make your site accessible is the concept of &lt;strong&gt;everything as a platform&lt;!-- coined 2008 by HB Stone --&gt;&lt;/strong&gt;. 
    Websites aren't just for browsers anymore; people may be browsing your site with their TV remote, phone, or &lt;a href="http://www.wiibrowser.com/"&gt;Nintendo Wii&lt;/a&gt;. 
    Others might be viewing it on screens embedded in their car, their refrigerator, or just about anything else. 
    Why deny yourself those hits?
&lt;/p&gt;
&lt;div&gt;
    &lt;img src="/img/blog/scan/flat-panel-fridge.jpg" alt="Samsung's flat panel fridge, with TV and Web connectivity"&gt;&lt;span&gt;Samsung's flat panel fridge, with TV and Web connectivity.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Graceful Degradation and Progressive Enhancement&lt;/h2&gt;
&lt;p&gt;
    &lt;strong&gt;Progressive Enhancement&lt;/strong&gt;
    is a design strategy based on the following &lt;a href="http://en.wikipedia.org/wiki/Progressive_enhancement"&gt;core principles&lt;/a&gt;
    (paraphrased):
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;
        basic content should be accessible to ALL platforms
    &lt;/li&gt;
    &lt;li&gt;
        additional content can be offered to compatible platforms
    &lt;/li&gt;
    &lt;li&gt;
        HTML markup should be &lt;a href="http://www.thefutureoftheweb.com/blog/writing-semantic-html/"&gt;sparse and semantic&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
        CSS styles should be &lt;a href="http://www.tizag.com/cssT/external.php"&gt;externally linked&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
        scripted behavior should be &lt;a href="http://icant.co.uk/articles/seven-rules-of-unobtrusive-javascript/"&gt;unobtrusive&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
        scripted behavior should be &lt;a href="http://www.tizag.com/javascriptT/javascriptexternal.php"&gt;externally linked&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
        visitors' browser preferences are respected (window size, font size, etc.)
    &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
    How can you meet these relatively simple goals? By building your website in &lt;strong&gt;iterations&lt;/strong&gt;, or cycles.&lt;strong&gt;Iterative design&lt;/strong&gt;
    meets the smallest possible set of requirements first, then the next smallest set,
    and so on, until you have a complete product. Designing a website in iterations involves the following steps:
&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;strong&gt;CONTENT:&lt;/strong&gt;
        Write your text, and mark it up in meaningful HTML tags that indicate what the content is, rather than how it should be displayed. Use H tags for headers, UL/OL/DL tags for lists, P tags for paragraphs.
    &lt;/li&gt;
    &lt;li&gt;
        &lt;strong&gt;PRESENTATION:&lt;/strong&gt;
        Add layout and positioning via externally linked CSS without changing your markup, other than adding IDs or CLASSes to help target elements. Don't forget to use &lt;a href="http://www.websiteoptimization.com/speed/tweak/descendant/"&gt;descendent selectors&lt;/a&gt;
        to avoid cluttering your markup with too many IDs and CLASSes.
    &lt;/li&gt;
    &lt;li&gt;
        &lt;strong&gt;BEHAVIOR:&lt;/strong&gt;
        Use JavaScript or another scripting language to add interactivity to your site. Animations, slideshows, AJAX updates and scrolling feeds.
    &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
    &lt;strong&gt;Graceful Degradation&lt;/strong&gt;, or "fault tolerance", means that your website can easily deal with browsers 
    that may not support one or more feature. It could be argued that graceful degradation is a byproduct of progressive
    enhancement &amp;mdash; if you use the iterative process described earlier, then visitors without JavaScript enabled will 
    still see your properly styled content. If particular browsers doesn't implement CSS properly (or at all), or 
    images are disabled, or your visitors are 
    
        blind
    
    or deaf, they can still access all of your content, and your semantic HTML means they can still understand the 
    relationships between different parts of your page.
&lt;/p&gt;
&lt;h2&gt;Cross-Browser UI Testing&lt;/h2&gt;
&lt;p&gt;
    &lt;a href="http://browsershots.org/"&gt;Browsershots&lt;/a&gt;
    is an excellent service that lets you see your website through 
    the eyes of dozens of browsers across all major operating systems. No emulation is taking place &amp;mdash; you get real
    screenshots from real computers. The one thing you can't completely check with Browsershots is behavior, because you 
    only get static screenshots. However, there are for-pay tools such as &lt;a href="http://www.crossbrowsertesting.com/"&gt;CrossBrowserTesting&lt;/a&gt;
    that give you full interactive access to test systems.
&lt;/p&gt;
&lt;p&gt;
    When doing your own testing, try to include the following settings to make sure your actual content is accessible.
    They're in order of most to least common &amp;mdash; many of your visitors may have JavaScript turned off, but few typically
    have CSS or images turned off.
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;
        &lt;strong&gt;Turn JavaScript off.&lt;/strong&gt;
        Does your navigation still work? Do your forms still work? If parts of 
        your page absolutely MUST use JavaScript in order to function, &lt;strong&gt;add them to the page with JavaScript&lt;/strong&gt;. 
        Otherwise, visitors will assume your site is broken (and they'll be right).
    &lt;/li&gt;
    &lt;li&gt;
        &lt;strong&gt;Turn CSS off.&lt;/strong&gt;
        Not all browsers have this option. The easy workaround is to edit 
        your source code and comment out the 
        &lt;code&gt;
            &amp;lt;link&amp;gt;
        &lt;/code&gt;
        tag that includes your external CSS 
        file. This is an excellent test to see if your HTML is semantic. Without CSS, how does your content look? 
        Examine font size, order of elements, proximity between content and the headers that describe that content.
    &lt;/li&gt;
    &lt;li&gt;
        &lt;strong&gt;Turn images off.&lt;/strong&gt;
        Again, not all browsers have an easy toggle for this. A shortcut is to 
        temporarily rename your actual images folder, that way you don't have to change any of your source code. Without
        images, is your content still clear? Can you still see and use some form of navigation? If you can't, neither can 
        your visitors. Don't assume this affects only blind people &amp;mdash; some phones turn images off by default to speed 
        up load time and minimize data transfer.
    &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Assist Disabled Visitors&lt;/h2&gt;
&lt;p&gt;
    Even though accessibility isn't ALL about disabled visitors, obviously they comprise an important audience for the 
    discussion. The US Department of Health &amp;amp; Human Services provides an excellent usability guide featuring a whole chapter on &lt;a href="http://www.usability.gov/pdfs/chapter3.pdf"&gt;accessibility and disabled visitors&lt;/a&gt;.
&lt;/p&gt;
&lt;h2&gt;More in my SCAN web design series:&lt;/h2&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;a href="/2009/03/28/scan-web-design-principles/"&gt;SCAN Introduction&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;a href="/2009/04/03/scan-web-design-principles-s-is-for-scanning/"&gt;S is for SCANNING&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;a href="/2009/04/10/scan-web-design-principles-c-is-for-content/"&gt;C is for CONTENT&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;a href="/2009/04/17/scan-web-design-principles-a-is-for-accessibility/"&gt;A is for ACCESSIBILITY&lt;/a&gt;
        (current page)
    &lt;/li&gt;
    &lt;li&gt;
        &lt;a href="/2009/04/24/scan-web-design-principles-n-is-for-navigation/"&gt;N is for NAVIGATION&lt;/a&gt;
    &lt;/li&gt;
&lt;/ol&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=1D00jV_OeyA:coZJyqz4RVg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=1D00jV_OeyA:coZJyqz4RVg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=1D00jV_OeyA:coZJyqz4RVg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=1D00jV_OeyA:coZJyqz4RVg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=1D00jV_OeyA:coZJyqz4RVg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=1D00jV_OeyA:coZJyqz4RVg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=1D00jV_OeyA:coZJyqz4RVg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=1D00jV_OeyA:coZJyqz4RVg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/1D00jV_OeyA" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[user experience]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/04/17/scan-web-design-principles-a-is-for-accessibility</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/04/15/click-and-drag-to-select-checkboxes</guid>
                <title>Click and Drag to Toggle Checkboxes</title>
                <link>http://feedproxy.google.com/~r/callee/~3/HXG9POk6kOE/click-and-drag-to-select-checkboxes</link>
                <pubDate>Wed, 15 Apr 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a&gt;(skip to the example)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A coworker showed me a demo for &lt;a href="http://cross-browser.com/x/examples/clickndrag_checkboxes.php"&gt;dragging 
across checkboxes to check or uncheck them&lt;/a&gt;, and asked that I implement it in my own
&lt;a href="http://jelo.callee.info/"&gt;AJAX library&lt;/a&gt;. The cross-browser demo he came across answered a specific 
question which required the use of &lt;code&gt;table&lt;/code&gt;s and &lt;code&gt;td&lt;/code&gt;s, but I wanted to be able to accomplish 
the same feat without depending on unrelated tags.&lt;/p&gt;
&lt;p&gt;I quickly whipped up &lt;a href="http://jelo.callee.info/docs/symbols/Jelo.html#.dragCheckbox"&gt;a solution&lt;/a&gt; that 
targets the checkboxes and their labels, but the solution was easy enough that I figured I'd get rid of the library
dependencies and share the method in good old fashioned cross-browser JavaScript.&lt;/p&gt;
&lt;pre&gt;
&amp;lt;!-- This HTML is used for the following example --&amp;gt;

&amp;lt;label for="check1"&amp;gt;Check:&amp;lt;/label&amp;gt; &amp;lt;input type="checkbox" id="check1"&amp;gt;

&amp;lt;label for="check2"&amp;gt;Check:&amp;lt;/label&amp;gt; &amp;lt;input type="checkbox" id="check2"&amp;gt;

&amp;lt;label for="check3"&amp;gt;Check:&amp;lt;/label&amp;gt; &amp;lt;input type="checkbox" id="check3"&amp;gt;

&amp;lt;label for="check4"&amp;gt;Check:&amp;lt;/label&amp;gt; &amp;lt;input type="checkbox" id="check4"&amp;gt;

&amp;lt;label for="check5"&amp;gt;Check:&amp;lt;/label&amp;gt; &amp;lt;input type="checkbox" id="check5"&amp;gt;

&amp;lt;label for="check6"&amp;gt;Check:&amp;lt;/label&amp;gt; &amp;lt;input type="checkbox" id="check6"&amp;gt;
&lt;/pre&gt;
&lt;pre&gt;
var dragCheckbox = function(root) {
    root = root || document;
    var dragging = false;
    var current = false;
    document.onmouseup = function() {
        dragging = false;
    };
    var getTarget = function(element) {
        switch (element.tagName.toLowerCase()) {
            case 'input':
                return element;
                break;
            case 'label':
                element.style['MozUserSelect'] = 'none';      // Firefox and other Gecko
                element.style['WebkitUserSelect'] = 'ignore'; // Safari, Chrome, AIR
                element.onselectstart = function() {
                    return false;                             // IE
                };
                var el = element.getAttribute('for') || element.getAttribute('htmlFor');
                return document.getElementById(el);
                break;
            default:
                return null; // invalid element
        }
    };
    var checkboxes = function() {
        var c = [];
        var inputs = root.getElementsByTagName('input');
        for (var i = 0; i &lt; inputs.length; i++) {
            if (inputs[i].getAttribute('type') == 'checkbox') {
                c.push(inputs[i]);
            }
        }
        return c;
    }();
    for (var i = 0; i &lt; checkboxes.length; i++) {
        (function() {
            var down = function() {
                var box = getTarget(this);
                if (box) {
                    dragging = true;
                    box.checked = !box.checked;
                    current = box.checked;
                }
            };
            var over = function() {
                var box = getTarget(this);
                if (box &amp;&amp; dragging) {
                    box.checked = current;
                }
            };
            var click = function() {
                var box = getTarget(this);
                if (box) {
                    box.checked = current;
                }
            };
            this.onmousedown = function() { down.call(this); };
            this.onmouseover = function() { over.call(this); };
            this.onclick = function() { click.call(this); };
            var label = (function() {
                var l = root.getElementsByTagName('label');
                for (var i = 0; i &lt; l.length; i++) {
                    var f = l[i].getAttribute('for') || l[i].getAttribute('htmlFor');
                    if (f == this.id) {
                        return l[i];
                    }
                }
                return l;
            }).call(this);
            if (label) {
                label.onmousedown = function() { down.call(label); };
                label.onmouseover = function() { over.call(label); };
                label.onclick = function() { click.call(label); };
            }
        }).call(checkboxes[i]);
    }
};
&lt;/pre&gt;
&lt;h2&gt;Example&lt;a name="wheat"&gt;&amp;nbsp;&lt;/a&gt;&lt;/h2&gt;
&lt;style type="text/css"&gt;
    .dragCheckbox-example {
        float: left;
        padding: 0 0 0 40px;
    }
    .dragCheckbox-example label {
        clear: left;
        float: left;
    }
    .dragCheckbox-example input {
        float: left;
    }
&lt;/style&gt;
&lt;p&gt;
    &lt;div&gt;
        &lt;strong&gt;WITHOUT dragCheckbox()&lt;/strong&gt;&lt;br&gt;
        &lt;label for="nocheck1"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="nocheck1"&gt;&lt;br&gt;
        &lt;label for="nocheck2"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="nocheck2"&gt;&lt;br&gt;
        &lt;label for="nocheck3"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="nocheck3"&gt;&lt;br&gt;
        &lt;label for="nocheck4"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="nocheck4"&gt;&lt;br&gt;
        &lt;label for="nocheck5"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="nocheck5"&gt;&lt;br&gt;
        &lt;label for="nocheck6"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="nocheck6"&gt;&lt;br&gt;
    &lt;/div&gt;
    &lt;div&gt;
        &lt;strong&gt;WITH dragCheckbox()&lt;/strong&gt;&lt;br&gt;
        &lt;label for="check1"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="check1"&gt;&lt;br&gt;
        &lt;label for="check2"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="check2"&gt;&lt;br&gt;
        &lt;label for="check3"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="check3"&gt;&lt;br&gt;
        &lt;label for="check4"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="check4"&gt;&lt;br&gt;
        &lt;label for="check5"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="check5"&gt;&lt;br&gt;
        &lt;label for="check6"&gt;Check: &lt;/label&gt; &lt;input type="checkbox" id="check6"&gt;&lt;br&gt;
    &lt;/div&gt;
    &lt;script type="text/javascript"&gt;
        window.onload = function() {
            Jelo.dragCheckbox($$('.dragCheckbox-example')[1]);
        };
    &lt;/script&gt;
    &lt;br style="clear: both;"&gt;
&lt;/p&gt;
&lt;p&gt;
    Useful? Not? You decide. 
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=HXG9POk6kOE:vt541juZ_5c:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HXG9POk6kOE:vt541juZ_5c:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HXG9POk6kOE:vt541juZ_5c:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HXG9POk6kOE:vt541juZ_5c:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HXG9POk6kOE:vt541juZ_5c:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HXG9POk6kOE:vt541juZ_5c:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HXG9POk6kOE:vt541juZ_5c:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HXG9POk6kOE:vt541juZ_5c:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/HXG9POk6kOE" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[html]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/04/15/click-and-drag-to-select-checkboxes</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/04/10/scan-web-design-principles-c-is-for-content</guid>
                <title>SCAN Web Design Principles: C is for Content</title>
                <link>http://feedproxy.google.com/~r/callee/~3/M6N_x2GKhRw/scan-web-design-principles-c-is-for-content</link>
                <pubDate>Fri, 10 Apr 2009 04:00:00 +0000</pubDate>
                <description>&lt;h2&gt;Your first impression&lt;/h2&gt;
&lt;p&gt;Your homepage should &lt;a href="http://www.usability.gov/pdfs/guidelines.html"&gt;clearly communicate your site's 
purpose or value&lt;/a&gt;. Provide all important options on the homepage, so your visitors can find what they were looking 
for quickly and easily. With that in mind, try to avoid clutter by presenting unnecessary options, text, or graphics.
If someone gets information overload looking at your homepage, they'll never bother to look deeper into the site.&lt;/p&gt;
&lt;div&gt;
    &lt;img src="/img/blog/scan/clear-homepage.jpg" alt=""&gt;
    &lt;span&gt;&lt;a href="http://www.aptana.com/"&gt;Aptana&lt;/a&gt; and &lt;a href="http://www.pomodorospizza.net/"&gt;Pomodoro's Pizza&lt;/a&gt; sell two very different products, but their homepages are remarkably similar in design. Uncluttered layouts make it obvious what they're offering, how to get it, and where to learn more.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Semantic HTML&lt;/h2&gt;
&lt;p&gt;HTML is considered "semantic" when the markup itself conveys meaning. List items are represented by 
&lt;code&gt;LI&lt;/code&gt; elements, a testimonial or long quote within &lt;code&gt;blockquote&lt;/code&gt; tags, section headers and 
subheaders in &lt;code&gt;h1&lt;/code&gt; through &lt;code&gt;h6&lt;/code&gt; tags, nested appropriately. If you look at the source code of
your page, it should be clear what your content is, and the context in which it sits. I'll get into more reasons for
doing this in the next SCAN post (A is for Accessibility), but a good rule of thumb is to write content and markup
FIRST, without regard to where things will be placed or how they will look. Then style the markup using CSS,
and finally add any additional behaviors using JavaScript or another language.&lt;/p&gt;
&lt;p&gt;Web designer &lt;a href="http://www.joedolson.com/"&gt;Joe Dolson&lt;/a&gt; has a simple and relatively thorough &lt;a href="http://www.joedolson.com/articles/2008/04/guide-to-semantic-html/"&gt;guide to semantic HTML&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Don't fight your visitors&lt;/h2&gt;
&lt;p&gt;Most people expect certain things when browsing the internet. When they hit their browser's Back button, they expect
to go back a "page", whether that means undoing an action on an AJAX app, exiting your site, or retrieving what they
last looked at. If something looks like a button, they expect to be able to click it to make something happen. Fighting
or abusing common expectations in a site's design will confuse and lose people. Catering to these expectations will
make your site pleasant to navigate, and encourage return visits.&lt;/p&gt;
&lt;div&gt;
    &lt;img src="/img/blog/scan/banner-blindness.jpg" alt=""&gt;
    &lt;span&gt;The US Population apparently looks like an ad to people viewing &lt;a href="http://www.census.gov/"&gt;census.gov&lt;/a&gt;, and the result is they just don't see it. This behavior is called &lt;a href="http://www.useit.com/alertbox/fancy-formatting.html"&gt;Banner Blindness&lt;/a&gt;.&lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;How do YOU WANT people to find your site?&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Does your content facilitate this?&lt;/strong&gt; If you don't include a word or phrase on your site, people generally won't be able to find you by looking for that phrase.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Does your content have "repost" or linking value?&lt;/strong&gt; Good content will encourage organic links, which drive targeted traffic to your site and boost search engine placement.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How do YOU EXPECT people to find your site?&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Are these expectations reasonable?&lt;/strong&gt; Searches in most arenas are short and general; assuming people will type in 20-letter words or 20-word phrases to find your site will lead to a lonely website in the vast majority of cases.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Does your content facilitate this?&lt;/strong&gt; If you don't include a word or phrase on your page that people actually look for, people will probably not be able to find it with a search engine. Use &lt;a href="http://www.google.com/support/insights/bin/answer.py?answer=96693"&gt;Google Insights&lt;/a&gt; and other &lt;a href="http://www.webmaster-toolkit.com/keyword-analysis-tool.shtml"&gt;keyword analysis tools&lt;/a&gt; to help make sure your content relates to what your target audience is looking for.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How do people REALLY FIND your site?&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Tracking takes time.&lt;/strong&gt; Don't make decisions based on a couple days of analytics. Don't expect to see immediate results in your analytics when you DO make a change. Let people get accustomed to the change, and track behavior over time.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Modify DESIGN and CONTENT based on your results.&lt;/strong&gt; If people aren't sticking around, take another look at your design. Are you cluttering up the field of vision? Are your graphics fighting your visitors' expectations of how websites are "supposed" to work? Also reconsider your content &amp;mdash; check for misspellings and typos, missing words (or paragraphs!), incorrect or uncommon phrases.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Make changes GRADUALLY.&lt;/strong&gt; Revamping a whole site at once might cause you to lose returning visitors before you can even tell if new visitors like the changes. Big changes can temporarily hurt SERPs too, depending on the circumstances. If you do need to introduce a completely new layout or format, warn your visitors ahead of time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;More in my SCAN web design series:&lt;/h2&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;a href="/2009/03/28/scan-web-design-principles/"&gt;SCAN Introduction&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/03/scan-web-design-principles-s-is-for-scanning/"&gt;S is for SCANNING&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/10/scan-web-design-principles-c-is-for-content/"&gt;C is for CONTENT&lt;/a&gt; (current page)&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/17/scan-web-design-principles-a-is-for-accessibility/"&gt;A is for ACCESSIBILITY&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/24/scan-web-design-principles-n-is-for-navigation/"&gt;N is for NAVIGATION&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=M6N_x2GKhRw:1O0qRP0-934:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=M6N_x2GKhRw:1O0qRP0-934:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=M6N_x2GKhRw:1O0qRP0-934:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=M6N_x2GKhRw:1O0qRP0-934:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=M6N_x2GKhRw:1O0qRP0-934:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=M6N_x2GKhRw:1O0qRP0-934:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=M6N_x2GKhRw:1O0qRP0-934:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=M6N_x2GKhRw:1O0qRP0-934:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/M6N_x2GKhRw" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[user experience]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/04/10/scan-web-design-principles-c-is-for-content</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/04/03/scan-web-design-principles-s-is-for-scanning</guid>
                <title>SCAN Web Design Principles: S is for Scanning</title>
                <link>http://feedproxy.google.com/~r/callee/~3/gPwmj86dHBY/scan-web-design-principles-s-is-for-scanning</link>
                <pubDate>Fri, 03 Apr 2009 04:00:00 +0000</pubDate>
                <description>&lt;h2&gt;Bounce Rate&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://www.google.com/support/analytics/bin/answer.py?hl=en&amp;answer=81986"&gt;Bounce rate&lt;/a&gt; is a statistic
available on most web analytics applications. It tells you what percentage of visitors arrived and left your website
after seeing only a single page. What it really tells you is that your website is not compelling enough to even click
&lt;strong&gt;one&lt;/strong&gt; link on the page. Why not? Find out by scanning your website with your eyes.&lt;/p&gt;
&lt;h2&gt;What your visitors see: The F Pattern (or E Pattern)&lt;/h2&gt;
&lt;p&gt;As you build your website, scan it the way your visitors will; in a general F-shaped or E-shaped pattern. That is,
in broad horizontal strokes.  The important part of this exercise is to &lt;strong&gt;not read your text&lt;/strong&gt;. Don't
look at any one part of the page for more than a split second. In fact, relax your gaze and look "past" the page as 
with a &lt;a href="http://www.vision3d.com/3views.html"&gt;Magic Eye image&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;
    &lt;img src="/img/blog/scan/heatmap-f-pattern.png" alt="A hypothetical heatmap tracking visitors' eyes across a webpage."&gt;
    &lt;span&gt;A hypothetical &lt;a href="http://www.useit.com/alertbox/reading_pattern.html"&gt;heatmap&lt;/a&gt; tracking visitors' eyes across a webpage.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;After your scan, consider the following questions:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;What's the FIRST thing I see? &lt;/strong&gt; Is that what I want people to see first? What made my eyes land there?&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Does anything DEMAND ATTENTION?&lt;/strong&gt; If so, should it? If not, should &lt;em&gt;something&lt;/em&gt;?&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Does everything have an OBVIOUS PURPOSE?&lt;/strong&gt; Show someone new your design and watch how they use it. Do they get stuck anywhere?&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Am I just used to my own design?&lt;/strong&gt; Do something else for a while, then come back later (or tomorrow) and scan again. You may come up with completely different results!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Making sites easy to scan&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Provide meaningful section headers.&lt;/strong&gt; This helps visitors zero in on what they want to actually read.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Group related items.&lt;/strong&gt; Don't confuse people by making them hunt and find the next step, next link, or next chunk of content.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;The truth, the whole truth, and nothing but the truth.&lt;/strong&gt; Tell your visitors everything necessary to understand that page, but no more. Don't make them remember things from other pages, don't leave "extra parts" around that will make them wonder why they're there.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Avoid jargon and acronyms whenever possible.&lt;/strong&gt; If you do use them, use tags such as &lt;code&gt;&amp;lt;dfn title="Tooltip description"&amp;gt;dfn&amp;lt;/dfn&amp;gt;&lt;/code&gt; to define them.
    &lt;li&gt;&lt;strong&gt;Keep the most important content above the fold.&lt;/strong&gt; If a page is extremely long, consider adding a &lt;a href="http://www.davesite.com/webstation/html/chap09.shtml"&gt;table of contents&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
    &lt;a href="http://www.wildflavors.com/"&gt;&lt;img src="/img/blog/scan/contrasting-colors.jpg" alt="An example of using contrasting colors to guide the eye."&gt;&lt;/a&gt;
    &lt;span&gt;&lt;a href="http://colorschemedesigner.com/"&gt;Strongly contrasting colors&lt;/a&gt; help guide the eye and tell visitors what is and isn't related on your page. Items with the same color background appear to be related, like the navigation in this example.&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;a href="http://www.centresource.com/"&gt;&lt;img src="/img/blog/scan/different-font-sizes.jpg" alt="An example of using different font sizes to guide the eye."&gt;&lt;/a&gt;
    &lt;span&gt;Differing font sizes can also guide the eye. Big fonts typically indicate section headers (in this example, "latest blog posts" and "discuss your project"), but they can also push keywords that you want visitors to remember about your website or brand.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Do you get anything useful from scanning your own websites? Why or why not? What about heatmap software &amp;mdash; 
leave a comment to let others know what you like and don't like about a particular heatmap solution. Or, mention
other ways you've made sites easy to absorb.&lt;/p&gt;
&lt;h2&gt;More in my SCAN web design series:&lt;/h2&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;a href="/2009/03/28/scan-web-design-principles/"&gt;SCAN Introduction&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/03/scan-web-design-principles-s-is-for-scanning/"&gt;S is for SCANNING&lt;/a&gt; (current page)&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/10/scan-web-design-principles-c-is-for-content/"&gt;C is for CONTENT&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/17/scan-web-design-principles-a-is-for-accessibility/"&gt;A is for ACCESSIBILITY&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/2009/04/24/scan-web-design-principles-n-is-for-navigation/"&gt;N is for NAVIGATION&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=gPwmj86dHBY:i7Jg8Tg402s:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gPwmj86dHBY:i7Jg8Tg402s:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=gPwmj86dHBY:i7Jg8Tg402s:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gPwmj86dHBY:i7Jg8Tg402s:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=gPwmj86dHBY:i7Jg8Tg402s:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gPwmj86dHBY:i7Jg8Tg402s:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gPwmj86dHBY:i7Jg8Tg402s:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=gPwmj86dHBY:i7Jg8Tg402s:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/gPwmj86dHBY" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[user experience]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/04/03/scan-web-design-principles-s-is-for-scanning</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/03/28/scan-web-design-principles</guid>
                <title>SCAN Web Design Principles</title>
                <link>http://feedproxy.google.com/~r/callee/~3/07qjMVw183U/scan-web-design-principles</link>
                <pubDate>Sat, 28 Mar 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;April's bringing a four-post series about interface design (primarily for websites). There's a mental checklist I run
through when building a new site, and while formalizing the idea the acronym &lt;strong&gt;SCAN&lt;/strong&gt; stuck with me. I 
encourage comments on any of my blog posts, but if you have anything to add to this design series, I truly hope you 
leave a comment. Enjoy!&lt;/p&gt;
&lt;h2&gt;&lt;a href="/2009/04/03/scan-web-design-principles-s-is-for-scanning/"&gt;S is for SCANNING&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Website visitors do not read websites they come across for the first time. They scan quickly, and if they don't see
something they need, they hit Back and move on. &lt;strong&gt;S is for SCANNING&lt;/strong&gt; discusses how visitors scan 
sites, offers questions to consider while scanning your own site, and provides visual examples of websites that 
are easy to scan. Knowing how to scan your own website will help you design in a way that places important 
information where it will be seen and reduce bounce rate.&lt;/p&gt;
&lt;p&gt;Read &lt;a href="/2009/04/03/scan-web-design-principles-s-is-for-scanning/"&gt;S is for Scanning&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;&lt;a href="/2009/04/10/scan-web-design-principles-c-is-for-content/"&gt;C is for CONTENT&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Catching a visitor's eye is only the first step &amp;mdash; you must also present valuable information. Solve your
visitors' problems quickly and thoroughly, fill their needs, give them what they want. Whatever the purpose of your
website, knowing how to create, organize, and present content will increase returning visits.&lt;/p&gt;
&lt;p&gt;Read &lt;a href="/2009/04/10/scan-web-design-principles-c-is-for-content/"&gt;C is for Content&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;&lt;a href="/2009/04/17/scan-web-design-principles-a-is-for-accessibility/"&gt;A is for ACCESSIBILITY&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Accessibility isn't just catering to disabilities. Tons of companies have restrictive firewalls that block most
(or all) JavaScript. Some may block images outside certain domains, others may even block CSS. You can't control
what behaviors are allowed client-side, but you can make sure that all clients can access your valuable content. 
Build a robust site that can stand on its own, even when one or more of its pillars fall.&lt;/p&gt;
&lt;p&gt;Read &lt;a href="/2009/04/17/scan-web-design-principles-a-is-for-accessibility/"&gt;A is for Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href="/2009/04/24/scan-web-design-principles-n-is-for-navigation/"&gt;N is for NAVIGATION&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Navigation is the method you present for finding information in your website. Whether that information is on the
same page or another page, help your visitors find it easily and quickly, or you'll lose those visitors. Don't trap
people in dead-end pages or frustrate them: provide navigation, make it easy to use, and provide feedback that tells
them where they are in your site.&lt;/p&gt;
&lt;p&gt;Read &lt;a href="/2009/04/24/scan-web-design-principles-n-is-for-navigation/"&gt;N is for Navigation&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=07qjMVw183U:kcOfgIyPm14:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=07qjMVw183U:kcOfgIyPm14:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=07qjMVw183U:kcOfgIyPm14:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=07qjMVw183U:kcOfgIyPm14:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=07qjMVw183U:kcOfgIyPm14:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=07qjMVw183U:kcOfgIyPm14:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=07qjMVw183U:kcOfgIyPm14:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=07qjMVw183U:kcOfgIyPm14:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/07qjMVw183U" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[user experience]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/03/28/scan-web-design-principles</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/03/20/permission-denied-to-call-method-location-tostring</guid>
                <title>Permission denied to call method Location.toString</title>
                <link>http://feedproxy.google.com/~r/callee/~3/0Kn67KUCIng/permission-denied-to-call-method-location-tostring</link>
                <pubDate>Fri, 20 Mar 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;For a while I've been noticing seemingly random errors popping up on some of my websites. It hadn't happened on 
my blog here, but other sites caused this error to appear in &lt;a href="http://getfirebug.com/"&gt;Firebug&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;
Permission denied to call method Location.toString
&lt;/pre&gt;
&lt;p&gt;No file name, no line number, no indication where the error occurs or why. Upon further investigation I 
discovered that it only happened when there was &lt;a href="http://adsense.blogspot.com/"&gt;Google Adsense&lt;/a&gt; or 
other remote flash media involved.&lt;/p&gt;
&lt;p&gt;As it turns out, the error is caused by a &lt;a href="http://willperone.net/Code/as3error.php"&gt;change in Flash's 
security policies&lt;/a&gt; between versions 9.0.115.0 and 9.0.124.0 &amp;mdash; this is both good and bad news. I'm glad to 
learn that it's not an error anywhere in my code, but it's unfortunate that there's nothing I can do to get rid 
of the error, short of removing Adsense from those pages.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=0Kn67KUCIng:fON9dWFMa-4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=0Kn67KUCIng:fON9dWFMa-4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=0Kn67KUCIng:fON9dWFMa-4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=0Kn67KUCIng:fON9dWFMa-4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=0Kn67KUCIng:fON9dWFMa-4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=0Kn67KUCIng:fON9dWFMa-4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=0Kn67KUCIng:fON9dWFMa-4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=0Kn67KUCIng:fON9dWFMa-4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/0Kn67KUCIng" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[personal]]></category>
		<category><![CDATA[flash and actionscript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/03/20/permission-denied-to-call-method-location-tostring</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/03/19/adobe-air-calling-javascript-from-a-swf</guid>
                <title>Adobe AIR: Call JavaScript from a SWF (also win money)</title>
                <link>http://feedproxy.google.com/~r/callee/~3/jlvIQUZQmbw/adobe-air-calling-javascript-from-a-swf</link>
                <pubDate>Thu, 19 Mar 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a&gt;(skip to the contest info)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The typical (and fairly safe) way to call a JavaScript function from a SWF is 
&lt;a href="http://www.adobe.com/devnet/flash/articles/external_interface_05.html"&gt;&lt;code&gt;ExternalInterface.call&lt;/code&gt;&lt;/a&gt;. 
However, &lt;a href="http://livedocs.adobe.com/labs/air/1/aslr/flash/external/ExternalInterface.html"&gt;Adobe AIR does 
NOT support &lt;code&gt;ExternalInterface&lt;/code&gt;&lt;/a&gt;, so we have to look for an alternative if we want to interact with 
&lt;a href="http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS4B441C24-BAE3-4110-91FD-A4E5EEFB2467.html"&gt;embeded SWFs 
in HTML AIR apps&lt;/a&gt;. Before &lt;code&gt;ExternalInterface&lt;/code&gt; was introduced to ActionScript, there was 
&lt;code&gt;fscommand&lt;/code&gt;, &lt;code&gt;navigateToURL&lt;/code&gt;, and &lt;code&gt;getURL&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;!-- This goes in your Adobe AIR application --&amp;gt;
&amp;lt;object id="swf" type="application/x-shockwave-flash"&amp;gt;
    &amp;lt;param name="allowScriptAccess" value="sameDomain"&amp;gt;
    &amp;lt;param name="movie" value="myFlashFile.swf"&amp;gt;
&amp;lt;/object&amp;gt;
&amp;lt;script type="text/javascript"&amp;gt;
    // this function must be in "window" scope
    function myFunc(str) {
        air.trace("Flash told me: " + str);
    }
&amp;lt;/script&amp;gt;

&amp;lt;!--
    NOTE: myFlashFile.swf contains this AS2 code...
    getURL('javascript:myFunc("some argument");void(0);');
--&amp;gt;    
&lt;/pre&gt;
&lt;p&gt;Just like typing &lt;code&gt;javascript:&lt;/code&gt; addresses into your browser's address bar, that example allows 
Flash to call a JavaScript 
function. HOWEVER...&lt;/p&gt;
&lt;p&gt;Most likely for security reasons, &lt;strong&gt;&lt;code&gt;getURL&lt;/code&gt; requires user interaction&lt;/strong&gt;. You can call 
&lt;code&gt;getURL&lt;/code&gt; in an &lt;code&gt;on(release)&lt;/code&gt; handler, but if you just want the SWF to call a JavaScript 
function at an arbitrary point in the timeline, there must be user interaction. The user must actually be clicking 
or moving the mouse on top of the SWF at the &lt;strong&gt;exact moment&lt;/strong&gt; &lt;code&gt;getURL&lt;/code&gt; is called, or the 
call will fail silently.&lt;/p&gt;
&lt;p&gt;What does this have to do with &lt;strong&gt;cash prizes&lt;/strong&gt;? Well, at work I'm writing a touchscreen AIR app 
that loads SWF presentations. The user is just supposed to watch the movie, but the movie needs to call a function 
on my HTML page from time to time. Obviously, I can't ask the user to keep tapping the screen or constantly 
move their finger around the screen during the presentation, but I haven't found a clean way to allow Flash to call my 
JavaScript function without user interaction. We figured out a cheesy workaround, but I'm certain there must be 
a "real" way to accomplish this. So here are the requirements:&lt;/p&gt;
&lt;h2&gt;Demo Application and Contest Rules&lt;a name="wheat"&gt;&amp;nbsp;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="/img/blog/swf-to-js/app.rar"&gt;This demo application&lt;/a&gt; reproduces the problem. Every four seconds, the 
included SWF tries to call &lt;code&gt;callMe()&lt;/code&gt;, a JavaScript function the app's HTML page. You can see that
while you move the mouse atop the SWF, the calls succeed, but as soon as you stop, so do the calls.&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Your &lt;strong&gt;SWF must call some JavaScript function&lt;/strong&gt; defined in your AIR app's HTML page. Doesn't matter what it does, I just want to see a working example. Alert something, air.trace something, change colors, write text to a div, whatever you want.&lt;/li&gt;
    &lt;li&gt;The function call must succeed with &lt;strong&gt;NO user interaction&lt;/strong&gt;. No clicks, no mouse movement, no keypress.&lt;/li&gt;
    &lt;li&gt;All &lt;strong&gt;source code must be available&lt;/strong&gt;. For obvious reasons I won't run any submission I can't test by compiling the source.&lt;/li&gt;
	&lt;li&gt;Your SWF can be written in either ActionScript &lt;strong&gt;2 or 3&lt;/strong&gt;, so long as you provide the source.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;No external applications&lt;/strong&gt; (AutoIt, etc.).&lt;/li&gt;
    &lt;li&gt;You can submit your solution in as many parts or files as you need, and any required installation or configuration process is fine as long as you can explain what to do.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I will send (via PayPal or personal check, whichever you prefer) &lt;strong&gt;$50&lt;/strong&gt; of my own hard earned 
cash to the first person to meet &lt;strong&gt;ALL&lt;/strong&gt; of the above requirements. You get the cash whether the 
solution is an easy one-liner or a crazy convoluted workaround. No haggling, please &amp;mdash; we already figured
out a hack for work, so this contest is only for my own satisfaction. And hopefully yours if you like money.&lt;/p&gt;
&lt;p&gt;To submit your solution, please post a link in the comments on this page. You can save the solution as text on &lt;a href="http://pastebin.com/"&gt;PasteBin&lt;/a&gt; if you don't have your own hosting.&lt;/p&gt;
&lt;p&gt;To embed a link in your comment here, use BBCode-style tags. For example, &lt;strong&gt;[url]http://your.web.site/path/file.ext[/url]&lt;/strong&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=jlvIQUZQmbw:V11jDCwYZPg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jlvIQUZQmbw:V11jDCwYZPg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=jlvIQUZQmbw:V11jDCwYZPg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jlvIQUZQmbw:V11jDCwYZPg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=jlvIQUZQmbw:V11jDCwYZPg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jlvIQUZQmbw:V11jDCwYZPg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jlvIQUZQmbw:V11jDCwYZPg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=jlvIQUZQmbw:V11jDCwYZPg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/jlvIQUZQmbw" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[adobe air]]></category>
		<category><![CDATA[flash and actionscript]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[personal]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/03/19/adobe-air-calling-javascript-from-a-swf</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/03/13/google-vs-firebug-console</guid>
                <title>Google vs. Firebug Console</title>
                <link>http://feedproxy.google.com/~r/callee/~3/VbrFUk4C_FU/google-vs-firebug-console</link>
                <pubDate>Fri, 13 Mar 2009 04:00:00 +0000</pubDate>
                <description>&lt;h2&gt;Round one: Gmail&lt;/h2&gt;
&lt;p&gt;It all started with &lt;a href="http://gmail.com/"&gt;GMail&lt;/a&gt;. The scary red bar at the top of the screen that proclaimed
&lt;a href="http://getfirebug.com/"&gt;Firebug's debug console&lt;/a&gt; doesn't play nicely with others. That's fine, they give
you the option to hide the message if you really need the console open, and tell you how to disable it for only GMail's
site if not. No harm, no foul.&lt;/p&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/google-hates-console.png"&gt;
    &lt;span&gt;Aw, GMail. Why so serious?&lt;/span&gt;
&lt;/div&gt;

&lt;h2&gt;Round two: Google APIs&lt;/h2&gt;
&lt;p&gt;As I began to use the various &lt;a href="http://code.google.com/more/"&gt;Google APIs&lt;/a&gt;, I noticed something strange.
While scripts on the page were loading, I could send debug info to the console just fine. However, if I opened Firebug
once the page was done loading, &lt;code&gt;window.console&lt;/code&gt; didn't work as I expected. On a mashup for a client
at work, it appeared &lt;code&gt;window.console&lt;/code&gt; had been overwritten by an &lt;a href="https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Closures"&gt;anonymous closure&lt;/a&gt;.
Take out the Google script, and you get back your console. Huh. Well, "console" is such a generic name, I guess naming
conflicts are to be expected when you apply something like that to the &lt;a href="http://www.softwareishard.com/blog/planet-mozilla/firefox-extensions-global-namespace-pollution/"&gt;global namespace&lt;/a&gt;. No harm, no foul.&lt;/p&gt;

&lt;h2&gt;Round three: Et tu, forward-facing apps?!&lt;/h2&gt;
&lt;p&gt;The &lt;a href="http://code.google.com/apis/maps/"&gt;Google Map API&lt;/a&gt; destroys &lt;code&gt;window.console&lt;/code&gt; during page load. 
Long story short: If you use a Google map on your personal site, you cannot debug it during page load using 
Firebug's JavaScript console. What about the &lt;a href="http://maps.google.com/"&gt;Google maps homepage&lt;/a&gt;? Well, 
apparently one of their own developers fell into this trap:&lt;/p&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/google-eats-console.png"&gt;
    &lt;span&gt;No debug for you, Google dev.&lt;/span&gt;
&lt;/div&gt;

&lt;p&gt;What's Google's problem with the debug console? Don't Google and Mozilla like each other anymore? 
&lt;a href="http://blogs.zdnet.com/Google/?p=1206"&gt;Oh&lt;/a&gt;, 
&lt;a href="http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9044160"&gt;maybe&lt;/a&gt; 
&lt;a href="http://www.businessweek.com/technology/content/mar2009/tc20090311_813488.htm"&gt;not&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=VbrFUk4C_FU:KDSQtCc1mlY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=VbrFUk4C_FU:KDSQtCc1mlY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=VbrFUk4C_FU:KDSQtCc1mlY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=VbrFUk4C_FU:KDSQtCc1mlY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=VbrFUk4C_FU:KDSQtCc1mlY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=VbrFUk4C_FU:KDSQtCc1mlY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=VbrFUk4C_FU:KDSQtCc1mlY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=VbrFUk4C_FU:KDSQtCc1mlY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/VbrFUk4C_FU" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/03/13/google-vs-firebug-console</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/03/09/adobe-air-why-wont-my-swf-load</guid>
                <title>Adobe AIR: Why won't my SWF load?!</title>
                <link>http://feedproxy.google.com/~r/callee/~3/yuuQaCwjm0c/adobe-air-why-wont-my-swf-load</link>
                <pubDate>Mon, 09 Mar 2009 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;If you develop &lt;a href="http://www.adobe.com/products/air/"&gt;Adobe AIR&lt;/a&gt; applications using &lt;a href="http://www.adobe.com/devnet/air/ajax/"&gt;AJAX&lt;/a&gt;,
you may eventually come across a strange bug. Adobe says you can 
&lt;a href="http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS4B441C24-BAE3-4110-91FD-A4E5EEFB2467.html"&gt;embed external 
SWF files in your HTML application&lt;/a&gt;, and from that example code it's simple! So why do SWFs seem
to load fine in some HTML AIR apps, but in others they don't show up at all?&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;!-- 
        Whether the window is transparent. 
        Only applicable when systemChrome is false. 
        Optional. Default false. 
    --&amp;gt;
    &amp;lt;transparent&amp;gt;&lt;span style="color: red;"&gt;true&lt;/span&gt;&amp;lt;/transparent&amp;gt;
&lt;/pre&gt;
&lt;p&gt;There's the culprit. One of the controversially cool features in AIR is that you can 
&lt;a href="http://www.adobe.com/devnet/air/flex/quickstart/creating_transparent_window.html"&gt;abandon system chrome to 
make your application transparent&lt;/a&gt;. Problem is, even in parts of your page that are visible, SWFs do not seem
to appear when your &lt;code&gt;application.xml&lt;/code&gt; file sets transparent to true. Set it to false and everything works again!&lt;/p&gt;
&lt;p&gt;A side effect of this workaround is that if your application has a &lt;a href="http://www.adobe.com/devnet/articles/air_sample_apps.html"&gt;non-rectangular background, or parts of the
application are supposed to be see-through&lt;/a&gt;, your users won't be able to click what's "beneath" the app on their
screen.&lt;/p&gt;
&lt;p&gt;So is this feature tradeoff a bug? I think so, as it seems of all the things that play nicely with Adobe AIR,
Adobe Flash should be one. Note that this problem may not apply to AIR apps created in Flex or Flash.&lt;/p&gt;
&lt;p&gt;This lesson brought to you by one very frustrating work day.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=yuuQaCwjm0c:oXHuT3sbFiw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=yuuQaCwjm0c:oXHuT3sbFiw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=yuuQaCwjm0c:oXHuT3sbFiw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=yuuQaCwjm0c:oXHuT3sbFiw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=yuuQaCwjm0c:oXHuT3sbFiw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=yuuQaCwjm0c:oXHuT3sbFiw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=yuuQaCwjm0c:oXHuT3sbFiw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=yuuQaCwjm0c:oXHuT3sbFiw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/yuuQaCwjm0c" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[adobe air]]></category>
		<category><![CDATA[personal]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/03/09/adobe-air-why-wont-my-swf-load</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/02/10/http-https-and-ssl-via-php</guid>
                <title>HTTP, HTTPS, and SSL via PHP</title>
                <link>http://feedproxy.google.com/~r/callee/~3/4CtizmcEOd0/http-https-and-ssl-via-php</link>
                <pubDate>Tue, 10 Feb 2009 05:00:00 +0000</pubDate>
                <description>&lt;h2&gt;Jump to parts of this post:&lt;/h2&gt;
&lt;ol&gt;
	&lt;li&gt;&lt;a href="#http-sockets"&gt;HTTP with PHP Sockets&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="#raw-ssl-sockets"&gt;Direct SSL Sockets in PHP&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="#https-curl"&gt;HTTPS via cURL&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;See Also:&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://php.net/fsockopen"&gt;&lt;code&gt;fsockopen&lt;/code&gt; on PHP.net&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://php.net/curl"&gt;cURL on PHP.net&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://php.net/curl_setopt"&gt;&lt;code&gt;curl_setopt&lt;/code&gt; on PHP.net&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.litfuel.net/tutorials/sockets/sockets.php"&gt;Writing server sockets to handle these requests&lt;/a&gt; (out of this post's scope)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;HTTP with PHP Sockets&lt;a name="http-sockets"&gt;&amp;nbsp;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;
// don't need to specify http, it's the default protocol
$hostname = "www.google.com";
$port     = 80;

// create and configure the client socket
$fp = fsockopen($hostname, $port); // optional: $error_number, $error_string, $connect_timeout
if ($fp) {
    stream_set_timeout($fp, 30); // seconds to wait for i/o operations
    
    // send request headers
    fwrite($fp, "GET / HTTP/1.1\r\n");
    fwrite($fp, "Host: $hostname\r\n");
    fwrite($fp, $additional_headers); // Accept, User-Agent, Referer, etc.
    fwrite($fp, "Connection: close\r\n");
    
    // read response
	$response = "";
    while (!feof($fp)) {
        $response .= fgets($fp, 128);
    }
	echo $response;
    
    // close the socket
    fclose($fp);
}
&lt;/pre&gt;
&lt;h2&gt;Direct SSL Sockets in PHP&lt;a name="raw-ssl-sockets"&gt;&amp;nbsp;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You don't have to send request headers in the above example. You can actually connect to some servers and send
raw text with &lt;code&gt;fwrite&lt;/code&gt;. Because you have to implement the HTTP/HTTPS protocol yourself if you want it,
sockets are considered "clean" connections.&lt;/p&gt;
&lt;pre&gt;
// note that "ssl" is the protocol, NOT "https"
$hostname  = "ssl://your-secure-site.com";
$port      = 443;

// create and configure the client socket
$fp = fsockopen($hostname, $port);
if ($fp) {
    stream_set_timeout($fp, 30);
    
	// send data (or build HTTPS headers similar to first example)
	fwrite($fp, "your message goes here");
    
    // read response
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    
    // close the socket
    fclose($fp);
}
&lt;/pre&gt;
&lt;h2&gt;HTTPS via cURL&lt;a name="https-curl"&gt;&amp;nbsp;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHP doesn't directly support attaching client certificates to web requests with &lt;code&gt;fsockopen&lt;/code&gt;, so 
you have to use the &lt;code&gt;cURL&lt;/code&gt; library.&lt;/p&gt;
&lt;pre&gt;
$url = "https://your-secure-site.com/secure-service.php";
$port = 443;

// POST data, formatted just like a GET query string
$request = "name=hb&amp;age=27&amp;site=arguments.callee.info";

// the client certificate path MUST be a physical path, not url
$certificate = "C:\\certificates\\test.crt"; // windows example
$certificate = "/etc/pki/tls/private/test.crt"; // unix/linux example
$password = 'mypassword'; // client certificate's key

// more details at http://php.net/curl
$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_PORT, $port);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSLCERT, $certificate);
curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $password);

// fetch response and close the socket
$response = curl_exec($ch);
curl_close($ch);
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=4CtizmcEOd0:Qi2mzcfUsS0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=4CtizmcEOd0:Qi2mzcfUsS0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=4CtizmcEOd0:Qi2mzcfUsS0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=4CtizmcEOd0:Qi2mzcfUsS0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=4CtizmcEOd0:Qi2mzcfUsS0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=4CtizmcEOd0:Qi2mzcfUsS0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=4CtizmcEOd0:Qi2mzcfUsS0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=4CtizmcEOd0:Qi2mzcfUsS0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/4CtizmcEOd0" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/02/10/http-https-and-ssl-via-php</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/01/26/choosing-a-doctype</guid>
                <title>Choosing a Valid DOCTYPE</title>
                <link>http://feedproxy.google.com/~r/callee/~3/SwowoEWwMGQ/choosing-a-doctype</link>
                <pubDate>Mon, 26 Jan 2009 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a&gt;(skip to the doctypes)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When building a website, it's important to &lt;a href="http://www.w3.org/QA/Tips/Doctype"&gt;add a DOCTYPE declaration&lt;/a&gt; at the very top of
your HTML or XHTML source code. In most cases, putting anything at all before the DOCTYPE (even whitespace!) will result in some 
browsers falling back to "quirks mode", causing your site to render improperly. Spelling and capitalization both matter, so often the easiest 
thing to do is copy and paste the particular DOCTYPE you want. But how do you know which to choose? &lt;/p&gt;
&lt;h2&gt;HTML vs XHTML&lt;/h2&gt;
&lt;p&gt;The current HTML standard is &lt;a href="http://www.w3.org/TR/REC-html40/"&gt;HTML 4.01&lt;/a&gt;, while XHTML authors might choose either 
&lt;a href="http://www.w3.org/TR/xhtml1/"&gt;XHTML 1.0&lt;/a&gt; or &lt;a href="http://www.w3.org/TR/xhtml11/"&gt;XHTML 1.1&lt;/a&gt;. The most commonly quoted 
reason for using XHTML is that it enforces good coding practices such as proper  nesting, consistent capitalization and quoting, etc. 
Don't listen to that crap; if you develop good HTML habits, you'll do all of that anyway. The differences are outlined 
&lt;a href="http://www.w3.org/TR/xhtml1/#diffs"&gt;at W3.org&lt;/a&gt;, but the main differences include capitalization of elements and attributes, 
and empty tag style:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="http://www.w3.org/TR/REC-html40/index/elements.html"&gt;HTML 4.01 elements&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://www.w3.org/TR/REC-html40/index/attributes.html"&gt;HTML 4.01 attributes&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://www.w3.org/TR/xhtml1/#diffs"&gt;Differences between XHTML 1.x and HTML 4.01&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="#html5"&gt;Cross-Browser HTML5 DOCTYPE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
    &amp;lt;!-- Empty tags in HTML --&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
    &amp;lt;img src="/img.png"&amp;gt;

    &amp;lt;!-- Empty tags in XHTML --&amp;gt;
    &amp;lt;p /&amp;gt;
    &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;img src="/img.png" /&amp;gt;
&lt;/pre&gt;
&lt;h2&gt;Strict vs Transitional&lt;/h2&gt;
&lt;p&gt;If you are concerned about cross-browser compatibility, I strongly suggest you use a strict DOCTYPE. Relying on transitional DOCTYPEs 
may require revision as deprecated features become obsolete features in newer browsers. Relying on "quirks mode" usually requires you to do
more work such as adding IE hacks to your HTML and CSS via conditional comments.&lt;/p&gt;
&lt;a name="wheat"&gt;&lt;h2&gt;DOCTYPEs&lt;/h2&gt;&lt;/a&gt;
&lt;pre&gt;
&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd"&amp;gt;
   &amp;lt;-- HTML 4.01 Strict --&amp;gt;

&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd"&amp;gt;
   &amp;lt;-- HTML 4.01 Transitional --&amp;gt;

&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
   "http://www.w3.org/TR/html4/frameset.dtd"&amp;gt;
   &amp;lt;-- HTML 4.01 Frameset --&amp;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;
   &amp;lt;-- XHTML 1.0 Strict --&amp;gt;

&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;
   &amp;lt;-- XHTML 1.0 Transitional --&amp;gt;

&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"&amp;gt;
   &amp;lt;-- XHTML 1.0 Frameset --&amp;gt;
    
&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&amp;gt;
   &amp;lt;-- XHTML 1.1 --&amp;gt;

&lt;/pre&gt;
&lt;p&gt;Unless you are serving &lt;a href="http://www.schillmania.com/content/entries/2004/10/24/application-xhtml+xml/"&gt;application/xhtml+xml&lt;/a&gt;
to a known set of visitors (the average internet wanderer's browser cannot necessarily understand application/xhtml+xml), I suggest using 
either HTML 4.01 Strict or XHTML 1.0 Strict.&lt;/p&gt;

&lt;h2 id="html5"&gt;Update: HTML5&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://html5.org/"&gt;HTML5&lt;/a&gt; has its own DOCTYPE, and 
interestingly enough it may be a good choice for standards-loving 
developers, even those who are not yet using any HTML5-specific 
content. It looks like this:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Short and sweet. What's great about this is that all DOCTYPE-sniffing
browsers (including Firefox, Safari, Opera, and even IE5/6/7/8) will
render in Standards Mode regardless of whether or not they support
HTML5. If you're already using a strict HTML or strict XHTML
DOCTYPE to induce Standards Mode in your visitors' browsers, you 
can switch to the above DOCTYPE whether or not you plan to incorporate
HTML5 in your website.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=SwowoEWwMGQ:gVvy6IbveSI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SwowoEWwMGQ:gVvy6IbveSI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=SwowoEWwMGQ:gVvy6IbveSI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SwowoEWwMGQ:gVvy6IbveSI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=SwowoEWwMGQ:gVvy6IbveSI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SwowoEWwMGQ:gVvy6IbveSI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=SwowoEWwMGQ:gVvy6IbveSI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=SwowoEWwMGQ:gVvy6IbveSI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/SwowoEWwMGQ" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[html]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/01/26/choosing-a-doctype</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/01/13/synchronous-sqlite-singleton</guid>
                <title>Synchronous SQLite Singleton</title>
                <link>http://feedproxy.google.com/~r/callee/~3/pKNNPyGag4Y/synchronous-sqlite-singleton</link>
                <pubDate>Tue, 13 Jan 2009 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a href="http://www.sqlite.org/"&gt;SQLite&lt;/a&gt; is already pretty easy to use, but here's a simple abstraction that makes it even easier to 
interact with a SQLite database. I developed this to reuse in my own &lt;a href="http://www.adobe.com/products/air/develop/ajax/"&gt;Adobe AIR&lt;/a&gt;
apps, because AIR uses SQLite as its native databse implementation.&lt;/p&gt;
&lt;pre&gt;
DB = function() {
    var database = air.File.applicationStorageDirectory.resolvePath('database.db');
    var exec = function(query, param) {
        try {
            c.open(database);
            s.clearParameters();
            if (typeof s.text == 'string') {
                s.text = query;
                if (typeof param == 'object') {
                    for (var i in param) {
                        s.parameters[':' + i] = param[i];
                    }
                }
                s.execute();
            }
            return s.getResult();
        } catch (e) {
            air.trace('Database error: ' + e.message);
        } finally {
            c.close();
        }
    };
    var c = new air.SQLConnection();
    var s = new air.SQLStatement();
    s.sqlConnection = c;
    return {
        exec : exec,
        init : function(filename) {
            filename = filename || 'database.db';
            database = air.File.applicationStorageDirectory.resolvePath(filename);
        }
    };
}();
&lt;/pre&gt;
&lt;pre&gt;
    // here's how to use it
    var employees = DB.exec("SELECT * FROM employees").data;
    for (var i = 0; i &lt; employees.length; i++) {
        alert(employees.firstName + " " + employees.lastName);
    }
    
    // if you're not using SELECT, you don't have to use an assignment
    DB.exec("DELETE FROM employees");
    
    // the second argument is an object for binding SQL statement parameters
    var insert = DB.exec("INSERT INTO employees VALUES (:id, :firstName, :lastName)", {
        id        : 123,
        firstName : 'John',
        lastName  : 'Smith'
    });
    
    // determine how many rows were affected by the last SQL statement
    if (insert.rowsAffected === 0) {
        air.trace("Database error!");
    } else {
        air.trace("Saved!");
    }
    
    // optionally, you can initialize (or REinitialize) it with a new database name
    DB.init('new-filename.db'); // future queries will act on this database
    
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=pKNNPyGag4Y:zsADFywz-W0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=pKNNPyGag4Y:zsADFywz-W0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=pKNNPyGag4Y:zsADFywz-W0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=pKNNPyGag4Y:zsADFywz-W0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=pKNNPyGag4Y:zsADFywz-W0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=pKNNPyGag4Y:zsADFywz-W0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=pKNNPyGag4Y:zsADFywz-W0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=pKNNPyGag4Y:zsADFywz-W0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/pKNNPyGag4Y" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[adobe air]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/01/13/synchronous-sqlite-singleton</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2009/01/09/adobe-air-1.5-gui-less-application-updates</guid>
                <title>Adobe AIR 1.5: GUI-less Application Updates</title>
                <link>http://feedproxy.google.com/~r/callee/~3/Fn7yq197mWk/adobe-air-1.5-gui-less-application-updates</link>
                <pubDate>Fri, 09 Jan 2009 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;Previously, I showed you how to &lt;a href="http://arguments.callee.info/2008/11/25/seamless-application-updates-with-adobe-air-1.5/"&gt;automatically 
update your Adobe AIR 1.5 application using Adobe's ApplicationUpdaterUI&lt;/a&gt;. What if you don't need (or want) to rely on user interaction
at all? While the &lt;a href="http://help.adobe.com/en_US/AIR/1.5/jslr/air/update/ApplicationUpdaterUI.html"&gt;ApplicationUpdaterUI&lt;/a&gt; class 
provides basic updating functionality and a default user interface, &lt;a href="http://help.adobe.com/en_US/AIR/1.5/jslr/air/update/ApplicationUpdater.html"&gt;ApplicationUpdater&lt;/a&gt; 
provides update functionality while letting you handle the UI yourself, or providing no update-related UI at all.&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;script type="text/javascript" src="lib/air/AIRAliases.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type="application/x-shockwave-flash" src="lib/air/applicationupdater.swf"&amp;gt;&amp;lt;/script&amp;gt;
    
    &amp;lt;!-- This is the file described below. --&amp;gt;
    &amp;lt;script type="text/javascript" src="Updater.js"&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;!-- Include the rest of your application however you normally would. --&amp;gt;
    &amp;lt;script type="text/javascript" src="your-application-files.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;p&gt;If you're using the Aptana or Dreamweaver extensions for Adobe AIR, applicationupdater.swf should be automatically generated
as part of a new Adobe AIR project. You may have to check an option to create it.&lt;/p&gt;
&lt;pre&gt;
// _Updater.js
Updater = function() {
    var _ = {
        /**
         * Don't change these directly!
         * Instead, use Updater.init("http://your.web.site/path/to/version.xml");
         */
        url     : false,
        updater : false
    };
    function error(e) {
        air.trace(e); // handle this error as necessary for your app
    }
    function checkComplete(e) {
        if (e.available) {
            /**
             * The application is now updating, notify other components here if necessary.
             */
        } else {
            MyApp.start(); // call your application entry point
        }
    }
    return {
        init  : function(ns, url) {
            _.url = (typeof url == "string") ? url : false;
            _.updater = (_.url) ? new runtime.air.update.ApplicationUpdater() : false;
            this.check();
        },
        check : function() {
            if (_.updater) {
                _.updater.delay = 1; // how many days between checks, can be a fraction
                _.updater.updateURL = _.url;
                assignHandler(runtime.air.update.events.UpdateEvent.INITIALIZED, function() {
                    _.updater.checkNow(); // have to call the first check manually
                });
                assignHandler(runtime.flash.events.ErrorEvent.ERROR, error);
                assignHandler(runtime.air.update.events.DownloadErrorEvent.DOWNLOAD_ERROR, error);
                assignHandler(runtime.air.update.events.StatusUpdateErrorEvent.UPDATE_ERROR, error);
                assignHandler(runtime.air.update.events.StatusUpdateEvent.UPDATE_STATUS, checkComplete);
                _.updater.initialize();
            }
        }
    };
}();
&lt;/pre&gt;
&lt;p&gt;Simply include the above file in a &lt;code&gt;script&lt;/code&gt; tag in your application's main HTML file, then call
&lt;code&gt;_Updater.init("http://your.web.site/path/to/version.xml")&lt;/code&gt;. The target XML file should follow the format below, as
per the &lt;a href="http://www.adobe.com/devnet/air/articles/air_update_framework_04.html"&gt;Adobe example&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
    &amp;lt;update xmlns="http://ns.adobe.com/air/framework/update/description/1.0"&amp;gt;
        &amp;lt;version&amp;gt;1.23&amp;lt;/version&amp;gt;
        &amp;lt;url&amp;gt;http://your.web.site/path/to/YourApplication.air&amp;lt;/url&amp;gt;
        &amp;lt;description&amp;gt;&amp;lt;![CDATA[
            This version has fixes for the
            following known issues:
            *First issue
            *Second issue 
        ]]&amp;gt;&amp;lt;/description&amp;gt;
    &amp;lt;/update&amp;gt; 
&lt;/pre&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=Fn7yq197mWk:vX3mkEHa8G4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Fn7yq197mWk:vX3mkEHa8G4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Fn7yq197mWk:vX3mkEHa8G4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Fn7yq197mWk:vX3mkEHa8G4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Fn7yq197mWk:vX3mkEHa8G4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Fn7yq197mWk:vX3mkEHa8G4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Fn7yq197mWk:vX3mkEHa8G4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Fn7yq197mWk:vX3mkEHa8G4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/Fn7yq197mWk" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[adobe air]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2009/01/09/adobe-air-1.5-gui-less-application-updates</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/12/21/ajax-phrasebook</guid>
                <title>AJAX Phrasebook</title>
                <link>http://feedproxy.google.com/~r/callee/~3/3YI_lgpoWWQ/ajax-phrasebook</link>
                <pubDate>Sun, 21 Dec 2008 05:00:00 +0000</pubDate>
                <description>&lt;ol&gt;
    &lt;li&gt;&lt;a href="#object-creation"&gt;Creating an AJAX object (XMLHttpRequest)&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#callback"&gt;Setting up a callback function&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#get-request"&gt;Making GET requests&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#request-headers"&gt;Setting HTTP request headers&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#post-request"&gt;POSTing form data&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#abort-request"&gt;Aborting an AJAX request in progress&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#php-processing"&gt;Server-side AJAX processing with PHP&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#mime-types"&gt;Serving XML, JSON, (X)HTML, or plain text&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="#singleton"&gt;Putting it all together: A reusable AJAX Singleton&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;a name="object-creation"&gt;&lt;h2&gt;Creating an AJAX object (XMLHttpRequest)&lt;/h2&gt;&lt;/a&gt;
&lt;pre&gt;
    // reusable XMLHttpRequest object
    var http = function() {
        return ('XMLHttpRequest' in window)
            ? new XMLHttpRequest()
            : (('ActiveXObject' in window)
                ? new ActiveXObject("Msxml2.XMLHTTP")
                : null);
    }();
&lt;/pre&gt;
&lt;a&gt;top&lt;/a&gt;
&lt;a name="callback"&gt;&lt;h2&gt;Setting up a callback function&lt;/h2&gt;&lt;/a&gt;
&lt;pre&gt;
    // IE requires that you use http.open BEFORE assigning this
    http.onreadystatechange = function() {
        if (http.readyState === 4) {   // XMLHttpRequest code for "Done"
            if (http.status === 200) { // HTTP status code for "OK"
                // use http.responseText or http.responseXML here
            }
        }
    };
&lt;/pre&gt;
&lt;a&gt;top&lt;/a&gt;
&lt;a name="get-request"&gt;&lt;h2&gt;Making GET requests&lt;/h2&gt;&lt;/a&gt;
&lt;pre&gt;
    // simple GET request
    http.open("GET", "myPage.php", true); // false for synchronous (blocking)
    http.send(null);                      // null is required here
    
    // passing variables to PHP by modifying the URL
    var name = escape("Your Name");
    var age = 27;
    var queryString = "?name=" + name + "&amp;age=" + age;
    http.open("GET", "myPage.php" + queryString, true);
    http.send(null);
&lt;/pre&gt;
&lt;a&gt;top&lt;/a&gt;
&lt;a name="request-headers"&gt;&lt;h2&gt;Setting HTTP request headers&lt;/h2&gt;&lt;/a&gt;
&lt;pre&gt;
    http.open("GET", "myPage.php", true);
    
    /**
     * Set headers after open() and before send().
     * For a complete list of request headers, see:
     * http://www.w3.org/Protocols/HTTP/HTRQ_Headers.html
     */
    http.setRequestHeader("User-Agent", "arguments.callee Custom UA");
    http.setRequestHeader("Accept", "text/plain, text/html, text/xml");
        
    http.send(null);
&lt;/pre&gt;
&lt;a&gt;top&lt;/a&gt;
&lt;a name="post-request"&gt;&lt;h2&gt;POSTing form data&lt;/h2&gt;&lt;/a&gt;
&lt;pre&gt;
    // gather form data
    var name = escape(document.getElementById("form-name").value);
    var address = escape(document.getElementById("form-address").value);
    
    // build query string
    var query = "?name=" + name + "&amp;address=" + address;
    
    // make AJAX request
    http.open("POST", "myPage.php", true);
    http.onreadystatechange = function() {};
    http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    http.setRequestHeader("Content-length", query.length);
    http.setRequestHeader("Connection", "close");
    http.send(query); // not "null" as with GET requests
&lt;/pre&gt;
&lt;a&gt;top&lt;/a&gt;
&lt;a name="abort-request"&gt;&lt;h2&gt;Aborting an AJAX request in progress&lt;/h2&gt;&lt;/a&gt;
&lt;pre&gt;
    http.abort(); // it's that easy!
&lt;/pre&gt;
&lt;a&gt;top&lt;/a&gt;
&lt;a name="php-processing"&gt;&lt;h2&gt;Server-side AJAX processing with PHP&lt;/h2&gt;&lt;/a&gt;
&lt;pre&gt;
&amp;lt;?php
    // gather POST (or GET) variables
    foreach($_POST as $key =&gt; $value) {
        $$key = $value; // note the double $$
        /**
         * Using the last POST example, this loop will
         * create the variables $name and $address.
         */
    }
    
    /**
     * ALWAYS sanitize user input immediately before using it.
     * &lt;a href="http://php.net/htmlspecialchars"&gt;htmlspecialchars()&lt;/a&gt; for displaying on a webpage
     * &lt;a href="http://php.net/mysql_real_escape_string"&gt;mysql_real_escape_string()&lt;/a&gt; when using a MySQL database
     * &lt;a href="http://php.net/filter_var"&gt;filter_var()&lt;/a&gt; for more customizable sanitization
     */
    echo htmlspecialchars("$name lives at $address!");
    exit;
?&amp;gt;
&lt;/pre&gt;
&lt;a&gt;top&lt;/a&gt;
&lt;a name="mime-types"&gt;&lt;h2&gt;Serving XML, JSON, (X)HTML, or plain text&lt;/h2&gt;&lt;/a&gt;
&lt;pre&gt;
&amp;lt;?php
    header("Content-Type: text/xml; charset=utf-8");
    echo '&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
        &amp;lt;root&amp;gt;
            &amp;lt;name&amp;gt;HB&amp;lt;/name&amp;gt;
            &amp;lt;address&amp;gt;123 Any St.&amp;lt;/address&amp;gt;
        &amp;lt;root&amp;gt;
    ';
    exit;
?&amp;gt;
&lt;/pre&gt;
&lt;pre&gt;
&amp;lt;?php
    // some older browsers don't recognize JSON, so serve plain text to them
    $mime = preg_match("/application\/json/i", $_SERVER["HTTP_ACCEPT"]) ? "application/json" : "text/plain";
    header("Content-Type: $mime; charset=utf-8");
    echo '[{"name":"HB","address":"123 Any St."}]';
    exit;
?&amp;gt;
&lt;/pre&gt;
&lt;pre&gt;
&amp;lt;?php
    header("Content-Type: text/html; charset=utf-8");
    
    /**
     * JavaScript may do something like this with the following response:
     * document.getElementById("targetDiv").innerHTML = http.responseText;
     */
    echo '&amp;lt;p&amp;gt;This paragraph was retrieved via AJAX!&amp;lt;/p&amp;gt;';
    exit;
?&amp;gt;
&lt;/pre&gt;
&lt;pre&gt;
&amp;lt;?php
    header("Content-Type: text/plain"); // useful for delimited data (CSV, etc.)
    echo "1, HB Stone, 123 Any St.\n";
    echo "2, Sample Name, 345 Another St.\n";
    exit;
?&amp;gt;
&lt;/pre&gt;
&lt;a&gt;top&lt;/a&gt;
&lt;a name="singleton"&gt;&lt;h2&gt;Putting it all together: A reusable AJAX Singleton&lt;/h2&gt;&lt;/a&gt;
&lt;pre&gt;
    var Http = function() {
        var ReadyStates = {
            UNINITIALIZED : 0,
            OPEN          : 1,
            SENT          : 2,
            RECEIVING     : 3,
            DONE          : 4
        };
        var Status = {
            OK           : 200,
            MULTIPLE     : 300,
            FORBIDDEN    : 403,
            NOT_FOUND    : 404,
            SERVER_ERROR : 500
        };
        var http = function() {
            return ('XMLHttpRequest' in window)
                ? new XMLHttpRequest()
                : (('ActiveXObject' in window)
                    ? new ActiveXObject("Msxml2.XMLHTTP")
                    : null);
        }();
        return {
            isBusy: function() {
                return ((http.readyState !== ReadyStates.UNINITIALIZED) &amp;&amp; (http.readyState !== ReadyStates.DONE));
            },
            request: function(obj) {
                if (typeof obj.url !== "string") {
                    return;
                }
                var orsc function() { // onreadystatechange
                    if (http.readyState === ReadyStates.DONE) {
                        switch(http.status) {
                            case Status.OK:
                                if (typeof obj.success == "function") {
                                    obj.success.call(http, http, obj);
                                }
                                break;
                            // handle other cases here as desired
                            default:
                                if (typeof obj.failure == "function") {
                                    obj.failure.call(http, http, obj);
                                }
                        }
                    }
                };
                var query = function() {
                    if (typeof obj.data !== "object") {
                        return "";
                    }
                    var q = "";
                    for (var i in obj.data) {
                        if (obj.data.hasOwnProperty(i)) {
                            q += (q.length) ? "&amp;" : "";
                            q += encodeURIComponent(i) + "=" + encodeURIComponent(obj.data[i]);
                        }
                    }
                    return q;
                }();
                switch(obj.method) {
                    // implement other methods as desired (HEAD, PUT, DELETE, etc.)
                    case "GET":
                        http.open(obj.method, obj.url + "?" + query, true);
                        http.onreadystatechange = orsc;
                        http.send(null);
                        break;
                    case "POST":
                        http.open(obj.method, obj.url, true);
                        http.onreadystatechange = orsc;
                        http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                        http.setRequestHeader("Content-length", query.length);
                        http.setRequestHeader("Connection", "close");
                        http.send(query);
                        break;
                    default:
                        return; // invalid or unsupported method
                }
            }
        };
    }();
&lt;/pre&gt;
&lt;p&gt;Any time you want to perform a request, you can do something like this:&lt;/p&gt;
&lt;pre&gt;
    Http.request({
        url: "weather.php",
        method: "GET",
        data: {
            zipCode = "92261"
        },
        success: function(http) {
            alert("Weather report: " + http.responseText);
        },
        failure: function(http) {
            alert("AJAX call failed! Status code: " + http.status);
        }
    });
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=3YI_lgpoWWQ:uCQSIXUISYA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=3YI_lgpoWWQ:uCQSIXUISYA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=3YI_lgpoWWQ:uCQSIXUISYA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=3YI_lgpoWWQ:uCQSIXUISYA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=3YI_lgpoWWQ:uCQSIXUISYA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=3YI_lgpoWWQ:uCQSIXUISYA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=3YI_lgpoWWQ:uCQSIXUISYA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=3YI_lgpoWWQ:uCQSIXUISYA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/3YI_lgpoWWQ" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/12/21/ajax-phrasebook</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/11/25/seamless-application-updates-with-adobe-air-1.5</guid>
                <title>Seamless Application Updates with Adobe AIR 1.5</title>
                <link>http://feedproxy.google.com/~r/callee/~3/Pfk1CY7QWW0/seamless-application-updates-with-adobe-air-1.5</link>
                <pubDate>Tue, 25 Nov 2008 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;Providing updates to people who use your AIR apps can be a bit of a hassle. The actual &lt;a href="http://help.adobe.com/en_US/AIR/1.5/jslr/flash/desktop/Updater.html#update()"&gt;Updater&lt;/a&gt; code only
takes one or two lines, but you essentially have to write your own framework around it to consider it full-featured (check, compare, download, install).
Some have supplied examples in &lt;a href="http://www.insideria.com/2008/02/air-api-performing-updates-in.html"&gt;ActionScript&lt;/a&gt; and &lt;a href="http://www.robertnyman.com/2008/06/09/how-to-update-adobe-air-applications-automatically-with-javascript-airupdaterjs/"&gt;JavaScript&lt;/a&gt;, 
but Adobe made it even easier in &lt;a href="http://www.adobe.com/products/air/"&gt;AIR 1.5&lt;/a&gt;. Here's the way I use the
new &lt;a href="http://help.adobe.com/en_US/AIR/1.5/jslr/air/update/ApplicationUpdaterUI.html"&gt;ApplicationUpdaterUI&lt;/a&gt; framework in JavaScript.&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;script type="text/javascript" src="lib/air/AIRAliases.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type="application/x-shockwave-flash" src="lib/air/applicationupdater_ui.swf"&amp;gt;&amp;lt;/script&amp;gt;
    
    &amp;lt;!-- This is the file described below. --&amp;gt;
    &amp;lt;script type="text/javascript" src="_UpdaterUI.js"&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;!-- Include the rest of your application however you normally would. --&amp;gt;
    &amp;lt;script type="text/javascript" src="your-application-files.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;p&gt;If you're using the Aptana or Dreamweaver extensions for Adobe AIR, your IDE should be generate applicationupdater_ui.swf
as part of a new Adobe AIR project. You may have to check an option to create it.&lt;/p&gt;
&lt;pre&gt;
// _UpdaterUI.js
_UpdaterUI = function() {
    var _ = {
        /**
         * Don't change these directly!
         * Instead, use _UpdaterUI.init("http://your.web.site/path/to/version.xml");
         */
        url     : false, // address of version.xml
        updater : false  // ApplicationUpdaterUI object
    };
    function onUpdate(e) {
        if (_.updater) {
            _.updater.checkNow();
        }
    }
    function onError(e) {
        air.trace(e); // replace this to log or handle as necessary for your app
    }
    return {
        init : function(url) {
            _.url = (typeof url == "string") ? url : false;
            _.updater = (_.url) ? function() {
                try {
                    var u = new runtime.air.update.ApplicationUpdaterUI();
                    u.updateURL = _.url;
                    u.isCheckForUpdateVisible = false; // if true: Ask user before checking
                    u.isFileUpdateVisible = false;     // if true: Ask user where to save .air file
                    u.isInstallUpdateVisible = true;   // if true: Ask user whether to update now or on restart
                    u.addEventListener(runtime.air.update.events.UpdateEvent.INITIALIZED, onUpdate);
                    u.addEventListener(runtime.flash.events.ErrorEvent.ERROR, onError);
                    u.initialize();
                } catch (e) {
                    air.trace(e);
                }
                return u;
            }() : false;
        }
    };
}();
&lt;/pre&gt;
&lt;p&gt;I basically encapsulated and "unobtrusified" (no, that's not really a word) the example from &lt;a href="http://www.adobe.com/devnet/air/articles/air_update_framework_04.html"&gt;Adobe's AIR Developer Center&lt;/a&gt;,
so that I could reuse it across multiple projects with minimal effort. You simply include the above file from your initial HTML
document, then call &lt;code&gt;_UpdaterUI.init("http://your.web.site/path/to/version.xml")&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Your version.xml file should have the same overall format as the &lt;a href="http://www.adobe.com/devnet/air/articles/air_update_framework_04.html"&gt;Adobe example&lt;/a&gt;,
reproduced below:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
    &amp;lt;update xmlns="http://ns.adobe.com/air/framework/update/description/1.0"&amp;gt;
        &amp;lt;version&amp;gt;1.23&amp;lt;/version&amp;gt;
        &amp;lt;url&amp;gt;http://your.web.site/path/to/YourApplication.air&amp;lt;/url&amp;gt;
        &amp;lt;description&amp;gt;&amp;lt;![CDATA[
            This version has fixes for the
            following known issues:
            *First issue
            *Second issue 
        ]]&amp;gt;&amp;lt;/description&amp;gt;
    &amp;lt;/update&amp;gt; 
&lt;/pre&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=Pfk1CY7QWW0:NVlpEUoAVH4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Pfk1CY7QWW0:NVlpEUoAVH4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Pfk1CY7QWW0:NVlpEUoAVH4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Pfk1CY7QWW0:NVlpEUoAVH4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Pfk1CY7QWW0:NVlpEUoAVH4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Pfk1CY7QWW0:NVlpEUoAVH4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Pfk1CY7QWW0:NVlpEUoAVH4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Pfk1CY7QWW0:NVlpEUoAVH4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/Pfk1CY7QWW0" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[adobe air]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/11/25/seamless-application-updates-with-adobe-air-1.5</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/11/17/htaccess-cache-rules-everything-around-me</guid>
                <title>.htaccess: Cache Rules Everything Around Me</title>
                <link>http://feedproxy.google.com/~r/callee/~3/cZ-9jGIja_8/htaccess-cache-rules-everything-around-me</link>
                <pubDate>Mon, 17 Nov 2008 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;Properly configuring the cacheability of your webpages and other components can greatly improve the performance
of your site for returning visitors. The longer a component is cached, the less often that same visitor has to redownload
the component, saving you bandwidth and your visitors time. There are many ways to set HTTP headers for caching.&lt;/p&gt;
&lt;pre&gt;
    // Example 1: cache via PHP headers
    if (!headers_sent()) {
        $offset = 60 * 60 * 24 * 7; // how long to cache the file, in seconds
        clearstatcache();           // important to call this before using filemtime()
        
        $lastmod = gmdate('D, d M Y H:i:s', filemtime($f)) . " GMT";
        $expires = gmdate('D, d M Y H:i:s', time() + $offset) . " GMT";
        
        header("Cache-Control: private, pre-check=$offset, max-age=$offset, must-revalidate");
        header("Content-Type: text/html; charset=utf-8");
        header("Last-Modified: $lastmod");
        header("Expires: $expires");
    }
&lt;/pre&gt;
&lt;p&gt;Headers &lt;strong&gt;must&lt;/strong&gt; be sent &lt;strong&gt;before any text is sent to the browser&lt;/strong&gt; (even blank lines).
If you try to set the headers after outputting anything at all, you'll raise a warning and the header will &lt;strong&gt;not&lt;/strong&gt;
be set.&lt;/p&gt;
&lt;p&gt;Example 1 tells our viistors to cache our page for one week. You could calculate that value yourself (60 seconds * 60 
minutes * 24 hours * 7 days = 604800) but leaving it as an expression makes it easier to adjust later if need be.&lt;/p&gt;
&lt;pre&gt;
    # Example 2: using .htaccess with mod_expires
    
    &amp;lt;IfModule mod_expires.c&amp;gt;
        ExpiresActive on
        
        # 60 seconds * 60 minutes * 24 hours * 7 days
        ExpiresDefault A604800
        
        # 60 seconds * 60 minutes * 24 hours
        ExpiresByType text/html A86400
    &amp;lt;/IfModule&amp;gt;
    
    &amp;lt;FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|swf|js|css)$"&amp;gt;
        # configure ETag
        FileETag -INode MTime Size
        
        # max-age set to one week as above
        Header set Cache-Control "max-age=604800, public, must-revalidate"
        
        # if you use ETags, you should unset Last-Modified
        Header unset Last-Modified
    &amp;lt;/FilesMatch&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The benefit of using &lt;code&gt;.htaccess&lt;/code&gt; to set your content headers is that you don't need to worry about
accidentally trying to set headers after content has already been sent to the browser &amp;mdash; &lt;code&gt;.htaccess&lt;/code&gt;
always hits before your page.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;FilesMatch&lt;/code&gt; directive lets you fine tune HTTP headers for certain file types, based on the
regular expression inside the &lt;code&gt;FilesMatch&lt;/code&gt; tag. Example 2 applies the rules to images, Adobe PDF documents,
embeddable videos, JavaScript files, and CSS stylesheets.&lt;/p&gt;
&lt;pre&gt;
    # Example 3: .htaccess when mod_expires is NOT available
    
    &amp;lt;FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|swf|mp3)$"&amp;gt;
        FileETag -INode MTime Size
        Header unset Last-Modified
        Header set Cache-Control "max-age=86400, public, must-revalidate"
        Header set Expires "Thu, 31 Dec 2010 20:00:00 GMT"
    &amp;lt;/FilesMatch&amp;gt;
    
    &amp;lt;FilesMatch "\.(htm|html|php)$"&amp;gt;
        FileETag None
        Header set Cache-Control "max-age=86400, public, must-revalidate"
    &amp;lt;/FilesMatch&amp;gt;
    
    &amp;lt;FilesMatch "\.(js|css)$"&amp;gt;
        FileETag None
        Header set Cache-Control "max-age=604800, public, must-revalidate"
        Header set Expires "Thu, 31 Dec 2010 20:00:00 GMT"
    &amp;lt;/FilesMatch&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;Net&lt;/strong&gt; tab of &lt;a href="http://getfirebug.com/"&gt;Firebug&lt;/a&gt; will show you exactly what headers 
your web server sends for every single file. If you tried Example 2 but your &lt;code&gt;Expires&lt;/code&gt; headers are missing 
or incorrect, your web server may not include the &lt;a href="http://httpd.apache.org/docs/1.3/mod/mod_expires.html"&gt;&lt;code&gt;mod_expires&lt;/code&gt;&lt;/a&gt;
module. The good news is that Example 3 should still work for your server. The less good news is that I think your 
Expires headers must be set to an explicit date and time, rather than a time relative to when the visitor last 
accessed the component. To avoid having to change our &lt;code&gt;.htaccess&lt;/code&gt; file every week to add more time, 
Example 3 sets it to a date and time &lt;strong&gt;far&lt;/strong&gt; in the future, and relies on the Cache-Control 
header to handle the rest.&lt;/p&gt;
&lt;p&gt;What if you don't want some files to cache at all? Dynamic ages that can change each time they are accessed should
not be cached, and Example 4 shows how to &lt;/p&gt;
&lt;pre&gt;
    &amp;lt;FilesMatch "\.(php|cgi|pl|htm)$"&amp;gt;
        ExpiresDefault A0
        Header set Cache-Control "no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, no-transform"
        Header set Pragma "no-cache"
    &amp;lt;/FilesMatch&amp;gt;
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=cZ-9jGIja_8:Jtkz1Xjr3eg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cZ-9jGIja_8:Jtkz1Xjr3eg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=cZ-9jGIja_8:Jtkz1Xjr3eg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cZ-9jGIja_8:Jtkz1Xjr3eg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=cZ-9jGIja_8:Jtkz1Xjr3eg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cZ-9jGIja_8:Jtkz1Xjr3eg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=cZ-9jGIja_8:Jtkz1Xjr3eg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=cZ-9jGIja_8:Jtkz1Xjr3eg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/cZ-9jGIja_8" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[htaccess]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/11/17/htaccess-cache-rules-everything-around-me</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/11/13/exceptional-performance-image-optimization</guid>
                <title>Exceptional Performance: Image Optimization</title>
                <link>http://feedproxy.google.com/~r/callee/~3/lIZ6L0-CLes/exceptional-performance-image-optimization</link>
                <pubDate>Thu, 13 Nov 2008 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;Image optimization should be a big part of tuning your website's performance. The largest components of a page
are typically images, and unlike HTML, CSS, and JavaScript, images don't reap much (if any) benefit from &lt;a href="http://www.webcodingtech.com/php/gzip-compression.php"&gt;being
served gzipped&lt;/a&gt;. There's still a lot that can be done to speed up your images that don't involve any loss in quality.&lt;/p&gt;

&lt;h2&gt;YSlow Serious?&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; is a tool developed by Yahoo as a plugin for &lt;a href="http://getfirebug.com/"&gt;Firebug&lt;/a&gt;.
It offers lots of information and advice that can help you target important aspects of your website's performance.
YSlow evaluates your pages and displays a sort of report card based on a bunch of weighted evaluations. One "grade"
it gives you that is not as useful for small- to medium-sized sites is &lt;a href="http://developer.yahoo.com/performance/rules.html#cdn"&gt;Use a Content Delivery Network&lt;/a&gt;.
However, you can tell YSlow not to count this grade in your overall page score by typing &lt;strong&gt;about:config&lt;/strong&gt;
in Firefox's address bar, creating the key &lt;code&gt;extensions.firebug.yslow.pointsNotCDN&lt;/code&gt; and setting its value to 0. 
YSlow offers other features such as cache statistics, which shows what your visitors have to download the first time they 
hit your page, versus what they have to download on subsequent visits. Make sure the second column is as small as possible 
to offer loyal users a faster website.&lt;/p&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/yslow-example-cache-stats.png"&gt;
    &lt;span&gt;Pretty pie charts! YSlow's cache statistics for this very page.&lt;/span&gt;
&lt;/div&gt;

&lt;h2&gt;Smush.it&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://smush.it/"&gt;Smush.it&lt;/a&gt; is a great site that gets rid of metadata in your images, reducing their
file sizes without reducing image quality. For some images, this can mean very significant savings. They even have
extensions and bookmarklets that help you quickly and easily optimize an entire page at once. Very cool.&lt;/p&gt;

&lt;h2&gt;Diff'rent Strokes&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Image format&lt;/strong&gt; can play a major role in both size and quality. Choosing a format that suits the 
type of image you're making can drastically reduce the filesize. The &lt;a href="http://yuiblog.com/blog/2008/11/04/imageopt-2/"&gt;YUI blog&lt;/a&gt;
has a recent post that says basically everything I was going say here, but here's a quick summary:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;JPG&lt;/strong&gt; should be used for real-world photographs, despite its generation loss.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;GIF&lt;/strong&gt; should be used for animations, and for some pixel art GIF results in a smaller file size than PNG.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;PNG&lt;/strong&gt; (usually 8-bit) should be used for everything that's not covered by the other two formats.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Minimize HTTP Requests&lt;/h2&gt;
&lt;p&gt;It takes less time for your visitors to download one 100k file than it does to download four 25k files. HTTP requests
add to the overall download time for your pages, so do everything you can to eliminate them.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;CSS Sprites&lt;/strong&gt; and &lt;strong&gt;image maps&lt;/strong&gt; are methods of combining multiple images into 
    a single file. One sprite and careful positioning can &lt;a href="http://arguments.callee.info/2008/11/04/generate-social-bookmarks-with-php/"&gt;cut several requests&lt;/a&gt;, 
    saving your visitors precious seconds.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;favicon.ico&lt;/strong&gt; &amp;mdash; make one, and make it tiny. You should make your &lt;a href="http://en.wikipedia.org/wiki/Favicon"&gt;favicon&lt;/a&gt;
    as small as possible to improve cacheability. If you don't have one at all, browsers can attempt to request it
    again on every single one of your pages. Worse yet, when they don't find it they have to make a second request
    to get your 404 page. Making even a simple favicon will not only avoid those "double requests", and a cached
    icon will eliminate dozens of potential requests, saving loyal visitors time on every page.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Cacheability&lt;/strong&gt; (which probably isn't a word) means making sure your visitors don't have to 
    redownload the same images every time they come back to your site, or even as they navigate from page to page. 
    Get the INode out of your &lt;a href="http://httpd.apache.org/docs/2.2/mod/core.html#fileetag"&gt;ETags&lt;/a&gt;, set an
    &lt;a href="http://httpd.apache.org/docs/2.0/mod/mod_expires.html"&gt;Expires header&lt;/a&gt; date in the future, and make 
    sure (&lt;strong&gt;only&lt;/strong&gt; if you use ETags as mentioned) to unset the Last Modified header to make sure your images can be cached.
    &lt;pre&gt;
    # .htaccess file in your document root
    &amp;lt;IfModule mod_expires.c&amp;gt;
        ExpiresActive on
        ExpiresDefault "access plus one month"
    &amp;lt;/IfModule&amp;gt;
    &amp;lt;FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|swf)$"&amp;gt;
    	FileETag -INode MTime Size
    	Header unset Last-Modified
    &amp;lt;/FilesMatch&amp;gt;
    &lt;/pre&gt;
    &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Maximize Parallel Downloads&lt;/h2&gt;
&lt;p&gt;Now that you've cut out unnecessary HTTP requests, it's time to make the remaining requests faster.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Flush early, flush often&lt;/strong&gt; &amp;mdash; no, that's not about bathroom etiquette. Flushing content
    to the browser immediately after the document &lt;code&gt;head&lt;/code&gt; lets the browser fetch your CSS file while
    the rest of the page is processed, and flushing again before the &lt;a href="http://arguments.callee.info/2008/10/29/exceptional-performance-styles-up-scripts-down/"&gt;scripts at the bottom of your page&lt;/a&gt;
    gives the impression of faster rendering while those scripts are fetched and executed.
    &lt;pre&gt;
    &amp;lt;!DOCTYPE ...&gt;
    &amp;lt;html&amp;gt;
        &amp;lt;head&amp;gt;
        &amp;lt;/head&amp;gt;
        &lt;strong&gt;&amp;lt;?php ob_flush(); flush(); ?&amp;gt;&lt;/strong&gt;
        &amp;lt;body&amp;gt;
            ... &amp;lt;!-- page content --&amp;gt;
            &lt;strong&gt;&amp;lt;?php ob_flush(); flush(); ?&amp;gt;&lt;/strong&gt;
            &amp;lt;script src=...&amp;gt;
        &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
    &lt;/pre&gt;
    &lt;li&gt;&lt;strong&gt;Serve images from another subdomain&lt;/strong&gt; &amp;mdash; Browsers may be configured to download only two
    items at a time from a single host. If you serve plain text from &lt;code&gt;www.yoursite.com&lt;/code&gt; and images from 
    &lt;code&gt;images.yoursite.com&lt;/code&gt;, those same browsers should be able to download four components at a time. 
    Obviously this also depends on the structure of your page and your visitors' browser configuration. More 
    details and spiffy graphs are at &lt;a href="http://yuiblog.com/blog/2007/04/11/performance-research-part-4/"&gt;Maximizing Parallel Downloads in the Carpool Lane&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Before and After&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Preload&lt;/strong&gt;. Once your document finishes loading, start anticipating where your visitor
will go next. Google and Yahoo each load additional components after their search homepages finish loading, because
they can safely guess where you'll end up next (their search results page). For other sites it's not as easy to
anticipate, but often you can use browser "down time" to prefetch components. Don't get too enthuastic about preloading,
though, or you may just end up wasting bandwidth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Postload&lt;/strong&gt;. As &lt;a href="http://developer.yahoo.com/performance/rules.html#postload"&gt;Yahoo&lt;/a&gt; puts it,
&lt;em&gt;"What's absolutely required in order to render the page initially?". The rest of the content and components can wait.&lt;/em&gt;
If you have some content split up into tabs or cards, only load the first item on startup. Load the rest of the content 
once the page is fully rendered. Don't make the visitor wait for content they can't use or see yet.&lt;/p&gt;

&lt;h2&gt;One Size Fits One&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Don't scale images at render time&lt;/strong&gt; via HTML or CSS. If your image is 100x100, your CSS should
indicate as much. Don't scale a big image down, just resize it &amp;mdash; otherwise the visitor still has to download 
the big image, but doesn't get the benefit of increased resolution. Scaling a small image up can save download time, 
but at the expense of image quality.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=lIZ6L0-CLes:SfLybJIOuAk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=lIZ6L0-CLes:SfLybJIOuAk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=lIZ6L0-CLes:SfLybJIOuAk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=lIZ6L0-CLes:SfLybJIOuAk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=lIZ6L0-CLes:SfLybJIOuAk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=lIZ6L0-CLes:SfLybJIOuAk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=lIZ6L0-CLes:SfLybJIOuAk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=lIZ6L0-CLes:SfLybJIOuAk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/lIZ6L0-CLes" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[htaccess]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/11/13/exceptional-performance-image-optimization</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/11/12/jquery-time-picker</guid>
                <title>jQuery Time Picker</title>
                <link>http://feedproxy.google.com/~r/callee/~3/-1mQVM9yz_s/jquery-time-picker</link>
                <pubDate>Wed, 12 Nov 2008 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;One of the major conventions in UI design is to use what people already know. If you present an interface that is
unfamiliar, people tend to be hesitant to accept or use it regardless of how good or bad it might be. That said,
sometimes someone comes up with a new UI component that is both cool and useful. &lt;a href="http://haineault.com/media/jquery/ui-timepickr/page/"&gt;Maxime Heinault's jQuery timepickr&lt;/a&gt;
is one such component.&lt;/p&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/jquery-time-picker.jpg" alt="jQuery time picker"&gt;
    &lt;span&gt;jQuery timepickr&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Its description professes a "maximum two-click" approach to choosing a time, but if the picker opens on hover it 
could be brought down to one click. It's unobtrusive, degrades gracefully, is navigable via keyboard or mouse, and the
button sizes are even touchscreen-friendly, for those of us in the kiosk world. The only issue I have with it so far is that
it lets you type in a time, but resets itself once you tab out... It should either not let you change the value or 
accept what you enter.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=-1mQVM9yz_s:qK64-eEuv6g:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=-1mQVM9yz_s:qK64-eEuv6g:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=-1mQVM9yz_s:qK64-eEuv6g:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=-1mQVM9yz_s:qK64-eEuv6g:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=-1mQVM9yz_s:qK64-eEuv6g:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=-1mQVM9yz_s:qK64-eEuv6g:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=-1mQVM9yz_s:qK64-eEuv6g:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=-1mQVM9yz_s:qK64-eEuv6g:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/-1mQVM9yz_s" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[review]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/11/12/jquery-time-picker</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/11/10/passing-arguments-to-settimeout-and-setinterval</guid>
                <title>Passing Arguments to setTimeout and setInterval</title>
                <link>http://feedproxy.google.com/~r/callee/~3/Aueb9sDG1ZE/passing-arguments-to-settimeout-and-setinterval</link>
                <pubDate>Mon, 10 Nov 2008 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a&gt;(skip intermediate solutions)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Often, we need to use &lt;code&gt;setTimeout&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt;
but also need to pass arguments to the function being called. There are a handful of ways to do this.&lt;/p&gt;
&lt;pre&gt;
    // Example 1, BAD! :(
    var hello = 'Hello World!';
    setTimeout("alert('" + hello + "')", 1000);
&lt;/pre&gt;
&lt;p&gt;Example 1 works, but it is not the generally accepted syntax for &lt;code&gt;setTimeout&lt;/code&gt;. Passing code as a string relies on the 
same behavior as &lt;code&gt;eval&lt;/code&gt;, and &lt;code&gt;eval&lt;/code&gt; is usually evil for reasons that are beyond the scope of this post... No 
pun intended. As an aside, this method is often restricted by &lt;a href="http://www.adobe.com/products/air/"&gt;Adobe AIR&lt;/a&gt;, because
of the implied &lt;code&gt;eval&lt;/code&gt;. Here's the common workaround:&lt;/p&gt;
&lt;pre&gt;
    // Example 2, better :)
    var hello = 'Hello World!';
    setTimeout(function() {
        alert(hello);
    }, 1000);
&lt;/pre&gt;
&lt;p&gt;Using a closure
is a much better solution than supplying a string as in the first example, but it can seem bulky. Turning our little alert into
a closure has basically doubled the amount of code required! Okay, it's still short, but it's not quite as clear as
what we would &lt;strong&gt;like&lt;/strong&gt; to do:&lt;a name="example-3"&gt;&amp;nbsp;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;
    // Example 3, best! :D
    var hello = "Hello World";
    setTimeout(alert, 1000, hello);
&lt;/pre&gt;
&lt;p&gt;The call to &lt;code&gt;setTimeout&lt;/code&gt; in Example 3 is clear about what arguments are being passed, rather than having to
read through the closure to see what variables it might refer to. The good news is that Example 3 &lt;strong&gt;already works&lt;/strong&gt; 
on modern browsers (including Adobe AIR). The common implementation for both &lt;code&gt;setTimeout&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt; 
allows you to pass additional arguments by simply... &lt;em&gt;passing additional arguments&lt;/em&gt;. Awesome.&lt;/p&gt;
&lt;p&gt;The exception to this rule is, of course, IE. At least up through version 7, but probably including version 8, IE 
will simply ignore extra arguments supplied to &lt;code&gt;setTimeout&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt;. It won't even throw an 
error, which can make this situation hard to debug (and is probably why this method is not as widely seen as using a 
closure). However, here's a bit of code I originally ran across on &lt;a href="http://webreflection.blogspot.com/2007/06/simple-settimeout-setinterval-extra.html"&gt;Web Reflection&lt;/a&gt;
that extends this behavior to IE.&lt;a name="wheat"&gt;&amp;nbsp;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;
    // Example 4, keep reading if this is confusing, don't worry...
     /*@cc_on
    (function(f) {
        window.setTimeout = f(window.setTimeout);   // overwrites the global function!
        window.setInterval = f(window.setInterval); // overwrites the global function!
    })(function(f) {
        return function(c, t) {
            var a = [].slice.call(arguments, 2);    // gathers the extra args
            return f(function() {
                c.apply(this, a);                   // passes them to your function
            }, t);
        };
    });
    @*/
&lt;/pre&gt;
&lt;p&gt;First things first, &lt;strong&gt;NO you do not have to retype the above code every time you want to use &lt;code&gt;setTimeout&lt;/code&gt;&lt;/strong&gt;.
Example 4 actually changes the way &lt;code&gt;setTimeout&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt; behave. You would include Example 4
at the top of your script, first (or early) in the code. Then you can call &lt;code&gt;setTimeout&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt;
with additional arguments the same way across all browsers. This code makes &lt;a href="#example-3"&gt;Example 3&lt;/a&gt; work 
everywhere.&lt;/p&gt;
&lt;p&gt;Of special note is the conditional comment, &lt;code&gt;/*@cc_on ... @*/&lt;/code&gt;. If you use a JavaScript minifier, it may
consider that whole block of code a comment and delete it... Not good. A less semantic but more reliable alternative is to use object 
detection to identify IE, typically something like &lt;code&gt;if (("ActiveXObject" in window) &amp;&amp; !("opera" in window)) { ... }&lt;/code&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=Aueb9sDG1ZE:VRxw-lenhJA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Aueb9sDG1ZE:VRxw-lenhJA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Aueb9sDG1ZE:VRxw-lenhJA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Aueb9sDG1ZE:VRxw-lenhJA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Aueb9sDG1ZE:VRxw-lenhJA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Aueb9sDG1ZE:VRxw-lenhJA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=Aueb9sDG1ZE:VRxw-lenhJA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=Aueb9sDG1ZE:VRxw-lenhJA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/Aueb9sDG1ZE" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/11/10/passing-arguments-to-settimeout-and-setinterval</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/11/04/generate-social-bookmarks-with-php</guid>
                <title>Generate Social Bookmarks with PHP</title>
                <link>http://feedproxy.google.com/~r/callee/~3/nZpGN-NvNz4/generate-social-bookmarks-with-php</link>
                <pubDate>Tue, 04 Nov 2008 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;Social bookmark links are a given on most modern sites, especially blogs or collections of articles. Here's one way
to keep your list of social bookmark links easy to maintain. Start with a CSS sprite
like the following:&lt;/p&gt;
&lt;p&gt;
    &lt;a href="/img/blog/social-bookmarks.png"&gt;
        &lt;img src="/img/blog/social-bookmarks.png" alt="social bookmarking css sprite"&gt;
    &lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;The sprite should be a single image that consists of all of the icons for the sites you want to link to. Each icon
should be the exact same size, in this case 16x16 pixels. Now lets turn that into a list.&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php
    $sTitle = isset($pageTitle) ? $pageTitle : "You can set this before including this script.";
    $sLink = $_SERVER["SCRIPT_URI"]; // permalink
    
    $sSites = array(
        "de.licio.us" =&amp;gt; "http://del.icio.us/post?v=4&amp;amp;amp;noui&amp;amp;amp;jump=close&amp;amp;amp;url=$sLink&amp;amp;amp;title=$sTitle",
        "digg" =&amp;gt; "http://digg.com/submit?phase=2&amp;amp;amp;url=$sLink&amp;amp;amp;title=$sTitle",
        "furl" =&amp;gt; "http://www.furl.net/store?s=f&amp;amp;amp;to=0&amp;amp;amp;ti=$sTitle&amp;amp;amp;u=$sLink",
        "google bookmarks" =&amp;gt; "http://www.google.com/bookmarks/mark?op=edit&amp;amp;amp;output=popup&amp;amp;amp;bkmk=$sLink&amp;amp;amp;title=$sTitle",
        "mixx" =&amp;gt; "http://www.mixx.com/submit?page_url=$sLink",
        "reddit" =&amp;gt; "http://reddit.com/submit?url=$sLink&amp;amp;amp;title=$sTitle",
        "slashdot" =&amp;gt; "http://slashdot.org/slashdot-it.pl?op=basic&amp;amp;amp;url=$sLink",
        "stumbleupon" =&amp;gt; "http://www.stumbleupon.com/submit?url=$sLink&amp;amp;amp;title=$sTitle",
        "technorati" =&amp;gt; "http://technorati.com/faves/?add=$sLink"
    );
    
    $sPos = 0;                         // sprite position, in pixels
    echo '&amp;lt;ul&amp;gt;';
    foreach ($sSites as $k =&amp;gt; $v) {    // "key" =&gt; "value"
        echo '&amp;lt;li style="background-position: 0px '.$sPos.'px;"&amp;gt;&amp;lt;a href="'.$v.'"&amp;gt;&amp;lt;img src="/img/transparent.gif"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;';
        $sPos -= 18;                   // shift the background image 18 pixels
    }
    echo '&amp;lt;/ul&amp;gt;';
?&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Notice that each URL has components like &lt;code&gt;url=$sLink&lt;/code&gt; and &amp;mdash; when applicable &amp;mdash; &lt;code&gt;title=$sTitle&lt;/code&gt;.
That means that you can use the same script on many pages (or even across many sites) without changing a thing. Just 
watch out for sites changing their submission link, because using the wrong URL may not let your visitor 
promote your site.&lt;/p&gt;
&lt;p&gt;Can't talk about CSS sprites without showing you the CSS to use them, so here you go:&lt;/p&gt;
&lt;pre&gt;
ul.social-bookmarks {
    list-style-type: none;
}
ul.social-bookmarks li {
    display: block;
    float: left;
    width: 16px;
    height: 16px;
    margin-right: 16px;
    background-image: url(/img/social-bookmarks.png);
}
ul.social-bookmarks img {
    width: 16px;
    height: 16px;
    border: 0;
}
&lt;/pre&gt;
&lt;p&gt;Floating the list elements tells them to appear horizontally. Each list item has the same background image; our PHP
script takes care of shifting the position of that background image. The only semi-hack involved is filling our list
items with a transparent gif. Some browsers don't let you apply &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; to empty
&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; elements, or don't let you click on empty links, so to make this cross-browser compatible 
I used a &lt;a href="/img/transparent.gif"&gt;1x1 pixel transparent gif&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=nZpGN-NvNz4:fStuCDC3cbU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=nZpGN-NvNz4:fStuCDC3cbU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=nZpGN-NvNz4:fStuCDC3cbU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=nZpGN-NvNz4:fStuCDC3cbU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=nZpGN-NvNz4:fStuCDC3cbU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=nZpGN-NvNz4:fStuCDC3cbU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=nZpGN-NvNz4:fStuCDC3cbU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=nZpGN-NvNz4:fStuCDC3cbU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/nZpGN-NvNz4" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[css]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/11/04/generate-social-bookmarks-with-php</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/11/03/order-of-operations-in-javascript</guid>
                <title>Order of Operations in JavaScript</title>
                <link>http://feedproxy.google.com/~r/callee/~3/2L490k4r1s4/order-of-operations-in-javascript</link>
                <pubDate>Mon, 03 Nov 2008 05:00:00 +0000</pubDate>
                <description>&lt;p&gt;Operators are evaluated in the order supplied in the following table. &lt;strong&gt;Assoc&lt;/strong&gt;iativity applies when
more than one operation with the same precedence takes place. &lt;strong&gt;L&lt;/strong&gt;eft-to-right &lt;strong&gt;Assoc&lt;/strong&gt;iativity
means that operations of equal weight in a single statement are performed from left to right.&lt;/p&gt;
&lt;table&gt;
    &lt;tr&gt;
        &lt;th&gt;Order&lt;/th&gt;
        &lt;th&gt;Assoc&lt;/th&gt;
        &lt;th&gt;Operator&lt;/th&gt;
        &lt;th&gt;Summary&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;1&lt;/td&gt;
        &lt;td&gt;
            L&lt;br&gt;
            L&lt;br&gt;
            L&lt;br&gt;
            R
        &lt;/td&gt;
        &lt;td&gt;
            .&lt;br&gt;
            []&lt;br&gt;
            ()&lt;br&gt;
            new
        &lt;/td&gt;
        &lt;td&gt;
            Retrieve object property.&lt;br&gt;
            Array index or associative object property.&lt;br&gt;
            Function call.&lt;br&gt;
            Instantiate an object.
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;
            R&lt;br&gt;
            R&lt;br&gt;
            R&lt;br&gt;
            R&lt;br&gt;
            R&lt;br&gt;
            R&lt;br&gt;
            R&lt;br&gt;
            R&lt;br&gt;
            R
        &lt;/td&gt;
        &lt;td&gt;
            ++&lt;br&gt;
            --&lt;br&gt;
            -&lt;br&gt;
            +&lt;br&gt;
            ~&lt;br&gt;
            !&lt;br&gt;
            delete&lt;br&gt;
            typeof&lt;br&gt;
            void
        &lt;/td&gt;
        &lt;td&gt;
            Pre- or post-increment. (unary)&lt;br&gt;
            Pre- or post-decrement. (unary)&lt;br&gt;
            Negation. (unary, NOT subtraction)&lt;br&gt;
            No operation, use numerical value. (unary, NOT addition)&lt;br&gt;
            Bitwise complement. (unary)&lt;br&gt;
            Logical complement. (unary)&lt;br&gt;
            Destroy an object property. (unary)&lt;br&gt;
            Return datatype. (unary)&lt;br&gt;
            Return undefined. (unary)
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;3&lt;/td&gt;
        &lt;td&gt;*, /, %&lt;/td&gt;
        &lt;td&gt;L&lt;/td&gt;
        &lt;td&gt;Multiply, divide, modulo (remainder)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;4&lt;/td&gt;
        &lt;td&gt;
            L&lt;br&gt;
            L
        &lt;/td&gt;
        &lt;td&gt;
            +, -&lt;br&gt;
            +
        &lt;/td&gt;
        &lt;td&gt;
            Add, subtract.&lt;br&gt;
            Concatenate strings.
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;5&lt;/td&gt;
        &lt;td&gt;
            L&lt;br&gt;
            L&lt;br&gt;
            L
        &lt;/td&gt;
        &lt;td&gt;
            &amp;lt;&amp;lt;&lt;br&gt;
            &amp;gt;&amp;gt;&lt;br&gt;
            &amp;gt;&amp;gt;&amp;gt;
        &lt;/td&gt;
        &lt;td&gt;
            Shift left.&lt;br&gt;
            Shift right and extend sign.&lt;br&gt;
            Shift right and pad with zeroes.
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;6&lt;/td&gt;
        &lt;td&gt;
            L&lt;br&gt;
            L&lt;br&gt;
            L&lt;br&gt;
            L
        &lt;/td&gt;
        &lt;td&gt;
            &amp;lt;, &amp;lt;=&lt;br&gt;
            &amp;gt;, &amp;gt;=&lt;br&gt;
            instanceof&lt;br&gt;
            in
        &lt;/td&gt;
        &lt;td&gt;
            Less than, less than or equal.&lt;br&gt;
            Greater than, greater than or equal.&lt;br&gt;
            Verify an object's constructor.&lt;br&gt;
            Verify an object's property exists.
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;7&lt;/td&gt;
        &lt;td&gt;
            L&lt;br&gt;
            L&lt;br&gt;
            L&lt;br&gt;
            L
        &lt;/td&gt;
        &lt;td&gt;
            ==&lt;br&gt;
            !=&lt;br&gt;
            ===&lt;br&gt;
            !==
        &lt;/td&gt;
        &lt;td&gt;
            Are operands equal?&lt;br&gt;
            Are operands NOT equal?&lt;br&gt;
            Are operands identical?&lt;br&gt;
            Are operands NOT identical?
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;8&lt;/td&gt;
        &lt;td&gt;L&lt;/td&gt;
        &lt;td&gt;&amp;amp;&lt;/td&gt;
        &lt;td&gt;Bitwise AND&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;9&lt;/td&gt;
        &lt;td&gt;L&lt;/td&gt;
        &lt;td&gt;^&lt;/td&gt;
        &lt;td&gt;Bitwise XOR&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;10&lt;/td&gt;
        &lt;td&gt;L&lt;/td&gt;
        &lt;td&gt;|&lt;/td&gt;
        &lt;td&gt;Bitwise OR&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;11&lt;/td&gt;
        &lt;td&gt;L&lt;/td&gt;
        &lt;td&gt;&amp;&amp;&lt;/td&gt;
        &lt;td&gt;Local AND&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;12&lt;/td&gt;
        &lt;td&gt;L&lt;/td&gt;
        &lt;td&gt;||&lt;/td&gt;
        &lt;td&gt;Local OR&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;13&lt;/td&gt;
        &lt;td&gt;R&lt;/td&gt;
        &lt;td&gt;? :&lt;/td&gt;
        &lt;td&gt;Conditional operator (ternary)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;14&lt;/td&gt;
        &lt;td&gt;
            R&lt;br&gt;
            R
        &lt;/td&gt;
        &lt;td&gt;
            =&lt;br&gt;
            *=, /=, %=, +=, -=, &amp;lt;&amp;lt;=, &lt;br&gt;&amp;gt;&amp;gt;=, &amp;gt;&amp;gt;&amp;gt;=, &amp;amp;=, ^=, |=
        &lt;/td&gt;
        &lt;td&gt;
            Assign value.&lt;br&gt;
            Perform operation, then assign value.
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;15&lt;/td&gt;
        &lt;td&gt;L&lt;/td&gt;
        &lt;td&gt;, (comma)&lt;/td&gt;
        &lt;td&gt;Multiple evaluation.&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=2L490k4r1s4:cT4FjeH6SVU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=2L490k4r1s4:cT4FjeH6SVU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=2L490k4r1s4:cT4FjeH6SVU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=2L490k4r1s4:cT4FjeH6SVU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=2L490k4r1s4:cT4FjeH6SVU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=2L490k4r1s4:cT4FjeH6SVU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=2L490k4r1s4:cT4FjeH6SVU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=2L490k4r1s4:cT4FjeH6SVU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/2L490k4r1s4" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/11/03/order-of-operations-in-javascript</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/10/31/specificity-in-css</guid>
                <title>Specificity in CSS</title>
                <link>http://feedproxy.google.com/~r/callee/~3/gTsfY4wG1Sg/specificity-in-css</link>
                <pubDate>Fri, 31 Oct 2008 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;Ever wonder why some styles don't seem to get applied to your pages? Ever come across (or produce) a stylesheet littered 
with &lt;code&gt;!important&lt;/code&gt; declarations? The reason is likely selector specificity. &lt;strong&gt;Specificity&lt;/strong&gt; in CSS is how browsers determine which rules should be applied to a webpage.
If two selectors can point to the same HTML element, the rule with the higher specificity is applied.&lt;/p&gt;
&lt;pre&gt;
    div {
        width: 200px; /* less specific */
    }
    div.someClass {
        width: 300px; /* more specific */
    }
    div#someID {
        width: 400px; /* most specific */
    }
&lt;/pre&gt;
&lt;ul&gt;
    &lt;li&gt;Element selectors (&lt;code&gt;div&lt;/code&gt;) are considered the least specific, because there could be any number of matching elements on a page.&lt;/li&gt;
    &lt;li&gt;Class selectors (&lt;code&gt;div.someClass&lt;/code&gt;) are more specific. If you have 10 &lt;code&gt;div&lt;/code&gt;s on a 
    page, it is likely that fewer than 10 will be assigned &lt;code&gt;.someClass&lt;/code&gt; &amp;mdash; if you find 
    otherwise it may be time to start your stylesheet over!&lt;/li&gt;
    &lt;li&gt;ID selectors (&lt;code&gt;div#someID&lt;/code&gt;) are the most specific, because by definition there should be 
    only one of a given ID on the page. Browsers tend not to enforce that rule, but the results of assigning 
    one ID to more than one element can be unpredictable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A common recommendation is to create the least specific selectors to reach an element. For example:&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;html&amp;gt;
      &amp;lt;body&amp;gt;
       &amp;lt;div id="navigation"&amp;gt;
        &amp;lt;ul class="links"&amp;gt;
         &amp;lt;li&amp;gt;&amp;lt;a&amp;gt;home&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
         &amp;lt;li&amp;gt;&amp;lt;a&amp;gt;...&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
       &amp;lt;/div&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;pre&gt;
    div#navigation ul.links li a {
        /* very specific! :(
         *  - hard to override if necessary
         *  - if we rearrange the HTML, we probably have to revise this CSS as well.
         */
    }
    ul.links a {
        /* much less specific :)
         *  - points to the same elements
         *  - we can move ul.links anywhere on the page and this rule will still work.
         *  - saves bandwidth, especially with long stylesheets
         */
    }
&lt;/pre&gt;
&lt;p&gt;Attribute selectors (&lt;code&gt;input[type=submit]&lt;/code&gt;) are grouped with class selectors when it comes to 
specificity. The official &lt;a href="http://www.w3.org/TR/CSS21/cascade.html#specificity"&gt;CSS documentation&lt;/a&gt; 
describes a more complex calculation, but if you keep in mind the relative weight of element, class, and ID 
selectors you should be able to assign styles using more efficient and effective rules.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=gTsfY4wG1Sg:No9SZjc9a-I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gTsfY4wG1Sg:No9SZjc9a-I:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=gTsfY4wG1Sg:No9SZjc9a-I:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gTsfY4wG1Sg:No9SZjc9a-I:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=gTsfY4wG1Sg:No9SZjc9a-I:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gTsfY4wG1Sg:No9SZjc9a-I:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=gTsfY4wG1Sg:No9SZjc9a-I:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=gTsfY4wG1Sg:No9SZjc9a-I:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/gTsfY4wG1Sg" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[css]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/10/31/specificity-in-css</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/10/31/generating-unique-ids-with-javascript</guid>
                <title>Generating Unique IDs with JavaScript</title>
                <link>http://feedproxy.google.com/~r/callee/~3/qp8LqFm8XLA/generating-unique-ids-with-javascript</link>
                <pubDate>Fri, 31 Oct 2008 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;Sometimes you want to generate unique IDs with JavaScript. Perhaps you're creating several DOM elements and
want your script to automatically avoid naming conflicts. Anywhere automatic name or number generation is used,
there's a chance you'll need to ensure you can get a unique ID.&lt;/p&gt;
&lt;pre&gt;
    var uniqueID = function() {
        var id = 1; // initial value
        return function() {
            return id++;
        }; // NOTE: return value is a function reference
    }(); // execute immediately
&lt;/pre&gt;
&lt;p&gt;This sneaky anonymous function doesn't actually return a "normal" value the first time it runs. We're using the 
powerful combination of function literal syntax with inline execution to 
return a new function, which is assigned to &lt;code&gt;var uniqueID&lt;/code&gt;. This new function has access to &lt;code&gt;var id&lt;/code&gt;,
because that variable was defined in the same scope as the function that got assigned to &lt;code&gt;uniqueID&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The first time we call &lt;code&gt;uniqueID()&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt; is returned. That number gets incremented every time 
we call &lt;code&gt;uniqueID()&lt;/code&gt;. Because the variable &lt;code&gt;id&lt;/code&gt; belonged to the sneaky anonymous function, there's
no way for us to accidentally overwrite that value and start getting duplicate values. For example, if our code later 
runs &lt;code&gt;uniqueID.id = 0;&lt;/code&gt;, that creates a &lt;strong&gt;new property&lt;/strong&gt; of the function uniqueID, and the result 
of calling &lt;code&gt;uniqueID();&lt;/code&gt; is unaffected. Handy!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=qp8LqFm8XLA:-oTLGAy9g2Q:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qp8LqFm8XLA:-oTLGAy9g2Q:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=qp8LqFm8XLA:-oTLGAy9g2Q:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qp8LqFm8XLA:-oTLGAy9g2Q:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=qp8LqFm8XLA:-oTLGAy9g2Q:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qp8LqFm8XLA:-oTLGAy9g2Q:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=qp8LqFm8XLA:-oTLGAy9g2Q:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=qp8LqFm8XLA:-oTLGAy9g2Q:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/qp8LqFm8XLA" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/10/31/generating-unique-ids-with-javascript</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/10/31/checking-network-availability-in-adobe-air</guid>
                <title>Checking Network Availability in Adobe AIR</title>
                <link>http://feedproxy.google.com/~r/callee/~3/nEynxyqvg3Y/checking-network-availability-in-adobe-air</link>
                <pubDate>Fri, 31 Oct 2008 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;Here's a module that detects whether your &lt;a href="http://www.adobe.com/products/air/"&gt;Adobe AIR&lt;/a&gt; app is connected to the internet.&lt;/p&gt;
&lt;pre&gt;
Net = function() {
    var url       = false; // assigned during init()
    var connected = false; // automatically updated
    var handlerEventComplete = function(e) {
        connected = true;
    };
    var handlerIoError = function(e) {
        connected = false;
    };
    var checkConnection = function() {
        var r = new air.URLRequest(url);
        r.useCache = false; // force a new fetch
        var l = new air.URLLoader();
        l.addEventListener(air.Event.COMPLETE, handlerEventComplete);
        l.addEventListener(air.IOErrorEvent.IO_ERROR, handlerIoError);
        l.load(r); // execute request
    };
    var start = function() {
        air.NativeApplication.nativeApplication.removeEventListener(air.Event.NETWORK_CHANGE, checkConnection);
        air.NativeApplication.nativeApplication.addEventListener(air.Event.NETWORK_CHANGE, checkConnection);
        checkConnection(); // 1st call must be manual, because an event may have already occurred
    };
    return {
        init        : function(u) {
            url = (typeof u == "string") ? u : false;
            start();
        },
        isConnected : function() {
            return connected;
        }
    };
}();
&lt;/pre&gt;
&lt;p&gt;When you're ready to start checking the network connection, you can call &lt;code&gt;Net.init("http://your.web.site/")&lt;/code&gt; &amp;mdash;
I usually use the URL for my app's version.xml file, which the &lt;a href="http://www.robertnyman.com/2008/06/09/how-to-update-adobe-air-applications-automatically-with-javascript-airupdaterjs/"&gt;Updater&lt;/a&gt; 
uses. If you just need to know whether you're connected, not necessarily whether a particular resource is available, then 
Google or some other reliable address should be fine.&lt;/p&gt;
&lt;p&gt;The module is event-driven, rather than relying on polling. If you want to change the resource it checks for, simply call &lt;code&gt;Net.init()&lt;/code&gt; with the new URL.
Note also that this method &lt;strong&gt;does not require&lt;/strong&gt; &lt;a href="http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ade46-7fcc.html"&gt;Adobe's servicemonitor framework&lt;/a&gt;,
which it would if it polled the address using URLMonitor or ServiceMonitor.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=nEynxyqvg3Y:1j9UIlZspNA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=nEynxyqvg3Y:1j9UIlZspNA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=nEynxyqvg3Y:1j9UIlZspNA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=nEynxyqvg3Y:1j9UIlZspNA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=nEynxyqvg3Y:1j9UIlZspNA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=nEynxyqvg3Y:1j9UIlZspNA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=nEynxyqvg3Y:1j9UIlZspNA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=nEynxyqvg3Y:1j9UIlZspNA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/nEynxyqvg3Y" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[adobe air]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/10/31/checking-network-availability-in-adobe-air</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/10/30/javascript-in-action-thesixtyone-com</guid>
                <title>JavaScript in Action: thesixtyone.com</title>
                <link>http://feedproxy.google.com/~r/callee/~3/HqipBYx47mE/javascript-in-action-thesixtyone-com</link>
                <pubDate>Thu, 30 Oct 2008 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;Just wanted to point you to &lt;a href="http://thesixtyone.com/"&gt;thesixtyone&lt;/a&gt; if you don't already use it. It's really
one of the few next-gen websites that I think really does it right. Thesixtyone calls itself a "music adventure" &amp;mdash;
you earn XP for listening to music as well as reaching various milestones.&lt;/p&gt;
&lt;p&gt;You can support songs you like by bumping them. Bumps cost XP, but the more popular the song gets, the more XP
you earn for having supported it. Sign up for free as a listener or an artist, and start leveling.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=HqipBYx47mE:rskmmcGfI9Q:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HqipBYx47mE:rskmmcGfI9Q:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HqipBYx47mE:rskmmcGfI9Q:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HqipBYx47mE:rskmmcGfI9Q:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HqipBYx47mE:rskmmcGfI9Q:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HqipBYx47mE:rskmmcGfI9Q:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=HqipBYx47mE:rskmmcGfI9Q:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=HqipBYx47mE:rskmmcGfI9Q:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/HqipBYx47mE" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[personal]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/10/30/javascript-in-action-thesixtyone-com</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/10/29/exceptional-performance-styles-up-scripts-down</guid>
                <title>Exceptional Performance: Styles Up, Scripts Down</title>
                <link>http://feedproxy.google.com/~r/callee/~3/jjlel-uVUDo/exceptional-performance-styles-up-scripts-down</link>
                <pubDate>Wed, 29 Oct 2008 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;This is the first post in a series about &lt;a href="http://arguments.callee.info/search/exceptional%20performance/"&gt;improving website performance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here's a simple tip that can significantly speed up your website loading time in the eyes of your visitors.
Since HTML defines the structure of your page and CSS defines how your website is presented, modern 
browsers hang onto your content until you tell them how it should be displayed. This behavior is called 
&lt;strong&gt;progressive rendering&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/blocked-vs-progressive-rendering.gif" alt="Blocked rendering versus progressive rendering."&gt;
    &lt;span&gt;Progressive rendering makes for a more responsive user experience.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;If you use &lt;code&gt;style&lt;/code&gt; tags throughout your page, or include external stylesheets in your page body,
most browsers avoid rendering until those styles are parsed. IE may avoid rendering entirely, leaving
your users to stare at a blank white page until everything is done loading. Declaring all of your styles
at the top of the page ensures that the browser can take full advantage of progressive rendering and display
content immediately. The &lt;a href="http://www.w3.org/TR/html4/struct/links.html#h-12.3"&gt;HTML specification&lt;/a&gt;
confirms that all &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags should be within the document &lt;code&gt;head&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;JavaScript, on the other hand, should be saved until the very &lt;strong&gt;end&lt;/strong&gt; of the page whenever possible.
When a browser comes across a &lt;code&gt;script&lt;/code&gt; tag, it waits until that entire script executes before moving
on. Putting your &lt;code&gt;script&lt;/code&gt;s immediately before your closing &lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt; tag does two things:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;All of your HTML and CSS will have already been loaded, so in your visitor's eyes, your page is "done" loading.&lt;/li&gt;
    &lt;li&gt;Your scripts can immediately start using DOM objects, and don't necessarily have to wait for the events that signify the document finished loading.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Hope that improves the speed of your website! Read more tips at &lt;a href="http://developer.yahoo.com/performance/"&gt;Yahoo&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=jjlel-uVUDo:fGKmQM0tj_w:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jjlel-uVUDo:fGKmQM0tj_w:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=jjlel-uVUDo:fGKmQM0tj_w:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jjlel-uVUDo:fGKmQM0tj_w:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=jjlel-uVUDo:fGKmQM0tj_w:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jjlel-uVUDo:fGKmQM0tj_w:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=jjlel-uVUDo:fGKmQM0tj_w:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=jjlel-uVUDo:fGKmQM0tj_w:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/jjlel-uVUDo" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[html]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/10/29/exceptional-performance-styles-up-scripts-down</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/10/28/zapping-trailing-commas-using-regular-expressions</guid>
                <title>Zapping Trailing Commas Using Regular Expressions</title>
                <link>http://feedproxy.google.com/~r/callee/~3/M64VDYdKoP0/zapping-trailing-commas-using-regular-expressions</link>
                <pubDate>Tue, 28 Oct 2008 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;a href="#wheat"&gt;(skip to the finished product)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here's a PHP tip that will hopefully come in handy. Say you have an automatically generated string that dumped
a bunch of stuff together and separated them with commas. Meta keywords, post tags, first and last names, whatever. Ideally
you could "fix" your generation process and not introduce the trailing comma to begin with, but we all know sometimes
life isn't perfect. There are many ways to eliminate that trailing comma.&lt;/p&gt;
&lt;pre&gt;
    // this would be generated somewhere else, it's just assigned here for comprehension
    $generated = "this, that, the other, and something else, ";
    
    ...
    
    $generated = substr($generated, 0, strlen($generated) - 2);
    echo $generated; // this, that, the other, and something else
&lt;/pre&gt;
&lt;p&gt;What if that trailing &lt;strong&gt;space&lt;/strong&gt; isn't there? Sometimes another process may have trimmed it already. Revision
number one...&lt;/p&gt;
&lt;pre&gt;
    $generated = "this, that, the other, and something else, ";
    
    if (substr($generated, -1) == ' ') {
        $generated = substr($generated, 0, strlen($generated) - 2);
    } else if (substr($generated, -1) == ',') {
        $generated = substr($generated, 0, strlen($generated) - 1);
    }
    echo $generated; // this, that, the other, and something else
&lt;/pre&gt;
&lt;p id="wheat"&gt;Little messier, but it gets the job done. Is there an easier way?&lt;/p&gt;
&lt;pre&gt;
    $generated = "this, that, the other, and something else, ";
    
    $generated = preg_replace('/, ?$/', '', $generated);
    echo $generated; // this, that, the other, and something else
&lt;/pre&gt;
&lt;p&gt;Back to one line, and now it doesn't matter if there is or isn't a space, or even if the whole trailing ", " isn't
found to begin with! It's a short regex, but lets break it down character by character:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;/&lt;/strong&gt; begins a Perl-style regular expression&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;,&lt;/strong&gt; is the comma we want to zap&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;(space)&lt;/strong&gt; is the space that may or may not already be trimmed&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;?&lt;/strong&gt; means match &lt;strong&gt;zero or one&lt;/strong&gt; of the last character (space)&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;$&lt;/strong&gt; means only match at the &lt;strong&gt;end&lt;/strong&gt; of &lt;code&gt;$generated&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;/&lt;/strong&gt; ends a Perl-style regular expression&lt;/li&gt;
&lt;/ol&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=M64VDYdKoP0:x0esc8qwOHw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=M64VDYdKoP0:x0esc8qwOHw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=M64VDYdKoP0:x0esc8qwOHw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=M64VDYdKoP0:x0esc8qwOHw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=M64VDYdKoP0:x0esc8qwOHw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=M64VDYdKoP0:x0esc8qwOHw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=M64VDYdKoP0:x0esc8qwOHw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=M64VDYdKoP0:x0esc8qwOHw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/M64VDYdKoP0" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[php]]></category>
		<category><![CDATA[regular expressions]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/10/28/zapping-trailing-commas-using-regular-expressions</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/10/28/im-a-pc-and-im-a-mac</guid>
                <title>I'm a PC, and I'm a Mac</title>
                <link>http://feedproxy.google.com/~r/callee/~3/4ZJ1qc78tn4/im-a-pc-and-im-a-mac</link>
                <pubDate>Tue, 28 Oct 2008 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;What do you get when you cross 50 Mac Minis, a case of Monster energy drinks, and a bunch of programmers in the &lt;a href="http://geekworksllc.com/"&gt;Geekworks&lt;/a&gt; basement? Check it out!&lt;/p&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/macs/fifty-mac-minis.jpg" alt="Fifty Mac Minis"&gt;
    &lt;span&gt;Fifty &lt;a href="http://www.apple.com/macmini/"&gt;Mac Mini&lt;/a&gt;s loaded with &lt;a href="http://www.microsoft.com/windows/embedded/"&gt;Windows XP Embedded&lt;/a&gt; and our digital signage app, written for &lt;a href="http://www.adobe.com/products/air/"&gt;Adobe AIR&lt;/a&gt;.&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/macs/wall-of-mac-minis.jpg" alt="Wall of Mac Minis"&gt;
    &lt;span&gt;iWall&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/macs/wall-of-monsters.jpg" alt="Wall of Monster energy drinks"&gt;
    &lt;span&gt;mWall? Monster really should be paying US, not the other way around.&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/macs/assembly-line.jpg" alt="Mac Mini assembly line"&gt;
    &lt;span&gt;Prepping the boxes many-at-a-time. Is this a fire hazard? Also notice the &lt;a href="http://www.monsterenergy.com/"&gt;Monster energy drinks&lt;/a&gt; used for fuel.&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/macs/macs-labeled.jpg" alt="Mac Minis labeled with Post-Its"&gt;
    &lt;span&gt;Each one is labeled with an address...&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;img src="http://arguments.callee.info/img/blog/macs/fresno.jpg" alt="Google Map of Fresno"&gt;
    &lt;span&gt;... that corresponds to one of these Christmas-colored dots in Fresno.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;The digital signage app is written in ActionScript, on Adobe AIR. I mostly worked on the data aggregation in PHP (news, sports scores, stock prices, and weather) which is fed to each player as XML.&lt;/p&gt;
&lt;p&gt;Even though the hardware's Mac minis, we use Windows XP Embedded, so they're both Macs and PCs. (nevermind that Macs &lt;strong&gt;are&lt;/strong&gt; PCs to begin with...)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=4ZJ1qc78tn4:g9Qh1AVk30s:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=4ZJ1qc78tn4:g9Qh1AVk30s:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=4ZJ1qc78tn4:g9Qh1AVk30s:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=4ZJ1qc78tn4:g9Qh1AVk30s:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=4ZJ1qc78tn4:g9Qh1AVk30s:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=4ZJ1qc78tn4:g9Qh1AVk30s:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=4ZJ1qc78tn4:g9Qh1AVk30s:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=4ZJ1qc78tn4:g9Qh1AVk30s:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/4ZJ1qc78tn4" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[personal]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/10/28/im-a-pc-and-im-a-mac</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/10/28/ajax-cross-browser-xmlhttprequest</guid>
                <title>AJAX: Cross-Browser XMLHttpRequest</title>
                <link>http://feedproxy.google.com/~r/callee/~3/FrS9VqpJx-Y/ajax-cross-browser-xmlhttprequest</link>
                <pubDate>Tue, 28 Oct 2008 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;The term AJAX has grown to encompass a broad range of technologies and programming methodologies. 
As you'll find throughout web development, there's often a DOM way to do something, and then there's 
The Microsoft Way to do it. In this case, there are actually two Microsoft ways.&lt;/p&gt;
&lt;pre&gt;
    var http = function() {
        try {
            return new XMLHttpRequest();
        } catch (e) {}
        try {
            return new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {}
        try {
            return new ActiveXObject("Microsoft.XMLHTTP");
        } catch (e) {}
        return null;
    }(); // inline execution
&lt;/pre&gt;
&lt;p&gt;You can think of this pattern as &lt;strong&gt;the Good, the Bad, and the Ugly&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;The Good&lt;/strong&gt;: The first &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; block 
    uses the DOM API standard method of generating an XMLHttpRequest object. Most browsers implement this method, 
    including Firefox, Safari, Opera, Google Chrome, and a host of others.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The Bad&lt;/strong&gt;: Msxml2.XMLHTTP is IE's 
    AJAX object. Msxml2.XMLHTTP is actually the preferred 
    of the two IE objects, according to &lt;a href="http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx"&gt;the Microsoft XML Team&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The Ugly&lt;/strong&gt;: Microsoft.XMLHTTP is bound to version 2.0 of Microsoft's implementation of the XMLHttpRequest object, while
    Msxml2.XMLHTTP (despite the name...) has progressed to at least version 6.0, possibly beyond. But if you're
    wondering why your killer app doesn't work in IE5 on Windows 98, make sure you include that third &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; block!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The beauty of this above pattern is that you don't have to try to detect what browser your visitor is running,
nor do object detection to determine which code to execute.&lt;/p&gt;
&lt;p&gt;Semantically, I should mention that object detection is probably the "right" way to create this object. If you're
going to use &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; blocks, you should actually handle the error, not have 
empty &lt;code&gt;catch&lt;/code&gt;es. However, in this particular case I believe this pattern to be the most efficient
and easiest to understand way to create your AJAX object.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=FrS9VqpJx-Y:p-WWTpXqB4w:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=FrS9VqpJx-Y:p-WWTpXqB4w:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=FrS9VqpJx-Y:p-WWTpXqB4w:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=FrS9VqpJx-Y:p-WWTpXqB4w:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=FrS9VqpJx-Y:p-WWTpXqB4w:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=FrS9VqpJx-Y:p-WWTpXqB4w:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=FrS9VqpJx-Y:p-WWTpXqB4w:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=FrS9VqpJx-Y:p-WWTpXqB4w:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/FrS9VqpJx-Y" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/10/28/ajax-cross-browser-xmlhttprequest</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/10/27/function-and-scope</guid>
                <title>(function(){})(); and Scope</title>
                <link>http://feedproxy.google.com/~r/callee/~3/ugVNK-vk69w/function-and-scope</link>
                <pubDate>Mon, 27 Oct 2008 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;No, that's not a typo. &lt;code&gt;(function(){})();&lt;/code&gt; has a lot of punctuation, but that's what it really looks like. It's easier to 
understand if you break it down:&lt;/p&gt;
&lt;pre&gt;
    (            // 1
        function // 2
        ()       // 3
        {        // 4
        alert("code goes here");
        }        // 5
    )            // 6
    ();          // 7
&lt;/pre&gt;
&lt;ol&gt;
    &lt;li&gt;Lets the JavaScript interpreter (browser) know that we want the next few js pieces to act as a group. This is no different than grouping operations in math.
    &lt;br&gt;&lt;code&gt;var mathStuff = (1 + 2) * 3&lt;/code&gt; means to figure out &lt;code&gt;1 + 2&lt;/code&gt; first.&lt;/li&gt;
    &lt;li&gt;We're declaring an anonymous function. Functions are first-class objects in JavaScript, and in many cases they don't need to be assigned a name to execute.&lt;/li&gt;
    &lt;li&gt;This is the list of arguments for our function, just like a regular function definition &lt;code&gt;function&lt;strong&gt;()&lt;/strong&gt; { ... }&lt;/code&gt; In this case there are no arguments, but just you wait...&lt;/li&gt;
    &lt;li&gt;Begins the function body.&lt;/li&gt;
    &lt;li&gt;Ends the function body.&lt;/li&gt;
    &lt;li&gt;Ends the "group" we created in line 1.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Inline execution&lt;/strong&gt; is when you execute a function immediately upon defining it. This works the same way as calling a function named &lt;code&gt;someFunction&lt;/code&gt; by typing &lt;code&gt;someFunction();&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Waitaminute...&lt;/strong&gt; What's the purpose of defining a function if we run it immediately and throw it away?
 Why not just type &lt;code&gt;alert("code goes here")&lt;/code&gt; and avoid that whole squiggly mess?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt;, that's why. Any variables or functions created in that anonymous function live and die with 
that anonymous function. A major concept of scope in javascript is inner functions can see into outer functions, but
outer functions can't see into inner functions. For example:&lt;/p&gt;
&lt;pre&gt;
    function withInline(){
        var year = (new Date()).getFullYear();
        (function(year){
            var lastYear = year - 1; // hidden from withInline!
            year++; // modifies this inner function's argument only
            alert("next year is " + year); // correct
        })(year); // passing the outer function's variable to the anon function
        alert("this year is " + year); // unchanged
        alert("last year is " + lastYear); // error: lastYear is not defined in this scope!
    }
    
    function withoutInline(){
        var year = (new Date()).getFullYear();
        var lastYear = year - 1; // not hidden
        year++;
        alert("next year is " + year); // correct
        alert("this year is " + year); // incorrect, overwritten :(
        alert("last year is " + lastYear); // correct
    }
&lt;/pre&gt;
&lt;script type="text/javascript"&gt;
    function withInline(){
        var year = (new Date()).getFullYear();
        (function(year){
            var lastYear = year - 1; // hidden from withInline!
            year++; // modifies this inner function's argument only
            alert("next year is " + year); // correct
        })(year); // passing the outer function's variable to the anon function
        alert("this year is " + year); // unchanged
        alert("last year is " + lastYear); // error: lastYear is not defined in this scope!
    }
    
    function withoutInline(){
        var year = (new Date()).getFullYear();
        var lastYear = year - 1; // not hidden
        year++;
        alert("next year is " + year); // correct
        alert("this year is " + year); // incorrect, overwritten :(
        alert("last year is " + lastYear); // correct
    }
&lt;/script&gt;
&lt;p&gt;
    &lt;input type="button" onclick="withInline()" value="withInline()"&gt;
    &lt;input type="button" onclick="withoutInline()" value="withoutInline()"&gt;
&lt;/p&gt;
&lt;p&gt;Notice both &lt;code&gt;withInline&lt;/code&gt; and &lt;code&gt;withoutInline &lt;/code&gt; execute the statements in the exact same order. 
However, the &lt;code&gt;withInline()&lt;/code&gt; button will only display two alert boxes, the third one throws an error because 
&lt;code&gt;lastYear&lt;/code&gt; is not visible to &lt;code&gt;withInline&lt;/code&gt;, it lives and dies with the anonymous inner function.
The &lt;code&gt;withoutInline()&lt;/code&gt; button doesn't throw any errors, but the value stored in &lt;code&gt;year&lt;/code&gt; is 
overwritten with next year! The anonymous function construct creates its own scope, or execution context.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=ugVNK-vk69w:_c30HDjA20M:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=ugVNK-vk69w:_c30HDjA20M:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=ugVNK-vk69w:_c30HDjA20M:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=ugVNK-vk69w:_c30HDjA20M:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=ugVNK-vk69w:_c30HDjA20M:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=ugVNK-vk69w:_c30HDjA20M:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=ugVNK-vk69w:_c30HDjA20M:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=ugVNK-vk69w:_c30HDjA20M:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/ugVNK-vk69w" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/10/27/function-and-scope</feedburner:origLink></item>
                <item>
                <guid isPermaLink="false">http://arguments.callee.info/2008/10/27/arguments-callee</guid>
                <title>arguments.callee</title>
                <link>http://feedproxy.google.com/~r/callee/~3/J14fwBq-tS8/arguments-callee</link>
                <pubDate>Mon, 27 Oct 2008 04:00:00 +0000</pubDate>
                <description>&lt;p&gt;&lt;code&gt;arguments.callee&lt;/code&gt; is a powerful JavaScript construct that allows a function to call itself.
Sound useless? It's not. &lt;code&gt;arguments.callee&lt;/code&gt; allows even anonymous functions to be 
recursive. A classic example
of recursion is something like this:&lt;/p&gt;
&lt;pre&gt;
    function getFactorial(x) {
        if (x &amp;lt;= 1) {
            return 1;
        } else {
            return x * arguments.callee(x - 1);
        }
    }
    alert(getFactorial(5)); // evaluates 5*4*3*2*1, alerts "120"
&lt;/pre&gt;
&lt;p&gt;That particular example can be done without recursion, and in general is not very exciting. However, this
blog will delve into various ways &lt;code&gt;arguments.callee&lt;/code&gt; can be used to solve real programming problems.
Another fun construct is &lt;code&gt;setTimeout(arguments.callee, x)&lt;/code&gt; which allows a function to call itself
after a delay of &lt;strong&gt;x&lt;/strong&gt; milliseconds. Here's a fun little example:&lt;/p&gt;
&lt;p id="post-example" style="height: 20px;"&gt;&lt;/p&gt;
&lt;script type="text/javascript"&gt;
    (function() {
        var message = "Asynchronous output!";
        var target = document.getElementById("post-example");
        if (target.innerHTML === message) {
            target.innerHTML = "";
        } else {
            target.innerHTML += message.charAt(target.innerHTML.length);
        }
        setTimeout(arguments.callee, 200);
    })();    
&lt;/script&gt;
&lt;pre&gt;
(function() {
    var message = "Asynchronous output!";                            // statement 1
    var target = document.getElementById("post-example");            // statement 2
    if (target.innerHTML === message) {
        target.innerHTML = "";                                       // statement 3
    } else {
        target.innerHTML += message.charAt(target.innerHTML.length); // statement 4
    }
    setTimeout(arguments.callee, 200);                               // statement 5
})();    
&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;(function(){})();&lt;/code&gt; creates an anonymous function, then immediately runs it. Handy when you
want to execute code without worrying about scope 
issues, but that's out of the scope of this post...&lt;/p&gt;
&lt;p&gt;The first two statements of our anonmyous function declare what we want to output, and where we want to
send it. Statement 3 resets the target once the full message has been displayed, and statement 4 
copies over a single character of our message.&lt;/p&gt;
&lt;p&gt;Statement 5 is the fun part. Our unnamed function calls itself 5 times per second, outputting one letter
each time. There's more to it than that, but hopefully that serves as an introduction to recustion using
&lt;code&gt;arguments.callee&lt;/code&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/callee?a=J14fwBq-tS8:pEXIyXuS7ug:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=J14fwBq-tS8:pEXIyXuS7ug:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=J14fwBq-tS8:pEXIyXuS7ug:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=J14fwBq-tS8:pEXIyXuS7ug:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=J14fwBq-tS8:pEXIyXuS7ug:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=J14fwBq-tS8:pEXIyXuS7ug:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/callee?a=J14fwBq-tS8:pEXIyXuS7ug:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/callee?i=J14fwBq-tS8:pEXIyXuS7ug:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/callee/~4/J14fwBq-tS8" height="1" width="1"/&gt;</description>
                
		<category><![CDATA[arguments.callee]]></category>
		<category><![CDATA[javascript]]></category>
            <feedburner:origLink>http://arguments.callee.info/2008/10/27/arguments-callee</feedburner:origLink></item>
    
    </channel>
</rss>

