Limiting the contents of a string via RegEx

Often, you will need to prevent users from entering data that doesn’t conform to a specific pattern. For example, you may want to allow users to enter only numbers or only valid email addresses. To this end, I’ve written a little utility function that returns the “standardised” version of a string, according to the regex you supply.

String.implement({
	limitContent: function(allowedRegex) {
		return $splat(this.match(allowedRegex)).join('');
	}
});

Basically, the function takes the result of evaluating the regular expression on the string, converts it into an array if it isn’t one, and then joins the array’s elements together with an empty string.

Examples:

console.log("12345".limitContent(/.{4}/)); // Only allow four characters
console.log("joe@mail.com".limitContent(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}/)); // Only allow email addresses

MooTools object messaging

Events

In JavaScript, we often tend to use events all over the place. In MooTools, the custom ‘domready’ event is particularly prevalent. However, events suffer from a few drawbacks:

You can’t attach events to non-existent objects.

Pretty self-explanatory, really. What this means in practice, though, is that you can’t easily let object A know when object B exists.

If an object starts to listen for an event after it’s already fired, it’ll never hear it.

Because content on the web isn’t always delivered in perfect order (especially when you’re loading scripts synchronously), it’s possible for an object to try to listen for an event after it’s already been fired. Obviously, this means that your listener object will never run the code that’s dependent on that event, which could be Bad Thing™.

It’s not particularly easy to know which object is listening for which events.

There are ways around this, but you can’t just dir() the listeners in Firebug.

A messaging system

For all of these reasons (and probably a few more that I’ve forgotten about), a messaging system can be an invaluable addition to your arsenal when writing JavaScript. How does a messaging system work? Well, interested objects ‘register’ themselves as listeners for particular message ‘handles’, and other objects can send messages using those ‘handles’. Below is a very simple MooTools messaging system that I knocked up, which has a few cool features, including:

  • When you register() a listener, you can have its callback immediately fire if that message has ever been sent before.
  • You can very easily see which callbacks are associated with which messages by simply dir()-ing the ‘listeners’ member.
  • You can unregister() a listener at any time (provided you’ve got a reference to the function and the handle).
  • Handles can be any valid JavaScript type — Strings, Numbers, even Objects.

Feel free to use and extend this system — as I mentioned, this is a very simple system. If you do extend it, let me know in the comments!

Let me know what you think about this system in the comments.

barryvan.base.Messaging = new Class({
	listeners: $H(),
	sentMessages: [],
	
	initialize: function() {
		
	},
	
	/**
	 * Register a listener for a particular handle.
	 * handle [String]: The message 'handle' to listen for.
	 * callback [Function]: The function to be called when the handle is sent a message. The contents of the messages will be included in the function call.
	 * dontCheck [Boolean]: If falsey and the handle has had a message sent to it, immediately call the callback function (without contents), and continue to add the listener as normal.
	 */
	register: function(handle, callback, dontCheck) {
		if ($type(callback) !== 'function') return;
		
		if (!dontCheck && this.sentMessages[handle]) {
			callback();
		}
		
		if (!this.listeners.has(handle)) this.listeners[handle] = [];
		this.listeners[handle].push(callback);
	},
	
	/**
	 * Unregister a listener for a particular handle.
	 * handle [String]: The message 'handle' to cease listening for.
	 * callback [Function]: The function which was earlier assigned as the callback for the messages.
	 */
	unregister: function(handle, callback) {
		if (this.listeners.has(handle)) {
			this.listeners[handle].erase(callback);
		}
	},
	
	/**
	 * Send a message to the given handle with the given contents -- send the contents to all the registered listeners for that handle.
	 * handle [String]: The message 'handle' to transmit to.
	 * contents [Mixed]: The contents to be sent to the listeners.
	 */
	send: function(handle, contents) {
		this.sentMessages.include(handle);
		if (this.listeners.has(handle)) {
			this.listeners[handle].each(function(callback) {
				callback(contents);
			});
		}
	}
});

Javascript-generated tables and rowspan

At work, I’ve recently been putting together a nice little calendar-like utility using Javascript. Basically, it has to generate a table consisting of cells which may span multiple rows. Surely the solution is simple enough: just set the rowspan on each td as we create it. Unfortunately, that doesn’t work, at least not in Firefox.

It appears that in Firefox, if you create a td and set its rowspan to some value when there are no rows for it to expand into, the attribute will be completely ignored, even if you add rows afterwards! Needless to say, this is very annoying. The solution? Build your table backwards.

The code I have now is something like this (note that I’m developing using the Mootools framework):

var tbl = new Element('table');
var trs = [];

for (var i = 0; i < 4; i++) {
  var tr = new Element('tr');
  tr.grab(new Element('td', {
    'html': 'Cell ' + i
  }));
  if (i % 2 == 0) {
    tr.grab(new Element('td', {
      'rowspan': 2,
      'html': 'Span ' + (i / 2)
    }));
  }
  trs.push(tr);
}

for (var i = trs.length - 1; i >= 0; i--) {
  tbl.grab(trs[i], 'top');
}

What does this code do? Well basically, we’re creating a table with ten rows and two columns; the cells in the right-hand column each occupy two rows. The result will be something like this:

Cell 1 Span 1
Cell 2
Cell 3 Span 2
Cell 4

MooTools and OO Javascript development

I’ve started work on a new project at my job — a fairly complex AJAX application for the education sector. For this project, I’ve been allowed to essentially choose my own direction, and I’ve chosen to implement the clientside Javascript using the MooTools framework. I’ll say it right here: I’m absolutely loving it.

What I’m really enjoying about MooTools is the object-orientedness it brings to development. Although syntactically it’s a little bit weird at first, the ability to create, extend, and implement classes makes my development progress much more quickly, and in a more efficient way. Add to that the plethora of utilities (like the .each prototype for arrays) and shorthand functions (like $ to replace document.getElementById), and all of a sudden Javascript development becomes a bit more, well, flexible.

I’m not saying that you can’t accomplish cool things in Javascript outside of MooTools (or other frameworks, for that matter); my point is that I believe you can accomplish cool things in Javascript more quickly using a good framework, which should really come as no surprise. Perhaps the reason I’m so enjoying this type of development, to the point of blogging about it, is that up till now, I’ve been stuck working in a non-frameworked, very non-OO Javascript development paradigm.

I mentioned the curious syntax that accompanies MooTools.  To create a new class, for example, you would probably write something like this:

var myClass = new Class({
  Implements: Options,
  options: {
    optionA: 'monkey',
    optionB: 'pony'
  },
  initializer: function(options) {
    this.setOptions(options);
    this.doSomeStuff();
  },
  doSomeStuff: function() {
    alert(this.options.optionA + ' eats ' + this.options.optionsB);
  }
});

And then you would initialise it like this:

var myInstance = new myClass({
  optionA: 'Big Pony'
});

Although it looks a bit weird, it’s actually not too bad. There are really only two problems I have with it:

  1. Remembering to put commas in all the right spots.
  2. Geany, my preferred IDE (cf. Geany IDE: Tango dark colour scheme) can’t pick up classes and members properly (actually, at all) in this style.

Other than that, though, I’m really enjoying it.