/**
 * The core LBi module contains core features of the LBi Javascript Framework. Some native objects 
 * are extended, some new classes are introduced, but most importantly; the global LBi namespace is
 * created. 
 *
 * @module    lbi
 * @version   1.10.100216
 * @requires  jQuery
 * @author    LBi Lost Boys
 */
var LBi = (function($){

	var TYPE_FUNCTION = 'function';
	var TYPE_STRING = 'string';

	/**
	 * The global LBi namespace
	 *
	 * @class LBi
	 * @static
	 */
	var LBi = function(runnable){
		switch (typeof runnable){
			case TYPE_FUNCTION:
				return runnable.call(LBi, $);
			case TYPE_STRING:
				LBi.log(runnable);
			break;
		}
	};

	$.extend(LBi, {
		/**
		 * Shorthand for LBi.Dispatcher.subscribe
		 * @see {LBi.Dispatcher}
		 */
		subscribe: function(type, handler){
			LBi.Dispatcher.subscribe(type, handler);
		},

		/**
		 * Shorthand for LBi.Dispatcher.unsubscribe
		 * @see {LBi.Dispatcher}
		 */
		unsubscribe: function(type, handler){
			LBi.Dispatcher.unsubscribe(type, handler);
		},

		/**
		 * Sets a cookie using the given name, value and options.
		 * 
		 * @param {String} name
		 * @param {String} value
		 * @param {Object} options Cookie options; expires (in days), path, domain and secure (boolean).
		 */
		setCookie:function(name, value, options){
			var opt = options || {};
			var date = new Date();
			date.setTime(date.getTime() + ((opt.expires || 14) * 86400000));
			
			var expires = '; expires=' + date.toUTCString();
			var path = '; path=' + (opt.path || '/');
			var domain = opt.domain? ('; domain=' + opt.domain) : '';
			var secure = opt.secure? '; secure' : '';
			
			document.cookie = name + "=" + encodeURIComponent(value) + expires + path + domain + secure;
		},
		
		/**
		 * Gets the cookie with the given name
		 * 
		 * @param {String} name
		 * @return {String} Cookie value
		 */
		getCookie:function(name){
			var reg = new RegExp(name+'=([^;$]+)', 'i');
			var cookie = reg.exec(document.cookie);
			return (cookie && cookie[1])? decodeURIComponent(cookie[1]) : '';
		},

		/**
		 * Stores a reference to the given object or class under the given namespace on the global LBi object
		 * 
		 * @param {String} namespace
		 * @param {Object} object
		 */
		namespace:function(name, object) {
			var spaces = name.split('.');
			var l = spaces.length;
			var scope = this;

			for(var i=0; i<l; i++) {
				var space = spaces[i];
				var last = i === (l-1);

				if(last) {
					if(scope[space]) {
						throw Error('Namespace "' + space + '" is already defined');
					} else {
						scope[space] = object;
					}
				} else if(!scope[space]) {
					throw Error('Namespace "' + space + '" in "' + name + '" is not defined');	
				}			

				scope = scope[space];
			}
		},

		/**
		 * Abstract method
		 * @private
		 */
		AbstractMethod:function() {
			throw new Error('Method not implemented');
		},

		/**
		 * Logs any arguments to the console
		 * @param {Object} param Anything you want to log; messages, objects, ...
		 */
		log:function(){
			try {
				console.log.apply(console, arguments);
			} catch (fail){ }
		}
	});	

	/**
	 *	The Class object enables simplified Object Oriented functionality
	 *
	 * @static
	 * @class LBi.Class
	 */
	var Class = {
		/**
		 * Extends the Base class with the new constructor and prototype.
		 *
		 * @param {Function} Base The base class' constructor
		 * @param {Function} constructor The extended class' constructor. The super constructor is automatically called.
		 * @param {Object} prototype Additional prototype object literal.
		 * @return {Function} The extended class
		 */
		extend:function(Base, constructor, prototype){
			var Extended = function(){
				Base.apply(this, arguments);
				if(constructor){
					constructor.apply(this, arguments);
				}
			};

			this.implement(Extended, Base.prototype);
			if(prototype){
				this.implement(Extended, prototype);
			}
			Extended.prototype.constructor = Extended;
			return Extended;
		},

		/**
		 * Implements an interface on a given class.
		 *
		 * @param {Function} Class The class to implement the interface on.
		 * @param {Object} interface The interface, as object literal with named properties / functions.
		 */
		implement:function(Class, protoface){
			for(var i in protoface){
				if(typeof i === TYPE_STRING){
					Class.prototype[i] = protoface[i];
				}
			}
		}
	};

	LBi.namespace('Class', Class);
		
	/**
	 * Function extentions
	 * @class Function
	 */

	/**
	 * Bind returns a function pointer, scoped to the given scope. 
	 *
	 * @param {Object} scope The scope for the function
	 * @return {Function} The scoped method
	 */
	Function.prototype.bind = function(scope){
		var method = this;
		return function(){
			return method.apply(scope, arguments);
		};
	};

	/**
	 * Native JS 1.6 Array functions patch for IE 6 to 8
	 * See https://developer.mozilla.org/en/New_in_JavaScript_1.6#Array_extras for details.
	 *
	 * @class Array
	 */
	var prototype = Array.prototype;

	if(!prototype.map){
		prototype.map = function(handler, scope){
			var result = [];
			var l = this.length;
			for(var i=0; i<l; i++){
				if(i in this){
					result[i] = handler.call(scope, this[i], i, this);
				}
			}	
			return result;
		};
	}

	if(!prototype.forEach){
		prototype.forEach = function(handler, scope){
			var l = this.length;
			for(var i=0; i<l; i++){
				if(i in this){
					handler.call(scope, this[i], i, this);
				}
			}
		};
	}

	if(!prototype.filter){
		prototype.filter = function(handler, scope){
			var result = [];
			var l = this.length;
			for(var i=0; i<l; i++){
				if(i in this){
					var value = this[i];
					if(handler.call(scope, value, i, this)){
						result.push(value);
					}
				}
			}
			return result;
		};
	}

	if(!prototype.some){
		prototype.some = function(handler, scope){
			var l = this.length;
			for(var i=0; i<l; i++){
				if(i in this && handler.call(scope, this[i], i, this)){
					return true;
				}
			}
			return false;
		};
	}

	if(!prototype.every){
		prototype.every = function(handler, scope){
			var l = this.length;
			for(var i=0; i<l; i++){
				if(i in this && !handler.call(scope, this[i], i, this)){
					return false;
				}
			}
			return true;
		};
	}

	if(!prototype.indexOf){
		prototype.indexOf = function(item, from){
			var l = this.length;
			var start = from || 0;
			if(from < 0){ start = l - from; }
			for(var i=start; i<l; i++){
				if(i in this && this[i] === item){
					return i;
				}
			}
			return -1;
		};
	}

	if(!prototype.lastIndexOf){
		prototype.lastIndexOf = function(item, from){
			var l = this.length -1;
			var start = from || l;
			if(from < 0){ start = l - from; }
			for(var i=start; i>=0; i--){
				if(i in this && this[i] === item){
					return i;
				}
			}
			return -1;
		};
	}

	/**
	 * A HashMap class, inspired by the Java 1.6 collections framework.
	 * The HashMap is used to store, retrieve and manage key/value pairs.
	 *
	 * @class LBi.HashMap
	 * @constructor
	 */
	var HashMap = function() {
		this.stack = {};
	};
	
	HashMap.prototype = {
		constructor: HashMap,
		
		/**
		 * Adds a key/value pair to the HashMap.
		 * If a key already exists in the HashMap it will do nothing.
		 *
		 * @param {String} key The key identifier in the HashMap
		 * @param {Object} value The value stored in the HashMap
		 */
		put: function(key, value) {
			this.stack[key] = value;
		},
		
		/**
		 * Get a value by the key with which it is stored.
		 *
		 * @param {String} key  The key of the value to retrieve from the HashMap
		 * @return {Object} The value
		 */
		get: function(key) {
			return this.stack[key];
		},

		/**
		 * Returns an Array of the keys on the HashMap
		 *
		 * @return {Array} Array of keys
		 */
		getKeys:function() {
			var keys = [];
			for(var i in this.stack) {
				if(typeof i === TYPE_STRING) {
					keys.push(i);
				}
			}
			return keys;
		},
		
		/**
		 * Remove a key/value pair from the HashMap
		 *
		 * @param {String} key The key of the key/value pair to remove from the HashMap
		 */
		remove: function(key) {
			delete this.stack[key];
		},

		/**
		 * Returns a clone of the HashMap. The clone is shallow; its keys are cloned, but the 
		 * clone may reference the same object values as the original HashMap. 
		 * 
		 * @return {HashMap} Clone of the HashMap
		 */
		clone:function() {
			var clone = new HashMap();
			for(var i in this.stack) {
				if(typeof i === TYPE_STRING) {
					clone.add(i, this.get(i));
				}
			}

			return clone;
		},
		
		/**
		 * Remove all key/value pairs from the HashMap.
		 */
		clear: function() {
			this.stack = {};
		},
		
		/**
		 * Returns the size of the HashMap. (i.e.) the amount of key/value pairs stored in the HashMap.
		 *
		 * @return {number} The size of the hashmap.
		 */
		size: function() {
			var size = 0;
			for(var i in this.stack) {
				if(typeof i === TYPE_STRING) {
					size ++;
				}
			}

			return size;
		},
		
		/**
		 * Returns true if the HashMap contains a value for the given key, or false if it doesn't.
		 *
		 * @param {String} key The key to test.
		 * @return {boolean} containsKey
		 */
		containsKey: function(key) {
			return this.stack[key]? true : false;
		},
		
		/**
		 * Returns true if the HashMap contains the given value, or false if it doesn't.
		 *
		 * @param {Object} value The value to test.
		 * @return {boolean} containsValue
		 */
		containsValue: function(value) {
			for(var i in this.stack) {
				if(typeof i === TYPE_STRING && this.stack[i] === value) {
					return true;
				}
			}
			return false;
		}
	};

	LBi.namespace('HashMap', HashMap);
	

	/**
	 * Lightweight templating engine that applies an html template to 1 or more object literals.
	 * This class may be used (for instance) to transform ajax json responses to html fragments.
	 * Named template properties marked with a "$" are replaced by the value of the corresponding
	 * object literal's (or json's) property.
	 * 
	 * @class LBi.Template
	 * @constructor
	 * @param {String} template The html template
	 */
	var Template = function(template) {
		this.set(template);
	};
	
	Template.prototype = {
		constructor: Template,

		/**
		 * Applies the template to the given data, and returns the resulting html as a string.
		 *
		 * @param {Object} data Object literal with named properties
		 * @return {String} The transformed template
		 */
		parse:function(data) {
			if(!data) {
				throw Error('No data provided');
			}

			var html = this.template;
			if(!html) {
				throw Error('The template is not specified');
			}

			for(var name in data) {
				if(typeof name === TYPE_STRING) {
					var property = new RegExp('\\$' + name, 'mg');
					html = html.replace(property, data[name]);
				}
			}
			
			return html;
		},

		/**
		 * Sets the current template
		 *
		 * @param {String} template 
		 */
		set: function(template) {
			this.template = template;
		},

		/**
		 * Gets the current template
		 * 
		 * @return {String} The current template
		 */
		get: function() {
			return this.template;
		}
	};

	LBi.namespace('Template', Template);


	/**
	 * The Animation class is a utility class for components that want to modify css properties of 
	 * nodes with an animation. An Animation instance may define several states, each of which 
	 * resulting in a different animation. A few animations are predefined, like FADE and SLIDE, 
	 * which respectively perform a fadein/out, and slidedown/up, depending on the given state.
	 *
	 * @constructor
	 * @class LBi.Animation
	 * @param {Function} animation The actual animation function. Should define a node, state and options parameter. May return a value.
	 */
	var Animation = function(animation) {
		this.animation = animation;
	};

	Animation.prototype = {
		constructor: Animation,
		
		/**
		 * Must be called by components utilizing this class to perform the animation.
		 * 
		 * @param {Node} node The animated node
		 * @param {Object} state State object, may be anything from a boolean to a complex object.
		 * @param {Object} options Animation options object, see Animation.Defaults. Specified values overrule the defaults.
		 * @return {Object} value as returned by the animation function.
		 */
		run:function(node, state, options) {
			var settings = $.extend({}, Animation.Defaults, options);
			return this.animation(node, state, settings);
		}
	};
	
	/**
	 * No animation. Evaluates to null.
	 * @property NONE
	 * @type Object
	 */
	Animation.NONE = null;

	/**
	 * Uses a show/hide to toggle a node's visibility
	 * @property TOGGLE
	 * @type LBi.Animation
	 */
	Animation.TOGGLE = new Animation(function(node, state, options) {
		$(node)[state? 'show' : 'hide'](
			options.duration, 
			options.complete
		);
	});
	
	/**
	 * Uses a fadein/out to toggle a node's visibility
	 * @property FADE
	 * @type LBi.Animation
	 */
	Animation.FADE = new Animation(function(node, state, options) {
		$(node)[state? 'fadeIn' : 'fadeOut'](
			options.duration, 
			options.complete
		);
	});
	
	/**
	 * Uses a slidedown/up to toggle a node's visibility
	 * @property SLIDE
	 * @type LBi.Animation
	 */
	Animation.SLIDE = new Animation(function(node, state, options) {
		$(node)[state? 'slideDown' : 'slideUp'](
			options.duration, 
			options.complete
		);
	});
	
	/**
	 * Combines a fade with a subtle padding-top animation of 10px to toggle a node's visibility.
	 * Use the options' "property" and "strength" settings to overrule these values.
	 * @property SMOOTH
	 * @type LBi.Animation
	 */
	Animation.SMOOTH = new Animation(function(node, state, options) {
		var time = options.duration;
		var name = options.property || 'padding-top';
		var strength = (options.strength || 10) * (state? 1 : -1);
		var property = {};
		property[name] = strength;
		var $node = $(node);
		if(state) { 
			$node.fadeIn(time).animate(property, options);
		} else {
			$node.fadeOut(time).animate(property, options);
		}
	});

	/**
	 * The Animation Defaults define a selection of common animation properties, like duration and 
	 * easing. Animation functions should manually query and apply these values, since the actual
	 * implementation of the animation is left to each Animation instance individually.
	 * 
	 * @static
	 * @class LBi.Animation.Defaults
	 */
	Animation.Defaults = {
		/**
		 * The animation's desired duration.
		 * @property duration
		 * @type String
		 * @default 1000
		 */
		duration: 1000,
		
		/**
		 * Desired callback to run after an animation has completed.
		 * @property complete
		 * @type Function
		 * @default null
		 */
		complete: null,
		
		/**
		 * Desired easing for the animation.
		 * @property easing
		 * @type String
		 * @default "linear"
		 */
		easing: "linear",

		/**
		 * Desired queueing for chained jQuery animations.
		 * @property queue
		 * @type boolean
		 * @default false
		 */
		queue: false
	};

	LBi.namespace('Animation', Animation);


	/**
	 * jQuery extensions on the global jQuery object
	 * 
	 * @module jquery
	 * @class jQuery
	 */
	$.extend($, {
		/**
		 * RegisterPlugin binds a Class to the jQuery functions under the given method name. When 
		 * calling the created jQuery method on a jQuery result, plugin instances will be created 
		 * for each individual node in it. A settings object may be sent along with the jQuery call.
		 * The Class constructor must define 2 paramters: the node, and the settings object.
		 *
		 * @param {String} name The method name that will be added to the jQuery functions.
		 * @param {Function} plugin The related plugin.
		 * @return {Array} An array to which instances will be added, since the plugin call must maintain the jQuery chain.
		 */
		registerPlugin: function(name, Constructor){
			var plugin = {};
			var instances = [];
			plugin[name] = function(settings){
				for(var i=0; i<this.length; i++){
					instances.push(new Constructor(this[i], settings));
				}
				return this;
			};
			$.extend($.fn, plugin);
			return instances;
		}
	});

	/**
	 * jQuery functions that may be called on jQuery results
	 * @class jQuery.functions
	 */

	/**
	 * The Event module introduces a global Dispatcher that serves as the central hub for all events, 
	 * both native as well as custom ones. By using LBi classes like DOM, Forms or LinkRelations, 
	 * it is possible to subscribe to events on a global level, and poll the Event parameter for the
	 * node (if any) that triggered the event. This way, events that natively don't bubble - like 
	 * "submit" - can be handled as if they did.
	 *
	 * @module event
	 */

	/**
	 * Fireable event, used by the Dispatcher as an argument to individual handlers. 
	 *
	 * @class LBi.Event
	 * @constructor
	 * @param {String} type The event's type
	 * @param {Node} target The event's target node
	 * @param {Object} data Optional data object
	 * @param {Event} nativeEvent The native event, if applicable.
	 */
	var Event = function(type, target, data, e){
		this.type = type;
		this.target = target;
		this.data = data || {};
		this.event = e;
		this.returnValue = true;
		
		if(e){
			var list = Event.Whitelist;
			for(var prop in list){
				if(list[prop]){
					(this[prop] = e[prop]);
				}
			}
		}
	};

	Event.prototype = {
		constructor: Event,

		/**
		 * Prevents the default action of the event; for native events this might be
		 * a link from being followed or a form from being submitted, for custom events
		 * it might be anything.
		 *
		 * @method preventDefault
		 */
		preventDefault:function(){
			this.returnValue = false;
			if(this.event){
				this.event.preventDefault();
			}
		},

		/**
		 * Returns a boolean whether the event's default action has been prevented.
		 * 
		 * @method isDefaultPrevented
		 * @return {boolean} prevented
		 */
		isDefaultPrevented:function() {
			return this.event? this.event.isDefaultPrevented() : !this.returnValue;
		},
		
		/**
		 * Stops the event propagation of a wrapped native event.
		 * 
		 * @method stopPropagation
		 */
		stopPropagation:function(){
			if(this.event){
				this.event.stopPropagation();
			}
		}
	};

	/**
	 * @property CLICK
	 * @type String
	 */
	Event.CLICK = 'click';
	/**
	 * @property MOUSEOVER
	 * @type String
	 */
	Event.MOUSEOVER = 'mouseover';
	/**
	 * @property MOUSEOUT
	 * @type String
	 */
	Event.MOUSEOUT = 'mouseout';
	/**
	 * @property MOUSEUP
	 * @type String
	 */
	Event.MOUSEUP = 'mouseup';
	/**
	 * @property MOUSEDOWN
	 * @type String
	 */
	Event.MOUSEDOWN = 'mousedown';
	/**
	 * @property MOUSEMOVE
	 * @type String
	 */
	Event.MOUSEMOVE = 'mousemove';
	/**
	 * @property FOCUS
	 * @type String
	 */
	Event.FOCUS = 'focus';
	/**
	 * @property BLUR
	 * @type String
	 */
	Event.BLUR = 'blur';
	/**
	 * @property CHANGE
	 * @type String
	 */
	Event.CHANGE = 'change';
	/**
	 * Fired by LBi.Forms instances, before a form is posted normally. May be prevented.
	 * @property SUBMIT
	 * @type String
	 */
	Event.SUBMIT = 'submit';
	/**
	 * @property RESET
	 * @type String
	 */
	Event.RESET = 'reset';
	/**
	 * Fired by LBi.Forms instances, before a form is posted via ajax. May be prevented.
	 * @property AJAX_SUBMIT
	 * @type String
	 */
	Event.AJAX_SUBMIT = 'ajaxsubmit';
	/**
	 * Fired by the LBi.DOM mediator when html is inserted into the current document.
	 * @property NODE_INSERTED
	 * @type String
	 */
	Event.NODE_INSERTED = 'DOMNodeInserted';
	/**
	 * Fired by the LBi.DOM mediator when html is removed from the current document.
	 * @property NODE_REMOVED
	 * @type String
	 */
	Event.NODE_REMOVED = 'DOMNodeRemoved';
	/**
	 * May be fired by objects that change the layout of a page by (for instance) changing classnames. 
	 * @property LAYOUT_CHANGED
	 * @type String
	 */
	Event.LAYOUT_CHANGED = 'layoutchanged';

	/**
	 * Whitelist of common properties on the native event that may be copied to the wrapper. 
	 * Includes relatedTarget, button, keycode, modifiers (ctrl, shift, alt), and page- 
	 * client- and screen coordinates.
	 *
	 * @private
	 * @static
	 * @class LBi.Event.Whitelist
	 */
	Event.Whitelist = {
		relatedTarget: 1, 
		button: 1, 
		keyCode: 1,
		ctrlKey: 1, 
		shiftKey: 1, 
		altKey: 1,
		pageX: 1, 
		pageY: 1, 
		clientX: 1, 
		clientY: 1, 
		screenX: 1, 
		screenY: 1
	};

	LBi.namespace('Event', Event);

	/**
	 * The Observable class enables observers to subscribe themselves to events that may be fired
	 * by Observable instances. This class may either be inherited from, or may be used by creating 
	 * instances on objects that require this functionality.
	 * 
	 * @class LBi.Observable
	 * @return Observable instance
	 */
	var Observable = function(){
		this.observers = {};
	};

	Observable.prototype = {
		constructor: Observable,

		/**
		 * Subscribes a handler to an event
		 * 
		 * @param {String} type The event's type
		 * @param {Function} handler The event handler
		 */
		subscribe: function(event, handler) {
			var observers = this.observers;
			if(!observers[event]) {
				observers[event] = [];
			}
			observers[event].push(handler);
		},
		
		/**
		 * Unsubscribes a handler of a given type
		 * 
		 * @param {String} type The event's type
		 * @param {Function} handler The event handler
		 */
		unsubscribe: function(event, handler) {
			var handlers = this.observers[event];
			var l = handlers.length;
			for(var i=0; i<l; i++) {
				if(handlers[i] === handler) {
					handlers.splice(i, 1);
					break;
				}
			}
		},

		/**
		 * Notifies all observers that subscribed to the event of the new state. Subclasses or 
		 * instances that use Observable instances must implement their own calls to this method, 
		 * as well as giving the state parameter an actual meaning or purpose.
		 * 
		 * @param {String} type The event's type
		 * @param {Object} state A state object that is passed to all observers
		 */
		notify: function(event, state) {
			var handlers = this.observers[event];
			if(handlers) {
				var l = handlers.length;
				for(var i=0; i<l; i++) {
					handlers[i](state);
				}
			}
		}
	};

	LBi.namespace('Observable', Observable);

	/**
	 * The Dispatcher is a static class or singleton that is used by various LBi classes. It allows 
	 * objects to observe and fire native and custom events on document level. Typically multiple 
	 * objects observe an event, which is fired by only a few (or one) objects. 
	 *
	 * @static
	 * @extends LBi.Observable
	 * @class LBi.Dispatcher
	 */
	var Dispatcher = new (Class.extend(
		Observable,	null, {

		/**
		 * Captures native events on nodes or document, and routes them to the dispatcher. In effect, 
		 * this transforms local events to global ones.
		 * 
		 * @param {String} event The type of event to capture
		 * @param {Object} nodes The node, nodelist or jquery result to capture the event on.
		 */
		capture:function(event, nodes){
			var self = this;
			$(nodes || document).bind(event, function(e, data){
				return self.fire(event, e.target, data, e);
			});
		},

		/**
		 * Creates a new event for objects to subscribe to, for instance "click:link" when 
		 * a click event specifically targeted a link. CreateEvent automatically subscribes
		 * a listener to the base event (eg "click"), and will fire the new event only if the 
		 * filter method returns a node. The node will be the event's target.
		 *
		 * @param {String} type The custom event type, eg "click:link"
		 * @param {Function} filter The filter function which will set the event's target.
		 */
		createEvent:function(type, filter){
			var self = this;
			var base = /(.*):[a-z0-9]+$/i.exec(type)[1];
			this.subscribe(base, function(e){
				var target = filter(e);
				if(target && target.nodeType){
					return self.fire(type, target, e.data, e);
				}
			});
		},

		/**
		 * Fires an event. Automatically called for events that are routed via the capture 
		 * and createEvent methods. Otherwise, must be called manually. The fired event is
		 * an LBi.Event instance that mimics native event functionality. 
		 * 
		 * @param {String} type The event's type
		 * @param {Node} target The event's target node
		 * @param {Object} data Optional data object
		 * @param {Event} nativeEvent The native event, if applicable.
		 */
		fire:function(type, target, data, nativeEvent) {
			var e = new Event(type, target, data, nativeEvent);
			this.notify(type, e);
			return e.returnValue;
		}
	}))();
	
	LBi.namespace('Dispatcher', Dispatcher);

	/**
	 * LinkRelations instances monitor page clicks, and allows handlers to be defined when a link
	 * is clicked that has a specific rel attribute value. 
	 * 
	 * @class LBi.LinkRelations
	 * @constructor
	 */
	var LinkRelations = function(){
		this.relations = [];
		Dispatcher.subscribe('click:link', this.handleClick.bind(this)); 
	};

	LinkRelations.prototype = {
		constructor: LinkRelations,

		/**
		 * Subscribe defines a handler for clicks on links of which the rel attribute 
		 * value matches the given expression. The handler receives the click event as its only
		 * parameter, which (as a rule) contains the target link with matching rel.
		 *
		 * @param {RegExp} expression The regular expression for the rel attribute value test.
		 * @param {Function} handler The handler that is called when a matching link is clicked.
		 * @return {Object} The relation object. Can be passed to unsubscribe to remove it. 
		 */
		subscribe:function(expression, handler){
			var relation = {
				expression: expression,
				handler: handler
			};

			this.relations.push(relation);
			return relation;
		},

		/**
		 * Removes a handler from the list.
		 *
		 * @param {Object} relation The relation object as returned by subscribe.
		 */
		unsubscribe:function(relation){
			for(var i=0; i<this.relations.length; i++){
				if(this.relations[i] === relation){
					this.relations.splice(i, 1);
					break;
				}
			}
		},
		
		/**
		 * Checks the current relations for a match when a click in the page occurs. This 
		 * method is called automatically.
		 * 
		 * @param {Event} event The event object
		 */
		handleClick:function(e){
			var link = e.target;
			var rel = link.rel;
			if(rel){ 
				for(var i=0; i<this.relations.length; i++){
					var relation = this.relations[i];
					if(relation.expression.test(rel)){
						relation.handler(e);
					}
				}
			}
		}
	};

	LBi.namespace('LinkRelations', LinkRelations);

	/**
	 * DOM Module
	 * @module dom 
	 */

	/**
	 * The DOM mediator can be used to modify the page's DOM (ajax or otherwise), and will
	 * fire global DOMNodeInserted and DOMNodeRemoved events for other objects to react to.
	 * The fired event's target property refers to added or removed nodes respectively.
	 * 
	 * @static
	 * @class LBi.DOM
	 */
	var DOM = {
		/**
		 * Writes the html to the given node and fires a DOMNodeInserted event on that node.
		 * 
		 * @param {Node} node The node to write the html to.
		 * @param {String} html The html to write.
		 */
		write:function(node, html){
			$(node).html(html);
			Dispatcher.fire(Event.NODE_INSERTED, node);
		},
		
		/**
		 * Appends the parent with the given node and fires a DOMNodeInserted event for the appended node.
		 *
		 * @param {Node} parent The parent to append the node to.
		 * @param {Node} node The appended node.
		 */
		append:function(parent, node){
			$(parent).append(node);
			Dispatcher.fire(Event.NODE_INSERTED, node);
		},

		/**
		 * Inserts the node before the insert point, and fires a DOMNodeInserted event for the inserted node.
		 * 
		 * @param {Node} node The node to insert.
		 * @param {Node} insert The insert point before which the node is inserted.
		 */
		insertBefore:function(node, insert){
			$(insert).before(node);
			Dispatcher.fire(Event.NODE_INSERTED, node);
		},

		/**
		 * Inserts the node after the insert point, and fires a DOMNodeInserted event for the inserted node.
		 * 
		 * @param {Node} node The node to insert.
		 * @param {Node} insert The insert point after which the node is inserted.
		 */
		insertAfter:function(node, insert){
			$(insert).after(node);
			Dispatcher.fire(Event.NODE_INSERTED, node);
		},

		/**
		 * Replaces one node for another. Fires a DOMNodeRemoved on the old node before it is removed, and
		 * a DOMNodeInserted on the new node after it is inserted.
		 * 
		 * @param {Node} node The node to insert.
		 * @param {Node} old The old node that is going to be replaced.
		 */
		replace:function(node, old){
			Dispatcher.fire(Event.NODE_REMOVED, old);
			$(old).replaceWith(node);
			Dispatcher.fire(Event.NODE_INSERTED, node);
		},

		/**
		 * Removes the given node. Fires a DOMNodeInserted event before it is removed.
		 * 
		 * @param {Node} node The node to remove.
		 */
		remove:function(node){
			Dispatcher.fire(Event.NODE_REMOVED, node);
			$(node).remove();
		}
	};

	LBi.namespace('DOM', DOM);

	/**
	 * The DOMListener class subscribes to the DOMNodeInserted and -removed events and
	 * may be used as a base class for components that rely on these events.
	 *
	 * @constructor
	 * @class LBi.DOMListener
	 */
	var DOMListener = function(){
		Dispatcher.subscribe(Event.NODE_INSERTED, this.nodeInserted.bind(this));
		Dispatcher.subscribe(Event.NODE_REMOVED, this.nodeRemoved.bind(this));
	};

	DOMListener.prototype = {
		constructor: DOMListener,

		/**
		 * Automaticallly called when a DOMNodeInserted event is fired. Subclasses must
		 * implement this method, or an error will be thrown.
		 * 
		 * @method nodeInserted
		 * @param {Event} e The event object. The target property contains the inserted node.
		 */
		nodeInserted: LBi.AbstractMethod,
		
		/**
		 * Automaticallly called when a DOMNodeRemoved event is fired. Subclasses must
		 * implement this method, or an error will be thrown.
		 * 
		 * @method nodeRemoved
		 * @param {Event} e The event object. The target property contains the removed node.
		 */
		nodeRemoved: LBi.AbstractMethod
	};

	LBi.namespace('DOMListener', DOMListener);


	/**
	 * route global clicks through the dispatcher
	 */ 
	Dispatcher.capture(Event.CLICK);
	
	/**
	 * create custom "click:link" event
	 */
	Dispatcher.createEvent('click:link', function(e){
		var link = $(e.target).closest('a');
		return link[0] || null;
	});

	/**
	 * expose the LBi object
	 */
	return LBi;

})(jQuery);
