Sunday, July 19, 2009

Events - Part 5

Now comes the fun part - listening for events. There are many special needs that arise when listening for and responding to events, so this will be more involved than the source-side of the discussion. Let's start as simply as possible.


var obj = new Base();

obj.on("foo", this.onFoo, this);

// The following will get an exception (obj has no "bar" event):
obj.on("bar", ...); // throws


In this simple example, some instance method of another object wants to listen to foo events from a Base object. The arguments are the event name ("foo"), the method to call (this.onFoo) and the instance scope to use when calling the method (this).

If you are familiar with ExtJS, you'll notice that this is pretty familiar and that is deliberate. I think ExtJS has a very clean model for event connection, so I followed its pattern. This continues for multiple event connections:


obj.on(
{
foo : this.onFoo,
bar : this.onBar,
scope: this // applied to onFoo and onBar
});

// Or:
obj.on(
{
foo : this.onFoo,
bar : { fn : that.onBar, scope: that },
scope: this // applies only to onFoo
});


And there is also the ability to filter out bubbled events (more on those next time).


obj.on(
{
foo : this.onFoo,
scope: this,
target: obj
});


And lastly, there is the ability to be fired only a certain number of times (often 1 for "just once"). This is a bit different than ExtJS.


obj.on(
{
foo : this.onFoo,
scope: this,
only: 1
});
// calls onFoo only 1 time (then disconnects)

// or
obj.on(
{
foo : this.onFoo,
bar : { fn : that.onBar, scope: that, only: 2 },
scope: this // only applies to this.onFoo
});
// calls onBar twice, but onFoo remains indefinitely


If you are really familiar with ExtJS, you're probably thinking I've missed a few things here. You are right if you noticed that "delay" and "buffer" are not present. I opted to implement these features in a more general way.

Not Now!

It is often the case that an event needs to be handled "really soon" after it is detected, but not immediately. Perhaps some of the state hasn't settled yet and you need to let the callstack unwind all the way out before doing the processing for the event. This kind of delaying is not limited to event handling, so I implemented that feature as a Function.prototype extension.


obj.on("foo", this.onFoo.delayed(10), this);


Everytime "foo" fires, the onFoo method is called 10msec later. The more complex buffer approach is also implemented as a Function.prototype extension, and has more flexibility as well.


// 10msec delay after LAST firing of foo
obj.on("foo", this.onFoo.buffered(), this);

// call 25msec after the LAST firing of foo:
obj.on("foo", this.onFoo.buffered(25), this);

// call 25msec after the FIRST firing of foo:
obj.on("foo", this.onFoo.buffered(-25), this);

// call 10msec after the LAST firing of foo but with
// the args from the FIRST:
obj.on("foo",
this.onFoo.buffered(
{
args: "first",
delay: 10
}),
this);


As you can see, the buffered method has a few more bells and whistles. Its 2nd argument is an optional "scope" parameter that determines the "this" context used for the method call. Since this is also handled by the eventing mechanism, it is not needed in this case but might be useful elsewhere.

Everyone Out Of The Pool!

It's all too easy. Fun and games really. Buy what happens when want to stop listening? Consider this ExtJS scenario:


var obj = new Base();

obj.on("foo", function () { ... }, this);


This works just fine - unless you need to disconnect such a thing. Which you cannot do as coded. This is because to disconnect a listener in ExtJS (and most other systems, e.g. our beloved DOM), you have to supply the same arguments passed at the time of connection. This can be very restrictive because you cannot use function literals or any form of function producer (like bind, head, tail, etc.).

I decided that the way window.setTimeout works is much better: it produces a token which is later used to deconstruct things via window.clearTimeout. In this case, however, the object returned by on has a destroy method which enables us to use the handy zjs.destroy function.


var obj = new Base();

this.unfoo = obj.on("foo", this.onFoo, this);

// ... later ...
zjs.destroy(this.unfoo);


A simple way to handle multiple such connections would be:


