Wednesday, 30 November 2011

The true story on return false

In JavaScript event handlers, you'll frequently see return false; at the end. Why? What does it do? The answer is: It depends. Before we get into the details, there are basically two things it could be doing:

  1. Preventing the default action of the event, such as when you click a link and the browser follows it.
  2. Stopping propagation of the event to ancestor elements.

So which does return false do? Just one, neither, or both:

  • DOM0 handlers: In an old-style DOM0 handler, hooked up via an attribute like this:
    <div onclick="return functionName();">
    ...it prevents the default action but doesn't stop propagation. Note that you need that return in the attribute. Try it here.
  • DOM0 handlers (again): The same is true of an old-style DOM0 handler hooked up via the reflected property for the attribute, like this:
    document.getElementById("someId").onclick = functionName;
    Try it here. As before, return false; prevents the default action but doesn't stop propagation.
  • DOM2 handlers: In a proper DOM2 handler hooked up via addEventListener, like this:
    document.getElementById("someId").addEventListener("click", functionName, false);
    ...return false does absolutely nothing. Instead, use the preventDefault and stopPropagation functions on the Event object your handler receives as an argument. Try it here (be sure to use a non-Microsoft browser, or use IE9 or higher).
  • Microsoft DOM2-ish handlers: In a DOM2-ish handler hooked up with Microsoft's attachEvent function, like this:
    document.getElementById("someId").attachEvent("onclick", functionName);
    ...return false prevents the default but not propagation, just like a DOM0 handler. Try it here (using IE8 or lower).
  • jQuery handlers: Event handlers hooked up with jQuery get a twofer: return false both prevents the default and stops propagation. It's a jQuery thing.

Happy handling!

Friday, 7 October 2011

Finally!

Google announces an honest-to-goodness relational DB for App Engine: Google Cloud SQL. And it's the nice, familiar MySQL engine with virtually full support and very few limitations. Notably missing from that FAQ is anything about limits on how long a statement can take to complete (I've asked the question), but other than that...

Friday, 26 August 2011

Potentially Good News on Software Patents

There's some potentially good news on software patents. The Federal Circuit appeals court has rejected a patent claim on the basis that it's just an "abstract idea," a door that was very slightly opened in a recent very narrowly-drawn Supreme Court patent decision. Might reason ultimately prevail over avarice and bullying...? Paraphrasing something a good friend of mine said to me once, I'm prepared to believe there really are patentable software ideas. I'm not sure I'm prepared to believe they number in the thousands, possibly not even in the hundreds.

Tuesday, 23 August 2011

Nifty Snippets bite-sized morsels

Sometimes the pressure for a proper "article" is too much and I end up not posting something here that I run across that I think is interesting. I thought "If only there were a service tailored to quick thoughts and links..." and of course, immediately thought of Tumblr (yes, and Twitter, but I was looking for something in between blogging and tweeting). So may I present: The Nifty Snippets tumbleblog, Nifty Snippets in bite-sized pieces.

The tumbleblog is for quick thoughts and observations, not necessarily critically reviewed and possibly fleeting. Things I don't want to commit to my proper blog. Where's the line? I have no flippin' idea. More fully-baked ideas go here, but if you like something more immediate and less wordy, go follow...

Thursday, 16 June 2011

Copying table structure in SQL Server

Why didn't I know this? For so many years I've bemoaned the fact that SQL Server doesn't have MySQL's CREATE TABLE LIKE statement:
CREATE TABLE NewTable LIKE ExistingTable
Um, er, except it does:
SELECT TOP 0 INTO NewTable FROM ExistingTable
The INTO clause of SELECT...
"...creates a new table in the default filegroup and inserts the resulting rows from the query into it." (MSDN link)
And of course, if you use TOP 0, there are no rows — but the table structure still gets created. (Naturally, if you want to copy the data as well, just leave off the TOP 0.)
Sometimes, you think you know something doesn't exist, and it turns out you just haven't looked hard enough to find it...
Happy coding,

Sunday, 12 June 2011

Google Apps migration and multiple accounts

Non-coding post today. If you use Google Apps for your Domain, you've either been transitioned to the new "full" infrastructure or you soon will be. If like a lot of people you have this crazy idea that your personal and professional lives should be separate, and so you have separate accounts for personal and private stuff, you'll run into the problem that your Google Apps stuff and your Google stuff now share an account cookie, and so trying to (say) view your personal email when you're logged into a Google service with your professional account will fail because you're logged in with your personal account. Sigh. If only there'd been a way to anticipate that this might be a problem and design and implement the solution properly before involuntarily migrating people over.

The good news is that Google now supports multiple account login in many of its services. You lose offline mail and calendar (huh?) and there are some other caveats, and it's still a PITA to use, but when/if you do manage to get signed into your multiple accounts on the services you mostly use, it does sort of work, a bit.

