
(function($)
{

	// Proxy the original Backbone.View setElement method:
	// See: http://backbonejs.org/#View-setElement

	var backboneOriginalElementSet = Backbone.View.prototype.setElement;

	Backbone.View.prototype.setElement = function(element)
	{
		if (this.el != element)
		{
			$(this.el).backboneView('unlink');
		}

		$(element).backboneView(this);

		return backboneOriginalElementSet.apply(this, arguments);
	};

	// Create a custom selector to search for the presence of a 'backboneView' data entry:
	// This avoids a dependency on a data selector plugin...

	$.expr[':'].backboneView = function(element, intStackIndex, arrProperties, arrNodeStack)
	{
		return $(element).data('backboneView') !== undefined;
	};

	// Plugin internal functions:

	var attatchViewToElement = function($el, v)
	{
		$el.data('backboneView', v);
	};

	var getNearestViewFromElement = function($el, type)
	{
		var retVal = null;

		type = type || Backbone.View;

		while ($el.length)
		{
			$el = $el.closest(':backboneView');
			retVal = $el.length ? $el.data('backboneView') : null;

			if (retVal instanceof type)
			{
				break;
			}
			else
			{
				$el = $el.parent();
			}
		}

		return retVal;
	};

	// Extra methods:

	var methods = {

		unlink: function($el)
		{
			$el.removeData('backboneView');
		}

	};

	// Plugin:

	$.fn.backboneView = function()
	{
		var retVal = this;
		var args = Array.prototype.slice.call(arguments, 0);

		if ($.isFunction(methods[args[0]]))
		{
			methods[args[0]](this);
		}
		else if (args[0] && args[0] instanceof Backbone.View)
		{
			attatchViewToElement(this.first(), args[0]);
		}
		else
		{
			retVal = getNearestViewFromElement(this.first(), args[0]);
		}

		return retVal;
	}

})(jQuery);
