/**
 *	LB Utils for jQuery
 *	---------------------------------------------
 *	(c) 2008 Lost Boys - http://www.lostboys.nl
 */

var 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;
	},

	implement:function(Class, protoface) {
		for(var i in protoface) {
			Class.prototype[i] = protoface[i];
		}
	}
}


/**
 *	LinkRelations for jQuery
 *	--------------------------
 */

function LinkListener(node) {
	this.relations = [];
	this.regLink = /^a$/i;
	var self = this;
	$(node).click(function(e) {
		self.handleClick(e);
	})
}

LinkListener.prototype = {
	register:function(expression, handler, scope) {
		this.relations.push({
			expression:expression, handler:handler, scope:(scope || window)
		});
	},

	getHandler:function(attribute) {
		for (var rel,i=0; i<this.relations.length; i++) {
			rel = this.relations[i];
			if(rel.expression.test(attribute)) { return rel; }
		}
	},

	handleClick:function(e) {
		var target = e.target;
		while(target && !this.regLink.test(target.nodeName)) {
			target = target.parentNode;
		}
		var attribute = target? target.getAttribute('rel') : null;
		var relation = target? this.getHandler(attribute) : null;
		if(relation && relation.handler.apply(relation.scope, [target, attribute])) {
			e.preventDefault();
		}
	}
}

/**
 *	Extend jQuery
 *	--------------------------
 */

jQuery.extend(jQuery, {
	registerPlugin:function(name, Constructor) {
		var plugin = {};
		plugin[name] = function(settings) {
			for(var i=0; i<this.length; i++) {
				new Constructor(this[i], settings);
			}
			return this;
		}
		
		jQuery.extend(jQuery.fn, plugin);
	}
});

/**
 *	Extend jQuery functions
 *	--------------------------
 */

jQuery.extend(jQuery.fn, {
	// links LinkListener to jQuery
	addRelation:function(expression, handler, scope) {
		this.each(function(){
			if(!this.$linkListener) {
				this.$linkListener = new LinkListener(this);
			}	this.$linkListener.register(expression, handler, scope);
		});
		return this;
	},

	// bubbles from given element to the one of given type (including self)
	bubbleTo:function(type) {
		var node = this[0], regType = new RegExp('^'+type+'$', 'i');
		while(node) {
			if(regType.test(node.nodeName)) return $(node);
			node = node.parentNode;
		}	return null;
	},

	// jQuery already has add, remove and toggle
	replaceClass:function(oldClass, newClass) {
		if(this.hasClass(oldClass)) {
			this.removeClass(oldClass);
			this.addClass(newClass);
		}	return this;
	},

	// jQuery doesn't do offset from a specified parent
	calculateLeft:function(parent) {
		var left = 0, node = this[0];
		while(node && (!parent || node != parent)) {
			left += node.offsetLeft;
			node = node.offsetParent;
		}	return left;
	},

	// jQuery doesn't do offset from a specified parent
	calculateTop:function(parent) {
		var top = 0, node = this[0];
		while(node && (!parent || node != parent)) {
			top += node.offsetTop;
			node = node.offsetParent;
		}	return top;
	},

	// allows a scoped handler to be bound to an event
	bindScoped:function(type, handler, scope) {
		var scoped = function (e) { handler.call(scope || this, e); };
		this.bind(type, scoped);
		handler.guid = scoped.guid;
		return this;
	}
});