Hopefully as time marches by, the multiple accounts stuff will improve and spread to their other services.

Wednesday, 8 June 2011

Release: place5 placeholder emulator plugin (alpha)

Just briefly, folks, I'm writing to announcing the alpha release of my place5 jQuery plugin. It's just a dead-simple plugin that emulates HTML5 placeholder attribute behavior on browsers that don't support it natively. On browsers that do, it stays out of the way (unless you tell it to take over), but it quietly does the job for you on browsers that don't do it themselves. Check it out! This is alpha, throw issues into the issue tracker on the project site if (when!) you find them.

Sunday, 22 May 2011

Appending to an array

JavaScript's Array#concat function is handy and cool, but it creates a copy of both arrays. That is, given array a and array b, a.concat(b) will create a third array c with the contents of both of them, leaving a and b intact. Now, sometimes that's what you want, but other times you just want to append b to a without creating a copy of a.

While JavaScript doesn't have a dedicated append function, there's a surprisingly efficient and concise way to do it:

a.push.apply(a, b);

That calls the push method via apply, which accepts a context argument (what the this value should be during the call) and any array-like object providing the arguments to give the call. Since push allows you to push multiple elements with a single call, that works a treat. And somewhat unusually for JavaScript, it's probably the most efficient way to do it on all platforms (where normally this sort of thing is faster on some and slower on others)

Now, if you do this in code other people are going to have to read, I'd recommend a comment saying "Appends `b` to `a` in place" or some such, because otherwise it's pretty opaque to people who don't know JavaScript well. Or, of course you could always add an append method to arrays:

(function() {
var push = Array.prototype.push;

function Array_append(array) {
push.apply(this, array);
return this;
}

if (Object.defineProperty) {
Object.defineProperty(Array.prototype, "append", {
enumerable: false,
value: Array_append
});
}
else {
Array.prototype.append = Array_append;
}
})();

As always, if you're doing that on platforms that don't have Object.defineProperty (and there are still a lot in the wild), make sure you don't have any broken for..in loops lying around.

Wednesday, 11 May 2011

jQuery 1.6.1 softens `attr` woes

jQuery 1.6's change to the attr function, and introduction of the prop function, raised quite a bit of confusion (and awareness) about properties and attributes. It was quite a breaking change, affecting a fair bit of code, and so jQuery 1.6.1 changes attr again to make it more backward-compatible. See the jQuery 1.6.1 blog post for details. According to the jQuery team, as of 1.6.1, most older use of attr should continue to work, although in many cases prop would be the preferred way to go.

Wednesday, 4 May 2011

Big compatibility break in jQuery 1.6

jQuery 1.6 is out, and unusually for jQuery, there's a pretty big API compatibility break with regard to the attr function. Details in the release notes, but basically, attr really is just for attributes now, whereas before it sort of conflated attributes and properties. There's a new prop function for interacting with properties.

Update 2011/05/11: jQuery 1.6.1 changes attr again to make it more backward-compatible for older code, details in the jQuery 1.6.1 blog post.

It's a seemingly-small change, but I've seen a lot of code that will be affected, especially around the "checked" property. If you've been checking whether a checkbox is checked using code along the lines of

// In an event handler, etc.:
if ($(this).attr('checked')) {
// It's checked
}
// Or if you already have a jQuery object for the checkbox:
if (foo.attr('checked')) {
// It's checked
}
...you'll need to update your code. Personally, I prefer going straight to the DOM element (that's what the jQuery team recommends as well):
// In an event handler, etc.:
if (this.checked)) {
// It's checked
}
// Or if you already have a jQuery object for the checkbox:
if (foo[0] && foo[0].checked) {
// It's checked
}
...and the above has the advantage of working regardless of what version of jQuery you're using. But you could use prop instead if you like:
// In an event handler, etc.:
if ($(this).prop('checked')) {
// It's checked
}
// Or if you already have a jQuery object for the checkbox:
if (foo.prop('checked')) {
// It's checked
}
(There's also foo.is(':checked'), but that's awfully round-about.)

The big properties I see people stumbling over are "checked" (hence the above) and "value" (but hopefully you were already using val anyway).

Tuesday, 26 April 2011

A brief, incomplete definition of 'an engineer'

Most of us, much of the time, react to "You're wrong" with "No I'm not! And you can't tell me otherwise!" An engineer's response is "How? Show me."

Saturday, 23 April 2011

Thinking outside the box with the ASP.Net Development Server

