if (!window.console) {
	window.console = {
		log: function(arg) {
			alert(arg)
		}
	};
}

var DomWrapper = Class.$extend({
	
	__init__: function(element) {
		this._ = element;
		this.eventHandlers = [];
		this.eventHandlerWrappers = [];
		this.guid = $.guid();
	},
	
	parent: function(tagName) {
		var currentParent = this._.parentNode || null;
		if (tagName) {
			while (currentParent) {
				if (currentParent.tagName === tagName.toUpperCase()) {
					break;
				} else {
					currentParent = currentParent.parentNode;
				}
			}
		}
		return $(currentParent);
	},
	
	hasClass: function(className) {
		var regexp = new RegExp('\\b' + className + '\\b');
		return regexp.test(this._.className);
	},
	
	addClass: function(className) {
		if ( !this.hasClass(className) ) {
			this._.className += className;
		}
		return this;
	},
	
	removeClass: function(className) {
		var regexp = new RegExp('\\b' + className + '\\b');
		this._.className = this._.className.replace(regexp, '');
		return this;
	},
	
	children: function(tagOrClassName) {
		var array = [], nodeList = this._.childNodes;
		
		// find by className
		if (tagOrClassName.indexOf('.') === 0) {
		
			for (var l = nodeList.length, n = 0; n < l; n++) {
				if (nodeList[n].tagName && $(nodeList[n]).hasClass(tagOrClassName)) {
					array.push( $(nodeList[n]) );
				}
			}
		
		// find by tagName
		} else {
			
			for (var l = nodeList.length, n = 0; n < l; n++) {
				if (nodeList[n].tagName === tagOrClassName.toUpperCase()) {
					array.push( $(nodeList[n]) );
				}
			}
		}
		
		return array;
	},
	
	get: function(prop) {
		return this._[ prop ];
	},
	
	set: function(prop, value) {
		this._[ prop ] = value;
		return this;
	},
	
	on: function(eventName, handler) {
		var this_ = this;
		
		var realEventName;
		if ( eventName == 'mouseenter' || eventName == 'mouseleave' ) {
			realEventName = (eventName == 'mouseenter') ? 'mouseover' : 'mouseout';
		} else {
			realEventName = eventName;
		}
		
		var addEventMethodName;
		if ( this_._.addEventListener ) {
			addEventMethodName = 'addEventListener';
		} else {
			addEventMethodName = 'attachEvent';
			realEventName = 'on' + realEventName;
		}
		
		var eventHandlerWrapper = function(e) {
			
			if ( !e.currentTarget ) {
				e.currentTarget = this_._;
			}
			if ( !e.target ) {
				e.target = e.srcElement;
			}
			if ( !e.stopPropagation ) {
				e.stopPropagation = function() {
					e.cancelBubble = true;
				};
			}
			if ( !e.preventDefault ) {
				e.preventDefault = function() {
					e.returnValue = false;
				};
			}
			
			if ( eventName == 'mouseenter' || eventName == 'mouseleave' ) {
				if ( !e.relatedTarget ) {
					e.relatedTarget = (eventName == 'mouseenter') ? e.fromElement : e.toElement;
				}
				if ( e.relatedTarget != this_._ && !$(e.relatedTarget).isDescendantOf(this_._) ) {
					handler.call( this_._, e );
				}
			} else {
				handler.call( this_._, e );
			}
			
		}
		this_.eventHandlerWrappers.push(eventHandlerWrapper);
		this_.eventHandlers.push(handler);
		
		this_._[addEventMethodName](realEventName, eventHandlerWrapper, false);
		
		return this;
	},
	
	removeEvent: function(eventName, handler) {
		var this_ = this;
		
		var eventHandlerWrapper, eventHandlerWrapperIndex = this_.eventHandlers.indexOf(handler);
		if (eventHandlerWrapperIndex !== -1) {
			eventHandlerWrapper = this_.eventHandlerWrappers[ eventHandlerWrapperIndex ];
			
			var realEventName = eventName;
			
			var removeEventMethodName;
			if ( this_._.removeEventListener ) {
				removeEventMethodName = 'removeEventListener';
			} else {
				removeEventMethodName = 'detachEvent';
				realEventName = 'on' + eventName;
			}
			
			this_._[removeEventMethodName](realEventName, eventHandlerWrapper, false);
			delete this_.eventHandlers[ eventHandlerWrapperIndex ];
			delete this_.eventHandlerWrappers[ eventHandlerWrapperIndex ];
		}
	},
	
	isDescendantOf: function(possibleParent) {
		var currentParent = this._.parentNode;
		while (currentParent) {
			if ( currentParent == possibleParent ) {
				return true;
			}
			currentParent = currentParent.parentNode;
		}
		return false;
	}
	
});

