(function() {
	var addEvent = function( obj, type, fn ) {
		if (obj.addEventListener)
			obj.addEventListener(type, fn, false);
		else if (obj.attachEvent)
			obj.attachEvent('on' + type, function() { return fn.call(obj, window.event);});
	},

	$ = function(n) {
		return document.getElementById(n);
	},

	/**
	 * Wrapper for getElementsByTagName
	 * @param string tn The tagname to search for
	 * @param object Optional. The parent object under which to search for these tags.
	 * @param string callback Optional.  A callback function called on each tag object: if returned true, the object
	 * 	is included; otherwise, it's not.
	 * @return array The array of matched objects.
	 */
	$tn = function(tn, obj, callback) {
		var i, objs, returnedObjs = [];
		obj = obj || document;
		callback = callback || null;
		objs = obj.getElementsByTagName(tn);
		if ( ! callback )
			return objs;
		else {
			for ( i = 0; i <= objs.length; i += 1 ) {
				if ( objs[i] && callback(objs[i]) )
					returnedObjs[returnedObjs.length] = objs[i];
			}
			return returnedObjs;
		}
	},

	/**
	 * Get the object that was the target of an event
	 * @param object e The event object (or null for ie)
	 * @return object The target object.
	 */
	getEventTarget = function(e) {
		e = e || window.event;
		return e.target || e.srcElement;
	},

	/**
	 * Show Chinese characters under the menu
	 */
	chineseCharacters = function() {
		var bodyEl = $tn('body')[0],
		chars = document.createElement('div'),
		charsObj = ObjectAnimation(chars),
		header = $('header'),
		i,
		menu = [
			$("learn-history-menu-item"),
			$("browse-collection-menu-item"),
			$("get-inspired-menu-item"),
			$("stay-current-menu-item")
		],
		nav = $('primary-nav');
		if ( bodyEl ) {
			chars.setAttribute('id', 'menu-characters');
			chars.style.display = 'none';
			bodyEl.appendChild(chars);
			
			if ( nav && header ) {
				addEvent(header, 'mouseout', function(e) {
					e = e || window.event;
					var j, 
					rel = e.relatedTarget || e.toElement;
					// if the mouseover ends up outside of nav or its children, hide the characters
					do {
						if ( rel && rel === nav )
							return;
						else if ( rel && rel.parentNode )
							rel = rel.parentNode;
						else
							rel = null;
					} while ( rel );
					charsObj.hide();
				});
			}

			for( i = 0; i < menu.length; i++ ) {
				(function(i,menu,charsObj) {
					if ( menu[i] ) {
						var menuPos = function() {
							var pos = getElementPos(menu[i]),
							left = pos[0] + 'px',
							top = (pos[1] + menu[i].offsetHeight + 5) + 'px';

							chars.style.left = left;
							chars.style.top = top;
							charsObj.show();

							menuPos = function() {
								chars.style.left = left;
								chars.style.top = top;
								charsObj.show();
							}
							return menuPos;
						};

						menu[i].onmouseover = function(e) {
							menuPos();
						}
					}
				})(i,menu,charsObj);
			}
		}
	},
	
	/**
	 * Fade an element
	 * @param object obj The object to fade.
	 * @param args Optional. Object of possible arguments:
	 * 	callback - A function called upon completion.  Passed object as first argument.
	 * 	from	 - The % of opacity to start at
	 * 	rate 	 - The number of milliseconds between steps
	 * 	time	 - The time from start to finish in milliseconds
	 * 	to	 - The % of opacity to end up at
	 */
	fade = function(obj, args) {
		if ( ! obj )
			return;
		var callback = args.callback || function(o) {},
		from = 'undefined' != typeof args.from ? args.from : 100,
		rate = 'undefined' != typeof args.rate ? args.rate : 25,
		time = 'undefined' != typeof args.time ? args.time : 300,
		to = 'undefined' != typeof args.to ? args.to : 0,
		
		steps = time / rate,
		inc = ( to - from ) / steps,
		i, last = false;
		
		obj.style.opacity = from/100;
		obj.style.filter = 'alpha(opacity='+from+')';

		for( i = 0; i <= steps; i += 1 ) {
			last = ( i + 1 ) <= steps ? false : true;
			(function(o) {
				var doCallback = last,
				j = i,
				perc = ( i * inc ) + from;
				setTimeout(function() {
					obj.style.opacity = perc/100;
					obj.style.filter = 'alpha(opacity=' + perc + ')';
					if ( doCallback )
						callback(o);
				}, j * rate );
			})(obj);
		}
	},

	/**
	 * Get the position of the object relative to the document
	 * @param obj The object in question
	 * @return array(x,y) The relative left and top position of the object.
	 */
	getElementPos = function(obj) {
		var curleft = curtop = 0;
		if (obj.offsetParent) {
			do {
				curleft += obj.offsetLeft;
				curtop += obj.offsetTop;
			} while (obj = obj.offsetParent);
			return [curleft,curtop];
		}
	},

	/**
	 * An object for animating objects without race collisions
	 * @param obj The object to be manipulated
	 * @return An object of helper functions.
	 */
	ObjectAnimation = function(obj) {
		if ( ! obj )
			return;
		var active = true,	// whether the object is active
		inProcess = false,	// whether the animation is in process
		opacity = 0,		// the current opacity of the object
		visible = true;		// whether the object is visible
		
		return {
			time:500, 	// the duration of the animation in milliseconds
			get:function() {
				return obj;
			},

			getVisibility:function() {
				return !! visible;
			},

			/**
			 * Hide the object.  Prevents race condition with show()
			 */
			hide:function() {
				if ( ! inProcess ) {
					inProcess = true;
					obj.style.display = 'block';
					visible = true;
					fade(obj,{
						from:opacity,
						to:0, 
						time:this.time,
						callback:function(obj) {
							obj.style.display = 'none';
							opacity = 0;
							visible = false;
							inProcess = false;
						}
					});
				}
			},
			
			/**
			 * Show the object.  Prevents race condition with hide()
			 */
			show:function() {
				if ( ! inProcess ) {
					inProcess = true;
					obj.style.display = 'block';
					visible = false;
					fade(obj,{
						from:opacity,
						to:100,
						time:this.time,
						callback:function() {
							inProcess = false;
							opacity = 100;
							visible = true;
						}
					});
				}
			},

			/**
			 * Decide what to do with the display of the object, based on its status and visibility
			 */
			render:function() {
				if ( visible && ! active ) 
					this.hide();
				else if ( ! visible && active )
					this.show();
			},

			/**
			 * Public method to get the status of the object
			 * @return The status of the object
			 */
			getStatus:function() {
				return true === active ? 'active' : 'inactive';
			},

			/**
			 * Public method to set the status of the object
			 * @param state The desired status of the object
			 */
			setStatus:function(state) {
				active = 'active' == state ? true : false;
				return this;
			}
		};
	},

	galleryMethods = function() {
		var wrap = $('product-image-wrap');
		if ( wrap && 'undefined' != typeof jQuery ) {
			var j = jQuery;
			//set up link wrapper for zoom feature
			var linkWrap = document.createElement('a');
			var src = j('img', wrap).attr('src'),
			origImg = new Image();
			origImg.src = src;
			j(linkWrap).addClass('zoom-link').attr('href', src).append(j('img', wrap));
			j(wrap).html(linkWrap);
			zoomInit({imageHeight:origImg.height,imageWidth:origImg.width});
			var maxWidth = j('#media-wrap').width();
			j('.gallery-item a').each(function(i, el) {
				if ( j(el).hasClass('image') ) {
					(function(elm, src) {
						var img = new Image(),
						cH, cW, oH, oW,
						wrapper = document.createElement('a');
						img.src = src;
						j(wrapper).attr('href', src).addClass('zoom-link');
						j(wrapper).append(img);
						// set the preview image size on load, after a delay
						setTimeout(function() {
							cW = j(img).width();
							cH = j(img).height();
							oH = img.height;
							oW = img.width;
							newHeight = maxWidth && 0 < cW ? maxWidth * ( cH / cW ) : 'auto';
							j(img).height(newHeight);
							j(img).width(maxWidth);
						}, 200);


						j(elm).click(function(e) {
							img.id = 'main-product-image';
							j(wrap).html(wrapper);
							zoomInit({imageWidth:oW,imageHeight:oH});
							return false;
						});
					})(el, j(el).attr('href'));
				// we're dealing with a video thumb
				} else if ( j(el).hasClass('flv') && 'undefined' != typeof flowplayer ) {
					(function(link, playerID, thumbSrc, vidPath) {
						j(link).click(function(e) {
							j(wrap).html('<div id="video-player" style="display:block;height:225px;width:300px;"></div>')
							flowplayer('video-player', playerPath, {
								playlist: [
									{
										url: thumbSrc, 
										scaling: 'orig'
									},
									{
										url: vidPath, 
										autoPlay: false, 
										autoBuffering: true 
									}
								]
							}).setVolume(50);
							return false;
						});
					})(el, j(el).attr('rel'), iconPath, j(el).attr('href'));
				}
			});
			
		}
	},

	inputReminder = function(id, text) {
		var el = $(id);
		if ( el ) {
			el.value = '' == el.value ? text : el.value;
			addEvent(el, 'focus', function(e) {
				el.value = text == el.value ? '' : el.value;
			});

			addEvent(el, 'blur', function(e) {
				el.value = '' == el.value ? text : el.value;
			});
		}
	},

	/**
	 * Do ajax stuff with product form
	 */
	productForm = function() {
		if ( 'undefined' != typeof jQuery ) {
			var j = jQuery;
			j('#product-form').submit(function(e) {
				// let's submit the only form element
				var data = {add: j('#product-id-field').val()};
				var url = j('#product-form').attr('action');
				j.ajax({
					data:data,
					url:url,
					type:'POST',
					success:function(resp) {
						window.location = '/?_g=co&_a=cart';
						return;
						// now let's get the usermeta info
						j.ajax({
							dataType:'html',
							type:'GET',
							success:function(userMeta) {
								// update cart ajaxily
								j('#main-user-meta')
									.html('')
									.append(userMeta)
									.fadeOut()
									.fadeIn()
									.fadeOut()
									.fadeIn()
							},
							url:'/ajax.php?get=box&boxName=userMeta'
						});
						// disable submit buttons
						j('.add-product-to-cart')
							.attr('disabled','disabled')
							.fadeTo('slow', .4)
							.css('cursor','default')
					}
				})
				return false;
			});
		}
	},

	setupUsermeta = function() {
		// setup usermeta (esp. for non-cc parts of the site)
		var userMeta = $('main-user-meta');
		if ( ! userMeta )
			return;
		var r = new xhr();
		r.open('GET', '/ajax.php?get=box&boxName=userMeta', true);
		r.onreadystatechange = function() {
			if ( 4 == r.readyState ) {
				userMeta.innerHTML = r.responseText;
				r = null;
			}
		};
		r.send(null);
	},

	shippingLookup = function() {
		if ( 'undefined' != typeof jQuery ) {
			var j = jQuery;
			j('#calculate-shipping').click(function(e) {
				var zip = j('#shipping-lookup-field').val();
				var url = cartAjaxPath || '/ajax.php';
				var prod = j('#product-id-field').val();
				if ( zip && prod ) {
					url += '?get=shipping&item_id='+prod+'&zip='+zip;
					j.ajax({
						dataType:'html',
						success:function(resp) {
							j('#shipping-discussion').hide().html(resp).fadeIn(1000);				
						},
						type:'GET',
						url:url
					});
				}
				return false;
			});
		}
	},

	zoomInit = function(args) {
		args = args || {};
		// resize thumbnail square so it looks proportional to images scaled to 300px width
		var zoomWidth = args.imageWidth ? ( args.imageWidth * 100 ) / 300 : 100;
		if ( 'undefined' != typeof jQuery ) {
			var options = {
				zoomWidth: zoomWidth,
				zoomHeight: zoomWidth,
				showEffect:'fadein',
				hideEffect:'fadeout',
				fadeinSpeed:'slow',
				fadeoutSpeed: 'slow',
				title :false
			}
			jQuery('.zoom-link').jqzoom(options);
		}
	},

	init = function() {
		// add zip code reminder
		inputReminder('shipping-lookup-field', 'Enter Zip Code');
		inputReminder('email', 'name@example.com');
		inputReminder('searchStr', 'Enter search term');

		galleryMethods();
		productForm();
		setupUsermeta();
		shippingLookup();
		// delay character setup until xhr-loaded elements have a chance to render
		setTimeout(chineseCharacters, 1400);
	},

	xhr = 'undefined' == typeof XMLHttpRequest ? 
		function() {
			return new ActiveXObject(navigator.userAgent.indexOf('MSIE 5') >= 0 ? 'Microsoft.XMLHTTP' : 'Msxml2.XMLHTTP');
		} :
		XMLHttpRequest;

	addEvent(window, 'load', init);
})();

