Monday, June 02, 2008

I couldn't resist one named "Tendrils"

While setting up the most recent addition to the house (my wife's new hi-res 17" MacBook Pro), I had a little time during Windows' installation into its VMWare image, so I took a look through some more of the visualizations. I saw this one, and ported it to JavaScript, adding a few lines to animate it:

13 long, winding tendrils starting from a central base

I recommend sticking to a low number of tendrils (especially in Opera, which gets choppy rather quickly), or the non-animated version.

Based on Tendrils, Created by Tom De Smedt (which Tom based on the "Tendrils" algorithm by ART+COM).

Labels: , ,

Saturday, May 31, 2008

Fun with visualizations

Looking through MacResearch, I saw NodeBox, "...a Mac OS X application that lets you create 2D visuals (static, animated or interactive) using Python programming code and export them as a PDF or a QuickTime movie." A bit of a hobbyist math geek, I checked it out a bit and realized that a lot of the Python source code used with NodeBox looks rather similar to JavaScript interacting with the canvas HTML5 element.

So I took a few and wrote a quick library to behave similarly to NodeBox, albeit for JavaScript to run in a browser. I just looked at the parametric equation examples for something easy to start off with, and wrote a few examples. The first, extremely simple example more or less just draws a circle within certain bounds:

function CircleEquation(dt, steps, r) {
 Frozen.Chart.Equation.call(this, steps, dt);
 this.r = r;
}
CircleEquation.prototype = new Frozen.Chart.Equation;
CircleEquation.prototype.next = function() {
 this.x = Math.cos(this.t) * this.r;
 this.y = Math.sin(this.t) * this.r;
 return Frozen.Chart.Equation.prototype.next.apply(this);
};

function circleTest1() {
 chart.clear();
 chart.map.lineWidth = 0.5;
 chart.renderEquation(new CircleEquation(4.5, 75, 95));
}

This results in the following visualization:

A spiral design made up of thin lines making up a 100 pixel circle

You can see it in action, along with a couple of slightly more interesting examples. NodeBox can do a lot more than simple charting, this part of its functionality just inspired a fun exercise in web+math geekery.

Labels: , ,

Tuesday, May 06, 2008

Opera Dragonfly (alpha) Released

Over a year ago, I wrote about the Opera Developer Console. This morning (for me, at any rate), Opera posted Dragonfly, which (two years in the making) offers a completely fresh look at browser-based debugging.

An Opera Dragonfly window showing a JavaScript console, stack trace, and active debugger, stepping through a call to add an event listener

It offers most of the familiar tools for DOM inspection (along with a nice DOM editing capability), error logging (with the same granularity as before wrapped in a more polished UI), a JavaScript debugger that rivals WebKit's Drosera, a JavaScript thread logger, and a lot more that I haven't explored yet.

Time will tell whether Dragonfly can get enough developers to use Opera and keep them there, and how much the developers behind the new developer tools listen to the community in the coming iterations, but so far this looks extremely promising.

Edit: Chris Mills from Opera gave me some additional info:

Part of the reason I haven't found the XMLHttpRequest logger/debugger apparently stems from it not getting exposed yet, though it will appear in an upcoming iteration, along with HTTP header inspection (not just for XMLHttpRequests, of course) and a new "single window mode" which sounds like it will make things much more usable! When using Safari's inspector, I almost always find myself attaching it to the window, and I always had the urge to do the same with Opera's developer console.

Chris also mentioned something which makes me very happy to hear: even though the Developer Tools (loaded via the Tools → Advanced → Developer Tools) download from Opera's server, it only does so the first time, and for each following update of the tools. This ensures that it not only saves Opera's servers when usage takes off, but it also ensures that developers can work offline, and the slow loading of the tools will only happen initially, loading from the local drive after installation.

Written in XML/CSS/JavaScript, Dragonfly will run in all browsers including Opera's Core-2.1 rendering engine (except Opera Mini for some reason), and even supports debugging on mobile devices by way of a proxy setup between the device and the desktop Dragonfly installation! This will prove invaluable for developers of web applications supporting devices, as they will have the ability to use their normal desktop tools to debug on the mobile browser without having to use emulation.

You can find more information in an Introduction to Opera Dragonfly and on the Opera Dragonfly product page (which has a very good start of fleshed-out documentation already, feedback and bugtracking, and of course a blog).

Edited to fix the Introduction to Opera Dragonfly link...

Labels: , , , ,

Monday, May 05, 2008

Heading to Google I/O at the end of the month

As an added benefit of living in the Bay Area again, I get much easier access to many of the conferences and events that go on around Ajax/RIAs/etc. May 28-29, I'll take BART into the city in order to attend Google I/O, which looks extremely interesting. Somehow, in the short two days, I need to find my way to the sessions on design patterns, performance, HTML5, open source, and chase down some of the speakers and fellow developers to pick their brains for a bit. Fun!

Labels: , , ,

Tuesday, February 12, 2008

Vienna + PHP (source code) + iUI

Source posted! BSD License. From the README:

You'll need to do a few things to get this working:

1. Move the extracted vienna directory to /Library/WebServer/Documents
2. Download iUI (http://code.google.com/p/iui/) and put the main iui directory into the extracted vienna/ directory
3a. Setup a cron job to copy ~/Library/Application Support/Vienna/messages.db to the extracted vienna/ directory
3b. For the adventurous: mv ~/Library/Application Support/Vienna/messages.db to the extracted vienna/ directory and sym-link it back to ~/Library/Application Support/Vienna/
4. Make sure to enable web sharing
5. Go to (your computer's name).local/vienna

Labels: , , , , , , , ,

Sunday, February 10, 2008

Vienna + PHP + iUI

So, sick of not having the ability to feed my XML addiction when away from my machine, I took an hour this morning and hacked together a quick'n'dirty iPhone/iPod Touch UI for my Vienna database using MacOS 10.5's standard PHP (with SQLite PDO), iui, and my Vienna database (stored in sqlite3). I gave it read-only access to the database, partly so I wouldn't have to worry about screwing it up, partly because I didn't feel like bothering just yet. I just need to add a "Mark Read" button to make an Ajax call.

I'll ask the Vienna devs if they'd like a copy of the source to toy with and generally improve, since I blatantly stole reused Joe Hewitt's design and markup for iui-enabled Digg.

Note: though my uploaded demo does pull from a copied Vienna database, it does not pull from a live one, since I wrote the code to run on a desktop MacOS 10.5 machine and not a Linux server. I then just made a bookmark on my iPod Touch to my iMac on the local network.

Labels: , , , , , , , ,

Thursday, January 03, 2008

Othello for iPhone/iPod Touch posted in Apple's Web Apps

Just saw this morning that it went up yesterday.

Labels: , , ,

Saturday, December 22, 2007

Othello for iPhone/iPod Touch

Since I woke up early today for some unknown reason and couldn't fall back asleep...I took fifteen minutes and made a game of Othello that I wrote ages (okay, six or seven years) ago in JavaScript ready for the iPhone and iPod Touch, since I now have the latter. It may not adhere exactly to the rules of the game when someone runs out of moves, but I wrote it from my memory of the game and not a rulesheet.

It doesn't have different levels of game play, or a polished look, or score saving, or anything like that. But it comes in a very small package and works just fine when not connected to a network. Basically, I only stripped out unnecessary markup, fixed the width to something friendly with the iPhone/iPod Touch's vertical orientation, and made it a single file under 5k (not counting the three images, which total 238 bytes).

Labels: , , ,

Thursday, November 08, 2007

Announcing Universe Conflict!

Posted to the new project space on SourceForge.net:

An implementation of Space War!, one of the first digital computer games, created in 1961 on the PDP-1 computer, as recreated using the <canvas> HTML5 element and Ajax. It uses the Frozen Toolkit for the client-server communication and currently renders all images and animation using canvas and JavaScript objects.

I had always intended to release this as an open source project, but then realized that I didn't really have the full time to dedicate to it. It also currently exists more as sample code and an interactive demo than anything else, and I figured I should just post it for all to see and hopefully for some to contribute. I initially posted the introduction to it on my blog.

The client-side of this game currently works in Firefox and Opera, though gravity and shooting do not yet exist. The server-side of this currently works insofar as the absolute minimum required to send messages from one browser to the other in order for the two players to fly around each other.

At any rate, check out the code (svn co https://@webspacewar.svn.sourceforge.net/svnroot/webspacewar), play around with it, and feel free to submit patches! :-) I would love to see this completed and fully playable. "Space War!" doesn't have a lot of (base) rules, so it shouldn't take much to get there.

Edit: I've also finally, finally checked in the source code for a link checker I started...oh, I think back in 2002... I created the Cocoa/WebKit branch in order to do two things:

  1. Learn Objective-C and Cocoa
  2. Use the Cocoa/WebKit API in order to take care of the HTTP handling

Labels: , , , , , ,

Sunday, June 17, 2007

Frozen Toolkit

So the toolkit came out of the book, and the toolkit basically exists primarily for education purposes. It provides an object-oriented abstraction of the XMLHttpRequest object, as well as providing a method of easily implementing event-driven application development. Throughout the book, the toolkit has grown, and by the final appendix (currently in writing) it meets OpenAjax compliance.

The toolkit has minimal documentation at this point, and zero examples (outside of the book, of course), but I rather like it, as it allows the use of Ajax connections, with support for various HTTP response codes, event dispatching available for extension, and external JavaScript file loading. However, I do have a bit of a bias, since I wrote it. The API will probably change quite a bit before it settles into itself, but it has at least stabilized to the point that I'll post a link to the current source files, consolidated and compressed source, and JSDoc2-generated docs.

Frozen Toolkit

Labels: , ,

Tuesday, May 08, 2007

Practice with the canvas tag: cellular automata

I decided to do a quick study on the canvas tag (which I'll use in the chapter on game development) before diving into the next chapter, so I ported an old PHP script that generated one-dimensional cellular automata to a JavaScript and <canvas> cellular automata display. I recommend numbers 30, 60, 182, and 225 (see Elementary Cellular Automaton on Mathworld for more, including previews of all 256 of them).

This obviously will not work in IE, and I have it defaulting to rendering them line by line so that it doesn't seize up Firefox and Safari for a few seconds (depending on which one you pick) while it draws it out. Opera displays it incredibly quickly and seems the only browser to have threaded rendering rather than locking up the browser until it finishes when drawing it all at once.

Labels: , ,

Saturday, May 05, 2007

Support for different HTTP status codes with Ajax

While writing about adding support for different status codes into the AjaxRequest object (very easy, as I'll show momentarily), I made a little test page to try each of the initial set: HTTP Status Codes. Just click each link (check Firebug) and it optionally fetches all, part, or none of a 400-line file.

Now on to the excerpt:

Most browsers and XML feed readers take full advantage of the HTTP/1.1 specification to greatly reduce the data sent from the server, and the XMLHttpRequest object gives developers access to the same functionality. Two intertwined aspects of the HTTP/1.1 specification in particular can help Ajax-driven applications: status codes and cache-control.

Supporting these in the previously defined AjaxRequest object comes easily. It already supports setting custom headers through its headers object, just like setting GET and POST variables. In order to support the various status codes that the server can return, it just takes adding event types and adding some more flexibility to the stateChanged() method by changing it from:

// Event dispatching
AjaxRequest.prototype.events = {
  abort : [],
  data : [],
  load : [],
  open : [],
  send : [],
 };

// Callback for this.xhr.onreadystatechanged
AjaxRequest.prototype.stateChanged = function() {
 // Only trigger load if finished returning
 switch(this.xhr.readyState) {
  case 3:
   var e = new AjaxEvent(this);
   this.dispatchEvent('data', e);
   break;
  case 4:
   // Only continue if status OK
   if (this.xhr.status == 200) {
    var e = new AjaxEvent(this);
    this.dispatchEvent('load', e);
   }
 }
}

...to a new version that can handle multiple status codes:

// Event dispatching
AjaxRequest.prototype.events = {
  abort : [],
  data : [],
  internalservererror : [],
  load : [],
  notfound : [],
  notmodified : [],
  open : [],
  partialload : [],
  requestedrangenotsatisfiable : [],
  send : [],
  unauthorized : []
 };
// Simple lookup of event types by status code
AjaxRequest.prototype.statusCodeEvents = {
  200 : 'load',
  206 : 'partialload',
  304 : 'notmodified',
  401 : 'unauthorized',
  404 : 'notfound',
  416 : 'requestedrangenotsatisfiable',
  500 : 'internalservererror'
 };
// Callback for this.xhr.onreadystatechanged
AjaxRequest.prototype.stateChanged = function() {
 // Only trigger load if finished returning
 switch(this.xhr.readyState) {
  case 3:
   var e = new AjaxEvent(this);
   this.dispatchEvent('data', e);
   break;
  case 4:
   if (this.statusCodeEvents[this.xhr.status]) {
    var e = new AjaxEvent(this);
    this.dispatchEvent(this.statusCodeEvents[this.xhr.status], e);
   }
 }
}

The new implementation of stateChanged() now simply triggers the event mapped to the returned status code from the request, if the AjaxRequest object implements that event type. While the list of status codes only includes the most commonly used codes for this chapter's usage, it can include any additional codes added to the events and statusCodeEvents objects.

Labels: , , , ,

Sunday, April 22, 2007

Note on JavaScript prototypes and object variables

This makes complete sense, once you think about it, but I didn't at first and it bit me.

>>> function Obj() { }
>>> Obj.prototype.position = { "x" : 0 , "y" : 0 };
Object x=0 y=0
>>> var a = new Obj();
>>> var b = new Obj();
>>> a.position.x = 10;
10
>>> b.position.x
10

This Firebug console output shows writing a simple object, Obj, and setting a prototype variable position to an object literal having two member variables, x and y, both initially set to 0. Since object instances simply reference their prototype variables when set to objects (Array, Object, Function, etc.), updating instance a's x position will update instance b's x position as well.

In order to get around this, while still setting aside a spot for the position variable in the Obj prototype, assign the value in the constructor like this:

>>> function Obj() { this.position = { "x" : 0 , "y" : 0 }; }
>>> Obj.prototype.position = null;
null
>>> var a = new Obj();
>>> var b = new Obj();
>>> a.position.x = 10;
10
>>> b.position.x
0

It doesn't look as nice, and requires that you call the parent constructor when you extend the object (always a fun process with OO JavaScript), but it works much better this way.

Labels: ,

Saturday, April 14, 2007

WAI-ARIA makes life much easier

I decided to re-post here my comment to the A List Apart article, Accessible Web 2.0 Applications with WAI-ARIA, as it seemed from most of the other comments that people don't really see just how much WAI-ARIA will help both developers and users.

In the current methods of adding screen reader support (see an example at Juicy Studio with excellent descriptions), it takes a lot of scripting in order to dynamically inform a screen reader that things have changed. These methods also rip the user out of whatever they’ve started reading or writing at that time, and drop their cursor wherever the change occurs. The later versions of Jaws don’t quite do that, but in order to support a broader user base, you need to do it that way...Jaws costs a lot of money for an individual to keep paying for every year.

With WAI-ARIA, this gets handled just by adding an attribute of aaa:live, and you can fully control the urgency with which the screen reader will read the updated DOM elements! This not only keeps the user in their current context, but allows “polite” live elements to wait for the user to complete their current task before the user needs to hear it. Errors and other urgent information can either user “assertive” or “rude” in the worst case scenarios.

This doesn’t even touch on some of the benefits of the other attributes available, but just the aaa:live attribute itself makes screen reader support infinitely easier to implement and much, much more flexible.

You can see some good examples on the Firevox (open source, cross platform Firefox screen reader) site.

Yes, this very new working draft has some time and work before it exists as a specification. But it needs support from developers in order to take off some of its rough edges, and for the more widely-used screen readers to start paying closer attention. WAI-ARIA can only help developers and we have every reason to help push this project forward.

Note: I edited the comment to use actual links and markup...and to fix some spelling errors.

Labels: , ,

Monday, March 12, 2007

Opera Developer Console

It figures I would find this as I wrap up the initial draft of the chapter on client-side debugging, but it makes me extremely happy to see it: Opera has started work on a Developer Console, which (among many other things) logs XMLHttpRequest calls.

An Opera debugging window showing HTTP, Cookies, and an XHR logger.

The Opera Developer Community runs through some of the DOM, JavaScript, and CSS tools, and the Opera Desktop team posted about it over a month ago when they initially started putting it into their development builds.

From what I can tell by peaking at the source (since, like most Opera extensions, they wrote it in JavaScript), it looks like they made a simple wrapper object and replaced the native object (after storing a backup reference for when logging gets shut off again) in order to create hooks into each of the events so it can report everything as it happens.

This means that I can now debug XMLHttpRequest usage in more than one browser, which makes life much easier than either guessing how it went or using a standalone traffic sniffer.

Edited two minutes later to add: you can get it from the Opera Developer Tools page.

Labels: , , , , ,

Thursday, March 08, 2007

Unit testing intro

Along with documentation, unit testing often gets left behind as a chore that should get done, but simply does not earn the attention from developers that it deserves. Maintaining and regularly running unit tests can have any number of beneficial effects on an application's development. These can include keeping the trunk stable, rather than having thoroughly untested changes create a ripple of frustration and lack of productivity hit other developers working on the same project.

More recently, unit testing has had a boost in popularity due to the Agile methods of software development, the short turnarounds of which thrive when the software has rigorous, frequent testing. No matter which methodology the development of an application follows, unit testing by individual developers can only help the quality and stability of the overall application.

For JavaScript, developers mostly use JsUnit, a JavaScript port of the Java JUnit unit testing tool. It works in all major browsers (IE6, IE7, Mozilla, Opera, and Safari), and provides a simple enough API to create tests almost immediately, while flexible enough to create entire nested suites of unit tests.

Labels: , ,

Wednesday, February 28, 2007

Some notes on Venkman

On a side note, after writing the bit on Venkman for the chapter on Debugging Client-side Code, I have to say that I have a whole new respect for the tool. I've generally fallen into the category of developers who installed Venkman, got a little overwhelmed at the interface, and moved on to other things in the rush to simply get things done, but I feel honestly quite a bit relieved to have sat down and gone through some use cases with it and actually learned how to use the amazingly flexible a tool. You can, for example, almost exactly replicate Firebug's JavaScript debugger within a remarkably small subset of Venkman's tools.

Additionally (I suppose, on a side-side note), Svend Tofte's tutorial definitely helped me discover some of the not-quite-so-obvious functionality Venkman offers. It doesn't provide (so far as I can tell, at least) the DOM Inspector hooks or anywhere near as concise or precise profiling as Firebug has available, but I have yet to see anything even close to its granularity around breakpoint management, from Meta Comments to conditional continuation, to Future breakpoints vs. standard breakpoints.

Labels: ,

Last call for the XMLHttpRequest object spec

The working draft for the XMLHttpRequest object has just reached the Last Call. As someone who touts the use of web standards fairly heavily, both in and out of the book-in-progress, having the ability to stop saying,"Follow web standards...except for this one bit of functionality the entire application centers around," definitely appeals.

Labels: ,

Tuesday, February 20, 2007

Rounding out chapter 3

Looking again at the UserProfile object, almost every aspect of it, from the events object to the loadValues method, can easily get pulled out into a parent object that other data models can extend. The same also goes for the ProfileController object. Using inheritence in this manner not only prevents writing redundant code, it also vastly reduces the amount of JavaScript the browsers must download in order to run the web application in the first place. Data objects then only need to contain the code specific to that data, such as custom validation or authorization checks, rather than requiring that each data object also manage its own communications with the server.

Coupling the power of inheritence with the MVC pattern, an application gains a wealth of power in the form of quickly developed modules of functionality, supporting the overall application structure. Data filtering, cleaning, and management get supported by default, along with automatic updating of the server with the appropriate data no matter how convoluted an interface the user uses to interact with it.

These methodologies, combined with event-driven application development, provide a well-rounded base for many Ajax-driven applications. By using events to trigger actions throughout the application, objects stay abstracted enough for reuse in multiple interfaces, without the need for custom code to "hook in" the objects needing to interact with it.

As with all patterns, they should get regarded as tools to use for their suited purpose rather than rules to follow. The MVC pattern can add unnecessary layers to an otherwise small and simple interface. Event-driven architectures can add meaningless abstractions and hoops to jump through when dealing with large data sets or "streaming" results. In short: design the application for the requirements at hand. Doing otherwise will add complications and make the code much more difficult to maintain and debug.

Labels: , ,

Monday, December 18, 2006

Altogether, rather neglected JavaScript functions

Terry's pointing to David Kellogg's post on JavaScript's watch() (coupled with some prompting from Jason) inspired me to add call and apply. At this point, both of these functions have made it into integral pieces of code, both for my own projects and for the book. Calling things like the following makes it infinitely easier to write OO JavaScript:

window.addEventListener('load', new Function("Throbber.prototype.attach.apply(throbber, arguments);"), false);

This needs to happen, rather than...

window.addEventListener('load', throbber.attach, false);

...as JavaScript's this can work in strange, mysterious ways to those uninitiated to prototype-based languages. In the first code block, this would return a reference to the throbber instance of the Throbber class. The the second code block, this would return a reference to the window object.

Edited (12/19/06): I just found that Bret Taylor wrote about this in July. How did I not find his site until now?

Labels:

Saturday, December 16, 2006

Updated sample and ajax.lib.js

The quick example now has some basic formatting and a link to run it.

More interestingly, from my perspective at least, I've updated the AjaxRequest and AjaxRequestManager objects.

The AjaxRequest object now has four events (open, send, load, and abort), and now has an actually readable onreadystatechange listener by using a quick trick in order to keep this references intact:

this.xhr.onreadystatechange = new Function("AjaxRequest.prototype.stateChanged.apply(requests["+id+"], arguments);");

The AjaxRequestManager now emulates support for event listening, in that it can accept addEventListener and removeEventListener calls. But instead of dispatching events, it auto-adds the listeners to each of the AjaxRequest objects it creates. This makes it a lot easier to write, for example, a Throbber object, since it can get added as a listener to each request in order to keep an eye on things while keeping things nice and loosely coupled.

Edited so you can read the code...

Labels: , ,

Friday, December 15, 2006

Current state of the Ajax library

A little large to post here, but you can check out the source and an example, where the usage boils down to:

this.request = request_manager.createAjaxRequest();
// For this example, this does nothing, but demonstrates how to set GET parameters
this.request.get = {
  one : 1,
  two : 2
 };
this.request.addEventListener('load', new Array(this, this.ran));
this.request.open('GET', 'xml.php');
return this.request.send();

In order to run the code, a simple new SimpleDemo().run(); will do the trick from Firebug (side note: sample only tested from Firefox - let me know how it fares in other browsers).

Basically, I need to ensure that usage like this makes things easier to read and port to other libraries. I honestly don't care if anyone actually uses this library, I just want the usability and abstraction to get through. The Ajax portion weighs in at just under seven kilobytes, including white-space, comments, and full names so you can actually read it, and I have every intention on keeping it that way. I've included a reference to Firebug Lite, just in case you don't have the full version installed.

Edit: okay, so the source code viewing works in Opera, but not Safari, and calling run(); from Firebug Lite pretty much does squat in either of them. Editing will commence, but I will still greatly appreciate any feedback and criticism.

Labels: , ,

Saturday, December 09, 2006

Contextually dispatching events

I just realized that I could implement EventDispatcher.prototype.dispatchEvent as (difference emphasized):

dispatchEvent : function(type, event) {
 if (this.events[type]) {
  for (var i in this.events[type]) {
   if (typeof this.events[type][i] == 'function') {
    this.events[type][i](event);
   // Accepts an array of the contextual object and the function to call
   } else if (typeof this.events[type][i] == 'object') {
    this.events[type][i][1].call(this.events[type][i][1], event);
   }
  }
 }
}

This makes it much easier to keep this references happy and I can't believe I didn't think of that until just now, since I used almost the same code to call parent object methods from a child that had overridden the method. So now you can create listeners from within object methods like this:

myeventdispatcher.addEventListener('load', new Array(this, this.myLoadEventListener));

or this:

myeventdispatcher.addEventListener('load', new Array(this, MyObject.prototype.myLoadEventListener));

Side note: I also realize I need to find a way of posted syntax-highlighted code consistently and easily. The code I had posted on Simple Event Generation came from copying the source into Kate, exporting the highlighted code as HTML, and pasting the mass of spans into Blogger.

Labels: , ,

Monday, December 04, 2006

Simple event generation

In an effort to keep each chapter and its included code samples as relevent as possible to the context around it, I decided that a toolkit made the most sense. I also decided to simple write the toolkit and include it for the following reasons:

  1. I haven't used any of the ones out there yet for long enough to use them to correctly accomplish exactly what I need without coding the samples specifically for that particular toolkit.
  2. I needed a toolkit anyway for other things I work on and (since I'll release this under the BSD License), I can write it and use it anywhere.
  3. The toolkit will only need to cover a comparatively limited set of features, so I won't re-write all of Dojo, for example.
  4. Incuding code samples like the following reinforce the overall method of development.

The following implements very simple, yet effective, event listening. Just extend the EventDispatcher with whatever object has events (form handlers save, fail, delete, load, clear, etc.) and extend the CustomEvent object to fit your needs. This gets used extensively throughout the book in order to simplify the objects through encapsulation.

function CustomEvent() { }
CustomEvent.prototype = {
 type : 'custom'
}

// Custom EventTarget equivalent
function EventDispatcher() { }
EventDispatcher.prototype = {
 // An object literal to store arrays of listeners by type
 events : {},
 
 // If it supports the type, add the listener (capture ignored)
 addEventListener : function(type, listener, capture) {
  if (this.events[type]) {
   this.events[type].push(listener);
  }
 },
 
 // If it supports the type, remove the listener (capture ignored)
 removeEventListener : function(type, listener, capture) {
  if (this.events[type] == undefined) {
   return;
  }
  var index = this.events[type].indexOf(listener);
  if (this.events[type][index]) {
   this.events[type].splice(index, 1);
  }
 },
 
 // Cycle through all of the event listeners, passing the event to the callbacks
 dispatchEvent : function(type, event) {
  if (this.events[type]) {
   for (var i in this.events[type]) {
    this.events[type][i](event);
   }
  }
 }
}

Edited so you can read the code.

12/8/06 - Edited to fix dispatchEvent to actually work.

Labels: , ,