if (typeof TinyMighty == 'undefined') {
  var TinyMighty = {}
}
/* Commented object names are for navigation in Espresso, do not remove! */

//TinyMighty.Util
TinyMighty.Util  = {
	//Copies over prototype methods & properties
	/* Thanks to http://chamnapchhorn.blogspot.com/2009/05/javascript-mixins.html */
	mixin: function mixin(recipient) {
		//console.group('Mixing in...');
		for(var i=1, len = arguments.length; i < len; i++){
			if(typeof arguments[i] != 'function' && typeof arguments[i] != 'object')
				throw 'InvalidArgument';
			var prototype = (typeof arguments[i] == 'object') ? false : true;
			var recurse = (prototype) ? arguments[i].prototype : arguments[i];
			var target = (recipient.prototype == undefined) ? recipient : recipient.prototype;
			for(methodName in recurse){

				if(!target[methodName] && methodName!='onMixin'){
					//console.log(methodName);
					target[methodName] = recurse[methodName];
				}
			}
			if(recurse.onMixin && typeof recurse.onMixin == 'function')
				recurse.onMixin.call(target);
			
		}
		//console.log('to...');
		//console.log(recipient);
		//console.groupEnd();
	},

	extend: function extend(recipient,newmethods){
		for(var i=1, len = arguments.length; i < len; i++){
			if(typeof arguments[i] != 'object')
				throw 'InvalidArgument';

			for(methodName in arguments[i]){
				recipient.prototype[methodName] = arguments[i][methodName];
			}
			
		}
	},
	
	object: function object(obj){
		if(typeof obj == 'function'){
			tmp = function(){}
			tmp.prototype = obj.prototype;
			return new tmp;
		}
		return obj;
	},
	
	bindEventScope: function bindEventScope(func,scope,args){
		//console.log(arguments);
		args = args || [];
		return function eventScopeClosure(event){
			args.unshift(event);
			return func.apply(scope,args);
		}
	},
	
	bindScope: function bindScope(func,scope,args){
		args = args || [];
		return function scopeClosure(){
			return func.apply(scope,args);
		}
	},
	
	/*
	thanks to http://my.opera.com/GreyWyvern/blog/show.dml/1725165 for the clone function
	this will iteratively copy all properties from one object to another.
	Note that while literals will be copied, functions will still be references to the original function, not copies.
	This *could* be added with  eval('['+functionprop.toString()+']')[0]; if necessary, but until it's needed I prefer to avoid eval...
	
	Also, beware of cyclical references!
	*/
	clone: function clone(object) {
	  var newObj = (this instanceof Array) ? [] : {};
	  for (i in object) {
	    //if (i == 'clone') continue;
	    if (object[i] && typeof this[i] == "object") {
	      newObj[i] = object[i].clone();
	    } else newObj[i] = object[i]
	  } return newObj;
	},
	
	merge: function(left, right){
		var newleft = this.clone(left);
		for(prop in right){
			if(newleft[prop] && typeof right[prop] == 'object'){
				newleft[prop] = this.merge(newleft[prop],right[prop]);
			}else{
				newleft[prop] = right[prop];
			}
		}
		return newleft;
	}
}


//ObjectRegistry
TinyMighty.ObjectRegistry = {
	registry:{},
	add: function add(type,id,obj){
		console.group('Adding object to registry: '+type+', '+id);
		console.log(obj);
		console.groupEnd();
		if(!this.registry[type])
			this.registry[type] = {}
		this.registry[type][id] = obj;
	},
	find: function find(type,id){
		//console.log('Attempting to fetch object from registry: '+type+', '+id);
		if(this.registry[type] && this.registry[type][id])
			return this.registry[type][id];
		return false;
	}
}

TinyMighty.Event = function event(name,data,info){
	this.name = name;
	this.data = data ? data : {}
	this.info = info ? info : {}
	this.stopped = false;
	this.propagationStopped = false;
	this.defaultPrevented = false;
}
TinyMighty.Event.prototype = {
	//stops the event propogating to the next bound callbacks
	stop: function stop(){
		this.stopped = true;
		return this;
	},
	isStopped: function isStopped(){
		return this.stopped;
	},
	stopPropagation: function stopPropagation(){
		this.propagationStopped = true;
	},
	isPropagationStopped: function isPropagationStopped(){
		return this.propagationStopped;
	},
	//prevents the default event action
	preventDefault: function preventDefault(){
		this.defaultPrevented = true;
		return this;
	},
	isDefaultPrevented: function isDefaultPrevented(){
		return this.defaultPrevented;
	},
	getName: function getName(){
		return this.name;
	},
	getInfo: function getInfo(){
		return this.info;
	},
	getData: function getData(){
		return this.data;
	}
}

/* Mixin Modules */
if(!TinyMighty.Mixin)
	TinyMighty.Mixin = {}
	
	
