function async_error_handler(error) {
	alert(error.message);
}

function hover(obj, state){
	if(!obj){ return; }
	state = Boolean(state);
	if(state && !/hover/i.test(obj.className)){
		obj.className += ' hover';
	} else if(!state) {
		obj.className = obj.className.replace(/\s?hover\s?/i,'');
	}
}

/**
 * rgbToHex - Converts an rgb color value into a hexidecimal value (without the leading '#')
 * @param	integer		r		Value of the color red in range 0-255;
 * @param	integer		g		Value of the color green in range 0-255;
 * @param	integer		b		Value of the color blue in range 0-255;
 * @return	string				Hexidecimal representation of the the color
 */
function rgbToHex(r,g,b){
	var rgb = slice(arguments,0,3), hex = '0123456789ABCDEF';
	for(var i=0;i < 3;i++){
		rgb[i] = isNaN(parseInt(rgb[i],10)) ? 0 : parseInt(rgb[i],10);
		rgb[i] = Math.round(Math.max(Math.min(0,rgb[i]),255));
		rgb[i] = hex.charAt( (rgb[i]-rgb[i]%16)/16 ) + hex.charAt( rgb[i]%16 );
	}
	return rgb.join('');
}

/**
 * getStyle - Retrieve the value of the current style property
 * @param	mixed		obj		Element id or object
 * @param	string		prop	Name of style property to retrieve
 * @return	mixed				Value of the style property. *Note: Unit values will be returned as integers. (ie. 'px' will be stripped off)*
 */
function getStyle(obj,prop){
	obj = (typeof obj == 'object') ? obj : did(obj);
	if(!obj || !prop){ return null; }
	var regRGB = /rgb\((\d+),\s(\d+),\s(\d+)\)/i, color;
	var compVal = obj.currentStyle ? obj.currentStyle[prop] : window.getComputedStyle(obj,null).getPropertyValue(prop);
	if((color = regRGB.exec(compVal)) != null){ color.shift(); compVal = '#'+rgbToHex.apply(null,color); }
	return isNaN(parseFloat(compVal)) ? compVal : parseFloat(compVal);
}

/**
 * DOM Utilities
 */

/**
 * dbn - Shorcut for getElementsByName
 * @param	name	The element name you are looking for
 * @param	parent	optional;The parent item to search within
 * @return			Collection of elements
 */
function dbn(name, parent) {
	return (parent || document).getElementsByName(name);
}
/**
 * dbt - Shorcut for getElementsByTagName
 * @param	tag		The element tagName you are looking for
 * @param	parent	optional;The parent item to search within
 * @return 			Collection of elements
 */
function dbt(tag, parent) {
	return (parent || document).getElementsByTagName(tag);
}
/**
 * dbc - Shortcut for getElementsByClass
 * @param       className       The className you are looking for
 * @param       parent          optional;The parent item to search within
 * @return                              Array of elements
 */
function dbc(className, parent) {
	//use default provided by browser if it exists otherwise use our implementation
	if(document.getElementsByClassName) {
		return (parent || document).getElementsByClassName(className);
	}
	return getElementsByClass((parent || document), className);
}

//Find elements with a given className and returns them in an array
function getElementsByClass(root, className) {
	var nodes = slice(dbt('*', root)), elms = [];
	var hasClass = new RegExp('(\b|\s*)'+className.trim()+'(\b|\s*)');
	nodes.forEach(function(node){
		if(hasClass.test(node.className)) {
			elms.push(node);
		}
	});
	return elms;
}
/**
 * next - Returns the next non-whitespace sibling element
 * @param	el	object	Element node from which to start
 * @return		object	Next non-whitespace child element
 */
function next(el) {
	if(!el || !el.nextSibling) { return null; }
	el=el.nextSibling;
	return (el.nodeType==1) ? el : next(el);
}
/**
 * prev - Returns the previous non-whitespace sibling element
 * @param	el	object	Element node from which to start
 * @return		object	Previous non-whitespace child element
 */
function prev(el) {
	if(!el || !el.previousSibling) { return null; }
	el=el.previousSibling;
	return (el.nodeType==1) ? el : prev(el);
}
/**
 * first - Returns the first non-whitespace child element
 * @param	el	object	Parent element node
 * @return		object	First non-whitespace child element
 */
function first(el) {
	if(!el || !el.firstChild) { return null; }
	el=el.firstChild;
	return (el.nodeType==1) ? el : next(el);
}
/**
 * last - Returns the last non-whitespace child element
 * @param	el	object	Parent element node
 * @return		object	Last non-whitespace child element
 */
function last(el) {
	if(!el || !el.lastChild) { return null; }
	el=el.lastChild;
	return (el.nodeType==1) ? el : prev(el);
}

/**
 * createElement - Shortcut for document.createElement
 * @param	tag		Tag of the element to create
 * @return			Element or null if no tag specified
 */
function createElement(tag) {
	if (!tag) { return null; }
	return document.createElement(tag);
}
/**
 * createTextNode - Shortcut for document.createTextNode
 * @param	text	Text to place inside the new node
 * @return			New text node
 */
