/**************************************************************************\
* Copyright 2006-2009 myShape Inc.                                         *
* --------------------------------------------                             *
* who to blame: Ryan Day =P                                                *
* --------------------------------------------                             *
*  This code is proprietary software; you cannot redistribute it           *
*  without the express written consent of myShape Inc. Certain portions    *
*  of this code rely on the jQuery framework and may fall within	   *
*  the restrictions of the jQuery license                       	   *
\**************************************************************************/

if(!window.console){
	window.console = {
		log:function(msg){},
		info:function(msg){},
		error:function(msg){},
		warn:function(msg){}
	};
}

(function($){

$.fn.extend({
	flashClass:function(cls,spd){
		var spds={'fast':2000,'normal':4500,'slow':6000};
		if (/[^\d]/.test(spd)) spd = (typeof(spds[spd])!='undefined'?spds[spd]:spds['normal']);
		if(spd>400){
			this.each(function(k,v){
				var el = this;
				$(el).addClass(cls);

				var hits = (+($(el).attr('jqhits')) || 0)+1;
				$(el).attr('jqhits',hits);
				setTimeout(function(){
					if(hits == $(el).attr('jqhits')){
						$(el).removeClass(cls);
					}
				},spd);
			});
		}
		return this;
	},
	getDims:function(outer){
		return $.positionHelper.elementDims(this,outer);
	},
	liveHover:function(on,off,intentDelay){
		var dlyOn, dlyOff, z, cbh;

		var moveTollerance = 10;

		if(typeof(intentDelay) == 'object'){
			dlyOn = +intentDelay.on || 0;
			dlyOff = +intentDelay.off || 0;
		} else {
			dlyOff = dlyOn = +intentDelay || 0;
		}

		var z = this;
		var hoverOn = false;
		var cbh = function(el,cb,ev){
			cb.call(el,ev);
		};

		var currentTimer = null;

		return this.live('mouseover',function(ev){

			var y = this;
			if(!hoverOn){
				hoverOn = true;
				if(dlyOn){
					var timeoutFiredPos = {x:ev.pageX,y:ev.pageY};

					var createTimer = function(){
						if(currentTimer){
							clearTimeout(currentTimer);
						}
						currentTimer = setTimeout(function(){
							if(hoverOn){
								$(y).unbind('mousemove');
								cbh(y,on,ev);
							}
						},dlyOn);
					}

					createTimer();

					$(y).unbind('mousemove');
					$(y).mousemove(function(moveEV){
						var x = moveEV.pageX;
						var y = moveEV.pageY;

						var x1 = timeoutFiredPos.x+moveTollerance;
						var x2 = timeoutFiredPos.x-moveTollerance;
	
						var y1 = timeoutFiredPos.y+moveTollerance;
						var y2 = timeoutFiredPos.y-moveTollerance;

						if((x > x1 || x < x2) && (y > y1 || y < y2)){
							timeoutFiredPos = {x:x,y:y};
							createTimer();
						}
					});

				} else {
					cbh(y,on,ev);
				}
			}
		}).live('mouseout',function(ev){
			var y = this;
			//we check ev.reatedTarget because if you are moused over an html select elements option drop down ie delivers a mouse out with undefined related target
			if(hoverOn && ev.relatedTarget && !$.withinElement(ev.relatedTarget,y)){
				hoverOn = false;
				if(currentTimer){
					clearTimeout(currentTimer);
				}
				if(dlyOff){
					setTimeout(function(){
						if(!hoverOn){
							$(y).unbind('mousemove');
							cbh(y,off,ev);
						}
					},dlyOff);
				} else {
					$(y).unbind('mousemove');
					cbh(y,off,ev);
				}
			}
		});
	},
	dieHover:function(){
		return this.removeClass('jqhover').die('mouseover').die('mouseout').unbind('mousemove');
	},
	xyInside:function(x,y){
		var dims = this.getDims();
		return (x >= dims.x) && (y >= dims.y) && (x <= (dims.x+dims.w)) && (y <= (dims.y+dims.h));
	},
	windowUncolide:function(cb){
		this.each(function(){
			var out = $(this).elementOverlap(window);
			if(out.x || out.y){
				var css = {};
				if(out.x){
					css.left = $(this).left()+out.x;
					if(css.left<0) css.left = 0;
				}
				if(out.y){
					css.top = $(this).top()+out.y;
					if(css.top<0) css.top = 0;
				}
				var elDims = $(this).getDims();
				//the blocker makes it so mouse events will not be fired on the object being animated untill animations are complete 
				var blocker = $(this).find(".jq-uncolide-blocker")[0];
				if(blocker) $(blocker).remove();
				blocker = $("<div class='jq-uncolide-blocker' style='width:"+elDims.w+"px;height:"+elDims.w+"px;position:absolute;top:0px;left:0px;'></div>").appendTo(this)[0];
				
				$(this).animate(css,function(){

					$(blocker).remove();
					if(typeof cb == 'function') cb.call(this);
				});
			} else {
				if(typeof cb == 'function') cb.call(this);
			}
		});
	},
	elementOverlap:function(element){
		var x,y,wd,d,el,out;

		el = $(element)[0];

		out = {x:0,y:0};

		if(this[0] === el) return out;

		wd = $(el).getDims();

		d = this.eq(0).getDims(true);

		if(!$(el).xyInside(d.x,d.y)){//top corner out?
			x = (d.x-wd.x);
			y = (d.y-wd.y);
			out.x = (x<0?x:0)*-1;
			out.y = (y<0?y:0)*-1;
		}

		if(!$(el).xyInside(d.x+d.w,d.y+d.h)){//bottom corner out?
			x = (wd.x+wd.w)-(d.x+d.w);
			y = (wd.y+wd.h)-(d.y+d.h);
			out.x = (x<0?x:out.x);
			out.y = (y<0?y:out.y);
		}

		return out;
	},
	center:function(x,y){
		var dims;
		//getter mode
		if(typeof x == 'undefined'){
			dims = this.getDims();
			x = dims.x+(Math.round(dims.w/2));
			y = dims.y+(Math.round(dims.h/2));
			return {x:x,y:y};
		} else {
		//setter mode
			if(typeof x == 'object'){
			//if x is an element i want to use the center of it as my x,y
				var center = $(x).center();
				x = center.x;
				y = center.y;
			}
			dims = this.getDims();
			return $(this).css({position:'absolute',top:y-Math.round(dims.h/2)+'px',left:x-Math.round(dims.w/2)+'px'});
		}
	},
	top:function(px,relative){
		if(px || px === 0) return  this.css({position:(relative?'relative':'absolute'),top:px+'px'});
		return this.getDims().y;
	},
	left:function(px,relative){
		if(px || px === 0) return this.css({position:(relative?'relative':'absolute'),left:px+'px'});
		return this.getDims().x;
	},
	fadeBox:function(html,duration,appendTo){
		duration = duration || 3000;
		var activeClass = 'fade-box-active';
		this.each(function(){
			if($(this).hasClass(activeClass)) return;

			var el = this;
			var dims = $(el).getDims();
			var left = dims.x;
			var btm = dims.y+dims.h;

			if(!appendTo){
				appendTo = $("body")[0];
			} else {
				appendTo = $(appendTo)[0];
				//make dims relative to appendto cont
				var appendToDims = $(appendTo).getDims();
				left = left-appendToDims.x;
				btm = btm-appendToDims.y;
			}

			var node = $("<div/>").appendTo(appendTo).hide().html(html).css({zIndex:1005}).top(btm).left(left).show()[0];
			$(el).addClass(activeClass);
			$(node).windowUncolide(function(){
				setTimeout(function(){
					$(node).fadeOut('fast',function(){
						$(el).removeClass(activeClass);
						$(node).remove();
					});
				},duration);
			});
		});
		return this;
	},
	log:function(msg){
		if(msg) $.logInfo(msg);
		arguments[0] = this;
		$.log.apply(this,arguments);
		return this;
	},
	liveHoverMouseable:function(on,off,move,intent){
		this.liveHover(function(ev){
			if(on) on.call(this,ev);

			if(move) $(this).mousemove(move);

		},function(ev){

			if(move) $(this).unbind('mousemove',move);

			if(off) off.call(this,ev);
		},intent);
		return this;
	},
	textAreaLimit:function(userConfig){
		var preConfig = {counterSelector:'.counter',textAreaSelector:'textarea',maxLen:null,onOverLimit:false}
		var config = $.extend(preConfig,userConfig||{});

		this.each(function(){

			var textArea = $(this).find(config.textAreaSelector)[0];
			var counter = $(this).find(config.counterSelector)[0];
			if(textArea){
				var maxLen = config.maxLen;
				if(!maxLen){
					var maxLen = $(textArea).attr("maxlength") || 0;
				}
				if(config.onOverLimit){
					textArea.onOverLimit = onOverLimit;
				}

				var enforce = function(){
					var value = $(textArea).val() || '';
					if (value.length > maxLen) {
						$(textArea).val(value.substring(0, maxLen));
						if(textArea.onOverLimit){
							textArea.onOverLimit();
						}
					}

					if(counter){
						var remaining = maxLen - value.length;
						if (remaining > 0) {
							$(counter).html(remaining);
						} else {
							$(counter).html(0);
						}
					}
					return true;
				};

				$(textArea).keypress(function(ev){
					//$.log(ev);
					//set a timeout because we want the key press to happen before we check value
					//its too much to check all of the non visible keycodes (backspace etc) against the ones that make letters so we get clever
					setTimeout(enforce,10);
					return true;//allow key to go through
				});
				$(textArea).keypress();
				//fire key press to make sure any text loaded by the browser as default gets limited and counters get set
			}
		});
	},
	liveHoverZoom:function(onStart,onStop,config,intent){
		var defaults = {zoomsrc:false,zoomtarget:false,zoomloading:false};
		config = $.extend(defaults,config || {});

		var lastX,//the lastX coord of the mouse in the image container
		lastY,//the lastY coord of the mouse in the image container
		preloaded,//has the image been preloaded yet
		state,//is the mouse over or out?
		box,//this is the handle that displays over the image
		boxShowing,//is the handle showing
		boxCenter,//width/2 and height/2 of handle used to center box on mouse
		zoom,//this is the zoom image dom element
		zoomerDims,//the dims of the zoom target
		imageDims,//the width and height of the image
		widthScaleFactor,//the scale factor based on the width of the zoom image and the width of the image
		heightScaleFactor;//the scale factor based on the height of the zoom image and the height of the image

		var hideBox = function(){
			if(box && boxShowing){
				$(box).hide();
				boxShowing = false;
			}
		}

		var showBox  = function(){
			if(box && !boxShowing){
				$(box).show();
				boxShowing = true;
			}
		}

		var preload = function(img,event){
			var makeLoader = function(zoomloading){
				if(zoomloading){
					var zl = $(zoomloading);
					if(zl.length) return zl.clone()[0];
				} else {
					return $("<div>Loading</div>")[0];
				}
				return false;
			}

			var loadStop = function(){
				if(loader){
					$(loader).remove();
				}
			}

			var fillZoom = function(zoomImage){
				imageDims = $(img).getDims();

				var zoomDims = $(zoomImage).getDims();

				zoomerDims = $(config.zoomtarget).css({overflow:'hidden',position:'relative'}).html("").append(zoomImage).getDims();


				widthScaleFactor = zoomDims.w/imageDims.w;
				heightScaleFactor = zoomDims.h/imageDims.h;

				zoom = zoomImage;

				makeBox();
			}

			var makeBox = function(){
				if(box) $(box).remove();
				var w = (zoomerDims.w/widthScaleFactor);
				var h = (zoomerDims.h/heightScaleFactor);
				boxShowing = false;
				box = $("<div class='opacity-50' style='display:none;background:#d4d4d4;width:"+w+"px;height:"+h+"px'></div>").appendTo(imgParent)[0];
				boxCenter = {w:Math.floor(w/2),h:Math.floor(h/2)};
				if(state){
					showBox(box);
				}
			}

			var imgParent = $(img).parent().css({overflow:'hidden',position:'relative',height:img.height+'px',width:img.width+'px'})[0];

			if(!config.zoomsrc || !img){
				preloaded = false;
				return false;
			}

			var loader;
			if(!$.isPreloaded(config.zoomsrc)){
				if(typeof config.zoomloading == 'function'){
					loader = config.zoomloading.call(this,img);
				} else {
					loader = makeLoader(config.zoomloading);
				}
			}
			if(loader){
				$(loader).top(5).left(5).appendTo(imgParent);
			}
			preloaded = false;
			$.preload(config.zoomsrc,function(){
				fillZoom(this);
				loadStop();
				preloaded = true;
				if(state){
					mouseMoved();
					if(onStart) onStart.call(imgParent,event,config);
				}
			},function(){loadStop();});

		}

		var mouseMoved = function(ev){
			var x = lastX-imageDims.x;
			var y = lastY-imageDims.y;
			if(box){
				x = x-boxCenter.w;
				y = y-boxCenter.h;
				$(box).top(y || 0).left(x || 0);
			}

			if(zoom){
				x = x*widthScaleFactor;
				y = y*heightScaleFactor;
				//x and y are now the coords of where the top corner of the view area should be in the zoom image
				//but if i invert them it mean i want to "push" x and y away from the corner
				$(zoom).top(-y || 0).left(-x || 0);
			}
		}

		$(this).liveHoverMouseable(function(ev){
			var dataset = $(this).dataset(['zoomsrc','zoomtarget','zoomloading']);
			if(dataset.zoomsrc != config.zoomsrc || dataset.zoomsrc && ($(config.zoomtarget).find('img').attr('src') != dataset.zoomsrc)){
				preloaded = undefined;
			}

			$.extend(config,dataset);

			state = true;
			if(preloaded){//if the size of the zoom target has changed reload the zoomer to update the box and zoomtarget size data
				var targetDims = $(config.zoomtarget).getDims();
				if(zoomerDims && (zoomerDims.w != targetDims.w || zoomerDims.h != targetDims.h)) preloaded = undefined;
			}

			var img = $(this).find('img')[0];
			imageDimsCheck = $(img).getDims();
			if(imageDims){
				if(imageDims.x != imageDimsCheck.x || imageDims.y != imageDimsCheck.y) preloaded = undefined;
				imageDims = imageDimsCheck;
			}

			if(preloaded === undefined){
				preload(img,ev);
			}
			if(preloaded){
				showBox();
				if(onStart) onStart.call(this,ev,config);
			}
		},function(ev){
			state = false;
			if(preloaded){
				if(box){
					hideBox();
				}
				if(onStop) onStop.call(this,ev,config);
			}
		},function(ev){
			lastX = ev.pageX;
			lastY = ev.pageY;
			if(preloaded){
				//update follow box position
				mouseMoved(ev);
			}
		},intent);
	},
	dataset:function(key,val){
		if(typeof key == 'object'){
			if(key instanceof Array){
				var out =  {};
				$.each($(this).dataset(),function(k,v){
					$.each(key,function(l,lk){
						if(lk == k){
							out[k] = v;
							return false;
						}
					});
				});
				return out;
			} else {
				$.each(key,function(k,v){
					$(this).dataset(k,v);
				});
				return this;
			}
		}
		if(val){
			$(this).attr('data-'+key,val);
			return this;
		}

		var el = $(this)[0];
		var data = el.dataset || {};
		if(!el.dataset){
			if(el.attributes){
				$.each(el.attributes,function(k,v){
					if(v.nodeName.indexOf('data-') == 0){
						var part = v.nodeName.substr('data-'.length);
						data[part] = v.value;
						if(key && part == key){
							val = v.value;
							return false;
						}
					}
				});
			} else {
				if(console && console.warn) console.warn('$.fn.dataset: no element attributes!',el,el.attributes,key,val);
			}
		} else if(key){
			$.each(el.dataset,function(k,v){
				if(key && k == key){
					val = v;
					return false;
				}
			});
		}
		return key?val:data;
	},
	watermark:function(defaultText,color){
		if(!color) color = "#aaa";
		var toger = function(el,txt,origColor){
			$(el).blur(function(){
				if($.trim($(this).val()) === ''){
					$(this).val(txt).css({color:color});
				} else $(this).css({color:''});
			});
			$(el).focus(function(){if($.trim($(this).val()) == txt) $(this).val('').css({color:origColor || ''});});
		}

		this.each(function(){
			if(this.nodeName != 'INPUT') return;
			if($(this).attr('type') != 'text') return;
			var txt = defaultText;
			if(!txt) txt = $(this).attr('title');
			if(txt){
				toger(this,txt,$(this).css('color'));
				$(this).blur();
			}
		});
	}
});

$.extend({
	positionHelper:{
		drawSlagBox:function(selector,html){

			if($("#slagbox").length > 0) $("#slagbox").remove();

			var dims = this.elementDims($(selector),true);

			var pos_str = 'width:'+dims.w+'px;height:'+dims.h+'px;position:absolute;z-index:1500;top:'+dims.y+'px;left:'+dims.x+'px';

			$('body').append('<div id="slagbox" style="background:purple;'+pos_str+'">SLAG BOX<div>'+(html || '')+'</div></div>');
	
		},
		elementDims:function(jq,outer){
			var el = $(jq)[0];
			var dims = {w:0,h:0,x:0,y:0};

			if(outer){
				dims.w = $(el).outerWidth();
				if(!dims.w || dims.w == 'NaN') dims.w = false;
				dims.h = $(el).outerHeight();
				if(!dims.h || dims.h == 'NaN') dims.h = false;
			}
			if(!dims.w){
				dims.w = $(el).width();
				//if an image is hidden this is the only way to get the dimensions
				if(!dims.w && el.nodeName == 'IMG') dims.w = el.width;
			}
			if(!dims.h){
				dims.h = $(el).height();
				if(!dims.h && el.nodeName == 'IMG') dims.h = el.height;
			}

			if(el === window){
				dims.x = jq.scrollLeft();
				dims.y = jq.scrollTop();
			} else if(el === document){
				dims.x = 0;
				dims.y = 0;
			} else {
				//IE7
				try{
					dims.x = jq.offset().left;
					dims.y = jq.offset().top;
				}catch(e){dims.x = 0;dims.y = 0;}
			}
			return dims;
		}
	},
	isPreloaded:function(src){
		return ($.preloaded[src]? true : false);
	},
	preloaded:{},
	preload:function(src,callback,error){
		if(typeof(src) == 'object' && src.length > 0){
			$.each(src,function(k,v){
				$.preload(v,callback,error);
			});
		} else {
			var img = new Image();
			$(img).bind('load',function(){
				$.preloaded[src] = {w:this.width,h:this.width};
				callback.call(img);
			});
			if( error ) $(img).bind('error',error);
			img.src = src;
		}
	},
	log:function(){
		console.log.apply(console,arguments);
	},
	logInfo:function(){
		console.info.apply(console,arguments);
	},
	logWarn:function(){
		console.warn.apply(console,arguments);
	},
	/*Used to manage mouseenter and mouseleave type behavior - this copies the behavior of the jQuery private "withinElement" function
	*	Checks if an event happened on an element within another element
	*	Used in jQuery.event.special.mouseenter and mouseleave handlers
	*/
	withinElement:function(child,parent) {
		// Traverse up the tree
		while ( child && child != parent )
			try { child = child.parentNode; }
			catch(e) { parent = child; }

		if( parent != child ){
			return false;
		}

		return true;
	},
	overlayCurrent:false,
	overlay:function(arg,onclose){
		arg = arg || 'create';
		var body = $("body")[0];
		if(arg == 'create'){
			if(!$.overlayCurrent){
				var docDims = $(document).getDims();
				$(body).css({overflow:'hidden'});//remove scrollbars
				$.overlayCurrent = $('<div class="opacity-50" style="background:#999;z-index:1000;"></div>').height(docDims.h).width(docDims.w).top(0).left(0).appendTo(body)[0];
				$($.overlayCurrent).click(function(ev){
					$.overlay('remove');
					if(typeof onclose == 'function'){
						this.onclose = onclose;
						this.onclose(ev);
					}
				});
				$(window).bind('resize',function(){
					$($.overlayCurrent).width($(document).width());
					$($.overlayCurrent).height($(document).height());
				});
			}
		} else {
			if($.overlayCurrent){
				$(window).unbind('resize');
				$($.overlayCurrent).remove();
				$(body).css({overflow:''});//put back scrollbars
				$.overlayCurrent = null;
			}
		}
		return $.overlayCurrent;
	},
	fireBugLite:function(){
		var firebug=document.createElement('script');
		firebug.setAttribute('src','http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js');
		document.body.appendChild(firebug);
		(function(){
			if(window.firebug){
				firebug.init();
			}else{
				setTimeout(arguments.callee);
			}
		})();
		fireBugOpened = true;
	},
	liveHoverIntentTimer:false
});

if(!window.firebug){
	if(location.hash.indexOf('firebug') != -1){
		$.fireBugLite();
		setTimeout(function(){
			window.console = firebug.d.console.cmd;
			window.console('welcome to the firbug lite hack for ie!');
		},2000);
	}
}
})(jQuery);