These two demos don't cover all of the complexities in finding an element's position. I didn't even touch tables.
There are also complications with padding and border on HTML root element. Unfortunately, IE8b1 doesn't support CSS
on the HTML root element (connect id 354453), so it can't be tested.
APE guards against all of the other problems by having a test suite that
tests this function with combinations of these cases.
This article describes the generic concepts for creating and mixing design patterns.
The basic principle for all design patterns is: Encapsulate the parts that vary.
The problem is finding a way to create a generic Factory that can be reused on
various constructor functions for element Decorators. This article explains
the problem and the process for finding the solution.
Decorator Factory Aspect
A Decorator Factory Aspect is a
Factory method, added as an Aspect to a constructor of a Decorator.
Before I explain how to add a Factory to a constructor function for an element decorator,
I should first define Decorator (also called a wrapper), Factory and Aspect.
Decorator Pattern
makes it possible to extend (decorate) the functionality of a class
by adding a new decorator class that wraps the original class.
(Wikipedia link)
Factory Pattern
The Factory pattern is a creational design pattern
that encapsulates the processes of creating objects
(Wikipedia link)
Aspect
introduces separation of concerns, specifically cross-cutting concerns, as an advance in modularization
(Wikipedia link)
Decorator Examples
Decorator is very common in JavaScript. For example: YAHOO.util.Element decorates an element,
jQuery decorates an array of elements.
Factory Example
The Factory gets or creates a decorated element.
The id of the wrapper is the same as the id of the element.
This is the part I want to make reusable:
/**
* @constructor
* @param {String} id - the id of the element and widget.
*/
function ElementWrapper(id, x) {
this.id = id;
this.x = x;
}
// Factory.
// TODO: How can I make this generic/reusable?
ElementWrapper.instances = {};
ElementWrapper.getById = function(id, x) {
if(this.instances.hasOwnProperty(id)) returnthis.instances[id];
return this.instances[id] = new this(id, x);
};
ElementWrapper.prototype = {
show : function() {
document.getElementById(this.id).style.visibility = "visible";
}
};
Benefits
Solves the problem of creating only one decorator per element id.
By calling getElementById, the decorator can avoid
some of the problems with changing node references with innerHTML (though state changes
must still be managed manually).
Problem: DRY
Don't Repeat Yourself
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
It is cumbersome and error-prone to write out a Factory each time.
Since this is an idiom I use a lot, it makes
sense to make it reusable.
I want to have a generic getById method that can be reused and will return
an instance of the constructor that it is called on. I want to be able to pass
extra arguments to that constructor (varargs).
Encapsulate the Parts That Vary
What varies?
The id parameter variable of getById does not change; it will
always be present in any generic Factory.
The parts of the Factory that vary are: The additional zero or more arguments (varargs,
this case, x),
and the context, or thisArg.
Resolving the context arg is easy.
If I can solve passing varargs to a constructor in a generic context,
it will be possible to create a generic Factory Aspect.
Function newApply
A way to call new with variable arguments would solve this problem.
A new + apply() would provide the varargs functionality of apply,
but passed to [[Construct]], not [[Call]].
This has been solved in APE core.
The source code for APE.newApply:
/**
* @param {Function} fun constructor to be invoked.
* @param {Array} args arguments to pass to the constructor.
* Instantiates a constructor and uses apply().
*/
newApply : function(fun, args) {
if(arguments.length === 0) return;
varf = arguments.callee, i;
f.prototype = fun.prototype; // Add prototype.
f.prototype.constructor = fun;
i = newf;
fun.apply(i, args); // Apply the original constructor.returni;
}
What's it Good For?
Now I can create the generic getById function I wanted. This function can be added as an aspect
to any constructor function. Factory Aspect APE.getById is a part of
APE core.
This getById method can be used with ElementWrapper (above)
or any other constructor that acts as a Decorator to an element and
accepts the element's id as its first argument.
In most patterns, encapsulating the parts that vary entails creating an class.
However, in JavaScript, this particular pattern was simple to implement by using just
two functions (APE.getById and APE.newApply) and leveraging the dynamic nature of
JavaScript.
Forward to ES4
ES4 has had some proposals for something called a splat operator.
fun(...argsOrArray);
new fun(...argsOrArray);
This proposal will allow passing varargs to a constructor or a function call.
It is unclear how the splat operator, if accepted into the language, will work with
functions which expect typed arguments, in strict mode.
By using a test driven approach, I was able to avoid problems that I found in other libraries.
APE Core
The core of APE is so small that the overhead of adding APE to a project is significantly less than that of other libraries.
I generally don't like a lot of the libraries that use a "GOD" object approach. These objects can be recognized by being undefinable by more than a few words. The approach is quite popular and annoys me to no end. How do you describe the dojo object, for example?
The APE object itself is defined in APE.js, providing only the most basic things that a framework would need: 1) Object creational features, and 2) a core namespace. Although some might argue that a library core needs more than that.
The APE object is only 3k minified and less than 1k gzipped. The size is not the point. The point is that APE is not a kitchen-sink, or God object.
Help Wanted
You can join, too! Here's what APE needs:
New Features
There is much more that can be added. For example, some ADT's: Resizable (croppers, windows), FormSerializer.
SVN and Bugzilla
I still need to add hosted SVN to the directories and install bugzilla.
Performance Tests
I've always been big on performance, and not just for JavaScript but all areas of life, and I am very competitive in nature.
APE needs benchmarks to compare with other libraries. This will provide useful analysis for other library authors who aspire to have code that is as performant as that in APE.
Anyone interested in joining this project contact dhtmlkitchen - at - gmail - dot - com.
An Event Notification System is an object that manages notification of events to multiple callbacks.
The Event Notification System uses an Event Registry to store the callbacks as bound methods. When the event
fires, the callbacks are invoked.
Event Registry
An Event Registry is a store of bound methods.
An Event Registry is used by an Event Notification System.
The Event Notification System is tightly coupled with the Event Registry. Sometimes it is referred to as the Registry.
In reality, the Registry is just a data structure and the Event Notification System is a behavioral object.
Almost Every JavaScript library has an Event Registry, or at least some way of dealing with event notification.
For example:
// YUI:
YAHOO.util.Event.addListener( link, "click", linkClickHandler, thisArg );
// Prototype: (not a registry, but the old 'addEvent' function renamed).
Event.observe( link, "click", linkClickHandler );
// Dojo:
dojo.connect( link, "onclick", window, "linkClickHandler" );
They're all different in how they work.
The Event Registry is useful for a few reasons.
It allows multiple callbacks to be assigned to a function call.
Provides a usable alternative to attachEvent. Internet Explorer 7 and below has attachEvent/detachEvent. The callback function for attachEvent executes in global
context (this is window), not the object it was attached to.
A good Event Registry solves these problems. A good Event Registry also allows for context resolution with an optional
thisArg. A good Event Registry also allows custom events to be registered using the same interface.
A poorly designed Event Registry concerns itself with things related to native events (DOMContentLoaded, keyPress, et c). A poorly designed
Event Registry does not pass an event object to the callback (perhaps trying to use eval to pass varargs).
Error Handling in an Event Notification System
Callback Errors Should not Break the Registry
A good Event Registry does not allow any callback to break the registry.
One common problem in most Event Notification Systems (such as Dojo, Mochikit, YUI, and jQuery) is that they allow the callback to break the System. If a callback fails, it prevents subsequent callbacks from firing. A callback should not be given the ability to break the Registry.
Here's how to break a Registry that doesn't consider errors:
var passed = false;
addCallback( link, "click", function(){ setTimeout(checkTitle, 500); } );
addCallback( link, "click", function(){ throw Error('bad'); } );
addCallback( link, "click", function(){ passed = true; } );
function checkTitle(){
if(!passed)
alert("registry broken: second callback did not fire.");
else
alert('passed');
}
Callbacks sometimes throw Errors. It is important for the Event Registry to consider this and take the responsibility to handle these errors properly. If an error occurs in a callback, it should not break the Registry.
It should be guaranteed that all callbacks fire, even when earlier callbacks throw errors. This is a natural
expectation; it's exactly how DOM Events work:
DOM Events Test
(function(){
var s = document.getElementById('r-test');
var el = document.getElementById("registry-dom-event-button");
if(!window.hasDocumentListeners) {
el.addEventListener( "click", setUpCheck, false );
el.addEventListener("click", throwError, false );
// setTitle must fire.
el.addEventListener( "click", setTitle, false );
window.hasDocumentListeners = true;
}
function setUpCheck(){ setTimeout(checkTitle, 500); }
function throwError(){ document.title = ""; throw Error('bad'); }
function setTitle(){ document.title += 'ok'; }
function checkTitle(ev) {
if(document.title != "ok") {
alert("DOM Events broken: setTitle did not fire. " + document.title );
}
else {
alert("passed");
}
}
})();
Result and Analysis
There should be 1 error and an alert passed. This indicates that after the error happened, the setTitle callback successfully fired.
This example assumes:
Callbacks fire in the order in which they were registered.
The bad error in the first callback does not stop subsequent callbacks from firing.
The button supports onfocus (not in safari bug 16331).
Proper Callback Error-Handling
Throwing the error in a separate thread allows the callstack to continue without breaking. Any errors that are thrown
are thrown in the correct order in the callstack. The Event Publisher's fire
function would have something like this:
try {
// If an error occurs, continue the event fire,
// but still throw the error.
callback.call( thisArg, ev );
}
catch( ex ) {
setTimeout("throw ex;", 1);
}
The one subtle issue is that setTimeout uses global scope, like the Function constructor, not like eval, which runs in the calling context's scope.
A closure must be used to preserve the ex variable.
try {
// If an error occurs, continue the event fire,
// but still throw the error.
callback.call( thisArg, ev );
}
catch( ex ) {
setTimeout(function(){ throw ex; }, 1);
}
Event Registry Test
The remaining problem with the above code is that the error condition is untestable. Writing a test suite forced me to realize this
and I changed the design.
I have included the source code for my own Event Registry, along with this
test,
which shows how I managed to test APE.deferError.
Performance?
Wrapping each callback call in a try catch might seem to be bad for performance.
I tried it with mousemove event on my drag code, dragging multiple drag objects at a time
(example), and it seemed fast enough; I did not notice performance
problems in any browser. There is most likely some performance overhead using this approach,
but I did not find a need to write a benchmark.
src should never be a string. Although this may seem obvious, YUI actually allows src to be a string,
where the string represents an element's ID. The document is polled regulary until
the element with the id matching string is found and then the callback is attached to that element. If the element has been renamed, the document is still polled and silent failure occurs.
This can lead to silent failure or corrupted application state if the element is not found. It is not recommended.
Packaging and API Design
The Event Notification System is a low level component with no external dependencies.
Being a low level component, the Event Notification System should be maximally stable (no efferent couplings), and maximally abstract. In this case, the Event Notification system is maximally abstract because
it can't be subclassed or used independently.
Stable Dependencies Principle
Depend in the direction of stability
Stable Abstractions Principle
A package should be as abstract as it is stable.
Reuse Equivalence Principle
The Granule of Reuse is the Granule of Release.
The Event Notification System is a low level component with no external dependencies. It is intentionally
packaged as a single, tested unit. It amplifies the essential (event notification) and eliminates the irrelevant.
Creating special cases for handling DOM events (keyCode, et c),
would reduce abstraction. These special cases are perfectly valid, but do not belong
in the Registry. Special case needs can either be hard-coded into end-implementation code
(using feature/capability detection) or, if the special-case logic is complex, programmed into an object that performs a task (such as an Adapter object).
An example of an Adapter object would be
a Content Load Adapter or a KeyEvent Adapter (key events are highly inconsistent across platforms).
Such objects would be slightly higher-level and, having at least one dependency, would be less stable (though this is not a bad thing).
Department Store JavaScript
[insert_popular_library_name_here] usually include more code than any one application could possibly use in an attempt to
cover the needs of every application.
Libraries that add more functionality into one module than is usually needed, or create modules
that are not cohesive do so in spite of commonly known software package design concepts. The one-stop library approach is appealing because it allows developers to "stop cobbling bits of javascript."
Performance (Again)
Load Time Performance problems can be acheived by creating custom javascript builds on the server. Hand-rolled "combination" files or utils files are fine for web sites with fewer pages. Sites that don't require 200k+ of additional javascript should not include such functionality.