- Improvements to $log for n0n-Firebug environments.
- Removed use of throw in $break. Slight usage changes are required, but all internal throws are gone.
- Added $foreach_sorted to address stable iteration of kes in a dictionary.
- Using Function.caller (supported by most browsers: Firefox, IE8, Chrome) when determining the super call. Depending on application requirements, this can be an even simpler way to call super methods (if Function.caller is supported on all required browsers). Sadly, Rhino does not support this property.
- General clean of up some documentation.
Saturday, April 2, 2011
ZJS 3.2 is Released
Saturday, February 12, 2011
ZJS 3.1 Released
Saturday, February 5, 2011
ZJS is 3.0 At Long Last!
One day I'll get back to refactoring the document generator and other Ant tasks to work over WSH, but that won't matter to those using ZJS.
Enjoy and let me know what you think - positive or negative.
Saturday, December 26, 2009
3.0.0rc1 Due Soon
With event support checked in, I'm updating the docs and gearing up for a build. This should be about the wrap up for v3. I'm not sure that I will crack open $interface and thoughts of $template are brewing, so I'll probably leave both of these for v4.
Thursday, December 24, 2009
Events - Part 7 (The Final Chapter)
This will be my final write up on Events (at least I hope so!). I've checked in the code and am content to let it rest for v3. After I post this, I will start updating the related docs. The classes involved are now:
- Event - This class is probably the most obvious. It is the class that is the base for all event description objects. These objects are created for each event and contain the details related to that event. Other than the data specific to the event type, all events have a consume method. This method can be called on an Event object to indicate that no further propogation or bubbling should be performed.
NOTE: it is actually the presence of a consume method that duck types an object as an Event Object. - EventSource - This class is analagous to the Observable class in ExtJS. This is the base class for objects that fire events. If deriving from EventSource is not possible, the mixinEventSource method can be used to bolt on these features to any class. The most important methods on an EventSource are on and fireEvent.
- EventHandler - This class is a base class for destroyable objects that need to also disconnect from event subscriptions. As with EventSource, if this base class cannot be used, the mixinEventHandler method can bolt on the appropriate functionality. The only method provided is mon. This works exactly like on except that it is called on the EventHandler object and the EventSource object is the first parameter. This approach was copied from ExtJS (those folks are clever).
- EventModel - This is very much a behind the scenes kind of thing. This is the class that implements the mechanics of connecting event subscribers to the event firing mechanism. The EventSource.on method does a lot of higher-level work, but then calls EventModel.subscribe to connect a method to an event. Similarly, the EventSource.fireEvent method calls EventModel.create and EventModel.fire. The EventModel then is responsible for the following:
- Connecting a method to (and disconnecting a method from) a named event on a source object.
- Creating event objects given parameters.
- Firing an event object (including bubbling).
- Connecting a method to (and disconnecting a method from) a named event on a source object.
This division of labor allows EventSource to be connected to the basic EventModel or an implementation of the DOM event model. I've sketched out how that would look in domevents.js, but am not worried about implementing and testing it or anything. In fact, the domevents.js file is not including in any of the build targets - it is purely proof of concept.
This accomplished the design goals of making an event subscription interface that would apply to both types of events and even reuses a good bit of the higher-level logic required. When the docs are posted I will explain in more detail how all the pieces are used. The good news is that it is very similar to ExtJS. If you are curious, the code comments already document all this. Perhaps one day I'll figure out a good solution to javadoc-like extraction of comments from JavaScript...
P.S. Merry Christmas!
Saturday, October 31, 2009
Events - Part 6 (Events Reloaded)
It's time for a confession. I'm afraid that my hiatus was only partially due to life getting busy. A bigger part was that I realized an error in my thinking related to events. Since I'm not inclined to revise history and pretend that I always had things squared in my mind, I'll let you read my older postings for details. I did go back and strike-through all the statements that are now changed (to avoid confusion).
The root of my wrong thinking came to light when I started to implement event bubbling. Until that point, my abstraction of event types seemed to fit. Essentially, each event had an EventType and possibly its own Event class. Both firing and listening for events on an object required that the event be declared on that object. Then bubbling happened.
The problem with bubbling is that the listener is attached to a different object than the object that fires the event. While it makes sense that an event must be declared to be fired by an object, the listener had to be less constrained. But, if the mechanism for listening were tied up with the event type (as I had it) and the event type is not knowable, we reach an impasse.
What I realized was that event handling had to be determined at the object level, not at the event level. So that meant EventType had to be refactored. More precisely, EventType had to go. That caused a bit of rework. And quite a bit of delay.
I think I've got everything put back together now, so I'll try to post a rejoinder on events soon. And post the code to SVN, of course.
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