The future of nugget functionality

I’ve spent some of today updating my “jQuery plugin”, doWhen, with the added help of a splendid chap and coworker, name of Erin Swenson-Healy. He adapted it to be more AMD friendly, and I expounded upon that. Basically, now, using requirejs, we can simply say in a given file:

define(["doWhen"],
function(doWhen){
  doWhen(....);
});

Which means for our purposes, it’s not really a jQuery plugin. And, realistically, it’s not. It’s just a tiny nugget of functionality we want to pull in. All it uses from jQuery is a flat extend, and that is easily replaceable (as it already is in the “plain” version).

However, not everybody will consume doWhen through AMD or a similar method. And not everybody who doesn’t necessarily uses jQuery. But I need *somewhere* to attach it to, if AMD or equivalent is not defined.

So, I’m thinking of ditching the jQuery requirement entirely. The “plain” version would be the only one. If AMD or something like it is present, it would define according to that pattern. If jQuery, underscore, or another common library is present, it would attach to one or more of those for more traditional use, defaulting perhaps to the global object if none are present?

doWhen

It occurs to me that I have neglected to post about the most bloody useful utility I’ve ever written: doWhen

While the main version is a jQuery plugin, I find myself using the non-jQuery version more often, so that’s the one I’ll describe here.

What doWhen does is allow you to create a single “virtual event” in the form of a test function which, when it returns true, runs a callback function.

Let’s say we were loading a library and wanted a quick way to do something when the library is ready:

<script type="text/javascript">
var global = (function(){return this;}());
Event.doWhen(function(){
  return !!global.jQuery;
}, function(){
  alert('jQuery loaded!');
});
</script>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.min.js"></script>

The !! ensures the result is a boolean. Every 100 milliseconds (configurable), it will run the first function. The first time that function returns true, it runs the second function.

I use this extremely frequently, even in wee code snippets, thanks to the tiny footprint (about 1/3kB minified).

See the github page for options.

Open question: is Event the place to put this? Keep in mind this isn’t extending the object’s prototype, so shouldn’t cause conflicts in other code.

I am considering whether there’s a need for an ajax capable one which periodically checks a remote state.

JWPlayer and Firefox hunting

Greetings m’chums! Welcome to this week’s big adventure: trapping and shooting an old bug in a very popular tool.

In the course of my everyday tinkering, I encountered a curious flaw in JWPlayer under Firefox which I think has plagued fellow adventurers of this fine Flash movie player for years, but which none seem to have sussed the essential nature of. Until today.

It all started when I found the player was firing no events. Initial investigation showed that I was never subscribing the events, because I was waiting for the event attaching method jwAddEventListener to arrive on the scene:

var player = $('#myplayer');
$.doWhen(function(){
  return !!player.jwAddEventListener;
}, function(){
  //attach events
});

(doWhen is a nifty little utility I wrote that runs one function once after another function returns true)

A full investigation of the matter revealed that, in Firefox, the entire Javascript API for the embed was never being created! This was quite curious, as I’ve had many players fully functional in Firefox before.

I set about identifying what was different, and at last deduced that in all the previous cases, I had been instantiating the player after an ajax call (for content). In this case, however, the player was being created immediately after jQuery(document).ready.

Pausing briefly to brew up another cup of brown joy strong enough that the spoon stood rigid, I continued the experiment by delaying the player instantiation by two seconds with setTimeout. Lo and behold, the missing API was rumbled!

This was far from an ideal solution, of course. I wondered at what critical juncture Firefox would be in a position to correctly instantiate JWPlayer. Time to dust off an old friend: window.onload. A quick test showed that yes, indeed, once the load event had fired off, the player’s entire API reappeared like Manchurians at a near-dead pub advertising half-off pints of Brown!

Naturally, I didn’t want to slow other browsers down unnecessarily, so I knew that the final solution would have to narrow its sights on Mozilla, and only if the load had not yet fired. The way to determine the latter is document.readyState: “interactive” means the page is still in its initial rendering phase, but the DOM is available; “complete” is the word we’re looking for, the readyState when load has fired.

if ($.browser.mozilla
      && document.readyState !== 'complete') {
  $(window).bind('load', function(){
    //write your embed code here
  });
} else {
  //write your embed code here
}

There you have it! Clearly, this should not be necessary; JWPlayer should do all of this internally. For now, it’s a workaround that seems 100% successful.

And with that, another successful week tromping through the deepest woods of my trade draws to a close. I bid your weekend be filled with lakes of Earl Grey and barrels of gorgeous Somerset gold. Tata!