//Events Mixin
/*
This is a chained events mixin.  Events will bubble up through the chain unless stop() or stopPropogation() is called on an event object.
The chain is set through the object options - options.events.chain - and must be another object with this mixin.
*/
TinyMighty.Mixin.Events = {
	onMixin: function mixin(){
		if(this.options.events == undefined){
			this.options.events = {} //prevent access to this.options.events[...] from erroring if options are not passed...
		}
		this.events = {'handlers':{}};
	},
	sendEvent: function sendEvent(event,data,info){
		//if we're passing a string as the event, instantiate a new event (for convenience)
		if(typeof event == 'string'){
			if(!info)
				info = {}
			if(!info.target)
				info.target = this;
			event = new TinyMighty.Event(event,data,info);
		}
		//iterate through all event handlers set for this event, and pass it the event
		if(this.events.handlers && this.events.handlers[event.name]){
			//send event
			for(var i=0; i<this.events.handlers[event.name].length; i++){
				var handler = this.events.handlers[event.name][i];
				handler.call(handler,event);
				if(event.isStopped()==true)
					break;
			}
		}		
		//try and pass the event up the propogation chain
		if(this.options.events.chain && event.isPropagationStopped()==false && event.isStopped()==false ){
			this.options.events.chain.sendEvent(this,event);
		}
		return event;
	},
	
	addEvent: function addEvent(eventName,handler){
		//if no handlers have previously set this event, we need to add it
		if(!this.events.handlers[eventName])
			this.events.handlers[eventName]=[];
		//add the handler to the event stack
		this.events.handlers[eventName].push(handler);

		//if(this.options.events.chain)
			//this.options.events.chain.bind(eventName,handler);
	}
}


//Options Mixin
TinyMighty.Mixin.Options = {
	onMixin: function mixin(){
		if(!this.settings)
			this.settings = {}
		if(!this.options)
			this.options = {}
	},
	setOptions: function setOptions(options){
		this.options = TinyMighty.Util.merge(TinyMighty.Util.merge(this.settings,this.options),options);
	}
}

//GetSet Mixin
TinyMighty.Mixin.GetSet = {
	onMixin: function mixin(){
		//console.log('mixing in GetSet');
		//mixin.requires(TinyMighty.Giving.Options);
		//console.log(this);
		if(!this.setOptions)
			throw new TinyMighty.Error('GetSet requires options');
		if(!this.settings.getset)
			this.settings.getset = {}
	},
	set: function set(key, value){
		if(this.options && this.options.getset && this.options.getset.dataStorePropertyName && this[this.options.getset.dataStorePropertyName]){
			var currentValue = this[this.options.getset.dataStorePropertyName];
		}else if(this[key]){
			var currentValue = this[key];
		}else{
			var currentValue = null;
		}
		if(this._beforeSet && typeof this._afterSet == 'function')
			value = this.beforeSet(key,value);
		this.sendEvent( new TinyMighty.Giving.Event('beforeSet',{keyName:key,currentValue:currentValue,newValue:value}) );
		
		var setterName = 'set'+key.charAt(0).toUpperCase() + key.slice(1);
		if(typeof this[setterName] == 'function' && arguments.callee.caller != this[setterName]){
			this[setterName](key,value);
		}else{
			if(this.options && this.options.getset && this.options.getset.dataStorePropertyName && this[this.options.getset.dataStorePropertyName]){
				this[this.options.getset.dataStorePropertyName][key] = value;
			}else{
				this[key] = value;
			}
		}
		if(this._afterSet && typeof this._afterSet == 'function')
			this._afterSet();
		this.sendEvent( new TinyMighty.Giving.Event('afterSet',{keyName:key,oldValue:currentValue,newValue:value}) );

	},
	
	get: function get(key){
		this.sendEvent( new TinyMighty.Giving.Event('beforeGet',{keyName:key}) );
		var getterName = 'get'+key.charAt(0).toUpperCase() + key.slice(1);
		var returnValue = false;
		if(typeof this[getterName] == 'function' && arguments.callee.caller != this[getterName]){
			returnValue = this[getterName](key);
		}else{
			if(this.options && this.options.getset && this.options.getset.dataStorePropertyName && this[this.options.getset.dataStorePropertyName]){
				returnValue = this[this.options.getset.dataStorePropertyName][key];
			}else{
				returnValue = this[key];
			}
		}
		this.sendEvent( new TinyMighty.Giving.Event('afterGet',{keyName:key,value:returnValue}) );
		return returnValue;
	}
}

//Is Mixin
TinyMighty.Mixin.Is = {
	onMixin: function mixin(){
		if(!this.settings.is)
			this.settings.is = {};
		//console.log('mixing in Is');
	},
	is: function is(property){
		if(this.options.is[property] && this.options.is[property]===true){
			return true;
		}else{
			return false;
		}
	}
}




TinyMighty.Error = function tinymightyError(message,code){
	if(!code)
		code = '';
	console.error(code+' '+message);
}