var ActionKit = new (function() {
	function TrivialDispatcher(f) {
		var self = this, event = f?f:null;
		this.register = function(f) {
			event = f;
		};
		this.invoke = function(e,data,scope) {
			return event.call(scope?scope:event,data,e);
		};
		this.delegate = function(e,data) {
			return new Action(self,e,data);
		};
		return true;
	}
	function UnicastDispatcher() {
		var self = this, event = {};
		this.unregister = function(e) {
			delete event[e];
		};
		this.register = function(e,f) {
			event[e] = f;
		};
		this.registerAll = function(o) {
			for(var k in o) self.register(k,o[k]);
		};
		this.invoke = function(e,data,scope) {
			return event[e].call(scope?scope:(event[e]),data,e);
		};
		this.delegate = function(e,data) {
			return new Action(self,e,data);
		};
		return true;
	}
	function MulticastDispatcher() {
		var self = this, event = {};
		this.unregister = function(e,f) {
			if(f == undefined) for(var f in event) arguments.callee(f,e);	// e,f swapped
			else if(e in event) for(var i in event[e]) if(event[e][i] == f) {
				event.splice(i,1);
				break;
			}
		};
		this.register = function(e,f) {
			self.unregister(e,f);
			if(e in event) event[e].push(f);
			else event[e] = [f];
		};
		this.registerAll = function(o) {
			for(var k in o) self.register(k,o[k]);
		};
		this.clear = function(e) {
			if(e == undefined) event = {};
			else if(e in event) delete event[e];
		};
		this.invoke = function(e,data,scope) {
			for(var f in event[e]) event[e][f].call(scope?scope:(event[e][f]),data,e);
		};
		this.delegate = function(e,data) {
			return new Action(self,e,data);
		};
		return true;
	}
	function Action(dispatcher,name,data) {	// name/data
		var self = this;
		var parsed = parse(name);
		this.dispatcher = dispatcher;
		this.name = parsed[0];
		this.data = data==undefined?parsed[1]:data;
		this.str = unparse(this.name,this.data);
		function parse(s) {
			var match = /^([^\/]+)(?:\/(.*))?$/i.exec(s);	// /^(\w+(?:\.\w+)*?)(?:\/(.*))?$/i.exec(s);
			if(!match) throw new SyntaxError();
			return [match[1],(match[2]==undefined)||(match[2]=='')?undefined:JSON.parse(match[2])];
		}
		function unparse(name,data) {
			return (data == undefined)?name:(name+'/'+JSON.stringify(data));
		}
		this.toString = function() {
			return self.str = unparse(self.name,self.data);
		};
		this.call = this.apply = function(scope) {
			return self.dispatcher.invoke(self.name,self.data,scope);
		};
		return true;
	}
	this.TrivialDispatcher = TrivialDispatcher;
	this.UnicastDispatcher = UnicastDispatcher;
	this.MulticastDispatcher = MulticastDispatcher;
	this.Action = Action;
	return true;
})();