var obj = new Base();

this.unall = [ obj.on("foo", this.onFoo, this),
obj2.on("bar", ... ),
obj3.on("bif", ... ),
obj4.on("jax", ... ),
obj5.on("raz", ... ) ]

// ... later ...
zjs.destroy(this.unall); // destroys each array element


While this is a departure from most other event subscription approaches, it really simplifies the process by which cleanup is accomplished. And just in case you were curious: if the on method connected multiple event listeners, the returned object's destroy method would, of course, disconnect all of them.

Miscellaneous

Wrapping up, there is a helper method behind the delayed method that can be useful:


this.method.later(10, this);


This is very similar to the defer method provided by ExtJS, but it uses the destroyable object return value approach for consistency:


var t = this.method.later(10, this);

zjs.destroy(t); // never mind

Events - Part 4 (OBSOLETE)

At last it is time to show some examples. The simpler side to illustrate is the event source, so let's start there. I've already shown how to declare events, but here's a complete example.


Base : $class(zjs.EventSource,
{
$events :
[
/**
Documentation for the foo event.
*/
"foo"
]
}),

Derived : $class("Base",
{
$events :
[
/**
Documentation for the bar event.
*/
"bar"
]
})


The above class hierarchy defines two events. The Base class defines the foo event while the Derived class inherits foo and adds the bar event.

Ready... Aim...

Now that we've declared our events, let's fire one.


this.fireEvent("foo", { x: 42 });


The above will use the zjs.Event class for events and this call will place an x property on the event instance with the value of 42. Any number of properties can be added, which forms the contract between the event source and the listener(s).

Special Cases

Perhaps an event needs a special class to represent it. This can be done by deriving a class from zjs.EventType and putting an instance in the $events array.


SpecialEventType : $class(zjs.EventType,
{
ctor : function ()
{
$super(arguments).call(this, "specevent");
},

create : function (target, params)
{
return new MySpecialEvent(target, params);
}
}),

MySpecialEvent : $class(zjs.Event,
{
ctor : function (target, params)
{
$super(arguments).call(this, "specevent", target, params);
}
}),

SpecialFellow : $class(
{
$events :
[
new SpecialEventType()
]
})


To handle the case where zjs.EventSource cannot be used as a base class:


RockAndHardPlace : $class(SomeOtherBase,
{
$events :
[
"sameoldthing"
],

fireEvent : function (name, params)
{
return zjs.fireEvent(this, name, params);
},

on : function ()
{
return zjs.subscribeEvent.apply(this, arguments);
}
})

Saturday, July 18, 2009

Events - Part 3 (OBSOLETE)

Events are implemented in 3 classes:


  • EventType: A stateless class that manages subscribing and (optionally) generating/firing events of its kind.
  • EventSource: A base class that provides convenient methods to subscribe to and fire events.
  • Event: A base class from which events can derive.

As I mentioned earlier, I wanted to events to be 1st class citizens, but at a glance it would seem that EventSource is contrary to that end as it requires event-generating classes to ultimately derive from EventSource. To clear this up, lets look at the contents of EventSource:


EventSource : $class(
{
fireEvent : function (name, params)
{
return zjs.fireEvent(this, name, params);
},

on : function (name, fn, scope, opt)
{
return zjs.subscribeEvent.apply(this, arguments);
}
})


As can be seen, the real work of subscribing and firing events is handled by methods in the ZJS namespace. Thus, classes that derive from a base which does not trace back to EventSource can likewise provide these convenience methods with ease.

I have previously discussed EventType, but not in sufficient detail to see how one might implement a custom event type.


EventType : $class(
{
subscribe : function (obj, fn) // Required
{
},

// Optional (required to programmatically create and fire events):

create : function (params)
{
},

deliver : function (obj, ev)
{
}
})


The subscribe method is given the source object and the function to be called when the source object fires this type of event. Event types are assumed to know their name (e.g., "click"). The return value of the subscribe method is an object whose destroy method will unsubscribe the function.

