function Actionable()
{
	this.ajax = new QSPR.Ajax();
	this.ajax.owner = this;
	var self = this;
	
	$(document).ready
	(
		function()
		{
			self.scopeBlock = (self.scope) ? $(self.scope) : $(document);
			self.attachActions(self.scopeBlock);
		}
	);

	this.callAction = function(actionName, link, params)
	{
		var beforeMethod = eval('this.before'+actionName.ucFirst());  // "beforeDelete"
		var actualMethod = eval('this.do'+actionName.ucFirst());  // "doDelete"
		var onSuccess = eval('this.on'+actionName.ucFirst()+'Success'); // "onDeleteSucess"
		var onError = eval('this.on'+actionName.ucFirst()+'Error'); // "onDeleteError"
		
		if (params)
		{
			// Explicitly passed handlers have priority under object-global handlers
			if (params.success)
				onSuccess = params.success;
			if (params.error)
				onError = params.error;
		}
		
		if (beforeMethod)
			if (!beforeMethod.call(this, link))
				return false;
		
		if (actualMethod != undefined)
			actualMethod.call(this, link);
		else
			this.doAction(actionName, onSuccess, onError);
		return true;
	}

	this.attachActions = function(block)
	{
		block.find('[@action]').filter(":not(form)").each(function()
		{
			this.actionable = self;
			$(this).attr('href', 'javascript:void(0)');
			$(this).click(
				function()
				{
					this.actionable.setPosition(this);
					var action = $(this).attr('action');
					return this.actionable.callAction(action, this);
				});	
		});
	}
	
	this.doAction = function(action, onSuccess, onError)
	{
		var data = "action="+action+"&"+this.getActionQuery(action);
		data = data.replace(/&&/, "&");
		this.ajax.send
		(
			{ 
				url: this.actionURL, 
				data: data, 
				success: onSuccess,
				error: onError
			}
		);
	}
	
	this.getActionQuery = function(action)
	{
		// stub to override
		return "";
	}
	
	this.setPosition = function(element)
	{
		// stub to override
	}
}

// Event dispatcher
function EventDispatcher()
{
	var self = this;
	this.eventScope = $('<div id="'+Math.random()+'" class="invisible"/>');
	$(document).append(this.eventScope);

	this.listenEvent = function(event, funk)
	{
		$(this.eventScope).bind(event, null, funk);
	}
	
	this.fireEvent = function(event, data)
	{
		$(this.eventScope).trigger(event, data);
	}
}

String.prototype.ucFirst = function()
{
   // split string
   var firstChar = this.substring(0,1);
   var remainChar = this.substring(1);

   // convert case
   firstChar = firstChar.toUpperCase(); 
   remainChar = remainChar.toLowerCase();

   return firstChar + remainChar;
}