window.$ = function( elementOrElementId ) {
	var element = (typeof elementOrElementId == 'string') ? document.getElementById( elementOrElementId ) : elementOrElementId;
	var wrappedElementIndex = window.$.wrappedElements.indexOf(element);
	if ( wrappedElementIndex !== -1 ) {
		var domWrapper = window.$.domWrappers[ wrappedElementIndex ];
		return domWrapper;
	} else {
		window.$.wrappedElements.push( element );
		var newDomWrapper = new DomWrapper( element );
		window.$.domWrappers.push( newDomWrapper );
		return newDomWrapper;
	}
}
window.$.wrappedElements = [];
window.$.domWrappers = [];

window.$$ = function( selector, context ) {
	var dowWrappers = [],
		nodeList = (context ? context : document).querySelectorAll( selector ),
		nodeListLength = nodeList.length;
	
	for ( i = 0; i < nodeListLength; i++ ) {
		dowWrappers[i] = $( nodeList[i] );
	}
	return dowWrappers;
}

$.guid = (function() {
	var guid = 1;
	return function() { return guid++ }
})();

var EventDispatcher = Class.$extend({
	
	__init__: function() {
		this.handlers = {};
	},
	
	addHandler: function(eventName, handler) {
		if (this.handlers[eventName] === undefined)
			this.handlers[eventName] = [];
		this.handlers[eventName].unshift(handler); // unshift rather than push because #dispatchEvent does a reverse-for
	},
	
	removeHandler: function(eventName, handler) {
		for (var h = this.handlers[eventName].length - 1; h >= 0; h--)
			if (arguments.length === 1 || this.handlers[eventName][h] === handler)
				delete this.handlers[eventName][h];
	},
	
	dispatchEvent: function(eventName) {
		if (this.handlers[eventName])
			for (var h = this.handlers[eventName].length - 1; h >= 0; h--)
				this.handlers[eventName][h]();
	}
	
});

var Interval = EventDispatcher.$extend({
	
	__init__: function(opts) {
		this.$super();
		
		this.duration = opts['duration'];
	},
	
	start: function() {
		this.interval = setInterval(this.dispatchEvent.bind(this, 'iteration'), this.duration);
	},
	
	stop: function() {
		clearInterval(this.interval);
		this.interval = null;
	}
	
});

var Looper = Interval.$extend({
	
	__init__: function(opts) {
		this.$super(opts);
		
		this.length = opts['length'];
		this.currentIndex = opts['startIndex'] || 0;
		
		this.addHandler('iteration', this.gotoNext.bind(this));
	},
	
	goto: function(newIndex) {
		this.currentIndex = newIndex;
	},
	
	gotoNext: function() {
		var newIndex;
		if (this.currentIndex >= this.length - 1) {
			newIndex = 0;
		} else {
			newIndex = this.currentIndex + 1;
		}
		this.goto(newIndex);
	},
	
	gotoPrevious: function() {
		var newIndex =
			this.currentIndex <= 0
				? this.length - 1
				: this.currentIndex - 1;
		
		this.goto(newIndex);
	}
	
});