The create and deliver methods are used to programmatically fire events. This can be done with DOM events, but is seldom an important use case. Even so, these methods can be implemented to create and fire DOM events. Outside the DOM, however, events must be created and fired in code since the browser will not know of them or when they should trigger.

As with browser-generated events, events in ZJS are objects. This is different than ExtJS where events are simply function calls that happen to have some arguments. For example, firing an event would be something like this in ExtJS:


this.fireEvent("foo", this, x, 42);


In ZJS, events are always a single object (as it is with DOM events). The reason for this (beyond simple consistency which would likely have been enough reason) is because there are behaviors for events themselves that are not related to their data members. The most important of these is the ability to control the propagation of an event. Further, by passing a single object, the event source is free to refactor and extend the data it supplies. The need for a container object with DOM events is plain because these events often have over a dozen properties!

The zjs.Event class is a basic implementation of a custom event. Obviously, a DOM event would not derive from zjs.Event, so it really serves as both an example of the interface to which events should conform as well as an implementation for the cases where it does fit. The method exposed by zjs.Event that should be supplied by all event types is consume.

In the DOM, there are two ways to effect event propagation. On the one hand, a DOM event's bubbling around for other event subscribers can be stopped. On the other, the default behavior provided by the browser for the event can be canceled (most of the time). In my experience, it is seldom that I want to cancel only one of these, so the consume method would handle both for this type of event.

Well, I think I've covered all the building blocks and concepts. Next time I'll put together an example of defining, firing and handling events.

Friday, July 17, 2009

Events - Part 2

Last time I talked about events in ZJS v3. There were many ways I considered for implementing events, but in the end I settled on a nested object to contain the event type definitions and linked that object to both the class and the instance (via the prototype). In other words:


$namespace(foo,
{
Bar : $class(
{
$events :
[
"beforechange",

"change"
]
});
});

var b = new foo.Bar();

assertTrue(b.$events === foo.Bar.$events);

// This is redundant given the first assert, but for clarity:
assertTrue(foo.Bar.prototype.$events === foo.Bar.$events);


The above could have been implemented "directly" in the $class and $mixin logic, but I decided to indulge of bit of architectural overkill and instead I added the concept of Class Plugins. This reduced the footprint of events in the core logic to about 3 lines of code which delegated the work to the plugin.

In the past I had briefly considered making $static work like the above. This would have made it much more direct to reach the statics of your class (this.$static) and would have allowed statics from base classes to be "inherited" due to the prototype chain on the $static object. For various reasons I decided against this, but now, with two uses cases in hand that had the same basic pattern, I figured a more general solution wouldn't be a bad idea. That and the logic in $class and $mixin was already complex enough that I shuddered to think of making it even more so.

With the plugin approach, the extra core code looks like this:


$class.plugins = {};

...

if (name in $class.plugins)
return $class.plugins[name].process(klass, v);


The above test is performed on each member of the object literal being mixed in to a class. The only method that is necessary for a Class Plugin object is the process method that accepts the class (i.e., the constructor function) and the value on which to operate. For convenience, there is a class that implements the machinery (described for chaining and adding to the prototype as well as the class) called zjs.ClassPlugin. The implementation of the "$events" plugin simply derives from zjs.ClassPlugin.

As can probably be guessed, the interesting work of converting an array of strings into something useful for event handling takes place in the plugin. In fact, it was this difference in behavior regarding what to do with the values that pushed me solidly into the whole plugin approach. You see, with events in ExtJS, you call addEvents and pass a bunch of strings. That's it. The string is simply the event name and that is sufficient to declare the fact that an event exists.

In ZJS, these strings are converted into zjs.EventType objects. I'll cover the details next time, but basically the EventType handles subscribing for events as well as creating event instances and triggering delivery of said events. Because these are the key differences between hand rolled events and true DOM events, one can implement EventTypes that do these jobs.