A well-known limitation of the ASP.Net Development Server is that it will only bind to the local interface of the development machine, never the LAN interface, and so you can't bring up pages served from the development server from anywhere but on the box itself via the localhost name or 127.0.0.1 address. And fair enough, it's meant to be a debugger aid, not a web server. But there are situations where you want to think outside that box, and fortunately, it's dead easy to do so.

I wanted to do that because I'm working on a project where the server-side code is C# assemblies and the client-side is pure HTML, CSS, and JavaScript (no server controls or other ASP.Net-isms). I wanted to use VS.Net for debugging server-side C# code, and Chrome's Dev Tools or Firebug for debugging the client-side code in the browser. VS.Net makes it remarkably easy to debug server-side code — just mark the "project" with the client code as the startup project, chooose a port if you want, set the virtual path, make sure the server-side assembly projects are in the solution, and hit F5 (or click Run). It fires up the ASP.Net Development Server and you have full debugging of the server-side code (and client-side code as well, if you like, with its integration with IE). Excellent, couldn't be much easier. But for me, this is all happening in a VM (this is for a client, and I keep all my client stuff isolated from my work and each other using VMs) but I'd like to do the client-side debugging with Chrome's Dev Tools on my main machine.

It was only a mild irritation (I can run a browser in the VM happily enough), so I didn't want to spend any time on it, but I figured this has to be one of the simplest examples of the need for port forwarding or proxy serving on the planet (just bridging interfaces), and so I did a quick web search and found this article about using Squid to do it. Now, Squid is a big hammer for this nail, but it's so easy to set up, that it's still reasonable to use it.