More on Randomness of Arrays

A few months ago, I posted about how the classic method most JS programmers would use to randomise an array completely fails. I proposed a quick solution to it, but that solution had flaws of its own.

So, for those of you who need to shuffle decks of cards, scramble virtual eggs or just want to toss a little confusion into your applications, here’s a fully working array randomiser:

Array.randomiseArray = function(arr){
  var i, v;
  for (i=0; i<arr.length; i++) {
    v = arr[i];
    arr[i] = {
      v: v,
      sort: Math.random()
    };
  }
  arr.sort(function(a, b){
      return a.sort - b.sort;
  });
  for (i=0; i<arr.length; i++) {
    arr[i] = arr[i].v;
  }
  return arr;
};

SMIRF 2.0

Late last year, I told you about the SMIRF JS pattern I’ve been working on. Since then, I’ve had quite a lot of opportunity to use it in the field and refine it.

The biggest weakness with the old SMIRF pattern was the need to pass the instance variables around all the time. It meant there was a tiny memory leak in the api, since wrapper functions needed to add the iVars argument to all calls.

What I’ve changed is that iVars is now accessed via a method getIVars in the “constructor”. There’s a localised randomly generated private key which must be passed in to get the instance variables. That way, nothing outside could grab them and clobber them (unless you write a method to deliver them, obviously!

So, here’s the updated pattern:

var employeeFactory = (function(){
  var PRIVATE_KEY = String(Math.random()), giveRaise, fire;
  
  giveRaise = function(percent){
    var iVars = this.getIVars(PRIVATE_KEY);
    percent = percent || 0;
    console.log(iVars.salary);
    iVars.salary = iVars.salary * (1 + (percent/100));
    return iVars.salary;
  };
  fire = function(){
    var iVars = this.getIVars(PRIVATE_KEY);
    //long, drawn out legal process that takes 5000 lines
    iVars.currentlyEmployed = false;
  };
  
  return function(name, salary){
    //This is essentially a constructor function
    var iVars = {
      //Instance variables. All state info should be stored here
      name: name,
      salary: salary,
      currentlyEmployed: true
    };
    return {
      getIVars: function(key){
        if (key === PRIVATE_KEY) {
          return iVars;
        }
      },
      giveRaise: giveRaise,
      fire: fire
    };
  };
}());
var employee1 = employeeFactory('Bob', 30000);
var employee2 = employeeFactory('Mary', 21000);

An Array of Random Thoughts

Today, I was asked to supply a way (in Javascript) to create an array of all integers 0-9, but randomised, with no duplicates. Immediately, I gave this solution:

var arr = [0,1,2,3,4,5,6,7,8,9].sort(function(){
  return 0.5 - Math.random();
});

That should do it, right? Well, one of the tests that was run on the resulting “random” array was to see whether or not the first five members were consistently even or odd:

var generateRands = function(){
  return [0,1,2,3,4,5,6,7,8,9].sort(function(){
    return 0.5 - Math.random();
  });
};
var isMoreEven = function(arr){
  var i, even = 0, odd = 0;
  for (i=0; i<5; i++) {
    if (arr[i] % 2) {
      odd++;
    } else {
      even++;
    }
  }
  return even > odd;
};
var runTests = function(iterations){
  var i, even = 0, odd = 0, arr;
  for (i=0; i<iterations; i++) {
    arr = generateRands();
    if (isMoreEven(arr)) {
      even++;
    } else {
      odd++;
    }
  }
  console.log('even won', even, 'or', (even/iterations * 100) + '%',
    '; odd won', odd, 'or', (odd/iterations * 100) + '%');
};
runTests(10000);

The expected result would be that even and odd would hover around 50%, with some variance either way. Here’s what it *actually* outputs:

even won 6762 or 67.62% ; odd won 3238 or 32.379999999999995%

WTF? It hovered consistently around this ratio with every test! I thought at first that it was perhaps a pattern in the pseudo random number generator, but no matter what I did, it hovered strongly in favour of even.

Keep in mind this is the method most commonly recommended on tutorial sites around the net for randomising an array’s order!

I added a further test to this, to see how frequently each number ends up in the first five. Here’s the test:

var runTests = function(iterations){
  var i, j, arr, nums = [], order = [];
  for (i=0; i<10; i++) {
    nums.push({
      num: i,
      count: 0
    });
  }
  for (i=0; i<iterations; i++) {
    arr = generateRands();
    for (j=0; j<5; j++) {
      nums[arr[j]].count++;
    }
  }
  nums.sort(function(a, b){
    return a.count - b.count;
  }, true)
  for (i=0; i<10; i++) {
    order.push(nums[i].num + ' (' + nums[i].count + ')');
  }
  console.log(order);
};
runTests(10000);

The output was consistently something like this:

["9 (309)", "8 (598)", "7 (1180)", "6 (2256)", "5 (3963)",
  "4 (6170)", "3 (7926)", "2 (8817)", "1 (9386)", "0 (9395)"]

The number in parentheses represents the number of times in 10000 that the number to the left appeared in the top five. This “random” sort didn’t seem to take array members very far from home. One and zero are the only two that I could get to swap how frequently they appeared in the first five, for some reason.

What I finally realised was that the flaw was in my use of sort. Sort swaps adjacent items until it reaches the conclusion that the array is in the proper order. Each item is “less than” or “greater than” the other, and this is meant to be a consistent value. If you randomise the compare function’s output, then it will swap items, then swap them back, and finally give up on your inconsistency.

Even was coming up more often than odd because there are a greater number of even numbers from 0-4.

And now, what you probably visited this page for. A *working* array randomiser:

UPDATE: I have a much better method now in this post!

var randomSort = function(arr){
  var i, l, sortOrder = {};
  for (i=0, l=arr.length; i<l; i++) {
    sortOrder[arr[i]] = Math.random();
  }
  return arr.sort(function(a, b){
    return sortOrder[a] - sortOrder[b];
  });
};
var generateRands = function(){
  return randomSort([0,1,2,3,4,5,6,7,8,9]);
};
for (var i=0; i<5; i++) {
  runTests(10000);
}

This worked! Here’s the output:

["4 (4933)", "0 (4953)", "7 (4983)", "6 (4990)", "9 (5002)",
  "5 (5010)", "1 (5017)", "3 (5029)", "2 (5038)", "8 (5045)"]
["9 (4915)", "4 (4940)", "2 (4941)", "8 (4957)", "5 (4974)",
  "7 (5011)", "3 (5044)", "1 (5046)", "0 (5077)", "6 (5095)"]
["2 (4947)", "4 (4963)", "5 (4977)", "0 (4987)", "3 (4988)",
  "9 (5002)", "6 (5013)", "1 (5022)", "7 (5034)", "8 (5067)"]
["8 (4871)", "4 (4938)", "9 (4962)", "1 (4977)", "0 (4989)",
  "2 (4993)", "3 (5012)", "6 (5051)", "7 (5057)", "5 (5150)"]
["7 (4944)", "3 (4953)", "8 (4968)", "5 (4976)", "0 (4991)",
  "4 (5017)", "1 (5024)", "6 (5028)", "2 (5030)", "9 (5069)"]

The even/odd test above works with this new method, too.

I just wonder how many faulty array shuffling implementations are out there that don’t work!

Note: This randomSort is not perfect! Identical array values will end up adjacent to one another.

Coding Javascript as Javascript

One of the most common questions that I hear at companies I’ve worked at and classes I’ve taught is, “Why don’t you write Javascript as if it’s ____ language?” Fill in the blank as you like.

The question I immediately ask is: “do you enjoy writing Javascript? Is it a fun language to code in?” The answer is invariably a strong “no!”

Javascript, for all its flaws (which are not few), is one of the most flexible programming languages ever created. It is a new type of language which is neither procedural nor class-based OOP, but can mimic both if you work hard at it. I don’t mean that it’s prototypal; that’s just one feature, and quite possibly the most misused one. I mean that it’s expressive.

Any object can be extended with any behaviour at any time, and that behaviour can be called at any time. You can write objects which aren’t classes, but groups of behaviours, and extend any other object with them when you need. Even JSON data coming in from a server can instantly be embedded with such behaviour.

Mimicking another language with Javascript is not only hard work, but will cause the author to lose the advantages of the new type of language (which, frankly, are fun when you get the hang of them!) There’s no need in Javascript for complicated class hierarchies to create a stable, maintainable body of behaviour in even the largest applications. Javascript, used properly and not as if it were another language, can create libraries and applications written in a tiny fraction of the number of lines that a classical OOP model requires. Fewer lines means less code to maintain, and less code for new team members to learn.

Do note that, due to the newness and, ironically, the flexibility of the language, these different patterns are still being developed. It’s hard for all of us to let go of old ideas and fully embrace the new ones, and deadlines often mean not doing that new feature in the way you’ve been learning (or brainstorming). Ultimately, you and the developers who follow after you (and ultimately the company) will pay the price for such shortcuts.