var undefined;

(function() {
	// Used for trimming whitespace
	rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;

	window.$ = function(eid) {
		return document.getElementById(eid);
	};

	window.fff = {
		_log: [],
		_tmr: false,
		log: function() {
			var ele = $('console');
			if (!ele) {
				alert('no console');
			} else {
				if (fff._tmr) clearTimeout(fff._tmr);
				fff._log.push( Array.prototype.slice.call(arguments).join(' '));
				fff._tmr = setTimeout(function() {
					ele.innerHTML += fff._log.join('<br/>') + '<br/>';
					fff._log = [];
				}, 1000);
			}
		},
		viewport: function() {
			var res = {};
			if (window.innerHeight) {
				//~ fff.log('vp innerHeight: ' + window.innerHeight);
				res.height = window.innerHeight;
				res.width = window.innerWidth;
			} else {
				//~ fff.log('vp doc clientHeight: ' + document.documentElement.clientHeight,
					//~ 'doc body clientHeight: ' + document.body.clientHeight);
				res.height = document.documentElement.clientHeight || document.body.clientHeight;
				res.width = document.documentElement.clientWidth || document.body.clientWidth;
			}
			if (window.pageXOffset) {
				res.scrollLeft = window.pageXOffset;
				res.scrollTop = window.pageYOffset;
			} else {
				res.scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
				res.scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
			}
			return res;
		},
		size: function(ele) {
			var res = {
				height : ele.offsetHeight,
				width : ele.offsetWidth
			};
			return res;
		},

		findParent: function( ele, callback ) {
			while( ele ) {
				if (callback( ele )) { return ele; }
				ele = ele.parentNode;
			}
			return false;
		},

		relPos: function(ele, containerEle) {
			var x = ele.offsetLeft,
				y = ele.offsetTop,
				p = ele;
			//~ if (bLog) fff.log('==relPos==', ele.getAttribute('id') || ele.tagName, p.offsetTop);
			while(p = p.offsetParent) {
				if (p == containerEle) break;
				if (p.currentStyle && p.currentStyle.position==='static') continue; // IE !!  skip this one
				//~ if (bLog) fff.log(p.getAttribute('id') || p.tagName, p.offsetTop);
				x += p.offsetLeft || 0;
				y += p.offsetTop || 0;
			}
			//~ fff.log('-->', y);
			return {top: y, left: x};
		},

		absPos: function(ele) {
			return fff.relPos(ele);
		},

		setProps: function(o, vals) {
			for(var p in vals) o[p] = vals[p];
		},
		trim: function( text ) {
			return (text || "").replace( rtrim, "" );
		},
		_setClass: function(ele, a) {
			var res=[], b4=ele.className;
			for (var i=0,len=a.length; i<len; i++) {
				if (fff.trim(a[i]) !== '') res.push(a[i]);
			}
			ele.className = (res.length===0) ? '' : res.join(' ');
			//~ fff.log( ele.id, b4, '->', ele.className);
		},
		addClass: function(x, s) {
			var ele = (typeof x === 'string') ? $(x) : x;
			//~ fff.log('addclass', ele);
			if (!ele) return;
			var c = ele.className.split(' ');
			for (var i=0,len=c.length; i<len; i++) {
				if (c[i] == s) return;
			}
			c.push(s);
			fff._setClass( ele, c);
		},
		removeClass: function(x, s) {
			var ele = (typeof x === 'string') ? $(x) : x;
			//~ fff.log('removeclass', ele);
			if (!ele) return;
			var c = ele.className.split(' '), res = [], b4 = ele.className;
			for (var i=0,len=c.length; i<len; i++) {
				if (c[i] != s) res.push(c[i]);
			}
			fff._setClass( ele, res);
		},
		addClassByClass: function(eleContainer, clss, s) {
			var eles = fff.getElementsByClassName( eleContainer, clss);
			for(var i=0,len=eles.length; i<len; i++) {
				fff.addClass( eles[i], s);
			}
		},
		removeClassByClass: function(eleContainer, clss, s) {
			var eles = fff.getElementsByClassName( eleContainer, clss);
			for(var i=0,len=eles.length; i<len; i++) {
				fff.removeClass( eles[i], s);
			}
		},

		// stylin'
		height: function( ele, x) {
			//~ fff.log('fff.height:', ele.id || ele.tagName , x);
			if (x!==undefined) ele.style.height = x;
			return ele.style.height;
		},
		width: function( ele, x) {
			//~ fff.log('fff.width:', ele.id || ele.tagName , x);
			if (x!==undefined) ele.style.width = x;
			return ele.style.width;
		},

		closestTag: function(ele, tag) {
			var eleOrig = ele;
			tag = tag.toUpperCase();
			do {
				if (ele.tagName == tag) return ele;
			} while(ele = ele.parentNode);
			alert('closestTag did not find ' + tag + ' for ' + eleOrig.tagName);
		},
		toggle: function(x, display) {
			var ele = (typeof x == 'string') ? $(x) : x,
				style;
			if (ele) {
				ele.style.display = display || (ele.style.display=='none' ? 'block' : 'none');
			}
		},
		// only works w/ element styles, not stylesheet styles
		// assumes ele w/o style is visible
		fade: function( out, durationMs, ele) {
			var displayTo = out ? 'none' : 'block',
				opacityTo = out ? 0 : 100,
				opacity = out ? 100 : 0,
				delta = out ? -10 : 10,
				curStyle = ele.getAttribute('style'),
				tmr;
			//~ fff.log(curStyle);
			if ((out && curStyle=='display:none;')
					|| (!out && (!curStyle || curStyle=='display:block;'))) {
				return;		// nothing to do
			}
			tmr = setInterval( function() {
				opacity += delta;
				if (opacity==opacityTo) {
					clearInterval(tmr);
					ele.setAttribute('style', 'display:' + displayTo + ';');
				} else {
					ele.setAttribute('style',
						'opacity:' + (opacity/100) + ';'
						+ 'filter:alpha(opacity=' + opacity + ');'
					);
				}
			}, durationMs/10);
		},
		scrollIn: function(ele) {
			var yDest, direction, tmr,
				container = ele.parentNode,
				transition = [200, 150, 100, 70, 50, 40, 30, 25, 20, 15, 10, 5];
			yDest = fff.relPos( ele, container).top;
			direction = (yDest <= transition[0] + 10) ? 1 : -1;
			container.scrollTop = yDest + (direction * transition.shift());
			tmr = setInterval( function() {
				if (transition.length===0) {
					clearInterval(tmr);
					container.scrollTop = yDest;
				} else {
					container.scrollTop = yDest + (direction * transition.shift());
				}
			}, 50);
		},
		// borrowed from jquery 1.4.2
		xhr: window.XMLHttpRequest
				&& (window.location.protocol !== "file:" || !window.ActiveXObject)
			? function() {
					return new window.XMLHttpRequest();
				}
			: function() {
					try {
						return new window.ActiveXObject("Microsoft.XMLHTTP");
					} catch(e) {}
				},
		ajaxGet: function(url, callback) {
			var xhr = fff.xhr();
			xhr.onreadystatechange = function() {
				if (xhr.readyState==4 && xhr.status==200) {
					callback(xhr.responseText);
				}
			};
			xhr.open('GET', url);
			xhr.send(null);
		},

		showDetails: function( url) {
			var ele = $( 'product-details'),
			  eleView = $( 'product-details-view'),
				eleClose = $('close-button'),
				vp = fff.viewport(), size,
				h, w, t, l,
				xhr = fff.xhr(),
				eleBody = document.getElementsByTagName('body')[0];
			if (!ele) return;
			// 1st time: move them to the top to make positioning simpler
			if (ele.parent != eleBody) {
				eleBody.appendChild(ele);
				if (eleClose) {
					eleBody.appendChild(eleClose);
					eleClose.onclick = function() {
						fff.toggle(eleClose);
						fff.toggle(ele);
					};
				}
			}
			fff.ajaxGet( url, function(txt) {
				eleView.innerHTML = txt;
				// show, measure it, position it
				fff.toggle(ele, 'block');
				size = fff.size(ele);
				t = (vp.height - size.height)/2 + vp.scrollTop;
				l = (vp.width - size.width)/2 + vp.scrollLeft;
				fff.setProps( ele.style, {
					top: t + 'px',
					left: l + 'px'
				});
				if (eleClose) {
					fff.setProps( eleClose.style, {
						top: (t - 10) + 'px',
						left: (l + size.width - 50) + 'px',
						display: 'block'
					});
				}
			});
		},

		// event binding abstraction
		bind: function(eventName, ele, fn) {
			var fn0 = (function(fn) {
				return function(evt) {
					evt = evt || window.event;
					if (evt.preventDefault===undefined) {
						evt.preventDefault = function() {this.returnValue=false;};
					}
					if (evt.stopPropagation===undefined) {
						evt.stopPropagation = function() {this.cancelBubble=true;};
					}
					if (evt.target===undefined) {
						evt.target = evt.srcElement;
					}
					var res = fn.call( evt.target, evt);
					if (!res) { evt.preventDefault(); }
					return res;
				};
			}(fn));
			if ( ele.addEventListener ) {
				ele.addEventListener( eventName, fn0, false);
			} else {
				ele.attachEvent( 'on' + eventName, fn0);
			}
			return fn0;		// so that one may call unbind
		},
		unbind: function(eventName, ele, fn) {
			if ( ele.addEventListener ) {
				ele.removeEventListener( eventName, fn, false);
			} else {
				ele.detachEvent( 'on' + eventName, fn);
			}
		}


	};

	var eles = document.documentElement.childNodes,
		rspace = /\s+/;
	fff.getElementsByClassName =
		(eles.length && (typeof eles[0].getElementsByClassName === 'function'))
		// use the native implementation
		? function(eleContainer, clss) {
				return eleContainer.getElementsByClassName(clss);
			}
		// roll our own
		// recursion is a killer....
		: function(eleContainer, clss) {
				var eles = [eleContainer], i=0, j, len, jlen, res = [], classNames, ele;
				while(i<eles.length) {
					ele = eles[i++];
					if (ele.childNodes && ele.childNodes.length) {
						children = ele.childNodes;
						for(j=0,len=children.length; j<len; j++) {
							eles[eles.length]=children[j];
						}
					}
				}
				for(i=0,len=eles.length; i<len; i++) {
					ele = eles[i];
					if (!ele.tagName || ele.tagName==='' || ele.className==='') continue;
					classNames = ele.className.split( rspace);
					for(j=0,jlen=classNames.length; j<jlen; j++) {
						if (classNames[j]==clss) {
							res[res.length]=ele;
							break;
						}
					}
				}
				return res;
			};
	var userAgent = navigator.userAgent.toLowerCase();
	fff.os = {win: userAgent.indexOf('windows')>-1};
	fff.browser = {safari: userAgent.indexOf('webkit')>-1 && userAgent.indexOf('chrome')==-1};

	fff.initDetails = function(clss) {
		var bodyEles = document.getElementsByTagName('body'),
			eles, pinfo,
			i, len, foundId;
		if (bodyEles.length === 0) return;  // wtf?
		eles = fff.getElementsByClassName( bodyEles[0], clss);
		pinfo = fff.productInfo;
		// clean product info
		for(i=0,len=pinfo.length; i<len; i++) {
			pinfo[i].title = pinfo[i].title
			.replace(/&#160;/, ' ')
			.replace(/&#39;/, "'");
		}
		// assign behaviour to matching eles
		for(i=0,len=eles.length; i<len; i++) {
			foundId = _f(eles[i].innerHTML);
			if (foundId) {
				_set( eles[i], foundId);
			}
		}

		function _f(s) {
			for(var i=0,len=pinfo.length; i<len; i++) {
				if (pinfo[i].title == s) return pinfo[i].id;
			}
			return false;
		}

		function _set( ele, id) {
			fff.addClass( ele, 'pinfo');
			ele.onclick = function(evt) {
				// control window's size here.
				fff.showDetails('/?id=' + id);
			};
		}
	};

})();