That article's good, but I thought a few things weren't as clear as they may have been, so here's my step-by-step:

  1. Tell the ASP.Net Development Server to use a particular port rather than picking one at random:
    1. Click the client code project in your solution
    2. Look at the properties
    3. Make sure that Use dynamic ports is False
    4. Choose your port under Port number (if you just turned off the Use dynamic ports option, you may have to click away from the properties and click back again to unlock the field; minor bug in VS.Net)
    I'll call that the "internal" port (the one that only works within the VM). In my case, I used 80; the VM doesn't serve any other web content.
  2. Be sure your dev machine's firewall allows incoming connections whatever external port you're going to use.
  3. Get and install Squid. The quick-and-easy thing is to get a binary from these nice people who are kind enough to maintain a Windows port built using the Microsoft toolchain: Just scroll down under the features list and click the "Squid download Page" link. Or, of course, you could grab the source and do it yourself (with the MS toolchain, or Cygwin and GCC). I grabbed 2.7.STABLE8 from the the nice people.
  4. Unzip it somewhere handy. The Windows binary assumes C:\Squid, so I went with the path of least resistance and put it there.
  5. Install it as a service:
    1. Open a command prompt (as Administrator, if you're on Windows 7 or similar)
    2. Change to C:\Squid\sbin.
    3. Type squid -i -n Squid
  6. Set up the default config: Using your favorite file browser:
    1. Go to C:\Squid\etc
    2. Copy squid.conf.default as squid.conf
    3. Copy mime.conf.default as mime.conf (we're not going to edit it, but it's required)
  7. Set up the proxying:
    1. Open squid.conf with your favorite text editor
    2. Go to the very end of the file
    3. Paste this in, replacing the bits in curly braces with your own values (without the curly braces) (mind the word-wrapping, Blogger is giving me a hard time yet again; best to copy and paste):
      http_port {EXTERNAL_PORT} accel defaultsite={EXTERNAL_IP_ADDRESS}
      cache_peer localhost parent {INTERNAL_PORT} 0 no-query originserver name=myAccel

      acl our_sites dstdomain {EXTERNAL_IP_ADDRESS}
      http_access allow our_sites
      cache_peer_access myAccel allow our_sites
      cache_peer_access myAccel deny all
      So in my case, since I want both the internal and external ports to be 80 and my VM's external (LAN) IP is 192.168.142.153, it's
      http_port 80 accel defaultsite=192.168.142.153
      cache_peer localhost parent 80 0 no-query originserver name=myAccel
      acl our_sites dstdomain 192.168.142.153
      http_access allow our_sites
      cache_peer_access myAccel allow our_sites
      cache_peer_access myAccel deny all
      ...but if you use different internal and external ports, be sure to set them according to the placeholders in the above.
    4. Save the file
  8. In your (Administrator) command window, tell Squid to validate the config:
    squid -z
    You should see the message Creating Swap Directories and pretty much nothing else.
  9. Start the service:
    net start Squid
  10. You might choose to have Squid start when you boot the machine, via the usual services.msc settings

Sorted!

Friday, 18 March 2011

No Excuse

I'm active in a couple of places where people go for help with, amongst other things, JavaScript code running in web browsers. Routinely, you'll get questions like this one where the person asking the question is having trouble visualizing what's happening on the client side, and is stabbing in the dark with alert or console.log to try to figure out what's going on. It's like the old days of debugging with printf. I see it over and over again.

Folks, maybe that was reasonable in, oh, 2002 or so, but here in 2011 if you're using a desktop browser there's just no excuse for not using a proper debugger to set breakpoints, walk through code, inspect variables, inspect the structure of the DOM, etc., etc. No excuse at all. None! Pas d'excuse! ¡No excusa! Keine Entschuldigung! Nessuna scusa!

Why not? Because these days, client-side JavaScript debuggers are a dime a dozen, and they're free. Pick your browser:

  • IE8 & IE9: Semi-reasonable debugger and tools built in, or use the free version of VS.Net
  • IE6 & IE7: Use the free version of VS.Net (or the old standalone script debugger)
  • Firefox: Get the excellent, and free, Firebug plug-in
  • Chrome: Has a good debugger and tools built in
  • Safari: Has a good debugger and tools built in (you may have to enable the menu item in the options)
  • Opera: Has a good debugger and tools built in
You get the idea. The next time you're running into a "cannot access property foo of undefined" error, don't guess where it happened, use a debugger to walk through the code and see!

Happy coding debugging!

Sunday, 30 January 2011

Skipping the protocol

I just found out about this incredibly useful trick for loading absolute resources with either http: or https: depending on the protocol with which your page was loaded (to avoid those "mixed content" complaints from browsers).

Amazingly, you can just leave the scheme (protocol) part off the URL entirely. So for instance, if you're loading jQuery from the Google CDN with this path:

<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js'></script>
you can load it like this instead:
<script src='//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js'></script>
If the page that's in is served via http, that path will become
http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js
If it's https, that will become
https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js
E.g., it's a path relative to the protocol (scheme) but in all other ways absolute.

(Naturally, if you load a file via the file: protocol — e.g., locally — it will try to resolve that locally and blow up. But you don't do that, do you?)

Don't trust it? Neither did I, but I haven't found a browser in which it doesn't work, nor apparently have these guys.

And you thought you knew all there was to know about URIs...

Sunday, 23 January 2011

A myth of arrays

When is an array not an array?

When it's a JavaScript array.

(Update: Here I'm talking about JavaScript's standard Array type. Many environments now also support new typed arrays, which are different.)

JavaScript objects are basically just key=value maps, and JavaScript arrays are nothing more than objects that have:

  • special handling for keys that are numeric strings
  • a special length property
  • and some functions they inherit from Array.prototype
That's it. Although we conventionally write array "indexes" as numbers, like all property names they are strings — a[0] is converted to a["0"] by the interpreter (although implementations are free to optimize this as long as the behavior remains as per the spec).

All of this is covered by Section 15.4 of the specification, which starts with this paragraph:

Array objects give special treatment to a certain class of property names. A property name P (in the form of a String value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 2^32−1. A property whose property name is an array index is also called an element. Every Array object has a length property whose value is always a nonnegative integer less than 2^32. The value of the length property is numerically greater than the name of every property whose name is an array index; whenever a property of an Array object is created or changed, other properties are adjusted as necessary to maintain this invariant. Specifically, whenever a property is added whose name is an array index, the length property is changed, if necessary, to be one more than the numeric value of that array index; and whenever the length property is changed, every property whose name is an array index whose value is not smaller than the new length is automatically deleted. This constraint applies only to own properties of an Array object and is unaffected by length or array index properties that may be inherited from its prototypes.

There are some consequences to this:

  • Barring implementation optimizations, accessing array elements by index is not a constant-time operation as it is in (say) C; like all maps, it will vary depending on the layout of the key storage, the size of the map, and the specific key you're looking up.
  • Barring implementation optimizations, using an array is no more efficient than using a plain object.
  • JavaScript arrays are inherently "sparse" (since the whole concept of a contiguous block of memory just doesn't exist for them anyway).
  • Arrays are inherently one-dimensional. A "2D" JavaScript array is merely an array in which each element is, in turn, an array, and it's important to remember that those sub-arrays may have different lengths (and you may even choose to intermix arrays and non-arrays as values of the outer array's elements).
  • Arrays can have non-index properties, which can be handy. For instance, this is perfectly valid:
    var a = ["zero", "one", "two", "three"];
    a.foo = "bar";
    alert(a.length); // alerts "4"
    alert(a.foo); // alerts "bar"
    This is one reason why it's important to understand for..in loops before using them on arrays (see Myths and realities of for..in).

None of which means that we shouldn't use arrays for ordered lists of things. We should. Doing so is useful semantically, and of course it's entirely possible that an implementation will optimize some of those things above. But it's important to remember what you're dealing with, and taking advantage of an array's non-array nature can be very useful sometimes.

Happy coding,