<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>BarneyBlog &#187; javascript</title>
	<atom:link href="http://www.barneyb.com/barneyblog/category/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.barneyb.com/barneyblog</link>
	<description>Thoughts, rants, and even some code from the mind of Barney Boisvert.</description>
	<lastBuildDate>Mon, 02 Mar 2020 13:20:35 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Adobe, Mobile Flash Player, JavaScript, etc.</title>
		<link>https://www.barneyb.com/barneyblog/2011/11/10/adobe-mobile-flash-player-javascript-etc/</link>
		<comments>https://www.barneyb.com/barneyblog/2011/11/10/adobe-mobile-flash-player-javascript-etc/#comments</comments>
		<pubDate>Thu, 10 Nov 2011 18:28:55 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[ajax]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[flex]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1743</guid>
		<description><![CDATA[Before you click over this because you know me as a Flash hater, give me two seconds.Â  That's not what this post is about.Â  It's about a larger issue.Â  It's about how awesome it is to be a web developer these days.
Ten years ago, being a web developer sucked.Â  Deployment was easy (rsync to production [...]]]></description>
			<content:encoded><![CDATA[<p>Before you click over this because you know me as a Flash hater, give me two seconds.Â  That's not what this post is about.Â  It's about a larger issue.Â  It's about how awesome it is to be a web developer these days.</p>
<p>Ten years ago, being a web developer sucked.Â  Deployment was easy (rsync to production and done), but the tooling available to us was dismal.Â  And I mean crying-naked-in-a-snowstorm dismal.Â  Browsers were inconsistent, their built-in programming environment (read JavaScript) was reasonably functional but horridly slow, and hardware wasn't beefy enough to deal with scripting languages for hard-core number crunching.</p>
<p>But we had the Flash Player.Â  Flash provided an environment that beat all three of those problems, and beat them soundly.Â  It was consistent across browsers and operating systems, used a similar language to what we were used to (both JavaScript and ActionScript are ECMAScript implementations), and it let the developer compile the script into something a little lower level to run in a dedicated VM on the user's machine which meant it was faster.Â  Of course, Flash is an animation toolkit, but we figured out how to bastardize it with the single-frame movie with a include-and-stop script in it so we could build applications entirely with script (like we wanted), but leverage the Flash player to actually run them.Â  Not to mention the rich support for visual stuff.Â  All this led to the concept of a RIA (Rich Internet Application).Â  Something similar to what we had on the desktop, but deployed to the web with all the benefits (and some drawbacks) that has.</p>
<p>Then browsers got their act together.Â  We started seeing a unifying focus on application development with the browser as the environment.Â  People got serious about fast JavaScript runtimes.Â  Standards were written (e.g., CSS2/3, Canvas) and largely adhered to.Â  And hardware got faster.Â  JavaScript application frameworks (EXT, YUI, GWT, etc.) showed up to leverage all that, and now it was possible to build RIAs using standards supported by a wide array of vendors.</p>
<p>In order to compete with this, Adobe released Flex, which is an application development framework for deploying to Flash Player.Â  It was horridly expensive, difficult to work with, had all kinds of implementation problems, but was better than what you got with JavaScript.Â  For a while.Â  Unfortunately a single software company, even one of the largest in the world, couldn't hope to compete with the widespread interest and momentum around browser-native RIA development.Â  Flex died as a web application framework pretty much before it was released.Â  Which isn't to say it wasn't used (it was and still is), but the browser RIA juggernaut crushed it like a bug.</p>
<p>Then we saw the huge surge in mobile devices.Â  It started with smartphones and now includes tablets and e-readers of various form factors.Â  Fortunately for both consumers and manufacturers, the web wasn't new, so they were able to jump right on top of all the standards and browser capabilities which had been created for the desktop.Â  A huge market segment opened up for web application developers and browser-native was reasonable right out of the box.</p>
<p>Adobe again tried to compete by creating Mobile Flash Player, but the benefits of Flash Player are small on new mobile devices, especially considering that so much of a mobile device experience is through web-connected native applications, not traditional web applications.Â  And here we have Adobe's smartest move yet around Flash Player: killing it for the mobile market in favor of restructuring the ecosystem around using it as a development environment for native applications.</p>
<p>Unfortunately, I don't think it's going to matter because Flash is still really slow and heavy and it's not really that much better to develop with than the truly native dev kits.Â  Yes, it offers the promise of cross-platform deploy, but just as Java demonstrated 10-15 years ago, that isn't much of a selling point for a single application.Â  People expect not just native execution, they expect native idioms, which means you have to develop for a specific platform, even if you're using a cross-platform toolkit.</p>
<p>So where does that leave us?Â  As consumers it leaves us in a great spot: there are lots of ways developers can deliver engaging applications to us, on all our devices.Â  As developers it leaves us in a weird spot: we're stuck with being either web or native developers, with Flash now trying to occupy a sort of middle ground (develop web-style, deploy native).Â  On the desktop I think Flash (via AIR) holds some promise, but ultimately I don't think it'll last.Â  The platform just isn't compelling enough to justify the dedication it requires to use it.Â  Java was designed for very much the same purpose as AIR, and virtually no one uses it.Â  Java moved almost entirely server side, something which Flash isn't likely to do.</p>
<p>Most interesting is the PhoneGap/Titanium/etc. movement which is very much paralleling the browser resurgence I talked about earlier.Â  Huge communities of people are working to take all the skills we have as web application developers and give us a build process to take web-ish apps and compile them into native applications, in much the same way Adobe has use AIR to compile Flash into native apps.Â  However, I think Flash is going to lose in exactly the same way, and for exactly the same reasons, as it lost in the browser.</p>
<p>Bottom line, if you use the web or a web-connected device (read: everybody) the world is going to be glorious in a couple years and only get better.Â  If you're a developer trying to work in that space, you need to learn browser technologies.Â  It's the way of the future.Â  Flash had it's run, but it's been on the way out for a long while.Â  It'll stick around, just like COBOL and Fortran have, but without an alternative path like Java ended up taking it isn't going to stay relevant to mainstream developers.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.barneyb.com/barneyblog/2011/11/10/adobe-mobile-flash-player-javascript-etc/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Even Better Inline Progress Bars</title>
		<link>https://www.barneyb.com/barneyblog/2011/05/17/even-better-inline-progress-bars/</link>
		<comments>https://www.barneyb.com/barneyblog/2011/05/17/even-better-inline-progress-bars/#comments</comments>
		<pubDate>Tue, 17 May 2011 17:29:18 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[css]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1680</guid>
		<description><![CDATA[In the handful of months since I last posted about inline progress bars, I've made some enhancements to the mechanism. In particular, the original mechanism didn't handle overages very gracefully, and while overage isn't strictly needed for progress, I've ended up wanting the same inline display for other types of measurements (e.g., fundraising dollars against [...]]]></description>
			<content:encoded><![CDATA[<p>In the handful of months since I last posted about <a href="http://www.barneyb.com/barneyblog/2010/09/15/inline-js-progress-bars/">inline progress bars,</a> I've made some enhancements to the mechanism. In particular, the original mechanism didn't handle overages very gracefully, and while overage isn't strictly needed for progress, I've ended up wanting the same inline display for other types of measurements (e.g., fundraising dollars against a goal).</p>
<p>The JS has grown slightly more complicated, creating a richer structure to style to handle some of the new stuff:</p>
<pre>jQuery(".progress").each(function() {
  var $this = jQuery(this)
  var frac = parseFloat($this.attr("rel"))
  if (isNaN(frac)) {
    return // bad data
  }
  var html = $this.html();
  $this.empty()
    .append(''
      + '&lt;span class="container"&gt;'
        + '&lt;span class="status-text-spacer"&gt;' + html + '&lt;/span&gt;'
        + '&lt;span class="progress-bar" style="width:' + (frac * 100) + '%;"&gt;&lt;/span&gt;'
        + '&lt;span class="status-text" style="width:' + Math.max(100, frac * 100) + '%;"&gt;'
          + '&lt;span class="status-text-spacer"&gt;' + html + '&lt;/span&gt;'
        + '&lt;/span&gt;'
      + '&lt;/span&gt;'
    )
  if (frac &gt;= 1) {
    $this.addClass("overage") // strictly for external targetting
      .find(".container")
        .css({
          marginRight: (100 - 100 / frac) + "%",
          width: (100 / frac) + "%"
        })
  }
})</pre>
<p>The CSS is still about the same for the base functionality, but with the new stuff, it's far richer:</p>
<pre>.progress {
  display: inline-block;
  position: relative;
}
.progress .container {
  display: inline-block;
  position: relative;
  border: 1px solid #999;
  background-color: #f7f7f7;
  color: #f7f7f7;
  white-space: nowrap;
  padding: 0;
  margin: 0;
  width: 100%;
}
.progress .container .progress-bar {
  display: inline-block;
  position: absolute;
  z-index: 0;
  top: 0;
  left: 0;
  height: 100%;
  filter: alpha(opacity=80); /* yeah IE! */
  opacity: 0.8;
  /* .neutral is the default */
  background-color: #ddd;
  border-right: 1px solid #666;
}
.progress.overage.neutral-overage .container .progress-bar {
  background-color: #ddd;
  border-right: 1px solid #666;
}
.progress.ok .container .progress-bar,
.progress.overage.ok-overage .container .progress-bar {
  background-color: #dfd;
  border-right: 1px solid #393;
}
.progress.error .container .progress-bar,
.progress.overage.error-overage .container .progress-bar {
  background-color: #fdd;
  border-right-color: #c66;
}
.progress .container .status-text {
  display: inline-block;
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  color: #000;
  margin: 0;
  padding: 0;
}
.progress .container .status-text-spacer {
  margin: 0 5px;
}
</pre>
<p>So now instead of just doing this (which will render a neutral bar):</p>
<pre>&lt;span class="progress" rel="#raised / goal#"&gt;
  #dollarFormat(raised)# of #dollarFormat(goal)# raised...
&lt;/span&gt;
</pre>
<p>You can control the various presentation very granularly (red below 100% and green from 100% up):</p>
<pre>&lt;span class="progress <span style="color: #ff0000;">error ok-overage</span>" rel="#raised / goal#"&gt;
  #dollarFormat(raised)# of #dollarFormat(goal)# raised...
&lt;/span&gt;
</pre>
<p>The original version rendered green bars for everything, with this incarnation you can have neutral, ok, or error semantics for your bars.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.barneyb.com/barneyblog/2011/05/17/even-better-inline-progress-bars/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>setTimeout/setInterval Bug in Android 2.2 WebKit</title>
		<link>https://www.barneyb.com/barneyblog/2011/02/20/settimeout-bug-in-android-2-2/</link>
		<comments>https://www.barneyb.com/barneyblog/2011/02/20/settimeout-bug-in-android-2-2/#comments</comments>
		<pubDate>Mon, 21 Feb 2011 07:33:31 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1645</guid>
		<description><![CDATA[If you develop web apps for Android devices, there's a really gnarly bug you should be aware of: setTimeout and setInterval will never fire while the soft keyboard is open.Â  When the soft keyboard closes, all the queued up timeouts/intervals will unspool all at once, which is not terribly good, but better than them disappearing.Â  [...]]]></description>
			<content:encoded><![CDATA[<p>If you develop web apps for Android devices, there's a <a href="http://code.google.com/p/android/issues/detail?id=13540">really gnarly bug</a> you should be aware of: setTimeout and setInterval will never fire while the soft keyboard is open.Â  When the soft keyboard closes, all the queued up timeouts/intervals will unspool all at once, which is not terribly good, but better than them disappearing.Â  Note that it's the <em>timer checks</em> which are put into stasis, so you'll get at most one fire per setInterval, regardless of how long the keyboard was open (e.g., a 500ms interval will fire once, even if they keyboard is open for many seconds).</p>
<p>The most damning place this bug rears it's head is with autocompletion scripts on form fields.Â  You necessarily focus the field, bringing up the keyboard, in order to interact with it.Â  But as soon as you do all your JS can't do timer-based actions.Â  If you have a naive implementation that responds directly to key events, you'll be ok, but particularly if you're doing remote data retrieval for the autocomplete dropdown, you're going to delay responding to key events so you can collapse multiple events which are temporally close into a single remote call.Â  Which means you're never going to get to making that call until the user removes focus from the form field.Â  Oof.</p>
<p>In many cases, you <strong>can</strong> code around the bug with a little trickery.Â  For example, on a key event, you can record the current time, and then set your timeout.Â  On the next key event, if your timeout hasn't triggered, but it's been longer than the delay, you can switch the autocomplete mechanism into direct invocation mode.Â  Hardly ideal, as it requires manual coding for every timeout/interval structure you have in your code, but it does work, and doesn't make everyone pay the price for the Android bug.Â  For simple autocomplete, this works reasonably well, but it doesn't do so hot for more complex situations.</p>
<p>Hopefully Google will get the bug (<a href="http://code.google.com/p/android/issues/detail?id=13540">Issue 13540</a>) fixed in the near future, and carriers will allow the update to trickle out to their devices.Â  But even in the best case scenario, that's still months away.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.barneyb.com/barneyblog/2011/02/20/settimeout-bug-in-android-2-2/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Inline JS Progress Bars</title>
		<link>https://www.barneyb.com/barneyblog/2010/09/15/inline-js-progress-bars/</link>
		<comments>https://www.barneyb.com/barneyblog/2010/09/15/inline-js-progress-bars/#comments</comments>
		<pubDate>Wed, 15 Sep 2010 23:42:28 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1581</guid>
		<description><![CDATA[2011-05-17: A richer version is available at http://www.barneyb.com/barneyblog/2011/05/17/even-better-inline-progress-bars/.
If you've ever built a web app that does background and/or batch processing of stuff, you've invariably created a bit of markup like this:
#numberFormat(sentEmailCount, ',')# of #numberFormat(emailCount, ',')# sent...
which then renders like this:

Wouldn't it be nice to create this markup (simply with a wrapping span) instead:

#numberFormat(sentEmailCount, ',')# of [...]]]></description>
			<content:encoded><![CDATA[<p style="padding-left: 30px;">2011-05-17: A richer version is available at <a href="http://www.barneyb.com/barneyblog/2011/05/17/even-better-inline-progress-bars/">http://www.barneyb.com/barneyblog/2011/05/17/even-better-inline-progress-bars/</a>.</p>
<p>If you've ever built a web app that does background and/or batch processing of stuff, you've invariably created a bit of markup like this:</p>
<pre>#numberFormat(sentEmailCount, ',')# of #numberFormat(emailCount, ',')# sent...</pre>
<p>which then renders like this:</p>
<p><img class="aligncenter size-full wp-image-1582" title="progress-1" src="http://www.barneyb.com/barneyblog/wp-content/uploads/2010/09/progress-1.png" alt="" width="183" height="49" /></p>
<p>Wouldn't it be nice to create this markup (simply with a wrapping span) instead:</p>
<pre><span class="progress" rel="#sentEmailCount / emailCount#">
#numberFormat(sentEmailCount, ',')# of #numberFormat(emailCount, ',')# sent...
</span></pre>
<p>and have it render like this:</p>
<p><img class="aligncenter size-full wp-image-1583" title="progress-2" src="http://www.barneyb.com/barneyblog/wp-content/uploads/2010/09/progress-2.png" alt="" width="199" height="51" /></p>
<p>You think it would be?  I thought so too, so I wrote a simple little jQuery snippet to do exactly that.  Here she be:</p>
<pre>jQuery(".progress").each(function() {
  var $this = jQuery(this)
  var frac = parseFloat($this.attr("rel"))
  if (isNaN(frac)) {
    return // nothing to do
  }
  $this.css({
      display: "inline-block",
      position: "relative",
      border: "1px solid #ccc",
      backgroundColor: "#eee",
      color: "#eee",
      padding: "0 5px"
    })
    .append(''
      + '<span style="display:inline-block;position:absolute;z-index:0;top:0;left:0;'
        + 'height:100%;width:' + (frac * 100) + '%;background-color:#cfc;"></span>'
      + '<span style="display:inline-block;position:absolute;z-index:1;top:0;left:5px;'
        + 'color:#000;">' + $this.html() + '</span>'
    )
})</pre>
<p>The basic idea is to take the span, make it render like a bar, hide the text (same color as the background), and then append a pair of absolutely positioned spans to draw the progress bar and then lay the existing content atop it.  It's not terribly robust, but it works like a champ if your needs are simple, and the code is straightforward enough that it should be easy to tweak if you have specific needs.</p>
<p>Use it as is, hack it up, turn it into a massively popular plugin and make a bazillion dollars.  Have fun. News of the gambling industry <a href="https://logincasino.org">here.</a> Read more.</p>
<p><span style="text-decoration: underline;"><strong>Update:</strong></span> here's the same functionality, repackaged into separate JS and CSS so it's easier to deal with.  JS first:</p>
<pre>jQuery(".progress").each(function() {
  var $this = jQuery(this)
  var frac = parseFloat($this.attr("rel"))
  if (isNaN(frac) || frac > 1) {
    return // nothing to do
  }
  $this.addClass("container")
    .append(''
      + '<span class="progress-bar" style="width:' + (frac * 100) + '%;"></span>'
      + '<span class="status-text">' + $this.html() + '</span>'
    )
})</pre>
<p>And the CSS:</p>
<pre>.progress.container {
  display: inline-block;
  position: relative;
  border: 1px solid #ccc;
  background-color: #eee;
  color: #eee;
  padding: 0 5px;
}
.progress.container .progress-bar {
  display: inline-block;
  position: absolute;
  z-index: 0;
  top: 0;
  left: 0;
  height: 100%;
  background-color: #dfd;
  border-right: 1px solid #6c6;
}
.progress.container .status-text {
  display: inline-block;
  position: absolute;
  z-index: 1;
  top: 0;
  left: 5px;
  color: #000;
}</pre>
]]></content:encoded>
			<wfw:commentRss>https://www.barneyb.com/barneyblog/2010/09/15/inline-js-progress-bars/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>On-The-Fly YUI Compressor</title>
		<link>https://www.barneyb.com/barneyblog/2010/08/03/on-the-fly-yui-compressor/</link>
		<comments>https://www.barneyb.com/barneyblog/2010/08/03/on-the-fly-yui-compressor/#comments</comments>
		<pubDate>Wed, 04 Aug 2010 01:35:14 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[cfml]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1570</guid>
		<description><![CDATA[A couple years ago I wrote about using YUI Compressor to do built-time aggregation and compression of static assets.Â  That works well and good if you have a build environment, but that's not always the case.Â  So, still using YUI Compressor, I set up a simple script that'll do runtime aggregation and compression of assets [...]]]></description>
			<content:encoded><![CDATA[<p>A couple years ago <a href="http://www.barneyb.com/barneyblog/2008/04/08/build-time-aggregation-of-jscss-assets/">I wrote about</a> using <a href="http://developer.yahoo.com/yui/compressor/">YUI Compressor</a> to do built-time aggregation and compression of static assets.Â  That works well and good if you have a build environment, but that's not always the case.Â  So, still using YUI Compressor, I set up a simple script that'll do runtime aggregation and compression of assets using my favorite mod_rewrite-based file caching mechanism.</p>
<p>The basic idea is that your HTML includes a reference to "agg_standard.js", which is an alias for whatever JS files you need for your standard user (as opposed to a mobile user, for example).Â  That request comes to your server and if the file exists, gets served back like any other JS file.Â  If it <em>doesn't</em> exist, however, mod_rewrite will forward it to a CFM page to generate it:</p>
<pre>RewriteCondÂ Â Â Â  %{REQUEST_FILENAME}Â Â Â Â  !-s
RewriteRuleÂ Â Â Â  (/my_app)/static/(agg_.*)\.js$ $1/aggregator.cfm?name=$2.js
</pre>
<p>In our example, the request passed to aggregator.cfm would have "agg_standard.js" as the 'name' attribute, which is how we'll figure out what we need to aggregate together:</p>
<pre>switch (url.name) {
case "<span style="color: #0000ff;">agg_standard.js</span>":
  files = [
    "jquery/jquery" &amp; (request.isProduction ? ".min" : ""),
    "jquery/jquery.ui" &amp; (request.isProduction ? ".min" : ""),
    "yui/yahoo-min",
    "yui/event-min",
    "yui/history-min",
    "util",
    "jscalendar/calendar",
    "jscalendar/lang/calendar-en",
    "jscalendar/calendar-setup",
    "dracula/raphael-min",
    "dracula/graffle",
    "dracula/graph"
  ];
  break;
case "agg_iphone.js":
  files = [
    "jquery/jquery" &amp; (request.isProduction ? ".min" : ""),
    "yui/yahoo-min",
    "yui/event-min",
    "yui/history-min",
    "util"
  ];
  break;
}
</pre>
<p>The important bits, of course, is the Groovy script that actually does the aggregation and compression.Â  It uses YUI Compressor, so you'll need to have the yiucompressor-x.y.z.jar file on your classpath (probably in /WEB-INF/lib).Â  Here it is:</p>
<pre>import com.yahoo.platform.yui.compressor.*

sw = new StringWriter()
<span style="color: #0000ff;">variables.files</span>.each {
  def f = new File(variables.STATIC_DIR + it + '.js')
  sw.append('/* ').append(f.name).append(' */\n')
  if (! it.endsWith(".min") &amp;&amp; ! it.endsWith("-min")) {
    def compressor = new JavaScriptCompressor(f.newReader(), null)
    compressor.compress(sw, -1, false, false, false, false)
  } else {
    sw.append(f.text.trim())
  }
  sw.append('\n')
}
variables.buffer = sw.toString()</pre>
<p>Pretty straightforward: it just loops over the files, using a StringWriter to build up the aggregated buffer.Â  Each file is either compressed into the Writer or simply appended based on whether the file has already been minified (based on ".min" or "-min" in the filename).Â  Each file also gets a comment label in the Writer above it's contents so that the aggregated file is a little easier to parse (at the expense of a few extra bytes).Â  Once done, the Writer's contents are stored in the 'buffer' variable caching on the filesystem:</p>
<pre>&lt;cfset fileWrite(STATIC_DIR &amp; url.name, buffer) /&gt;
&lt;cflocation url="#url.name#" addtoken="false" /&gt;</pre>
<p>You'll notice that I'm not streaming the buffer back out to the user, but instead 302-ing back to the same URL.Â  This is important.Â  The reason is that Apache does a whole bunch of stuff to optimize static assets, and if I serve the content back with CFCONTENT, I'll miss out on all of that.Â  Yes, the 302 has a little bit of overhead on the initial page load, but it reduces the total transfer size by several hundred KB (because of the GZIPping), and avoids a rerequest on the next page load (because of the cache headers).Â  So it's completely worth it, the moreso because this is an application likely to generate extended usage rather than a content-centric site that is likely to see single-page "bounce" visits from search engines.</p>
<p>The last piece of the puzzle is handling versioning of your assets.Â  When you change your JS file, you necessarily have to invalidate your cache (by deleting the files) so the aggregated version can be rebuilt with the new JS.Â  The easiest way to do that is to use psuedo-versioning of your assets.Â  You'll see a lot of sites will add a timestamp or a version number to their files (e.g., "arc/yui/reset_2.6.5.css" from Yahoo.com) so that when the update the file it gets a new filename, and is therefore redownloaded by everyone (because it doesn't exist in their cache).Â  That's great, but it means you have to rename your files all the time which is kind of a pain.Â  But you can fake it:</p>
<pre>&lt;cfset STATIC_ASSET_VERSION = 15 /&gt;
&lt;script type="text/javascript" src="static/agg_standard_#STATIC_ASSET_VERSION#.js"&gt;&lt;/script&gt;
</pre>
<p>That'll generate a request to "agg_standard_15.js", as you might imagine, which isn't going to work so well.Â  But we can just change the 'switch' line from the first snippet to this:</p>
<pre>switch (REReplace(url.name, "^(agg_.*?)(_[0-9]+)?\.js$", "\1.js")) {</pre>
<p>Now it'll strip out that "fake" number string and switch on just "agg_standard.js", which is what we want.Â  But that 'fileWrite' call later will still use the full filename (with the number embedded).Â  That way subsequents will get the filesystem cacheing, the headers and GZIPping from Apache, and all the other love.Â  And when you rev your files, you need only increment the STATIC_ASSET_VERSION variable and you'll have a brand new set of virtual URLs for all your assets, no fuss, no muss.</p>
<p>Oh, and just in case you're wondering, the aggregation and compression is <em>fast</em>.Â  If you've ever used the command line or Ant task, you might fear that it's slow, but most of the time you see there is from the JVM spinning up, not the actual compression.Â  Since this is all in-JVM, you don't pay any of that cost.Â  It's certainly not fast enough to have it run in production on every request (hence the file caching), but it's totally reasonable to do on your production box as part of deploying a new version of the app.Â  It's also probably fast enough to have running every request on your internal test/staging boxes, though that'll depend on how much you're aggregating/compressing among other things.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.barneyb.com/barneyblog/2010/08/03/on-the-fly-yui-compressor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Improving the JSON/P Twitter Widget</title>
		<link>https://www.barneyb.com/barneyblog/2010/01/15/improving-the-jsonp-twitter-widget/</link>
		<comments>https://www.barneyb.com/barneyblog/2010/01/15/improving-the-jsonp-twitter-widget/#comments</comments>
		<pubDate>Fri, 15 Jan 2010 17:28:18 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[meta]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1173</guid>
		<description><![CDATA[For quite some time now I've had a sidebar widget that displays an excerpt of my Twitter feed on my blog.Â  It uses the standard Twitter JSON/P interface for loading the tweets and then a Twitter-provided script (http://twitter.com/javascripts/blogger.js) for rendering them on the page.Â  Unfortunately the default installation instructions would have you set up something [...]]]></description>
			<content:encoded><![CDATA[<p>For quite some time now I've had a sidebar widget that displays an excerpt of <a href="http://twitter.com/barneyb">my Twitter feed</a> on my blog.Â  It uses the standard Twitter JSON/P interface for loading the tweets and then a Twitter-provided script (<a href="http://twitter.com/javascripts/blogger.js">http://twitter.com/javascripts/blogger.js</a>) for rendering them on the page.Â  Unfortunately the default installation instructions would have you set up something like this:</p>
<pre>&lt;ul id="twitter_update_list"&gt;&lt;/ul&gt;
&lt;script type="text/javascript" src="http://twitter.com/javascripts/blogger.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="http://twitter.com/statuses/user_timeline/barneyb.json?callback=twitterCallback2&amp;count=5"&gt;&lt;/script&gt;</pre>
<p>That works, but it puts the JSON/P call (the second SCRIPT tag) into the main page flow, so anything after the call won't render until the script load is complete.Â  When Twitter is slow/down it can cause problems, but even when it's fast you still get a brief hang with a partial page before the rest snaps into view.Â  Fortunately, the solution is simple: do the call on DOMLoaded instead of inline.Â  I'm using jQuery, but the principle is the same regardless of what framework you use (or none at all):</p>
<pre>&lt;ul id="twitter_update_list"&gt;&lt;li class="load-ind"&gt;&lt;img src="http://www.barneyb.com/s/loading_indicator.gif" /&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;script type="text/javascript"&gt;
jQuery(function() {
  jQuery("body").append(
    '&lt;sc'+'ript type="text/javascript" src="http://twitter.com/javascripts/blogger.js"&gt;&lt;/sc'+'ript&gt;' +
    '&lt;sc'+'ript type="text/javascript" src="http://twitter.com/statuses/user_timeline/barneyb.json?callback=twitterCallback2&amp;amp;count=5"&gt;&lt;/sc'+'ript&gt;'
  );
});
&lt;/script&gt;</pre>
<p>Now the SCRIPT tags will be written on DOMReady, allowing the full page to render before the calls to Twitter are dispatched.Â  This gives a faster user experience, though you do get a reflow of the sidebar when the statuses load.Â  This has become so prevalent on modern websites, however, that I don't think it's worth caring about, but if you're concerned, you could specify a height to the loading indicator LI that is approximately what the rendered tweet list will be to mitigate the effect.Â  It won't avoid the reflow, but it'll make things move less because the space will be reserved up front.</p>
<p>I've also added a simple "loading" image to the UL that the JS will place the tweets in.Â  The way blogger.js is written, any content within the UL will be removed (i.e. the tweets overwrite the content; they're not appended to the content), so no need to explicitly remove the image or it's container LI.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.barneyb.com/barneyblog/2010/01/15/improving-the-jsonp-twitter-widget/feed/</wfw:commentRss>
		<slash:comments>40</slash:comments>
		</item>
		<item>
		<title>jQuery TableSorter Comma Parser</title>
		<link>https://www.barneyb.com/barneyblog/2009/06/03/jquery-tablesorter-comma-parser/</link>
		<comments>https://www.barneyb.com/barneyblog/2009/06/03/jquery-tablesorter-comma-parser/#comments</comments>
		<pubDate>Wed, 03 Jun 2009 17:29:40 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=992</guid>
		<description><![CDATA[Last night I needed a simple HTML table with sortable columns, and after a quick Googling, found the TableSorter jQuery plugin.Â  I'd come across it before, but never actually used it.Â  Added a class to my table (for targeting and to leverage the CSS that it comes with), added the required line of JavaScript to [...]]]></description>
			<content:encoded><![CDATA[<p>Last night I needed a simple HTML table with sortable columns, and after a quick Googling, found the <a href="http://tablesorter.com/docs/">TableSorter</a> jQuery plugin.Â  I'd come across it before, but never actually used it.Â  Added a class to my table (for targeting and to leverage the CSS that it comes with), added the required line of JavaScript to initialize it, and I was up and running.</p>
<p>As I tuned the presentation, however, I ran into a couple issues.Â  First, the included styling sets the table width to 100%, but that was easily fixed with a .css("width", "auto") call.Â  I also wanted alternating row backgrounds, and a quick look showed that there is a 'zebra' widget built in that does exactly that, it just has to be enabled.</p>
<p>The biggest problem was with number formatting.Â  Some of the sortable columns contained large numbers, and when I wrapped the output with numberFormat() to add commas, the sorting reverted to textual instead of numeric.Â  A quick look through the source showed why: the 'digit' parser respects only numbers, the decimal point, and an optional leading +/-.Â  So the commas made it skip any numeric sorting and fall back to textual sorting.Â  Fortunately, the fix was simple.Â  Before initializing the table, I just added a new parser that piggybacks on the 'digit' parser's implementation:</p>
<pre>jQuery.tablesorter.addParser({
  id: "commaDigit",
  is: function(s, table) {
    var c = table.config;
    return jQuery.tablesorter.isDigit(s<span style="color: #0000ff;">.replace(/,/g, "")</span>, c);
  },
  format: function(s) {
    return jQuery.tablesorter.formatFloat(s<span style="color: #0000ff;">.replace(/,/g, "")</span>);
  },
  type: "numeric"
});</pre>
<p>The only difference between it and the 'digit' parser is that it strips all commas from the string before handing off to the backing methods (the <span style="color: #0000ff;">blue</span> stuff).Â  Works like a champ.Â  The separator should probably be parameterized like the decimal stuff is, but I only deal with comma separators in this app, so I didn't bother.</p>
<p>The only thing I wasn't able to figure out (though I'll admit I didn't try very hard) was setting the initial sort to be descending, so when you click a column header for the first time it does a descending sort rather than ascending (with subsequent clicks reversing the sort as normal).Â  Setting up the on-initialization sort to be descending is simple, but I didn't see a way to parameterize user-triggered sorts.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.barneyb.com/barneyblog/2009/06/03/jquery-tablesorter-comma-parser/feed/</wfw:commentRss>
		<slash:comments>33</slash:comments>
		</item>
		<item>
		<title>jQuery.bind() Data</title>
		<link>https://www.barneyb.com/barneyblog/2009/04/10/jquery-bind-data/</link>
		<comments>https://www.barneyb.com/barneyblog/2009/04/10/jquery-bind-data/#comments</comments>
		<pubDate>Fri, 10 Apr 2009 07:11:43 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=818</guid>
		<description><![CDATA[If you only ever use the type-specific event helpers (.click(), .load(), .change(), etc.), you're potentially missing out on a really handy feature of jQuery: bind data.Â  Bind data is data associated with the bound handler function, available to every invocation of it, but not in any "normal" variable scope.Â  It's kind of like currying, except [...]]]></description>
			<content:encoded><![CDATA[<p>If you only ever use the type-specific event helpers (.click(), .load(), .change(), etc.), you're potentially missing out on a really handy feature of jQuery: bind data.Â  Bind data is data associated with the bound handler function, available to every invocation of it, but not in any "normal" variable scope.Â  It's kind of like currying, except with an event attribute instead of arguments.Â  You declare bind data like this (as the optional middle parameter of the .bind() method):</p>
<pre>jQuery("#selector").bind("click", <span style="color: #0000ff;">{name: "barney"}</span>, clickHandler);</pre>
<p>And then you use it like this:</p>
<pre>function clickHandler(<span style="color: #ff0000;">e</span>) {
  alert(<span style="color: #ff0000;">e</span>.<span style="color: #0000ff;">data.name</span>);
}</pre>
<p>The '<span style="color: #ff0000;">e</span>' variable is the standard jQuery event object passed to all event handler functions.Â  The '<span style="color: #0000ff;">data</span>' key within it contains the bind data created when the handler function was bound, if any.Â  Even this simple case is potentially interesting because you're controlling the alert message at bind-time, not in the function itself, without the extra wrapper function you'd otherwise use:</p>
<pre>jQuery("#selector").click(function() { clickHandler("barney")});
function clickHandler(name) {
  alert(name);
}</pre>
<p>But that's not the really useful bit.Â  What you might not have noticed about the two examples is that in the first, the string "barney" is evaluated once (at bind time), but in the second it's evaluated every time the click event is dispatched.Â  No big deal for a string, but for complex expressions that can create a performance issue.Â  Since event handlers are typically executed more than once (and often a <em>lot</em> more than once), moving stuff out of the callback and into bind data can actually yield a noticeable difference in UI performance, as I learned this evening.</p>
<p>In a photo viewing app I built I'm using JS to display multiple photos sequentially via a single IMG tag, just swapping out the source, width/height, title, etc.Â  I wanted to center the photo tag, but couldn't do it with CSS because some dynamically-sized absolute-positioned elements needed to be accounted for, as well as the size of the photo itself.Â  I'd originally written a handler that looked something like this:</p>
<pre>jQuery("#theImage").<span style="color: #339966;">load</span>(function() {
  var base = getOffsets("content");
  var total = jQuery("#floatyControlBar").position().left;
  var object = jQuery("#imageContainer");
  var xd = Math.max(0, (total - object.width()) / 2)
  object.css({
    left: base.x + xd + "px",
    top: base.y + "px"
  });
});</pre>
<p>It worked great, except that it made a noticeable jump from the old position to the new position when the image loaded.Â  I first tried the Flash-style "add an animation to mask the slowness", but the browser couldn't handle sliding a big image around very gracefully, so it wasn't any better.Â  Enter bind data.Â  Changing the handler to this resolved the issue:</p>
<pre>jQuery("#theImage").<span style="color: #339966;">bind</span>(<span style="color: #339966;">"load"</span>, {
  base: getOffsets("content"),
  total: jQuery("#floatyControlBar").position().left,
  object: jQuery("#imageContainer")
}, function(e) {
  var xd = Math.max(0, (e.data.total - e.data.object.width()) / 2);
  e.data.object.css({
    left: e.data.base.x + xd + "px",
    top: e.data.base.y + "px"
  });
});</pre>
<p>Basically I just changed it so most of the data collection happens at bind time (so it only happens once) and as little as possible happens when the handler is actually called.Â  It's a simple optimization, but it eliminated the visible jump.Â  The code has paid a slight price in readability, no question there, but while we all tout readable code as a hallmark of good code, user experience is still what software is about, so the latter wins.</p>
<p>Of course, you could achieve the same effect by setting the base, total, and object variables into the scope containing the .bind() call so the handler can access them via closure-nature.Â  But that pollutes (breaks encapsulation), so using bind data is a better solution in most cases.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.barneyb.com/barneyblog/2009/04/10/jquery-bind-data/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>jQuery.live(&quot;click&quot;) Gotcha</title>
		<link>https://www.barneyb.com/barneyblog/2009/04/09/jquery-live-click-gotcha/</link>
		<comments>https://www.barneyb.com/barneyblog/2009/04/09/jquery-live-click-gotcha/#comments</comments>
		<pubDate>Fri, 10 Apr 2009 00:42:07 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=815</guid>
		<description><![CDATA[If you're using the new jQuery.live("type", fn) handlers in jQuery 1.3, be careful with the "click" event type.Â  Unlike jQuery.bind("click", fn) and jQuery.click(fn), jQuery.live("click", fn) doesn't distinguish between mouse buttons.Â  So while the "normal" handlers will only trigger when the left button is used, the live handler will trigger on any button.
The workaround is pretty [...]]]></description>
			<content:encoded><![CDATA[<p>If you're using the new jQuery.live("type", fn) handlers in jQuery 1.3, be careful with the "click" event type.Â  Unlike jQuery.bind("click", fn) and jQuery.click(fn), jQuery.live("click", fn) doesn't distinguish between mouse buttons.Â  So while the "normal" handlers will only trigger when the left button is used, the live handler will trigger on any button.</p>
<p>The workaround is pretty simple, though kind of kludgey: add the stuff in bold to your handler.</p>
<pre>jQuery("#selector").live("click", function(event) {
  <strong>if (event.button != 0) {
    // wasn't the left button - ignore
    return true;
  }</strong>
  // do some stuff
  return false; // "capture" the click
});</pre>
<p>Hopefully this will be addressed in the future, but for right now you have to pick between manual button detection and sticking with the "normal" handlers.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.barneyb.com/barneyblog/2009/04/09/jquery-live-click-gotcha/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>HTTP is an API</title>
		<link>https://www.barneyb.com/barneyblog/2009/03/17/http-is-an-api/</link>
		<comments>https://www.barneyb.com/barneyblog/2009/03/17/http-is-an-api/#comments</comments>
		<pubDate>Wed, 18 Mar 2009 05:11:20 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[cfml]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=727</guid>
		<description><![CDATA[Ray Camden posted an interesting article over on InsideRIA about expanding short urls using jQuery and ColdFusion.Â  After reading the article, I thought he was overcomplicating things somewhat by relying on the url shortening services' APIs to do the lookups.Â  Yes, that's what APIs are for, but for this case, HTTP happens to be a [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.coldfusionjedi.com/">Ray Camden</a> posted an interesting article over on InsideRIA about <a href="http://www.insideria.com/2009/03/mimicing-twitterfalls-short-ur.html">expanding short urls</a> using jQuery and ColdFusion.Â  After reading the article, I thought he was overcomplicating things somewhat by relying on the url shortening services' APIs to do the lookups.Â  Yes, that's what APIs are for, but for this case, HTTP happens to be a perfectly sufficient API, it's consistent across services, it requires no special interface code, and it's crazy simple.Â  I commented to that effect, and then decided I ought to put myÂ  money where my mouth is.</p>
<p>To that end, I wrote a <a href="http://barneyb.com/r/expandurl.cfm">small demo app</a> that uses several different bits of tech to get the job done (render an ugly page with auto-expanding URLs).Â  It uses JSON/P to load some tweets containing shortened URLs from the Twitter Search API, writes them to the DOM with jQuery after wrapping the URLs with A tags (the part Ray did manually), and then wires a jQuery mouseover listener to trigger expansion (virtually identical to Ray's).Â  Of course, the server side is what I was actually interested in, so here it is:</p>
<pre>&lt;cfhttp method="get"
  url="#attributes.expand#"
  redirect="false"
  result="cfhttp" /&gt;
&lt;cfoutput&gt;#cfhttp.responseHeader["Location"]#&lt;/cfoutput&gt;</pre>
<p>Pretty simple, eh?Â  Both a rapier and a howitzer can kill a man; picking the right one is important.</p>
<p>Why does this work?Â  Because every service is just doing a HTTP 301 with a Location header from the short URL to the full URL.Â  Some of them expose APIs for creating and querying the short URLs, but for this task, we don't actually care, we only care about the Location header.Â  The only special processing require is for preview.tinyurl.com links; I simply convert them to normal tinyurl.com links so they do the "right" thing.</p>
<p>As always, there is <a href="http://barneyb.com/r/expandurl.cfm#source">full source</a> (all 55 lines of it, in one CFM template) displayed beneath the app itself should you wish to see all the details.Â  The JavaScript stuff is far more complex than needed for a demo app, but I wanted to do a little experimentation on the client side as well as the server-side stuff.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.barneyb.com/barneyblog/2009/03/17/http-is-an-api/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
