MouseIntent jQuery Plug-in

Categories: HTML/ColdFusion, jQuery, JavaScript

One common pattern I've seen in usability, is that users don't always have great control over their mouse. It's easy to accidentally overshoot a target area with you mouse, which is really frustrating with things like nested dropdown menus, where accidentally mousing away from a target area may end up collapsing/closing the open menu.

Giva's recently released the MouseIntent jQuery Plug-in which aims to give developers a way to control this behavior. It works by monitoring an invisible border around the element to see what the user appears to be doing. If a user quickly moves back into the original element, then then no mouseaway event is ever fired. The plugin has a number of settings that allow you to control the behavior—such as monitoring whether or not the user is still moving the mouse in the invisible border area.

I've used the plugin in one of our dropdown menus that has nested menus and it's really helped to improve our user's experience.

Maskerade Date Mask Input Plugin

Categories: HTML/ColdFusion, jQuery, JavaScript

Giva has released released a new plugin (Maskerade jQuery Plug-in) which can convert a normal text field into a power date mask input field. The plugin supports a large array of date masks (even quarters) and even supports copy/paste. Here's a list of some of it's key features:

  • Keypress validation (ie. you don't need to submit the form for the mask to be applied)
  • Full keyboard support, including number to text-date interpretation (eg. typing 6 for a month will show June) and number-entry interpretation (eg. typing 02 in a yyyy date field will be interpreted as 2002)
  • Full mouse support
  • Masks can be defined as attributes of the input field; individual jQuery mask calls are not needed
  • Includes time-mask capability, with a date or alone
  • Default values and masks set as placeholders in the input field
  • Ability to set min and max dates allowed on a field
  • Allows for enforcing relational validation (ie. date1 must be before date2)
  • Automatic adjusting for invalid dates (eg. Feb 29, 2001 is adjusted to Feb 28, 2001)
  • Each date/time part fully highlighted on focus
  • Automatic tabbing to next date/time part once interpreted (eg. typing 2 in a "mm" date part will automatically tab you to the next date part), which allows quick keyboard entry
  • Allows for dask masks by quarters (eg. Q1, Q2, etc.)
  • Ability to support multiple languages
  • Custom event handlers; for example, a single keystroke can be defined to change the date to the current date
  • Detach/attach Maskerade behavior from the element

We have actually been using this plugin in production for a long time with great success.

ColdFusion 9/10 generating hidden exceptions when using Images stored in RAM disk

Categories: HTML/ColdFusion

We recently discovered an issue in ColdFusion 9+ when images that were temporarily stored in the RAM disk, but later removed would start throwing exceptions in the application.log, exception.log and coldfusion-out.log. The code itself would run just fine, so the issue is a bit masked because you won't see it unless you're monitoring your log files.

What we were seeing was a lot of errors like the following being thrown throughout our logs:

Could not read from ""ram:///797C39D0-CAE4-21F9-D573CFDC3FE7482E.jpg"" because it is a not a file.

When we tracked down why the log entries were being written, we discovered that the following workflow was causing the problem:

  1. Called a UDF to return a reference to a ColdFusion image object. The UDF would:
    • Use the RAM disk to convert the image into a common image format
    • It would then remove the temp file from the RAM disk
    • It would scale the image
    • Finally, it returned a reference to the ColdFusion image
  2. We would then attempt to write the image object to disk

It was when trying to write the image to disk, we'd start to see 3 exceptions being logged, but the code would generate the expected output. What appears to be happening, is that internally ColdFusion is trying to access the original "source" of the file for some reason.

What we did to fix the issue, was to return a new copy of the image using imageNew(imageGetBufferedImage(source)). What this does is create a copy of the image that no longer references any file on disk, but creates an image purely in RAM.

I'm sure this isn't a very common problem, but if you found that you're using Dave Ferguson's ColdFusion 9 PNG image processing fix you may find yourself running into this issue.

I've filed a Bug #3690487 with Adobe. This problem does affect ColdFusion 9 and 10, so if you think Adobe should fix it, make sure to vote it up!

Windows XP Windows Update issue (i.e. the svchost.exe 100% CPU issue)

Categories: Technology

While I left Windows XP behind a long time ago as my main operating system, I still run numerous virtual machines running Windows XP in order to test with older versions of Internet Explorer. One problem I've been running into with my VMs is when the Windows Update was running, the CPU would get pegged at 99% – 100% usage, which makes Windows unusable.

I tried a number of things to work around the problem to no avail and finally just decided to shut down Windows Update in order to make the VMs usable. However, that leaves my unable to patch my VMs to make sure they're completely up-to-date.

Today I finally had to update one of my VMs, so I really needed to resolve the problem. After some reading, I found that Microsoft is aware of the problem and that it relates to parsing the update tree to find out which updates are needed. The good news is I found a fix that seems to work for me. The trick is to manually update 2 different Security Updates.

Here's how I finally resolved the problem:

  1. Disable automatic Windows Updates
  2. If your CPU is pegged, open the Windows Task Manager (CTLR+ALT+DEL) and kill the svchost.exe pegging the CPU
  3. Install the following updates, rebooting after each one:
  4. Manually run the Windows Update, it should now run normally
  5. If you wish, enable automatic Windows Updates

Hope that helps someone!

Update to the Linkselect jQuery Plugin (v1.5.11)

Categories: Source Code, jQuery, JavaScript

I just pushed out an update to the Linkselect jQuery Plugin.


v1.5.11 (2013-07-09)

  • Linkselect plugin now annouces an "update" event whenever the value changes--which allows you to set up listeners for when the value has changed.

Update to the mcDropdown jQuery Plugin (v1.3.3)

Categories: Source Code, jQuery, JavaScript

I just pushed out an update to the mcDropdown jQuery Plugin.


v1.3.3 (2013-07-09)

  • Fixed issue where menu option underneath a selected sub-menu option would sometimes cause the option to disappear from the menu
  • Fixed issue where sub-menus would sometimes still be open after re-opening a menu

Using queryName.columnName shorthand can generate errors with <cfqueryparam />

Categories: HTML/ColdFusion

Yesterday I ran into a very strange bug with ColdFusion 9 and I thought it worth blogging about. I think this probably affects earlier versions of the product, but I haven't tested to confirm.

What was happening is whenever I tried executing a specific query, I was seeing the following error:

Invalid data coldfusion.sql.QueryColumn@540350 for CFSQLTYPE CF_SQL_INTEGER

The error had me very perplexed, because the variable that it was complaining about I knew was an integer. When displayed on screen, it showed as an integer. It would even return true when passed to the isNumeric() function. After spending way to much time on the issue, I finally track down the root problem.

What was happening is the variable's value was coming from a ColdFusion query, that I was converting to a structure. Since the query was designed to return at most a single row, I was using the shortcut notation of queryName.columnName to update the variable. I've used this shorthand plenty in the past, because it will either display the value in the first row (when not inside a <cfoutput query=""> or <cfloop query="">) or it will display an empty string if the query returned no rows. This has generally worked fine for me, but apparently this ends up storing a reference to the query object, instead of copying the value directly—which <cfqueryparam /> did not like.

The fix was pretty straightforward, all I need to do was to change my code to reflect grabbing the value from the first row of the dataset: queryName.columnName[1].

My original code looked like this:

<cfquery name="data" attributeCollection="#Application.dsn.getAttributes()#">
    Name, Email, Phone
    EmployeeId = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.EmployeeId#" />

<!---// get the column names from the query //--->
<cfset columns = getMetaData(data) />

<!---// return the preferences as a struct //--->
<cfloop index="column" array="#columns#">
  <cfset results[column.Name] = data[column.Name] />

All I did was change the code to:

<cfquery name="data" attributeCollection="#Application.dsn.getAttributes()#">
    Name, Email, Phone
    EmployeeId = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.EmployeeId#" />

<!---// get the column names from the query //--->
<cfset columns = getMetaData(data) />

<!---// return the preferences as a struct //--->
<cfloop index="column" array="#columns#">
  <cfset results[column.Name] = data[column.Name][1] />
You'll notice the only change is the [1] after data[column.Name].

What I learned from this is that I shouldn't trust using the queryName.columnName shorthand—at least not outside a cfoutput/cfloop query block. Instead I need to make sure to reference an actual row from the query.

Forcing your Application.onError event to run to completion in ColdFusion

Categories: HTML/ColdFusion

A common practice when building applications in ColdFusion is to utilize the onError event in the Application.cfc in order to track and log errors that occur in your application, so that you can track down the problems and resolve them. However, there's one type of error that can often escape your onError event handler—and that's requests that are timing out.

First, just some background information on how ColdFusion handles "request timeouts". If the server (or current page) is designed to timeout after 30 seconds, ColdFusion will not simply stop executing when the length of the running request gets to 30 seconds. Instead there are specific operations in ColdFusion1 that check the current running time to see if the request should be halted. That's why if you have a SQL query that takes 45 seconds to run, the page doesn't simple stop after 30 seconds. Instead the query will finish executing and your code won't halt execution until it tries to execute logic that would check the current execution runtime.

This also why when ColdFusion reports the line that took to long to run, it often isn't pointing to the actual line of code that was the real culprit, but a tag like <cfoutput>—which is just displaying the information.

Now that you hopefully have a better understanding of when request timeouts are thrown, let's examine why the Application.onError might not run.

The problem isn't that the Application.onError event doesn't get fired—it does. The problem is that because your page has already been running longer than the allotted time, as soon as ColdFusion encounters one of the operations that checks to see if the page should timeout, it will throw a second error—which effectively breaks your onError event.

The way you can get around this problem is by tracking the current execution timing in your Application and then having your Application.onError immediately adjust the page's request timeout setting as it's first line of logic. Since the <cfsetting /> tag does not check against the current execution time, this allow you to add a buffer to your onError request so that the event can run to completion.

Here's a sample snippet of an Application.cfc which will allow the Application.onError event to run for another 10 seconds—regardless of how long the current template has been running:

<cfcomponent output="false">
  <!---// track the starting execution time //--->
  <cfset executionStartTime = getTickCount() />

  <!---// onError //--->
  <cffunction name="onError" returnType="void" output="true">
    <cfargument name="exception" type="any" required="true" />
    <cfargument name="eventName" type="string" required="true" />

    <!---// declare local variables //--->

      take the current time the request has been running and add 
      10 seconds, to attempt to run the onError handler succesfully 
    <cfsetting requesttimeout="#(((getTickCount()-executionStartTime)/1000)+10)#" />

    <!---// insert error code handling here //--->

I've been using this trick to help make sure any requests that are timing out are fully logged, so I can evaluate the issue and look for ways to fix the root problem.

1 Unfortunately I do not have a list of which operations in ColdFusion do an integrity check on the request lifecycle. I do know that <cfloop>, <cfoutput>, <cfquery> and most complex cf-based tags do check the current running time against the page's request timeout setting.

Merging changes after a successful pull request back into your fork using EGit

Categories: Source Code

Yesterday I finally had the incentive to download Git so I could fork a Github project (Mustache.cfc) and contribute some changes I'd made. Being that Eclipse is my main IDE, EGit seemed to be a logical client to install. The install process was painless and I was able to figure out how to clone a local copy, commit and push changes back to Github. Even the Github pull request process was painless. Much to my chagrin, my pull request was accepted almost immediately and merged back into the main branch.

Naturally the next thing I wanted to do was to sync my fork with the original repository. This is where I got stuck. Whether due to my lack of understanding of Git terminology or just a lack of my Google Search skills, I was not able to easily find instructions for how to sync the original repository back to my fork. After much searching, I was finally able to find some help.

However, I figured I'd clean up the instructions a bit and provide a little more detail on the steps for merging the main repository back into your fork.

The first thing you need to do, is set up a new remote repository linked to your fork:

  1. Open the "Git Repositories" view (Window > Show View > Other > Git > Git Repositories)
  2. Locate your local copy of the fork
  3. Expand the "Remotes" branch (you should see a "origin" entry)
  4. Right-click on "Remotes" and select "Create Remote"
  5. Enter a name representing the master repository, for this example I'll use "mainrepo"
  6. Select the "Configure fetch" option
  7. Click "OK"
  8. Go to the main repository in your browser
  9. Copy the Git URI into your clipboard
  10. Go back to Eclipse
  11. Click on the "Change" button
  12. Paste the URI into the "URI:" field
  13. Click "Finish"
  14. Click the "Add…" button
  15. In the "Source" field, type in the name of the branch you want to import from.

    NOTE: In most cases if you start typing "m", it should show you an auto-complete entry with the "master" branch—just select that option to sync to the master branch.
  16. Click "Finish" (or click "Next" if you want more options)
  17. Click "Save and Fetch"

Now that you have a linked the original repository to your fork, you can merge changes from the original repository to your fork:

  1. Right-click on your project and go to Team > Merge
  2. Under "Remote Tracking", find the "mainrepo/master" (or the appropriate repository/branch based on the remote repository you just configured.)
  3. Click "Merge"
  4. If your repository is up-to-date, there's nothing else to do.
  5. Finally, just commit your changes locally and push back to the upstream

These steps worked for me, so hopefully they'll help guide someone else!

Adding custom callbacks to existing JavaScript functions

Categories: jQuery, JavaScript

This morning I was reading Adding your own callbacks to existing JavaScript functions by Dave Ward—which covers how to overwrite an existing function so you can add some additional functionality (in this case, adding callbacks.) While the article is informative, a couple of improvements can dramatically improve his suggestion.

If you don't want to take the time to read Dave's article, in a nutshell he describes how we can overwrite a JavaScript function by storing a reference to the original function in a variable. So, we can take the following function:

function sayHello(name){
  alert("Hello, " + name + "!");

And we can now overwrite that function by storing a reference to the original function in a variable:

var sayHelloOld = sayHello;

function sayHello(){
  var name = prompt("Enter your name");
  sayHelloOld.apply(this, [name]);

Now there's a couple of problems with the above code.

  1. As written, this would only work if the original sayHello() function was defined in another <script> tag because of function hoisting.
  2. We're polluting the global name space.

We can solve both those problems by using a closure around our code:

// define a closure and pass in a reference to the global window object
(function (w){
  var sayHelloOld = w.sayHello;
  w.sayHello = function (){
    var name = prompt("Enter your name");
    sayHelloOld.apply(this, [name]);
})(window || {});

(NOTE: You can see a working copy on JSFiddle.)

The other topic Dave discusses is how to add callback hooks to run before and after a the original function code runs. His suggestion is built around using the global name space to declare some function names. Since the example is based around jQuery, I'd suggest a much better method would be to add in custom events to your function. This gives you a way to bind callbacks to run, but your neither cluttering the global namespace nor running into issues if the callbacks aren't needed.

Dave's Original Solution

Here's what Dave's original solution looks like:

var oldTmpl = jQuery.fn.tmpl;
// Note: the parameters don't need to be named the same as in the
//  original. This could just as well be function(a, b, c).
jQuery.fn.tmpl = function() {
  if (typeof onBeforeTmpl === 'function')
    onBeforeTmpl.apply(this, arguments);
  // Make a call to the old tmpl() function, maintaining the value 
  //  of "this" and its expected function arguments.
  var tmplResult = oldTmpl.apply(this, arguments);
  if (typeof onAfterTmpl === 'function')
    onAfterTmpl.apply(this, arguments);
  // Returning the result of tmpl() back so that it's actually 
  //  useful, but also to preserve jQuery's chaining.
  return tmplResult;

Improved Solution

Using the two previously mentioned techniques combined, here's how I'd change that code:

(function ($){
  var oldTmpl = $.fn.tmpl;

  // Note: the parameters don't need to be named the same as in the
  //  original. This could just as well be function(a, b, c).
  $.fn.tmpl = function(){
    // trigger the before callback
    // to attach a callback, we just bind() this custom event to our jQuery object
    this.trigger("onBeforeTmpl", arguments);

    // Make a call to the old tmpl() function, maintaining the value 
    //  of "this" and its expected function arguments.
    var tmplResult = oldTmpl.apply(this, arguments);

    // trigger the after callback
    // to attach a callback, we just bind() this custom event to our jQuery object
    this.trigger("onAfterTmpl", arguments);

    // Returning the result of tmpl() back so that it's actually 
    //  useful, but also to preserve jQuery's chaining.
    return tmplResult;
})(jQuery || {});

This gives use a few benefits over Dave's original solution:

  1. We're not polluting the global namespace
  2. We can now attach custom callbacks to each jQuery object separately

To use our code, we can do:

  .bind("onBeforeTmpl", function (){
  .bind("onAfterTmpl", function (){
  .tmpl(data, options, parentItem);
If your prefer to run the same callbacks for all $.tmpl() calls, you could attach the custom events globally.

Any comments on how to make this solution even better?

jQuery Linkselect Plug-in v1.5 released!

Categories: HTML/ColdFusion, JavaScript

Today a new version of the jQuery Linkselect Plug-in was released. The new version under went a ton of changes from the previous version and includes a much better CSS skinning mechanism (see the Linkselect Example.)

The coolest new feature is probably the "placeholder" support, which allows you to specify an <option /> element in your <select /> box to use as a label/placeholder. The "placeholder" <option /> tag uses the <option /> tag as the title of the dropdown menu that's always visible, but still selectable. Here's an example:

Here's a list of all the changes:

  • Revised CSS naming structure to make it to have multiple visual styles of linkselect elements on the same page. The changes in v1.5 are not backwards compatible, so you will need to update your CSS in order to upgrage to v1.5. You can either replace you existing CSS files with one included in the zip file, or you can modify your existing CSS files to match the new naming syntax (which for the most part is just changing the camel casing from "linkselectLink" to "linkselect-link", "linkselectLinkOpen" to "linkselect-link-open", etc. Also, all CSS classes now using the prefix "linkselect-" by default.
  • Added new "style" option. This replaces all the previous class* options. The default style is "linkselect" and is simply used as the prefix used for all the CSS classes. You can create custom linkselect skins by just copying one of the included CSS files and changing the prefix to match your style option value.
  • Added "text" API call to return the label of the currently selected option.
  • Added "destroy" API call to destroy a linkselect element and return to original select element.
  • Changed the "replaceOptions" API to add a new "includeFilter" argument. The API change is backwards compatible.
  • jQuery.delegate() is now used to monitor event changes
  • Changed <li /> elements to use "data-" attributes for custom attributes
  • Changed <li /> elements to use "data-value" for storing selectable value instead of the "rel" attribute.
  • Fixed issue with Chrome not correctly accepting focus when the linkselect element was clicked.
  • Events related to closing/resizing of the open linkselect element are now registered/unregistered when the menu opens. This helps improve performance when there are many linkselect elements on the page as it reduces the events that fire to see if the menu should be closed.
  • Added ability to use both "name" and "id" attributes on a select element with different values.
  • Added new "placeholder" functionality. You can now give an option element a class of "placeholder" to have it used as a selectable title for the linkselect element. This allows you to use a "Please select..." option element in your select and have it used as the title of the linkselect. Users will be able to select this option, just like any other option element.
  • Fixed various positioning bugs.

Upgrading Trac with integrated SVN to new version of Python

Categories: Source Code, HTML/ColdFusion

We recently upgraded our version of Subversion from a very old version of Subversion to v1.7.1. We really wanted the newer merging capabilities that arrived in Subversion 1.5 and figured it was worth the energy to upgrade Subversion to the latest version. Fortunately upgrading our Subversion repositories went very smoothly. I even decided to just do a full dump/load to make sure the repositories were fresh 1.7.x repositories. While this process took a little extra time, the upgrade itself went very smoothly.

However, where things really fell apart for us was with our Trac/SVN integration. The problem is we were still running Python 2.4, which include old SVN bindings, so any attempt to do anything with our SVN ended up with an error that looked like:

TracError: Couldn't open Subversion repository c:/path/to/repository: SubversionException: ("Expected FS format '2'; found format '4'", 160043)

In order to get the SVN commit hooks working with Trac, I was going to need SVN bindings that worked with SVN v1.7.1, but also were compiled for my version of Python. While one might think this wouldn't be difficult, I couldn't find any SVN bindings for Python 2.4. This left my options at either trying to compile my own versions or upgrade Python. Since I don't have easy access to tools for compiling my own version of the Windows binaries, I thought the best option was just to upgrade Python—especially since Trac is soon dropping support for Python 2.4.

The problem is there's no easy way to just migrate a Trac install to a new version of Python, because you're going to need to recompile all your Python *.egg files for the version of Python you're using. After spending way too much time getting all this working, I thought it was wise that I compile a list of all the steps you may need to take in order to get Trac migrated to a new version of Python.

Here is the general install instructions. I don't break this down as a true step-by-step instructions, because so much varies based on your environment. However I do break down all the major steps you'll need to take and try to point you to step-by-step instructions for each bullet when possible.

  1. Install the version of Python you intend to run
  2. Make sure to install Python into a new folder (do not install over your old version of Python)
  3. Compile a list of all the Trac plug-ins you're currently running (check both your Trac/path/to/env/eggs folder and the Python/Lib/site-packages folders for EGG packages you currently have installed)
  4. Back up your current Trac environment folder (where all your Trac repository files are)
  5. Follow the instructions for installing Trac (but ignore the steps for creating new repositories—you can use your existing ones)
  6. Delete any old EGG packages from the Trac/path/to/env/eggs folder
  7. Reinstall all your old Trac Plugins
  8. Shut down your Trac server
  9. Follow the Trac guide for upgrading Trac, with the following notes:
    • You don't need to update the Trac Code (you just installed the latest version)
    • Skip the "Resynchronize the Trac Environment Against the Source Code Repository" step—you'll do that after updating SVN
  10. For Trac/SVN Integration, the Trac and Subversion page outlines the basic steps needed, but this is the step that cost me the most pain, so I'm going to break down some extra notes.
    1. Download the SVN binaries from You'll want to download the Windows binaries and Python bindings for the version of Subversion you plan on using.

      NOTE: This site has the largest collection of Python SVN bindings that I could find and also offers the command line SVN binaries you'll also need. While you should be able to use another SVN command line binaries, using the Setup-Subversion-*.msi installer is probably going to be the most forward way to get started.
    2. Install the Subversion binaries.

      NOTE: You may be required to reboot your server. You will also want to confirm that the Subversion/bin path is in your Window PATH environment variable.
    3. If running Apache, you'll want to make sure to replace the and in your /Apache/modules with the versions in  the /Subversion/bin folder.
    4. Copy the files in the Python bindings file (e.g. to the Python/Lib/site-packages folder. You'll want to overwrite the existing files or delete the existing directories.
    5. You'll now want to test your Python SVN installation:
      1. Go to your Python folder and type "python"
      2. Verify that you can load the bindings using the Python interactive interpreter by typing:

        from svn import core
      3. If all is successful you should just see a new Python prompt. The only time you should see output is if there is an error.

        NOTE: This is the part where I spent a lot of time troubleshooting. I kept getting a "ImportError: DLL load failed: The specified module could not be found." After much digging and research, this turns out to be related to Trac Issue #665. The fix for me was to copy the following files into the Windows\system32 folder:
        • libdb42.dll
        • libeay32.dll
        • ssleay32.dll
        I'd recommend backing up existing files before replacing them.
      4. If you're still getting errors, see the Trac/Subversion checklist for more tips.
    6. Resynchronize the Trac Environment Against the Source Code Repository, using the following syntax for each repository:

      trac-admin /path/to/projenv repository resync '*'
  11. Check all your SVN hooks and update any paths pointing to the old version of Python to make sure they point to the new install. For example, your trac-svn-post-commit-hook.cmd may point to your old Python path and these need to be updated in order to work.
  12. Check your Windows PATH environment variable and make sure the new version of Python is in the PATH statement. If it's not, update/add it and reboot your server.
  13. Restart your Trac server.

If all goes well, your environment should now be running under your new Python installation.

This process ended up costing me way more time than expected, so hopefully this guide will prevent you from struggling the way I did.

Using simple conditional logic in JsRender

Categories: JavaScript

I've been updating some code that was built using jQuery Templates, but since jQuery Templates has been discontinued, I've decided to migrate my code to use JsRender.

The syntax is close enough for the most part that translating existing templates is straightforward enough. For the most part it's updating to the new mustache-like bracket syntax and changing variables from ${var} to {{=var}}. However, there was one thing that was tripping me up—simple conditional statements.

JsRender uses the mustache like "codeless" syntax for conditions:

{{#if varName}}output{{/if}}

This fine if varName is a truthy statement, but what if you need to check if varName  is equal to a specific value? Having gone through all the JsRender sample code, it appeared the only way to accomplish any kind of comparison conditional statement was to use custom code. This syntax ends up being very ugly and verbose. Here's an example of writing an attribute dynamically based on the value of a variable named varName :

<div class="base{{* if($ == 'None'){ result += ' none';  } }}">{{=varName}}</div>

This seems overly complicated for such a simple comparison. I figured there had to be a better way.

After digging through the code, I found there's some undocumented features of the if tag. You can actually use the following syntax to do the same thing:

<div class="base{{#if varName eq='None')}} none{{/if}}">{{=varName}}</div>

While this syntax ends up being pretty close to jQuery Templates, it uses the "hash" functionality of JsRender's custom tags. The odd looking part is that you need the equal sign between the conditional operator and the value. Other operators include:

  • eq = equals
  • ne = not equals
  • lt = less than
  • le = less than or equal
  • gt = greater than
  • ge = greater than or equal

I actually would prefer to see "lte" and "gte" used for the "or equal" functions, so I've added that to a ticket.

So, while the syntax isn't ideal, it's certainly better than using using the code syntax. Since the project is still in development, it possible the syntax will change.

iPhone 3GS vs 4S data speed

Categories: Technology

On Friday I upgraded my iPhone 3GS to the new iPhone 4S. One of the things that's changed in the 4S is that it's supposed to support HSPA+. Well not truly "4G", it should offer faster data transfers on AT&T's network. Before heading into the AT&T store to pick up my new phone, I decided to run a couple of tests on my 3GS in my car while parked in the parking lot.

My initial tests on the 3GS reported data xfer speeds around 970Kbits/s.

Once I had my new 4S in hand, I returned to my car to repeat my performance tests. I immediately saw the numbers jump to somewhere in the 1820Kbits/s—almost double the speed.

While this was not an exhaustive test by any means, it does appear that there is a nice speed jump on AT&T's network—provided your in an area covered by HSPA+.

Here's the screenshot showing my min/max results:



Important! The Olson Time Zone Database has been shut down

Categories: Java, HTML/ColdFusion

There was some disturbing news yesterday—and I'm not talking about the death of Steve Jobs. The "Olson" time zone database was taken offline. While the immediate impact may not be felt, this is a very important open project and shutting it down is potentially going to cause a lot of problems.

For those that don't know, the time zone database has been maintained by a group of volunteers over the years and hosted at National Institutes for Health and contains historical, present day and known future information on day light savings changes around the world. This database is used by a huge of array of platforms to keep accurate time—Java, Unix, *nix, smart phones, etc. If you have an app that can tell you the current time in another location, there's a good bet it's based on this time zone database (either directly or via the underlying language's platform.)

Here are some good articles with more insight:

As many have already commented on, I'm not sure how this law suit can hold up since historical information can not be copyrighted. However, the law suit will have an impact—especially for applications in use by locations where the day light savings is in frequent flux.