function createTextNode(text) {
	return document.createTextNode(String(text));
}

/**
 * getDocSize - Get the size of the current viewable document area
 * @return	object		The width, height of the current viewable document area in a keyed object ( obj.width obj.height )
 */
function getDocSize() {
  var w = 0, h = 0;
  if(typeof(window.innerWidth) == 'number'){
    //Non-IE
    w = window.innerWidth;
    h = window.innerHeight;
  }else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)){
    //IE 6+ in 'standards mode'
    w = document.documentElement.clientWidth;
    h = document.documentElement.clientHeight;
  }else if(document.body && (document.body.clientWidth || document.body.clientHeight)){
    //IE 4 compatible
    w = document.body.clientWidth;
    h = document.body.clientHeight;
  }
  return {'width':w,'height':h};
}

/**
 * getMaxDocSize - Get the total document size
 * @return	object		The width, height of the total document sie in a keyed object ( obj.width obj.height )
 */
function getMaxDocSize() {
	var docSize = getDocSize();
	var w = 0, h = 0;
	
	if(window.innerHeight && window.scrollMaxY) {
		w = docSize['width'] + window.scrollMaxX;
		h = docSize['height'] + window.scrollMaxY;
		//Remove 15 pixels for the scrollbars
		if(window.scrollMaxY > 0){ w -= 15; }
		if(window.scrollMaxX > 0){ h -= 15; }
	} else if(document.body.scrollHeight > document.body.offsetHeight) {
		w = document.body.scrollWidth;
		h = document.body.scrollHeight;
	} else {
		w = document.body.offsetWidth;
		h = document.body.offsetHeight;
	}
	return {'width':w,'height':h};
}

/**
 * getScrollXY - Gets the distance the page has been scrolled vertically and horizontally
 * @return	object		The x,y distance in a keyed object ( obj.x obj.y )
 */
function getScrollXY() {
	var sX = 0, sY = 0;
	if(typeof(window.pageYOffset)=='number'){
		//Non-IE
		sY = window.pageYOffset;
		sX = window.pageXOffset;
	}else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)){
		//IE 6+ in 'standards mode'
		sY = document.documentElement.scrollTop;
		sX = document.documentElement.scrollLeft;
	}else if(document.body && (document.body.scrollLeft || document.body.scrollTop)){
		//IE 6 in 'strict mode' & some other browsers
		sY = document.body.scrollTop;
		sX = document.body.scrollLeft;
	}
	return {'x':sX,'y':sY};
}

/**
 * Array and object functions
 */

/**
 * Array methods indexOf, forEach, map, filter
 * 		Added for browsers without native support
 */
if(!Array.indexOf){
	Array.prototype.indexOf = function(obj,start){
		var len = this.length;
		start = Number(start) || 0;
		start = (start < 0) ? Math.ceil(start) : Math.floor(start);
		if(start < 0){ start+= len; }
		for(;start < len;start++){
			if(start in this && this[start]===obj){ return start; }
		}
		return -1;
	}
}
if(!Array.forEach){
	Array.prototype.forEach = function(fn /*, bind*/){
		var len = this.length;
		if(typeof(fn) != 'function'){ throw new TypeError(); }
		var bind = arguments[1];
		for(var i=0;i < len;i++){
			if(i in this){ fn.call(bind, this[i], i, this); }
		}
	};
}
if(!Array.map){
	Array.prototype.map = function(fn /*, bind*/){
		var len = this.length;
		if(typeof(fn) != 'function'){ throw new TypeError(); }
		var ret = [], bind = arguments[1];
		for (var i=0;i < len;i++){
			if(i in this){ ret[i] = fn.call(bind, this[i], i, this); }
		}
		return ret;
	};
}
if(!Array.filter){
	Array.prototype.filter = function(fn /*, bind*/){
		var len = this.length;
		if(typeof(fn) != 'function'){ throw new TypeError(); }
		var ret = [], bind = arguments[1], val=null;
		for(var i=0;i < len;i++){
			if(i in this){
				val=this[i];
				if(fn.call(bind,val,i,this)){ ret.push(val); }
			}
		}
		return ret;
	};
}

/**
 * slice - Shortcut for Array.prototype.slice.call(obj, idx)
 * @param	obj		mixed		Object to call array.slice on
 * @param	start	integer		Index at which to begin slicing
 * @param	end		integer		Index at which to end slicing
 * @return			array		New array containing values from the idx to the end of the obj
 *		Note: Useful for transforming arguments object and collections into regular arrays
 *		ex.	function(){ var args = slice(arguments); alert(args instanceof Array); }
 */
function slice(obj, start, end){
	var ret = obj;
	if(/msie/i.test(navigator.appVersion)){
		if(typeof obj.length=='undefined'){ obj.length = getLength(obj); }
		ret = Array.prototype.map.call(obj,function(item){return item;});
	}
	var args = [(start || 0)];
	if(end && !isNaN(Number(end))) {
		args.push(Number(end));
	}
	return Array.prototype.slice.apply(ret,args);
}

