(function(window, undefined) {

	"use strict";

	var d3 = window.d3;

	var errors = {
		noBase: new Error("d3.layer must be initialized with a base"),
		noDataBind: new Error("d3.layer must specify a `dataBind` method."),
		noInsert: new Error("d3.layer must specify an `insert` method.")
	};

	var Layer = function(base) {
		if (!base) {
			throw errors.noBase;
		}
		this._base = base;
		this._handlers = {};
	};

	// dataBind
	Layer.prototype.dataBind = function() {
		throw errors.noDataBind;
	};

	// insert
	Layer.prototype.insert = function() {
		throw errors.noInsert;
	};

	// on
	// Attach the specified handler to the specified event type.
	Layer.prototype.on = function(eventName, handler, options) {
		options = options || {};
		if (!(eventName in this._handlers)) {
			this._handlers[eventName] = [];
		}
		this._handlers[eventName].push({
			callback: handler,
			chart : options.chart || null
		});
	};

	// off
	// Remove the specified handler. If no handler is supplied, remove *all*
	// handlers from the specified event type.
	Layer.prototype.off = function(eventName, handler) {

		var handlers = this._handlers[eventName];
		var idx;

		if (!handlers) {
			return;
		}

		if (arguments.length === 1) {
			handlers.length = 0;
			return;
		}

		for (idx = handlers.length - 1; idx > -1; --idx) {
			if (handlers[idx].callback === handler) {
				handlers.splice(idx, 1);
			}
		}
	};

	// draw
	// Bind the data to the layer, make lifecycle selections, and invoke all
	// relevant handlers.
	Layer.prototype.draw = function(data) {
		var bound, entering, events, selection, handlers, eventName, idx, len;

		bound = this.dataBind.call(this._base, data);

		if (!(bound instanceof d3.selection)) {
			throw new Error('Invalid selection defined by `dataBind` method.');
		}

		entering = bound.enter();
		entering._chart = this._base._chart;

		events = [
			{
				name: 'update',
				selection: bound
			},
			{
				name: 'enter',
				// Defer invocation of the `insert` method so that the previous
				// `update` selection does not contain the new nodes.
				selection: this.insert.bind(entering)
			},
			{
				name: 'merge',
				// This selection will be modified when the previous selection
				// is made.
				selection: bound
			},
			{
				name: 'exit',
				selection: bound.exit.bind(bound)
			}
		];

		for (var i = 0, l = events.length; i < l; ++i) {
			eventName = events[i].name;
			selection = events[i].selection;

			// Some lifecycle selections are expressed as functions so that
			// they may be delayed.
			if (typeof selection === 'function') {
				selection = selection();
			}

			if (!(selection instanceof d3.selection)) {
				throw new Error('Invalid selection defined for "' + eventName +
					"' lifecycle event.");
			}

			handlers = this._handlers[eventName];

			if (handlers) {
				for (idx = 0, len = handlers.length; idx < len; ++idx) {
					// Attach a reference to the parent chart so the selection"s
					// `chart` method will function correctly.
					selection._chart = handlers[idx].chart || this._base._chart;
					selection.call(handlers[idx].callback);
				}
			}

			handlers = this._handlers[eventName + ":transition"];

			if (handlers && handlers.length) {
				selection = selection.transition();
				for (idx = 0, len = handlers.length; idx < len; ++idx) {
					selection._chart = handlers[idx].chart || this._base._chart;
					selection.call(handlers[idx].callback);
				}
			}
		}
	};

	d3.selection.prototype.layer = function(options) {
		var layer = new Layer(this);
		var eventName;

		// Set layer methods (required)
		layer.dataBind = options.dataBind;
		layer.insert = options.insert;

		// Bind events (optional)
		if ("events" in options) {
			for (eventName in options.events) {
				layer.on(eventName, options.events[eventName]);
			}
		}

		// Mix the public methods into the D3.js selection (bound appropriately)
		this.on = function() { return layer.on.apply(layer, arguments); };
		this.off = function() { return layer.off.apply(layer, arguments); };
		this.draw = function() { return layer.draw.apply(layer, arguments); };

		return this;
	};

}(this));

@@ -877,17 +877,16 @@
 %09%09%09chart
- 
 : option
@@ -901,16 +901,16 @@
 %7C%7C null%0A
+
 %09%09%7D);%0A%09%7D
@@ -1720,33 +1720,33 @@
 throw new Error(
-'
+%22
 Invalid selectio
@@ -1776,17 +1776,17 @@
  method.
-'
+%22
 );%0A%09%09%7D%0A%0A
@@ -1885,16 +1885,16 @@
 me: 
-'
+%22
 update
-'
+%22
 ,%0A%09%09
@@ -1937,15 +1937,15 @@
 me: 
-'
+%22
 enter
-'
+%22
 ,%0A%09%09
@@ -2135,15 +2135,15 @@
 me: 
-'
+%22
 merge
-'
+%22
 ,%0A%09%09
@@ -2269,14 +2269,14 @@
 me: 
-'
+%22
 exit
-'
+%22
 ,%0A%09%09
@@ -2567,17 +2567,17 @@
 === 
-'
+%22
 function
 ') %7B
@@ -2572,17 +2572,17 @@
 function
-'
+%22
 ) %7B%0A%09%09%09%09
@@ -2679,17 +2679,17 @@
 w Error(
-'
+%22
 Invalid 
@@ -2710,18 +2710,18 @@
 ned for 
-%22
 '
+%22
  + event

Fix minor code style inconsistencies