While the zjs.EventType class implements the necessary apparatus for custom events, I have also implemented a proof of concept DomEventType. I wanted to do this to prove to myself that the mechanism can be extended in that direction. It seemed reasonable to pick DOM events because they require a completely different approach to all aspects of their life cycle.

Because of the EventType abstraction, the zjs.EventSource class can provide a consistent interface to any type of event. But that is a story for next time.

Sunday, July 5, 2009

Events - Part 1

Recently I started using ExtJS at work. I have to say that I really like it. They've done a great job at consistency and predictability. It's not perfect (what is really?) and I do a fair bit of fighting with it to make it do what I need. But, of the large JavaScript frameworks out there, I have to give my vote for the winner being ExtJS.

There was a key concept in ExtJS (and YUI and others I'm sure) that I hadn't given enough thought to in ZJS: events. The reason I had skipped over eventing in ZJS is its entanglement with the DOM and browser. The core features of ZJS ($class, $namespace, et.al.) do not require a browser. They are deliberately language-centric and can run in just about any JavaScript host. For example, I've tested ZJS core.js under Rhino and WSH (Window Script Host).

Well, ExtJS reminded me that I can implement events in a similar manner. It will be limited somewhat in environments that don't have asynchronous notifications flying about, but ExtJS had good use for events from its Store objects and grids. The Store would fire events whenever data was loaded, for example, which did not require any advanced features.

In ExtJS, the event source classes derive from Ext.util.Observable. This class provides the addListener (a.k.a. "on") and removeListener (a.k.a. "un") methods and many others as well. A class that publishes events does so like this:


foo.Bar = Ext.extend(Ext.util.Observable,
{
constructor : function ()
{
this.addEvents(
"beforechange",

"change"
);
}
});


This creates some minimal bookkeepping for each event which is expanded when (or if) a listener comes along. This helps keep the memory footprint down. There are two things that bother me about this approach for events.


  • Events are 3rd class citizens in the object model. They are not added to a class like other pieces - rather a method of a specific base class does the work.
  • Being defined on the instances and not on the class not only wastes memory but also means there is no data about events at the class level (without an instance).

Events should be treated similarly to the methods and properties of a class - ideally declared in some fashion. There should be no required base class to expose events. Events should be inheirtable and have zero per instance overhead (until a listener arrives). Lastly (for now), the mechanism should be extensible enough to support DOM events and their many quirks.

Here's the syntax I've settled on:


$namespace(foo,
{
Bar : $class(
{
$events :
[
"beforechange",

"change"
]
});
});


Next time I'll cover how the above syntax was implemented and how to make use of it.

Friday, July 3, 2009

JSLint

One of the most frustrating things about writing in JavaScript has to be the general lack of help catching syntax errors. This is closely followed by the lack of ability to check for semantic errors. Due to JavaScript's late binding nature, and its history of being a component of the browser, these kinds of tools are only now (and slowly) appearing.

Some time ago I discovered jslint. Like many such tools, the only way to use it was to paste your code into the textarea of a web page and then click a button to get the report. While this works and does find your problems, it is not ideal for continuous use and is tedious to use for resolving more than a handful of reported issues. What I wanted was a JavaScript "compiler": something that could be integrated into the normal development cycle to find errors as I write code. Some time back I incorporated jsmin into the ZJS build process as an Ant task. Since the code for jsmin was written in JavaScript, I used a <scriptdef> to wrap the code in the necessary boilerplate.

As I wrote last time, I have recently extended <jsmin> to produce <jsbuild>. Where <jsmin> is purely fileset based, <jsbuild> actually reads the files to determine dependencies. Since I was already in the code, I figured I might as well dig in and include jslint as well. The code for jslint (5000+ lines) is a tad larger than jsmin (300+ lines)! And it had a particular philosophy on code style that I couldn't tweak via its provided options. You see, jslint likes this:


if (x == 4) {
doSomething();
}


But hates this:


if (x == 4)
doSomething();


This is clearly documented (see "Required Blocks") and when I asked Mr. Crockford if he would consider adding an option to not generate warnings for this style, he replied with a rather blunt "Nope.". Given the plethora of options (30 or more) in jslint, I was taken aback by his refusal to accommodate such a simple aesthetic difference of opinion. He's entitled, of course, and I cannot exactly complain since he is giving away the code for jslint. So I added the "reqblocks" option to his code as I incorporated it into ZJS.

It turns out that it was an utterly trivial insertion of a single "if" statement - the hard part, as you might imagine, was figuring out where to place it!

So now with the guts of <jslint> ready to go, I added a bit more logic to <jsbuild> to be able to invoke a nested sequential task whenever it decided to generate its output. The build script now looks like this:


<jsbuild out="${build.dir}/@{tag}/src/zjs/zjs-all.js"
level="@{jsmin}" verbosity="quiet"
packages="zjs=${src.dir}/zjs" modules="zjs.**">
<comment>${jscomment}</comment>
<onbuild>
<jslint src="${build.dir}/@{tag}/src/zjs/zjs-all.js"
report="${build.dir}/@{tag}/zjs-all-lint.html"
options="reqblocks=false,strict=false,onevar=false,undef=true,browser=true,forin=true"
console="true" globals="false" if="'debug' == '@{tag}'"
/>
</onbuild>
</jsbuild>


I experimented with <jslint> all by itself against each individual file:


<jslint options="reqblocks=false,strict=false,onevar=false,undef=true,browser=true,forin=true"
console="true" globals="false">
<fileset dir="${src.dir}/zjs">
<include name="*.js"/>
</fileset>
</jslint>


The <jslint> task supports the nested <fileset> or the "src" attribute, but what I found was that jslint gets cranky when dealing with files that depend on each other. It complains about any globals defined in file 1 and used in file 2. This is because I had to invoke jslint anew for each file for its reports to have the proper line numbers.

Things worked much better when jslint was invoked against the output file from <jsbuild> since it had included (almost) all dependencies. While there may still be gaps (due to excluded modules or packages), the result was much closer to what I wanted. The last detail I had to sort out was how to streamline dealing the numerous reports spewing from jslint. By default, the jslint report looked like this:

Problem at line 1234 character 17: Missing semicolon.
return x

The "problem" with this format was that the NetBeans output window just viewed it as dumb text. I wanted to be able to click on the output and jump to the offending code. I also did not want to write a NetBeans plug-in to do the necessary hyperlink magic!

And it turns out I didn't have to. It dawned on that tools like javac and Ant were unaware of NetBeans and its output window, yet their output was getting linked to the source. I figured there must be some recognizer in NetBeans that translates certain output patterns into hyperlinks. To test this out, I remembered that the output of javac for the same diagnostic would have looked like this:

/foo/inputfile.js:1234: Missing semicolon.
return x
^

With a little HTML stripping (the jslint report is laden with HTML) and some regex juju, I translated the jslint output into the javac format - and bingo! Links in my output window! Granted they go to my generate zjs-all.js file, but that's good enough for now! Well, that and the "if" attribute on jslint so I can run it only on the debug build - jslint also doesn't like the output of jsmin as its input. Go figure.

Future: As the dust settled, I realized what is really called for in <jslint>: a hybridization with <jsbuild>. If I could express the same package paths and scan for dependencies, I could squeeze the last of the noise out. I could produce a temporary buffer that contained all the dependent files for each input file (even those I would normally filter out in my <jsbuild>) and pass that to jslint. I could then scrub all the messages from jslint that did not apply to the lines coming from the target input file and rebase the line numbers as well. With a little caching of these files and dependencies to speed things up, I could see getting a convenient and super accurate result pointing at the proper source files.

This would easily take 4-8 hours or more and I'm just not feeling like it today. I'll just sit happy with my now jslint clean build of ZJS!