/**
 * getLength - Finds the total number of all non-function properties owned by the object (see hasOwnProperty)
 * @param obj    object    The object whose properties to count.
 * @return       int       The length of the object or 0 if obj was invalid
 */
function getLength(obj){
    if(!obj){ return 0; }
    var i=0;
    for(var key in obj){
        if(obj.hasOwnProperty(key) && typeof obj[key] !='function'){i++;}
    }
    return i;
}

/**
 * DOM Event Functions & Objects
 */

/* domLoader - detects when the DOM is ready in the browser (typically before onload would fire).
 * Also allows registration of functions to execute once ready.
 * 
 * Example using the optional object and args parameters:
 * 		-keyword 'this' will be the person object
 * 		-arg1 and arg2 become 'name' and 'job'
 * 		-function will log:
 * 			name: bob job: plummer
 * 		
 * 		var person = {'name':'bob','job':'plummer'};
 * 		domLoader.register(function(arg1, arg2){
 * 			console.log(arg1+': '+this[arg1]+' arg2: '+this[arg2]);
 * 		}, person, ['name','job']); 
 */
var domLoader = {
	'isReady': false,
	'isBound': false,
	'queue': [],
	'binds': [],
	'args': [],
	/**
	 * register - Register a function to be executed when the dom is ready
	 * @param       fn              function        Function to execute
	 * @param       obj             object          optional;Object that will be the scope for fn(keyword 'this' inside function, default is window)
	 * @param       args    array           optional;Array of parameters to be passed to the function upon execution
	 */
	'register': function(fn, obj, args) {
		this.checkReady();
		this.queue = (this.queue instanceof Array) ? this.queue : [];
		if(!fn || typeof fn != 'function') {
			return;
		}

		var offset = this.queue.push(fn) - 1;

		obj = obj || null;
		this.binds = (this.binds instanceof Array) ? this.binds : [];
		this.binds[offset] = obj;

		args = args ? ((args instanceof Array) ? args :  slice(args)) : [];
		this.args = (this.args instanceof Array) ? this.args : [];
		this.args[offset] = args;
		return this;
	},
	/**
	 * checkReady - Starts checking the for the dom to be ready (internal use)
	 */
	'checkReady': function() {
		if(this.isBound) {
			return;
		}
		this.isBound = true;
		var self = this;
		var events = {'load': window};
		// Mozilla, Opera and Safari
		if(document.addEventListener) {
			events['DOMContentLoaded'] = document;
		// IE
		} else if(document.attachEvent) {
			events['onreadystatechange'] = document;
			// If IE and not an iframe -- continually check to see if the document is ready
			if(document.documentElement.doScroll && window == window.top) {
				(function(){
					if(self.isReady) {
						return;
					}
					try {
						// If IE is used, use the trick by Diego Perini -- http://javascript.nwbox.com/IEContentLoaded/
						document.documentElement.doScroll("left");
					} catch(error) {
						setTimeout(arguments.callee, 0);
						return;
					}
					// and execute any waiting functions
					self.isReady = true;
					self.ready();
				})();
			}
		}
		
		//Faster Safari detection
		if(typeof navigator.taintEnabled === 'undefined') {
			var timer = window.setInterval(function() {
				if(/loaded|complete/.test(document.readyState)){
					window.clearInterval(timer);
					if(self.isReady) {
						return;
					}
					self.isReady = true;
					self.ready();
				}
			}, 10);
		}
		
		for(var name in events) {
			(function(name, obj) {
				registerEvent(obj, name, function() {
					unregisterEvent(obj, name, arguments.callee);
					if(!self.isReady) {
						self.isReady = true;
						self.ready();
					}
				});
			})(name, events[name]);
		}
	},
	/**
	 * ready - Executes all registered functions with any bound scope & args (internal use)
	 */
	'ready': function(){
		if(!this.isReady || !(this.queue instanceof Array)) {
			return;
		}

		var fn, obj, args;
		while(fn = this.queue.shift()) {
			obj = this.binds.shift() || window;
			args = this.args.shift();
			fn.apply(obj, args);
		}
		
		this.queue = [];
		this.binds = [];
		this.args = [];
	}
};

/**
 * fixEvent - Returns an event object with common properties/methods normalized for easier cross browser usage.
 * @param e		object	optional;Event object to normalize
 * @return		object	Event object after normalization
 */
function fixEvent(e){
	var evnt = e || window.event;
	if (!evnt) { return null; }
	if(!evnt.target){ evnt.target = evnt.srcElement; }
	evnt.preventDefault = (evnt.preventDefault)? evnt.preventDefault : function(){ this.returnValue = false; };
	evnt.stopPropagation = (evnt.stopPropagation)? evnt.stopPropagation : function(){ this.cancelBubble = true; }
	return evnt;
}

function noRightClick(img){
	registerEvent(img, 'mousedown', function(e) {
		e = fixEvent(e);
		if(e.which == 3) {
			e.preventDefault();
		}
	});
	registerEvent(img, 'contextmenu', function(e){
		fixEvent(e).preventDefault();
	});
}

domLoader.register(function(){
	var imgs = slice(dbt('img'));
	imgs.forEach(noRightClick);
});
